mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-17 13:22:58 +00:00
LibJS: Disallow Temporal.Duration input values to be non-integers
This is a normative change in the Temporal spec. See: https://github.com/tc39/proposal-temporal/commit/8c85450
This commit is contained in:
parent
92708746c8
commit
ec1e1f4f12
|
@ -1477,7 +1477,7 @@ ThrowCompletionOr<double> to_positive_integer(GlobalObject& global_object, Value
|
|||
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)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
|
@ -1528,7 +1528,7 @@ ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject& global_object,
|
|||
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)
|
||||
{
|
||||
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_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>
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
any = true;
|
||||
|
||||
// ii. Let val be ? ToNumber(val).
|
||||
value = TRY(value.to_number(global_object));
|
||||
// ii. Let val be 𝔽(? ToIntegerWithoutRounding(val)).
|
||||
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
|
||||
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.
|
||||
// iii. Set result's internal slot whose name is the Internal Slot value of the current row to val.
|
||||
result.*internal_slot = value.as_double();
|
||||
}
|
||||
}
|
||||
|
@ -198,16 +192,10 @@ ThrowCompletionOr<PartialDuration> to_partial_duration(GlobalObject& global_obje
|
|||
// i. Set any to true.
|
||||
any = true;
|
||||
|
||||
// ii. Set value to ? ToNumber(value).
|
||||
value = TRY(value.to_number(global_object));
|
||||
// ii. Set value to 𝔽(? ToIntegerWithoutRounding(value)).
|
||||
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
|
||||
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.
|
||||
// iii. Set result's internal slot whose name is the Internal Slot value of the current row to value.
|
||||
result.*internal_slot = value.as_double();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,35 +51,35 @@ ThrowCompletionOr<Object*> DurationConstructor::construct(FunctionObject& new_ta
|
|||
auto& vm = this->vm();
|
||||
auto& global_object = this->global_object();
|
||||
|
||||
// 2. Let y be ? ToIntegerThrowOnInfinity(years).
|
||||
auto y = TRY(to_integer_throw_on_infinity(global_object, vm.argument(0), ErrorType::TemporalInvalidDuration));
|
||||
// 2. Let y be ? ToIntegerWithoutRounding(years).
|
||||
auto y = TRY(to_integer_without_rounding(global_object, vm.argument(0), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 3. Let mo be ? ToIntegerThrowOnInfinity(months).
|
||||
auto mo = TRY(to_integer_throw_on_infinity(global_object, vm.argument(1), ErrorType::TemporalInvalidDuration));
|
||||
// 3. Let mo be ? ToIntegerWithoutRounding(months).
|
||||
auto mo = TRY(to_integer_without_rounding(global_object, vm.argument(1), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 4. Let w be ? ToIntegerThrowOnInfinity(weeks).
|
||||
auto w = TRY(to_integer_throw_on_infinity(global_object, vm.argument(2), ErrorType::TemporalInvalidDuration));
|
||||
// 4. Let w be ? ToIntegerWithoutRounding(weeks).
|
||||
auto w = TRY(to_integer_without_rounding(global_object, vm.argument(2), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 5. Let d be ? ToIntegerThrowOnInfinity(days).
|
||||
auto d = TRY(to_integer_throw_on_infinity(global_object, vm.argument(3), ErrorType::TemporalInvalidDuration));
|
||||
// 5. Let d be ? ToIntegerWithoutRounding(days).
|
||||
auto d = TRY(to_integer_without_rounding(global_object, vm.argument(3), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 6. Let h be ? ToIntegerThrowOnInfinity(hours).
|
||||
auto h = TRY(to_integer_throw_on_infinity(global_object, vm.argument(4), ErrorType::TemporalInvalidDuration));
|
||||
// 6. Let h be ? ToIntegerWithoutRounding(hours).
|
||||
auto h = TRY(to_integer_without_rounding(global_object, vm.argument(4), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 7. Let m be ? ToIntegerThrowOnInfinity(minutes).
|
||||
auto m = TRY(to_integer_throw_on_infinity(global_object, vm.argument(5), ErrorType::TemporalInvalidDuration));
|
||||
// 7. Let m be ? ToIntegerWithoutRounding(minutes).
|
||||
auto m = TRY(to_integer_without_rounding(global_object, vm.argument(5), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 8. Let s be ? ToIntegerThrowOnInfinity(seconds).
|
||||
auto s = TRY(to_integer_throw_on_infinity(global_object, vm.argument(6), ErrorType::TemporalInvalidDuration));
|
||||
// 8. Let s be ? ToIntegerWithoutRounding(seconds).
|
||||
auto s = TRY(to_integer_without_rounding(global_object, vm.argument(6), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 9. Let ms be ? ToIntegerThrowOnInfinity(milliseconds).
|
||||
auto ms = TRY(to_integer_throw_on_infinity(global_object, vm.argument(7), ErrorType::TemporalInvalidDuration));
|
||||
// 9. Let ms be ? ToIntegerWithoutRounding(milliseconds).
|
||||
auto ms = TRY(to_integer_without_rounding(global_object, vm.argument(7), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 10. Let mis be ? ToIntegerThrowOnInfinity(microseconds).
|
||||
auto mis = TRY(to_integer_throw_on_infinity(global_object, vm.argument(8), ErrorType::TemporalInvalidDuration));
|
||||
// 10. Let mis be ? ToIntegerWithoutRounding(microseconds).
|
||||
auto mis = TRY(to_integer_without_rounding(global_object, vm.argument(8), ErrorType::TemporalInvalidDuration));
|
||||
|
||||
// 11. Let ns be ? ToIntegerThrowOnInfinity(nanoseconds).
|
||||
auto ns = TRY(to_integer_throw_on_infinity(global_object, vm.argument(9), ErrorType::TemporalInvalidDuration));
|
||||
// 11. Let ns be ? ToIntegerWithoutRounding(nanoseconds).
|
||||
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).
|
||||
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);
|
||||
});
|
||||
|
||||
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
|
||||
test.skip("Duration string argument", () => {
|
||||
const duration = Temporal.Duration.from("TODO");
|
||||
|
@ -60,11 +67,5 @@ describe("errors", () => {
|
|||
RangeError,
|
||||
"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", () => {
|
||||
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(() => {
|
||||
new Temporal.Duration().with({ [property]: value });
|
||||
}).toThrowWithMessage(
|
||||
|
|
Loading…
Reference in a new issue