Skip to content

Commit

Permalink
Temporal: Copy options object in {Plain,Zoned}DateTime.{from,p.with}
Browse files Browse the repository at this point in the history
  • Loading branch information
ptomato committed Aug 16, 2023
1 parent 01a4950 commit 5ecb902
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -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");
Original file line number Diff line number Diff line change
@@ -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);
16 changes: 10 additions & 6 deletions test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 = [];

Expand All @@ -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");
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@

/*---
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() {
super("iso8601");
}
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);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 = [];

Expand Down Expand Up @@ -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");
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
Original file line number Diff line number Diff line change
@@ -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);
19 changes: 12 additions & 7 deletions test/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -116,6 +120,7 @@ function createOptionsObserver({ overflow = "constrain", disambiguation = "compa
overflow,
disambiguation,
offset,
extra: "property",
}, "options");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
Loading

0 comments on commit 5ecb902

Please sign in to comment.