From e845e7c8147805611a931cb82a96e9f98b0aa4de Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Wed, 1 Sep 2021 00:20:45 +0100 Subject: [PATCH] LibJS: Throw TypeError if Instant.prototype.round() options is undefined This is a normative change in the Temporal spec. See: https://github.com/tc39/proposal-temporal/commit/943018f --- Userland/Libraries/LibJS/Runtime/ErrorTypes.h | 1 + .../Runtime/Temporal/InstantPrototype.cpp | 33 +++++++++++-------- .../Instant/Instant.prototype.round.js | 7 ++++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index cbba762fe82..3886b43c0fa 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -190,6 +190,7 @@ M(TemporalInvalidPlainYearMonth, "Invalid plain year month") \ M(TemporalInvalidTime, "Invalid time") \ M(TemporalInvalidTimeZoneName, "Invalid time zone name") \ + M(TemporalMissingOptionsObject, "Required options object is missing or undefined") \ M(TemporalMissingRequiredProperty, "Required property {} is missing or undefined") \ M(TemporalPropertyMustBePositiveInteger, "Property must be a positive integer") \ M(ThisHasNotBeenInitialized, "|this| has not been initialized") \ diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp index b4d80ec3497..278f09d4e61 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp @@ -191,17 +191,24 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round) if (vm.exception()) return {}; - // 3. Set options to ? GetOptionsObject(options). + // 3. If options is undefined, then + if (vm.argument(0).is_undefined()) { + // a. Throw a TypeError exception. + vm.throw_exception(global_object, ErrorType::TemporalMissingOptionsObject); + return {}; + } + + // 4. Set options to ? GetOptionsObject(options). auto* options = get_options_object(global_object, vm.argument(0)); if (vm.exception()) return {}; - // 4. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », undefined). + // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », undefined). auto smallest_unit_value = to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, {}); if (vm.exception()) return {}; - // 5. If smallestUnit is undefined, throw a RangeError exception. + // 6. If smallestUnit is undefined, throw a RangeError exception. if (!smallest_unit_value.has_value()) { vm.throw_exception(global_object, ErrorType::OptionIsNotValidValue, vm.names.undefined.as_string(), "smallestUnit"); return {}; @@ -209,38 +216,38 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round) // At this point smallest_unit_value can only be a string auto& smallest_unit = *smallest_unit_value; - // 6. Let roundingMode be ? ToTemporalRoundingMode(options, "halfExpand"). + // 7. Let roundingMode be ? ToTemporalRoundingMode(options, "halfExpand"). auto rounding_mode = to_temporal_rounding_mode(global_object, *options, "halfExpand"); if (vm.exception()) return {}; double maximum; - // 7. If smallestUnit is "hour", then + // 8. If smallestUnit is "hour", then if (smallest_unit == "hour"sv) { // a. Let maximum be 24. maximum = 24; } - // 8. Else if smallestUnit is "minute", then + // 9. Else if smallestUnit is "minute", then else if (smallest_unit == "minute"sv) { // a. Let maximum be 1440. maximum = 1440; } - // 9. Else if smallestUnit is "second", then + // 10. Else if smallestUnit is "second", then else if (smallest_unit == "second"sv) { // a. Let maximum be 86400. maximum = 86400; } - // 10. Else if smallestUnit is "millisecond", then + // 11. Else if smallestUnit is "millisecond", then else if (smallest_unit == "millisecond"sv) { // a. Let maximum be 8.64 × 10^7. maximum = 86400000; } - // 11. Else if smallestUnit is "microsecond", then + // 12. Else if smallestUnit is "microsecond", then else if (smallest_unit == "microsecond"sv) { // a. Let maximum be 8.64 × 10^10. maximum = 86400000000; } - // 12. Else, + // 13. Else, else { // a. Assert: smallestUnit is "nanosecond". VERIFY(smallest_unit == "nanosecond"sv); @@ -248,17 +255,17 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round) maximum = 86400000000000; } - // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, true). + // 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, true). auto rounding_increment = to_temporal_rounding_increment(global_object, *options, maximum, true); if (vm.exception()) return {}; - // 14. Let roundedNs be ? RoundTemporalInstant(instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode). + // 15. Let roundedNs be ? RoundTemporalInstant(instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode). auto* rounded_ns = round_temporal_instant(global_object, instant->nanoseconds(), rounding_increment, smallest_unit, *rounding_mode); if (vm.exception()) return {}; - // 15. Return ! CreateTemporalInstant(roundedNs). + // 16. Return ! CreateTemporalInstant(roundedNs). return create_temporal_instant(global_object, *rounded_ns); } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Instant/Instant.prototype.round.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Instant/Instant.prototype.round.js index 58d98a3f156..b6c74f20c49 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Instant/Instant.prototype.round.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Instant/Instant.prototype.round.js @@ -30,6 +30,13 @@ describe("errors", () => { }).toThrowWithMessage(TypeError, "Not a Temporal.Instant"); }); + test("missing options object", () => { + expect(() => { + const instant = new Temporal.Instant(1n); + instant.round(); + }).toThrowWithMessage(TypeError, "Required options object is missing or undefined"); + }); + test("invalid rounding mode", () => { expect(() => { const instant = new Temporal.Instant(1n);