mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 07:17:35 +00:00
LibJS: Disallow Temporal.Duration input values to be non-integers
This is a normative change in the Temporal spec.
See: 8c85450
This commit is contained in:
parent
92708746c8
commit
ec1e1f4f12
6 changed files with 60 additions and 48 deletions
|
@ -1477,7 +1477,7 @@ ThrowCompletionOr<double> to_positive_integer(GlobalObject& global_object, Value
|
||||||
return integer;
|
return integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.48 PrepareTemporalFields ( fields, fieldNames, requiredFields ), https://tc39.es/proposal-temporal/#sec-temporal-preparetemporalfields
|
// 13.49 PrepareTemporalFields ( fields, fieldNames, requiredFields ), https://tc39.es/proposal-temporal/#sec-temporal-preparetemporalfields
|
||||||
ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject& global_object, Object const& fields, Vector<String> const& field_names, Vector<StringView> const& required_fields)
|
ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject& global_object, Object const& fields, Vector<String> const& field_names, Vector<StringView> const& required_fields)
|
||||||
{
|
{
|
||||||
auto& vm = global_object.vm();
|
auto& vm = global_object.vm();
|
||||||
|
@ -1528,7 +1528,7 @@ ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject& global_object,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.49 PreparePartialTemporalFields ( fields, fieldNames ), https://tc39.es/proposal-temporal/#sec-temporal-preparepartialtemporalfields
|
// 13.50 PreparePartialTemporalFields ( fields, fieldNames ), https://tc39.es/proposal-temporal/#sec-temporal-preparepartialtemporalfields
|
||||||
ThrowCompletionOr<Object*> prepare_partial_temporal_fields(GlobalObject& global_object, Object const& fields, Vector<String> const& field_names)
|
ThrowCompletionOr<Object*> prepare_partial_temporal_fields(GlobalObject& global_object, Object const& fields, Vector<String> const& field_names)
|
||||||
{
|
{
|
||||||
auto& vm = global_object.vm();
|
auto& vm = global_object.vm();
|
||||||
|
|
|
@ -142,7 +142,7 @@ ThrowCompletionOr<double> to_positive_integer(GlobalObject&, Value argument);
|
||||||
ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names, Vector<StringView> const& required_fields);
|
ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names, Vector<StringView> const& required_fields);
|
||||||
ThrowCompletionOr<Object*> prepare_partial_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names);
|
ThrowCompletionOr<Object*> prepare_partial_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names);
|
||||||
|
|
||||||
// 13.46 ToIntegerThrowOnInfinity ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerthrowoninfinity
|
// 13.47 ToIntegerThrowOnInfinity ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerthrowoninfinity
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ThrowCompletionOr<double> to_integer_throw_on_infinity(GlobalObject& global_object, Value argument, ErrorType error_type, Args... args)
|
ThrowCompletionOr<double> to_integer_throw_on_infinity(GlobalObject& global_object, Value argument, ErrorType error_type, Args... args)
|
||||||
{
|
{
|
||||||
|
@ -161,4 +161,25 @@ ThrowCompletionOr<double> to_integer_throw_on_infinity(GlobalObject& global_obje
|
||||||
return integer;
|
return integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 13.48 ToIntegerWithoutRounding ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerwithoutrounding
|
||||||
|
template<typename... Args>
|
||||||
|
ThrowCompletionOr<double> to_integer_without_rounding(GlobalObject& global_object, Value argument, ErrorType error_type, Args... args)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
// 1. Let number be ? ToNumber(argument).
|
||||||
|
auto number = TRY(argument.to_number(global_object));
|
||||||
|
|
||||||
|
// 2. If number is NaN, +0𝔽, or −0𝔽 return 0.
|
||||||
|
if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// 3. If ! IsIntegralNumber(number) is false, throw a RangeError exception.
|
||||||
|
if (!number.is_integral_number())
|
||||||
|
return vm.template throw_completion<RangeError>(global_object, error_type, args...);
|
||||||
|
|
||||||
|
// 4. Return ℝ(number).
|
||||||
|
return number.as_double();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,16 +102,10 @@ ThrowCompletionOr<TemporalDuration> to_temporal_duration_record(GlobalObject& gl
|
||||||
// i. Set any to true.
|
// i. Set any to true.
|
||||||
any = true;
|
any = true;
|
||||||
|
|
||||||
// ii. Let val be ? ToNumber(val).
|
// ii. Let val be 𝔽(? ToIntegerWithoutRounding(val)).
|
||||||
value = TRY(value.to_number(global_object));
|
value = Value(TRY(to_integer_without_rounding(global_object, value, ErrorType::TemporalInvalidDurationPropertyValueNonIntegral, property.as_string(), value.to_string_without_side_effects())));
|
||||||
|
|
||||||
// iii. If ! IsIntegralNumber(val) is false, then
|
// iii. Set result's internal slot whose name is the Internal Slot value of the current row to val.
|
||||||
if (!value.is_integral_number()) {
|
|
||||||
// 1. Throw a RangeError exception.
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidDurationPropertyValueNonIntegral, property.as_string(), value.to_string_without_side_effects());
|
|
||||||
}
|
|
||||||
|
|
||||||
// iv. Set result's internal slot whose name is the Internal Slot value of the current row to val.
|
|
||||||
result.*internal_slot = value.as_double();
|
result.*internal_slot = value.as_double();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,16 +192,10 @@ ThrowCompletionOr<PartialDuration> to_partial_duration(GlobalObject& global_obje
|
||||||
// i. Set any to true.
|
// i. Set any to true.
|
||||||
any = true;
|
any = true;
|
||||||
|
|
||||||
// ii. Set value to ? ToNumber(value).
|
// ii. Set value to 𝔽(? ToIntegerWithoutRounding(value)).
|
||||||
value = TRY(value.to_number(global_object));
|
value = Value(TRY(to_integer_without_rounding(global_object, value, ErrorType::TemporalInvalidDurationPropertyValueNonIntegral, property.as_string(), value.to_string_without_side_effects())));
|
||||||
|
|
||||||
// iii. If ! IsIntegralNumber(value) is false, then
|
// iii. Set result's internal slot whose name is the Internal Slot value of the current row to value.
|
||||||
if (!value.is_integral_number()) {
|
|
||||||
// 1. Throw a RangeError exception.
|
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidDurationPropertyValueNonIntegral, property.as_string(), value.to_string_without_side_effects());
|
|
||||||
}
|
|
||||||
|
|
||||||
// iv. Set result's internal slot whose name is the Internal Slot value of the current row to value.
|
|
||||||
result.*internal_slot = value.as_double();
|
result.*internal_slot = value.as_double();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,35 +51,35 @@ ThrowCompletionOr<Object*> DurationConstructor::construct(FunctionObject& new_ta
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
auto& global_object = this->global_object();
|
auto& global_object = this->global_object();
|
||||||
|
|
||||||
// 2. Let y be ? ToIntegerThrowOnInfinity(years).
|
// 2. Let y be ? ToIntegerWithoutRounding(years).
|
||||||
auto y = TRY(to_integer_throw_on_infinity(global_object, vm.argument(0), ErrorType::TemporalInvalidDuration));
|
auto y = TRY(to_integer_without_rounding(global_object, vm.argument(0), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 3. Let mo be ? ToIntegerThrowOnInfinity(months).
|
// 3. Let mo be ? ToIntegerWithoutRounding(months).
|
||||||
auto mo = TRY(to_integer_throw_on_infinity(global_object, vm.argument(1), ErrorType::TemporalInvalidDuration));
|
auto mo = TRY(to_integer_without_rounding(global_object, vm.argument(1), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 4. Let w be ? ToIntegerThrowOnInfinity(weeks).
|
// 4. Let w be ? ToIntegerWithoutRounding(weeks).
|
||||||
auto w = TRY(to_integer_throw_on_infinity(global_object, vm.argument(2), ErrorType::TemporalInvalidDuration));
|
auto w = TRY(to_integer_without_rounding(global_object, vm.argument(2), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 5. Let d be ? ToIntegerThrowOnInfinity(days).
|
// 5. Let d be ? ToIntegerWithoutRounding(days).
|
||||||
auto d = TRY(to_integer_throw_on_infinity(global_object, vm.argument(3), ErrorType::TemporalInvalidDuration));
|
auto d = TRY(to_integer_without_rounding(global_object, vm.argument(3), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 6. Let h be ? ToIntegerThrowOnInfinity(hours).
|
// 6. Let h be ? ToIntegerWithoutRounding(hours).
|
||||||
auto h = TRY(to_integer_throw_on_infinity(global_object, vm.argument(4), ErrorType::TemporalInvalidDuration));
|
auto h = TRY(to_integer_without_rounding(global_object, vm.argument(4), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 7. Let m be ? ToIntegerThrowOnInfinity(minutes).
|
// 7. Let m be ? ToIntegerWithoutRounding(minutes).
|
||||||
auto m = TRY(to_integer_throw_on_infinity(global_object, vm.argument(5), ErrorType::TemporalInvalidDuration));
|
auto m = TRY(to_integer_without_rounding(global_object, vm.argument(5), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 8. Let s be ? ToIntegerThrowOnInfinity(seconds).
|
// 8. Let s be ? ToIntegerWithoutRounding(seconds).
|
||||||
auto s = TRY(to_integer_throw_on_infinity(global_object, vm.argument(6), ErrorType::TemporalInvalidDuration));
|
auto s = TRY(to_integer_without_rounding(global_object, vm.argument(6), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 9. Let ms be ? ToIntegerThrowOnInfinity(milliseconds).
|
// 9. Let ms be ? ToIntegerWithoutRounding(milliseconds).
|
||||||
auto ms = TRY(to_integer_throw_on_infinity(global_object, vm.argument(7), ErrorType::TemporalInvalidDuration));
|
auto ms = TRY(to_integer_without_rounding(global_object, vm.argument(7), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 10. Let mis be ? ToIntegerThrowOnInfinity(microseconds).
|
// 10. Let mis be ? ToIntegerWithoutRounding(microseconds).
|
||||||
auto mis = TRY(to_integer_throw_on_infinity(global_object, vm.argument(8), ErrorType::TemporalInvalidDuration));
|
auto mis = TRY(to_integer_without_rounding(global_object, vm.argument(8), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 11. Let ns be ? ToIntegerThrowOnInfinity(nanoseconds).
|
// 11. Let ns be ? ToIntegerWithoutRounding(nanoseconds).
|
||||||
auto ns = TRY(to_integer_throw_on_infinity(global_object, vm.argument(9), ErrorType::TemporalInvalidDuration));
|
auto ns = TRY(to_integer_without_rounding(global_object, vm.argument(9), ErrorType::TemporalInvalidDuration));
|
||||||
|
|
||||||
// 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget).
|
// 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget).
|
||||||
return TRY(create_temporal_duration(global_object, y, mo, w, d, h, m, s, ms, mis, ns, &new_target));
|
return TRY(create_temporal_duration(global_object, y, mo, w, d, h, m, s, ms, mis, ns, &new_target));
|
||||||
|
|
|
@ -39,6 +39,13 @@ describe("correct behavior", () => {
|
||||||
expectDurationOneToTen(duration);
|
expectDurationOneToTen(duration);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("NaN value becomes zero", () => {
|
||||||
|
// NOTE: NaN does *not* throw a RangeError anymore - which is questionable, IMO - as of:
|
||||||
|
// https://github.com/tc39/proposal-temporal/commit/8c854507a52efbc6e9eb2642f0f928df38e5c021
|
||||||
|
const duration = Temporal.Duration.from({ years: "foo" });
|
||||||
|
expect(duration.years).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
// Un-skip once ParseTemporalDurationString is implemented
|
// Un-skip once ParseTemporalDurationString is implemented
|
||||||
test.skip("Duration string argument", () => {
|
test.skip("Duration string argument", () => {
|
||||||
const duration = Temporal.Duration.from("TODO");
|
const duration = Temporal.Duration.from("TODO");
|
||||||
|
@ -60,11 +67,5 @@ describe("errors", () => {
|
||||||
RangeError,
|
RangeError,
|
||||||
"Invalid value for duration property 'years': must be an integer, got 1.2" // ...29999999999999 - let's not include that in the test :^)
|
"Invalid value for duration property 'years': must be an integer, got 1.2" // ...29999999999999 - let's not include that in the test :^)
|
||||||
);
|
);
|
||||||
expect(() => {
|
|
||||||
Temporal.Duration.from({ years: "foo" });
|
|
||||||
}).toThrowWithMessage(
|
|
||||||
RangeError,
|
|
||||||
"Invalid value for duration property 'years': must be an integer, got NaN"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -79,7 +79,9 @@ describe("errors", () => {
|
||||||
|
|
||||||
test("invalid duration value", () => {
|
test("invalid duration value", () => {
|
||||||
for (const property of DURATION_PROPERTIES) {
|
for (const property of DURATION_PROPERTIES) {
|
||||||
for (const value of [1.23, NaN, Infinity]) {
|
// NOTE: NaN does *not* throw a RangeError anymore - which is questionable, IMO - as of:
|
||||||
|
// https://github.com/tc39/proposal-temporal/commit/8c854507a52efbc6e9eb2642f0f928df38e5c021
|
||||||
|
for (const value of [1.23, Infinity]) {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new Temporal.Duration().with({ [property]: value });
|
new Temporal.Duration().with({ [property]: value });
|
||||||
}).toThrowWithMessage(
|
}).toThrowWithMessage(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue