diff options
Diffstat (limited to 'js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract')
48 files changed, 2021 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js new file mode 100644 index 0000000000..2396a974c4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-max.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Maximum allowed duration +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(1970, 1); + +const maxCases = [ + ["P273790Y8M42DT23H59M59.999999999S", "string with max years"], + [{ years: 273790, months: 8, days: 42, nanoseconds: 86399999999999 }, "property bag with max years"], + ["P3285488M42DT23H59M59.999999999S", "string with max months"], + [{ months: 3285488, days: 42, nanoseconds: 86399999999999 }, "property bag with max months"], + ["P14285718W5DT23H59M59.999999999S", "string with max weeks"], + [{ weeks: 14285718, days: 5, nanoseconds: 86399999999999 }, "property bag with max weeks"], + ["P100000031DT23H59M59.999999999S", "string with max days"], + [{ days: 100000031, nanoseconds: 86399999999999 }, "property bag with max days"], + ["PT2400000767H59M59.999999999S", "string with max hours"], + [{ hours: 2400000767, nanoseconds: 3599999999999 }, "property bag with max hours"], + ["PT144000046079M59.999999999S", "string with max minutes"], + [{ minutes: 144000046079, nanoseconds: 59999999999 }, "property bag with max minutes"], + ["PT8640002764799.999999999S", "string with max seconds"], + [{ seconds: 8640002764799, nanoseconds: 999999999 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = instance.subtract(arg); + TemporalHelpers.assertPlainYearMonth(result, -271821, 4, "M04", `operation succeeds with ${descr}`); +} + +const minCases = [ + ["-P273790Y8M12DT23H59M59.999999999S", "string with min years"], + [{ years: -273790, months: -8, days: -12, nanoseconds: -86399999999999 }, "property bag with min years"], + ["-P3285488M12DT23H59M59.999999999S", "string with min months"], + [{ months: -3285488, days: -12, nanoseconds: -86399999999999 }, "property bag with min months"], + ["-P14285714W2DT23H59M59.999999999S", "string with min weeks"], + [{ weeks: -14285714, days: -2, nanoseconds: -86399999999999 }, "property bag with min weeks"], + ["-P100000000DT23H59M59.999999999S", "string with min days"], + [{ days: -100000000, nanoseconds: -86399999999999 }, "property bag with min days"], + ["-PT2400000023H59M59.999999999S", "string with min hours"], + [{ hours: -2400000023, nanoseconds: -3599999999999 }, "property bag with min hours"], + ["-PT144000001439M59.999999999S", "string with min minutes"], + [{ minutes: -144000001439, nanoseconds: -59999999999 }, "property bag with min minutes"], + ["-PT8640000086399.999999999S", "string with min seconds"], + [{ seconds: -8640000086399, nanoseconds: -999999999 }, "property bag with min seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = instance.subtract(arg); + TemporalHelpers.assertPlainYearMonth(result, 275760, 9, "M09", `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js new file mode 100644 index 0000000000..3d72383b35 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-object.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: A Duration object is supported as the argument +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const jun13 = Temporal.PlainYearMonth.from("2013-06"); +const diff = Temporal.Duration.from("P18Y7M"); +TemporalHelpers.assertPlainYearMonth(jun13.subtract(diff), 1994, 11, "M11"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js new file mode 100644 index 0000000000..d0977de87a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-duration-out-of-range.js @@ -0,0 +1,75 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Duration-like argument that is out of range +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(1970, 1); + +const cases = [ + // 2^32 = 4294967296 + ["P4294967296Y", "string with years > max"], + [{ years: 4294967296 }, "property bag with years > max"], + ["-P4294967296Y", "string with years < min"], + [{ years: -4294967296 }, "property bag with years < min"], + ["P4294967296M", "string with months > max"], + [{ months: 4294967296 }, "property bag with months > max"], + ["-P4294967296M", "string with months < min"], + [{ months: -4294967296 }, "property bag with months < min"], + ["P4294967296W", "string with weeks > max"], + [{ weeks: 4294967296 }, "property bag with weeks > max"], + ["-P4294967296W", "string with weeks < min"], + [{ weeks: -4294967296 }, "property bag with weeks < min"], + + // ceil(max safe integer / 86400) = 104249991375 + ["P104249991375D", "string with days > max"], + [{ days: 104249991375 }, "property bag with days > max"], + ["P104249991374DT24H", "string where hours balance into days > max"], + [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"], + ["-P104249991375D", "string with days < min"], + [{ days: -104249991375 }, "property bag with days < min"], + ["-P104249991374DT24H", "string where hours balance into days < min"], + [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"], + + // ceil(max safe integer / 3600) = 2501999792984 + ["PT2501999792984H", "string with hours > max"], + [{ hours: 2501999792984 }, "property bag with hours > max"], + ["PT2501999792983H60M", "string where minutes balance into hours > max"], + [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"], + ["-PT2501999792984H", "string with hours < min"], + [{ hours: -2501999792984 }, "property bag with hours < min"], + ["-PT2501999792983H60M", "string where minutes balance into hours < min"], + [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"], + + // ceil(max safe integer / 60) = 150119987579017 + ["PT150119987579017M", "string with minutes > max"], + [{ minutes: 150119987579017 }, "property bag with minutes > max"], + ["PT150119987579016M60S", "string where seconds balance into minutes > max"], + [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"], + ["-PT150119987579017M", "string with minutes < min"], + [{ minutes: -150119987579017 }, "property bag with minutes < min"], + ["-PT150119987579016M60S", "string where seconds balance into minutes < min"], + [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"], + + // 2^53 = 9007199254740992 + ["PT9007199254740992S", "string with seconds > max"], + [{ seconds: 9007199254740992 }, "property bag with seconds > max"], + [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"], + [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"], + [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"], + ["-PT9007199254740992S", "string with seconds < min"], + [{ seconds: -9007199254740992 }, "property bag with seconds < min"], + [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"], + [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"], + [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"], +]; + +for (const [arg, descr] of cases) { + assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js new file mode 100644 index 0000000000..21ff0edfc5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-invalid-property.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: temporalDurationLike object must contain at least one correctly spelled property +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); + +assert.throws( + TypeError, + () => instance.subtract({}), + "Throws TypeError if no property is present" +); + +assert.throws( + TypeError, + () => instance.subtract({ nonsense: true }), + "Throws TypeError if no recognized property is present" +); + +assert.throws( + TypeError, + () => instance.subtract({ sign: 1 }), + "Sign property is not recognized" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js new file mode 100644 index 0000000000..ea3156580b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Using lower units in subtract() works +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const ym = Temporal.PlainYearMonth.from("2019-11"); + +const tests = [ + [{ days: 1 }, 2019, 11, "M11"], + [{ hours: 1 }, 2019, 11, "M11"], + [{ minutes: 1 }, 2019, 11, "M11"], + [{ seconds: 1 }, 2019, 11, "M11"], + [{ milliseconds: 1 }, 2019, 11, "M11"], + [{ microseconds: 1 }, 2019, 11, "M11"], + [{ nanoseconds: 1 }, 2019, 11, "M11"], + [{ days: 29 }, 2019, 11, "M11"], + [{ days: 30 }, 2019, 10, "M10"], + [{ days: 60 }, 2019, 10, "M10"], + [{ days: 61 }, 2019, 9, "M09"], + [{ hours: 720 }, 2019, 10, "M10"], + [{ minutes: 43200 }, 2019, 10, "M10"], + [{ seconds: 2592000 }, 2019, 10, "M10"], + [{ milliseconds: 2592000_000 }, 2019, 10, "M10"], + [{ microseconds: 2592000_000_000 }, 2019, 10, "M10"], + [{ nanoseconds: 2592000_000_000_000 }, 2019, 10, "M10"], +]; + +for (const [argument, ...expected] of tests) { + TemporalHelpers.assertPlainYearMonth(ym.subtract(argument), ...expected, "no options"); + TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "constrain" }), ...expected, "constrain"); + TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "reject" }), ...expected, "reject"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js new file mode 100644 index 0000000000..f66b8abada --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-mixed-sign.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Positive and negative values in the temporalDurationLike argument are not acceptable +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); + +["constrain", "reject"].forEach((overflow) => { + assert.throws( + RangeError, + () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }), + `mixed positive and negative values always throw (overflow = "${overflow}")` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js new file mode 100644 index 0000000000..f9460b024d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-not-object.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Passing a primitive other than string to subtract() throws +features: [Symbol, Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); +assert.throws(TypeError, () => instance.subtract(undefined), "undefined"); +assert.throws(TypeError, () => instance.subtract(null), "null"); +assert.throws(TypeError, () => instance.subtract(true), "boolean"); +assert.throws(RangeError, () => instance.subtract(""), "empty string"); +assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol"); +assert.throws(TypeError, () => instance.subtract(7), "number"); +assert.throws(TypeError, () => instance.subtract(7n), "bigint"); +assert.throws(TypeError, () => instance.subtract([]), "array"); +assert.throws(TypeError, () => instance.subtract(() => {}), "function"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js new file mode 100644 index 0000000000..8b7393da1d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-object.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Passing an object to subtract() works +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const ym = Temporal.PlainYearMonth.from("2019-11"); + +const tests = [ + [{ months: 2 }, 2019, 9, "M09"], + [{ years: 1 }, 2018, 11, "M11"], + [{ months: -2 }, 2020, 1, "M01"], + [{ years: -1 }, 2020, 11, "M11"], +]; + +for (const [argument, ...expected] of tests) { + TemporalHelpers.assertPlainYearMonth(ym.subtract(argument), ...expected, "no options"); + TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "constrain" }), ...expected, "constrain"); + TemporalHelpers.assertPlainYearMonth(ym.subtract(argument, { overflow: "reject" }), ...expected, "reject"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js new file mode 100644 index 0000000000..779b7dbd08 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-singular-properties.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Singular properties in the property bag are always ignored +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); + +[ + { year: 1 }, + { month: 2 }, + { week: 3 }, + { day: 4 }, + { hour: 5 }, + { minute: 6 }, + { second: 7 }, + { millisecond: 8 }, + { microsecond: 9 }, + { nanosecond: 10 }, +].forEach((badObject) => { + assert.throws(TypeError, () => instance.subtract(badObject), + "Throw TypeError if temporalDurationLike is not valid"); +}); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js new file mode 100644 index 0000000000..b3e7c9a74c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string-negative-fractional-units.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Strings with fractional duration units are treated with the correct sign +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); + +const resultHours = instance.subtract("-PT24.567890123H"); +TemporalHelpers.assertPlainYearMonth(resultHours, 2000, 5, "M05", "negative fractional hours"); + +const resultMinutes = instance.subtract("-PT1440.567890123M"); +TemporalHelpers.assertPlainYearMonth(resultMinutes, 2000, 5, "M05", "negative fractional minutes"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js new file mode 100644 index 0000000000..deb2820797 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-string.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: A string is parsed into the correct object when passed as the argument +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 }); +const result = instance.subtract("P3M"); +TemporalHelpers.assertPlainYearMonth(result, 2000, 2, "M02"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js new file mode 100644 index 0000000000..badb8f195c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const subtract = Temporal.PlainYearMonth.prototype.subtract; + +assert.sameValue(typeof subtract, "function"); + +const args = [new Temporal.Duration(5)]; + +assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => subtract.apply(null, args), "null"); +assert.throws(TypeError, () => subtract.apply(true, args), "true"); +assert.throws(TypeError, () => subtract.apply("", args), "empty string"); +assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => subtract.apply(1, args), "1"); +assert.throws(TypeError, () => subtract.apply({}, args), "plain object"); +assert.throws(TypeError, () => subtract.apply(Temporal.PlainYearMonth, args), "Temporal.PlainYearMonth"); +assert.throws(TypeError, () => subtract.apply(Temporal.PlainYearMonth.prototype, args), "Temporal.PlainYearMonth.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..97b965d7d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-array-iteration.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const instance = new Temporal.PlainYearMonth(2023, 5, "iso8601"); +instance.subtract({ years: 5, months: 2 }); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..6b2834cf80 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd"); +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateAdd should not be looked up"); + }, +}); + +const instance = new Temporal.PlainYearMonth(2000, 5, "iso8601", 1); +instance.subtract(new Temporal.Duration(1)); + +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js new file mode 100644 index 0000000000..e69c799d31 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: > + Tests that Temporal.PlainYearMonth.prototype.subtract + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.PlainYearMonth.prototype.subtract), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.PlainYearMonth.prototype.subtract), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.PlainYearMonth.prototype.subtract), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.PlainYearMonth.prototype.subtract.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js new file mode 100644 index 0000000000..b636036de0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments-extra-options.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: plainyearmonth.prototype.subtract should pass extra fields in copied options objects. +info: | + YearMonthFromFields ( calendar, fields [ , options ] ) + + 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.extra", + "get options.extra", + // Temporal.Calendar.prototype.dateAdd + "get options.overflow", + // overwriting property in custom calendar dateAdd + "getOwnPropertyDescriptor options.overflow", +]; +const options = TemporalHelpers.propertyBagObserver(actual, { extra: 5 }, "options"); + +let dateAddCalls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateAdd(date, duration, options) { + const result = super.dateAdd(date, duration, options); + dateAddCalls++; + if (dateAddCalls == 2) + options.overflow = 'meatloaf'; + return result; + } + yearMonthFromFields(...args) { + assert.sameValue(args.length, 2, "args.length"); + assert.sameValue(typeof args[0], "object", "args[0]"); + assert.notSameValue(args[1], options, "args[1]"); + return super.yearMonthFromFields(...args); + } +} +const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar()); +const result = plainYearMonth.subtract({ months: 5 }, options); +TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10"); +assert.compareArray(actual, expected, "extra field options object order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js new file mode 100644 index 0000000000..49e74d46b8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-arguments.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: plainyearmonth.prototype.subtract should respect calendar arguments and pass copied options objects. +info: | + YearMonthFromFields ( calendar, fields [ , options ] ) + + 5. Let yearMonth be ? Invoke(calendar, "yearMonthFromFields", « fields, options »). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + // Temporal.Calendar.prototype.dateAdd + "get options.overflow", + "get options.overflow.toString", + "call options.overflow.toString", + // overwriting property in custom calendar dateAdd + "getOwnPropertyDescriptor options.overflow", + // Temporal.Calendar.prototype.yearMonthFromFields (toPrimitiveObserver copied but not options object) + "get options.overflow.toString", + "call options.overflow.toString", +]; +const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); + +let dateAddCalls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateAdd(date, duration, options) { + const result = super.dateAdd(date, duration, options); + dateAddCalls++; + if (dateAddCalls == 2) + options.overflow = 'meatloaf'; + return result; + } + yearMonthFromFields(...args) { + assert.sameValue(args.length, 2, "args.length"); + assert.sameValue(typeof args[0], "object", "args[0]"); + assert.notSameValue(args[1], options, "args[1]"); + return super.yearMonthFromFields(...args); + } +} +const plainYearMonth = new Temporal.PlainYearMonth(2000, 7, new CustomCalendar()); +const result = plainYearMonth.subtract({ months: 9 }, options); +TemporalHelpers.assertPlainYearMonth(result, 1999, 10, "M10"); +assert.compareArray(actual, expected, "copied options object order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js new file mode 100644 index 0000000000..c788dd00cf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Duration subtraction from PlainYearMonth calls Calendar.dateAdd the right number of times +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddPlainDateInstance(); +const instance = new Temporal.PlainYearMonth(1983, 3, calendar); +TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: 5}), 1983, 2, 'M02', "Removing 5 weeks from March in is8601 calendar") +assert.sameValue(calendar.dateAddCallCount, 2, "dateAdd called 2 times with positive subtract"); + +calendar.dateAddCallCount = 0; +TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: -5}), 1983, 4, 'M04', "Removing -5 weeks from March in is8601 calendar") +assert.sameValue(calendar.dateAddCallCount, 1, "dateAdd called once with negative subtract"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js new file mode 100644 index 0000000000..72455866bc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: PlainYearMonth.prototype.subtract should call dateAdd with the appropriate values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateAdd(plainDate, duration, options) { + ++calls; + if (calls == 2) { + TemporalHelpers.assertPlainDate(plainDate, 2000, 3, "M03", 31, "plainDate argument"); + TemporalHelpers.assertDuration(duration, 0, -10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument"); + assert.sameValue(typeof options, "object", "options argument: type"); + assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype"); + } + return super.dateAdd(plainDate, duration, options); + } +} + +const plainYearMonth = new Temporal.PlainYearMonth(2000, 3, new CustomCalendar()); +const result = plainYearMonth.subtract({ months: 10 }); +TemporalHelpers.assertPlainYearMonth(result, 1999, 5, "M05"); +assert.sameValue(calls, 2, "should have called dateAdd 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js new file mode 100644 index 0000000000..b83c3ccd36 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js @@ -0,0 +1,164 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: > + Calls calendar's dateFromFields method to obtain a start date for the + operation, based on the sign of the duration +info: | + 9. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»). + 10. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0). + 11. If _sign_ < 0, then + a. Let _dayFromCalendar_ be ? CalendarDaysInMonth(_calendar_, _yearMonth_). + b. Let _day_ be ? ToPositiveInteger(_dayFromCalendar_). + 12. Else, + a. Let _day_ be 1. + 13. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, _day_). + 14. Let _date_ be ? DateFromFields(_calendar_, _fields_, *undefined*). +includes: [deepEqual.js, temporalHelpers.js] +features: [Temporal] +---*/ + +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCalls = []; + } + year(date) { + // years in this calendar start and end on the same day as ISO 8601 years + return date.getISOFields().isoYear; + } + month(date) { + // this calendar has 10 months of 36 days each, plus an 11th month of 5 or 6 + const { isoYear, isoMonth, isoDay } = date.getISOFields(); + const isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay); + return Math.floor((isoDate.dayOfYear - 1) / 36) + 1; + } + monthCode(date) { + return "M" + this.month(date).toString().padStart(2, "0"); + } + day(date) { + return (date.dayOfYear - 1) % 36 + 1; + } + daysInMonth(date) { + if (this.month(date) < 11) return 36; + return this.daysInYear(date) - 360; + } + _dateFromFieldsImpl({ year, month, monthCode, day }) { + if (year === undefined) throw new TypeError("year required"); + if (month === undefined && monthCode === undefined) throw new TypeError("one of month or monthCode required"); + if (month !== undefined && month < 1) throw new RangeError("month < 1"); + if (day === undefined) throw new TypeError("day required"); + + if (monthCode !== undefined) { + const numberPart = +(monthCode.slice(1)); + if ("M" + `${numberPart}`.padStart(2, "0") !== monthCode) throw new RangeError("invalid monthCode"); + if (month === undefined) { + month = numberPart; + } else if (month !== numberPart) { + throw new RangeError("month and monthCode must match"); + } + } + + const isoDayOfYear = (month - 1) * 36 + day; + return new Temporal.PlainDate(year, 1, 1).add({ days: isoDayOfYear - 1 }).withCalendar(this); + } + dateFromFields(...args) { + this.dateFromFieldsCalls.push(args); + return this._dateFromFieldsImpl(...args); + } + yearMonthFromFields(fields, options) { + const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, day: 1 }, options).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + monthDayFromFields(fields, options) { + const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, year: 2000 }, options).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + dateAdd(date, duration, options) { + const {isoYear, isoMonth, isoDay} = date.getISOFields(); + let {years, months, weeks, days} = duration; + let iter = new Temporal.PlainDate(isoYear + years, isoMonth, isoDay, "iso8601"); + const monthsDays = months * 36; + if (iter.dayOfYear + monthsDays > iter.daysInYear || iter.dayOfYear + monthsDays < 1) + throw new Error("complicated addition not implemented in this test"); + return iter.add({ weeks, days: monthsDays + days }).withCalendar(this); + } + toString() { + return "thirty-six"; + } +} + +const calendar = new CustomCalendar(); +const month2 = Temporal.PlainYearMonth.from({ year: 2022, month: 2, calendar }); +const lessThanOneMonth = new Temporal.Duration(0, 0, 0, 35); +const oneMonth = new Temporal.Duration(0, 0, 0, 36); + +// Reference ISO dates in the custom calendar: +// M01 = 01-01 +// M02 = 02-06 +// M03 = 03-14 + +calendar.dateFromFieldsCalls = []; +TemporalHelpers.assertPlainYearMonth( + month2.subtract(lessThanOneMonth), + 2022, 2, "M02", + "subtracting positive less than one month's worth of days yields the same month", + /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6 +); +assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice"); +assert.deepEqual( + calendar.dateFromFieldsCalls[1][0], + { year: 2022, monthCode: "M02", day: 36 }, + "last day of month 2 passed to dateFromFields when subtracting positive duration" +); +assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); + +calendar.dateFromFieldsCalls = []; +TemporalHelpers.assertPlainYearMonth( + month2.subtract(oneMonth), + 2022, 1, "M01", + "subtracting positive one month's worth of days yields the previous month", + /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 1 +); +assert.sameValue(calendar.dateFromFieldsCalls.length, 2, "dateFromFields was called twice"); +assert.deepEqual( + calendar.dateFromFieldsCalls[1][0], + { year: 2022, monthCode: "M02", day: 36 }, + "last day of month 2 passed to dateFromFields when subtracting positive duration" +); +assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); + +calendar.dateFromFieldsCalls = []; +TemporalHelpers.assertPlainYearMonth( + month2.subtract(lessThanOneMonth.negated()), + 2022, 2, "M02", + "subtracting negative less than one month's worth of days yields the same month", + /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 6 +); +assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); +assert.deepEqual( + calendar.dateFromFieldsCalls[0][0], + { year: 2022, monthCode: "M02", day: 1 }, + "first day of month 2 passed to dateFromFields when subtracting negative duration" +); +assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); + +calendar.dateFromFieldsCalls = []; +TemporalHelpers.assertPlainYearMonth( + month2.subtract(oneMonth.negated()), + 2022, 3, "M03", + "subtracting negative one month's worth of days yields the following month", + /* era = */ undefined, /* eraYear = */ undefined, /* referenceISODay = */ 14 +); +assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); +assert.deepEqual( + calendar.dateFromFieldsCalls[0][0], + { year: 2022, monthCode: "M02", day: 1 }, + "first day of month 2 passed to dateFromFields when subtracting negative duration" +); +assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js new file mode 100644 index 0000000000..0c5ca99437 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fields-iterable.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.plainyearmonth.prototype.subtract step 8: + 8. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "monthCode", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const yearmonth = new Temporal.PlainYearMonth(2000, 5, calendar); +yearmonth.subtract({ months: 1 }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..897d365a8f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-fromfields-called-with-null-prototype-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.PlainYearMonth(2000, 5, calendar); +instance.subtract(new Temporal.Duration(1)); +assert.sameValue(calendar.dateFromFieldsCallCount, 2, "dateFromFields should have been called twice on the calendar"); +assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js new file mode 100644 index 0000000000..b3ad583339 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-yearmonthfromfields-called-with-null-prototype-options.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: > + Calendar.yearMonthFromFields method is called with a null-prototype object + as the options value when call originates internally +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution(); +const instance = new Temporal.PlainYearMonth(2019, 6, calendar); +const argument = new Temporal.Duration(1, 1); +instance.subtract(argument); +assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should be called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..5ee587243a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/constructor-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const ym = new Temporal.PlainYearMonth(2023, 5, calendar); + +assert.throws(RangeError, () => ym.subtract({days: 123})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js new file mode 100644 index 0000000000..d611f5e50c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/custom-daysInMonth-irrelevant.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Subtraction of positive duration to a PlainYearMonth is not influenced by the implementation of daysInMonth() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + daysInMonth(ym, ...args) { + return 15; + } +} + +const customCalendar = new CustomCalendar(); +const instance = new Temporal.PlainYearMonth(2023, 3, customCalendar); + +TemporalHelpers.assertPlainYearMonth(instance.subtract({days: 30}), 2023, 3, 'M03', "Subtracting 30 days from calendar reimplementing daysinMonth()") + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js new file mode 100644 index 0000000000..9a56dc9ee7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/duplicate-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const ym = new Temporal.PlainYearMonth(2023, 5, calendar); + + assert.throws(RangeError, () => ym.subtract({days: 123})); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js new file mode 100644 index 0000000000..947f07589f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/end-of-month-out-of-range.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: RangeError thrown when subtracting positive duration and end of month is out of range +features: [Temporal] +info: | + AddDurationToOrSubtractDurationFromPlainYearMonth: + 12. If _sign_ < 0, then + a. Let _oneMonthDuration_ be ! CreateTemporalDuration(0, 1, 0, 0, 0, 0, 0, 0, 0, 0). + b. Let _nextMonth_ be ? CalendarDateAdd(_calendar_, _intermediateDate_, _oneMonthDuration_, *undefined*, _dateAdd_). + c. Let _endOfMonthISO_ be ! AddISODate(_nextMonth_.[[ISOYear]], _nextMonth_.[[ISOMonth]], _nextMonth_.[[ISODay]], 0, 0, 0, -1, *"constrain"*). + d. Let _endOfMonth_ be ? CreateTemporalDate(_endOfMonthISO_.[[Year]], _endOfMonthISO_.[[Month]], _endOfMonthISO_.[[Day]], _calendar_). +---*/ + +// Based on a test case by André Bargull <andre.bargull@gmail.com> + +const duration = new Temporal.Duration(0, 0, 0, 1); + +// Calendar addition result is out of range +assert.throws(RangeError, () => new Temporal.PlainYearMonth(275760, 9).subtract(duration), "Addition of 1 month to receiver out of range"); + +// Calendar addition succeeds, but subtracting 1 day gives out of range result +const cal = new class extends Temporal.Calendar { + dateAdd() { + return new Temporal.PlainDate(-271821, 4, 19); + } +}("iso8601"); +assert.throws(RangeError, () => new Temporal.PlainYearMonth(2000, 1, cal).subtract(duration), "Subtraction of 1 day from next month out of range"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..d529ef21c1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is Infinity +esid: sec-temporal.plainyearmonth.prototype.subtract +features: [Temporal] +---*/ +const overflows = ["constrain", "reject"]; +const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"]; + +const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 }); + +overflows.forEach((overflow) => { + fields.forEach((field) => { + assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { overflow })); + }); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return Infinity; + } +}; + +overflows.forEach((overflow) => { + fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js new file mode 100644 index 0000000000..7b0ef46739 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Temporal.PlainYearMonth.prototype.subtract.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.PlainYearMonth.prototype.subtract, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js new file mode 100644 index 0000000000..6489ed09a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/limits.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: RangeError thrown when going out of range +features: [Temporal] +---*/ + +const min = Temporal.PlainYearMonth.from("-271821-04"); +for (const overflow of ["reject", "constrain"]) { + assert.throws(RangeError, () => min.subtract({ months: 1 }, { overflow }), overflow); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js new file mode 100644 index 0000000000..ab18f8ef9a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/month-length.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: subtract() takes month length into account +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const ym = Temporal.PlainYearMonth.from("2019-11"); + +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 27 }), + 2019, 2, "M02"); +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-02").subtract({ days: 28 }), + 2019, 1, "M01"); +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 28 }), + 2020, 2, "M02"); +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-02").subtract({ days: 29 }), + 2020, 1, "M01"); +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 29 }), + 2019, 11, "M11"); +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2019-11").subtract({ days: 30 }), + 2019, 10, "M10"); +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 30 }), + 2020, 1, "M01"); +TemporalHelpers.assertPlainYearMonth(Temporal.PlainYearMonth.from("2020-01").subtract({ days: 31 }), + 2019, 12, "M12"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js new file mode 100644 index 0000000000..fe1133c53d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Temporal.PlainYearMonth.prototype.subtract.name is "subtract". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.PlainYearMonth.prototype.subtract, "name", { + value: "subtract", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..5412bb0f7e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.PlainYearMonth.prototype.subtract throws a RangeError if any value in a property bag is -Infinity +esid: sec-temporal.plainyearmonth.prototype.subtract +features: [Temporal] +---*/ + +const overflows = ["constrain", "reject"]; +const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"]; + +const instance = Temporal.PlainYearMonth.from({ year: 2000, month: 5 }); + +overflows.forEach((overflow) => { + fields.forEach((field) => { + assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { overflow })); + }); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return -Infinity; + } +}; + +overflows.forEach((overflow) => { + fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { overflow })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js new file mode 100644 index 0000000000..e5073101a8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/non-integer-throws-rangeerror.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: A non-integer value for any recognized property in the property bag, throws a RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2000, 5); +const fields = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; +fields.forEach((field) => { + assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 })); + assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js new file mode 100644 index 0000000000..14d79bcdd5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: > + Temporal.PlainYearMonth.prototype.subtract does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.PlainYearMonth.prototype.subtract(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.PlainYearMonth.prototype.subtract), false, + "isConstructor(Temporal.PlainYearMonth.prototype.subtract)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js new file mode 100644 index 0000000000..73622a2eb3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-invalid.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Invalid options throw +features: [Temporal] +---*/ + +const ym = Temporal.PlainYearMonth.from("2019-11"); +const values = [null, true, "hello", Symbol("foo"), 1, 1n]; +for (const badOptions of values) { + assert.throws(TypeError, () => ym.subtract({ years: 1 }, badOptions)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js new file mode 100644 index 0000000000..7c8aa9302d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-object.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: Empty or a function object may be used as options +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.PlainYearMonth(2019, 10); + +const result1 = instance.subtract({ months: 1 }, {}); +TemporalHelpers.assertPlainYearMonth( + result1, 2019, 9, "M09", + "options may be an empty plain object" +); + +const result2 = instance.subtract({ months: 1 }, () => {}); +TemporalHelpers.assertPlainYearMonth( + result2, 2019, 9, "M09", + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js new file mode 100644 index 0000000000..fa37762e41 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Verify that undefined options are handled correctly. +features: [Temporal] +---*/ + +// overflow option has no effect on addition in the ISO calendar, so verify this +// with a custom calendar +class CheckedAdd extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.called = 0; + } + dateAdd(date, duration, options, constructor) { + this.called += 1; + if (this.called == 2) + assert.notSameValue(options, undefined, "options not undefined"); + return super.dateAdd(date, duration, options, constructor); + } +} +const calendar = new CheckedAdd(); + +const yearmonth = new Temporal.PlainYearMonth(2000, 3, calendar); +const duration = { months: 1 }; + +yearmonth.subtract(duration, undefined); +assert.sameValue(calendar.called, 2, "dateAdd should have been called twice"); + +calendar.called = 0; +yearmonth.subtract(duration); +assert.sameValue(calendar.called, 2, "dateAdd should have been called twice"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js new file mode 100644 index 0000000000..cec4d21c4e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.PlainYearMonth(2019, 10); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.subtract({ months: 1 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js new file mode 100644 index 0000000000..c12b7bd1a7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js @@ -0,0 +1,190 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Properties on an object passed to subtract() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalDuration + "get fields.days", + "get fields.days.valueOf", + "call fields.days.valueOf", + "get fields.hours", + "get fields.hours.valueOf", + "call fields.hours.valueOf", + "get fields.microseconds", + "get fields.microseconds.valueOf", + "call fields.microseconds.valueOf", + "get fields.milliseconds", + "get fields.milliseconds.valueOf", + "call fields.milliseconds.valueOf", + "get fields.minutes", + "get fields.minutes.valueOf", + "call fields.minutes.valueOf", + "get fields.months", + "get fields.months.valueOf", + "call fields.months.valueOf", + "get fields.nanoseconds", + "get fields.nanoseconds.valueOf", + "call fields.nanoseconds.valueOf", + "get fields.seconds", + "get fields.seconds.valueOf", + "call fields.seconds.valueOf", + "get fields.weeks", + "get fields.weeks.valueOf", + "call fields.weeks.valueOf", + "get fields.years", + "get fields.years.valueOf", + "call fields.years.valueOf", + // lookup + "get this.calendar.dateAdd", + "get this.calendar.dateFromFields", + "get this.calendar.day", + "get this.calendar.fields", + "get this.calendar.yearMonthFromFields", + // CalendarFields + "call this.calendar.fields", + // PrepareTemporalFields on receiver + "get this.calendar.monthCode", + "call this.calendar.monthCode", + "get this.calendar.year", + "call this.calendar.year", + // calculate last day of month + "call this.calendar.dateFromFields", + "call this.calendar.dateAdd", + "call this.calendar.day", + "call this.calendar.dateFromFields", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + // CalendarDateAdd + "call this.calendar.dateAdd", + // inside Calendar.p.dateAdd + "get options.overflow", + "get options.overflow.toString", + "call options.overflow.toString", + // PrepareTemporalFields on added date + "get this.calendar.monthCode", + "call this.calendar.monthCode", + "get this.calendar.year", + "call this.calendar.year", + // CalendarYearMonthFromFields + "call this.calendar.yearMonthFromFields", + // inside Calendar.p.yearMonthFromFields + "get options.overflow.toString", + "call options.overflow.toString", +]; +const actual = []; + +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.PlainYearMonth(2000, 5, calendar); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +const fields = TemporalHelpers.propertyBagObserver(actual, { + years: 1, + months: 1, + weeks: 1, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); + +const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); + +instance.subtract(fields, options); +assert.compareArray(actual, expected, "order of operations"); + +actual.splice(0); // clear + +const noCalendarExpected = [ + // ToTemporalDuration + "get fields.days", + "get fields.days.valueOf", + "call fields.days.valueOf", + "get fields.hours", + "get fields.hours.valueOf", + "call fields.hours.valueOf", + "get fields.microseconds", + "get fields.microseconds.valueOf", + "call fields.microseconds.valueOf", + "get fields.milliseconds", + "get fields.milliseconds.valueOf", + "call fields.milliseconds.valueOf", + "get fields.minutes", + "get fields.minutes.valueOf", + "call fields.minutes.valueOf", + "get fields.months", + "get fields.nanoseconds", + "get fields.nanoseconds.valueOf", + "call fields.nanoseconds.valueOf", + "get fields.seconds", + "get fields.seconds.valueOf", + "call fields.seconds.valueOf", + "get fields.weeks", + "get fields.years", + // lookup + "get this.calendar.dateAdd", + "get this.calendar.dateFromFields", + "get this.calendar.day", + "get this.calendar.fields", + "get this.calendar.yearMonthFromFields", + // CalendarFields + "call this.calendar.fields", + // PrepareTemporalFields on receiver + "get this.calendar.monthCode", + "call this.calendar.monthCode", + "get this.calendar.year", + "call this.calendar.year", + // CalendarDateFromFields + "call this.calendar.dateFromFields", + // calculate last day of month + "call this.calendar.dateAdd", + "call this.calendar.day", + "call this.calendar.dateFromFields", + // SnapshotOwnProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + // AddDate + "get options.overflow", + "get options.overflow.toString", + "call options.overflow.toString", + // PrepareTemporalFields on added date + "get this.calendar.monthCode", + "call this.calendar.monthCode", + "get this.calendar.year", + "call this.calendar.year", + // CalendarYearMonthFromFields + "call this.calendar.yearMonthFromFields", + // inside Calendar.p.yearMonthFromFields + "get options.overflow.toString", + "call options.overflow.toString", +]; + +const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, { + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); + +instance.subtract(noCalendarFields, options); +assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar units"); + +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js new file mode 100644 index 0000000000..96af1e7847 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-invalid-string.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: RangeError thrown when overflow option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-isoyearmonthfromfields step 2: + 2. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.plainyearmonth.prototype.subtract steps 13–15: + 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_). + 14. ... + 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_). +features: [Temporal] +---*/ + +const yearmonth = new Temporal.PlainYearMonth(2000, 5); +const duration = new Temporal.Duration(1, 1); + +const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"]; +for (const overflow of badOverflows) { + assert.throws( + RangeError, + () => yearmonth.subtract(duration, { overflow }), + `invalid overflow ("${overflow}")` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js new file mode 100644 index 0000000000..f87903ef12 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-undefined.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Fallback value for overflow option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-isoyearmonthfromfields step 2: + 2. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.plainyearmonth.prototype.subtract steps 13–15: + 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_). + 14. ... + 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// In the ISO calendar, PlainYearMonth.prototype.subtract() actually ignores the +// overflow option. There is no subtraction in the ISO calendar that we could +// test which would actually show a difference between the 'constrain' and +// 'reject' values. +const yearmonth = new Temporal.PlainYearMonth(2000, 5); +const duration = new Temporal.Duration(1, 1); +const explicit = yearmonth.subtract(duration, { overflow: undefined }); +TemporalHelpers.assertPlainYearMonth(explicit, 1999, 4, "M04", "default overflow is constrain"); +const implicit = yearmonth.subtract(duration, {}); +TemporalHelpers.assertPlainYearMonth(implicit, 1999, 4, "M04", "default overflow is constrain"); +const lambda = yearmonth.subtract(duration, () => {}); +TemporalHelpers.assertPlainYearMonth(lambda, 1999, 4, "M04", "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js new file mode 100644 index 0000000000..30a23df9cf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow-wrong-type.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Type conversions for overflow option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-isoyearmonthfromfields step 2: + 2. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.plainyearmonth.prototype.subtract steps 13–15: + 13. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _date_, _durationToAdd_, _options_). + 14. ... + 15. Return ? YearMonthFromFields(_calendar_, _addedDateFields_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const yearmonth = new Temporal.PlainYearMonth(2000, 5); +const duration = new Temporal.Duration(1, 1); + +// See TemporalHelpers.checkStringOptionWrongType(); this code path has +// different expectations for observable calls + +assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: null }), "null"); +assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: true }), "true"); +assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: false }), "false"); +assert.throws(TypeError, () => yearmonth.subtract(duration, { overflow: Symbol() }), "symbol"); +assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: 2 }), "number"); +assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: 2n }), "bigint"); +assert.throws(RangeError, () => yearmonth.subtract(duration, { overflow: {} }), "plain object"); + +// toString property is read once by Calendar.dateAdd() and then once again by +// calendar.yearMonthFromFields(). +const expected = [ + "get overflow.toString", + "call overflow.toString", + "get overflow.toString", + "call overflow.toString", +]; +const actual = []; +const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); +const result = yearmonth.subtract(duration, { overflow: observer }); +TemporalHelpers.assertPlainYearMonth(result, 1999, 4, "M04", "object with toString"); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js new file mode 100644 index 0000000000..ed4a5ff063 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: The "subtract" property of Temporal.PlainYearMonth.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.PlainYearMonth.prototype.subtract, + "function", + "`typeof PlainYearMonth.prototype.subtract` is `function`" +); + +verifyProperty(Temporal.PlainYearMonth.prototype, "subtract", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js new file mode 100644 index 0000000000..5660520393 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/proto-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.plainyearmonth.prototype.subtract +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const ym = new Temporal.PlainYearMonth(2023, 5, calendar); + +assert.throws(RangeError, () => ym.subtract({days: 123})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js new file mode 100644 index 0000000000..346758ebd5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/shell.js @@ -0,0 +1,353 @@ +// GENERATED, DO NOT EDIT +// file: deepEqual.js +// Copyright 2019 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Compare two values structurally +defines: [assert.deepEqual] +---*/ + +assert.deepEqual = function(actual, expected, message) { + var format = assert.deepEqual.format; + assert( + assert.deepEqual._compare(actual, expected), + `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}` + ); +}; + +assert.deepEqual.format = function(value, seen) { + switch (typeof value) { + case 'string': + return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`; + case 'number': + case 'boolean': + case 'symbol': + case 'bigint': + return value.toString(); + case 'undefined': + return 'undefined'; + case 'function': + return `[Function${value.name ? `: ${value.name}` : ''}]`; + case 'object': + if (value === null) return 'null'; + if (value instanceof Date) return `Date "${value.toISOString()}"`; + if (value instanceof RegExp) return value.toString(); + if (!seen) { + seen = { + counter: 0, + map: new Map() + }; + } + + let usage = seen.map.get(value); + if (usage) { + usage.used = true; + return `[Ref: #${usage.id}]`; + } + + usage = { id: ++seen.counter, used: false }; + seen.map.set(value, usage); + + if (typeof Set !== "undefined" && value instanceof Set) { + return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`; + } + if (typeof Map !== "undefined" && value instanceof Map) { + return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`; + } + if (Array.isArray ? Array.isArray(value) : value instanceof Array) { + return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`; + } + let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object'; + if (tag === 'Object' && Object.getPrototypeOf(value) === null) { + tag = '[Object: null prototype]'; + } + return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`; + default: + return typeof value; + } +}; + +assert.deepEqual._compare = (function () { + var EQUAL = 1; + var NOT_EQUAL = -1; + var UNKNOWN = 0; + + function deepEqual(a, b) { + return compareEquality(a, b) === EQUAL; + } + + function compareEquality(a, b, cache) { + return compareIf(a, b, isOptional, compareOptionality) + || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality) + || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache) + || NOT_EQUAL; + } + + function compareIf(a, b, test, compare, cache) { + return !test(a) + ? !test(b) ? UNKNOWN : NOT_EQUAL + : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache); + } + + function tryCompareStrictEquality(a, b) { + return a === b ? EQUAL : UNKNOWN; + } + + function tryCompareTypeOfEquality(a, b) { + return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN; + } + + function tryCompareToStringTagEquality(a, b) { + var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined; + var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined; + return aTag !== bTag ? NOT_EQUAL : UNKNOWN; + } + + function isOptional(value) { + return value === undefined + || value === null; + } + + function compareOptionality(a, b) { + return tryCompareStrictEquality(a, b) + || NOT_EQUAL; + } + + function isPrimitiveEquatable(value) { + switch (typeof value) { + case 'string': + case 'number': + case 'bigint': + case 'boolean': + case 'symbol': + return true; + default: + return isBoxed(value); + } + } + + function comparePrimitiveEquality(a, b) { + if (isBoxed(a)) a = a.valueOf(); + if (isBoxed(b)) b = b.valueOf(); + return tryCompareStrictEquality(a, b) + || tryCompareTypeOfEquality(a, b) + || compareIf(a, b, isNaNEquatable, compareNaNEquality) + || NOT_EQUAL; + } + + function isNaNEquatable(value) { + return typeof value === 'number'; + } + + function compareNaNEquality(a, b) { + return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL; + } + + function isObjectEquatable(value) { + return typeof value === 'object'; + } + + function compareObjectEquality(a, b, cache) { + if (!cache) cache = new Map(); + return getCache(cache, a, b) + || setCache(cache, a, b, EQUAL) // consider equal for now + || cacheComparison(a, b, tryCompareStrictEquality, cache) + || cacheComparison(a, b, tryCompareToStringTagEquality, cache) + || compareIf(a, b, isValueOfEquatable, compareValueOfEquality) + || compareIf(a, b, isToStringEquatable, compareToStringEquality) + || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache) + || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache) + || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || cacheComparison(a, b, fail, cache); + } + + function isBoxed(value) { + return value instanceof String + || value instanceof Number + || value instanceof Boolean + || typeof Symbol === 'function' && value instanceof Symbol + || typeof BigInt === 'function' && value instanceof BigInt; + } + + function isValueOfEquatable(value) { + return value instanceof Date; + } + + function compareValueOfEquality(a, b) { + return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isToStringEquatable(value) { + return value instanceof RegExp; + } + + function compareToStringEquality(a, b) { + return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isArrayLikeEquatable(value) { + return (Array.isArray ? Array.isArray(value) : value instanceof Array) + || (typeof Uint8Array === 'function' && value instanceof Uint8Array) + || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray) + || (typeof Uint16Array === 'function' && value instanceof Uint16Array) + || (typeof Uint32Array === 'function' && value instanceof Uint32Array) + || (typeof Int8Array === 'function' && value instanceof Int8Array) + || (typeof Int16Array === 'function' && value instanceof Int16Array) + || (typeof Int32Array === 'function' && value instanceof Int32Array) + || (typeof Float32Array === 'function' && value instanceof Float32Array) + || (typeof Float64Array === 'function' && value instanceof Float64Array) + || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array) + || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array); + } + + function compareArrayLikeEquality(a, b, cache) { + if (a.length !== b.length) return NOT_EQUAL; + for (var i = 0; i < a.length; i++) { + if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + return EQUAL; + } + + function isStructurallyEquatable(value) { + return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference + || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference + || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference + || typeof Map === 'function' && value instanceof Map // comparable via @@iterator + || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator + } + + function compareStructuralEquality(a, b, cache) { + var aKeys = []; + for (var key in a) aKeys.push(key); + + var bKeys = []; + for (var key in b) bKeys.push(key); + + if (aKeys.length !== bKeys.length) { + return NOT_EQUAL; + } + + aKeys.sort(); + bKeys.sort(); + + for (var i = 0; i < aKeys.length; i++) { + var aKey = aKeys[i]; + var bKey = bKeys[i]; + if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + + return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || EQUAL; + } + + function isIterableEquatable(value) { + return typeof Symbol === 'function' + && typeof value[Symbol.iterator] === 'function'; + } + + function compareIteratorEquality(a, b, cache) { + if (typeof Map === 'function' && a instanceof Map && b instanceof Map || + typeof Set === 'function' && a instanceof Set && b instanceof Set) { + if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size + } + + var ar, br; + while (true) { + ar = a.next(); + br = b.next(); + if (ar.done) { + if (br.done) return EQUAL; + if (b.return) b.return(); + return NOT_EQUAL; + } + if (br.done) { + if (a.return) a.return(); + return NOT_EQUAL; + } + if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) { + if (a.return) a.return(); + if (b.return) b.return(); + return NOT_EQUAL; + } + } + } + + function compareIterableEquality(a, b, cache) { + return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache); + } + + function cacheComparison(a, b, compare, cache) { + var result = compare(a, b, cache); + if (cache && (result === EQUAL || result === NOT_EQUAL)) { + setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result)); + } + return result; + } + + function fail() { + return NOT_EQUAL; + } + + function setCache(cache, left, right, result) { + var otherCache; + + otherCache = cache.get(left); + if (!otherCache) cache.set(left, otherCache = new Map()); + otherCache.set(right, result); + + otherCache = cache.get(right); + if (!otherCache) cache.set(right, otherCache = new Map()); + otherCache.set(left, result); + } + + function getCache(cache, left, right) { + var otherCache; + var result; + + otherCache = cache.get(left); + result = otherCache && otherCache.get(right); + if (result) return result; + + otherCache = cache.get(right); + result = otherCache && otherCache.get(left); + if (result) return result; + + return UNKNOWN; + } + + return deepEqual; +})(); + +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js new file mode 100644 index 0000000000..14e0e703f6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subclassing-ignored.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plainyearmonth.prototype.subtract +description: Objects of a subclass are never created as return values for subtract() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.PlainYearMonth, + [2000, 5], + "subtract", + [{ months: 1 }], + (result) => TemporalHelpers.assertPlainYearMonth(result, 2000, 4, "M04"), +); + +reportCompare(0, 0); |