From 5ecb902a0a38bb5923a2d6cc4db4edb22d3d728c Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 2 Mar 2023 17:19:24 -0800 Subject: [PATCH] Temporal: Copy options object in {Plain,Zoned}DateTime.{from,p.with} --- ...ervable-get-overflow-argument-primitive.js | 34 +++++++++++++ ...le-get-overflow-argument-string-invalid.js | 21 ++++++++ .../PlainDateTime/from/order-of-operations.js | 16 +++--- .../PlainDateTime/from/overflow-wrong-type.js | 8 ++- .../prototype/with/calendar-options.js | 12 +++-- .../prototype/with/order-of-operations.js | 16 +++--- .../prototype/with/overflow-wrong-type.js | 8 ++- ...ervable-get-overflow-argument-primitive.js | 49 +++++++++++++++++++ ...le-get-overflow-argument-string-invalid.js | 29 +++++++++++ .../ZonedDateTime/from/order-of-operations.js | 19 ++++--- .../ZonedDateTime/from/overflow-wrong-type.js | 8 ++- .../prototype/with/calendar-options.js | 32 ++++++++++++ .../prototype/with/order-of-operations.js | 18 ++++--- .../prototype/with/overflow-wrong-type.js | 8 ++- 14 files changed, 229 insertions(+), 49 deletions(-) create mode 100644 test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js create mode 100644 test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js create mode 100644 test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js diff --git a/test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js b/test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js new file mode 100644 index 00000000000..babe403a9cd --- /dev/null +++ b/test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-primitive.js @@ -0,0 +1,34 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: overflow property is extracted with string argument. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "get options.overflow.toString", + "call options.overflow.toString", +]; + +let actual = []; +const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options"); + +const result = Temporal.PlainDateTime.from("2021-05-17T12:34:56", options); +assert.compareArray(actual, expected, "Successful call"); +TemporalHelpers.assertPlainDateTime(result, 2021, 5, "M05", 17, 12, 34, 56, 0, 0, 0); + +actual.splice(0); // empty it for the next check +const failureExpected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", +]; + +assert.throws(TypeError, () => Temporal.PlainDateTime.from(7, options)); +assert.compareArray(actual, failureExpected, "Failing call"); diff --git a/test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js b/test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js new file mode 100644 index 00000000000..9eff4e745b5 --- /dev/null +++ b/test/built-ins/Temporal/PlainDateTime/from/observable-get-overflow-argument-string-invalid.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.from +description: overflow property is extracted with ISO-invalid string argument. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", +]; + +let actual = []; +const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "reject" }, "options"); + +assert.throws(RangeError, () => Temporal.PlainDateTime.from("2020-13-34T25:60:60", options)); +assert.compareArray(actual, expected); diff --git a/test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js index ad11f628b94..ba5123b1d2b 100644 --- a/test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js +++ b/test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js @@ -9,6 +9,12 @@ features: [Temporal] ---*/ const expected = [ + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "getOwnPropertyDescriptor options.extra", + "get options.extra", // GetTemporalCalendarSlotValueWithISODefault "get fields.calendar", "has fields.calendar.dateAdd", @@ -67,15 +73,10 @@ const expected = [ "get fields.year.valueOf", "call fields.year.valueOf", // InterpretTemporalDateTimeFields - "get options.overflow", "get options.overflow.toString", "call options.overflow.toString", "get fields.calendar.dateFromFields", "call fields.calendar.dateFromFields", - // inside Calendar.p.dateFromFields - "get options.overflow", - "get options.overflow.toString", - "call options.overflow.toString", ]; const actual = []; @@ -93,7 +94,10 @@ const fields = TemporalHelpers.propertyBagObserver(actual, { calendar: TemporalHelpers.calendarObserver(actual, "fields.calendar"), }, "fields"); -const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); +const options = TemporalHelpers.propertyBagObserver(actual, { + overflow: "constrain", + extra: "property", +}, "options"); Temporal.PlainDateTime.from(fields, options); assert.compareArray(actual, expected, "order of operations"); diff --git a/test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js index 150c22f8cdd..5da8062c25a 100644 --- a/test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js +++ b/test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js @@ -48,14 +48,12 @@ assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overf assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: 2n }), "bigint"); assert.throws(RangeError, () => Temporal.PlainDateTime.from(propertyBag, { overflow: {} }), "plain object"); -// toString property is read once by Calendar.dateFromFields() in the builtin -// calendars, to get the option value for the date part, and then once again -// internally to get the option value for the time part. +// toString property should only be read and converted to a string once, because +// a copied object with the resulting string on it is passed to +// Calendar.dateFromFields(). const expected = [ "get overflow.toString", "call overflow.toString", - "get overflow.toString", - "call overflow.toString", ]; const actual = []; const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js index 935c1333d12..69a22591fae 100644 --- a/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js +++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-options.js @@ -3,12 +3,16 @@ /*--- esid: sec-temporal.plaindatetime.prototype.with -description: The options argument is passed through to Calendar#dateFromFields as-is. +description: > + The options argument is copied and the copy is passed to + Calendar#dateFromFields. includes: [temporalHelpers.js] features: [Temporal] ---*/ -const options = {}; +const options = { + extra: "property", +}; let calledDateFromFields = 0; class Calendar extends Temporal.Calendar { constructor() { @@ -16,7 +20,9 @@ class Calendar extends Temporal.Calendar { } dateFromFields(fields, optionsArg) { ++calledDateFromFields; - assert.sameValue(optionsArg, options, "should pass options object through"); + assert.notSameValue(optionsArg, options, "should pass copied options object"); + assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object"); + assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype"); return super.dateFromFields(fields, optionsArg); } }; diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js index ec3bd35c9f1..bd98fe55a17 100644 --- a/test/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js +++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/order-of-operations.js @@ -12,6 +12,12 @@ const expected = [ // RejectObjectWithCalendarOrTimeZone "get fields.calendar", "get fields.timeZone", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "getOwnPropertyDescriptor options.extra", + "get options.extra", // CalendarFields "get this.calendar.fields", "call this.calendar.fields", @@ -59,15 +65,10 @@ const expected = [ "get this.calendar.mergeFields", "call this.calendar.mergeFields", // InterpretTemporalDateTimeFields - "get options.overflow", "get options.overflow.toString", "call options.overflow.toString", "get this.calendar.dateFromFields", "call this.calendar.dateFromFields", - // inside Calendar.p.dateFromFields - "get options.overflow", - "get options.overflow.toString", - "call options.overflow.toString", ]; const actual = []; @@ -96,7 +97,10 @@ const fields = TemporalHelpers.propertyBagObserver(actual, { nanosecond: 1.7, }, "fields"); -const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); +const options = TemporalHelpers.propertyBagObserver(actual, { + overflow: "constrain", + extra: "property", +}, "options"); instance.with(fields, options); assert.compareArray(actual, expected, "order of operations"); diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js index 75f6fe0aae2..081c4ba8980 100644 --- a/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js +++ b/test/built-ins/Temporal/PlainDateTime/prototype/with/overflow-wrong-type.js @@ -31,14 +31,12 @@ assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2 }), assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: 2n }), "bigint"); assert.throws(RangeError, () => datetime.with({ minute: 45 }, { overflow: {} }), "plain object"); -// toString property is read once by Calendar.dateFromFields() in the builtin -// calendars, to get the option value for the date part, and then once again -// internally to get the option value for the time part. +// toString property should only be read and converted to a string once, because +// a copied object with the resulting string on it is passed to +// Calendar.dateFromFields(). const expected = [ "get overflow.toString", "call overflow.toString", - "get overflow.toString", - "call overflow.toString", ]; const actual = []; const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); diff --git a/test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js b/test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js new file mode 100644 index 00000000000..6d3724cfabb --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: options properties are extracted with string argument. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "get options.disambiguation.toString", + "call options.disambiguation.toString", + "get options.offset.toString", + "call options.offset.toString", + "get options.overflow.toString", + "call options.overflow.toString", +]; + +let actual = []; +const options = TemporalHelpers.propertyBagObserver(actual, { + disambiguation: "compatible", + offset: "ignore", + overflow: "reject", +}, "options"); + +const result = Temporal.ZonedDateTime.from("2001-09-09T01:46:40+00:00[UTC]", options); +assert.compareArray(actual, expected, "Successful call"); +assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n); + +actual.splice(0); // empty it for the next check +const failureExpected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", +]; +assert.throws(TypeError, () => Temporal.ZonedDateTime.from(7, options)); +assert.compareArray(actual, failureExpected, "Failing call"); diff --git a/test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js b/test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js new file mode 100644 index 00000000000..c7ab1200a3d --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: options properties are extracted with ISO-invalid string argument. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", +]; + +let actual = []; +const options = TemporalHelpers.propertyBagObserver(actual, { + disambiguation: "compatible", + offset: "ignore", + overflow: "reject", +}, "options"); + +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-13-34T25:60:60+99:99[UTC]", options)); +assert.compareArray(actual, expected); diff --git a/test/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js index 3e9a74abab6..8ec1a747715 100644 --- a/test/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js +++ b/test/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js @@ -9,6 +9,17 @@ features: [Temporal] ---*/ const expected = [ + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.extra", + "get options.extra", + // ToTemporalCalendar "get item.calendar", "has item.calendar.dateAdd", "has item.calendar.dateFromFields", @@ -72,21 +83,14 @@ const expected = [ "has item.timeZone.getPossibleInstantsFor", "has item.timeZone.id", // InterpretTemporalDateTimeFields - "get options.disambiguation", "get options.disambiguation.toString", "call options.disambiguation.toString", - "get options.offset", "get options.offset.toString", "call options.offset.toString", - "get options.overflow", "get options.overflow.toString", "call options.overflow.toString", "get item.calendar.dateFromFields", "call item.calendar.dateFromFields", - // inside calendar.dateFromFields - "get options.overflow", - "get options.overflow.toString", - "call options.overflow.toString", // InterpretISODateTimeOffset "get item.timeZone.getPossibleInstantsFor", "call item.timeZone.getPossibleInstantsFor", @@ -116,6 +120,7 @@ function createOptionsObserver({ overflow = "constrain", disambiguation = "compa overflow, disambiguation, offset, + extra: "property", }, "options"); } diff --git a/test/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js index aa3f4899570..27086cd0ca6 100644 --- a/test/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js +++ b/test/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js @@ -49,14 +49,12 @@ assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overf assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2 }), "number"); assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: {} }), "plain object"); -// toString property is read once by Calendar.dateFromFields() in the builtin -// calendars, to get the option value for the date part, and then once again -// internally to get the option value for the time part. +// toString property should only be read and converted to a string once, because +// a copied object with the resulting string on it is passed to +// Calendar.dateFromFields(). const expected = [ "get overflow.toString", "call overflow.toString", - "get overflow.toString", - "call overflow.toString", ]; const actual = []; const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js new file mode 100644 index 00000000000..fba8b67aa32 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js @@ -0,0 +1,32 @@ +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + The options argument is copied and the copy is passed to + Calendar#dateFromFields. +features: [Temporal] +---*/ + +const options = { + extra: "property", +}; +let calledDateFromFields = 0; +class Calendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateFromFields(fields, optionsArg) { + ++calledDateFromFields; + assert.notSameValue(optionsArg, options, "should pass copied options object"); + assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object"); + assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype"); + return super.dateFromFields(fields, optionsArg); + } +}; +const calendar = new Calendar(); +const datetime = new Temporal.ZonedDateTime(0n, "UTC", calendar); +const result = datetime.with({ year: 1971 }, options); +assert.sameValue(result.epochNanoseconds, 365n * 86400_000_000_000n, "year changed from 1970 to 1971") +assert.sameValue(calledDateFromFields, 1, "should have called overridden dateFromFields once"); diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js index 002c2827319..0374bdcb1da 100644 --- a/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js @@ -12,6 +12,16 @@ const expected = [ // RejectObjectWithCalendarOrTimeZone "get fields.calendar", "get fields.timeZone", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.extra", + "get options.extra", // GetOffsetNanosecondsFor on receiver "get this.timeZone.getOffsetNanosecondsFor", "call this.timeZone.getOffsetNanosecondsFor", @@ -65,21 +75,14 @@ const expected = [ "get this.calendar.mergeFields", "call this.calendar.mergeFields", // InterpretTemporalDateTimeFields - "get options.disambiguation", "get options.disambiguation.toString", "call options.disambiguation.toString", - "get options.offset", "get options.offset.toString", "call options.offset.toString", - "get options.overflow", "get options.overflow.toString", "call options.overflow.toString", "get this.calendar.dateFromFields", "call this.calendar.dateFromFields", - // Calendar.p.dateFromFields - "get options.overflow", - "get options.overflow.toString", - "call options.overflow.toString", // InterpretISODateTimeOffset "get this.timeZone.getPossibleInstantsFor", "call this.timeZone.getPossibleInstantsFor", @@ -112,6 +115,7 @@ const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain", disambiguation: "compatible", offset: "prefer", + extra: "property", }, "options"); instance.with(fields, options); diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js index 43bcec49c64..309f87d19c1 100644 --- a/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js @@ -31,14 +31,12 @@ assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2 }), assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2n }), "bigint"); assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: {} }), "plain object"); -// toString property is read once by Calendar.dateFromFields() in the builtin -// calendars, to get the option value for the date part, and then once again -// internally to get the option value for the time part. +// toString property should only be read and converted to a string once, because +// a copied object with the resulting string on it is passed to +// Calendar.dateFromFields(). const expected = [ "get overflow.toString", "call overflow.toString", - "get overflow.toString", - "call overflow.toString", ]; const actual = []; const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow");