diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/tests/test262/staging | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
230 files changed, 35614 insertions, 0 deletions
diff --git a/js/src/tests/test262/staging/ArrayBuffer/browser.js b/js/src/tests/test262/staging/ArrayBuffer/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/browser.js diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/access-out-of-bounds-typed-array-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/access-out-of-bounds-typed-array-strict.js new file mode 100644 index 0000000000..a5135998af --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/access-out-of-bounds-typed-array-strict.js @@ -0,0 +1,96 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from AccessOutOfBoundsTypedArray test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +for (let ctor of ctors) { + if (ctor.BYTES_PER_ELEMENT != 1) { + continue; + } + const rab = CreateResizableArrayBuffer(16, 40); + const array = new ctor(rab, 0, 4); + // Initial values + for (let i = 0; i < 4; ++i) { + assert.sameValue(array[i], 0); + } + // Within-bounds write + for (let i = 0; i < 4; ++i) { + array[i] = i; + } + // Within-bounds read + for (let i = 0; i < 4; ++i) { + assert.sameValue(array[i], i); + } + rab.resize(2); + // OOB read. If the RAB isn't large enough to fit the entire TypedArray, + // the length of the TypedArray is treated as 0. + for (let i = 0; i < 4; ++i) { + assert.sameValue(array[i], undefined); + } + // OOB write (has no effect) + for (let i = 0; i < 4; ++i) { + array[i] = 10; + } + rab.resize(4); + // Within-bounds read + for (let i = 0; i < 2; ++i) { + assert.sameValue(array[i], i); + } + // The shrunk-and-regrown part got zeroed. + for (let i = 2; i < 4; ++i) { + assert.sameValue(array[i], 0); + } + rab.resize(40); + // Within-bounds read + for (let i = 0; i < 2; ++i) { + assert.sameValue(array[i], i); + } + for (let i = 2; i < 4; ++i) { + assert.sameValue(array[i], 0); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/array-fill-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-fill-parameter-conversion-resizes-strict.js new file mode 100644 index 0000000000..eb28c9d602 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-fill-parameter-conversion-resizes-strict.js @@ -0,0 +1,113 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ArrayFillParameterConversionResizes test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function ReadDataFromBuffer(ab, ctor) { + let result = []; + const ta = new ctor(ab, 0, ab.byteLength / ctor.BYTES_PER_ELEMENT); + for (let item of ta) { + result.push(Number(item)); + } + return result; +} + +function ArrayFillHelper(ta, n, start, end) { + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + Array.prototype.fill.call(ta, BigInt(n), start, end); + } else { + Array.prototype.fill.call(ta, n, start, end); + } +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 3; + } + }; + ArrayFillHelper(fixedLength, evil, 1, 2); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 0, + 0 + ]); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 1; + } + }; + ArrayFillHelper(fixedLength, 3, evil, 2); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 0, + 0 + ]); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + ArrayFillHelper(fixedLength, 3, 1, evil); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 0, + 0 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/array-sort-with-default-comparison-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-sort-with-default-comparison-strict.js new file mode 100644 index 0000000000..486c3a96e7 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-sort-with-default-comparison-strict.js @@ -0,0 +1,239 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ArraySortWithDefaultComparison test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +// The default comparison function for Array.prototype.sort is the string sort. + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const ArraySortHelper = (ta, ...rest) => { + Array.prototype.sort.call(ta, ...rest); +}; + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taFull = new ctor(rab, 0); + function WriteUnsortedData() { + // Write some data into the array. + for (let i = 0; i < taFull.length; ++i) { + WriteToTypedArray(taFull, i, 10 - 2 * i); + } + } + // Orig. array: [10, 8, 6, 4] + // [10, 8, 6, 4] << fixedLength + // [6, 4] << fixedLengthWithOffset + // [10, 8, 6, 4, ...] << lengthTracking + // [6, 4, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + ArraySortHelper(fixedLength); + assert.compareArray(ToNumbers(taFull), [ + 10, + 4, + 6, + 8 + ]); + WriteUnsortedData(); + ArraySortHelper(fixedLengthWithOffset); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 4, + 6 + ]); + WriteUnsortedData(); + ArraySortHelper(lengthTracking); + assert.compareArray(ToNumbers(taFull), [ + 10, + 4, + 6, + 8 + ]); + WriteUnsortedData(); + ArraySortHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 4, + 6 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [10, 8, 6] + // [10, 8, 6, ...] << lengthTracking + // [6, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + ArraySortHelper(fixedLength); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 6 + ]); + ArraySortHelper(fixedLengthWithOffset); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 6 + ]); + ArraySortHelper(lengthTracking); + assert.compareArray(ToNumbers(taFull), [ + 10, + 6, + 8 + ]); + WriteUnsortedData(); + ArraySortHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 6 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + WriteUnsortedData(); + ArraySortHelper(fixedLength); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), [10]); + ArraySortHelper(fixedLengthWithOffset); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), [10]); + ArraySortHelper(lengthTrackingWithOffset); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), [10]); + ArraySortHelper(lengthTracking); + assert.compareArray(ToNumbers(taFull), [10]); + + // Shrink to zero. + rab.resize(0); + ArraySortHelper(fixedLength); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), []); + ArraySortHelper(fixedLengthWithOffset); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), []); + ArraySortHelper(lengthTrackingWithOffset); // OOB -> NOOP + assert.compareArray(ToNumbers(taFull), []); + ArraySortHelper(lengthTracking); + assert.compareArray(ToNumbers(taFull), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [10, 8, 6, 4, 2, 0] + // [10, 8, 6, 4] << fixedLength + // [6, 4] << fixedLengthWithOffset + // [10, 8, 6, 4, 2, 0, ...] << lengthTracking + // [6, 4, 2, 0, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + ArraySortHelper(fixedLength); + assert.compareArray(ToNumbers(taFull), [ + 10, + 4, + 6, + 8, + 2, + 0 + ]); + WriteUnsortedData(); + ArraySortHelper(fixedLengthWithOffset); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 4, + 6, + 2, + 0 + ]); + WriteUnsortedData(); + ArraySortHelper(lengthTracking); + assert.compareArray(ToNumbers(taFull), [ + 0, + 10, + 2, + 4, + 6, + 8 + ]); + WriteUnsortedData(); + ArraySortHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 0, + 2, + 4, + 6 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/at-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-parameter-conversion-resizes-strict.js new file mode 100644 index 0000000000..6f7e006b38 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-parameter-conversion-resizes-strict.js @@ -0,0 +1,95 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from AtParameterConversionResizes test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function TypedArrayAtHelper(ta, index) { + const result = ta.at(index); + return Convert(result); +} + +function ArrayAtHelper(ta, index) { + const result = Array.prototype.at.call(ta, index); + return Convert(result); +} + +function AtParameterConversionResizes(atHelper) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2); + return 0; + } + }; + assert.sameValue(atHelper(fixedLength, evil), undefined); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + let evil = { + valueOf: () => { + rab.resize(2); + return -1; + } + }; + // The TypedArray is *not* out of bounds since it's length-tracking. + assert.sameValue(atHelper(lengthTracking, evil), undefined); + } +} + +AtParameterConversionResizes(TypedArrayAtHelper); +AtParameterConversionResizes(ArrayAtHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/at-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-strict.js new file mode 100644 index 0000000000..f7c0f112ab --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-strict.js @@ -0,0 +1,139 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from At test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function TypedArrayAtHelper(ta, index) { + const result = ta.at(index); + return Convert(result); +} + +function ArrayAtHelper(ta, index) { + const result = Array.prototype.at.call(ta, index); + return Convert(result); +} + +function At(atHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + let ta_write = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(ta_write, i, i); + } + assert.sameValue(atHelper(fixedLength, -1), 3); + assert.sameValue(atHelper(lengthTracking, -1), 3); + assert.sameValue(atHelper(fixedLengthWithOffset, -1), 3); + assert.sameValue(atHelper(lengthTrackingWithOffset, -1), 3); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + atHelper(fixedLength, -1); + }); + assert.throws(TypeError, () => { + atHelper(fixedLengthWithOffset, -1); + }); + } else { + assert.sameValue(atHelper(fixedLength, -1), undefined); + assert.sameValue(atHelper(fixedLengthWithOffset, -1), undefined); + } + assert.sameValue(atHelper(lengthTracking, -1), 2); + assert.sameValue(atHelper(lengthTrackingWithOffset, -1), 2); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + atHelper(fixedLength, -1); + }); + assert.throws(TypeError, () => { + atHelper(fixedLengthWithOffset, -1); + }); + assert.throws(TypeError, () => { + atHelper(lengthTrackingWithOffset, -1); + }); + } else { + assert.sameValue(atHelper(fixedLength, -1), undefined); + assert.sameValue(atHelper(fixedLengthWithOffset, -1), undefined); + assert.sameValue(atHelper(lengthTrackingWithOffset, -1), undefined); + } + assert.sameValue(atHelper(lengthTracking, -1), 0); + + // Grow so that all TAs are back in-bounds. New memory is zeroed. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + assert.sameValue(atHelper(fixedLength, -1), 0); + assert.sameValue(atHelper(lengthTracking, -1), 0); + assert.sameValue(atHelper(fixedLengthWithOffset, -1), 0); + assert.sameValue(atHelper(lengthTrackingWithOffset, -1), 0); + } +} + +At(TypedArrayAtHelper, true); +At(ArrayAtHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/browser.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/browser.js diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-from-typed-array-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-from-typed-array-strict.js new file mode 100644 index 0000000000..ffc0364625 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-from-typed-array-strict.js @@ -0,0 +1,212 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ConstructFromTypedArray test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function IsBigIntTypedArray(ta) { + return ta instanceof BigInt64Array || ta instanceof BigUint64Array; +} + +function AllBigIntMatchedCtorCombinations(test) { + for (let targetCtor of ctors) { + for (let sourceCtor of ctors) { + if (IsBigIntTypedArray(new targetCtor()) != IsBigIntTypedArray(new sourceCtor())) { + continue; + } + test(targetCtor, sourceCtor); + } + } +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +AllBigIntMatchedCtorCombinations((targetCtor, sourceCtor) => { + const rab = CreateResizableArrayBuffer(4 * sourceCtor.BYTES_PER_ELEMENT, 8 * sourceCtor.BYTES_PER_ELEMENT); + const fixedLength = new sourceCtor(rab, 0, 4); + const fixedLengthWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new sourceCtor(rab, 0); + const lengthTrackingWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taFull = new sourceCtor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taFull, i, i + 1); + } + + // Orig. array: [1, 2, 3, 4] + // [1, 2, 3, 4] << fixedLength + // [3, 4] << fixedLengthWithOffset + // [1, 2, 3, 4, ...] << lengthTracking + // [3, 4, ...] << lengthTrackingWithOffset + + assert.compareArray(ToNumbers(new targetCtor(fixedLength)), [ + 1, + 2, + 3, + 4 + ]); + assert.compareArray(ToNumbers(new targetCtor(fixedLengthWithOffset)), [ + 3, + 4 + ]); + assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [ + 1, + 2, + 3, + 4 + ]); + assert.compareArray(ToNumbers(new targetCtor(lengthTrackingWithOffset)), [ + 3, + 4 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * sourceCtor.BYTES_PER_ELEMENT); + + // Orig. array: [1, 2, 3] + // [1, 2, 3, ...] << lengthTracking + // [3, ...] << lengthTrackingWithOffset + + assert.throws(TypeError, () => { + new targetCtor(fixedLength); + }); + assert.throws(TypeError, () => { + new targetCtor(fixedLengthWithOffset); + }); + assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [ + 1, + 2, + 3 + ]); + assert.compareArray(ToNumbers(new targetCtor(lengthTrackingWithOffset)), [3]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * sourceCtor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + new targetCtor(fixedLength); + }); + assert.throws(TypeError, () => { + new targetCtor(fixedLengthWithOffset); + }); + assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [1]); + assert.throws(TypeError, () => { + new targetCtor(lengthTrackingWithOffset); + }); + + // Shrink to zero. + rab.resize(0); + assert.throws(TypeError, () => { + new targetCtor(fixedLength); + }); + assert.throws(TypeError, () => { + new targetCtor(fixedLengthWithOffset); + }); + assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), []); + assert.throws(TypeError, () => { + new targetCtor(lengthTrackingWithOffset); + }); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * sourceCtor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taFull, i, i + 1); + } + + // Orig. array: [1, 2, 3, 4, 5, 6] + // [1, 2, 3, 4] << fixedLength + // [3, 4] << fixedLengthWithOffset + // [1, 2, 3, 4, 5, 6, ...] << lengthTracking + // [3, 4, 5, 6, ...] << lengthTrackingWithOffset + + assert.compareArray(ToNumbers(new targetCtor(fixedLength)), [ + 1, + 2, + 3, + 4 + ]); + assert.compareArray(ToNumbers(new targetCtor(fixedLengthWithOffset)), [ + 3, + 4 + ]); + assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [ + 1, + 2, + 3, + 4, + 5, + 6 + ]); + assert.compareArray(ToNumbers(new targetCtor(lengthTrackingWithOffset)), [ + 3, + 4, + 5, + 6 + ]); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-invalid-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-invalid-strict.js new file mode 100644 index 0000000000..7ef2fa1295 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-invalid-strict.js @@ -0,0 +1,74 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ConstructInvalid test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const rab = CreateResizableArrayBuffer(40, 80); +for (let ctor of ctors) { + // Length too big. + assert.throws(RangeError, () => { + new ctor(rab, 0, 40 / ctor.BYTES_PER_ELEMENT + 1); + }); + // Offset too close to the end. + assert.throws(RangeError, () => { + new ctor(rab, 40 - ctor.BYTES_PER_ELEMENT, 2); + }); + // Offset beyond end. + assert.throws(RangeError, () => { + new ctor(rab, 40, 1); + }); + if (ctor.BYTES_PER_ELEMENT > 1) { + // Offset not a multiple of the byte size. + assert.throws(RangeError, () => { + new ctor(rab, 1, 1); + }); + assert.throws(RangeError, () => { + new ctor(rab, 1); + }); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-grows-strict.js new file mode 100644 index 0000000000..d3b6cbe667 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-grows-strict.js @@ -0,0 +1,118 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from CopyWithinParameterConversionGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + const evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + WriteToTypedArray(lengthTracking, 4, 4); + WriteToTypedArray(lengthTracking, 5, 5); + return 0; + } + }; + // Orig. array: [0, 1, 2, 3] [4, 5] + // ^ ^ ^ new elements + // target start + lengthTracking.copyWithin(evil, 2); + assert.compareArray(ToNumbers(lengthTracking), [ + 2, + 3, + 2, + 3, + 4, + 5 + ]); + rab.resize(4 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + + // Orig. array: [0, 1, 2, 3] [4, 5] + // ^ ^ ^ new elements + // start target + lengthTracking.copyWithin(2, evil); + assert.compareArray(ToNumbers(lengthTracking), [ + 0, + 1, + 0, + 1, + 4, + 5 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-shrinks-strict.js new file mode 100644 index 0000000000..53aedbf7b6 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-shrinks-strict.js @@ -0,0 +1,143 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from CopyWithinParameterConversionShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + assert.throws(TypeError, () => { + fixedLength.copyWithin(evil, 0, 1); + }); + rab.resize(4 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + fixedLength.copyWithin(0, evil, 3); + }); + rab.resize(4 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + fixedLength.copyWithin(0, 1, evil); + }); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + // [0, 1, 2, 3] + // ^ + // target + // ^ + // start + const evil = { + valueOf: () => { + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + lengthTracking.copyWithin(evil, 0); + assert.compareArray(ToNumbers(lengthTracking), [ + 0, + 1, + 0 + ]); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + // [0, 1, 2, 3] + // ^ + // start + // ^ + // target + const evil = { + valueOf: () => { + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + lengthTracking.copyWithin(0, evil); + assert.compareArray(ToNumbers(lengthTracking), [ + 2, + 1, + 2 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/destructuring-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/destructuring-strict.js new file mode 100644 index 0000000000..b01425f6cc --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/destructuring-strict.js @@ -0,0 +1,262 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from Destructuring test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + let ta_write = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(ta_write, i, i); + } + { + let [a, b, c, d, e] = fixedLength; + assert.compareArray(ToNumbers([ + a, + b, + c, + d + ]), [ + 0, + 1, + 2, + 3 + ]); + assert.sameValue(e, undefined); + } + { + let [a, b, c] = fixedLengthWithOffset; + assert.compareArray(ToNumbers([ + a, + b + ]), [ + 2, + 3 + ]); + assert.sameValue(c, undefined); + } + { + let [a, b, c, d, e] = lengthTracking; + assert.compareArray(ToNumbers([ + a, + b, + c, + d + ]), [ + 0, + 1, + 2, + 3 + ]); + assert.sameValue(e, undefined); + } + { + let [a, b, c] = lengthTrackingWithOffset; + assert.compareArray(ToNumbers([ + a, + b + ]), [ + 2, + 3 + ]); + assert.sameValue(c, undefined); + } + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + let [a, b, c] = fixedLength; + }); + assert.throws(TypeError, () => { + let [a, b, c] = fixedLengthWithOffset; + }); + { + let [a, b, c, d] = lengthTracking; + assert.compareArray(ToNumbers([ + a, + b, + c + ]), [ + 0, + 1, + 2 + ]); + assert.sameValue(d, undefined); + } + { + let [a, b] = lengthTrackingWithOffset; + assert.compareArray(ToNumbers([a]), [2]); + assert.sameValue(b, undefined); + } + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + let [a, b, c] = fixedLength; + }); + assert.throws(TypeError, () => { + let [a, b, c] = fixedLengthWithOffset; + }); + assert.throws(TypeError, () => { + let [a, b, c] = lengthTrackingWithOffset; + }); + { + let [a, b] = lengthTracking; + assert.compareArray(ToNumbers([a]), [0]); + assert.sameValue(b, undefined); + } + + // Shrink to 0. + rab.resize(0); + assert.throws(TypeError, () => { + let [a, b, c] = fixedLength; + }); + assert.throws(TypeError, () => { + let [a, b, c] = fixedLengthWithOffset; + }); + assert.throws(TypeError, () => { + let [a, b, c] = lengthTrackingWithOffset; + }); + { + let [a] = lengthTracking; + assert.sameValue(a, undefined); + } + + // Grow so that all TAs are back in-bounds. The new memory is zeroed. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + { + let [a, b, c, d, e] = fixedLength; + assert.compareArray(ToNumbers([ + a, + b, + c, + d + ]), [ + 0, + 0, + 0, + 0 + ]); + assert.sameValue(e, undefined); + } + { + let [a, b, c] = fixedLengthWithOffset; + assert.compareArray(ToNumbers([ + a, + b + ]), [ + 0, + 0 + ]); + assert.sameValue(c, undefined); + } + { + let [a, b, c, d, e, f, g] = lengthTracking; + assert.compareArray(ToNumbers([ + a, + b, + c, + d, + e, + f + ]), [ + 0, + 0, + 0, + 0, + 0, + 0 + ]); + assert.sameValue(g, undefined); + } + { + let [a, b, c, d, e] = lengthTrackingWithOffset; + assert.compareArray(ToNumbers([ + a, + b, + c, + d + ]), [ + 0, + 0, + 0, + 0 + ]); + assert.sameValue(e, undefined); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..dfe2aea4f8 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-grow-mid-iteration-strict.js @@ -0,0 +1,303 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from EntriesKeysValuesGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +includes: [compareArray.js] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayEntriesHelper(ta) { + return ta.entries(); +} + +function ArrayEntriesHelper(ta) { + return Array.prototype.entries.call(ta); +} + +function TypedArrayKeysHelper(ta) { + return ta.keys(); +} + +function ArrayKeysHelper(ta) { + return Array.prototype.keys.call(ta); +} + +function TypedArrayValuesHelper(ta) { + return ta.values(); +} + +function ArrayValuesHelper(ta) { + return Array.prototype.values.call(ta); +} + +function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) { + let values = []; + let resized = false; + for (const value of ta) { + if (value instanceof Array) { + values.push([ + value[0], + Number(value[1]) + ]); + } else { + values.push(Number(value)); + } + if (!resized && values.length == resize_after) { + rab.resize(new_byte_length); + resized = true; + } + } + assert.compareArray(values.flat(), expected.flat()); + assert(resized); +} + +function EntriesKeysValuesGrowMidIteration(entriesHelper, keysHelper, valuesHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + + // Iterating with entries() (the 4 loops below). + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + // The fixed length array is not affected by resizing. + TestIterationAndResize(entriesHelper(fixedLength), [ + [ + 0, + 0 + ], + [ + 1, + 2 + ], + [ + 2, + 4 + ], + [ + 3, + 6 + ] + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + // The fixed length array is not affected by resizing. + TestIterationAndResize(entriesHelper(fixedLengthWithOffset), [ + [ + 0, + 4 + ], + [ + 1, + 6 + ] + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + TestIterationAndResize(entriesHelper(lengthTracking), [ + [ + 0, + 0 + ], + [ + 1, + 2 + ], + [ + 2, + 4 + ], + [ + 3, + 6 + ], + [ + 4, + 0 + ], + [ + 5, + 0 + ] + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + TestIterationAndResize(entriesHelper(lengthTrackingWithOffset), [ + [ + 0, + 4 + ], + [ + 1, + 6 + ], + [ + 2, + 0 + ], + [ + 3, + 0 + ] + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + + // Iterating with keys() (the 4 loops below). + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + // The fixed length array is not affected by resizing. + TestIterationAndResize(keysHelper(fixedLength), [ + 0, + 1, + 2, + 3 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + // The fixed length array is not affected by resizing. + TestIterationAndResize(keysHelper(fixedLengthWithOffset), [ + 0, + 1 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + TestIterationAndResize(keysHelper(lengthTracking), [ + 0, + 1, + 2, + 3, + 4, + 5 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + TestIterationAndResize(keysHelper(lengthTrackingWithOffset), [ + 0, + 1, + 2, + 3 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + + // Iterating with values() (the 4 loops below). + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + // The fixed length array is not affected by resizing. + TestIterationAndResize(valuesHelper(fixedLength), [ + 0, + 2, + 4, + 6 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + // The fixed length array is not affected by resizing. + TestIterationAndResize(valuesHelper(fixedLengthWithOffset), [ + 4, + 6 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + TestIterationAndResize(valuesHelper(lengthTracking), [ + 0, + 2, + 4, + 6, + 0, + 0 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + TestIterationAndResize(valuesHelper(lengthTrackingWithOffset), [ + 4, + 6, + 0, + 0 + ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT); + } +} + +EntriesKeysValuesGrowMidIteration(TypedArrayEntriesHelper, TypedArrayKeysHelper, TypedArrayValuesHelper); +EntriesKeysValuesGrowMidIteration(ArrayEntriesHelper, ArrayKeysHelper, ArrayValuesHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..08cfb4dd2c --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-shrink-mid-iteration-strict.js @@ -0,0 +1,248 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from EntriesKeysValuesShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +includes: [compareArray.js] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayEntriesHelper(ta) { + return ta.entries(); +} + +function ArrayEntriesHelper(ta) { + return Array.prototype.entries.call(ta); +} + +function TypedArrayKeysHelper(ta) { + return ta.keys(); +} + +function ArrayKeysHelper(ta) { + return Array.prototype.keys.call(ta); +} + +function TypedArrayValuesHelper(ta) { + return ta.values(); +} + +function ArrayValuesHelper(ta) { + return Array.prototype.values.call(ta); +} + +function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) { + let values = []; + let resized = false; + for (const value of ta) { + if (value instanceof Array) { + values.push([ + value[0], + Number(value[1]) + ]); + } else { + values.push(Number(value)); + } + if (!resized && values.length == resize_after) { + rab.resize(new_byte_length); + resized = true; + } + } + assert.compareArray(values.flat(), expected.flat()); + assert(resized); +} + +function EntriesKeysValuesShrinkMidIteration(entriesHelper, keysHelper, valuesHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + + // Iterating with entries() (the 4 loops below). + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + + // The fixed length array goes out of bounds when the RAB is resized. + assert.throws(TypeError, () => { + TestIterationAndResize(entriesHelper(fixedLength), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + }); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + + // The fixed length array goes out of bounds when the RAB is resized. + assert.throws(TypeError, () => { + TestIterationAndResize(entriesHelper(fixedLengthWithOffset), null, rab, 1, 3 * ctor.BYTES_PER_ELEMENT); + }); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + TestIterationAndResize(entriesHelper(lengthTracking), [ + [ + 0, + 0 + ], + [ + 1, + 2 + ], + [ + 2, + 4 + ] + ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + TestIterationAndResize(entriesHelper(lengthTrackingWithOffset), [ + [ + 0, + 4 + ], + [ + 1, + 6 + ] + ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + } + + // Iterating with keys() (the 4 loops below). + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + + // The fixed length array goes out of bounds when the RAB is resized. + assert.throws(TypeError, () => { + TestIterationAndResize(keysHelper(fixedLength), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + }); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + + // The fixed length array goes out of bounds when the RAB is resized. + assert.throws(TypeError, () => { + TestIterationAndResize(keysHelper(fixedLengthWithOffset), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + }); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + TestIterationAndResize(keysHelper(lengthTracking), [ + 0, + 1, + 2 + ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + TestIterationAndResize(keysHelper(lengthTrackingWithOffset), [ + 0, + 1 + ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + } + + // Iterating with values() (the 4 loops below). + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + + // The fixed length array goes out of bounds when the RAB is resized. + assert.throws(TypeError, () => { + TestIterationAndResize(valuesHelper(fixedLength), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + }); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + assert.throws(TypeError, () => { + TestIterationAndResize(valuesHelper(fixedLengthWithOffset), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + }); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + TestIterationAndResize(valuesHelper(lengthTracking), [ + 0, + 2, + 4 + ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + } + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // The fixed length array goes out of bounds when the RAB is resized. + TestIterationAndResize(valuesHelper(lengthTrackingWithOffset), [ + 4, + 6 + ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT); + } +} + +EntriesKeysValuesShrinkMidIteration(TypedArrayEntriesHelper, TypedArrayKeysHelper, TypedArrayValuesHelper); +EntriesKeysValuesShrinkMidIteration(ArrayEntriesHelper, ArrayKeysHelper, ArrayValuesHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-strict.js new file mode 100644 index 0000000000..13d4cb1f89 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-strict.js @@ -0,0 +1,509 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from EntriesKeysValues test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayEntriesHelper(ta) { + return ta.entries(); +} + +function ArrayEntriesHelper(ta) { + return Array.prototype.entries.call(ta); +} + +function ValuesFromTypedArrayEntries(ta) { + let result = []; + let expectedKey = 0; + for (let [key, value] of ta.entries()) { + assert.sameValue(key, expectedKey); + ++expectedKey; + result.push(Number(value)); + } + return result; +} + +function ValuesFromArrayEntries(ta) { + let result = []; + let expectedKey = 0; + for (let [key, value] of Array.prototype.entries.call(ta)) { + assert.sameValue(key, expectedKey); + ++expectedKey; + result.push(Number(value)); + } + return result; +} + +function TypedArrayKeysHelper(ta) { + return ta.keys(); +} + +function ArrayKeysHelper(ta) { + return Array.prototype.keys.call(ta); +} + +function TypedArrayValuesHelper(ta) { + return ta.values(); +} + +function ArrayValuesHelper(ta) { + return Array.prototype.values.call(ta); +} + +function ValuesFromTypedArrayValues(ta) { + let result = []; + for (let value of ta.values()) { + result.push(Number(value)); + } + return result; +} + +function ValuesFromArrayValues(ta) { + const result = []; + for (let value of Array.prototype.values.call(ta)) { + result.push(Number(value)); + } + return result; +} + +function EntriesKeysValues(entriesHelper, keysHelper, valuesHelper, valuesFromEntries, valuesFromValues, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + assert.compareArray(valuesFromEntries(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(valuesFromValues(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Array.from(keysHelper(fixedLength)), [ + 0, + 1, + 2, + 3 + ]); + assert.compareArray(valuesFromEntries(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(valuesFromValues(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(Array.from(keysHelper(fixedLengthWithOffset)), [ + 0, + 1 + ]); + assert.compareArray(valuesFromEntries(lengthTracking), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(valuesFromValues(lengthTracking), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Array.from(keysHelper(lengthTracking)), [ + 0, + 1, + 2, + 3 + ]); + assert.compareArray(valuesFromEntries(lengthTrackingWithOffset), [ + 4, + 6 + ]); + assert.compareArray(valuesFromValues(lengthTrackingWithOffset), [ + 4, + 6 + ]); + assert.compareArray(Array.from(keysHelper(lengthTrackingWithOffset)), [ + 0, + 1 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + // TypedArray.prototype.{entries, keys, values} throw right away when + // called. Array.prototype.{entries, keys, values} don't throw, but when + // we try to iterate the returned ArrayIterator, that throws. + if (oobThrows) { + assert.throws(TypeError, () => { + entriesHelper(fixedLength); + }); + assert.throws(TypeError, () => { + valuesHelper(fixedLength); + }); + assert.throws(TypeError, () => { + keysHelper(fixedLength); + }); + assert.throws(TypeError, () => { + entriesHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + valuesHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + keysHelper(fixedLengthWithOffset); + }); + } else { + entriesHelper(fixedLength); + valuesHelper(fixedLength); + keysHelper(fixedLength); + entriesHelper(fixedLengthWithOffset); + valuesHelper(fixedLengthWithOffset); + keysHelper(fixedLengthWithOffset); + } + assert.throws(TypeError, () => { + Array.from(entriesHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(entriesHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(fixedLengthWithOffset)); + }); + assert.compareArray(valuesFromEntries(lengthTracking), [ + 0, + 2, + 4 + ]); + assert.compareArray(valuesFromValues(lengthTracking), [ + 0, + 2, + 4 + ]); + assert.compareArray(Array.from(keysHelper(lengthTracking)), [ + 0, + 1, + 2 + ]); + assert.compareArray(valuesFromEntries(lengthTrackingWithOffset), [4]); + assert.compareArray(valuesFromValues(lengthTrackingWithOffset), [4]); + assert.compareArray(Array.from(keysHelper(lengthTrackingWithOffset)), [0]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + entriesHelper(fixedLength); + }); + assert.throws(TypeError, () => { + valuesHelper(fixedLength); + }); + assert.throws(TypeError, () => { + keysHelper(fixedLength); + }); + assert.throws(TypeError, () => { + entriesHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + valuesHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + keysHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + entriesHelper(lengthTrackingWithOffset); + }); + assert.throws(TypeError, () => { + valuesHelper(lengthTrackingWithOffset); + }); + assert.throws(TypeError, () => { + keysHelper(lengthTrackingWithOffset); + }); + } else { + entriesHelper(fixedLength); + valuesHelper(fixedLength); + keysHelper(fixedLength); + entriesHelper(fixedLengthWithOffset); + valuesHelper(fixedLengthWithOffset); + keysHelper(fixedLengthWithOffset); + entriesHelper(lengthTrackingWithOffset); + valuesHelper(lengthTrackingWithOffset); + keysHelper(lengthTrackingWithOffset); + } + assert.throws(TypeError, () => { + Array.from(entriesHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(entriesHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(entriesHelper(lengthTrackingWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(lengthTrackingWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(lengthTrackingWithOffset)); + }); + assert.compareArray(valuesFromEntries(lengthTracking), [0]); + assert.compareArray(valuesFromValues(lengthTracking), [0]); + assert.compareArray(Array.from(keysHelper(lengthTracking)), [0]); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + entriesHelper(fixedLength); + }); + assert.throws(TypeError, () => { + valuesHelper(fixedLength); + }); + assert.throws(TypeError, () => { + keysHelper(fixedLength); + }); + assert.throws(TypeError, () => { + entriesHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + valuesHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + keysHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + entriesHelper(lengthTrackingWithOffset); + }); + assert.throws(TypeError, () => { + valuesHelper(lengthTrackingWithOffset); + }); + assert.throws(TypeError, () => { + keysHelper(lengthTrackingWithOffset); + }); + } else { + entriesHelper(fixedLength); + valuesHelper(fixedLength); + keysHelper(fixedLength); + entriesHelper(fixedLengthWithOffset); + valuesHelper(fixedLengthWithOffset); + keysHelper(fixedLengthWithOffset); + entriesHelper(lengthTrackingWithOffset); + valuesHelper(lengthTrackingWithOffset); + keysHelper(lengthTrackingWithOffset); + } + assert.throws(TypeError, () => { + Array.from(entriesHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(fixedLength)); + }); + assert.throws(TypeError, () => { + Array.from(entriesHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(fixedLengthWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(entriesHelper(lengthTrackingWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(valuesHelper(lengthTrackingWithOffset)); + }); + assert.throws(TypeError, () => { + Array.from(keysHelper(lengthTrackingWithOffset)); + }); + assert.compareArray(valuesFromEntries(lengthTracking), []); + assert.compareArray(valuesFromValues(lengthTracking), []); + assert.compareArray(Array.from(keysHelper(lengthTracking)), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert.compareArray(valuesFromEntries(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(valuesFromValues(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Array.from(keysHelper(fixedLength)), [ + 0, + 1, + 2, + 3 + ]); + assert.compareArray(valuesFromEntries(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(valuesFromValues(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(Array.from(keysHelper(fixedLengthWithOffset)), [ + 0, + 1 + ]); + assert.compareArray(valuesFromEntries(lengthTracking), [ + 0, + 2, + 4, + 6, + 8, + 10 + ]); + assert.compareArray(valuesFromValues(lengthTracking), [ + 0, + 2, + 4, + 6, + 8, + 10 + ]); + assert.compareArray(Array.from(keysHelper(lengthTracking)), [ + 0, + 1, + 2, + 3, + 4, + 5 + ]); + assert.compareArray(valuesFromEntries(lengthTrackingWithOffset), [ + 4, + 6, + 8, + 10 + ]); + assert.compareArray(valuesFromValues(lengthTrackingWithOffset), [ + 4, + 6, + 8, + 10 + ]); + assert.compareArray(Array.from(keysHelper(lengthTrackingWithOffset)), [ + 0, + 1, + 2, + 3 + ]); + } +} + +EntriesKeysValues(TypedArrayEntriesHelper, TypedArrayKeysHelper, TypedArrayValuesHelper, ValuesFromTypedArrayEntries, ValuesFromTypedArrayValues, true); +EntriesKeysValues(ArrayEntriesHelper, ArrayKeysHelper, ArrayValuesHelper, ValuesFromArrayEntries, ValuesFromArrayValues, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/enumerate-elements-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/enumerate-elements-strict.js new file mode 100644 index 0000000000..12da5209c5 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/enumerate-elements-strict.js @@ -0,0 +1,59 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from EnumerateElements test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +let rab = CreateResizableArrayBuffer(100, 200); +for (let ctor of ctors) { + const ta = new ctor(rab, 0, 3); + let keys = ''; + for (const key in ta) { + keys += key; + } + assert.sameValue(keys, '012'); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/every-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..5594faf89c --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-grow-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from EveryGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArrayEveryHelper = (ta, ...rest) => { + return ta.every(...rest); +}; + +const ArrayEveryHelper = (ta, ...rest) => { + return Array.prototype.every.call(ta, ...rest); +}; + +function EveryGrowMidIteration(everyHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return true; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(fixedLength, CollectValuesAndResize)); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(fixedLengthWithOffset, CollectValuesAndResize)); + assert.compareArray(values, [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(lengthTracking, CollectValuesAndResize)); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(lengthTrackingWithOffset, CollectValuesAndResize)); + assert.compareArray(values, [ + 4, + 6 + ]); + } +} + +EveryGrowMidIteration(TypedArrayEveryHelper); +EveryGrowMidIteration(ArrayEveryHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/every-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..acddb44be2 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-shrink-mid-iteration-strict.js @@ -0,0 +1,176 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from EveryShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArrayEveryHelper = (ta, ...rest) => { + return ta.every(...rest); +}; + +const ArrayEveryHelper = (ta, ...rest) => { + return Array.prototype.every.call(ta, ...rest); +}; + +function EveryShrinkMidIteration(everyHelper, hasUndefined) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return true; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(fixedLength, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 0, + 2, + undefined, + undefined + ]); + } else { + assert.compareArray(values, [ + 0, + 2 + ]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(fixedLengthWithOffset, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 4, + undefined + ]); + } else { + assert.compareArray(values, [4]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(lengthTracking, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 0, + 2, + 4, + undefined + ]); + } else { + assert.compareArray(values, [ + 0, + 2, + 4 + ]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(everyHelper(lengthTrackingWithOffset, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 4, + undefined + ]); + } else { + assert.compareArray(values, [4]); + } + } +} + +EveryShrinkMidIteration(TypedArrayEveryHelper, true); +EveryShrinkMidIteration(ArrayEveryHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/every-some-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-some-strict.js new file mode 100644 index 0000000000..0454e1583e --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-some-strict.js @@ -0,0 +1,256 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from EverySome test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArrayEveryHelper = (ta, ...rest) => { + return ta.every(...rest); +}; + +const ArrayEveryHelper = (ta, ...rest) => { + return Array.prototype.every.call(ta, ...rest); +}; + +const TypedArraySomeHelper = (ta, ...rest) => { + return ta.some(...rest); +}; + +const ArraySomeHelper = (ta, ...rest) => { + return Array.prototype.some.call(ta, ...rest); +}; + +function EverySome(everyHelper, someHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + function div3(n) { + return Number(n) % 3 == 0; + } + function even(n) { + return Number(n) % 2 == 0; + } + function over10(n) { + return Number(n) > 10; + } + assert(!everyHelper(fixedLength, div3)); + assert(everyHelper(fixedLength, even)); + assert(someHelper(fixedLength, div3)); + assert(!someHelper(fixedLength, over10)); + assert(!everyHelper(fixedLengthWithOffset, div3)); + assert(everyHelper(fixedLengthWithOffset, even)); + assert(someHelper(fixedLengthWithOffset, div3)); + assert(!someHelper(fixedLengthWithOffset, over10)); + assert(!everyHelper(lengthTracking, div3)); + assert(everyHelper(lengthTracking, even)); + assert(someHelper(lengthTracking, div3)); + assert(!someHelper(lengthTracking, over10)); + assert(!everyHelper(lengthTrackingWithOffset, div3)); + assert(everyHelper(lengthTrackingWithOffset, even)); + assert(someHelper(lengthTrackingWithOffset, div3)); + assert(!someHelper(lengthTrackingWithOffset, over10)); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + everyHelper(fixedLength, div3); + }); + assert.throws(TypeError, () => { + someHelper(fixedLength, div3); + }); + assert.throws(TypeError, () => { + everyHelper(fixedLengthWithOffset, div3); + }); + assert.throws(TypeError, () => { + someHelper(fixedLengthWithOffset, div3); + }); + } else { + assert(everyHelper(fixedLength, div3)); + assert(!someHelper(fixedLength, div3)); + assert(everyHelper(fixedLengthWithOffset, div3)); + assert(!someHelper(fixedLengthWithOffset, div3)); + } + assert(!everyHelper(lengthTracking, div3)); + assert(everyHelper(lengthTracking, even)); + assert(someHelper(lengthTracking, div3)); + assert(!someHelper(lengthTracking, over10)); + assert(!everyHelper(lengthTrackingWithOffset, div3)); + assert(everyHelper(lengthTrackingWithOffset, even)); + assert(!someHelper(lengthTrackingWithOffset, div3)); + assert(!someHelper(lengthTrackingWithOffset, over10)); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + everyHelper(fixedLength, div3); + }); + assert.throws(TypeError, () => { + someHelper(fixedLength, div3); + }); + assert.throws(TypeError, () => { + everyHelper(fixedLengthWithOffset, div3); + }); + assert.throws(TypeError, () => { + someHelper(fixedLengthWithOffset, div3); + }); + assert.throws(TypeError, () => { + everyHelper(lengthTrackingWithOffset, div3); + }); + assert.throws(TypeError, () => { + someHelper(lengthTrackingWithOffset, div3); + }); + } else { + assert(everyHelper(fixedLength, div3)); + assert(!someHelper(fixedLength, div3)); + assert(everyHelper(fixedLengthWithOffset, div3)); + assert(!someHelper(fixedLengthWithOffset, div3)); + assert(everyHelper(lengthTrackingWithOffset, div3)); + assert(!someHelper(lengthTrackingWithOffset, div3)); + } + assert(everyHelper(lengthTracking, div3)); + assert(everyHelper(lengthTracking, even)); + assert(someHelper(lengthTracking, div3)); + assert(!someHelper(lengthTracking, over10)); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + everyHelper(fixedLength, div3); + }); + assert.throws(TypeError, () => { + someHelper(fixedLength, div3); + }); + assert.throws(TypeError, () => { + everyHelper(fixedLengthWithOffset, div3); + }); + assert.throws(TypeError, () => { + someHelper(fixedLengthWithOffset, div3); + }); + assert.throws(TypeError, () => { + everyHelper(lengthTrackingWithOffset, div3); + }); + assert.throws(TypeError, () => { + someHelper(lengthTrackingWithOffset, div3); + }); + } else { + assert(everyHelper(fixedLength, div3)); + assert(!someHelper(fixedLength, div3)); + assert(everyHelper(fixedLengthWithOffset, div3)); + assert(!someHelper(fixedLengthWithOffset, div3)); + assert(everyHelper(lengthTrackingWithOffset, div3)); + assert(!someHelper(lengthTrackingWithOffset, div3)); + } + assert(everyHelper(lengthTracking, div3)); + assert(everyHelper(lengthTracking, even)); + assert(!someHelper(lengthTracking, div3)); + assert(!someHelper(lengthTracking, over10)); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert(!everyHelper(fixedLength, div3)); + assert(everyHelper(fixedLength, even)); + assert(someHelper(fixedLength, div3)); + assert(!someHelper(fixedLength, over10)); + assert(!everyHelper(fixedLengthWithOffset, div3)); + assert(everyHelper(fixedLengthWithOffset, even)); + assert(someHelper(fixedLengthWithOffset, div3)); + assert(!someHelper(fixedLengthWithOffset, over10)); + assert(!everyHelper(lengthTracking, div3)); + assert(everyHelper(lengthTracking, even)); + assert(someHelper(lengthTracking, div3)); + assert(!someHelper(lengthTracking, over10)); + assert(!everyHelper(lengthTrackingWithOffset, div3)); + assert(everyHelper(lengthTrackingWithOffset, even)); + assert(someHelper(lengthTrackingWithOffset, div3)); + assert(!someHelper(lengthTrackingWithOffset, over10)); + } +} + +EverySome(TypedArrayEveryHelper, TypedArraySomeHelper, true); +EverySome(ArrayEveryHelper, ArraySomeHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/fill-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/fill-parameter-conversion-resizes-strict.js new file mode 100644 index 0000000000..3544bc20ed --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/fill-parameter-conversion-resizes-strict.js @@ -0,0 +1,97 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FillParameterConversionResizes test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function TypedArrayFillHelper(ta, n, start, end) { + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + ta.fill(BigInt(n), start, end); + } else { + ta.fill(n, start, end); + } +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 3; + } + }; + assert.throws(TypeError, () => { + TypedArrayFillHelper(fixedLength, evil, 1, 2); + }); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 1; + } + }; + assert.throws(TypeError, () => { + TypedArrayFillHelper(fixedLength, 3, evil, 2); + }); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + assert.throws(TypeError, () => { + TypedArrayFillHelper(fixedLength, 3, 1, evil); + }); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..87ad8440a8 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-grow-mid-iteration-strict.js @@ -0,0 +1,168 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FilterGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArrayFilterHelper = (ta, ...rest) => { + return ta.filter(...rest); +}; + +const ArrayFilterHelper = (ta, ...rest) => { + return Array.prototype.filter.call(ta, ...rest); +}; + +function FilterGrowMidIteration(filterHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(filterHelper(fixedLength, CollectValuesAndResize)), []); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(filterHelper(fixedLengthWithOffset, CollectValuesAndResize)), []); + assert.compareArray(values, [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(filterHelper(lengthTracking, CollectValuesAndResize)), []); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, CollectValuesAndResize)), []); + assert.compareArray(values, [ + 4, + 6 + ]); + } +} + +FilterGrowMidIteration(TypedArrayFilterHelper); +FilterGrowMidIteration(ArrayFilterHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..d08f29ca4f --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-shrink-mid-iteration-strict.js @@ -0,0 +1,155 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FilterShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [4, 6] << fixedLengthWithOffset +// [0, 2, 4, 6, ...] << lengthTracking +// [4, 6, ...] << lengthTrackingWithOffset +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} +let values; +let rab; +let resizeAfter; +let resizeTo; +function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(fixedLength.filter(CollectValuesAndResize)), []); + assert.compareArray(values, [ + 0, + 2, + undefined, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(fixedLengthWithOffset.filter(CollectValuesAndResize)), []); + assert.compareArray(values, [ + 4, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(lengthTracking.filter(CollectValuesAndResize)), []); + assert.compareArray(values, [ + 0, + 2, + 4, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ToNumbers(lengthTrackingWithOffset.filter(CollectValuesAndResize)), []); + assert.compareArray(values, [ + 4, + undefined + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-strict.js new file mode 100644 index 0000000000..c2b5f183dc --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-strict.js @@ -0,0 +1,209 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from Filter test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArrayFilterHelper = (ta, ...rest) => { + return ta.filter(...rest); +}; + +const ArrayFilterHelper = (ta, ...rest) => { + return Array.prototype.filter.call(ta, ...rest); +}; + +function Filter(filterHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + // Orig. array: [0, 1, 2, 3] + // [0, 1, 2, 3] << fixedLength + // [2, 3] << fixedLengthWithOffset + // [0, 1, 2, 3, ...] << lengthTracking + // [2, 3, ...] << lengthTrackingWithOffset + + function isEven(n) { + return n != undefined && Number(n) % 2 == 0; + } + assert.compareArray(ToNumbers(filterHelper(fixedLength, isEven)), [ + 0, + 2 + ]); + assert.compareArray(ToNumbers(filterHelper(fixedLengthWithOffset, isEven)), [2]); + assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [ + 0, + 2 + ]); + assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, isEven)), [2]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 1, 2] + // [0, 1, 2, ...] << lengthTracking + // [2, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + filterHelper(fixedLength, isEven); + }); + assert.throws(TypeError, () => { + filterHelper(fixedLengthWithOffset, isEven); + }); + } else { + assert.compareArray(filterHelper(fixedLength, isEven), []); + assert.compareArray(filterHelper(fixedLengthWithOffset, isEven), []); + } + assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [ + 0, + 2 + ]); + assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, isEven)), [2]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + filterHelper(fixedLength, isEven); + }); + assert.throws(TypeError, () => { + filterHelper(fixedLengthWithOffset, isEven); + }); + assert.throws(TypeError, () => { + filterHelper(lengthTrackingWithOffset, isEven); + }); + } else { + assert.compareArray(filterHelper(fixedLength, isEven), []); + assert.compareArray(filterHelper(fixedLengthWithOffset, isEven), []); + assert.compareArray(filterHelper(lengthTrackingWithOffset, isEven), []); + } + assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [0]); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + filterHelper(fixedLength, isEven); + }); + assert.throws(TypeError, () => { + filterHelper(fixedLengthWithOffset, isEven); + }); + assert.throws(TypeError, () => { + filterHelper(lengthTrackingWithOffset, isEven); + }); + } else { + assert.compareArray(filterHelper(fixedLength, isEven), []); + assert.compareArray(filterHelper(fixedLengthWithOffset, isEven), []); + assert.compareArray(filterHelper(lengthTrackingWithOffset, isEven), []); + } + assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + // Orig. array: [0, 1, 2, 3, 4, 5] + // [0, 1, 2, 3] << fixedLength + // [2, 3] << fixedLengthWithOffset + // [0, 1, 2, 3, 4, 5, ...] << lengthTracking + // [2, 3, 4, 5, ...] << lengthTrackingWithOffset + + assert.compareArray(ToNumbers(filterHelper(fixedLength, isEven)), [ + 0, + 2 + ]); + assert.compareArray(ToNumbers(filterHelper(fixedLengthWithOffset, isEven)), [2]); + assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [ + 0, + 2, + 4 + ]); + assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, isEven)), [ + 2, + 4 + ]); + } +} + +Filter(TypedArrayFilterHelper, true); +Filter(ArrayFilterHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-find-index-find-last-find-last-index-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-find-index-find-last-find-last-index-strict.js new file mode 100644 index 0000000000..6d66d11e29 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-find-index-find-last-find-last-index-strict.js @@ -0,0 +1,332 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindFindIndexFindLastFindLastIndex test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindHelper(ta, p) { + return ta.find(p); +} + +function ArrayFindHelper(ta, p) { + return Array.prototype.find.call(ta, p); +} + +function TypedArrayFindIndexHelper(ta, p) { + return ta.findIndex(p); +} + +function ArrayFindIndexHelper(ta, p) { + return Array.prototype.findIndex.call(ta, p); +} + +function TypedArrayFindLastHelper(ta, p) { + return ta.findLast(p); +} + +function ArrayFindLastHelper(ta, p) { + return Array.prototype.findLast.call(ta, p); +} + +function TypedArrayFindLastIndexHelper(ta, p) { + return ta.findLastIndex(p); +} + +function ArrayFindLastIndexHelper(ta, p) { + return Array.prototype.findLastIndex.call(ta, p); +} + +function FindFindIndexFindLastFindLastIndex(findHelper, findIndexHelper, findLastHelper, findLastIndexHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + function isTwoOrFour(n) { + return n == 2 || n == 4; + } + assert.sameValue(Number(findHelper(fixedLength, isTwoOrFour)), 2); + assert.sameValue(Number(findHelper(fixedLengthWithOffset, isTwoOrFour)), 4); + assert.sameValue(Number(findHelper(lengthTracking, isTwoOrFour)), 2); + assert.sameValue(Number(findHelper(lengthTrackingWithOffset, isTwoOrFour)), 4); + assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), 1); + assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), 0); + assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), 1); + assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0); + assert.sameValue(Number(findLastHelper(fixedLength, isTwoOrFour)), 4); + assert.sameValue(Number(findLastHelper(fixedLengthWithOffset, isTwoOrFour)), 4); + assert.sameValue(Number(findLastHelper(lengthTracking, isTwoOrFour)), 4); + assert.sameValue(Number(findLastHelper(lengthTrackingWithOffset, isTwoOrFour)), 4); + assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), 2); + assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), 0); + assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), 2); + assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + findHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour); + }); + } else { + assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + } + assert.sameValue(Number(findHelper(lengthTracking, isTwoOrFour)), 2); + assert.sameValue(Number(findHelper(lengthTrackingWithOffset, isTwoOrFour)), 4); + assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), 1); + assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0); + assert.sameValue(Number(findLastHelper(lengthTracking, isTwoOrFour)), 4); + assert.sameValue(Number(findLastHelper(lengthTrackingWithOffset, isTwoOrFour)), 4); + assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), 2); + assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + findHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + } else { + assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + assert.sameValue(findHelper(lengthTrackingWithOffset, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1); + assert.sameValue(findLastHelper(lengthTrackingWithOffset, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1); + } + assert.sameValue(findHelper(lengthTracking, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), -1); + assert.sameValue(findLastHelper(lengthTracking, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), -1); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + findHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(fixedLength, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findIndexHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + assert.throws(TypeError, () => { + findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour); + }); + } else { + assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + assert.sameValue(findHelper(lengthTrackingWithOffset, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1); + assert.sameValue(findLastHelper(lengthTrackingWithOffset, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1); + } + assert.sameValue(findHelper(lengthTracking, isTwoOrFour), undefined); + assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), -1); + assert.sameValue(findLastHelper(lengthTracking, isTwoOrFour), undefined); + assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), -1); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 0); + } + WriteToTypedArray(taWrite, 4, 2); + WriteToTypedArray(taWrite, 5, 4); + + // Orig. array: [0, 0, 0, 0, 2, 4] + // [0, 0, 0, 0] << fixedLength + // [0, 0] << fixedLengthWithOffset + // [0, 0, 0, 0, 2, 4, ...] << lengthTracking + // [0, 0, 2, 4, ...] << lengthTrackingWithOffset + + assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(Number(findHelper(lengthTracking, isTwoOrFour)), 2); + assert.sameValue(Number(findHelper(lengthTrackingWithOffset, isTwoOrFour)), 2); + assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), 4); + assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 2); + assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined); + assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined); + assert.sameValue(Number(findLastHelper(lengthTracking, isTwoOrFour)), 4); + assert.sameValue(Number(findLastHelper(lengthTrackingWithOffset, isTwoOrFour)), 4); + assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1); + assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1); + assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), 5); + assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 3); + } +} + +FindFindIndexFindLastFindLastIndex(TypedArrayFindHelper, TypedArrayFindIndexHelper, TypedArrayFindLastHelper, TypedArrayFindLastIndexHelper, true); +FindFindIndexFindLastFindLastIndex(ArrayFindHelper, ArrayFindIndexHelper, ArrayFindLastHelper, ArrayFindLastIndexHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..ec36398067 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-grow-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindHelper(ta, p) { + return ta.find(p); +} + +function ArrayFindHelper(ta, p) { + return Array.prototype.find.call(ta, p); +} + +function FindGrowMidIteration(findHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(fixedLength, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(lengthTracking, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 4, + 6 + ]); + } +} + +FindGrowMidIteration(TypedArrayFindHelper); +FindGrowMidIteration(ArrayFindHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..d11967b305 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-grow-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindIndexGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindIndexHelper(ta, p) { + return ta.findIndex(p); +} + +function ArrayFindIndexHelper(ta, p) { + return Array.prototype.findIndex.call(ta, p); +} + +function FindIndexGrowMidIteration(findIndexHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(fixedLength, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(lengthTracking, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 4, + 6 + ]); + } +} + +FindIndexGrowMidIteration(TypedArrayFindIndexHelper); +FindIndexGrowMidIteration(ArrayFindIndexHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..3331a9aef3 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-shrink-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindIndexShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindIndexHelper(ta, p) { + return ta.findIndex(p); +} + +function ArrayFindIndexHelper(ta, p) { + return Array.prototype.findIndex.call(ta, p); +} + +function FindIndexShrinkMidIteration(findIndexHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(fixedLength, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 0, + 2, + undefined, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 4, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(lengthTracking, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 0, + 2, + 4, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 4, + undefined + ]); + } +} + +FindIndexShrinkMidIteration(TypedArrayFindIndexHelper); +FindIndexShrinkMidIteration(ArrayFindIndexHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..8e1ee4edc5 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-grow-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindLastGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindLastHelper(ta, p) { + return ta.findLast(p); +} + +function ArrayFindLastHelper(ta, p) { + return Array.prototype.findLast.call(ta, p); +} + +function FindLastGrowMidIteration(findLastHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(fixedLength, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + 4 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(lengthTracking, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + 4 + ]); + } +} + +FindLastGrowMidIteration(TypedArrayFindLastHelper); +FindLastGrowMidIteration(ArrayFindLastHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..639d3d5767 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-grow-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindLastIndexGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindLastIndexHelper(ta, p) { + return ta.findLastIndex(p); +} + +function ArrayFindLastIndexHelper(ta, p) { + return Array.prototype.findLastIndex.call(ta, p); +} + +function FindLastIndexGrowMidIteration(findLastIndexHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(fixedLength, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + 4 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(lengthTracking, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + 4 + ]); + } +} + +FindLastIndexGrowMidIteration(TypedArrayFindLastIndexHelper); +FindLastIndexGrowMidIteration(ArrayFindLastIndexHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..e489835d75 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-shrink-mid-iteration-strict.js @@ -0,0 +1,167 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindLastIndexShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindLastIndexHelper(ta, p) { + return ta.findLastIndex(p); +} + +function ArrayFindLastIndexHelper(ta, p) { + return Array.prototype.findLastIndex.call(ta, p); +} + +function FindLastIndexShrinkMidIteration(findLastIndexHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(fixedLength, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + 4, + undefined, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(lengthTracking, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 1; + resizeTo = 2 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(lengthTracking, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + undefined, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1); + assert.compareArray(values, [ + 6, + 4 + ]); + } +} + +FindLastIndexShrinkMidIteration(TypedArrayFindLastIndexHelper); +FindLastIndexShrinkMidIteration(ArrayFindLastIndexHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..c4fd72479c --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-shrink-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindLastShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindLastHelper(ta, p) { + return ta.findLast(p); +} + +function ArrayFindLastHelper(ta, p) { + return Array.prototype.findLast.call(ta, p); +} + +function FindLastShrinkMidIteration(findLastHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(fixedLength, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + 4, + undefined, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(lengthTracking, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findLastHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 6, + 4 + ]); + } +} + +FindLastShrinkMidIteration(TypedArrayFindLastHelper); +FindLastShrinkMidIteration(ArrayFindLastHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..de3096e7e5 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-shrink-mid-iteration-strict.js @@ -0,0 +1,153 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FindShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayFindHelper(ta, p) { + return ta.find(p); +} + +function ArrayFindHelper(ta, p) { + return Array.prototype.find.call(ta, p); +} + +function FindShrinkMidIteration(findHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(fixedLength, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 0, + 2, + undefined, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 4, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(lengthTracking, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 0, + 2, + 4, + undefined + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.sameValue(findHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined); + assert.compareArray(values, [ + 4, + undefined + ]); + } +} + +FindShrinkMidIteration(TypedArrayFindHelper); +FindShrinkMidIteration(ArrayFindHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..97e2409e3c --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-grow-mid-iteration-strict.js @@ -0,0 +1,277 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ForEachReduceReduceRightGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArrayForEachHelper = (ta, ...rest) => { + return ta.forEach(...rest); +}; + +const ArrayForEachHelper = (ta, ...rest) => { + return Array.prototype.forEach.call(ta, ...rest); +}; + +const TypedArrayReduceHelper = (ta, ...rest) => { + return ta.reduce(...rest); +}; + +const ArrayReduceHelper = (ta, ...rest) => { + return Array.prototype.reduce.call(ta, ...rest); +}; + +const TypedArrayReduceRightHelper = (ta, ...rest) => { + return ta.reduceRight(...rest); +}; + +const ArrayReduceRightHelper = (ta, ...rest) => { + return Array.prototype.reduceRight.call(ta, ...rest); +}; + +function ForEachReduceReduceRightGrowMidIteration(forEachHelper, reduceHelper, reduceRightHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return true; + } + function ForEachHelper(array) { + values = []; + forEachHelper(array, CollectValuesAndResize); + return values; + } + function ReduceHelper(array) { + values = []; + reduceHelper(array, (acc, n) => { + CollectValuesAndResize(n); + }, 'initial value'); + return values; + } + function ReduceRightHelper(array) { + values = []; + reduceRightHelper(array, (acc, n) => { + CollectValuesAndResize(n); + }, 'initial value'); + return values; + } + + // Test for forEach. + + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(fixedLengthWithOffset), [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(lengthTracking), [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(lengthTrackingWithOffset), [ + 4, + 6 + ]); + } + + // Test for reduce. + + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(fixedLengthWithOffset), [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(lengthTracking), [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(lengthTrackingWithOffset), [ + 4, + 6 + ]); + } + + // Test for reduceRight. + + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceRightHelper(fixedLength), [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceRightHelper(fixedLengthWithOffset), [ + 6, + 4 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceRightHelper(lengthTracking), [ + 6, + 4, + 2, + 0 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceRightHelper(lengthTrackingWithOffset), [ + 6, + 4 + ]); + } +} + +ForEachReduceReduceRightGrowMidIteration(TypedArrayForEachHelper, TypedArrayReduceHelper, TypedArrayReduceRightHelper); +ForEachReduceReduceRightGrowMidIteration(ArrayForEachHelper, ArrayReduceHelper, ArrayReduceRightHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..bce655454e --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-shrink-mid-iteration-strict.js @@ -0,0 +1,262 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ForEachReduceReduceRightShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [4, 6] << fixedLengthWithOffset +// [0, 2, 4, 6, ...] << lengthTracking +// [4, 6, ...] << lengthTrackingWithOffset +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} +let values; +let rab; +let resizeAfter; +let resizeTo; +function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return true; +} +function ForEachHelper(array) { + values = []; + array.forEach(CollectValuesAndResize); + return values; +} +function ReduceHelper(array) { + values = []; + array.reduce((acc, n) => { + CollectValuesAndResize(n); + }, 'initial value'); + return values; +} +function ReduceRightHelper(array) { + values = []; + array.reduceRight((acc, n) => { + CollectValuesAndResize(n); + }, 'initial value'); + return values; +} + +// Test for forEach. + +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(fixedLength), [ + 0, + 2, + undefined, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(fixedLengthWithOffset), [ + 4, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(lengthTracking), [ + 0, + 2, + 4, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ForEachHelper(lengthTrackingWithOffset), [ + 4, + undefined + ]); +} + +// Tests for reduce. + +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(fixedLength), [ + 0, + 2, + undefined, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(fixedLengthWithOffset), [ + 4, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(lengthTracking), [ + 0, + 2, + 4, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceHelper(lengthTrackingWithOffset), [ + 4, + undefined + ]); +} + +// Tests for reduceRight. + +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceRightHelper(fixedLength), [ + 6, + 4, + undefined, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceRightHelper(fixedLengthWithOffset), [ + 6, + undefined + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + // Unaffected by the shrinking, since we've already iterated past the point. + assert.compareArray(ReduceRightHelper(lengthTracking), [ + 6, + 4, + 2, + 0 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 1; + resizeTo = 2 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(ReduceRightHelper(lengthTracking), [ + 6, + undefined, + 2, + 0 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + // Unaffected by the shrinking, since we've already iterated past the point. + assert.compareArray(ReduceRightHelper(lengthTrackingWithOffset), [ + 6, + 4 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-strict.js new file mode 100644 index 0000000000..39b5b6318d --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-strict.js @@ -0,0 +1,260 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ForEachReduceReduceRight test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArrayForEachHelper = (ta, ...rest) => { + return ta.forEach(...rest); +}; + +const ArrayForEachHelper = (ta, ...rest) => { + return Array.prototype.forEach.call(ta, ...rest); +}; + +const TypedArrayReduceHelper = (ta, ...rest) => { + return ta.reduce(...rest); +}; + +const ArrayReduceHelper = (ta, ...rest) => { + return Array.prototype.reduce.call(ta, ...rest); +}; + +const TypedArrayReduceRightHelper = (ta, ...rest) => { + return ta.reduceRight(...rest); +}; + +const ArrayReduceRightHelper = (ta, ...rest) => { + return Array.prototype.reduceRight.call(ta, ...rest); +}; + +function ForEachReduceReduceRight(forEachHelper, reduceHelper, reduceRightHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + function Helper(array) { + const forEachValues = []; + const reduceValues = []; + const reduceRightValues = []; + forEachHelper(array, n => { + forEachValues.push(n); + }); + reduceHelper(array, (acc, n) => { + reduceValues.push(n); + }, 'initial value'); + reduceRightHelper(array, (acc, n) => { + reduceRightValues.push(n); + }, 'initial value'); + assert.compareArray(forEachValues, reduceValues); + reduceRightValues.reverse(); + assert.compareArray(reduceRightValues, reduceValues); + return ToNumbers(forEachValues); + } + assert.compareArray(Helper(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Helper(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Helper(lengthTrackingWithOffset), [ + 4, + 6 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + Helper(fixedLength); + }); + assert.throws(TypeError, () => { + Helper(fixedLengthWithOffset); + }); + } else { + assert.compareArray(Helper(fixedLength), []); + assert.compareArray(Helper(fixedLengthWithOffset), []); + } + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4 + ]); + assert.compareArray(Helper(lengthTrackingWithOffset), [4]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + Helper(fixedLength); + }); + assert.throws(TypeError, () => { + Helper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + Helper(lengthTrackingWithOffset); + }); + } else { + assert.compareArray(Helper(fixedLength), []); + assert.compareArray(Helper(fixedLengthWithOffset), []); + } + assert.compareArray(Helper(lengthTracking), [0]); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + Helper(fixedLength); + }); + assert.throws(TypeError, () => { + Helper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + Helper(lengthTrackingWithOffset); + }); + } else { + assert.compareArray(Helper(fixedLength), []); + assert.compareArray(Helper(fixedLengthWithOffset), []); + assert.compareArray(Helper(lengthTrackingWithOffset), []); + } + assert.compareArray(Helper(lengthTracking), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert.compareArray(Helper(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Helper(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4, + 6, + 8, + 10 + ]); + assert.compareArray(Helper(lengthTrackingWithOffset), [ + 4, + 6, + 8, + 10 + ]); + } +} + +ForEachReduceReduceRight(TypedArrayForEachHelper, TypedArrayReduceHelper, TypedArrayReduceRightHelper, true); +ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, ArrayReduceRightHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/function-apply-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/function-apply-strict.js new file mode 100644 index 0000000000..1da01f4826 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/function-apply-strict.js @@ -0,0 +1,160 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from FunctionApply test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + function func(...args) { + return [...args]; + } + assert.compareArray(ToNumbers(func.apply(null, fixedLength)), [ + 0, + 1, + 2, + 3 + ]); + assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), [ + 2, + 3 + ]); + assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [ + 0, + 1, + 2, + 3 + ]); + assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), [ + 2, + 3 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + assert.compareArray(ToNumbers(func.apply(null, fixedLength)), []); + assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), []); + assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [ + 0, + 1, + 2 + ]); + assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), [2]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.compareArray(ToNumbers(func.apply(null, fixedLength)), []); + assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), []); + assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [0]); + assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), []); + + // Shrink to zero. + rab.resize(0); + assert.compareArray(ToNumbers(func.apply(null, fixedLength)), []); + assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), []); + assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), []); + assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), []); + + // Grow so that all TAs are back in-bounds. New memory is zeroed. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + assert.compareArray(ToNumbers(func.apply(null, fixedLength)), [ + 0, + 0, + 0, + 0 + ]); + assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), [ + 0, + 0 + ]); + assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [ + 0, + 0, + 0, + 0, + 0, + 0 + ]); + assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), [ + 0, + 0, + 0, + 0 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-parameter-conversion-resizes-strict.js new file mode 100644 index 0000000000..19fd438ed8 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-parameter-conversion-resizes-strict.js @@ -0,0 +1,147 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IncludesParameterConversionResizes test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer, Array.prototype.includes] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayIncludesHelper(array, n, fromIndex) { + if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) { + return array.includes(BigInt(n), fromIndex); + } + return array.includes(n, fromIndex); +} + +function ArrayIncludesHelper(array, n, fromIndex) { + if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) { + return Array.prototype.includes.call(array, BigInt(n), fromIndex); + } + return Array.prototype.includes.call(array, n, fromIndex); +} + +function IncludesParameterConversionResizes(helper) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert(!helper(fixedLength, undefined)); + // The TA is OOB so it includes only "undefined". + assert(helper(fixedLength, undefined, evil)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert(helper(fixedLength, 0)); + // The TA is OOB so it includes only "undefined". + assert(!helper(fixedLength, 0, evil)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert(!helper(lengthTracking, undefined)); + // "includes" iterates until the original length and sees "undefined"s. + assert(helper(lengthTracking, undefined, evil)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, 1); + } + let evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert(!helper(lengthTracking, 0)); + // The TA grew but we only look at the data until the original length. + assert(!helper(lengthTracking, 0, evil)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + WriteToTypedArray(lengthTracking, 0, 1); + let evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return -4; + } + }; + assert(helper(lengthTracking, 1, -4)); + // The TA grew but the start index conversion is done based on the original + // length. + assert(helper(lengthTracking, 1, evil)); + } +} + +IncludesParameterConversionResizes(TypedArrayIncludesHelper); +IncludesParameterConversionResizes(ArrayIncludesHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-special-values-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-special-values-strict.js new file mode 100644 index 0000000000..1581e2369e --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-special-values-strict.js @@ -0,0 +1,39 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IncludesSpecialValues test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyFloat32Array extends Float32Array { +} + +const floatCtors = [ + Float32Array, + Float64Array, + MyFloat32Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +for (let ctor of floatCtors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + lengthTracking[0] = -Infinity; + lengthTracking[1] = Infinity; + lengthTracking[2] = NaN; + assert(lengthTracking.includes(-Infinity)); + assert(lengthTracking.includes(Infinity)); + assert(lengthTracking.includes(NaN)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-strict.js new file mode 100644 index 0000000000..cc14e2d783 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-strict.js @@ -0,0 +1,212 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from Includes test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer, Array.prototype.includes] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayIncludesHelper(array, n, fromIndex) { + if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) { + return array.includes(BigInt(n), fromIndex); + } + return array.includes(n, fromIndex); +} + +function ArrayIncludesHelper(array, n, fromIndex) { + if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) { + return Array.prototype.includes.call(array, BigInt(n), fromIndex); + } + return Array.prototype.includes.call(array, n, fromIndex); +} + +function Includes(helper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + assert(helper(fixedLength, 2)); + assert(!helper(fixedLength, undefined)); + assert(helper(fixedLength, 2, 1)); + assert(!helper(fixedLength, 2, 2)); + assert(helper(fixedLength, 2, -3)); + assert(!helper(fixedLength, 2, -2)); + assert(!helper(fixedLengthWithOffset, 2)); + assert(helper(fixedLengthWithOffset, 4)); + assert(!helper(fixedLengthWithOffset, undefined)); + assert(helper(fixedLengthWithOffset, 4, 0)); + assert(!helper(fixedLengthWithOffset, 4, 1)); + assert(helper(fixedLengthWithOffset, 4, -2)); + assert(!helper(fixedLengthWithOffset, 4, -1)); + assert(helper(lengthTracking, 2)); + assert(!helper(lengthTracking, undefined)); + assert(helper(lengthTracking, 2, 1)); + assert(!helper(lengthTracking, 2, 2)); + assert(helper(lengthTracking, 2, -3)); + assert(!helper(lengthTracking, 2, -2)); + assert(!helper(lengthTrackingWithOffset, 2)); + assert(helper(lengthTrackingWithOffset, 4)); + assert(!helper(lengthTrackingWithOffset, undefined)); + assert(helper(lengthTrackingWithOffset, 4, 0)); + assert(!helper(lengthTrackingWithOffset, 4, 1)); + assert(helper(lengthTrackingWithOffset, 4, -2)); + assert(!helper(lengthTrackingWithOffset, 4, -1)); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + helper(fixedLength, 2); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 2); + }); + } else { + assert(!helper(fixedLength, 2)); + assert(!helper(fixedLengthWithOffset, 2)); + } + assert(helper(lengthTracking, 2)); + assert(!helper(lengthTracking, undefined)); + assert(!helper(lengthTrackingWithOffset, 2)); + assert(helper(lengthTrackingWithOffset, 4)); + assert(!helper(lengthTrackingWithOffset, undefined)); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + helper(fixedLength, 2); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 2); + }); + assert.throws(TypeError, () => { + helper(lengthTrackingWithOffset, 2); + }); + } else { + assert(!helper(fixedLength, 2)); + assert(!helper(fixedLengthWithOffset, 2)); + assert(!helper(lengthTrackingWithOffset, 2)); + } + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + helper(fixedLength, 2); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 2); + }); + assert.throws(TypeError, () => { + helper(lengthTrackingWithOffset, 2); + }); + } else { + assert(!helper(fixedLength, 2)); + assert(!helper(fixedLengthWithOffset, 2)); + assert(!helper(lengthTrackingWithOffset, 2)); + } + assert(!helper(lengthTracking, 2)); + assert(!helper(lengthTracking, undefined)); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert(helper(fixedLength, 2)); + assert(!helper(fixedLength, undefined)); + assert(!helper(fixedLength, 8)); + assert(!helper(fixedLengthWithOffset, 2)); + assert(helper(fixedLengthWithOffset, 4)); + assert(!helper(fixedLengthWithOffset, undefined)); + assert(!helper(fixedLengthWithOffset, 8)); + assert(helper(lengthTracking, 2)); + assert(!helper(lengthTracking, undefined)); + assert(helper(lengthTracking, 8)); + assert(!helper(lengthTrackingWithOffset, 2)); + assert(helper(lengthTrackingWithOffset, 4)); + assert(!helper(lengthTrackingWithOffset, undefined)); + assert(helper(lengthTrackingWithOffset, 8)); + } +} + +Includes(TypedArrayIncludesHelper, true); +Includes(ArrayIncludesHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-special-values-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-special-values-strict.js new file mode 100644 index 0000000000..35b408cced --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-special-values-strict.js @@ -0,0 +1,46 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IndexOfLastIndexOfSpecialValues test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyFloat32Array extends Float32Array { +} + +const floatCtors = [ + Float32Array, + Float64Array, + MyFloat32Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +for (let ctor of floatCtors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + lengthTracking[0] = -Infinity; + lengthTracking[1] = -Infinity; + lengthTracking[2] = Infinity; + lengthTracking[3] = Infinity; + lengthTracking[4] = NaN; + lengthTracking[5] = NaN; + assert.sameValue(lengthTracking.indexOf(-Infinity), 0); + assert.sameValue(lengthTracking.lastIndexOf(-Infinity), 1); + assert.sameValue(lengthTracking.indexOf(Infinity), 2); + assert.sameValue(lengthTracking.lastIndexOf(Infinity), 3); + // NaN is never found. + assert.sameValue(lengthTracking.indexOf(NaN), -1); + assert.sameValue(lengthTracking.lastIndexOf(NaN), -1); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-strict.js new file mode 100644 index 0000000000..6269766782 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-strict.js @@ -0,0 +1,329 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IndexOfLastIndexOf test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return ta.indexOf(BigInt(n)); + } + return ta.indexOf(BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return ta.indexOf(n); + } + return ta.indexOf(n, fromIndex); +} + +function ArrayIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return Array.prototype.indexOf.call(ta, BigInt(n)); + } + return Array.prototype.indexOf.call(ta, BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return Array.prototype.indexOf.call(ta, n); + } + return Array.prototype.indexOf.call(ta, n, fromIndex); +} + +function TypedArrayLastIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return ta.lastIndexOf(BigInt(n)); + } + return ta.lastIndexOf(BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return ta.lastIndexOf(n); + } + return ta.lastIndexOf(n, fromIndex); +} + +function ArrayLastIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return Array.prototype.lastIndexOf.call(ta, BigInt(n)); + } + return Array.prototype.lastIndexOf.call(ta, BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return Array.prototype.lastIndexOf.call(ta, n); + } + return Array.prototype.lastIndexOf.call(ta, n, fromIndex); +} + +function IndexOfLastIndexOf(indexOfHelper, lastIndexOfHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, Math.floor(i / 2)); + } + + // Orig. array: [0, 0, 1, 1] + // [0, 0, 1, 1] << fixedLength + // [1, 1] << fixedLengthWithOffset + // [0, 0, 1, 1, ...] << lengthTracking + // [1, 1, ...] << lengthTrackingWithOffset + + assert.sameValue(indexOfHelper(fixedLength, 0), 0); + assert.sameValue(indexOfHelper(fixedLength, 0, 1), 1); + assert.sameValue(indexOfHelper(fixedLength, 0, 2), -1); + assert.sameValue(indexOfHelper(fixedLength, 0, -2), -1); + assert.sameValue(indexOfHelper(fixedLength, 0, -3), 1); + assert.sameValue(indexOfHelper(fixedLength, 1, 1), 2); + assert.sameValue(indexOfHelper(fixedLength, 1, -3), 2); + assert.sameValue(indexOfHelper(fixedLength, 1, -2), 2); + assert.sameValue(indexOfHelper(fixedLength, undefined), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, 0), 1); + assert.sameValue(lastIndexOfHelper(fixedLength, 0, 1), 1); + assert.sameValue(lastIndexOfHelper(fixedLength, 0, 2), 1); + assert.sameValue(lastIndexOfHelper(fixedLength, 0, -2), 1); + assert.sameValue(lastIndexOfHelper(fixedLength, 0, -3), 1); + assert.sameValue(lastIndexOfHelper(fixedLength, 1, 1), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, 1, -2), 2); + assert.sameValue(lastIndexOfHelper(fixedLength, 1, -3), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, undefined), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1), 0); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1, -2), 0); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1, -1), 1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, undefined), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1), 1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1, -2), 0); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1, -1), 1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, undefined), -1); + assert.sameValue(indexOfHelper(lengthTracking, 0), 0); + assert.sameValue(indexOfHelper(lengthTracking, 0, 2), -1); + assert.sameValue(indexOfHelper(lengthTracking, 1, -3), 2); + assert.sameValue(indexOfHelper(lengthTracking, undefined), -1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 0), 1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 0, 2), 1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 0, -3), 1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 1, 1), -1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 1, 2), 2); + assert.sameValue(lastIndexOfHelper(lengthTracking, 1, -3), -1); + assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1), 0); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1, 1), 1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1, -2), 0); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, undefined), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1), 1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1, 1), 1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1, -2), 0); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1, -1), 1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, undefined), -1); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 0, 1] + // [0, 0, 1, ...] << lengthTracking + // [1, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + indexOfHelper(fixedLength, 1); + }); + assert.throws(TypeError, () => { + indexOfHelper(fixedLengthWithOffset, 1); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(fixedLength, 1); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(fixedLengthWithOffset, 1); + }); + } else { + assert.sameValue(indexOfHelper(fixedLength, 1), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, 1), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1), -1); + } + assert.sameValue(indexOfHelper(lengthTracking, 1), 2); + assert.sameValue(indexOfHelper(lengthTracking, undefined), -1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 0), 1); + assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1), 0); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, undefined), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1), 0); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, undefined), -1); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + indexOfHelper(fixedLength, 0); + }); + assert.throws(TypeError, () => { + indexOfHelper(fixedLengthWithOffset, 0); + }); + assert.throws(TypeError, () => { + indexOfHelper(lengthTrackingWithOffset, 0); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(fixedLength, 0); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(fixedLengthWithOffset, 0); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(lengthTrackingWithOffset, 0); + }); + } else { + assert.sameValue(indexOfHelper(fixedLength, 0), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, 0), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1); + } + assert.sameValue(indexOfHelper(lengthTracking, 0), 0); + assert.sameValue(lastIndexOfHelper(lengthTracking, 0), 0); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + indexOfHelper(fixedLength, 0); + }); + assert.throws(TypeError, () => { + indexOfHelper(fixedLengthWithOffset, 0); + }); + assert.throws(TypeError, () => { + indexOfHelper(lengthTrackingWithOffset, 0); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(fixedLength, 0); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(fixedLengthWithOffset, 0); + }); + assert.throws(TypeError, () => { + lastIndexOfHelper(lengthTrackingWithOffset, 0); + }); + } else { + assert.sameValue(indexOfHelper(fixedLength, 0), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, 0), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1); + } + assert.sameValue(indexOfHelper(lengthTracking, 0), -1); + assert.sameValue(indexOfHelper(lengthTracking, undefined), -1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 0), -1); + assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, Math.floor(i / 2)); + } + + // Orig. array: [0, 0, 1, 1, 2, 2] + // [0, 0, 1, 1] << fixedLength + // [1, 1] << fixedLengthWithOffset + // [0, 0, 1, 1, 2, 2, ...] << lengthTracking + // [1, 1, 2, 2, ...] << lengthTrackingWithOffset + + assert.sameValue(indexOfHelper(fixedLength, 1), 2); + assert.sameValue(indexOfHelper(fixedLength, 2), -1); + assert.sameValue(indexOfHelper(fixedLength, undefined), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, 1), 3); + assert.sameValue(lastIndexOfHelper(fixedLength, 2), -1); + assert.sameValue(lastIndexOfHelper(fixedLength, undefined), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1), 0); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, 2), -1); + assert.sameValue(indexOfHelper(fixedLengthWithOffset, undefined), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1), 1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 2), -1); + assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, undefined), -1); + assert.sameValue(indexOfHelper(lengthTracking, 1), 2); + assert.sameValue(indexOfHelper(lengthTracking, 2), 4); + assert.sameValue(indexOfHelper(lengthTracking, undefined), -1); + assert.sameValue(lastIndexOfHelper(lengthTracking, 1), 3); + assert.sameValue(lastIndexOfHelper(lengthTracking, 2), 5); + assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1), 0); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 2), 2); + assert.sameValue(indexOfHelper(lengthTrackingWithOffset, undefined), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1), 1); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 2), 3); + assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, undefined), -1); + } +} + +IndexOfLastIndexOf(TypedArrayIndexOfHelper, TypedArrayLastIndexOfHelper, true); +IndexOfLastIndexOf(ArrayIndexOfHelper, ArrayLastIndexOfHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-grows-strict.js new file mode 100644 index 0000000000..676e4ebe64 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-grows-strict.js @@ -0,0 +1,123 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IndexOfParameterConversionGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return ta.indexOf(BigInt(n)); + } + return ta.indexOf(BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return ta.indexOf(n); + } + return ta.indexOf(n, fromIndex); +} + +function ArrayIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return Array.prototype.indexOf.call(ta, BigInt(n)); + } + return Array.prototype.indexOf.call(ta, BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return Array.prototype.indexOf.call(ta, n); + } + return Array.prototype.indexOf.call(ta, n, fromIndex); +} + +function IndexOfParameterConversionGrows(indexOfHelper) { + // Growing + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, 1); + } + let evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.sameValue(indexOfHelper(lengthTracking, 0), -1); + // The TA grew but we only look at the data until the original length. + assert.sameValue(indexOfHelper(lengthTracking, 0, evil), -1); + } + + // Growing + length-tracking TA, index conversion. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + WriteToTypedArray(lengthTracking, 0, 1); + let evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return -4; + } + }; + assert.sameValue(indexOfHelper(lengthTracking, 1, -4), 0); + // The TA grew but the start index conversion is done based on the original + // length. + assert.sameValue(indexOfHelper(lengthTracking, 1, evil), 0); + } +} + +IndexOfParameterConversionGrows(TypedArrayIndexOfHelper); +IndexOfParameterConversionGrows(ArrayIndexOfHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-shrinks-strict.js new file mode 100644 index 0000000000..fb2d146f08 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-shrinks-strict.js @@ -0,0 +1,134 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IndexOfParameterConversionShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return ta.indexOf(BigInt(n)); + } + return ta.indexOf(BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return ta.indexOf(n); + } + return ta.indexOf(n, fromIndex); +} + +function ArrayIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return Array.prototype.indexOf.call(ta, BigInt(n)); + } + return Array.prototype.indexOf.call(ta, BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return Array.prototype.indexOf.call(ta, n); + } + return Array.prototype.indexOf.call(ta, n, fromIndex); +} + +function IndexOfParameterConversionShrinks(indexOfHelper, lastIndexOfHelper) { + // Shrinking + fixed-length TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.sameValue(indexOfHelper(fixedLength, 0), 0); + // The TA is OOB so indexOf returns -1. + assert.sameValue(indexOfHelper(fixedLength, 0, evil), -1); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.sameValue(indexOfHelper(fixedLength, 0), 0); + // The TA is OOB so indexOf returns -1, also for undefined). + assert.sameValue(indexOfHelper(fixedLength, undefined, evil), -1); + } + + // Shrinking + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.sameValue(indexOfHelper(lengthTracking, 2), 2); + // 2 no longer found. + assert.sameValue(indexOfHelper(lengthTracking, 2, evil), -1); + } +} + +IndexOfParameterConversionShrinks(TypedArrayIndexOfHelper); +IndexOfParameterConversionShrinks(ArrayIndexOfHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-just-before-iteration-would-end-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-just-before-iteration-would-end-strict.js new file mode 100644 index 0000000000..f32949dc76 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-just-before-iteration-would-end-strict.js @@ -0,0 +1,126 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IterateTypedArrayAndGrowJustBeforeIterationWouldEnd test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +includes: [compareArray.js] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function CreateRab(buffer_byte_length, ctor) { + const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length); + let ta_write = new ctor(rab); + for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) { + WriteToTypedArray(ta_write, i, i % 128); + } + return rab; +} + +function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) { + let values = []; + let resized = false; + for (const value of ta) { + if (value instanceof Array) { + values.push([ + value[0], + Number(value[1]) + ]); + } else { + values.push(Number(value)); + } + if (!resized && values.length == resize_after) { + rab.resize(new_byte_length); + resized = true; + } + } + assert.compareArray(values, expected); + assert(resized); +} + +const no_elements = 10; +const offset = 2; + +// We need to recreate the RAB between all TA tests, since we grow it. +for (let ctor of ctors) { + const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT; + const byte_offset = offset * ctor.BYTES_PER_ELEMENT; + + // Create various different styles of TypedArrays with the RAB as the + // backing store and iterate them. + + let rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta = new ctor(rab); + { + let expected = []; + for (let i = 0; i < no_elements; ++i) { + expected.push(i % 128); + } + // After resizing, the new memory contains zeros. + for (let i = 0; i < no_elements; ++i) { + expected.push(0); + } + TestIterationAndResize(length_tracking_ta, expected, rab, no_elements, buffer_byte_length * 2); + } + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta_with_offset = new ctor(rab, byte_offset); + { + let expected = []; + for (let i = offset; i < no_elements; ++i) { + expected.push(i % 128); + } + for (let i = 0; i < no_elements; ++i) { + expected.push(0); + } + TestIterationAndResize(length_tracking_ta_with_offset, expected, rab, no_elements - offset, buffer_byte_length * 2); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..9c281202fa --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-mid-iteration-strict.js @@ -0,0 +1,139 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IterateTypedArrayAndGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +includes: [compareArray.js] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function CreateRab(buffer_byte_length, ctor) { + const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length); + let ta_write = new ctor(rab); + for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) { + WriteToTypedArray(ta_write, i, i % 128); + } + return rab; +} + +function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) { + let values = []; + let resized = false; + for (const value of ta) { + if (value instanceof Array) { + values.push([ + value[0], + Number(value[1]) + ]); + } else { + values.push(Number(value)); + } + if (!resized && values.length == resize_after) { + rab.resize(new_byte_length); + resized = true; + } + } + assert.compareArray(values, expected); + assert(resized); +} + +const no_elements = 10; +const offset = 2; +for (let ctor of ctors) { + const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT; + const byte_offset = offset * ctor.BYTES_PER_ELEMENT; + + // Create various different styles of TypedArrays with the RAB as the + // backing store and iterate them. + + // Fixed-length TAs aren't affected by resizing. + let rab = CreateRab(buffer_byte_length, ctor); + const ta = new ctor(rab, 0, 3); + TestIterationAndResize(ta, [ + 0, + 1, + 2 + ], rab, 2, buffer_byte_length * 2); + rab = CreateRab(buffer_byte_length, ctor); + const ta_with_offset = new ctor(rab, byte_offset, 3); + TestIterationAndResize(ta_with_offset, [ + 2, + 3, + 4 + ], rab, 2, buffer_byte_length * 2); + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta = new ctor(rab); + { + let expected = []; + for (let i = 0; i < no_elements; ++i) { + expected.push(i % 128); + } + for (let i = 0; i < no_elements; ++i) { + // After resizing, the new memory contains zeros. + expected.push(0); + } + TestIterationAndResize(length_tracking_ta, expected, rab, 2, buffer_byte_length * 2); + } + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta_with_offset = new ctor(rab, byte_offset); + { + let expected = []; + for (let i = offset; i < no_elements; ++i) { + expected.push(i % 128); + } + for (let i = 0; i < no_elements; ++i) { + expected.push(0); + } + TestIterationAndResize(length_tracking_ta_with_offset, expected, rab, 2, buffer_byte_length * 2); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..2227212b6d --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-mid-iteration-strict.js @@ -0,0 +1,154 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IterateTypedArrayAndShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +includes: [compareArray.js] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function CreateRab(buffer_byte_length, ctor) { + const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length); + let ta_write = new ctor(rab); + for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) { + WriteToTypedArray(ta_write, i, i % 128); + } + return rab; +} + +function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) { + let values = []; + let resized = false; + for (const value of ta) { + if (value instanceof Array) { + values.push([ + value[0], + Number(value[1]) + ]); + } else { + values.push(Number(value)); + } + if (!resized && values.length == resize_after) { + rab.resize(new_byte_length); + resized = true; + } + } + assert.compareArray(values, expected); + assert(resized); +} + +const no_elements = 10; +const offset = 2; +for (let ctor of ctors) { + const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT; + const byte_offset = offset * ctor.BYTES_PER_ELEMENT; + + // Create various different styles of TypedArrays with the RAB as the + // backing store and iterate them. + + // Fixed-length TAs aren't affected by shrinking if they stay in-bounds. + // They appear detached after shrinking out of bounds. + let rab = CreateRab(buffer_byte_length, ctor); + const ta1 = new ctor(rab, 0, 3); + TestIterationAndResize(ta1, [ + 0, + 1, + 2 + ], rab, 2, buffer_byte_length / 2); + rab = CreateRab(buffer_byte_length, ctor); + const ta2 = new ctor(rab, 0, 3); + assert.throws(TypeError, () => { + TestIterationAndResize(ta2, null, rab, 2, 1); + }); + rab = CreateRab(buffer_byte_length, ctor); + const ta_with_offset1 = new ctor(rab, byte_offset, 3); + TestIterationAndResize(ta_with_offset1, [ + 2, + 3, + 4 + ], rab, 2, buffer_byte_length / 2); + rab = CreateRab(buffer_byte_length, ctor); + const ta_with_offset2 = new ctor(rab, byte_offset, 3); + assert.throws(TypeError, () => { + TestIterationAndResize(ta_with_offset2, null, rab, 2, 0); + }); + + // Length-tracking TA with offset 0 doesn't throw, but its length gracefully + // reduces too. + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta = new ctor(rab); + TestIterationAndResize(length_tracking_ta, [ + 0, + 1, + 2, + 3, + 4 + ], rab, 2, buffer_byte_length / 2); + + // Length-tracking TA appears detached when the buffer is resized beyond the + // offset. + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta_with_offset = new ctor(rab, byte_offset); + assert.throws(TypeError, () => { + TestIterationAndResize(length_tracking_ta_with_offset, null, rab, 2, byte_offset / 2); + }); + + // Length-tracking TA reduces its length gracefully when the buffer is + // resized to barely cover the offset. + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta_with_offset2 = new ctor(rab, byte_offset); + TestIterationAndResize(length_tracking_ta_with_offset2, [ + 2, + 3 + ], rab, 2, byte_offset); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-to-zero-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-to-zero-mid-iteration-strict.js new file mode 100644 index 0000000000..1a6c154188 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-to-zero-mid-iteration-strict.js @@ -0,0 +1,126 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IterateTypedArrayAndShrinkToZeroMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +includes: [compareArray.js] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function CreateRab(buffer_byte_length, ctor) { + const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length); + let ta_write = new ctor(rab); + for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) { + WriteToTypedArray(ta_write, i, i % 128); + } + return rab; +} + +function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) { + let values = []; + let resized = false; + for (const value of ta) { + if (value instanceof Array) { + values.push([ + value[0], + Number(value[1]) + ]); + } else { + values.push(Number(value)); + } + if (!resized && values.length == resize_after) { + rab.resize(new_byte_length); + resized = true; + } + } + assert.compareArray(values, expected); + assert(resized); +} + +const no_elements = 10; +const offset = 2; +for (let ctor of ctors) { + const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT; + const byte_offset = offset * ctor.BYTES_PER_ELEMENT; + + // Create various different styles of TypedArrays with the RAB as the + // backing store and iterate them. + + // Fixed-length TAs appear detached after shrinking out of bounds. + let rab = CreateRab(buffer_byte_length, ctor); + const ta = new ctor(rab, 0, 3); + assert.throws(TypeError, () => { + TestIterationAndResize(ta, null, rab, 2, 0); + }); + rab = CreateRab(buffer_byte_length, ctor); + const ta_with_offset = new ctor(rab, byte_offset, 3); + assert.throws(TypeError, () => { + TestIterationAndResize(ta_with_offset, null, rab, 2, 0); + }); + + // Length-tracking TA with offset 0 doesn't throw, but its length gracefully + // goes to zero too. + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta = new ctor(rab); + TestIterationAndResize(length_tracking_ta, [ + 0, + 1 + ], rab, 2, 0); + + // Length-tracking TA which is resized beyond the offset appars detached. + rab = CreateRab(buffer_byte_length, ctor); + const length_tracking_ta_with_offset = new ctor(rab, byte_offset); + assert.throws(TypeError, () => { + TestIterationAndResize(length_tracking_ta_with_offset, null, rab, 2, 0); + }); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-strict.js new file mode 100644 index 0000000000..c485044559 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-strict.js @@ -0,0 +1,118 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from IterateTypedArray test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +includes: [compareArray.js] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const no_elements = 10; +const offset = 2; +function TestIteration(ta, expected) { + let values = []; + for (const value of ta) { + values.push(Number(value)); + } + assert.compareArray(values, expected); +} +for (let ctor of ctors) { + const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT; + // We can use the same RAB for all the TAs below, since we won't modify it + // after writing the initial values. + const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length); + const byte_offset = offset * ctor.BYTES_PER_ELEMENT; + + // Write some data into the array. + let ta_write = new ctor(rab); + for (let i = 0; i < no_elements; ++i) { + WriteToTypedArray(ta_write, i, i % 128); + } + + // Create various different styles of TypedArrays with the RAB as the + // backing store and iterate them. + const ta = new ctor(rab, 0, 3); + TestIteration(ta, [ + 0, + 1, + 2 + ]); + const empty_ta = new ctor(rab, 0, 0); + TestIteration(empty_ta, []); + const ta_with_offset = new ctor(rab, byte_offset, 3); + TestIteration(ta_with_offset, [ + 2, + 3, + 4 + ]); + const empty_ta_with_offset = new ctor(rab, byte_offset, 0); + TestIteration(empty_ta_with_offset, []); + const length_tracking_ta = new ctor(rab); + { + let expected = []; + for (let i = 0; i < no_elements; ++i) { + expected.push(i % 128); + } + TestIteration(length_tracking_ta, expected); + } + const length_tracking_ta_with_offset = new ctor(rab, byte_offset); + { + let expected = []; + for (let i = offset; i < no_elements; ++i) { + expected.push(i % 128); + } + TestIteration(length_tracking_ta_with_offset, expected); + } + const empty_length_tracking_ta_with_offset = new ctor(rab, buffer_byte_length); + TestIteration(empty_length_tracking_ta_with_offset, []); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-grows-strict.js new file mode 100644 index 0000000000..2fcffb813b --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-grows-strict.js @@ -0,0 +1,89 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from JoinParameterConversionGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const TypedArrayJoinHelper = (ta, ...rest) => { + return ta.join(...rest); +}; + +const ArrayJoinHelper = (ta, ...rest) => { + return Array.prototype.join.call(ta, ...rest); +}; + +function JoinParameterConversionGrows(joinHelper) { + // Growing + fixed-length TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + toString: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + assert.sameValue(joinHelper(fixedLength, evil), '0.0.0.0'); + } + + // Growing + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + let evil = { + toString: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + // We iterate 4 elements, since it was the starting length. + assert.sameValue(joinHelper(lengthTracking, evil), '0.0.0.0'); + } +} + +JoinParameterConversionGrows(TypedArrayJoinHelper); +JoinParameterConversionGrows(ArrayJoinHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-shrinks-strict.js new file mode 100644 index 0000000000..ab090558cc --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-shrinks-strict.js @@ -0,0 +1,93 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from JoinParameterConversionShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const TypedArrayJoinHelper = (ta, ...rest) => { + return ta.join(...rest); +}; + +const ArrayJoinHelper = (ta, ...rest) => { + return Array.prototype.join.call(ta, ...rest); +}; + +function JoinParameterConversionShrinks(joinHelper) { + // Shrinking + fixed-length TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + toString: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + // We iterate 4 elements, since it was the starting length, but the TA is + // OOB right after parameter conversion, so all elements are converted to + // the empty string. + assert.sameValue(joinHelper(fixedLength, evil), '...'); + } + + // Shrinking + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + let evil = { + toString: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return '.'; + } + }; + // We iterate 4 elements, since it was the starting length. Elements beyond + // the new length are converted to the empty string. + assert.sameValue(joinHelper(lengthTracking, evil), '0.0..'); + } +} + +JoinParameterConversionShrinks(TypedArrayJoinHelper); +JoinParameterConversionShrinks(ArrayJoinHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/join-to-locale-string-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-to-locale-string-strict.js new file mode 100644 index 0000000000..0867bb6d26 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-to-locale-string-strict.js @@ -0,0 +1,223 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from JoinToLocaleString test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArrayJoinHelper = (ta, ...rest) => { + return ta.join(...rest); +}; + +const ArrayJoinHelper = (ta, ...rest) => { + return Array.prototype.join.call(ta, ...rest); +}; + +const TypedArrayToLocaleStringHelper = (ta, ...rest) => { + return ta.toLocaleString(...rest); +}; + +const ArrayToLocaleStringHelper = (ta, ...rest) => { + return Array.prototype.toLocaleString.call(ta, ...rest); +}; + +function JoinToLocaleString(joinHelper, toLocaleStringHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + + // Write some data into the array. + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + assert.sameValue(joinHelper(fixedLength), '0,2,4,6'); + assert.sameValue(toLocaleStringHelper(fixedLength), '0,2,4,6'); + assert.sameValue(joinHelper(fixedLengthWithOffset), '4,6'); + assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), '4,6'); + assert.sameValue(joinHelper(lengthTracking), '0,2,4,6'); + assert.sameValue(toLocaleStringHelper(lengthTracking), '0,2,4,6'); + assert.sameValue(joinHelper(lengthTrackingWithOffset), '4,6'); + assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '4,6'); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + joinHelper(fixedLength); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(fixedLength); + }); + assert.throws(TypeError, () => { + joinHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(fixedLengthWithOffset); + }); + } else { + assert.sameValue(joinHelper(fixedLength), ''); + assert.sameValue(toLocaleStringHelper(fixedLength), ''); + assert.sameValue(joinHelper(fixedLengthWithOffset), ''); + assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), ''); + } + assert.sameValue(joinHelper(lengthTracking), '0,2,4'); + assert.sameValue(toLocaleStringHelper(lengthTracking), '0,2,4'); + assert.sameValue(joinHelper(lengthTrackingWithOffset), '4'); + assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '4'); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + joinHelper(fixedLength); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(fixedLength); + }); + assert.throws(TypeError, () => { + joinHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + joinHelper(lengthTrackingWithOffset); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(lengthTrackingWithOffset); + }); + } else { + assert.sameValue(joinHelper(fixedLength), ''); + assert.sameValue(toLocaleStringHelper(fixedLength), ''); + assert.sameValue(joinHelper(fixedLengthWithOffset), ''); + assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), ''); + assert.sameValue(joinHelper(lengthTrackingWithOffset), ''); + assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), ''); + } + assert.sameValue(joinHelper(lengthTracking), '0'); + assert.sameValue(toLocaleStringHelper(lengthTracking), '0'); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + joinHelper(fixedLength); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(fixedLength); + }); + assert.throws(TypeError, () => { + joinHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + joinHelper(lengthTrackingWithOffset); + }); + assert.throws(TypeError, () => { + toLocaleStringHelper(lengthTrackingWithOffset); + }); + } else { + assert.sameValue(joinHelper(fixedLength), ''); + assert.sameValue(toLocaleStringHelper(fixedLength), ''); + assert.sameValue(joinHelper(fixedLengthWithOffset), ''); + assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), ''); + assert.sameValue(joinHelper(lengthTrackingWithOffset), ''); + assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), ''); + } + assert.sameValue(joinHelper(lengthTracking), ''); + assert.sameValue(toLocaleStringHelper(lengthTracking), ''); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert.sameValue(joinHelper(fixedLength), '0,2,4,6'); + assert.sameValue(toLocaleStringHelper(fixedLength), '0,2,4,6'); + assert.sameValue(joinHelper(fixedLengthWithOffset), '4,6'); + assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), '4,6'); + assert.sameValue(joinHelper(lengthTracking), '0,2,4,6,8,10'); + assert.sameValue(toLocaleStringHelper(lengthTracking), '0,2,4,6,8,10'); + assert.sameValue(joinHelper(lengthTrackingWithOffset), '4,6,8,10'); + assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '4,6,8,10'); + } +} + +JoinToLocaleString(TypedArrayJoinHelper, TypedArrayToLocaleStringHelper, true); +JoinToLocaleString(ArrayJoinHelper, ArrayToLocaleStringHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-grows-strict.js new file mode 100644 index 0000000000..242853fab6 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-grows-strict.js @@ -0,0 +1,125 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from LastIndexOfParameterConversionGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayLastIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return ta.lastIndexOf(BigInt(n)); + } + return ta.lastIndexOf(BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return ta.lastIndexOf(n); + } + return ta.lastIndexOf(n, fromIndex); +} + +function ArrayLastIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return Array.prototype.lastIndexOf.call(ta, BigInt(n)); + } + return Array.prototype.lastIndexOf.call(ta, BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return Array.prototype.lastIndexOf.call(ta, n); + } + return Array.prototype.lastIndexOf.call(ta, n, fromIndex); +} + +function LastIndexOfParameterConversionGrows(lastIndexOfHelper) { + // Growing + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, 1); + } + let evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return -1; + } + }; + assert.sameValue(lastIndexOfHelper(lengthTracking, 0), -1); + // Because lastIndexOf iterates from the given index downwards, it's not + // possible to test that "we only look at the data until the original + // length" without also testing that the index conversion happening with the + // original length. + assert.sameValue(lastIndexOfHelper(lengthTracking, 0, evil), -1); + } + + // Growing + length-tracking TA, index conversion. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + let evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return -4; + } + }; + assert.sameValue(lastIndexOfHelper(lengthTracking, 0, -4), 0); + // The TA grew but the start index conversion is done based on the original + // length. + assert.sameValue(lastIndexOfHelper(lengthTracking, 0, evil), 0); + } +} + +LastIndexOfParameterConversionGrows(TypedArrayLastIndexOfHelper); +LastIndexOfParameterConversionGrows(ArrayLastIndexOfHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-shrinks-strict.js new file mode 100644 index 0000000000..3ebc0b2329 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-shrinks-strict.js @@ -0,0 +1,134 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from LastIndexOfParameterConversionShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function TypedArrayLastIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return ta.lastIndexOf(BigInt(n)); + } + return ta.lastIndexOf(BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return ta.lastIndexOf(n); + } + return ta.lastIndexOf(n, fromIndex); +} + +function ArrayLastIndexOfHelper(ta, n, fromIndex) { + if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) { + if (fromIndex == undefined) { + return Array.prototype.lastIndexOf.call(ta, BigInt(n)); + } + return Array.prototype.lastIndexOf.call(ta, BigInt(n), fromIndex); + } + if (fromIndex == undefined) { + return Array.prototype.lastIndexOf.call(ta, n); + } + return Array.prototype.lastIndexOf.call(ta, n, fromIndex); +} + +function LastIndexOfParameterConversionShrinks(lastIndexOfHelper) { + // Shrinking + fixed-length TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + assert.sameValue(lastIndexOfHelper(fixedLength, 0), 3); + // The TA is OOB so lastIndexOf returns -1. + assert.sameValue(lastIndexOfHelper(fixedLength, 0, evil), -1); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + assert.sameValue(lastIndexOfHelper(fixedLength, 0), 3); + // The TA is OOB so lastIndexOf returns -1, also for undefined). + assert.sameValue(lastIndexOfHelper(fixedLength, undefined, evil), -1); + } + + // Shrinking + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 2; + } + }; + assert.sameValue(lastIndexOfHelper(lengthTracking, 2), 2); + // 2 no longer found. + assert.sameValue(lastIndexOfHelper(lengthTracking, 2, evil), -1); + } +} + +LastIndexOfParameterConversionShrinks(TypedArrayLastIndexOfHelper); +LastIndexOfParameterConversionShrinks(ArrayLastIndexOfHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-1-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-1-strict.js new file mode 100644 index 0000000000..86e436ad15 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-1-strict.js @@ -0,0 +1,96 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from LengthTracking1 test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const rab = CreateResizableArrayBuffer(16, 40); +let tas = []; +for (let ctor of ctors) { + tas.push(new ctor(rab)); +} +for (let ta of tas) { + assert.sameValue(ta.length, 16 / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteLength, 16); +} +rab.resize(40); +for (let ta of tas) { + assert.sameValue(ta.length, 40 / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteLength, 40); +} +// Resize to a number which is not a multiple of all byte_lengths. +rab.resize(19); +for (let ta of tas) { + const expected_length = Math.floor(19 / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.length, expected_length); + assert.sameValue(ta.byteLength, expected_length * ta.BYTES_PER_ELEMENT); +} +rab.resize(1); +for (let ta of tas) { + if (ta.BYTES_PER_ELEMENT == 1) { + assert.sameValue(ta.length, 1); + assert.sameValue(ta.byteLength, 1); + } else { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); + } +} +rab.resize(0); +for (let ta of tas) { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); +} +rab.resize(8); +for (let ta of tas) { + assert.sameValue(ta.length, 8 / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteLength, 8); +} +rab.resize(40); +for (let ta of tas) { + assert.sameValue(ta.length, 40 / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteLength, 40); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-2-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-2-strict.js new file mode 100644 index 0000000000..cf4dbc2a7c --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-2-strict.js @@ -0,0 +1,116 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from LengthTracking2 test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +// length-tracking-1 but with offsets. + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const rab = CreateResizableArrayBuffer(16, 40); +const offset = 8; +let tas = []; +for (let ctor of ctors) { + tas.push(new ctor(rab, offset)); +} +for (let ta of tas) { + assert.sameValue(ta.length, (16 - offset) / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteLength, 16 - offset); + assert.sameValue(ta.byteOffset, offset); +} +rab.resize(40); +for (let ta of tas) { + assert.sameValue(ta.length, (40 - offset) / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteLength, 40 - offset); + assert.sameValue(ta.byteOffset, offset); +} +// Resize to a number which is not a multiple of all byte_lengths. +rab.resize(20); +for (let ta of tas) { + const expected_length = Math.floor((20 - offset) / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.length, expected_length); + assert.sameValue(ta.byteLength, expected_length * ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteOffset, offset); +} +// Resize so that all TypedArrays go out of bounds (because of the offset). +rab.resize(7); +for (let ta of tas) { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); + assert.sameValue(ta.byteOffset, 0); +} +rab.resize(0); +for (let ta of tas) { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); + assert.sameValue(ta.byteOffset, 0); +} +rab.resize(8); +for (let ta of tas) { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); + assert.sameValue(ta.byteOffset, offset); +} +// Resize so that the TypedArrays which have element size > 1 go out of bounds +// (because less than 1 full element would fit). +rab.resize(offset + 1); +for (let ta of tas) { + if (ta.BYTES_PER_ELEMENT == 1) { + assert.sameValue(ta.length, 1); + assert.sameValue(ta.byteLength, 1); + assert.sameValue(ta.byteOffset, offset); + } else { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); + assert.sameValue(ta.byteOffset, offset); + } +} +rab.resize(40); +for (let ta of tas) { + assert.sameValue(ta.length, (40 - offset) / ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteLength, 40 - offset); + assert.sameValue(ta.byteOffset, offset); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..b98d8faddd --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-grow-mid-iteration-strict.js @@ -0,0 +1,150 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from MapGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArrayMapHelper = (ta, ...rest) => { + return ta.map(...rest); +}; + +const ArrayMapHelper = (ta, ...rest) => { + return Array.prototype.map.call(ta, ...rest); +}; + +function MapGrowMidIteration(mapHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return n; + } + function Helper(array) { + values = []; + mapHelper(array, CollectValuesAndResize); + return values; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(Helper(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(Helper(fixedLengthWithOffset), [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert.compareArray(Helper(lengthTrackingWithOffset), [ + 4, + 6 + ]); + } +} + +MapGrowMidIteration(TypedArrayMapHelper); +MapGrowMidIteration(ArrayMapHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..535b3d04f9 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-shrink-mid-iteration-strict.js @@ -0,0 +1,182 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from MapShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function IsBigIntTypedArray(ta) { + return ta instanceof BigInt64Array || ta instanceof BigUint64Array; +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArrayMapHelper = (ta, ...rest) => { + return ta.map(...rest); +}; + +const ArrayMapHelper = (ta, ...rest) => { + return Array.prototype.map.call(ta, ...rest); +}; + +function MapShrinkMidIteration(mapHelper, hasUndefined) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n, ix, ta) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + // We still need to return a valid BigInt / non-BigInt, even if + // n is `undefined`. + if (IsBigIntTypedArray(ta)) { + return 0n; + } + return 0; + } + function Helper(array) { + values = []; + mapHelper(array, CollectValuesAndResize); + return values; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + if (hasUndefined) { + assert.compareArray(Helper(fixedLength), [ + 0, + 2, + undefined, + undefined + ]); + } else { + assert.compareArray(Helper(fixedLength), [ + 0, + 2 + ]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + if (hasUndefined) { + assert.compareArray(Helper(fixedLengthWithOffset), [ + 4, + undefined + ]); + } else { + assert.compareArray(Helper(fixedLengthWithOffset), [4]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + if (hasUndefined) { + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4, + undefined + ]); + } else { + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4 + ]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + if (hasUndefined) { + assert.compareArray(Helper(lengthTrackingWithOffset), [ + 4, + undefined + ]); + } else { + assert.compareArray(Helper(lengthTrackingWithOffset), [4]); + } + } +} + +MapShrinkMidIteration(TypedArrayMapHelper, true); +MapShrinkMidIteration(ArrayMapHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-grows-strict.js new file mode 100644 index 0000000000..e1780d8cc7 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-grows-strict.js @@ -0,0 +1,135 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from MapSpeciesCreateGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function IsBigIntTypedArray(ta) { + return ta instanceof BigInt64Array || ta instanceof BigUint64Array; +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +let values; +let rab; +function CollectValues(n, ix, ta) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + // We still need to return a valid BigInt / non-BigInt, even if + // n is `undefined`. + if (IsBigIntTypedArray(ta)) { + return 0n; + } + return 0; +} +function Helper(array) { + values = []; + array.map(CollectValues); + return values; +} +for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + } + } + } + ; + const fixedLength = new MyArray(rab, 0, 4); + resizeWhenConstructorCalled = true; + assert.compareArray(Helper(fixedLength), [ + 0, + 1, + 2, + 3 + ]); + assert.sameValue(rab.byteLength, 6 * ctor.BYTES_PER_ELEMENT); +} +for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + } + } + } + ; + const lengthTracking = new MyArray(rab); + resizeWhenConstructorCalled = true; + assert.compareArray(Helper(lengthTracking), [ + 0, + 1, + 2, + 3 + ]); + assert.sameValue(rab.byteLength, 6 * ctor.BYTES_PER_ELEMENT); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-shrinks-strict.js new file mode 100644 index 0000000000..46fd48b006 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-shrinks-strict.js @@ -0,0 +1,131 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from MapSpeciesCreateShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function IsBigIntTypedArray(ta) { + return ta instanceof BigInt64Array || ta instanceof BigUint64Array; +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +let values; +let rab; +function CollectValues(n, ix, ta) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (IsBigIntTypedArray(ta)) { + // We still need to return a valid BigInt / non-BigInt, even if + // n is `undefined`. + return 0n; + } + return 0; +} +function Helper(array) { + values = []; + array.map(CollectValues); + return values; +} +for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + } + } + ; + const fixedLength = new MyArray(rab, 0, 4); + resizeWhenConstructorCalled = true; + assert.compareArray(Helper(fixedLength), [ + undefined, + undefined, + undefined, + undefined + ]); + assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT); +} +for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + } + } + ; + const lengthTracking = new MyArray(rab); + resizeWhenConstructorCalled = true; + assert.compareArray(Helper(lengthTracking), [ + 0, + 1, + undefined, + undefined + ]); + assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-define-properties-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-define-properties-strict.js new file mode 100644 index 0000000000..011c23b98f --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-define-properties-strict.js @@ -0,0 +1,279 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ObjectDefinePropertyDefineProperties test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +function ObjectDefinePropertyHelper(ta, index, value) { + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + Object.defineProperty(ta, index, { value: BigInt(value) }); + } else { + Object.defineProperty(ta, index, { value: value }); + } +} + +function ObjectDefinePropertiesHelper(ta, index, value) { + const values = {}; + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + values[index] = { value: BigInt(value) }; + } else { + values[index] = { value: value }; + } + Object.defineProperties(ta, values); +} + +for (let helper of [ + ObjectDefinePropertyHelper, + ObjectDefinePropertiesHelper + ]) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taFull = new ctor(rab, 0); + + // Orig. array: [0, 0, 0, 0] + // [0, 0, 0, 0] << fixedLength + // [0, 0] << fixedLengthWithOffset + // [0, 0, 0, 0, ...] << lengthTracking + // [0, 0, ...] << lengthTrackingWithOffset + + helper(fixedLength, 0, 1); + assert.compareArray(ToNumbers(taFull), [ + 1, + 0, + 0, + 0 + ]); + helper(fixedLengthWithOffset, 0, 2); + assert.compareArray(ToNumbers(taFull), [ + 1, + 0, + 2, + 0 + ]); + helper(lengthTracking, 1, 3); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 2, + 0 + ]); + helper(lengthTrackingWithOffset, 1, 4); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 2, + 4 + ]); + assert.throws(TypeError, () => { + helper(fixedLength, 4, 8); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 2, 8); + }); + assert.throws(TypeError, () => { + helper(lengthTracking, 4, 8); + }); + assert.throws(TypeError, () => { + helper(lengthTrackingWithOffset, 2, 8); + }); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [1, 3, 2] + // [1, 3, 2, ...] << lengthTracking + // [2, ...] << lengthTrackingWithOffset + + assert.throws(TypeError, () => { + helper(fixedLength, 0, 8); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 0, 8); + }); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 2 + ]); + helper(lengthTracking, 0, 5); + assert.compareArray(ToNumbers(taFull), [ + 5, + 3, + 2 + ]); + helper(lengthTrackingWithOffset, 0, 6); + assert.compareArray(ToNumbers(taFull), [ + 5, + 3, + 6 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + helper(fixedLength, 0, 8); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 0, 8); + }); + assert.throws(TypeError, () => { + helper(lengthTrackingWithOffset, 0, 8); + }); + assert.compareArray(ToNumbers(taFull), [5]); + helper(lengthTracking, 0, 7); + assert.compareArray(ToNumbers(taFull), [7]); + + // Shrink to zero. + rab.resize(0); + assert.throws(TypeError, () => { + helper(fixedLength, 0, 8); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 0, 8); + }); + assert.throws(TypeError, () => { + helper(lengthTracking, 0, 8); + }); + assert.throws(TypeError, () => { + helper(lengthTrackingWithOffset, 0, 8); + }); + assert.compareArray(ToNumbers(taFull), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + helper(fixedLength, 0, 9); + assert.compareArray(ToNumbers(taFull), [ + 9, + 0, + 0, + 0, + 0, + 0 + ]); + helper(fixedLengthWithOffset, 0, 10); + assert.compareArray(ToNumbers(taFull), [ + 9, + 0, + 10, + 0, + 0, + 0 + ]); + helper(lengthTracking, 1, 11); + assert.compareArray(ToNumbers(taFull), [ + 9, + 11, + 10, + 0, + 0, + 0 + ]); + helper(lengthTrackingWithOffset, 2, 12); + assert.compareArray(ToNumbers(taFull), [ + 9, + 11, + 10, + 0, + 12, + 0 + ]); + + // Trying to define properties out of the fixed-length bounds throws. + assert.throws(TypeError, () => { + helper(fixedLength, 5, 13); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 3, 13); + }); + assert.compareArray(ToNumbers(taFull), [ + 9, + 11, + 10, + 0, + 12, + 0 + ]); + helper(lengthTracking, 4, 14); + assert.compareArray(ToNumbers(taFull), [ + 9, + 11, + 10, + 0, + 14, + 0 + ]); + helper(lengthTrackingWithOffset, 3, 15); + assert.compareArray(ToNumbers(taFull), [ + 9, + 11, + 10, + 0, + 14, + 15 + ]); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows-strict.js new file mode 100644 index 0000000000..36375f0a15 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows-strict.js @@ -0,0 +1,117 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ObjectDefinePropertyParameterConversionGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +function ObjectDefinePropertyHelper(ta, index, value) { + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + Object.defineProperty(ta, index, { value: BigInt(value) }); + } else { + Object.defineProperty(ta, index, { value: value }); + } +} + +const helper = ObjectDefinePropertyHelper; + +// Fixed length. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + // Make fixedLength go OOB. + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + const evil = { + toString: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + helper(fixedLength, evil, 8); + assert.compareArray(ToNumbers(fixedLength), [ + 8, + 0, + 0, + 0 + ]); +} + +// Length tracking. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab, 0); + const evil = { + toString: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return 4; // Index valid after resize. + } + }; + helper(lengthTracking, evil, 8); + assert.compareArray(ToNumbers(lengthTracking), [ + 0, + 0, + 0, + 0, + 8, + 0 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks-strict.js new file mode 100644 index 0000000000..b8eae3741b --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks-strict.js @@ -0,0 +1,89 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ObjectDefinePropertyParameterConversionShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function ObjectDefinePropertyHelper(ta, index, value) { + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + Object.defineProperty(ta, index, { value: BigInt(value) }); + } else { + Object.defineProperty(ta, index, { value: value }); + } +} + +const helper = ObjectDefinePropertyHelper; + +// Fixed length. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + toString: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.throws(TypeError, () => { + helper(fixedLength, evil, 8); + }); +} + +// Length tracking. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab, 0); + const evil = { + toString: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 3; // Index too large after resize. + } + }; + assert.throws(TypeError, () => { + helper(lengthTracking, evil, 8); + }); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-freeze-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-freeze-strict.js new file mode 100644 index 0000000000..f6a1a19945 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-freeze-strict.js @@ -0,0 +1,91 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ObjectFreeze test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +// Freezing non-OOB non-zero-length TAs throws. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + Object.freeze(fixedLength); + }); + assert.throws(TypeError, () => { + Object.freeze(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + Object.freeze(lengthTracking); + }); + assert.throws(TypeError, () => { + Object.freeze(lengthTrackingWithOffset); + }); +} +// Freezing zero-length TAs doesn't throw. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 0); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 0); + const lengthTrackingWithOffset = new ctor(rab, 4 * ctor.BYTES_PER_ELEMENT); + Object.freeze(fixedLength); + Object.freeze(fixedLengthWithOffset); + Object.freeze(lengthTrackingWithOffset); +} +// If the buffer has been resized to make length-tracking TAs zero-length, +// freezing them also doesn't throw. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + Object.freeze(lengthTrackingWithOffset); + rab.resize(0 * ctor.BYTES_PER_ELEMENT); + Object.freeze(lengthTracking); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/oobbehaves-like-detached-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/oobbehaves-like-detached-strict.js new file mode 100644 index 0000000000..58d7d9cdb3 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/oobbehaves-like-detached-strict.js @@ -0,0 +1,35 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from OOBBehavesLikeDetached test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function ReadElement2(ta) { + return ta[2]; +} +function HasElement2(ta) { + return 2 in ta; +} +const rab = CreateResizableArrayBuffer(16, 40); +const i8a = new Int8Array(rab, 0, 4); +i8a.__proto__ = { 2: 'wrong value' }; +i8a[2] = 10; +assert.sameValue(ReadElement2(i8a), 10); +assert(HasElement2(i8a)); +rab.resize(0); +assert.sameValue(ReadElement2(i8a), undefined); +assert(!HasElement2(i8a)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/out-of-bounds-typed-array-and-has-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/out-of-bounds-typed-array-and-has-strict.js new file mode 100644 index 0000000000..41a553b8d5 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/out-of-bounds-typed-array-and-has-strict.js @@ -0,0 +1,77 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from OutOfBoundsTypedArrayAndHas test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +for (let ctor of ctors) { + if (ctor.BYTES_PER_ELEMENT != 1) { + continue; + } + const rab = CreateResizableArrayBuffer(16, 40); + const array = new ctor(rab, 0, 4); + // Within-bounds read + for (let i = 0; i < 4; ++i) { + assert(i in array); + } + rab.resize(2); + // OOB read. If the RAB isn't large enough to fit the entire TypedArray, + // the length of the TypedArray is treated as 0. + for (let i = 0; i < 4; ++i) { + assert(!(i in array)); + } + rab.resize(4); + // Within-bounds read + for (let i = 0; i < 4; ++i) { + assert(i in array); + } + rab.resize(40); + // Within-bounds read + for (let i = 0; i < 4; ++i) { + assert(i in array); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/reverse-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/reverse-strict.js new file mode 100644 index 0000000000..9e6ff827a1 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/reverse-strict.js @@ -0,0 +1,273 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from Reverse test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArrayReverseHelper = ta => { + ta.reverse(); +}; + +const ArrayReverseHelper = ta => { + Array.prototype.reverse.call(ta); +}; + +function Reverse(reverseHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const wholeArrayView = new ctor(rab); + function WriteData() { + // Write some data into the array. + for (let i = 0; i < wholeArrayView.length; ++i) { + WriteToTypedArray(wholeArrayView, i, 2 * i); + } + } + WriteData(); + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + reverseHelper(fixedLength); + assert.compareArray(ToNumbers(wholeArrayView), [ + 6, + 4, + 2, + 0 + ]); + reverseHelper(fixedLengthWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [ + 6, + 4, + 0, + 2 + ]); + reverseHelper(lengthTracking); + assert.compareArray(ToNumbers(wholeArrayView), [ + 2, + 0, + 4, + 6 + ]); + reverseHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [ + 2, + 0, + 6, + 4 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + WriteData(); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + reverseHelper(fixedLength); + }); + assert.throws(TypeError, () => { + reverseHelper(fixedLengthWithOffset); + }); + } else { + reverseHelper(fixedLength); + assert.compareArray(ToNumbers(wholeArrayView), [ + 0, + 2, + 4 + ]); + reverseHelper(fixedLengthWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [ + 0, + 2, + 4 + ]); + } + reverseHelper(lengthTracking); + assert.compareArray(ToNumbers(wholeArrayView), [ + 4, + 2, + 0 + ]); + reverseHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [ + 4, + 2, + 0 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + WriteData(); + if (oobThrows) { + assert.throws(TypeError, () => { + reverseHelper(fixedLength); + }); + assert.throws(TypeError, () => { + reverseHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + reverseHelper(lengthTrackingWithOffset); + }); + } else { + reverseHelper(fixedLength); + assert.compareArray(ToNumbers(wholeArrayView), [0]); + reverseHelper(fixedLengthWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [0]); + reverseHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [0]); + } + reverseHelper(lengthTracking); + assert.compareArray(ToNumbers(wholeArrayView), [0]); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + reverseHelper(fixedLength); + }); + assert.throws(TypeError, () => { + reverseHelper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + reverseHelper(lengthTrackingWithOffset); + }); + } else { + reverseHelper(fixedLength); + assert.compareArray(ToNumbers(wholeArrayView), []); + reverseHelper(fixedLengthWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), []); + reverseHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), []); + } + reverseHelper(lengthTracking); + assert.compareArray(ToNumbers(wholeArrayView), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + WriteData(); + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + reverseHelper(fixedLength); + assert.compareArray(ToNumbers(wholeArrayView), [ + 6, + 4, + 2, + 0, + 8, + 10 + ]); + reverseHelper(fixedLengthWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [ + 6, + 4, + 0, + 2, + 8, + 10 + ]); + reverseHelper(lengthTracking); + assert.compareArray(ToNumbers(wholeArrayView), [ + 10, + 8, + 2, + 0, + 4, + 6 + ]); + reverseHelper(lengthTrackingWithOffset); + assert.compareArray(ToNumbers(wholeArrayView), [ + 10, + 8, + 6, + 4, + 0, + 2 + ]); + } +} + +Reverse(TypedArrayReverseHelper, true); +Reverse(ArrayReverseHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-grow-target-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-grow-target-mid-iteration-strict.js new file mode 100644 index 0000000000..6be7a34e88 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-grow-target-mid-iteration-strict.js @@ -0,0 +1,192 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SetGrowTargetMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [4, 6] << fixedLengthWithOffset +// [0, 2, 4, 6, ...] << lengthTracking +// [4, 6, ...] << lengthTrackingWithOffset +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} +let rab; +// Resizing will happen when we're calling Get for the `resizeAt`:th data +// element, but we haven't yet written it to the target. +let resizeAt; +let resizeTo; +function CreateSourceProxy(length) { + let requestedIndices = []; + return new Proxy({}, { + get(target, prop, receiver) { + if (prop == 'length') { + return length; + } + requestedIndices.push(prop); + if (requestedIndices.length == resizeAt) { + rab.resize(resizeTo); + } + return true; // Can be converted to both BigInt and Number. + } + }); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAt = 2; + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + fixedLength.set(CreateSourceProxy(4)); + assert.compareArray(ToNumbers(fixedLength), [ + 1, + 1, + 1, + 1 + ]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 1, + 1, + 1, + 1, + 0, + 0 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAt = 1; + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + fixedLengthWithOffset.set(CreateSourceProxy(2)); + assert.compareArray(ToNumbers(fixedLengthWithOffset), [ + 1, + 1 + ]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 1, + 1, + 0, + 0 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAt = 2; + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + lengthTracking.set(CreateSourceProxy(2)); + assert.compareArray(ToNumbers(lengthTracking), [ + 1, + 1, + 4, + 6, + 0, + 0 + ]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 1, + 1, + 4, + 6, + 0, + 0 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAt = 1; + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + lengthTrackingWithOffset.set(CreateSourceProxy(2)); + assert.compareArray(ToNumbers(lengthTrackingWithOffset), [ + 1, + 1, + 0, + 0 + ]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 1, + 1, + 0, + 0 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-shrink-target-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-shrink-target-mid-iteration-strict.js new file mode 100644 index 0000000000..547606ee0b --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-shrink-target-mid-iteration-strict.js @@ -0,0 +1,172 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SetShrinkTargetMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [4, 6] << fixedLengthWithOffset +// [0, 2, 4, 6, ...] << lengthTracking +// [4, 6, ...] << lengthTrackingWithOffset +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} +let rab; +// Resizing will happen when we're calling Get for the `resizeAt`:th data +// element, but we haven't yet written it to the target. +let resizeAt; +let resizeTo; +function CreateSourceProxy(length) { + let requestedIndices = []; + return new Proxy({}, { + get(target, prop, receiver) { + if (prop == 'length') { + return length; + } + requestedIndices.push(prop); + if (requestedIndices.length == resizeAt) { + rab.resize(resizeTo); + } + return true; // Can be converted to both BigInt and Number. + } + }); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeAt = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + fixedLength.set(CreateSourceProxy(4)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 1, + 2, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeAt = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + fixedLengthWithOffset.set(CreateSourceProxy(2)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 1 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAt = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + lengthTracking.set(CreateSourceProxy(2)); + assert.compareArray(ToNumbers(lengthTracking), [ + 1, + 1, + 4 + ]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 1, + 1, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAt = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + lengthTrackingWithOffset.set(CreateSourceProxy(2)); + assert.compareArray(ToNumbers(lengthTrackingWithOffset), [1]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 1 + ]); +} + +// Length-tracking TA goes OOB because of the offset. +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeAt = 1; + resizeTo = 1 * ctor.BYTES_PER_ELEMENT; + lengthTrackingWithOffset.set(CreateSourceProxy(2)); + assert.compareArray(ToNumbers(new ctor(rab)), [0]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-grows-target-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-grows-target-strict.js new file mode 100644 index 0000000000..71bcc27ad4 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-grows-target-strict.js @@ -0,0 +1,137 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SetSourceLengthGetterGrowsTarget test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [4, 6] << fixedLengthWithOffset +// [0, 2, 4, 6, ...] << lengthTracking +// [4, 6, ...] << lengthTrackingWithOffset +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} +let rab; +let resizeTo; +function CreateSourceProxy(length) { + return new Proxy({}, { + get(target, prop, receiver) { + if (prop == 'length') { + rab.resize(resizeTo); + return length; + } + return true; // Can be converted to both BigInt and Number. + } + }); +} + +// Test that we still throw for lengthTracking TAs if the source length is +// too large, even though we resized in the length getter (we check against +// the original length). +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + assert.throws(RangeError, () => { + lengthTracking.set(CreateSourceProxy(6)); + }); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4, + 6, + 0, + 0 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + assert.throws(RangeError, () => { + lengthTrackingWithOffset.set(CreateSourceProxy(4)); + }); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4, + 6, + 0, + 0 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-shrinks-target-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-shrinks-target-strict.js new file mode 100644 index 0000000000..8a06215b30 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-shrinks-target-strict.js @@ -0,0 +1,219 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SetSourceLengthGetterShrinksTarget test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [4, 6] << fixedLengthWithOffset +// [0, 2, 4, 6, ...] << lengthTracking +// [4, 6, ...] << lengthTrackingWithOffset +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} +let rab; +let resizeTo; +function CreateSourceProxy(length) { + return new Proxy({}, { + get(target, prop, receiver) { + if (prop == 'length') { + rab.resize(resizeTo); + return length; + } + return true; // Can be converted to both BigInt and Number. + } + }); +} + +// Tests where the length getter returns a non-zero value -> these are nop if +// the TA went OOB. +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + fixedLength.set(CreateSourceProxy(1)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + fixedLengthWithOffset.set(CreateSourceProxy(1)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + lengthTracking.set(CreateSourceProxy(1)); + assert.compareArray(ToNumbers(lengthTracking), [ + 1, + 2, + 4 + ]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 1, + 2, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + lengthTrackingWithOffset.set(CreateSourceProxy(1)); + assert.compareArray(ToNumbers(lengthTrackingWithOffset), [1]); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 1 + ]); +} + +// Length-tracking TA goes OOB because of the offset. +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeTo = 1 * ctor.BYTES_PER_ELEMENT; + lengthTrackingWithOffset.set(CreateSourceProxy(1)); + assert.compareArray(ToNumbers(new ctor(rab)), [0]); +} + +// Tests where the length getter returns a zero -> these don't throw even if +// the TA went OOB. +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + fixedLength.set(CreateSourceProxy(0)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + fixedLengthWithOffset.set(CreateSourceProxy(0)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + lengthTracking.set(CreateSourceProxy(0)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4 + ]); +} +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + lengthTrackingWithOffset.set(CreateSourceProxy(0)); + assert.compareArray(ToNumbers(new ctor(rab)), [ + 0, + 2, + 4 + ]); +} + +// Length-tracking TA goes OOB because of the offset. +for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + resizeTo = 1 * ctor.BYTES_PER_ELEMENT; + lengthTrackingWithOffset.set(CreateSourceProxy(0)); + assert.compareArray(ToNumbers(new ctor(rab)), [0]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-source-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-source-strict.js new file mode 100644 index 0000000000..f56a1e86f4 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-source-strict.js @@ -0,0 +1,292 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SetWithResizableSource test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function IsBigIntTypedArray(ta) { + return ta instanceof BigInt64Array || ta instanceof BigUint64Array; +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +function SetHelper(target, source, offset) { + if (target instanceof BigInt64Array || target instanceof BigUint64Array) { + const bigIntSource = []; + for (const s of source) { + bigIntSource.push(BigInt(s)); + } + source = bigIntSource; + } + if (offset == undefined) { + return target.set(source); + } + return target.set(source, offset); +} + +for (let targetIsResizable of [ + false, + true + ]) { + for (let targetCtor of ctors) { + for (let sourceCtor of ctors) { + const rab = CreateResizableArrayBuffer(4 * sourceCtor.BYTES_PER_ELEMENT, 8 * sourceCtor.BYTES_PER_ELEMENT); + const fixedLength = new sourceCtor(rab, 0, 4); + const fixedLengthWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new sourceCtor(rab, 0); + const lengthTrackingWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taFull = new sourceCtor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taFull, i, i + 1); + } + + // Orig. array: [1, 2, 3, 4] + // [1, 2, 3, 4] << fixedLength + // [3, 4] << fixedLengthWithOffset + // [1, 2, 3, 4, ...] << lengthTracking + // [3, 4, ...] << lengthTrackingWithOffset + + const targetAb = targetIsResizable ? new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT) : new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT, { maxByteLength: 8 * targetCtor.BYTES_PER_ELEMENT }); + const target = new targetCtor(targetAb); + if (IsBigIntTypedArray(target) != IsBigIntTypedArray(taFull)) { + // Can't mix BigInt and non-BigInt types. + continue; + } + SetHelper(target, fixedLength); + assert.compareArray(ToNumbers(target), [ + 1, + 2, + 3, + 4, + 0, + 0 + ]); + SetHelper(target, fixedLengthWithOffset); + assert.compareArray(ToNumbers(target), [ + 3, + 4, + 3, + 4, + 0, + 0 + ]); + SetHelper(target, lengthTracking, 1); + assert.compareArray(ToNumbers(target), [ + 3, + 1, + 2, + 3, + 4, + 0 + ]); + SetHelper(target, lengthTrackingWithOffset, 1); + assert.compareArray(ToNumbers(target), [ + 3, + 3, + 4, + 3, + 4, + 0 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * sourceCtor.BYTES_PER_ELEMENT); + + // Orig. array: [1, 2, 3] + // [1, 2, 3, ...] << lengthTracking + // [3, ...] << lengthTrackingWithOffset + + assert.throws(TypeError, () => { + SetHelper(target, fixedLength); + }); + assert.throws(TypeError, () => { + SetHelper(target, fixedLengthWithOffset); + }); + assert.compareArray(ToNumbers(target), [ + 3, + 3, + 4, + 3, + 4, + 0 + ]); + SetHelper(target, lengthTracking); + assert.compareArray(ToNumbers(target), [ + 1, + 2, + 3, + 3, + 4, + 0 + ]); + SetHelper(target, lengthTrackingWithOffset); + assert.compareArray(ToNumbers(target), [ + 3, + 2, + 3, + 3, + 4, + 0 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * sourceCtor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + SetHelper(target, fixedLength); + }); + assert.throws(TypeError, () => { + SetHelper(target, fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + SetHelper(target, lengthTrackingWithOffset); + }); + SetHelper(target, lengthTracking, 3); + assert.compareArray(ToNumbers(target), [ + 3, + 2, + 3, + 1, + 4, + 0 + ]); + + // Shrink to zero. + rab.resize(0); + assert.throws(TypeError, () => { + SetHelper(target, fixedLength); + }); + assert.throws(TypeError, () => { + SetHelper(target, fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + SetHelper(target, lengthTrackingWithOffset); + }); + SetHelper(target, lengthTracking, 4); + assert.compareArray(ToNumbers(target), [ + 3, + 2, + 3, + 1, + 4, + 0 + ]); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * sourceCtor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taFull, i, i + 1); + } + + // Orig. array: [1, 2, 3, 4, 5, 6] + // [1, 2, 3, 4] << fixedLength + // [3, 4] << fixedLengthWithOffset + // [1, 2, 3, 4, 5, 6, ...] << lengthTracking + // [3, 4, 5, 6, ...] << lengthTrackingWithOffset + + SetHelper(target, fixedLength); + assert.compareArray(ToNumbers(target), [ + 1, + 2, + 3, + 4, + 4, + 0 + ]); + SetHelper(target, fixedLengthWithOffset); + assert.compareArray(ToNumbers(target), [ + 3, + 4, + 3, + 4, + 4, + 0 + ]); + SetHelper(target, lengthTracking, 0); + assert.compareArray(ToNumbers(target), [ + 1, + 2, + 3, + 4, + 5, + 6 + ]); + SetHelper(target, lengthTrackingWithOffset, 1); + assert.compareArray(ToNumbers(target), [ + 1, + 3, + 4, + 5, + 6, + 6 + ]); + } + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-target-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-target-strict.js new file mode 100644 index 0000000000..3bacca2986 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-target-strict.js @@ -0,0 +1,580 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SetWithResizableTarget test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +function SetHelper(target, source, offset) { + if (target instanceof BigInt64Array || target instanceof BigUint64Array) { + const bigIntSource = []; + for (const s of source) { + bigIntSource.push(BigInt(s)); + } + source = bigIntSource; + } + if (offset == undefined) { + return target.set(source); + } + return target.set(source, offset); +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taFull = new ctor(rab); + + // Orig. array: [0, 0, 0, 0] + // [0, 0, 0, 0] << fixedLength + // [0, 0] << fixedLengthWithOffset + // [0, 0, 0, 0, ...] << lengthTracking + // [0, 0, ...] << lengthTrackingWithOffset + + // For making sure we're not calling the source length or element getters + // if the target is OOB. + const throwingProxy = new Proxy({}, { + get(target, prop, receiver) { + throw new Error('Called getter for ' + prop); + } + }); + SetHelper(fixedLength, [ + 1, + 2 + ]); + assert.compareArray(ToNumbers(taFull), [ + 1, + 2, + 0, + 0 + ]); + SetHelper(fixedLength, [ + 3, + 4 + ], 1); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 4, + 0 + ]); + assert.throws(RangeError, () => { + SetHelper(fixedLength, [ + 0, + 0, + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(fixedLength, [ + 0, + 0, + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 4, + 0 + ]); + SetHelper(fixedLengthWithOffset, [ + 5, + 6 + ]); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 5, + 6 + ]); + SetHelper(fixedLengthWithOffset, [7], 1); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 5, + 7 + ]); + assert.throws(RangeError, () => { + SetHelper(fixedLengthWithOffset, [ + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(fixedLengthWithOffset, [ + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 1, + 3, + 5, + 7 + ]); + SetHelper(lengthTracking, [ + 8, + 9 + ]); + assert.compareArray(ToNumbers(taFull), [ + 8, + 9, + 5, + 7 + ]); + SetHelper(lengthTracking, [ + 10, + 11 + ], 1); + assert.compareArray(ToNumbers(taFull), [ + 8, + 10, + 11, + 7 + ]); + assert.throws(RangeError, () => { + SetHelper(lengthTracking, [ + 0, + 0, + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(lengthTracking, [ + 0, + 0, + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 8, + 10, + 11, + 7 + ]); + SetHelper(lengthTrackingWithOffset, [ + 12, + 13 + ]); + assert.compareArray(ToNumbers(taFull), [ + 8, + 10, + 12, + 13 + ]); + SetHelper(lengthTrackingWithOffset, [14], 1); + assert.compareArray(ToNumbers(taFull), [ + 8, + 10, + 12, + 14 + ]); + assert.throws(RangeError, () => { + SetHelper(lengthTrackingWithOffset, [ + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(lengthTrackingWithOffset, [ + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 8, + 10, + 12, + 14 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [8, 10, 12] + // [8, 10, 12, ...] << lengthTracking + // [12, ...] << lengthTrackingWithOffset + + assert.throws(TypeError, () => { + SetHelper(fixedLength, throwingProxy); + }); + assert.throws(TypeError, () => { + SetHelper(fixedLengthWithOffset, throwingProxy); + }); + assert.compareArray(ToNumbers(taFull), [ + 8, + 10, + 12 + ]); + SetHelper(lengthTracking, [ + 15, + 16 + ]); + assert.compareArray(ToNumbers(taFull), [ + 15, + 16, + 12 + ]); + SetHelper(lengthTracking, [ + 17, + 18 + ], 1); + assert.compareArray(ToNumbers(taFull), [ + 15, + 17, + 18 + ]); + assert.throws(RangeError, () => { + SetHelper(lengthTracking, [ + 0, + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(lengthTracking, [ + 0, + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 15, + 17, + 18 + ]); + SetHelper(lengthTrackingWithOffset, [19]); + assert.compareArray(ToNumbers(taFull), [ + 15, + 17, + 19 + ]); + assert.throws(RangeError, () => { + SetHelper(lengthTrackingWithOffset, [ + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(lengthTrackingWithOffset, [0], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 15, + 17, + 19 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + SetHelper(fixedLength, throwingProxy); + }); + assert.throws(TypeError, () => { + SetHelper(fixedLengthWithOffset, throwingProxy); + }); + assert.throws(TypeError, () => { + SetHelper(lengthTrackingWithOffset, throwingProxy); + }); + assert.compareArray(ToNumbers(taFull), [15]); + SetHelper(lengthTracking, [20]); + assert.compareArray(ToNumbers(taFull), [20]); + + // Shrink to zero. + rab.resize(0); + assert.throws(TypeError, () => { + SetHelper(fixedLength, throwingProxy); + }); + assert.throws(TypeError, () => { + SetHelper(fixedLengthWithOffset, throwingProxy); + }); + assert.throws(TypeError, () => { + SetHelper(lengthTrackingWithOffset, throwingProxy); + }); + assert.throws(RangeError, () => { + SetHelper(lengthTracking, [0]); + }); + assert.compareArray(ToNumbers(taFull), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 0, 0, 0, 0, 0] + // [0, 0, 0, 0] << fixedLength + // [0, 0] << fixedLengthWithOffset + // [0, 0, 0, 0, 0, 0, ...] << lengthTracking + // [0, 0, 0, 0, ...] << lengthTrackingWithOffset + SetHelper(fixedLength, [ + 21, + 22 + ]); + assert.compareArray(ToNumbers(taFull), [ + 21, + 22, + 0, + 0, + 0, + 0 + ]); + SetHelper(fixedLength, [ + 23, + 24 + ], 1); + assert.compareArray(ToNumbers(taFull), [ + 21, + 23, + 24, + 0, + 0, + 0 + ]); + assert.throws(RangeError, () => { + SetHelper(fixedLength, [ + 0, + 0, + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(fixedLength, [ + 0, + 0, + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 21, + 23, + 24, + 0, + 0, + 0 + ]); + SetHelper(fixedLengthWithOffset, [ + 25, + 26 + ]); + assert.compareArray(ToNumbers(taFull), [ + 21, + 23, + 25, + 26, + 0, + 0 + ]); + SetHelper(fixedLengthWithOffset, [27], 1); + assert.compareArray(ToNumbers(taFull), [ + 21, + 23, + 25, + 27, + 0, + 0 + ]); + assert.throws(RangeError, () => { + SetHelper(fixedLengthWithOffset, [ + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(fixedLengthWithOffset, [ + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 21, + 23, + 25, + 27, + 0, + 0 + ]); + SetHelper(lengthTracking, [ + 28, + 29, + 30, + 31, + 32, + 33 + ]); + assert.compareArray(ToNumbers(taFull), [ + 28, + 29, + 30, + 31, + 32, + 33 + ]); + SetHelper(lengthTracking, [ + 34, + 35, + 36, + 37, + 38 + ], 1); + assert.compareArray(ToNumbers(taFull), [ + 28, + 34, + 35, + 36, + 37, + 38 + ]); + assert.throws(RangeError, () => { + SetHelper(lengthTracking, [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(lengthTracking, [ + 0, + 0, + 0, + 0, + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 28, + 34, + 35, + 36, + 37, + 38 + ]); + SetHelper(lengthTrackingWithOffset, [ + 39, + 40, + 41, + 42 + ]); + assert.compareArray(ToNumbers(taFull), [ + 28, + 34, + 39, + 40, + 41, + 42 + ]); + SetHelper(lengthTrackingWithOffset, [ + 43, + 44, + 45 + ], 1); + assert.compareArray(ToNumbers(taFull), [ + 28, + 34, + 39, + 43, + 44, + 45 + ]); + assert.throws(RangeError, () => { + SetHelper(lengthTrackingWithOffset, [ + 0, + 0, + 0, + 0, + 0 + ]); + }); + assert.throws(RangeError, () => { + SetHelper(lengthTrackingWithOffset, [ + 0, + 0, + 0, + 0 + ], 1); + }); + assert.compareArray(ToNumbers(taFull), [ + 28, + 34, + 39, + 43, + 44, + 45 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/shell.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/shell.js diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-grows-strict.js new file mode 100644 index 0000000000..c3fff45e27 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-grows-strict.js @@ -0,0 +1,107 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SliceParameterConversionGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArraySliceHelper = (ta, ...rest) => { + return ta.slice(...rest); +}; + +const ArraySliceHelper = (ta, ...rest) => { + return Array.prototype.slice.call(ta, ...rest); +}; + +function SliceParameterConversionGrows(sliceHelper) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i + 1); + } + const evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.compareArray(ToNumbers(sliceHelper(lengthTracking, evil)), [ + 1, + 2, + 3, + 4 + ]); + assert.sameValue(rab.byteLength, 6 * ctor.BYTES_PER_ELEMENT); + } +} + +SliceParameterConversionGrows(TypedArraySliceHelper); +SliceParameterConversionGrows(ArraySliceHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-shrinks-strict.js new file mode 100644 index 0000000000..9f46f9e17e --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-shrinks-strict.js @@ -0,0 +1,108 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SliceParameterConversionShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.throws(TypeError, () => { + fixedLength.slice(evil); + }); + assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i + 1); + } + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.compareArray(ToNumbers(lengthTracking.slice(evil)), [ + 1, + 2, + 0, + 0 + ]); + assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-species-create-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-species-create-resizes-strict.js new file mode 100644 index 0000000000..d71dd89186 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-species-create-resizes-strict.js @@ -0,0 +1,191 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SliceSpeciesCreateResizes test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +// The corresponding test for Array.prototype.slice is not possible, since it +// doesn't call the species constructor if the "original array" is not an Array. + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + } + } + ; + const fixedLength = new MyArray(rab, 0, 4); + resizeWhenConstructorCalled = true; + assert.throws(TypeError, () => { + fixedLength.slice(); + }); + assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT); +} +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 1); + } + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + } + } + ; + const lengthTracking = new MyArray(rab); + resizeWhenConstructorCalled = true; + const a = lengthTracking.slice(); + assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT); + // The length of the resulting TypedArray is determined before + // TypedArraySpeciesCreate is called, and it doesn't change. + assert.sameValue(a.length, 4); + assert.compareArray(ToNumbers(a), [ + 1, + 1, + 0, + 0 + ]); +} + +// Test that the (start, end) parameters are computed based on the original +// length. +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 1); + } + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + } + } + ; + const lengthTracking = new MyArray(rab); + resizeWhenConstructorCalled = true; + const a = lengthTracking.slice(-3, -1); + assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT); + // The length of the resulting TypedArray is determined before + // TypedArraySpeciesCreate is called, and it doesn't change. + assert.sameValue(a.length, 2); + assert.compareArray(ToNumbers(a), [ + 1, + 0 + ]); +} + +// Test where the buffer gets resized "between elements". +{ + const rab = CreateResizableArrayBuffer(8, 16); + const taWrite = new Uint8Array(rab); + for (let i = 0; i < 8; ++i) { + WriteToTypedArray(taWrite, i, 255); + } + let resizeWhenConstructorCalled = false; + class MyArray extends Uint16Array { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(5); + } + } + } + ; + const lengthTracking = new MyArray(rab); + assert.compareArray(ToNumbers(lengthTracking), [ + 65535, + 65535, + 65535, + 65535 + ]); + resizeWhenConstructorCalled = true; + const a = lengthTracking.slice(); + assert.sameValue(rab.byteLength, 5); + assert.sameValue(a.length, 4); + assert.sameValue(a[0], 65535); + assert.sameValue(a[1], 65535); + assert.sameValue(a[2], 0); + assert.sameValue(a[3], 0); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-strict.js new file mode 100644 index 0000000000..17e8f7551d --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-strict.js @@ -0,0 +1,206 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from Slice test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + const fixedLengthSlice = fixedLength.slice(); + assert.compareArray(ToNumbers(fixedLengthSlice), [ + 0, + 1, + 2, + 3 + ]); + assert(!fixedLengthSlice.buffer.resizable); + const fixedLengthWithOffsetSlice = fixedLengthWithOffset.slice(); + assert.compareArray(ToNumbers(fixedLengthWithOffsetSlice), [ + 2, + 3 + ]); + assert(!fixedLengthWithOffsetSlice.buffer.resizable); + const lengthTrackingSlice = lengthTracking.slice(); + assert.compareArray(ToNumbers(lengthTrackingSlice), [ + 0, + 1, + 2, + 3 + ]); + assert(!lengthTrackingSlice.buffer.resizable); + const lengthTrackingWithOffsetSlice = lengthTrackingWithOffset.slice(); + assert.compareArray(ToNumbers(lengthTrackingWithOffsetSlice), [ + 2, + 3 + ]); + assert(!lengthTrackingWithOffsetSlice.buffer.resizable); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + fixedLength.slice(); + }); + assert.throws(TypeError, () => { + fixedLengthWithOffset.slice(); + }); + assert.compareArray(ToNumbers(lengthTracking.slice()), [ + 0, + 1, + 2 + ]); + assert.compareArray(ToNumbers(lengthTrackingWithOffset.slice()), [2]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.throws(TypeError, () => { + fixedLength.slice(); + }); + assert.throws(TypeError, () => { + fixedLengthWithOffset.slice(); + }); + assert.compareArray(ToNumbers(lengthTracking.slice()), [0]); + assert.throws(TypeError, () => { + lengthTrackingWithOffset.slice(); + }); + + // Shrink to zero. + rab.resize(0); + assert.throws(TypeError, () => { + fixedLength.slice(); + }); + assert.throws(TypeError, () => { + fixedLengthWithOffset.slice(); + }); + assert.compareArray(ToNumbers(lengthTracking.slice()), []); + assert.throws(TypeError, () => { + lengthTrackingWithOffset.slice(); + }); + + // Verify that the previously created slices aren't affected by the + // shrinking. + assert.compareArray(ToNumbers(fixedLengthSlice), [ + 0, + 1, + 2, + 3 + ]); + assert.compareArray(ToNumbers(fixedLengthWithOffsetSlice), [ + 2, + 3 + ]); + assert.compareArray(ToNumbers(lengthTrackingSlice), [ + 0, + 1, + 2, + 3 + ]); + assert.compareArray(ToNumbers(lengthTrackingWithOffsetSlice), [ + 2, + 3 + ]); + + // Grow so that all TAs are back in-bounds. New memory is zeroed. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + assert.compareArray(ToNumbers(fixedLength.slice()), [ + 0, + 0, + 0, + 0 + ]); + assert.compareArray(ToNumbers(fixedLengthWithOffset.slice()), [ + 0, + 0 + ]); + assert.compareArray(ToNumbers(lengthTracking.slice()), [ + 0, + 0, + 0, + 0, + 0, + 0 + ]); + assert.compareArray(ToNumbers(lengthTrackingWithOffset.slice()), [ + 0, + 0, + 0, + 0 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/some-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-grow-mid-iteration-strict.js new file mode 100644 index 0000000000..38e9aaca7a --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-grow-mid-iteration-strict.js @@ -0,0 +1,154 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SomeGrowMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArraySomeHelper = (ta, ...rest) => { + return ta.some(...rest); +}; + +const ArraySomeHelper = (ta, ...rest) => { + return Array.prototype.some.call(ta, ...rest); +}; + +function SomeGrowMidIteration(someHelper) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values = []; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(fixedLength, CollectValuesAndResize)); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(fixedLengthWithOffset, CollectValuesAndResize)); + assert.compareArray(values, [ + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(lengthTracking, CollectValuesAndResize)); + assert.compareArray(values, [ + 0, + 2, + 4, + 6 + ]); + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + rab = rab; + resizeAfter = 1; + resizeTo = 5 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(lengthTrackingWithOffset, CollectValuesAndResize)); + assert.compareArray(values, [ + 4, + 6 + ]); + } +} + +SomeGrowMidIteration(TypedArraySomeHelper); +SomeGrowMidIteration(ArraySomeHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/some-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-shrink-mid-iteration-strict.js new file mode 100644 index 0000000000..990e32c561 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-shrink-mid-iteration-strict.js @@ -0,0 +1,176 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SomeShrinkMidIteration test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +const TypedArraySomeHelper = (ta, ...rest) => { + return ta.some(...rest); +}; + +const ArraySomeHelper = (ta, ...rest) => { + return Array.prototype.some.call(ta, ...rest); +}; + +function SomeShrinkMidIteration(someHelper, hasUndefined) { + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; + } + let values; + let rab; + let resizeAfter; + let resizeTo; + function CollectValuesAndResize(n) { + if (typeof n == 'bigint') { + values.push(Number(n)); + } else { + values.push(n); + } + if (values.length == resizeAfter) { + rab.resize(resizeTo); + } + return false; + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(fixedLength, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 0, + 2, + undefined, + undefined + ]); + } else { + assert.compareArray(values, [ + 0, + 2 + ]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(fixedLengthWithOffset, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 4, + undefined + ]); + } else { + assert.compareArray(values, [4]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(lengthTracking, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 0, + 2, + 4, + undefined + ]); + } else { + assert.compareArray(values, [ + 0, + 2, + 4 + ]); + } + } + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + values = []; + resizeAfter = 1; + resizeTo = 3 * ctor.BYTES_PER_ELEMENT; + assert(!someHelper(lengthTrackingWithOffset, CollectValuesAndResize)); + if (hasUndefined) { + assert.compareArray(values, [ + 4, + undefined + ]); + } else { + assert.compareArray(values, [4]); + } + } +} + +SomeShrinkMidIteration(TypedArraySomeHelper, true); +SomeShrinkMidIteration(ArraySomeHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-grows-strict.js new file mode 100644 index 0000000000..983b2e53f0 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-grows-strict.js @@ -0,0 +1,143 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SortCallbackGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArraySortHelper = (ta, ...rest) => { + ta.sort(...rest); +}; + +const ArraySortHelper = (ta, ...rest) => { + Array.prototype.sort.call(ta, ...rest); +}; + +function SortCallbackGrows(sortHelper) { + function WriteUnsortedData(taFull) { + for (let i = 0; i < taFull.length; ++i) { + WriteToTypedArray(taFull, i, 10 - i); + } + } + let rab; + let resizeTo; + function CustomComparison(a, b) { + rab.resize(resizeTo); + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + return 0; + } + + // Fixed length TA. + for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + const fixedLength = new ctor(rab, 0, 4); + const taFull = new ctor(rab, 0); + WriteUnsortedData(taFull); + sortHelper(fixedLength, CustomComparison); + // Growing doesn't affect the sorting. + assert.compareArray(ToNumbers(taFull), [ + 7, + 8, + 9, + 10, + 0, + 0 + ]); + } + + // Length-tracking TA. + for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + resizeTo = 6 * ctor.BYTES_PER_ELEMENT; + const lengthTracking = new ctor(rab, 0); + const taFull = new ctor(rab, 0); + WriteUnsortedData(taFull); + sortHelper(lengthTracking, CustomComparison); + // Growing doesn't affect the sorting. Only the elements that were part of + // the original TA are sorted. + assert.compareArray(ToNumbers(taFull), [ + 7, + 8, + 9, + 10, + 0, + 0 + ]); + } +} + +SortCallbackGrows(TypedArraySortHelper); +SortCallbackGrows(ArraySortHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-shrinks-strict.js new file mode 100644 index 0000000000..21a3def6a9 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-shrinks-strict.js @@ -0,0 +1,145 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SortCallbackShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer, Array.prototype.includes] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArraySortHelper = (ta, ...rest) => { + ta.sort(...rest); +}; + +const ArraySortHelper = (ta, ...rest) => { + Array.prototype.sort.call(ta, ...rest); +}; + +function SortCallbackShrinks(sortHelper) { + function WriteUnsortedData(taFull) { + for (let i = 0; i < taFull.length; ++i) { + WriteToTypedArray(taFull, i, 10 - i); + } + } + let rab; + let resizeTo; + function CustomComparison(a, b) { + rab.resize(resizeTo); + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + return 0; + } + + // Fixed length TA. + for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + resizeTo = 2 * ctor.BYTES_PER_ELEMENT; + const fixedLength = new ctor(rab, 0, 4); + const taFull = new ctor(rab, 0); + WriteUnsortedData(taFull); + sortHelper(fixedLength, CustomComparison); + // The data is unchanged. + assert.compareArray(ToNumbers(taFull), [ + 10, + 9 + ]); + } + + // Length-tracking TA. + for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + resizeTo = 2 * ctor.BYTES_PER_ELEMENT; + const lengthTracking = new ctor(rab, 0); + const taFull = new ctor(rab, 0); + WriteUnsortedData(taFull); + sortHelper(lengthTracking, CustomComparison); + // The sort result is implementation defined, but it contains 2 elements out + // of the 4 original ones. + const newData = ToNumbers(taFull); + assert.sameValue(newData.length, 2); + assert([ + 10, + 9, + 8, + 7 + ].includes(newData[0])); + assert([ + 10, + 9, + 8, + 7 + ].includes(newData[1])); + } +} + +SortCallbackShrinks(TypedArraySortHelper); +SortCallbackShrinks(ArraySortHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-custom-comparison-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-custom-comparison-strict.js new file mode 100644 index 0000000000..fc1f227003 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-custom-comparison-strict.js @@ -0,0 +1,309 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SortWithCustomComparison test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArraySortHelper = (ta, ...rest) => { + ta.sort(...rest); +}; + +const ArraySortHelper = (ta, ...rest) => { + Array.prototype.sort.call(ta, ...rest); +}; + +function SortWithCustomComparison(sortHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taFull = new ctor(rab, 0); + function WriteUnsortedData() { + // Write some data into the array. + for (let i = 0; i < taFull.length; ++i) { + WriteToTypedArray(taFull, i, 10 - i); + } + } + function CustomComparison(a, b) { + // Sort all odd numbers before even numbers. + a = Number(a); + b = Number(b); + if (a % 2 == 1 && b % 2 == 0) { + return -1; + } + if (a % 2 == 0 && b % 2 == 1) { + return 1; + } + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + return 0; + } + // Orig. array: [10, 9, 8, 7] + // [10, 9, 8, 7] << fixedLength + // [8, 7] << fixedLengthWithOffset + // [10, 9, 8, 7, ...] << lengthTracking + // [8, 7, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + sortHelper(fixedLength, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 7, + 9, + 8, + 10 + ]); + WriteUnsortedData(); + sortHelper(fixedLengthWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 7, + 8 + ]); + WriteUnsortedData(); + sortHelper(lengthTracking, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 7, + 9, + 8, + 10 + ]); + WriteUnsortedData(); + sortHelper(lengthTrackingWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 7, + 8 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [10, 9, 8] + // [10, 9, 8, ...] << lengthTracking + // [8, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + if (oobThrows) { + assert.throws(TypeError, () => { + sortHelper(fixedLength, CustomComparison); + }); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 8 + ]); + assert.throws(TypeError, () => { + sortHelper(fixedLengthWithOffset, CustomComparison); + }); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 8 + ]); + } else { + sortHelper(fixedLength, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 8 + ]); + sortHelper(fixedLengthWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 8 + ]); + } + WriteUnsortedData(); + sortHelper(lengthTracking, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 9, + 8, + 10 + ]); + WriteUnsortedData(); + sortHelper(lengthTrackingWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 8 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + WriteUnsortedData(); + if (oobThrows) { + assert.throws(TypeError, () => { + sortHelper(fixedLength, CustomComparison); + }); + assert.compareArray(ToNumbers(taFull), [10]); + assert.throws(TypeError, () => { + sortHelper(fixedLengthWithOffset, CustomComparison); + }); + assert.compareArray(ToNumbers(taFull), [10]); + assert.throws(TypeError, () => { + sortHelper(lengthTrackingWithOffset, CustomComparison); + }); + assert.compareArray(ToNumbers(taFull), [10]); + } else { + sortHelper(fixedLength, CustomComparison); + assert.compareArray(ToNumbers(taFull), [10]); + sortHelper(fixedLengthWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [10]); + sortHelper(lengthTrackingWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [10]); + } + WriteUnsortedData(); + sortHelper(lengthTracking, CustomComparison); + assert.compareArray(ToNumbers(taFull), [10]); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + sortHelper(fixedLength, CustomComparison); + }); + assert.throws(TypeError, () => { + sortHelper(fixedLengthWithOffset, CustomComparison); + }); + assert.throws(TypeError, () => { + sortHelper(lengthTrackingWithOffset, CustomComparison); + }); + } else { + sortHelper(fixedLength, CustomComparison); + sortHelper(fixedLengthWithOffset, CustomComparison); + sortHelper(lengthTrackingWithOffset, CustomComparison); + } + sortHelper(lengthTracking, CustomComparison); + assert.compareArray(ToNumbers(taFull), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [10, 9, 8, 7, 6, 5] + // [10, 9, 8, 7] << fixedLength + // [8, 7] << fixedLengthWithOffset + // [10, 9, 8, 7, 6, 5, ...] << lengthTracking + // [8, 7, 6, 5, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + sortHelper(fixedLength, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 7, + 9, + 8, + 10, + 6, + 5 + ]); + WriteUnsortedData(); + sortHelper(fixedLengthWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 7, + 8, + 6, + 5 + ]); + WriteUnsortedData(); + sortHelper(lengthTracking, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 5, + 7, + 9, + 6, + 8, + 10 + ]); + WriteUnsortedData(); + sortHelper(lengthTrackingWithOffset, CustomComparison); + assert.compareArray(ToNumbers(taFull), [ + 10, + 9, + 5, + 7, + 6, + 8 + ]); + } +} + +SortWithCustomComparison(TypedArraySortHelper, true); +SortWithCustomComparison(ArraySortHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-default-comparison-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-default-comparison-strict.js new file mode 100644 index 0000000000..6f739ae977 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-default-comparison-strict.js @@ -0,0 +1,245 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SortWithDefaultComparison test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +// This test cannot be reused between TypedArray.protoype.sort and +// Array.prototype.sort, since the default sorting functions differ. + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + const taFull = new ctor(rab, 0); + function WriteUnsortedData() { + // Write some data into the array. + for (let i = 0; i < taFull.length; ++i) { + WriteToTypedArray(taFull, i, 10 - 2 * i); + } + } + // Orig. array: [10, 8, 6, 4] + // [10, 8, 6, 4] << fixedLength + // [6, 4] << fixedLengthWithOffset + // [10, 8, 6, 4, ...] << lengthTracking + // [6, 4, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + fixedLength.sort(); + assert.compareArray(ToNumbers(taFull), [ + 4, + 6, + 8, + 10 + ]); + WriteUnsortedData(); + fixedLengthWithOffset.sort(); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 4, + 6 + ]); + WriteUnsortedData(); + lengthTracking.sort(); + assert.compareArray(ToNumbers(taFull), [ + 4, + 6, + 8, + 10 + ]); + WriteUnsortedData(); + lengthTrackingWithOffset.sort(); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 4, + 6 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [10, 8, 6] + // [10, 8, 6, ...] << lengthTracking + // [6, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + assert.throws(TypeError, () => { + fixedLength.sort(); + }); + WriteUnsortedData(); + assert.throws(TypeError, () => { + fixedLengthWithOffset.sort(); + }); + WriteUnsortedData(); + lengthTracking.sort(); + assert.compareArray(ToNumbers(taFull), [ + 6, + 8, + 10 + ]); + WriteUnsortedData(); + lengthTrackingWithOffset.sort(); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 6 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + WriteUnsortedData(); + assert.throws(TypeError, () => { + fixedLength.sort(); + }); + WriteUnsortedData(); + assert.throws(TypeError, () => { + fixedLengthWithOffset.sort(); + }); + WriteUnsortedData(); + lengthTracking.sort(); + assert.compareArray(ToNumbers(taFull), [10]); + WriteUnsortedData(); + assert.throws(TypeError, () => { + lengthTrackingWithOffset.sort(); + }); + + // Shrink to zero. + rab.resize(0); + WriteUnsortedData(); + assert.throws(TypeError, () => { + fixedLength.sort(); + }); + WriteUnsortedData(); + assert.throws(TypeError, () => { + fixedLengthWithOffset.sort(); + }); + WriteUnsortedData(); + lengthTracking.sort(); + assert.compareArray(ToNumbers(taFull), []); + WriteUnsortedData(); + assert.throws(TypeError, () => { + lengthTrackingWithOffset.sort(); + }); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [10, 8, 6, 4, 2, 0] + // [10, 8, 6, 4] << fixedLength + // [6, 4] << fixedLengthWithOffset + // [10, 8, 6, 4, 2, 0, ...] << lengthTracking + // [6, 4, 2, 0, ...] << lengthTrackingWithOffset + + WriteUnsortedData(); + fixedLength.sort(); + assert.compareArray(ToNumbers(taFull), [ + 4, + 6, + 8, + 10, + 2, + 0 + ]); + WriteUnsortedData(); + fixedLengthWithOffset.sort(); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 4, + 6, + 2, + 0 + ]); + WriteUnsortedData(); + lengthTracking.sort(); + assert.compareArray(ToNumbers(taFull), [ + 0, + 2, + 4, + 6, + 8, + 10 + ]); + WriteUnsortedData(); + lengthTrackingWithOffset.sort(); + assert.compareArray(ToNumbers(taFull), [ + 10, + 8, + 0, + 2, + 4, + 6 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-grows-strict.js new file mode 100644 index 0000000000..9727cb6e30 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-grows-strict.js @@ -0,0 +1,141 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SubarrayParameterConversionGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [0, 2, 4, 6, ...] << lengthTracking +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} + +// Growing a fixed length TA back in bounds. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + // Make `fixedLength` OOB. + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + const evil = { + valueOf: () => { + rab.resize(4 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + // The length computation is done before parameter conversion. At that + // point, the length is 0, since the TA is OOB. + assert.compareArray(ToNumbers(fixedLength.subarray(evil, 0, 1)), []); +} + +// Growing + fixed-length TA. Growing won't affect anything. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.compareArray(ToNumbers(fixedLength.subarray(evil)), [ + 0, + 2, + 4, + 6 + ]); +} + +// Growing + length-tracking TA. The length computation is done with the +// original length. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + const evil = { + valueOf: () => { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.compareArray( + ToNumbers(lengthTracking.subarray(evil, lengthTracking.length)), [ + 0, + 2, + 4, + 6 + ]); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-shrinks-strict.js new file mode 100644 index 0000000000..24b2ccd8ad --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-shrinks-strict.js @@ -0,0 +1,223 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from SubarrayParameterConversionShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +// Orig. array: [0, 2, 4, 6] +// [0, 2, 4, 6] << fixedLength +// [0, 2, 4, 6, ...] << lengthTracking +function CreateRabForTest(ctor) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + return rab; +} + +// Fixed-length TA + first parameter conversion shrinks. The old length is +// used in the length computation, and the subarray construction fails. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.throws(RangeError, () => { + fixedLength.subarray(evil); + }); +} + +// Like the previous test, but now we construct a smaller subarray and it +// succeeds. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.compareArray(ToNumbers(fixedLength.subarray(evil, 1)), [0]); +} + +// Fixed-length TA + second parameter conversion shrinks. The old length is +// used in the length computation, and the subarray construction fails. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 3; + } + }; + assert.throws(RangeError, () => { + fixedLength.subarray(0, evil); + }); +} + +// Like the previous test, but now we construct a smaller subarray and it +// succeeds. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 1; + } + }; + assert.compareArray(ToNumbers(fixedLength.subarray(0, evil)), [0]); +} + +// Shrinking + fixed-length TA, subarray construction succeeds even though the +// TA goes OOB. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const fixedLength = new ctor(rab, 0, 4); + const evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.compareArray(ToNumbers(fixedLength.subarray(evil, 1)), [0]); +} + +// Length-tracking TA + first parameter conversion shrinks. The old length is +// used in the length computation, and the subarray construction fails. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.throws(RangeError, () => { + lengthTracking.subarray(evil, lengthTracking.length); + }); +} + +// Like the previous test, but now we construct a smaller subarray and it +// succeeds. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.compareArray(ToNumbers(lengthTracking.subarray(evil, 1)), [0]); +} + +// Length-tracking TA + first parameter conversion shrinks. The second +// parameter is negative -> the relative index is not recomputed, and the +// subarray construction fails. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 0; + } + }; + assert.throws(RangeError, () => { + lengthTracking.subarray(evil, -1); + }); +} + +// Length-tracking TA + second parameter conversion shrinks. The second +// parameter is too large -> the subarray construction fails. +for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab); + let evil = { + valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + return 3; + } + }; + assert.throws(RangeError, () => { + lengthTracking.subarray(0, evil); + }); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-strict.js new file mode 100644 index 0000000000..93735d9326 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-strict.js @@ -0,0 +1,234 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from Subarray test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + const fixedLengthSubFull = fixedLength.subarray(0); + assert.compareArray(ToNumbers(fixedLengthSubFull), [ + 0, + 2, + 4, + 6 + ]); + const fixedLengthWithOffsetSubFull = fixedLengthWithOffset.subarray(0); + assert.compareArray(ToNumbers(fixedLengthWithOffsetSubFull), [ + 4, + 6 + ]); + const lengthTrackingSubFull = lengthTracking.subarray(0); + assert.compareArray(ToNumbers(lengthTrackingSubFull), [ + 0, + 2, + 4, + 6 + ]); + const lengthTrackingWithOffsetSubFull = lengthTrackingWithOffset.subarray(0); + assert.compareArray(ToNumbers(lengthTrackingWithOffsetSubFull), [ + 4, + 6 + ]); + + // Relative offsets + assert.compareArray(ToNumbers(fixedLength.subarray(-2)), [ + 4, + 6 + ]); + assert.compareArray(ToNumbers(fixedLengthWithOffset.subarray(-1)), [6]); + assert.compareArray(ToNumbers(lengthTracking.subarray(-2)), [ + 4, + 6 + ]); + assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(-1)), [6]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + // We can create subarrays of OOB arrays (which have length 0), as long as + // the new arrays are not OOB. + assert.compareArray(ToNumbers(fixedLength.subarray(0)), []); + assert.compareArray(ToNumbers(fixedLengthWithOffset.subarray(0)), []); + assert.compareArray(ToNumbers(lengthTracking.subarray(0)), [ + 0, + 2, + 4 + ]); + assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(0)), [4]); + + // Also the previously created subarrays are OOB. + assert.sameValue(fixedLengthSubFull.length, 0); + assert.sameValue(fixedLengthWithOffsetSubFull.length, 0); + + // Relative offsets + assert.compareArray(ToNumbers(lengthTracking.subarray(-2)), [ + 2, + 4 + ]); + assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(-1)), [4]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + assert.compareArray(ToNumbers(fixedLength.subarray(0)), []); + assert.compareArray(ToNumbers(lengthTracking.subarray(0)), [0]); + + // Even the 0-length subarray of fixedLengthWithOffset would be OOB -> + // this throws. + assert.throws(RangeError, () => { + fixedLengthWithOffset.subarray(0); + }); + + // Also the previously created subarrays are OOB. + assert.sameValue(fixedLengthSubFull.length, 0); + assert.sameValue(fixedLengthWithOffsetSubFull.length, 0); + assert.sameValue(lengthTrackingWithOffsetSubFull.length, 0); + + // Shrink to zero. + rab.resize(0); + assert.compareArray(ToNumbers(fixedLength.subarray(0)), []); + assert.compareArray(ToNumbers(lengthTracking.subarray(0)), []); + assert.throws(RangeError, () => { + fixedLengthWithOffset.subarray(0); + }); + assert.throws(RangeError, () => { + lengthTrackingWithOffset.subarray(0); + }); + + // Also the previously created subarrays are OOB. + assert.sameValue(fixedLengthSubFull.length, 0); + assert.sameValue(fixedLengthWithOffsetSubFull.length, 0); + assert.sameValue(lengthTrackingWithOffsetSubFull.length, 0); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert.compareArray(ToNumbers(fixedLength.subarray(0)), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(ToNumbers(fixedLengthWithOffset.subarray(0)), [ + 4, + 6 + ]); + assert.compareArray(ToNumbers(lengthTracking.subarray(0)), [ + 0, + 2, + 4, + 6, + 8, + 10 + ]); + assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(0)), [ + 4, + 6, + 8, + 10 + ]); + + // Also the previously created subarrays are no longer OOB. + assert.sameValue(fixedLengthSubFull.length, 4); + assert.sameValue(fixedLengthWithOffsetSubFull.length, 2); + // Subarrays of length-tracking TAs are also length-tracking. + assert.sameValue(lengthTrackingSubFull.length, 6); + assert.sameValue(lengthTrackingWithOffsetSubFull.length, 4); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/test-copy-within-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-copy-within-strict.js new file mode 100644 index 0000000000..897e8ea4ef --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-copy-within-strict.js @@ -0,0 +1,272 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from TestCopyWithin test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArrayCopyWithinHelper = (ta, ...rest) => { + ta.copyWithin(...rest); +}; + +const ArrayCopyWithinHelper = (ta, ...rest) => { + Array.prototype.copyWithin.call(ta, ...rest); +}; + +function TestCopyWithin(helper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + // Orig. array: [0, 1, 2, 3] + // [0, 1, 2, 3] << fixedLength + // [2, 3] << fixedLengthWithOffset + // [0, 1, 2, 3, ...] << lengthTracking + // [2, 3, ...] << lengthTrackingWithOffset + + helper(fixedLength, 0, 2); + assert.compareArray(ToNumbers(fixedLength), [ + 2, + 3, + 2, + 3 + ]); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + helper(fixedLengthWithOffset, 0, 1); + assert.compareArray(ToNumbers(fixedLengthWithOffset), [ + 3, + 3 + ]); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + helper(lengthTracking, 0, 2); + assert.compareArray(ToNumbers(lengthTracking), [ + 2, + 3, + 2, + 3 + ]); + helper(lengthTrackingWithOffset, 0, 1); + assert.compareArray(ToNumbers(lengthTrackingWithOffset), [ + 3, + 3 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 3; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + // Orig. array: [0, 1, 2] + // [0, 1, 2, ...] << lengthTracking + // [2, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + helper(fixedLength, 0, 1); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 0, 1); + }); + } else { + helper(fixedLength, 0, 1); + helper(fixedLengthWithOffset, 0, 1); + // We'll check below that these were no-op. + } + assert.compareArray(ToNumbers(lengthTracking), [ + 0, + 1, + 2 + ]); + helper(lengthTracking, 0, 1); + assert.compareArray(ToNumbers(lengthTracking), [ + 1, + 2, + 2 + ]); + helper(lengthTrackingWithOffset, 0, 1); + assert.compareArray(ToNumbers(lengthTrackingWithOffset), [2]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + WriteToTypedArray(taWrite, 0, 0); + if (oobThrows) { + assert.throws(TypeError, () => { + helper(fixedLength, 0, 1, 1); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 0, 1, 1); + }); + assert.throws(TypeError, () => { + helper(lengthTrackingWithOffset, 0, 1, 1); + }); + } else { + helper(fixedLength, 0, 1, 1); + helper(fixedLengthWithOffset, 0, 1, 1); + helper(lengthTrackingWithOffset, 0, 1, 1); + } + assert.compareArray(ToNumbers(lengthTracking), [0]); + helper(lengthTracking, 0, 0, 1); + assert.compareArray(ToNumbers(lengthTracking), [0]); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + helper(fixedLength, 0, 1, 1); + }); + assert.throws(TypeError, () => { + helper(fixedLengthWithOffset, 0, 1, 1); + }); + assert.throws(TypeError, () => { + helper(lengthTrackingWithOffset, 0, 1, 1); + }); + } else { + helper(fixedLength, 0, 1, 1); + helper(fixedLengthWithOffset, 0, 1, 1); + helper(lengthTrackingWithOffset, 0, 1, 1); + } + assert.compareArray(ToNumbers(lengthTracking), []); + helper(lengthTracking, 0, 0, 1); + assert.compareArray(ToNumbers(lengthTracking), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + // Orig. array: [0, 1, 2, 3, 4, 5] + // [0, 1, 2, 3] << fixedLength + // [2, 3] << fixedLengthWithOffset + // [0, 1, 2, 3, 4, 5, ...] << lengthTracking + // [2, 3, 4, 5, ...] << lengthTrackingWithOffset + + helper(fixedLength, 0, 2); + assert.compareArray(ToNumbers(fixedLength), [ + 2, + 3, + 2, + 3 + ]); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, i); + } + helper(fixedLengthWithOffset, 0, 1); + assert.compareArray(ToNumbers(fixedLengthWithOffset), [ + 3, + 3 + ]); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + // [0, 1, 2, 3, 4, 5, ...] << lengthTracking + // target ^ ^ start + helper(lengthTracking, 0, 2); + assert.compareArray(ToNumbers(lengthTracking), [ + 2, + 3, + 4, + 5, + 4, + 5 + ]); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + // [2, 3, 4, 5, ...] << lengthTrackingWithOffset + // target ^ ^ start + helper(lengthTrackingWithOffset, 0, 1); + assert.compareArray(ToNumbers(lengthTrackingWithOffset), [ + 3, + 4, + 5, + 5 + ]); + } +} + +TestCopyWithin(TypedArrayCopyWithinHelper, true); +TestCopyWithin(ArrayCopyWithinHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/test-fill-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-fill-strict.js new file mode 100644 index 0000000000..644b9066ad --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-fill-strict.js @@ -0,0 +1,243 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from TestFill test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function ReadDataFromBuffer(ab, ctor) { + let result = []; + const ta = new ctor(ab, 0, ab.byteLength / ctor.BYTES_PER_ELEMENT); + for (let item of ta) { + result.push(Number(item)); + } + return result; +} + +function TypedArrayFillHelper(ta, n, start, end) { + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + ta.fill(BigInt(n), start, end); + } else { + ta.fill(n, start, end); + } +} + +function ArrayFillHelper(ta, n, start, end) { + if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) { + Array.prototype.fill.call(ta, BigInt(n), start, end); + } else { + Array.prototype.fill.call(ta, n, start, end); + } +} + +function TestFill(helper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 0, + 0, + 0, + 0 + ]); + helper(fixedLength, 1); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 1, + 1, + 1, + 1 + ]); + helper(fixedLengthWithOffset, 2); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 1, + 1, + 2, + 2 + ]); + helper(lengthTracking, 3); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 3, + 3, + 3, + 3 + ]); + helper(lengthTrackingWithOffset, 4); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 3, + 3, + 4, + 4 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => helper(fixedLength, 5)); + assert.throws(TypeError, () => helper(fixedLengthWithOffset, 6)); + } else { + helper(fixedLength, 5); + helper(fixedLengthWithOffset, 6); + // We'll check below that these were no-op. + } + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 3, + 3, + 4 + ]); + helper(lengthTracking, 7); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 7, + 7, + 7 + ]); + helper(lengthTrackingWithOffset, 8); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 7, + 7, + 8 + ]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => helper(fixedLength, 9)); + assert.throws(TypeError, () => helper(fixedLengthWithOffset, 10)); + assert.throws(TypeError, () => helper(lengthTrackingWithOffset, 11)); + } else { + // We'll check below that these were no-op. + helper(fixedLength, 9); + helper(fixedLengthWithOffset, 10); + helper(lengthTrackingWithOffset, 11); + } + assert.compareArray(ReadDataFromBuffer(rab, ctor), [7]); + helper(lengthTracking, 12); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [12]); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + helper(fixedLength, 13); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 13, + 13, + 13, + 13, + 0, + 0 + ]); + helper(fixedLengthWithOffset, 14); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 13, + 13, + 14, + 14, + 0, + 0 + ]); + helper(lengthTracking, 15); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 15, + 15, + 15, + 15, + 15, + 15 + ]); + helper(lengthTrackingWithOffset, 16); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 15, + 15, + 16, + 16, + 16, + 16 + ]); + + // Filling with non-undefined start & end. + helper(fixedLength, 17, 1, 3); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 15, + 17, + 17, + 16, + 16, + 16 + ]); + helper(fixedLengthWithOffset, 18, 1, 2); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 15, + 17, + 17, + 18, + 16, + 16 + ]); + helper(lengthTracking, 19, 1, 3); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 15, + 19, + 19, + 18, + 16, + 16 + ]); + helper(lengthTrackingWithOffset, 20, 1, 2); + assert.compareArray(ReadDataFromBuffer(rab, ctor), [ + 15, + 19, + 19, + 20, + 16, + 16 + ]); + } +} + +TestFill(TypedArrayFillHelper, true); +TestFill(ArrayFillHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/test-map-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-map-strict.js new file mode 100644 index 0000000000..255a4ef9e9 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-map-strict.js @@ -0,0 +1,247 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from TestMap test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +function WriteToTypedArray(array, index, value) { + if (array instanceof BigInt64Array || array instanceof BigUint64Array) { + array[index] = BigInt(value); + } else { + array[index] = value; + } +} + +function Convert(item) { + if (typeof item == 'bigint') { + return Number(item); + } + return item; +} + +function ToNumbers(array) { + let result = []; + for (let item of array) { + result.push(Convert(item)); + } + return result; +} + +const TypedArrayMapHelper = (ta, ...rest) => { + return ta.map(...rest); +}; + +const ArrayMapHelper = (ta, ...rest) => { + return Array.prototype.map.call(ta, ...rest); +}; + +function TestMap(mapHelper, oobThrows) { + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); + const lengthTracking = new ctor(rab, 0); + const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); + + // Write some data into the array. + const taWrite = new ctor(rab); + for (let i = 0; i < taWrite.length; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, ...] << lengthTracking + // [4, 6, ...] << lengthTrackingWithOffset + + function Helper(array) { + const values = []; + function GatherValues(n, ix) { + assert.sameValue(ix, values.length); + values.push(n); + if (typeof n == 'bigint') { + return n + 1n; + } + return n + 1; + } + const newValues = mapHelper(array, GatherValues); + for (let i = 0; i < values.length; ++i) { + if (typeof values[i] == 'bigint') { + assert.sameValue(values[i] + 1n, newValues[i]); + } else { + assert.sameValue(values[i] + 1, newValues[i]); + } + } + return ToNumbers(values); + } + assert.compareArray(Helper(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Helper(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Helper(lengthTrackingWithOffset), [ + 4, + 6 + ]); + + // Shrink so that fixed length TAs go out of bounds. + rab.resize(3 * ctor.BYTES_PER_ELEMENT); + + // Orig. array: [0, 2, 4] + // [0, 2, 4, ...] << lengthTracking + // [4, ...] << lengthTrackingWithOffset + + if (oobThrows) { + assert.throws(TypeError, () => { + Helper(fixedLength); + }); + assert.throws(TypeError, () => { + Helper(fixedLengthWithOffset); + }); + } else { + assert.compareArray(Helper(fixedLength), []); + assert.compareArray(Helper(fixedLengthWithOffset), []); + } + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4 + ]); + assert.compareArray(Helper(lengthTrackingWithOffset), [4]); + + // Shrink so that the TAs with offset go out of bounds. + rab.resize(1 * ctor.BYTES_PER_ELEMENT); + if (oobThrows) { + assert.throws(TypeError, () => { + Helper(fixedLength); + }); + assert.throws(TypeError, () => { + Helper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + Helper(lengthTrackingWithOffset); + }); + } else { + assert.compareArray(Helper(fixedLength), []); + assert.compareArray(Helper(fixedLengthWithOffset), []); + assert.compareArray(Helper(lengthTrackingWithOffset), []); + } + assert.compareArray(Helper(lengthTracking), [0]); + + // Shrink to zero. + rab.resize(0); + if (oobThrows) { + assert.throws(TypeError, () => { + Helper(fixedLength); + }); + assert.throws(TypeError, () => { + Helper(fixedLengthWithOffset); + }); + assert.throws(TypeError, () => { + Helper(lengthTrackingWithOffset); + }); + } else { + assert.compareArray(Helper(fixedLength), []); + assert.compareArray(Helper(fixedLengthWithOffset), []); + assert.compareArray(Helper(lengthTrackingWithOffset), []); + } + assert.compareArray(Helper(lengthTracking), []); + + // Grow so that all TAs are back in-bounds. + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + for (let i = 0; i < 6; ++i) { + WriteToTypedArray(taWrite, i, 2 * i); + } + + // Orig. array: [0, 2, 4, 6, 8, 10] + // [0, 2, 4, 6] << fixedLength + // [4, 6] << fixedLengthWithOffset + // [0, 2, 4, 6, 8, 10, ...] << lengthTracking + // [4, 6, 8, 10, ...] << lengthTrackingWithOffset + + assert.compareArray(Helper(fixedLength), [ + 0, + 2, + 4, + 6 + ]); + assert.compareArray(Helper(fixedLengthWithOffset), [ + 4, + 6 + ]); + assert.compareArray(Helper(lengthTracking), [ + 0, + 2, + 4, + 6, + 8, + 10 + ]); + assert.compareArray(Helper(lengthTrackingWithOffset), [ + 4, + 6, + 8, + 10 + ]); + } +} + +TestMap(TypedArrayMapHelper, true); +TestMap(ArrayMapHelper, false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-grows-strict.js new file mode 100644 index 0000000000..c1cba050b7 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-grows-strict.js @@ -0,0 +1,116 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ToLocaleStringNumberPrototypeToLocaleStringGrows test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const TypedArrayToLocaleStringHelper = (ta, ...rest) => { + return ta.toLocaleString(...rest); +}; + +const ArrayToLocaleStringHelper = (ta, ...rest) => { + return Array.prototype.toLocaleString.call(ta, ...rest); +}; + +function ToLocaleStringNumberPrototypeToLocaleStringGrows(toLocaleStringHelper) { + const oldNumberPrototypeToLocaleString = Number.prototype.toLocaleString; + const oldBigIntPrototypeToLocaleString = BigInt.prototype.toLocaleString; + + // Growing + fixed-length TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let resizeAfter = 2; + Number.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + } + return oldNumberPrototypeToLocaleString.call(this); + }; + BigInt.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + } + return oldBigIntPrototypeToLocaleString.call(this); + }; + + // We iterate 4 elements since it was the starting length. Resizing doesn't + // affect the TA. + assert.sameValue(toLocaleStringHelper(fixedLength), '0,0,0,0'); + } + + // Growing + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + let resizeAfter = 2; + Number.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + } + return oldNumberPrototypeToLocaleString.call(this); + }; + BigInt.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(6 * ctor.BYTES_PER_ELEMENT); + } + return oldBigIntPrototypeToLocaleString.call(this); + }; + + // We iterate 4 elements since it was the starting length. + assert.sameValue(toLocaleStringHelper(lengthTracking), '0,0,0,0'); + } + Number.prototype.toLocaleString = oldNumberPrototypeToLocaleString; + BigInt.prototype.toLocaleString = oldBigIntPrototypeToLocaleString; +} + +ToLocaleStringNumberPrototypeToLocaleStringGrows(TypedArrayToLocaleStringHelper); +ToLocaleStringNumberPrototypeToLocaleStringGrows(ArrayToLocaleStringHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-shrinks-strict.js new file mode 100644 index 0000000000..ba448bb4dc --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-shrinks-strict.js @@ -0,0 +1,117 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from ToLocaleStringNumberPrototypeToLocaleStringShrinks test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const TypedArrayToLocaleStringHelper = (ta, ...rest) => { + return ta.toLocaleString(...rest); +}; + +const ArrayToLocaleStringHelper = (ta, ...rest) => { + return Array.prototype.toLocaleString.call(ta, ...rest); +}; + +function ToLocaleStringNumberPrototypeToLocaleStringShrinks(toLocaleStringHelper) { + const oldNumberPrototypeToLocaleString = Number.prototype.toLocaleString; + const oldBigIntPrototypeToLocaleString = BigInt.prototype.toLocaleString; + + // Shrinking + fixed-length TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const fixedLength = new ctor(rab, 0, 4); + let resizeAfter = 2; + Number.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + return oldNumberPrototypeToLocaleString.call(this); + }; + BigInt.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + return oldBigIntPrototypeToLocaleString.call(this); + }; + + // We iterate 4 elements, since it was the starting length. The TA goes + // OOB after 2 elements. + assert.sameValue(toLocaleStringHelper(fixedLength), '0,0,,'); + } + + // Shrinking + length-tracking TA. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + let resizeAfter = 2; + Number.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + return oldNumberPrototypeToLocaleString.call(this); + }; + BigInt.prototype.toLocaleString = function () { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); + } + return oldBigIntPrototypeToLocaleString.call(this); + }; + + // We iterate 4 elements, since it was the starting length. Elements beyond + // the new length are converted to the empty string. + assert.sameValue(toLocaleStringHelper(lengthTracking), '0,0,,'); + } + Number.prototype.toLocaleString = oldNumberPrototypeToLocaleString; + BigInt.prototype.toLocaleString = oldBigIntPrototypeToLocaleString; +} + +ToLocaleStringNumberPrototypeToLocaleStringShrinks(TypedArrayToLocaleStringHelper); +ToLocaleStringNumberPrototypeToLocaleStringShrinks(ArrayToLocaleStringHelper); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-and-byte-length-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-and-byte-length-strict.js new file mode 100644 index 0000000000..24d7c90f9c --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-and-byte-length-strict.js @@ -0,0 +1,83 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from TypedArrayLengthAndByteLength test + in V8's mjsunit test typedarray-resizablearraybuffer.js +includes: [compareArray.js] +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const rab = CreateResizableArrayBuffer(40, 80); +for (let ctor of ctors) { + const ta = new ctor(rab, 0, 3); + assert.compareArray(ta.buffer, rab); + assert.sameValue(ta.length, 3); + assert.sameValue(ta.byteLength, 3 * ctor.BYTES_PER_ELEMENT); + const empty_ta = new ctor(rab, 0, 0); + assert.compareArray(empty_ta.buffer, rab); + assert.sameValue(empty_ta.length, 0); + assert.sameValue(empty_ta.byteLength, 0); + const ta_with_offset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 3); + assert.compareArray(ta_with_offset.buffer, rab); + assert.sameValue(ta_with_offset.length, 3); + assert.sameValue(ta_with_offset.byteLength, 3 * ctor.BYTES_PER_ELEMENT); + const empty_ta_with_offset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 0); + assert.compareArray(empty_ta_with_offset.buffer, rab); + assert.sameValue(empty_ta_with_offset.length, 0); + assert.sameValue(empty_ta_with_offset.byteLength, 0); + const length_tracking_ta = new ctor(rab); + assert.compareArray(length_tracking_ta.buffer, rab); + assert.sameValue(length_tracking_ta.length, 40 / ctor.BYTES_PER_ELEMENT); + assert.sameValue(length_tracking_ta.byteLength, 40); + const offset = 8; + const length_tracking_ta_with_offset = new ctor(rab, offset); + assert.compareArray(length_tracking_ta_with_offset.buffer, rab); + assert.sameValue(length_tracking_ta_with_offset.length, (40 - offset) / ctor.BYTES_PER_ELEMENT); + assert.sameValue(length_tracking_ta_with_offset.byteLength, 40 - offset); + const empty_length_tracking_ta_with_offset = new ctor(rab, 40); + assert.compareArray(empty_length_tracking_ta_with_offset.buffer, rab); + assert.sameValue(empty_length_tracking_ta_with_offset.length, 0); + assert.sameValue(empty_length_tracking_ta_with_offset.byteLength, 0); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-1-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-1-strict.js new file mode 100644 index 0000000000..632241a333 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-1-strict.js @@ -0,0 +1,81 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from TypedArrayLengthWhenResizedOutOfBounds1 test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const rab = CreateResizableArrayBuffer(16, 40); + +// Create TAs which cover the bytes 0-7. +let tas_and_lengths = []; +for (let ctor of ctors) { + const length = 8 / ctor.BYTES_PER_ELEMENT; + tas_and_lengths.push([ + new ctor(rab, 0, length), + length + ]); +} +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, length); + assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT); +} +rab.resize(2); +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); +} +// Resize the rab so that it just barely covers the needed 8 bytes. +rab.resize(8); +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, length); + assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT); +} +rab.resize(40); +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, length); + assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-2-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-2-strict.js new file mode 100644 index 0000000000..a46a4558af --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-2-strict.js @@ -0,0 +1,87 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from TypedArrayLengthWhenResizedOutOfBounds2 test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +// typed-array-length-when-resized-out-of-bounds-1 but with offsets. + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const rab = CreateResizableArrayBuffer(20, 40); + +// Create TAs which cover the bytes 8-15. +let tas_and_lengths = []; +for (let ctor of ctors) { + const length = 8 / ctor.BYTES_PER_ELEMENT; + tas_and_lengths.push([ + new ctor(rab, 8, length), + length + ]); +} +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, length); + assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteOffset, 8); +} +rab.resize(10); +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, 0); + assert.sameValue(ta.byteLength, 0); + assert.sameValue(ta.byteOffset, 0); +} +// Resize the rab so that it just barely covers the needed 8 bytes. +rab.resize(16); +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, length); + assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteOffset, 8); +} +rab.resize(40); +for (let [ta, length] of tas_and_lengths) { + assert.sameValue(ta.length, length); + assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT); + assert.sameValue(ta.byteOffset, 8); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-prototype-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-prototype-strict.js new file mode 100644 index 0000000000..33225c8fd6 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-prototype-strict.js @@ -0,0 +1,57 @@ +// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options +'use strict'; +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-arraybuffer-length +description: > + Automatically ported from TypedArrayPrototype test + in V8's mjsunit test typedarray-resizablearraybuffer.js +features: [resizable-arraybuffer] +flags: [onlyStrict] +---*/ + +class MyUint8Array extends Uint8Array { +} + +class MyFloat32Array extends Float32Array { +} + +class MyBigInt64Array extends BigInt64Array { +} + +const builtinCtors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Float32Array, + Float64Array, + Uint8ClampedArray, + BigUint64Array, + BigInt64Array +]; + +const ctors = [ + ...builtinCtors, + MyUint8Array, + MyFloat32Array, + MyBigInt64Array +]; + +function CreateResizableArrayBuffer(byteLength, maxByteLength) { + return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength }); +} + +const rab = CreateResizableArrayBuffer(40, 80); +const ab = new ArrayBuffer(80); +for (let ctor of ctors) { + const ta_rab = new ctor(rab, 0, 3); + const ta_ab = new ctor(ab, 0, 3); + assert.sameValue(ta_ab.__proto__, ta_rab.__proto__); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/ArrayBuffer/shell.js b/js/src/tests/test262/staging/ArrayBuffer/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/ArrayBuffer/shell.js diff --git a/js/src/tests/test262/staging/Intl402/Temporal/browser.js b/js/src/tests/test262/staging/Intl402/Temporal/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/browser.js diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/addition-across-lunisolar-leap-months.js b/js/src/tests/test262/staging/Intl402/Temporal/old/addition-across-lunisolar-leap-months.js new file mode 100644 index 0000000000..6e44e1974f --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/addition-across-lunisolar-leap-months.js @@ -0,0 +1,96 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Addition across lunisolar leap months +features: [Temporal] +---*/ + +// Adding years across Hebrew leap month +var date = Temporal.PlainDate.from({ + year: 5783, + monthCode: "M08", + day: 2, + calendar: "hebrew" +}); +var added = date.add({ years: 1 }); +assert.sameValue(added.monthCode, date.monthCode); +assert.sameValue(added.year, date.year + 1); + +// Adding months across Hebrew leap month +var date = Temporal.PlainDate.from({ + year: 5783, + monthCode: "M08", + day: 2, + calendar: "hebrew" +}); +var added = date.add({ months: 13 }); +assert.sameValue(added.monthCode, date.monthCode); +assert.sameValue(added.year, date.year + 1); + +// Adding months and years across Hebrew leap month +var date = Temporal.PlainDate.from({ + year: 5783, + monthCode: "M08", + day: 2, + calendar: "hebrew" +}); +var added = date.add({ + years: 1, + months: 12 +}); +assert.sameValue(added.monthCode, date.monthCode); +assert.sameValue(added.year, date.year + 2); +var testChineseData = new Date("2001-02-01T00:00Z").toLocaleString("en-US-u-ca-chinese", { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC" +}); +var hasOutdatedChineseIcuData = !testChineseData.endsWith("2001"); + +// Adding years across Chinese leap month" +if(hasOutdatedChineseIcuData) { + var date = Temporal.PlainDate.from({ + year: 2000, + monthCode: "M08", + day: 2, + calendar: "chinese" + }); + var added = date.add({ years: 1 }); + assert.sameValue(added.monthCode, date.monthCode); + assert.sameValue(added.year, date.year + 1); +} +// Adding months across Chinese leap month +if(hasOutdatedChineseIcuData) { + var date = Temporal.PlainDate.from({ + year: 2000, + monthCode: "M08", + day: 2, + calendar: "chinese" + }); + var added = date.add({ months: 13 }); + assert.sameValue(added.monthCode, date.monthCode); + assert.sameValue(added.year, date.year + 1); +} + +// Adding months and years across Chinese leap month +if(hasOutdatedChineseIcuData) { + var date = Temporal.PlainDate.from({ + year: 2001, + monthCode: "M08", + day: 2, + calendar: "chinese" + }); + var added = date.add({ + years: 1, + months: 12 + }); + assert.sameValue(added.monthCode, date.monthCode); + assert.sameValue(added.year, date.year + 2); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/browser.js b/js/src/tests/test262/staging/Intl402/Temporal/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/browser.js diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/date-time-format.js b/js/src/tests/test262/staging/Intl402/Temporal/old/date-time-format.js new file mode 100644 index 0000000000..2729754353 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/date-time-format.js @@ -0,0 +1,1668 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: DateTimeFormat +includes: [deepEqual.js] +features: [Temporal] +---*/ + +// Tolerate implementation variance by expecting consistency without being prescriptive. +// TODO: can we change tests to be less reliant on CLDR formats while still testing that +// Temporal and Intl are behaving as expected? +const usDayPeriodSpace = + new Intl.DateTimeFormat("en-US", { timeStyle: "short" }) + .formatToParts(0) + .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || ""; +const usDateRangeSeparator = new Intl.DateTimeFormat("en-US", { dateStyle: "short" }) + .formatRangeToParts(1 * 86400 * 1000, 366 * 86400 * 1000) + .find((part) => part.type === "literal" && part.source === "shared").value; +const deDateRangeSeparator = new Intl.DateTimeFormat("de-AT", { dateStyle: "short" }) + .formatRangeToParts(1 * 86400 * 1000, 366 * 86400 * 1000) + .find((part) => part.type === "literal" && part.source === "shared").value; + +// Workarounds for https:// unicode-org.atlassian.net/browse/CLDR-16243 +const deMonthDayRangeSeparator = new Intl.DateTimeFormat("de-AT", { month: "numeric", day: "numeric" }) + .formatRangeToParts(1 * 86400 * 1000, 90 * 86400 * 1000) + .find((part) => part.type === "literal" && part.source === "shared").value; +const deMonthYearSeparator = new Intl.DateTimeFormat("de-AT", { year: "numeric", month: "numeric" }) + .formatToParts(0) + .find((part) => part.type === "literal").value; +const deMonthInYearMonthHasLeadingZero = new Intl.DateTimeFormat("de-AT", { + year: "numeric", + month: "numeric" +}) + .formatToParts(new Date(2000, 3, 1)) + .find((part) => part.type === "month") + .value.startsWith("0"); + +// should return an Array +assert(Array.isArray(Intl.DateTimeFormat.supportedLocalesOf())); +var onlyOnce = (value) => { + var obj = { + calls: 0, + toString() { + if (++this.calls > 1) throw new RangeError("prop read twice"); + return value; + } + }; + return obj; +}; +var optionsAT = { timeZone: onlyOnce("Europe/Vienna") }; +var optionsUS = { + calls: 0, + value: "America/New_York", + get timeZone() { + if (++this.calls > 1) throw new RangeError("prop read twice"); + return this.value; + }, + set timeZone(val) { + this.value = val; + } +}; +var localesAT = ["de-AT"]; +var us = new Intl.DateTimeFormat("en-US", optionsUS); +var at = new Intl.DateTimeFormat(localesAT, optionsAT); +optionsAT.timeZone = { + toString: () => "Bogus/Time-Zone", + toJSON: () => "Bogus/Time-Zone" +}; +optionsUS.timeZone = "Bogus/Time-Zone"; +var us2 = new Intl.DateTimeFormat("en-US"); +var at2 = new Intl.DateTimeFormat(localesAT); +localesAT[0] = ["invalid locale"]; +var usCalendar = us.resolvedOptions().calendar; +var atCalendar = at.resolvedOptions().calendar; +var t1 = "1976-11-18T14:23:30+00:00[UTC]"; +var t2 = "2020-02-20T15:44:56-05:00[America/New_York]"; +var start = new Date("1922-12-30"); +var end = new Date("1991-12-26"); + +// should work for Instant +assert.sameValue(us.format(Temporal.Instant.from(t1)), `11/18/1976, 9:23:30${usDayPeriodSpace}AM`); +assert.sameValue(at.format(Temporal.Instant.from(t1)), "18.11.1976, 15:23:30"); + +// should work for DateTime +assert.sameValue(us.format(Temporal.PlainDateTime.from(t1)), `11/18/1976, 2:23:30${usDayPeriodSpace}PM`); +assert.sameValue(at.format(Temporal.PlainDateTime.from(t1)), "18.11.1976, 14:23:30"); + +// should work for Time +assert.sameValue(us.format(Temporal.PlainTime.from(t1)), `2:23:30${usDayPeriodSpace}PM`); +assert.sameValue(at.format(Temporal.PlainTime.from(t1)), "14:23:30"); + +// should work for Date +assert.sameValue(us.format(Temporal.PlainDate.from(t1)), "11/18/1976"); +assert.sameValue(at.format(Temporal.PlainDate.from(t1)), "18.11.1976"); + +// should work for YearMonth +var t = Temporal.PlainDate.from(t1); +assert.sameValue(us.format(t.withCalendar(usCalendar).toPlainYearMonth()), "11/1976"); +assert.sameValue(at.format(t.withCalendar(atCalendar).toPlainYearMonth()), `11${deMonthYearSeparator}1976`); + +// should work for MonthDay +var t = Temporal.PlainDate.from(t1); +assert.sameValue(us.format(t.withCalendar(usCalendar).toPlainMonthDay()), "11/18"); +assert.sameValue(at.format(t.withCalendar(atCalendar).toPlainMonthDay()), "18.11."); + +// should not break legacy Date +assert.sameValue(us.format(start), "12/29/1922"); +assert.sameValue(at.format(start), "30.12.1922"); + +// should work for Instant +assert.deepEqual(us.formatToParts(Temporal.Instant.from(t2)), [ + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "/" + }, + { + type: "day", + value: "20" + }, + { + type: "literal", + value: "/" + }, + { + type: "year", + value: "2020" + }, + { + type: "literal", + value: ", " + }, + { + type: "hour", + value: "3" + }, + { + type: "literal", + value: ":" + }, + { + type: "minute", + value: "44" + }, + { + type: "literal", + value: ":" + }, + { + type: "second", + value: "56" + }, + { + type: "literal", + value: usDayPeriodSpace + }, + { + type: "dayPeriod", + value: "PM" + } +]); +assert.deepEqual(at.formatToParts(Temporal.Instant.from(t2)), [ + { + type: "day", + value: "20" + }, + { + type: "literal", + value: "." + }, + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "." + }, + { + type: "year", + value: "2020" + }, + { + type: "literal", + value: ", " + }, + { + type: "hour", + value: "21" + }, + { + type: "literal", + value: ":" + }, + { + type: "minute", + value: "44" + }, + { + type: "literal", + value: ":" + }, + { + type: "second", + value: "56" + } +]); +// should work for DateTime +assert.deepEqual(us.formatToParts(Temporal.PlainDateTime.from(t2)), [ + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "/" + }, + { + type: "day", + value: "20" + }, + { + type: "literal", + value: "/" + }, + { + type: "year", + value: "2020" + }, + { + type: "literal", + value: ", " + }, + { + type: "hour", + value: "3" + }, + { + type: "literal", + value: ":" + }, + { + type: "minute", + value: "44" + }, + { + type: "literal", + value: ":" + }, + { + type: "second", + value: "56" + }, + { + type: "literal", + value: usDayPeriodSpace + }, + { + type: "dayPeriod", + value: "PM" + } +]); +assert.deepEqual(at.formatToParts(Temporal.PlainDateTime.from(t2)), [ + { + type: "day", + value: "20" + }, + { + type: "literal", + value: "." + }, + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "." + }, + { + type: "year", + value: "2020" + }, + { + type: "literal", + value: ", " + }, + { + type: "hour", + value: "15" + }, + { + type: "literal", + value: ":" + }, + { + type: "minute", + value: "44" + }, + { + type: "literal", + value: ":" + }, + { + type: "second", + value: "56" + } +]); +// should work for Time +assert.deepEqual(us.formatToParts(Temporal.PlainTime.from(t2)), [ + { + type: "hour", + value: "3" + }, + { + type: "literal", + value: ":" + }, + { + type: "minute", + value: "44" + }, + { + type: "literal", + value: ":" + }, + { + type: "second", + value: "56" + }, + { + type: "literal", + value: usDayPeriodSpace + }, + { + type: "dayPeriod", + value: "PM" + } +]); +assert.deepEqual(at.formatToParts(Temporal.PlainTime.from(t2)), [ + { + type: "hour", + value: "15" + }, + { + type: "literal", + value: ":" + }, + { + type: "minute", + value: "44" + }, + { + type: "literal", + value: ":" + }, + { + type: "second", + value: "56" + } +]); +// should work for Date +assert.deepEqual(us.formatToParts(Temporal.PlainDate.from(t2)), [ + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "/" + }, + { + type: "day", + value: "20" + }, + { + type: "literal", + value: "/" + }, + { + type: "year", + value: "2020" + } +]); +assert.deepEqual(at.formatToParts(Temporal.PlainDate.from(t2)), [ + { + type: "day", + value: "20" + }, + { + type: "literal", + value: "." + }, + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "." + }, + { + type: "year", + value: "2020" + } +]); +// should work for YearMonth +var t = Temporal.PlainDate.from(t2); +assert.deepEqual(us.formatToParts(t.withCalendar(usCalendar).toPlainYearMonth()), [ + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "/" + }, + { + type: "year", + value: "2020" + } +]); +assert.deepEqual(at.formatToParts(t.withCalendar(atCalendar).toPlainYearMonth()), [ + { + type: "month", + value: deMonthInYearMonthHasLeadingZero ? "02" : "2" + }, + { + type: "literal", + value: deMonthYearSeparator + }, + { + type: "year", + value: "2020" + } +]); +// should work for MonthDay +var t = Temporal.PlainDate.from(t2); +assert.deepEqual(us.formatToParts(t.withCalendar(usCalendar).toPlainMonthDay()), [ + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "/" + }, + { + type: "day", + value: "20" + } +]); +assert.deepEqual(at.formatToParts(t.withCalendar(atCalendar).toPlainMonthDay()), [ + { + type: "day", + value: "20" + }, + { + type: "literal", + value: "." + }, + { + type: "month", + value: "2" + }, + { + type: "literal", + value: "." + } +]); +// should not break legacy Date +assert.deepEqual(us.formatToParts(end), [ + { + type: "month", + value: "12" + }, + { + type: "literal", + value: "/" + }, + { + type: "day", + value: "25" + }, + { + type: "literal", + value: "/" + }, + { + type: "year", + value: "1991" + } +]); +assert.deepEqual(at.formatToParts(end), [ + { + type: "day", + value: "26" + }, + { + type: "literal", + value: "." + }, + { + type: "month", + value: "12" + }, + { + type: "literal", + value: "." + }, + { + type: "year", + value: "1991" + } +]); +// formatRange +// should work for Instant +assert.sameValue( + us.formatRange(Temporal.Instant.from(t1), Temporal.Instant.from(t2)), + `11/18/1976, 9:23:30${usDayPeriodSpace}AM${usDateRangeSeparator}2/20/2020, 3:44:56${usDayPeriodSpace}PM` +); +assert.sameValue( + at.formatRange(Temporal.Instant.from(t1), Temporal.Instant.from(t2)), + `18.11.1976, 15:23:30${deDateRangeSeparator}20.2.2020, 21:44:56` +); + +// should work for DateTime +assert.sameValue( + us.formatRange(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)), + `11/18/1976, 2:23:30${usDayPeriodSpace}PM${usDateRangeSeparator}2/20/2020, 3:44:56${usDayPeriodSpace}PM` +); +assert.sameValue( + at.formatRange(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)), + `18.11.1976, 14:23:30${deDateRangeSeparator}20.2.2020, 15:44:56` +); + +// should work for Time +assert.sameValue( + us.formatRange(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)), + `2:23:30${usDayPeriodSpace}PM${usDateRangeSeparator}3:44:56${usDayPeriodSpace}PM` +); +assert.sameValue( + at.formatRange(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)), + `14:23:30${deDateRangeSeparator}15:44:56` +); + +// should work for Date +assert.sameValue( + us.formatRange(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)), + `11/18/1976${usDateRangeSeparator}2/20/2020` +); +assert.sameValue( + at.formatRange(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)), + `18.11.1976${deDateRangeSeparator}20.02.2020` +); + +// should work for YearMonth +var date1 = Temporal.PlainDate.from(t1); +var date2 = Temporal.PlainDate.from(t2); +assert.sameValue( + us.formatRange(date1.withCalendar(usCalendar).toPlainYearMonth(), date2.withCalendar(usCalendar).toPlainYearMonth()), + `11/1976${usDateRangeSeparator}2/2020` +); +assert.sameValue( + at.formatRange(date1.withCalendar(atCalendar).toPlainYearMonth(), date2.withCalendar(atCalendar).toPlainYearMonth()), + `11${deMonthYearSeparator}1976${deDateRangeSeparator}02${deMonthYearSeparator}2020` +); + +// should work for MonthDay +var date1 = Temporal.PlainDate.from(t1); +var date2 = Temporal.PlainDate.from(t2); +assert.sameValue( + us.formatRange(date2.withCalendar(usCalendar).toPlainMonthDay(), date1.withCalendar(usCalendar).toPlainMonthDay()), + `2/20${usDateRangeSeparator}11/18` +); +assert.sameValue( + at.formatRange(date2.withCalendar(atCalendar).toPlainMonthDay(), date1.withCalendar(atCalendar).toPlainMonthDay()), + `20.02${deMonthDayRangeSeparator}18.11.` +); + +// should not break legacy Date +assert.sameValue(us.formatRange(start, end), `12/29/1922${usDateRangeSeparator}12/25/1991`); +assert.sameValue(at.formatRange(start, end), `30.12.1922${deDateRangeSeparator}26.12.1991`); + +// should throw a RangeError when called with different calendars +assert.throws(RangeError, () => + us.formatRange(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2).withCalendar("japanese")) +); +assert.throws(RangeError, () => + us.formatRange(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2).withCalendar("japanese")) +); + +// formatRangeToParts +// should work for Instant +assert.deepEqual(us.formatRangeToParts(Temporal.Instant.from(t1), Temporal.Instant.from(t2)), [ + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "day", + value: "18", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: ", ", + source: "startRange" + }, + { + type: "hour", + value: "9", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "minute", + value: "23", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "second", + value: "30", + source: "startRange" + }, + { + type: "literal", + value: usDayPeriodSpace, + source: "startRange" + }, + { + type: "dayPeriod", + value: "AM", + source: "startRange" + }, + { + type: "literal", + value: usDateRangeSeparator, + source: "shared" + }, + { + type: "month", + value: "2", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "day", + value: "20", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + }, + { + type: "literal", + value: ", ", + source: "endRange" + }, + { + type: "hour", + value: "3", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "minute", + value: "44", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "second", + value: "56", + source: "endRange" + }, + { + type: "literal", + value: usDayPeriodSpace, + source: "endRange" + }, + { + type: "dayPeriod", + value: "PM", + source: "endRange" + } +]); +assert.deepEqual(at.formatRangeToParts(Temporal.Instant.from(t1), Temporal.Instant.from(t2)), [ + { + type: "day", + value: "18", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: ", ", + source: "startRange" + }, + { + type: "hour", + value: "15", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "minute", + value: "23", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "second", + value: "30", + source: "startRange" + }, + { + type: "literal", + value: deDateRangeSeparator, + source: "shared" + }, + { + type: "day", + value: "20", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "month", + value: "2", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + }, + { + type: "literal", + value: ", ", + source: "endRange" + }, + { + type: "hour", + value: "21", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "minute", + value: "44", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "second", + value: "56", + source: "endRange" + } +]); +// should work for DateTime +assert.deepEqual(us.formatRangeToParts(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)), [ + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "day", + value: "18", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: ", ", + source: "startRange" + }, + { + type: "hour", + value: "2", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "minute", + value: "23", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "second", + value: "30", + source: "startRange" + }, + { + type: "literal", + value: usDayPeriodSpace, + source: "startRange" + }, + { + type: "dayPeriod", + value: "PM", + source: "startRange" + }, + { + type: "literal", + value: usDateRangeSeparator, + source: "shared" + }, + { + type: "month", + value: "2", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "day", + value: "20", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + }, + { + type: "literal", + value: ", ", + source: "endRange" + }, + { + type: "hour", + value: "3", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "minute", + value: "44", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "second", + value: "56", + source: "endRange" + }, + { + type: "literal", + value: usDayPeriodSpace, + source: "endRange" + }, + { + type: "dayPeriod", + value: "PM", + source: "endRange" + } +]); +assert.deepEqual(at.formatRangeToParts(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)), [ + { + type: "day", + value: "18", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: ", ", + source: "startRange" + }, + { + type: "hour", + value: "14", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "minute", + value: "23", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "second", + value: "30", + source: "startRange" + }, + { + type: "literal", + value: deDateRangeSeparator, + source: "shared" + }, + { + type: "day", + value: "20", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "month", + value: "2", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + }, + { + type: "literal", + value: ", ", + source: "endRange" + }, + { + type: "hour", + value: "15", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "minute", + value: "44", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "second", + value: "56", + source: "endRange" + } +]); +// should work for Time +assert.deepEqual(us.formatRangeToParts(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)), [ + { + type: "hour", + value: "2", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "minute", + value: "23", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "second", + value: "30", + source: "startRange" + }, + { + type: "literal", + value: usDayPeriodSpace, + source: "startRange" + }, + { + type: "dayPeriod", + value: "PM", + source: "startRange" + }, + { + type: "literal", + value: usDateRangeSeparator, + source: "shared" + }, + { + type: "hour", + value: "3", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "minute", + value: "44", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "second", + value: "56", + source: "endRange" + }, + { + type: "literal", + value: usDayPeriodSpace, + source: "endRange" + }, + { + type: "dayPeriod", + value: "PM", + source: "endRange" + } +]); +assert.deepEqual(at.formatRangeToParts(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)), [ + { + type: "hour", + value: "14", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "minute", + value: "23", + source: "startRange" + }, + { + type: "literal", + value: ":", + source: "startRange" + }, + { + type: "second", + value: "30", + source: "startRange" + }, + { + type: "literal", + value: deDateRangeSeparator, + source: "shared" + }, + { + type: "hour", + value: "15", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "minute", + value: "44", + source: "endRange" + }, + { + type: "literal", + value: ":", + source: "endRange" + }, + { + type: "second", + value: "56", + source: "endRange" + } +]); +// should work for Date +assert.deepEqual(us.formatRangeToParts(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)), [ + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "day", + value: "18", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: usDateRangeSeparator, + source: "shared" + }, + { + type: "month", + value: "2", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "day", + value: "20", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + } +]); +assert.deepEqual(at.formatRangeToParts(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)), [ + { + type: "day", + value: "18", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: deDateRangeSeparator, + source: "shared" + }, + { + type: "day", + value: "20", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "month", + value: "02", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + } +]); +// should work for YearMonth +var date1 = Temporal.PlainDate.from(t1); +var date2 = Temporal.PlainDate.from(t2); +assert.deepEqual( + us.formatRangeToParts( + date1.withCalendar(usCalendar).toPlainYearMonth(), + date2.withCalendar(usCalendar).toPlainYearMonth() + ), + [ + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: usDateRangeSeparator, + source: "shared" + }, + { + type: "month", + value: "2", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + } + ] +); +assert.deepEqual( + at.formatRangeToParts( + date1.withCalendar(atCalendar).toPlainYearMonth(), + date2.withCalendar(atCalendar).toPlainYearMonth() + ), + [ + { + type: "month", + value: "11", + source: "startRange" + }, + { + type: "literal", + value: deMonthYearSeparator, + source: "startRange" + }, + { + type: "year", + value: "1976", + source: "startRange" + }, + { + type: "literal", + value: deDateRangeSeparator, + source: "shared" + }, + { + type: "month", + value: "02", + source: "endRange" + }, + { + type: "literal", + value: deMonthYearSeparator, + source: "endRange" + }, + { + type: "year", + value: "2020", + source: "endRange" + } + ] +); +// should work for MonthDay +var date1 = Temporal.PlainDate.from(t1); +var date2 = Temporal.PlainDate.from(t2); +assert.deepEqual( + us.formatRangeToParts( + date2.withCalendar(usCalendar).toPlainMonthDay(), + date1.withCalendar(usCalendar).toPlainMonthDay() + ), + [ + { + type: "month", + value: "2", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "day", + value: "20", + source: "startRange" + }, + { + type: "literal", + value: usDateRangeSeparator, + source: "shared" + }, + { + type: "month", + value: "11", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "day", + value: "18", + source: "endRange" + } + ] +); +assert.deepEqual( + at.formatRangeToParts( + date2.withCalendar(atCalendar).toPlainMonthDay(), + date1.withCalendar(atCalendar).toPlainMonthDay() + ), + [ + { + type: "day", + value: "20", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "month", + value: "02", + source: "startRange" + }, + { + type: "literal", + value: deMonthDayRangeSeparator, + source: "shared" + }, + { + type: "day", + value: "18", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "month", + value: "11", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "shared" + } + ] +); +// should not break legacy Date +assert.deepEqual(us.formatRangeToParts(start, end), [ + { + type: "month", + value: "12", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "day", + value: "29", + source: "startRange" + }, + { + type: "literal", + value: "/", + source: "startRange" + }, + { + type: "year", + value: "1922", + source: "startRange" + }, + { + type: "literal", + value: usDateRangeSeparator, + source: "shared" + }, + { + type: "month", + value: "12", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "day", + value: "25", + source: "endRange" + }, + { + type: "literal", + value: "/", + source: "endRange" + }, + { + type: "year", + value: "1991", + source: "endRange" + } +]); +assert.deepEqual(at.formatRangeToParts(start, end), [ + { + type: "day", + value: "30", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "month", + value: "12", + source: "startRange" + }, + { + type: "literal", + value: ".", + source: "startRange" + }, + { + type: "year", + value: "1922", + source: "startRange" + }, + { + type: "literal", + value: deDateRangeSeparator, + source: "shared" + }, + { + type: "day", + value: "26", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "month", + value: "12", + source: "endRange" + }, + { + type: "literal", + value: ".", + source: "endRange" + }, + { + type: "year", + value: "1991", + source: "endRange" + } +]); +// should throw a TypeError when called with dissimilar types +assert.throws(TypeError, () => at.formatRangeToParts(Temporal.Instant.from(t1), Temporal.PlainDateTime.from(t2))); +// should throw a RangeError when called with different calendars +assert.throws(RangeError, () => + at.formatRangeToParts(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2).withCalendar("japanese")) +); +assert.throws(RangeError, () => + at.formatRangeToParts(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2).withCalendar("japanese")) +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/date-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/date-toLocaleString.js new file mode 100644 index 0000000000..973705f4ac --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/date-toLocaleString.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: date.toLocaleString() +features: [Temporal] +---*/ + +function maybeGetWeekdayOnlyFormat() { + const fmt = new Intl.DateTimeFormat('en', { weekday: 'long', timeZone: 'Europe/Vienna' }); + if ( + ['era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName'].some( + (prop) => prop in fmt.resolvedOptions() + ) + ) { + //no weekday-only format available + return null; + } + return fmt; +} + +var date = Temporal.PlainDate.from("1976-11-18T15:23:30"); +assert.sameValue(`${ date.toLocaleString("en-US", { timeZone: "America/New_York" }) }`, "11/18/1976"); +assert.sameValue(`${ date.toLocaleString("de-AT", { timeZone: "Europe/Vienna" }) }`, "18.11.1976"); +var fmt = maybeGetWeekdayOnlyFormat(); +if (fmt) assert.sameValue(fmt.format(date), "Thursday"); + +// should ignore units not in the data type +assert.sameValue(date.toLocaleString("en-US", { timeZoneName: "long" }), "11/18/1976"); +assert.sameValue(date.toLocaleString("en-US", { hour: "numeric" }), "11/18/1976"); +assert.sameValue(date.toLocaleString("en-US", { minute: "numeric" }), "11/18/1976"); +assert.sameValue(date.toLocaleString("en-US", { second: "numeric" }), "11/18/1976"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/datetime-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/datetime-toLocaleString.js new file mode 100644 index 0000000000..3cdced3faa --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/datetime-toLocaleString.js @@ -0,0 +1,54 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: datetime.toLocaleString() +features: [Temporal] +---*/ + +// Tolerate implementation variance by expecting consistency without being prescriptive. +// TODO: can we change tests to be less reliant on CLDR formats while still testing that +// Temporal and Intl are behaving as expected? +const usDayPeriodSpace = + new Intl.DateTimeFormat("en-US", { timeStyle: "short" }) + .formatToParts(0) + .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || ""; + +function maybeGetWeekdayOnlyFormat() { + const fmt = new Intl.DateTimeFormat("en-US", { weekday: "long", timeZone: "Europe/Vienna" }); + if ( + ["era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"].some( + (prop) => prop in fmt.resolvedOptions() + ) + ) { + // no weekday-only format available + return null; + } + return fmt; +} + +var datetime = Temporal.PlainDateTime.from("1976-11-18T15:23:30"); +assert.sameValue( + `${datetime.toLocaleString("en-US", { timeZone: "America/New_York" })}`, + `11/18/1976, 3:23:30${usDayPeriodSpace}PM` +); +assert.sameValue(`${datetime.toLocaleString("de-AT", { timeZone: "Europe/Vienna" })}`, "18.11.1976, 15:23:30"); +var fmt = maybeGetWeekdayOnlyFormat(); +if (fmt) assert.sameValue(fmt.format(datetime), "Thursday"); + +// should ignore units not in the data type +assert.sameValue( + datetime.toLocaleString("en-US", { timeZoneName: "long" }), + `11/18/1976, 3:23:30${usDayPeriodSpace}PM` +); + +// should use compatible disambiguation option +var dstStart = new Temporal.PlainDateTime(2020, 3, 8, 2, 30); +assert.sameValue( + `${dstStart.toLocaleString("en-US", { timeZone: "America/Los_Angeles" })}`, + `3/8/2020, 3:30:00${usDayPeriodSpace}AM` +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/duration-arithmetic-dst.js b/js/src/tests/test262/staging/Intl402/Temporal/old/duration-arithmetic-dst.js new file mode 100644 index 0000000000..08e9c8b428 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/duration-arithmetic-dst.js @@ -0,0 +1,76 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: > + Various DST arithmetic tests that it's impractical to do without a time zone + database in the implementation +features: [Temporal] +---*/ + +// Tests for arithmetic that start inside a repeated hour, and end in a skipped +// hour. We have TemporalHelpers.springForwardFallBackTimeZone which is +// sufficient to test this for Temporal.Duration.prototype.add, and +// Temporal.Duration.prototype.round, but it's impractical to replicate all the +// TZDB data for testing it with other methods such as subtract() where we need +// to calculate to the _next_ transition + +var skippedHourDay = Temporal.ZonedDateTime.from("2019-03-10T00:00[America/Vancouver]"); +var repeatedHourDay = Temporal.ZonedDateTime.from("2019-11-03T00:00[America/Vancouver]"); +var inRepeatedHour = Temporal.ZonedDateTime.from("2019-11-03T01:00-07:00[America/Vancouver]"); + +// subtract() + +var oneDay = new Temporal.Duration(0, 0, 0, 1); +assert.sameValue(`${ Temporal.Duration.from({ + days: 127, + hours: 1 +}).subtract(oneDay, { relativeTo: inRepeatedHour }) }`, "P126DT1H"); +var hours24 = new Temporal.Duration(0, 0, 0, 0, 24); +assert.sameValue(`${ Temporal.Duration.from({ + days: 127, + hours: 1 +}).subtract(hours24, { relativeTo: inRepeatedHour }) }`, "P126D"); + +// total() +var totalDays = Temporal.Duration.from({ + days: 126, + hours: 1 +}).total({ + unit: "days", + relativeTo: inRepeatedHour +}); +assert(Math.abs(totalDays - (126 + 1 / 23)) < Number.EPSILON); +assert.sameValue(Temporal.Duration.from({ + days: 126, + hours: 1 +}).total({ + unit: "hours", + relativeTo: inRepeatedHour +}), 3026); + +// Tests for casting relativeTo to ZonedDateTime when possible: +// Without a TZDB, it's not possible to get a ZonedDateTime with DST from a +// string. + +assert.sameValue( + `${ oneDay.add(hours24, { relativeTo: "2019-11-02T00:00[America/Vancouver]" }) }`, + "P1DT24H" +); +var hours25 = new Temporal.Duration(0, 0, 0, 0, 25); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: "2019-11-03T00:00[America/Vancouver]" +}) }`, "P1D"); +assert.sameValue( + `${ oneDay.subtract(hours24, { relativeTo: "2019-11-03T00:00[America/Vancouver]" }) }`, + "PT1H" +); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: "2019-11-03T00:00[America/Vancouver]" +}), 25); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/hebrew-leap-months.js b/js/src/tests/test262/staging/Intl402/Temporal/old/hebrew-leap-months.js new file mode 100644 index 0000000000..bdd410c4cf --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/hebrew-leap-months.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Hebrew leap months +features: [Temporal] +---*/ + +// Valid leap month: Adar I 5779 +var date = Temporal.PlainDate.from({ + year: 5779, + month: 6, + day: 1, + calendar: "hebrew" +}); +assert.sameValue(date.month, 6); +assert.sameValue(date.monthCode, "M05L"); +assert.sameValue(date.day, 1); +date = Temporal.PlainDate.from({ + year: 5779, + monthCode: "M05L", + day: 1, + calendar: "hebrew" +}); +assert.sameValue(date.month, 6); +assert.sameValue(date.monthCode, "M05L"); +assert.sameValue(date.day, 1); + +// Creating dates in later months in a leap year +var date = Temporal.PlainDate.from({ + year: 5779, + month: 7, + day: 1, + calendar: "hebrew" +}); +assert.sameValue(date.month, 7); +assert.sameValue(date.monthCode, "M06"); +assert.sameValue(date.day, 1); +date = Temporal.PlainDate.from({ + year: 5779, + monthCode: "M06", + day: 1, + calendar: "hebrew" +}); +assert.sameValue(date.month, 7); +assert.sameValue(date.monthCode, "M06"); +assert.sameValue(date.day, 1); + +// Invalid leap months: e.g. M02L +for (var i = 1; i <= 12; i++) { + if (i === 5) + continue; + assert.throws(RangeError, () => Temporal.PlainDate.from({ + year: 5779, + monthCode: `M${ i.toString().padStart(2, "0") }L`, + day: 1, + calendar: "hebrew" + })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/indian-calendar.js b/js/src/tests/test262/staging/Intl402/Temporal/old/indian-calendar.js new file mode 100644 index 0000000000..1e82b4bc3f --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/indian-calendar.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Indian calendar +features: [Temporal] +---*/ + + +// throws in Node 12 & 14 before 1 CE +var vulnerableToBceBug = new Date("0000-01-01T00:00Z").toLocaleDateString("en-US-u-ca-indian", { timeZone: "UTC" }) !== "10/11/-79 Saka"; +if (vulnerableToBceBug) { + assert.throws(RangeError, () => Temporal.PlainDate.from("0000-01-01").withCalendar("indian").day); +} + +// handles leap days +var leapYearFirstDay = Temporal.PlainDate.from("2004-03-21[u-ca=indian]"); +assert.sameValue(leapYearFirstDay.year, 2004 - 78); +assert.sameValue(leapYearFirstDay.month, 1); +assert.sameValue(leapYearFirstDay.day, 1); +var leapYearLastDay = leapYearFirstDay.with({ day: 31 }); +assert.sameValue(leapYearLastDay.year, 2004 - 78); +assert.sameValue(leapYearLastDay.month, 1); +assert.sameValue(leapYearLastDay.day, 31); + +// handles non-leap years +var nonLeapYearFirstDay = Temporal.PlainDate.from("2005-03-22[u-ca=indian]"); +assert.sameValue(nonLeapYearFirstDay.year, 2005 - 78); +assert.sameValue(nonLeapYearFirstDay.month, 1); +assert.sameValue(nonLeapYearFirstDay.day, 1); +var leapYearLastDay = nonLeapYearFirstDay.with({ day: 31 }); +assert.sameValue(leapYearLastDay.year, 2005 - 78); +assert.sameValue(leapYearLastDay.month, 1); +assert.sameValue(leapYearLastDay.day, 30); +assert.throws(RangeError, () => nonLeapYearFirstDay.with({ day: 31 }, { overflow: "reject" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/instant-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/instant-toLocaleString.js new file mode 100644 index 0000000000..5854d0dcdf --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/instant-toLocaleString.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Instant.toLocaleString() +features: [Temporal] +---*/ + +// Tolerate implementation variance by expecting consistency without being prescriptive. +// TODO: can we change tests to be less reliant on CLDR formats while still testing that +// Temporal and Intl are behaving as expected? +const usDayPeriodSpace = + new Intl.DateTimeFormat("en-US", { timeStyle: "short" }) + .formatToParts(0) + .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || ""; + +function maybeGetWeekdayOnlyFormat() { + const fmt = new Intl.DateTimeFormat("en-US", { weekday: "long", timeZone: "Europe/Vienna" }); + if ( + ["era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"].some( + (prop) => prop in fmt.resolvedOptions() + ) + ) { + // no weekday-only format available + return null; + } + return fmt; +} + +var instant = Temporal.Instant.from("1976-11-18T14:23:30Z"); +assert.sameValue( + `${instant.toLocaleString("en-US", { timeZone: "America/New_York" })}`, + `11/18/1976, 9:23:30${usDayPeriodSpace}AM` +); +assert.sameValue(`${instant.toLocaleString("de-AT", { timeZone: "Europe/Vienna" })}`, "18.11.1976, 15:23:30"); +var fmt = maybeGetWeekdayOnlyFormat(); +if (fmt) assert.sameValue(fmt.format(instant), "Thursday"); + +// outputs timeZoneName if requested +var str = instant.toLocaleString("en-US", { + timeZone: "America/New_York", + timeZoneName: "short" +}); +assert(str.includes("EST")); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/islamic-calendars.js b/js/src/tests/test262/staging/Intl402/Temporal/old/islamic-calendars.js new file mode 100644 index 0000000000..c0726b0cae --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/islamic-calendars.js @@ -0,0 +1,94 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Islamic calendars (note there are 6 variants) +features: [Temporal] +---*/ + +// Test data obtained from ICU + +const tests = [ + { + calendar: "islamic", + inLeapYear: false, + daysInYear: 354, + daysInMonth12: 29, + isoYear: 2023, + isoMonth: 7, + isoDay: 18 + }, + { + calendar: "islamic-umalqura", + inLeapYear: false, + daysInYear: 354, + daysInMonth12: 30, + isoYear: 2023, + isoMonth: 7, + isoDay: 19 + }, + { + calendar: "islamic-civil", + inLeapYear: true, + daysInYear: 355, + daysInMonth12: 30, + isoYear: 2023, + isoMonth: 7, + isoDay: 19 + }, + { + calendar: "islamicc", // deprecated version of islamic-civil + inLeapYear: true, + daysInYear: 355, + daysInMonth12: 30, + isoYear: 2023, + isoMonth: 7, + isoDay: 19 + }, + { + calendar: "islamic-rgsa", + inLeapYear: false, + daysInYear: 354, + daysInMonth12: 29, + isoYear: 2023, + isoMonth: 7, + isoDay: 18 + }, + { + calendar: "islamic-tbla", + inLeapYear: true, + daysInYear: 355, + daysInMonth12: 30, + isoYear: 2023, + isoMonth: 7, + isoDay: 18 + } +]; + +for (const test of tests) { + const { calendar, inLeapYear, daysInYear, daysInMonth12, isoYear, isoMonth, isoDay } = test; + const year = 1445; + const date = Temporal.PlainDate.from({ year, month: 1, day: 1, calendar }); + const isoFields = date.getISOFields(); + assert.sameValue(date.calendarId, calendar); + assert.sameValue(date.year, year); + assert.sameValue(date.month, 1); + assert.sameValue(date.monthCode, "M01"); + assert.sameValue(date.day, 1); + assert.sameValue(date.inLeapYear, inLeapYear); + assert.sameValue(date.daysInYear, daysInYear); + assert.sameValue(date.monthsInYear, 12); + assert.sameValue(date.dayOfYear, 1); + const startOfNextYear = date.with({ year: year + 1 }); + const lastDayOfThisYear = startOfNextYear.subtract({ days: 1 }); + assert.sameValue(lastDayOfThisYear.dayOfYear, daysInYear); + const dateMonth12 = date.with({ month: 12 }); + assert.sameValue(dateMonth12.daysInMonth, daysInMonth12); + assert.sameValue(isoYear, isoFields.isoYear); + assert.sameValue(isoMonth, isoFields.isoMonth); + assert.sameValue(isoDay, isoFields.isoDay); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/japanese-era.js b/js/src/tests/test262/staging/Intl402/Temporal/old/japanese-era.js new file mode 100644 index 0000000000..47e16fbcb5 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/japanese-era.js @@ -0,0 +1,140 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Japanese eras +features: [Temporal] +---*/ + + +// Reiwa (2019-) +var date = Temporal.PlainDate.from({ + era: "reiwa", + eraYear: 2, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "2020-01-01[u-ca=japanese]"); + +// Heisei (1989-2019) +var date = Temporal.PlainDate.from({ + era: "heisei", + eraYear: 2, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1990-01-01[u-ca=japanese]"); + +// Showa (1926-1989) +var date = Temporal.PlainDate.from({ + era: "showa", + eraYear: 2, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1927-01-01[u-ca=japanese]"); + +// Taisho (1912-1926) +var date = Temporal.PlainDate.from({ + era: "taisho", + eraYear: 2, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1913-01-01[u-ca=japanese]"); + +// Meiji (1868-1912) +var date = Temporal.PlainDate.from({ + era: "meiji", + eraYear: 2, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1869-01-01[u-ca=japanese]"); + +// Dates in same year before Japanese era starts will resolve to previous era +var date = Temporal.PlainDate.from({ + era: "reiwa", + eraYear: 1, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "2019-01-01[u-ca=japanese]"); +assert.sameValue(date.era, "heisei"); +assert.sameValue(date.eraYear, 31); + +date = Temporal.PlainDate.from({ + era: "heisei", + eraYear: 1, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1989-01-01[u-ca=japanese]"); +assert.sameValue(date.era, "showa"); +assert.sameValue(date.eraYear, 64); +date = Temporal.PlainDate.from({ + era: "showa", + eraYear: 1, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1926-01-01[u-ca=japanese]"); +assert.sameValue(date.era, "taisho"); +assert.sameValue(date.eraYear, 15); + +date = Temporal.PlainDate.from({ + era: "taisho", + eraYear: 1, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1912-01-01[u-ca=japanese]"); +assert.sameValue(date.era, "meiji"); +assert.sameValue(date.eraYear, 45); + +date = Temporal.PlainDate.from({ + era: "meiji", + eraYear: 1, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${ date }`, "1868-01-01[u-ca=japanese]"); +assert.sameValue(date.era, "ce"); +assert.sameValue(date.eraYear, 1868); + +// Verify that CE and BCE eras (before Meiji) are recognized +date = Temporal.PlainDate.from({ + era: "ce", + eraYear: 1000, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${date}`, "1000-01-01[u-ca=japanese]"); +assert.sameValue(date.era, "ce"); +assert.sameValue(date.eraYear, 1000); + +date = Temporal.PlainDate.from({ + era: "bce", + eraYear: 1, + month: 1, + day: 1, + calendar: "japanese" +}); +assert.sameValue(`${date}`, "0000-01-01[u-ca=japanese]"); +assert.sameValue(date.era, "bce"); +assert.sameValue(date.eraYear, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/monthday-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/monthday-toLocaleString.js new file mode 100644 index 0000000000..a0452c1ab1 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/monthday-toLocaleString.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: monthday.toLocaleString() +features: [Temporal] +---*/ + +var calendar = new Intl.DateTimeFormat("en-US").resolvedOptions().calendar; +var monthday = Temporal.PlainMonthDay.from({ + monthCode: "M11", + day: 18, + calendar +}); +assert.sameValue(`${ monthday.toLocaleString("en-US", { timeZone: "America/New_York" }) }`, "11/18"); +assert.sameValue(`${ monthday.toLocaleString("de-AT", { + timeZone: "Europe/Vienna", + calendar +}) }`, "18.11."); + +// should ignore units not in the data type +assert.sameValue(monthday.toLocaleString("en-US", { timeZoneName: "long" }), "11/18"); +assert.sameValue(monthday.toLocaleString("en-US", { year: "numeric" }), "11/18"); +assert.sameValue(monthday.toLocaleString("en-US", { hour: "numeric" }), "11/18"); +assert.sameValue(monthday.toLocaleString("en-US", { minute: "numeric" }), "11/18"); +assert.sameValue(monthday.toLocaleString("en-US", { second: "numeric" }), "11/18"); +assert.sameValue(monthday.toLocaleString("en-US", { weekday: "long" }), "11/18"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/non-iso-calendars.js b/js/src/tests/test262/staging/Intl402/Temporal/old/non-iso-calendars.js new file mode 100644 index 0000000000..a4f1a2908f --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/non-iso-calendars.js @@ -0,0 +1,1380 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Non-ISO Calendars +features: [Temporal, Array.prototype.includes] +---*/ + +var testChineseData = new Date("2001-02-01T00:00Z").toLocaleString("en-US-u-ca-chinese", { + day: "numeric", + month: "numeric", + year: "numeric", + era: "short", + timeZone: "UTC" +}); +var hasOutdatedChineseIcuData = !testChineseData.endsWith("2001"); + +// verify that Intl.DateTimeFormat.formatToParts output matches snapshot data +var getLocalizedDates = isoString => { + var calendars = [ + "iso8601", + "buddhist", + "chinese", + "coptic", + "dangi", + "ethioaa", + "ethiopic", + "gregory", + "hebrew", + "indian", + "islamic", + "islamic-umalqura", + "islamic-tbla", + "islamic-civil", + "islamic-rgsa", + "islamicc", + "japanese", + "persian", + "roc" + ]; + var date = new Date(isoString); + return calendars.map(id => `${ id }: ${ date.toLocaleDateString(`en-US-u-ca-${ id }`, { timeZone: "UTC" }) }`).join("\n"); +}; +var year2000Content = getLocalizedDates("2000-01-01T00:00Z"); +var year1Content = getLocalizedDates("0001-01-01T00:00Z"); +var year2000Snapshot = "iso8601: 1/1/2000\n" + "buddhist: 1/1/2543 BE\n" + "chinese: 11/25/1999\n" + "coptic: 4/22/1716 ERA1\n" + "dangi: 11/25/1999\n" + "ethioaa: 4/22/7492 ERA0\n" + "ethiopic: 4/22/1992 ERA1\n" + "gregory: 1/1/2000\n" + "hebrew: 23 Tevet 5760\n" + "indian: 10/11/1921 Saka\n" + "islamic: 9/25/1420 AH\n" + "islamic-umalqura: 9/24/1420 AH\n" + "islamic-tbla: 9/25/1420 AH\n" + "islamic-civil: 9/24/1420 AH\n" + "islamic-rgsa: 9/25/1420 AH\n" + "islamicc: 9/24/1420 AH\n" + "japanese: 1/1/12 H\n" + "persian: 10/11/1378 AP\n" + "roc: 1/1/89 Minguo"; +assert.sameValue(year2000Content, year2000Snapshot); + +// Several calendars based on the Gregorian calendar use Julian dates (not +// proleptic Gregorian dates) before the Gregorian switchover in Oct 1582. See +// https://bugs.chromium.org/p/chromium/issues/detail?id=1173158. The code below +// allows these tests to pass regardless of the bug, while still remaining +// sensitive to other bugs that may crop up. +const yearOneMonthDay = new Map( + ["iso8601", "gregory", "roc", "buddhist", "japanese"].map(calendar => { + hasGregorianSwitchoverBug = new Date("+001001-01-01T00:00Z") + .toLocaleDateString(`en-US-u-ca-${calendar}`, { timeZone: "UTC" }) + .startsWith("12"); + return [calendar, hasGregorianSwitchoverBug ? "1/3" : "1/1"] + })); +var year1Snapshot = + `iso8601: ${yearOneMonthDay.get("iso8601")}/1\n` + + `buddhist: ${yearOneMonthDay.get("buddhist")}/544 BE\n` + + "chinese: 11/21/0\n" + + "coptic: 5/8/284 ERA0\n" + + "dangi: 11/21/0\n" + + "ethioaa: 5/8/5493 ERA0\n" + + "ethiopic: 5/8/5493 ERA0\n" + + `gregory: ${yearOneMonthDay.get("gregory")}/1\n` + + "hebrew: 18 Tevet 3761\n" + + "indian: 10/11/-78 Saka\n" + + "islamic: 5/20/-640 AH\n" + + "islamic-umalqura: 5/18/-640 AH\n" + + "islamic-tbla: 5/19/-640 AH\n" + + "islamic-civil: 5/18/-640 AH\n" + + "islamic-rgsa: 5/20/-640 AH\n" + + "islamicc: 5/18/-640 AH\n" + + `japanese: ${yearOneMonthDay.get("japanese")}/-643 Taika (645\u2013650)\n` + + "persian: 10/11/-621 AP\n" + + `roc: ${yearOneMonthDay.get("roc")}/1911 B.R.O.C.`; +assert.sameValue(year1Content, year1Snapshot); +var fromWithCases = { + iso8601: { + year2000: { + year: 2000, + month: 1, + day: 1 + }, + year1: { + year: 1, + month: 1, + day: 1 + } + }, + buddhist: { + year2000: { + year: 2543, + month: 1, + day: 1, + era: "be" + }, + year1: RangeError + }, + chinese: { + year2000: { + year: 1999, + month: 11, + day: 25 + }, + year1: { + year: 0, + month: 12, + monthCode: "M11", + day: 21 + } + }, + coptic: { + year2000: { + year: 1716, + month: 4, + day: 22, + era: "era1" + }, + year1: { + year: -283, + eraYear: 284, + month: 5, + day: 8, + era: "era0" + } + }, + dangi: { + year2000: { + year: 1999, + month: 11, + day: 25 + }, + year1: { + year: 0, + month: 12, + monthCode: "M11", + day: 21 + } + }, + ethioaa: { + year2000: { + year: 7492, + month: 4, + day: 22, + era: "era0" + }, + year1: { + year: 5493, + month: 5, + day: 8, + era: "era0" + } + }, + ethiopic: { + year2000: { + eraYear: 1992, + year: 7492, + month: 4, + day: 22, + era: "era1" + }, + year1: { + year: 5493, + month: 5, + day: 8, + era: "era0" + } + }, + gregory: { + year2000: { + year: 2000, + month: 1, + day: 1, + era: "ce" + }, + year1: { + year: 1, + month: 1, + day: 1, + era: "ce" + } + }, + hebrew: { + year2000: { + year: 5760, + month: 4, + day: 23 + }, + year1: { + year: 3761, + month: 4, + day: 18 + } + }, + indian: { + year2000: { + year: 1921, + month: 10, + day: 11, + era: "saka" + }, + year1: { + year: -78, + month: 10, + day: 11, + era: "saka" + } + }, + islamic: { + year2000: { + year: 1420, + month: 9, + day: 25, + era: "ah" + }, + year1: { + year: -640, + month: 5, + day: 20, + era: "ah" + } + }, + "islamic-umalqura": { + year2000: { + year: 1420, + month: 9, + day: 24, + era: "ah" + }, + year1: { + year: -640, + month: 5, + day: 18, + era: "ah" + } + }, + "islamic-tbla": { + year2000: { + year: 1420, + month: 9, + day: 25, + era: "ah" + }, + year1: { + year: -640, + month: 5, + day: 19, + era: "ah" + } + }, + "islamic-civil": { + year2000: { + year: 1420, + month: 9, + day: 24, + era: "ah" + }, + year1: { + year: -640, + month: 5, + day: 18, + era: "ah" + } + }, + "islamic-rgsa": { + year2000: { + year: 1420, + month: 9, + day: 25, + era: "ah" + }, + year1: { + year: -640, + month: 5, + day: 20, + era: "ah" + } + }, + islamicc: { + year2000: { + year: 1420, + month: 9, + day: 24, + era: "ah" + }, + year1: { + year: -640, + month: 5, + day: 18, + era: "ah" + } + }, + japanese: { + year2000: { + year: 2000, + eraYear: 12, + month: 1, + day: 1, + era: "heisei" + }, + year1: RangeError + }, + persian: { + year2000: { + year: 1378, + month: 10, + day: 11, + era: "ap" + }, + year1: { + year: -621, + month: 10, + day: 11, + era: "ap" + } + }, + roc: { + year2000: { + year: 89, + month: 1, + day: 1, + era: "minguo" + }, + year1: RangeError + } +}; +var logPerf = false; +var totalNow = 0; +for (var [id, tests] of Object.entries(fromWithCases)) { + var dates = { + year2000: Temporal.PlainDate.from("2000-01-01"), + year1: Temporal.PlainDate.from("0001-01-01") + }; + for (var [name, date] of Object.entries(dates)) { + var values = tests[name]; + var errorExpected = values === RangeError; + if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) { + var now = globalThis.performance ? globalThis.performance.now() : Date.now(); + if (errorExpected) { + assert.throws(RangeError, () => { + var inCal = date.withCalendar(id); + Temporal.PlainDate.from({ + calendar: id, + year: inCal.year, + day: inCal.day, + monthCode: inCal.monthCode + }); + }); + } + var inCal = date.withCalendar(id); + assert.sameValue(`${ name } ${ id } day: ${ inCal.day }`, `${ name } ${ id } day: ${ values.day }`); + if (values.eraYear === undefined && values.era !== undefined) + values.eraYear = values.year; + assert.sameValue(`${ name } ${ id } eraYear: ${ inCal.eraYear }`, `${ name } ${ id } eraYear: ${ values.eraYear }`); + assert.sameValue(`${ name } ${ id } era: ${ inCal.era }`, `${ name } ${ id } era: ${ values.era }`); + assert.sameValue(`${ name } ${ id } year: ${ inCal.year }`, `${ name } ${ id } year: ${ values.year }`); + assert.sameValue(`${ name } ${ id } month: ${ inCal.month }`, `${ name } ${ id } month: ${ values.month }`); + if (values.monthCode === undefined) + values.monthCode = `M${ values.month.toString().padStart(2, "0") }`; + assert.sameValue(`${ name } ${ id } monthCode: ${ inCal.monthCode }`, `${ name } ${ id } monthCode: ${ values.monthCode }`); + if (values.era) { + var dateRoundtrip1 = Temporal.PlainDate.from({ + calendar: id, + eraYear: values.eraYear, + era: values.era, + day: values.day, + monthCode: values.monthCode + }); + assert.sameValue(dateRoundtrip1.toString(), inCal.toString()); + assert.throws(RangeError, () => Temporal.PlainDate.from({ + calendar: id, + eraYear: values.eraYear, + era: values.era, + day: values.day, + monthCode: values.monthCode, + year: values.year + 1 + })); + } + var dateRoundtrip2 = Temporal.PlainDate.from({ + calendar: id, + year: values.year, + day: values.day, + monthCode: values.monthCode + }); + assert.sameValue(dateRoundtrip2.toString(), inCal.toString()); + var dateRoundtrip3 = Temporal.PlainDate.from({ + calendar: id, + year: values.year, + day: values.day, + month: values.month + }); + assert.sameValue(dateRoundtrip3.toString(), inCal.toString()); + var dateRoundtrip4 = Temporal.PlainDate.from({ + calendar: id, + year: values.year, + day: values.day, + monthCode: values.monthCode + }); + assert.sameValue(dateRoundtrip4.toString(), inCal.toString()); + assert.throws(RangeError, () => Temporal.PlainDate.from({ + calendar: id, + day: values.day, + month: values.month === 1 ? 2 : values.month - 1, + monthCode: values.monthCode, + year: values.year + })); + var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now; + totalNow += ms; + if (logPerf) + console.log(`from: ${ id } ${ name }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`); + }; + if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) { + var now = globalThis.performance ? globalThis.performance.now() : Date.now(); + var inCal = date.withCalendar(id); + if (errorExpected) { + assert.throws(RangeError, () => inCal.with({ day: 1 }).year); + } + var afterWithDay = inCal.with({ day: 1 }); + var t = "(after setting day)"; + assert.sameValue(`${ t } year: ${ afterWithDay.year }`, `${ t } year: ${ inCal.year }`); + assert.sameValue(`${ t } month: ${ afterWithDay.month }`, `${ t } month: ${ inCal.month }`); + assert.sameValue(`${ t } day: ${ afterWithDay.day }`, `${ t } day: 1`); + var afterWithMonth = afterWithDay.with({ month: 1 }); + t = "(after setting month)"; + assert.sameValue(`${ t } year: ${ afterWithMonth.year }`, `${ t } year: ${ inCal.year }`); + assert.sameValue(`${ t } month: ${ afterWithMonth.month }`, `${ t } month: 1`); + assert.sameValue(`${ t } day: ${ afterWithMonth.day }`, `${ t } day: 1`); + var afterWithYear = afterWithMonth.with({ year: 2220 }); + t = "(after setting year)"; + assert.sameValue(`${ t } year: ${ afterWithYear.year }`, `${ t } year: 2220`); + assert.sameValue(`${ t } month: ${ afterWithYear.month }`, `${ t } month: 1`); + assert.sameValue(`${ t } day: ${ afterWithYear.day }`, `${ t } day: 1`); + var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now; + totalNow += ms; + if (logPerf) + console.log(`with: ${ id } ${ name }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`); + }; + } +} +var addDaysWeeksCases = { + iso8601: { + year: 2000, + month: 10, + day: 7, + monthCode: "M10", + eraYear: undefined, + era: undefined + }, + buddhist: RangeError, + chinese: { + year: 2000, + month: 10, + day: 16, + monthCode: "M10", + eraYear: undefined, + era: undefined + }, + coptic: { + year: 2000, + month: 10, + day: 11, + monthCode: "M10", + eraYear: 2000, + era: "era1" + }, + dangi: { + year: 2000, + month: 10, + day: 16, + monthCode: "M10", + eraYear: undefined, + era: undefined + }, + ethioaa: { + year: 2000, + month: 10, + day: 11, + monthCode: "M10", + eraYear: 2000, + era: "era0" + }, + ethiopic: { + year: 2000, + month: 10, + day: 11, + monthCode: "M10", + eraYear: 2000, + era: "era0" + }, + gregory: { + year: 2000, + month: 10, + day: 7, + monthCode: "M10", + eraYear: 2000, + era: "ce" + }, + hebrew: { + year: 2000, + month: 10, + day: 14, + monthCode: "M10", + eraYear: undefined, + era: undefined + }, + indian: { + year: 2000, + month: 10, + day: 6, + monthCode: "M10", + eraYear: 2000, + era: "saka" + }, + islamic: { + year: 2000, + month: 10, + day: 15, + monthCode: "M10", + eraYear: 2000, + era: "ah" + }, + "islamic-umalqura": { + year: 2000, + month: 10, + day: 15, + monthCode: "M10", + eraYear: 2000, + era: "ah" + }, + "islamic-tbla": { + year: 2000, + month: 10, + day: 15, + monthCode: "M10", + eraYear: 2000, + era: "ah" + }, + "islamic-civil": { + year: 2000, + month: 10, + day: 15, + monthCode: "M10", + eraYear: 2000, + era: "ah" + }, + "islamic-rgsa": { + year: 2000, + month: 10, + day: 15, + monthCode: "M10", + eraYear: 2000, + era: "ah" + }, + islamicc: { + year: 2000, + month: 10, + day: 15, + monthCode: "M10", + eraYear: 2000, + era: "ah" + }, + japanese: { + year: 2000, + month: 10, + day: 7, + monthCode: "M10", + eraYear: 12, + era: "heisei" + }, + persian: { + year: 2000, + month: 10, + day: 5, + monthCode: "M10", + eraYear: 2000, + era: "ap" + }, + roc: { + year: 2000, + month: 10, + day: 8, + monthCode: "M10", + eraYear: 2000, + era: "minguo" + } +}; +const addMonthsCases = { + iso8601: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: undefined, era: undefined }, + // See https://bugs.chromium.org/p/chromium/issues/detail?id=1173158 + buddhist: RangeError, // { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'be' }, + chinese: { year: 2001, month: 6, day: 1, monthCode: 'M05', eraYear: undefined, era: undefined }, + coptic: { year: 2001, month: 5, day: 1, monthCode: 'M05', eraYear: 2001, era: 'era1' }, + dangi: { year: 2001, month: 6, day: 1, monthCode: 'M05', eraYear: undefined, era: undefined }, + ethioaa: { year: 2001, month: 5, day: 1, monthCode: 'M05', eraYear: 2001, era: 'era0' }, + ethiopic: { year: 2001, month: 5, day: 1, monthCode: 'M05', eraYear: 2001, era: 'era0' }, + gregory: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ce' }, + hebrew: { year: 2001, month: 6, day: 1, monthCode: 'M05L', eraYear: undefined, era: undefined }, + indian: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'saka' }, + islamic: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' }, + 'islamic-umalqura': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' }, + 'islamic-tbla': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' }, + 'islamic-civil': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' }, + 'islamic-rgsa': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' }, + islamicc: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' }, + japanese: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 13, era: 'heisei' }, + persian: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ap' }, + roc: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'minguo' } +}; +var i = 0; +const addYearsMonthsDaysCases = Object.entries(addMonthsCases).reduce((obj, entry) => { + obj[entry[0]] = entry[1] === RangeError ? RangeError : { ...entry[1], day: 18 }; + return obj; +}, {}); +var tests = { + days: { + duration: { days: 280 }, + results: addDaysWeeksCases, + startDate: { + year: 2000, + month: 1, + day: 1 + } + }, + weeks: { + duration: { weeks: 40 }, + results: addDaysWeeksCases, + startDate: { + year: 2000, + month: 1, + day: 1 + } + }, + months: { + duration: { months: 6 }, + results: addMonthsCases, + startDate: { + year: 2000, + month: 12, + day: 1 + } + }, + years: { + duration: { + years: 3, + months: 6, + days: 17 + }, + results: addYearsMonthsDaysCases, + startDate: { + year: 1997, + monthCode: "M12", + day: 1 + } + } +}; +var calendars = Object.keys(addMonthsCases); +for (var id of calendars) { + for (var [unit, {duration, results, startDate}] of Object.entries(tests)) { + var values = results[id]; + duration = Temporal.Duration.from(duration); + if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) { + var now = globalThis.performance ? globalThis.performance.now() : Date.now(); + if (values === RangeError) { + assert.throws(RangeError, () => Temporal.PlainDate.from({ + ...startDate, + calendar: id + })); + } + var start = Temporal.PlainDate.from({ + ...startDate, + calendar: id + }); + var end = start.add(duration); + assert.sameValue(`add ${ unit } ${ id } day: ${ end.day }`, `add ${ unit } ${ id } day: ${ values.day }`); + assert.sameValue(`add ${ unit } ${ id } eraYear: ${ end.eraYear }`, `add ${ unit } ${ id } eraYear: ${ values.eraYear }`); + assert.sameValue(`add ${ unit } ${ id } era: ${ end.era }`, `add ${ unit } ${ id } era: ${ values.era }`); + assert.sameValue(`add ${ unit } ${ id } year: ${ end.year }`, `add ${ unit } ${ id } year: ${ values.year }`); + assert.sameValue(`add ${ unit } ${ id } month: ${ end.month }`, `add ${ unit } ${ id } month: ${ values.month }`); + assert.sameValue(`add ${ unit } ${ id } monthCode: ${ end.monthCode }`, `add ${ unit } ${ id } monthCode: ${ values.monthCode }`); + var calculatedStart = end.subtract(duration); + var isLunisolar = [ + "chinese", + "dangi", + "hebrew" + ].includes(id); + var expectedCalculatedStart = isLunisolar && duration.years !== 0 && !end.monthCode.endsWith("L") ? start.subtract({ months: 1 }) : start; + assert.sameValue(`start ${ calculatedStart.toString() }`, `start ${ expectedCalculatedStart.toString() }`); + var diff = start.until(end, { largestUnit: unit }); + assert.sameValue(`diff ${ unit } ${ id }: ${ diff }`, `diff ${ unit } ${ id }: ${ duration }`); + if (unit === "months") { + var startYesterday = start.subtract({ days: 1 }); + var endYesterday = startYesterday.add(duration); + assert.sameValue(`add from end-of-month ${ unit } ${ id } day (initial): ${ endYesterday.day }`, `add from end-of-month ${ unit } ${ id } day (initial): ${ Math.min(startYesterday.day, endYesterday.daysInMonth) }`); + var endYesterdayNextDay = endYesterday.add({ days: 1 }); + while (endYesterdayNextDay.day !== 1) { + endYesterdayNextDay = endYesterdayNextDay.add({ days: 1 }); + } + assert.sameValue(`add from end-of-month ${ unit } ${ id } day: ${ endYesterdayNextDay.day }`, `add from end-of-month ${ unit } ${ id } day: ${ values.day }`); + assert.sameValue(`add from end-of-month ${ unit } ${ id } eraYear: ${ endYesterdayNextDay.eraYear }`, `add from end-of-month ${ unit } ${ id } eraYear: ${ values.eraYear }`); + assert.sameValue(`add from end-of-month ${ unit } ${ id } era: ${ endYesterdayNextDay.era }`, `add from end-of-month ${ unit } ${ id } era: ${ values.era }`); + assert.sameValue(`add from end-of-month ${ unit } ${ id } year: ${ endYesterdayNextDay.year }`, `add from end-of-month ${ unit } ${ id } year: ${ values.year }`); + assert.sameValue(`add from end-of-month ${ unit } ${ id } month: ${ endYesterdayNextDay.month }`, `add from end-of-month ${ unit } ${ id } month: ${ values.month }`); + assert.sameValue(`add from end-of-month ${ unit } ${ id } monthCode: ${ endYesterdayNextDay.monthCode }`, `add from end-of-month ${ unit } ${ id } monthCode: ${ values.monthCode }`); + var endReverse = endYesterdayNextDay.subtract({ days: 1 }); + var startReverse = endReverse.subtract(duration); + assert.sameValue(`subtract from end-of-month ${ unit } ${ id } day (initial): ${ startReverse.day }`, `subtract from end-of-month ${ unit } ${ id } day (initial): ${ Math.min(endReverse.day, startReverse.daysInMonth) }`); + var startReverseNextDay = startReverse.add({ days: 1 }); + while (startReverseNextDay.day !== 1) { + startReverseNextDay = startReverseNextDay.add({ days: 1 }); + } + assert.sameValue(`subtract from end-of-month ${ unit } ${ id } day: ${ startReverseNextDay.day }`, `subtract from end-of-month ${ unit } ${ id } day: ${ start.day }`); + assert.sameValue(`subtract from end-of-month ${ unit } ${ id } eraYear: ${ startReverseNextDay.eraYear }`, `subtract from end-of-month ${ unit } ${ id } eraYear: ${ start.eraYear }`); + assert.sameValue(`subtract from end-of-month ${ unit } ${ id } era: ${ startReverseNextDay.era }`, `subtract from end-of-month ${ unit } ${ id } era: ${ start.era }`); + assert.sameValue(`subtract from end-of-month ${ unit } ${ id } year: ${ startReverseNextDay.year }`, `subtract from end-of-month ${ unit } ${ id } year: ${ start.year }`); + assert.sameValue(`subtract from end-of-month ${ unit } ${ id } month: ${ startReverseNextDay.month }`, `subtract from end-of-month ${ unit } ${ id } month: ${ start.month }`); + assert.sameValue(`subtract from end-of-month ${ unit } ${ id } monthCode: ${ startReverseNextDay.monthCode }`, `subtract from end-of-month ${ unit } ${ id } monthCode: ${ start.monthCode }`); + } + var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now; + totalNow += ms; + if (logPerf) + console.log(`${ id } add ${ duration }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`); + }; + } +} +var daysInMonthCases = { + iso8601: { + year: 2001, + leap: false, + days: [ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 + ] + }, + buddhist: { + year: 4001, + leap: false, + days: [ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 + ] + }, + chinese: { + year: 2001, + leap: "M04L", + days: [ + 30, + 30, + 29, + 30, + 29, + 30, + 29, + 29, + 30, + 29, + 30, + 29, + 30 + ] + }, + coptic: { + year: 2001, + leap: false, + days: [ + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 5 + ] + }, + dangi: { + year: 2001, + leap: "M04L", + days: [ + 30, + 30, + 30, + 29, + 29, + 30, + 29, + 29, + 30, + 29, + 30, + 29, + 30 + ] + }, + ethioaa: { + year: 2001, + leap: false, + days: [ + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 5 + ] + }, + ethiopic: { + year: 2001, + leap: false, + days: [ + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 5 + ] + }, + gregory: { + year: 2001, + leap: false, + days: [ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 + ] + }, + hebrew: { + year: 2001, + leap: "M05L", + days: [ + 30, + 30, + 30, + 29, + 30, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29 + ] + }, + indian: { + year: 2001, + leap: false, + days: [ + 30, + 31, + 31, + 31, + 31, + 31, + 30, + 30, + 30, + 30, + 30, + 30 + ] + }, + islamic: { + year: 2001, + leap: false, + days: [ + 29, + 30, + 29, + 29, + 30, + 29, + 30, + 30, + 29, + 30, + 30, + 29 + ] + }, + "islamic-umalqura": { + year: 2001, + leap: true, + days: [ + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 30 + ] + }, + "islamic-tbla": { + year: 2001, + leap: true, + days: [ + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 30 + ] + }, + "islamic-civil": { + year: 2001, + leap: true, + days: [ + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 30 + ] + }, + "islamic-rgsa": { + year: 2001, + leap: false, + days: [ + 29, + 30, + 29, + 29, + 30, + 29, + 30, + 30, + 29, + 30, + 30, + 29 + ] + }, + islamicc: { + year: 2001, + leap: true, + days: [ + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 29, + 30, + 30 + ] + }, + japanese: { + year: 2001, + leap: false, + days: [ + 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 + ] + }, + persian: { + year: 2001, + leap: false, + days: [ + 31, + 31, + 31, + 31, + 31, + 31, + 30, + 30, + 30, + 30, + 30, + 29 + ] + }, + roc: { + year: 2001, + leap: true, + days: [ + 31, + 29, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 + ] + } +}; +totalNow = 0; +for (var id of calendars) { + var {year, leap, days} = daysInMonthCases[id]; + var date = hasOutdatedChineseIcuData && (id === "chinese" || id === "dangi") ? undefined : Temporal.PlainDate.from({ + year, + month: 1, + day: 1, + calendar: id + }); + if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) { + if (typeof leap === "boolean") { + assert.sameValue(date.inLeapYear, leap); + } else { + assert.sameValue(date.inLeapYear, true); + var leapMonth = date.with({ monthCode: leap }); + assert.sameValue(leapMonth.monthCode, leap); + } + }; + if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) { + var now = globalThis.performance ? globalThis.performance.now() : Date.now(); + var {monthsInYear} = date; + assert.sameValue(monthsInYear, days.length); + for (var i = monthsInYear, leapMonthIndex = undefined, monthStart = undefined; i >= 1; i--) { + monthStart = monthStart ? monthStart.add({ months: -1 }) : date.add({ months: monthsInYear - 1 }); + var {month, monthCode, daysInMonth} = monthStart; + assert.sameValue(`${ id } month ${ i } (code ${ monthCode }) days: ${ daysInMonth }`, `${ id } month ${ i } (code ${ monthCode }) days: ${ days[i - 1] }`); + if (monthCode.endsWith("L")) { + assert.sameValue(date.with({ monthCode }).monthCode, monthCode); + leapMonthIndex = i; + } else { + if (leapMonthIndex && i === leapMonthIndex - 1) { + var inLeapMonth = monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` }); + assert.sameValue(inLeapMonth.monthCode, `${ monthCode }L`); + } else { + assert.throws(RangeError, () => monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` }, { overflow: "reject" })); + if ([ + "chinese", + "dangi" + ].includes(id)) { + if (i === 1 || i === 12 || i === 13) { + assert.throws(RangeError, () => monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` })); + } else { + var fakeL = monthStart.with({ + monthCode: `M${ month.toString().padStart(2, "0") }L`, + day: 5 + }); + assert.sameValue(`fake leap month ${ fakeL.monthCode }`, `fake leap month M${ month.toString().padStart(2, "0") }`); + assert.sameValue(fakeL.day, fakeL.daysInMonth); + } + } + } + if (![ + "chinese", + "dangi", + "hebrew" + ].includes(id)) { + assert.throws(RangeError, () => monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` })); + } + } + assert.throws(RangeError, () => monthStart.with({ day: daysInMonth + 1 }, { overflow: "reject" })); + var oneDayPastMonthEnd = monthStart.with({ day: daysInMonth + 1 }); + assert.sameValue(oneDayPastMonthEnd.day, daysInMonth); + } + var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now; + totalNow += ms; + if (logPerf) + console.log(`${ id } months check ${ id }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`); + }; +} +var monthDayCases = [ + { + calendar: "iso8601", + isoReferenceYear: 1972, + year: 2004, + month: 2, + day: 29 + }, + { + calendar: "buddhist", + isoReferenceYear: 1972, + year: 2004, + month: 2, + day: 29 + }, + { + calendar: "chinese", + isoReferenceYear: 1963, + year: 2001, + month: 5, + monthCode: "M04L", + day: 15 + }, + { + calendar: "chinese", + isoReferenceYear: 1971, + year: 2000, + month: 6, + day: 30 + }, + { + calendar: "coptic", + isoReferenceYear: 1971, + year: 2006, + month: 13, + day: 6 + }, + { + calendar: "dangi", + isoReferenceYear: 1963, + year: 2001, + month: 5, + monthCode: "M04L", + day: 15 + }, + { + calendar: "dangi", + isoReferenceYear: 1971, + year: 2000, + month: 6, + day: 30 + }, + { + calendar: "ethiopic", + isoReferenceYear: 1971, + year: 2006, + month: 13, + day: 6 + }, + { + calendar: "ethioaa", + isoReferenceYear: 1971, + year: 2006, + month: 13, + day: 6 + }, + { + calendar: "gregory", + isoReferenceYear: 1972, + year: 2004, + month: 2, + day: 29 + }, + { + calendar: "hebrew", + isoReferenceYear: 1970, + year: 5779, + month: 6, + monthCode: "M05L", + day: 15 + }, + { + calendar: "hebrew", + isoReferenceYear: 1971, + year: 5776, + month: 2, + day: 30 + }, + { + calendar: "hebrew", + isoReferenceYear: 1971, + year: 5772, + month: 3, + day: 30 + }, + { + calendar: "indian", + isoReferenceYear: 1968, + year: 2002, + month: 1, + day: 31 + }, + { + calendar: "islamic", + isoReferenceYear: 1970, + year: 2001, + month: 1, + day: 30 + }, + { + calendar: "islamic-umalqura", + isoReferenceYear: 1969, + year: 2001, + month: 1, + day: 30 + }, + { + calendar: "islamic-tbla", + isoReferenceYear: 1971, + year: 2001, + month: 1, + day: 30 + }, + { + calendar: "islamic-civil", + isoReferenceYear: 1971, + year: 2001, + month: 1, + day: 30 + }, + { + calendar: "islamic-rgsa", + isoReferenceYear: 1970, + year: 2001, + month: 1, + day: 30 + }, + { + calendar: "islamicc", + isoReferenceYear: 1971, + year: 2001, + month: 1, + day: 30 + }, + { + calendar: "japanese", + isoReferenceYear: 1972, + year: 2004, + month: 2, + day: 29 + }, + { + calendar: "persian", + isoReferenceYear: 1972, + year: 2004, + month: 12, + day: 30 + }, + { + calendar: "roc", + isoReferenceYear: 1972, + year: 93, + month: 2, + day: 29 + } +]; +var i = 0; +for (var test of monthDayCases) { + var id = test.calendar; + if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) { + if (test.monthCode === undefined) + test.monthCode = `M${ test.month.toString().padStart(2, "0") }`; + var {calendar, monthCode, month, day, year, isoReferenceYear} = test; + var md = Temporal.PlainMonthDay.from({ + year, + month, + day, + calendar + }); + var isoString = md.toString(); + var mdFromIso = Temporal.PlainMonthDay.from(isoString); + assert.sameValue(mdFromIso, md); + var isoFields = md.getISOFields(); + assert.sameValue(md.monthCode, monthCode); + assert.sameValue(md.day, day); + assert.sameValue(isoFields.isoYear, isoReferenceYear); + var md2 = Temporal.PlainMonthDay.from({ + monthCode, + day, + calendar + }); + var isoFields2 = md2.getISOFields(); + assert.sameValue(md2.monthCode, monthCode); + assert.sameValue(md2.day, day); + assert.sameValue(isoFields2.isoYear, isoReferenceYear); + assert.sameValue(md.equals(md2), true); + assert.throws(RangeError, () => { + Temporal.PlainMonthDay.from({ + monthCode: "M15", + day: 1, + calendar + }, { overflow: "reject" }); + }); + assert.throws(RangeError, () => { + Temporal.PlainMonthDay.from({ + monthCode: "M15", + day: 1, + calendar + }); + }); + assert.throws(RangeError, () => { + Temporal.PlainMonthDay.from({ + year, + month: 15, + day: 1, + calendar + }, { overflow: "reject" }); + }); + var constrained = Temporal.PlainMonthDay.from({ + year, + month: 15, + day: 1, + calendar + }); + var {monthCode: monthCodeConstrained} = constrained; + assert(monthCodeConstrained === "M12" || monthCodeConstrained === "M13"); + }; +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/persian-calendar.js b/js/src/tests/test262/staging/Intl402/Temporal/old/persian-calendar.js new file mode 100644 index 0000000000..a3578f3208 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/persian-calendar.js @@ -0,0 +1,93 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: Persian calendar +features: [Temporal] +---*/ + +// Test data obtained from ICU + +const tests = [ + { + testYear: 1395, + inLeapYear: true, + daysInYear: 366, + daysInMonth12: 30, + isoYear: 2016, + isoMonth: 3, + isoDay: 20 + }, + { + testYear: 1396, + inLeapYear: false, + daysInYear: 365, + daysInMonth12: 29, + isoYear: 2017, + isoMonth: 3, + isoDay: 21 + }, + { + testYear: 1397, + inLeapYear: false, + daysInYear: 365, + daysInMonth12: 29, + isoYear: 2018, + isoMonth: 3, + isoDay: 21 + }, + { + testYear: 1398, + inLeapYear: false, + daysInYear: 365, + daysInMonth12: 29, + isoYear: 2019, + isoMonth: 3, + isoDay: 21 + }, + { + testYear: 1399, + inLeapYear: true, + daysInYear: 366, + daysInMonth12: 30, + isoYear: 2020, + isoMonth: 3, + isoDay: 20 + }, + { + testYear: 1400, + inLeapYear: false, + daysInYear: 365, + daysInMonth12: 29, + isoYear: 2021, + isoMonth: 3, + isoDay: 21 + } +]; + +for (const test of tests) { + const { testYear, inLeapYear, daysInYear, daysInMonth12, isoYear, isoMonth, isoDay } = test; + const date = Temporal.PlainDate.from({ year: testYear, month: 1, day: 1, calendar: "persian" }); + const isoFields = date.getISOFields(); + assert.sameValue(date.calendarId, "persian"); + assert.sameValue(date.year, testYear); + assert.sameValue(date.month, 1); + assert.sameValue(date.monthCode, "M01"); + assert.sameValue(date.day, 1); + assert.sameValue(date.inLeapYear, inLeapYear); + assert.sameValue(date.daysInYear, daysInYear); + assert.sameValue(date.monthsInYear, 12); + assert.sameValue(date.dayOfYear, 1); + const startOfNextYear = date.with({ year: testYear + 1 }); + const lastDayOfThisYear = startOfNextYear.subtract({ days: 1 }); + assert.sameValue(lastDayOfThisYear.dayOfYear, daysInYear); + const dateMonth12 = date.with({ month: 12 }); + assert.sameValue(dateMonth12.daysInMonth, daysInMonth12); + assert.sameValue(isoYear, isoFields.isoYear); + assert.sameValue(isoMonth, isoFields.isoMonth); + assert.sameValue(isoDay, isoFields.isoDay); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/shell.js b/js/src/tests/test262/staging/Intl402/Temporal/old/shell.js new file mode 100644 index 0000000000..276ee734ff --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/shell.js @@ -0,0 +1,329 @@ +// 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; +})(); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/time-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/time-toLocaleString.js new file mode 100644 index 0000000000..a521123333 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/time-toLocaleString.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: time.toLocaleString() +features: [Temporal] +---*/ + +// Tolerate implementation variance by expecting consistency without being prescriptive. +// TODO: can we change tests to be less reliant on CLDR formats while still testing that +// Temporal and Intl are behaving as expected? +const usDayPeriodSpace = + new Intl.DateTimeFormat("en-US", { timeStyle: "short" }) + .formatToParts(0) + .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || ""; + +var time = Temporal.PlainTime.from("1976-11-18T15:23:30"); +assert.sameValue(`${time.toLocaleString("en-US", { timeZone: "America/New_York" })}`, `3:23:30${usDayPeriodSpace}PM`); +assert.sameValue(`${time.toLocaleString("de-AT", { timeZone: "Europe/Vienna" })}`, "15:23:30"); + +// should ignore units not in the data type +assert.sameValue(time.toLocaleString("en-US", { timeZoneName: "long" }), `3:23:30${usDayPeriodSpace}PM`); +assert.sameValue(time.toLocaleString("en-US", { year: "numeric" }), `3:23:30${usDayPeriodSpace}PM`); +assert.sameValue(time.toLocaleString("en-US", { month: "numeric" }), `3:23:30${usDayPeriodSpace}PM`); +assert.sameValue(time.toLocaleString("en-US", { day: "numeric" }), `3:23:30${usDayPeriodSpace}PM`); +assert.sameValue(time.toLocaleString("en-US", { weekday: "long" }), `3:23:30${usDayPeriodSpace}PM`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/timezone-america-la.js b/js/src/tests/test262/staging/Intl402/Temporal/old/timezone-america-la.js new file mode 100644 index 0000000000..662f54d05c --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/timezone-america-la.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: America/Los_Angeles +features: [Temporal] +---*/ + +var zone = new Temporal.TimeZone("America/Los_Angeles"); +var inst = new Temporal.Instant(0n); +var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); +assert.sameValue(zone.id, `${ zone }`) +assert.sameValue(zone.getOffsetNanosecondsFor(inst), -8 * 3600000000000) +assert.sameValue(zone.getOffsetStringFor(inst), "-08:00") +assert(zone.getInstantFor(dtm) instanceof Temporal.Instant) +for (var i = 0, txn = inst; i < 4; i++) { + var transition = zone.getNextTransition(txn); + assert(transition instanceof Temporal.Instant); + assert(!transition.equals(txn)); + txn = transition; +} +for (var i = 0, txn = inst; i < 4; i++) { + var transition = zone.getPreviousTransition(txn); + assert(transition instanceof Temporal.Instant); + assert(!transition.equals(txn)); + txn = transition; +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-string-parsing.js b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-string-parsing.js new file mode 100644 index 0000000000..16690270bc --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-string-parsing.js @@ -0,0 +1,101 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: String parsing cases that require a TZDB in the implementation +features: [Temporal] +---*/ + +// parses with an IANA zone but no offset (with disambiguation) +var zdt = Temporal.ZonedDateTime.from("2020-03-08T02:30[America/Los_Angeles]", { disambiguation: "earlier" }); +assert.sameValue(zdt.toString(), "2020-03-08T01:30:00-08:00[America/Los_Angeles]"); + +// "Z" means preserve the exact time in the given IANA time zone +var zdt = Temporal.ZonedDateTime.from("2020-03-08T09:00:00Z[America/Los_Angeles]"); +assert.sameValue(zdt.toString(), "2020-03-08T01:00:00-08:00[America/Los_Angeles]"); + +// Offset options + +// { offset: 'prefer' } if offset matches time zone (first 1:30 when DST ends) +var zdt = Temporal.ZonedDateTime.from("2020-11-01T01:30-07:00[America/Los_Angeles]", { offset: "prefer" }); +assert.sameValue(zdt.toString(), "2020-11-01T01:30:00-07:00[America/Los_Angeles]"); + +// { offset: 'prefer' } if offset matches time zone (second 1:30 when DST ends) +var zdt = Temporal.ZonedDateTime.from("2020-11-01T01:30-08:00[America/Los_Angeles]", { offset: "prefer" }); +assert.sameValue(zdt.toString(), "2020-11-01T01:30:00-08:00[America/Los_Angeles]"); + +// { offset: 'prefer' } if offset does not match time zone +var zdt = Temporal.ZonedDateTime.from("2020-11-01T04:00-07:00[America/Los_Angeles]", { offset: "prefer" }); +assert.sameValue(zdt.toString(), "2020-11-01T04:00:00-08:00[America/Los_Angeles]"); + +// { offset: 'ignore' } uses time zone only +var zdt = Temporal.ZonedDateTime.from("2020-11-01T04:00-12:00[America/Los_Angeles]", { offset: "ignore" }); +assert.sameValue(zdt.toString(), "2020-11-01T04:00:00-08:00[America/Los_Angeles]"); + +// { offset: 'use' } uses offset only +var zdt = Temporal.ZonedDateTime.from("2020-11-01T04:00-07:00[America/Los_Angeles]", { offset: "use" }); +assert.sameValue(zdt.toString(), "2020-11-01T03:00:00-08:00[America/Los_Angeles]"); + +// Disambiguation options + +// plain datetime with multiple instants - Fall DST in Brazil +var str = "2019-02-16T23:45[America/Sao_Paulo]"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(str) }`, "2019-02-16T23:45:00-02:00[America/Sao_Paulo]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "compatible" }) }`, "2019-02-16T23:45:00-02:00[America/Sao_Paulo]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "earlier" }) }`, "2019-02-16T23:45:00-02:00[America/Sao_Paulo]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "later" }) }`, "2019-02-16T23:45:00-03:00[America/Sao_Paulo]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { disambiguation: "reject" })); + +// plain datetime with multiple instants - Spring DST in Los Angeles +var str = "2020-03-08T02:30[America/Los_Angeles]"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(str) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "compatible" }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "earlier" }) }`, "2020-03-08T01:30:00-08:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "later" }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { disambiguation: "reject" })); + +// uses disambiguation if offset is ignored +var str = "2020-03-08T02:30[America/Los_Angeles]"; +var offset = "ignore"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { offset }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "compatible" +}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "earlier" +}) }`, "2020-03-08T01:30:00-08:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "later" +}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "reject" +})); + +// uses disambiguation if offset is wrong and option is prefer +var str = "2020-03-08T02:30-23:59[America/Los_Angeles]"; +var offset = "prefer"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { offset }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "compatible" +}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "earlier" +}) }`, "2020-03-08T01:30:00-08:00[America/Los_Angeles]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "later" +}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { + offset, + disambiguation: "reject" +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-transitions.js b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-transitions.js new file mode 100644 index 0000000000..52978e40fb --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-transitions.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Tests that are impractical to do without a TZDB +features: [Temporal] +---*/ + +// getNextTransition() + +var nyc = Temporal.TimeZone.from("America/New_York"); + +// should not have bug #510 +var a1 = Temporal.Instant.from("2019-04-16T21:01Z"); +var a2 = Temporal.Instant.from("1800-01-01T00:00Z"); +assert.sameValue(nyc.getNextTransition(a1).toString(), "2019-11-03T06:00:00Z"); +assert.sameValue(nyc.getNextTransition(a2).toString(), "1883-11-18T17:00:00Z"); + +// should not return the same as its input if the input is a transition point +var inst = Temporal.Instant.from("2019-01-01T00:00Z"); +assert.sameValue(`${ nyc.getNextTransition(inst) }`, "2019-03-10T07:00:00Z"); +assert.sameValue(`${ nyc.getNextTransition(nyc.getNextTransition(inst)) }`, "2019-11-03T06:00:00Z"); + +// casts argument +assert.sameValue(`${ nyc.getNextTransition("2019-04-16T21:01Z") }`, "2019-11-03T06:00:00Z"); + +// getPreviousTransition() + +var london = Temporal.TimeZone.from("Europe/London"); + +// should return first and last transition +var a1 = Temporal.Instant.from("2020-06-11T21:01Z"); +var a2 = Temporal.Instant.from("1848-01-01T00:00Z"); +assert.sameValue(london.getPreviousTransition(a1).toString(), "2020-03-29T01:00:00Z"); +assert.sameValue(london.getPreviousTransition(a2).toString(), "1847-12-01T00:01:15Z"); + +// should not return the same as its input if the input is a transition point +var inst = Temporal.Instant.from("2020-06-01T00:00Z"); +assert.sameValue(`${ london.getPreviousTransition(inst) }`, "2020-03-29T01:00:00Z"); +assert.sameValue(`${ london.getPreviousTransition(london.getPreviousTransition(inst)) }`, "2019-10-27T01:00:00Z"); + +// casts argument +assert.sameValue(`${ london.getPreviousTransition("2020-06-11T21:01Z") }`, "2020-03-29T01:00:00Z"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/yearmonth-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/yearmonth-toLocaleString.js new file mode 100644 index 0000000000..c92aa506d5 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/yearmonth-toLocaleString.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-intl +description: yearmonth.toLocaleString() +features: [Temporal] +---*/ + +// Tolerate implementation variance by expecting consistency without being prescriptive. +// TODO: can we change tests to be less reliant on CLDR formats while still testing that +// Temporal and Intl are behaving as expected? +// Workarounds for https://unicode-org.atlassian.net/browse/CLDR-16243 +const deMonthDayRangeSeparator = new Intl.DateTimeFormat("de-AT", { month: "numeric", day: "numeric" }) + .formatRangeToParts(1 * 86400 * 1000, 90 * 86400 * 1000) + .find((part) => part.type === "literal" && part.source === "shared").value; +const deMonthYearSeparator = new Intl.DateTimeFormat("de-AT", { year: "numeric", month: "numeric" }) + .formatToParts(0) + .find((part) => part.type === "literal").value; + +var calendar = new Intl.DateTimeFormat("en-US").resolvedOptions().calendar; +var yearmonth = Temporal.PlainYearMonth.from({ + year: 1976, + month: 11, + calendar +}); +assert.sameValue(`${yearmonth.toLocaleString("en-US", { timeZone: "America/New_York" })}`, "11/1976"); +assert.sameValue( + `${yearmonth.toLocaleString("de-AT", { timeZone: "Europe/Vienna", calendar })}`, + `11${deMonthYearSeparator}1976` +); + +// should ignore units not in the data type +assert.sameValue(yearmonth.toLocaleString("en-US", { timeZoneName: "long" }), "11/1976"); +assert.sameValue(yearmonth.toLocaleString("en-US", { day: "numeric" }), "11/1976"); +assert.sameValue(yearmonth.toLocaleString("en-US", { hour: "numeric" }), "11/1976"); +assert.sameValue(yearmonth.toLocaleString("en-US", { minute: "numeric" }), "11/1976"); +assert.sameValue(yearmonth.toLocaleString("en-US", { second: "numeric" }), "11/1976"); +assert.sameValue(yearmonth.toLocaleString("en-US", { weekday: "long" }), "11/1976"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/zoneddatetime-dst-corner-cases.js b/js/src/tests/test262/staging/Intl402/Temporal/old/zoneddatetime-dst-corner-cases.js new file mode 100644 index 0000000000..d66a8d0715 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/old/zoneddatetime-dst-corner-cases.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Corner cases of time zone offset shifts +features: [Temporal] +---*/ + +// hoursInDay works with non-hour DST change +var zdt1 = Temporal.ZonedDateTime.from("2020-10-04T12:00[Australia/Lord_Howe]"); +assert.sameValue(zdt1.hoursInDay, 23.5); +var zdt2 = Temporal.ZonedDateTime.from("2020-04-05T12:00[Australia/Lord_Howe]"); +assert.sameValue(zdt2.hoursInDay, 24.5); + +// hoursInDay works with non-half-hour DST change +var zdt = Temporal.ZonedDateTime.from("1933-01-01T12:00[Asia/Singapore]"); +assert(Math.abs(zdt.hoursInDay - 23.666666666666668) < Number.EPSILON); + +// hoursInDay works when day starts at 1:00 due to DST start at midnight +var zdt = Temporal.ZonedDateTime.from("2015-10-18T12:00:00-02:00[America/Sao_Paulo]"); +assert.sameValue(zdt.hoursInDay, 23); + +// startOfDay works when day starts at 1:00 due to DST start at midnight +var zdt = Temporal.ZonedDateTime.from("2015-10-18T12:00:00-02:00[America/Sao_Paulo]"); +assert.sameValue(`${ zdt.startOfDay().toPlainTime() }`, "01:00:00"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Intl402/Temporal/shell.js b/js/src/tests/test262/staging/Intl402/Temporal/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/Temporal/shell.js diff --git a/js/src/tests/test262/staging/Intl402/browser.js b/js/src/tests/test262/staging/Intl402/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/browser.js diff --git a/js/src/tests/test262/staging/Intl402/shell.js b/js/src/tests/test262/staging/Intl402/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Intl402/shell.js diff --git a/js/src/tests/test262/staging/JSON/browser.js b/js/src/tests/test262/staging/JSON/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/JSON/browser.js diff --git a/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js b/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js new file mode 100644 index 0000000000..45465333d4 --- /dev/null +++ b/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js @@ -0,0 +1,97 @@ +// |reftest| skip -- json-parse-with-source is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: V8 mjsunit test for JSON.parse with source snapshotting +includes: [deepEqual.js] +features: [json-parse-with-source] +---*/ + +const replacements = [42, + ['foo'], + {foo:'bar'}, + 'foo']; + +function TestArrayForwardModify(replacement) { + let alreadyReplaced = false; + let expectedKeys = ['0','1','']; + // lol who designed reviver semantics + if (typeof replacement === 'object') { + expectedKeys.splice(1, 0, ...Object.keys(replacement)); + } + const o = JSON.parse('[1, 2]', function (k, v, { source }) { + assert.sameValue(expectedKeys.shift(), k); + if (k === '0') { + if (!alreadyReplaced) { + this[1] = replacement; + alreadyReplaced = true; + } + } else if (k !== '') { + assert.sameValue(undefined, source); + } + return this[k]; + }); + assert.sameValue(0, expectedKeys.length); + assert.deepEqual([1, replacement], o); +} + +function TestObjectForwardModify(replacement) { + let alreadyReplaced = false; + let expectedKeys = ['p','q','']; + if (typeof replacement === 'object') { + expectedKeys.splice(1, 0, ...Object.keys(replacement)); + } + const o = JSON.parse('{"p":1, "q":2}', function (k, v, { source }) { + assert.sameValue(expectedKeys.shift(), k); + if (k === 'p') { + if (!alreadyReplaced) { + this.q = replacement; + alreadyReplaced = true; + } + } else if (k !== '') { + assert.sameValue(undefined, source); + } + return this[k]; + }); + assert.sameValue(0, expectedKeys.length); + assert.deepEqual({p:1, q:replacement}, o); +} + +for (const r of replacements) { + TestArrayForwardModify(r); + TestObjectForwardModify(r); +} + +(function TestArrayAppend() { + let log = []; + const o = JSON.parse('[1,[]]', function (k, v, { source }) { + log.push([k, v, source]); + if (v === 1) { + this[1].push('barf'); + } + return this[k]; + }); + assert.deepEqual([['0', 1, '1'], + ['0', 'barf', undefined], + ['1', ['barf'], undefined], + ['', [1, ['barf']], undefined]], + log); +})(); + +(function TestObjectAddProperty() { + let log = []; + const o = JSON.parse('{"p":1,"q":{}}', function (k, v, { source }) { + log.push([k, v, source]); + if (v === 1) { + this.q.added = 'barf'; + } + return this[k]; + }); + assert.deepEqual([['p', 1, '1'], + ['added', 'barf', undefined], + ['q', {added:'barf'}, undefined], + ['', {p:1, q:{added:'barf'}}, undefined]], + log); +})(); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/JSON/json-parse-with-source.js b/js/src/tests/test262/staging/JSON/json-parse-with-source.js new file mode 100644 index 0000000000..f8674511ef --- /dev/null +++ b/js/src/tests/test262/staging/JSON/json-parse-with-source.js @@ -0,0 +1,292 @@ +// |reftest| skip -- json-parse-with-source is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: V8 mjsunit test for JSON.parse with source +includes: [deepEqual.js] +features: [json-parse-with-source] +---*/ + +(function TestBigInt() { + const tooBigForNumber = BigInt(Number.MAX_SAFE_INTEGER) + 2n; + const intToBigInt = (key, val, { source }) => + typeof val === 'number' && val % 1 === 0 ? BigInt(source) : val; + const roundTripped = JSON.parse(String(tooBigForNumber), intToBigInt); + assert.sameValue(tooBigForNumber, roundTripped); + + const bigIntToRawJSON = (key, val) => + typeof val === 'bigint' ? JSON.rawJSON(val) : val; + const embedded = JSON.stringify({ tooBigForNumber }, bigIntToRawJSON); + assert.sameValue('{"tooBigForNumber":9007199254740993}', embedded); +})(); + +function GenerateParseReviverFunction(texts) { + let i = 0; + return function (key, value, context) { + assert(typeof context === 'object'); + assert.sameValue(Object.prototype, Object.getPrototypeOf(context)); + // The json value is a primitive value, it's context only has a source property. + if (texts[i] !== undefined) { + const descriptor = Object.getOwnPropertyDescriptor(context, 'source'); + assert(descriptor.configurable); + assert(descriptor.enumerable); + assert(descriptor.writable); + assert.sameValue(undefined, descriptor.get); + assert.sameValue(undefined, descriptor.set); + assert.sameValue(texts[i++], descriptor.value); + + assert.deepEqual(['source'], Object.getOwnPropertyNames(context)); + assert.deepEqual([], Object.getOwnPropertySymbols(context)); + } else { + // The json value is JSArray or JSObject, it's context has no property. + assert(!Object.hasOwn(context, 'source')); + assert.deepEqual([], Object.getOwnPropertyNames(context)); + assert.deepEqual([], Object.getOwnPropertySymbols(context)); + i++; + } + return value; + }; +} + +(function TestNumber() { + assert.sameValue(1, JSON.parse('1', GenerateParseReviverFunction(['1']))); + assert.sameValue(1.1, JSON.parse('1.1', GenerateParseReviverFunction(['1.1']))); + assert.sameValue(-1, JSON.parse('-1', GenerateParseReviverFunction(['-1']))); + assert.sameValue( + -1.1, + JSON.parse('-1.1', GenerateParseReviverFunction(['-1.1'])) + ); + assert.sameValue( + 11, + JSON.parse('1.1e1', GenerateParseReviverFunction(['1.1e1'])) + ); + assert.sameValue( + 11, + JSON.parse('1.1e+1', GenerateParseReviverFunction(['1.1e+1'])) + ); + assert.sameValue( + 0.11, + JSON.parse('1.1e-1', GenerateParseReviverFunction(['1.1e-1'])) + ); + assert.sameValue( + 11, + JSON.parse('1.1E1', GenerateParseReviverFunction(['1.1E1'])) + ); + assert.sameValue( + 11, + JSON.parse('1.1E+1', GenerateParseReviverFunction(['1.1E+1'])) + ); + assert.sameValue( + 0.11, + JSON.parse('1.1E-1', GenerateParseReviverFunction(['1.1E-1'])) + ); + + assert.sameValue('1', JSON.stringify(JSON.rawJSON(1))); + assert.sameValue('1.1', JSON.stringify(JSON.rawJSON(1.1))); + assert.sameValue('-1', JSON.stringify(JSON.rawJSON(-1))); + assert.sameValue('-1.1', JSON.stringify(JSON.rawJSON(-1.1))); + assert.sameValue('11', JSON.stringify(JSON.rawJSON(1.1e1))); + assert.sameValue('0.11', JSON.stringify(JSON.rawJSON(1.1e-1))); +})(); + +(function TestBasic() { + assert.sameValue( + null, + JSON.parse('null', GenerateParseReviverFunction(['null'])) + ); + assert.sameValue( + true, + JSON.parse('true', GenerateParseReviverFunction(['true'])) + ); + assert.sameValue( + false, + JSON.parse('false', GenerateParseReviverFunction(['false'])) + ); + assert.sameValue( + 'foo', + JSON.parse('"foo"', GenerateParseReviverFunction(['"foo"'])) + ); + + assert.sameValue('null', JSON.stringify(JSON.rawJSON(null))); + assert.sameValue('true', JSON.stringify(JSON.rawJSON(true))); + assert.sameValue('false', JSON.stringify(JSON.rawJSON(false))); + assert.sameValue('"foo"', JSON.stringify(JSON.rawJSON('"foo"'))); +})(); + +(function TestObject() { + assert.deepEqual( + {}, + JSON.parse('{}', GenerateParseReviverFunction([])) + ); + assert.deepEqual( + { 42: 37 }, + JSON.parse('{"42":37}', GenerateParseReviverFunction(['37'])) + ); + assert.deepEqual( + { x: 1, y: 2 }, + JSON.parse('{"x": 1, "y": 2}', GenerateParseReviverFunction(['1', '2'])) + ); + // undefined means the json value is JSObject or JSArray and the passed + // context to the reviver function has no source property. + assert.deepEqual( + { x: [1, 2], y: [2, 3] }, + JSON.parse( + '{"x": [1,2], "y": [2,3]}', + GenerateParseReviverFunction(['1', '2', undefined, '2', '3', undefined]) + ) + ); + assert.deepEqual( + { x: { x: 1, y: 2 } }, + JSON.parse( + '{"x": {"x": 1, "y": 2}}', + GenerateParseReviverFunction(['1', '2', undefined, undefined]) + ) + ); + + assert.sameValue('{"42":37}', JSON.stringify({ 42: JSON.rawJSON(37) })); + assert.sameValue( + '{"x":1,"y":2}', + JSON.stringify({ x: JSON.rawJSON(1), y: JSON.rawJSON(2) }) + ); + assert.sameValue( + '{"x":{"x":1,"y":2}}', + JSON.stringify({ x: { x: JSON.rawJSON(1), y: JSON.rawJSON(2) } }) + ); +})(); + +(function TestArray() { + assert.deepEqual([1], JSON.parse('[1.0]', GenerateParseReviverFunction(['1.0']))); + assert.deepEqual( + [1.1], + JSON.parse('[1.1]', GenerateParseReviverFunction(['1.1'])) + ); + assert.deepEqual([], JSON.parse('[]', GenerateParseReviverFunction([]))); + assert.deepEqual( + [1, '2', true, null, { x: 1, y: 1 }], + JSON.parse( + '[1, "2", true, null, {"x": 1, "y": 1}]', + GenerateParseReviverFunction(['1', '"2"', 'true', 'null', '1', '1']) + ) + ); + + assert.sameValue('[1,1.1]', JSON.stringify([JSON.rawJSON(1), JSON.rawJSON(1.1)])); + assert.sameValue( + '["1",true,null,false]', + JSON.stringify([ + JSON.rawJSON('"1"'), + JSON.rawJSON(true), + JSON.rawJSON(null), + JSON.rawJSON(false), + ]) + ); + assert.sameValue( + '[{"x":1,"y":1}]', + JSON.stringify([{ x: JSON.rawJSON(1), y: JSON.rawJSON(1) }]) + ); +})(); + +function assertIsRawJson(rawJson, expectedRawJsonValue) { + assert.sameValue(null, Object.getPrototypeOf(rawJson)); + assert(Object.hasOwn(rawJson, 'rawJSON')); + assert.deepEqual(['rawJSON'], Object.getOwnPropertyNames(rawJson)); + assert.deepEqual([], Object.getOwnPropertySymbols(rawJson)); + assert.sameValue(expectedRawJsonValue, rawJson.rawJSON); +} + +(function TestRawJson() { + assertIsRawJson(JSON.rawJSON(1), '1'); + assertIsRawJson(JSON.rawJSON(null), 'null'); + assertIsRawJson(JSON.rawJSON(true), 'true'); + assertIsRawJson(JSON.rawJSON(false), 'false'); + assertIsRawJson(JSON.rawJSON('"foo"'), '"foo"'); + + assert.throws(TypeError, () => { + JSON.rawJSON(Symbol('123')); + }); + + assert.throws(SyntaxError, () => { + JSON.rawJSON(undefined); + }); + + assert.throws(SyntaxError, () => { + JSON.rawJSON({}); + }); + + assert.throws(SyntaxError, () => { + JSON.rawJSON([]); + }); + + const ILLEGAL_END_CHARS = ['\n', '\t', '\r', ' ']; + for (const char of ILLEGAL_END_CHARS) { + assert.throws(SyntaxError, () => { + JSON.rawJSON(`${char}123`); + }); + assert.throws(SyntaxError, () => { + JSON.rawJSON(`123${char}`); + }); + } + + assert.throws(SyntaxError, () => { + JSON.rawJSON(''); + }); + + const values = [1, 1.1, null, false, true, '123']; + for (const value of values) { + assert(!JSON.isRawJSON(value)); + assert(JSON.isRawJSON(JSON.rawJSON(value))); + } + assert(!JSON.isRawJSON(undefined)); + assert(!JSON.isRawJSON(Symbol('123'))); + assert(!JSON.isRawJSON([])); + assert(!JSON.isRawJSON({ rawJSON: '123' })); +})(); + +(function TestReviverModifyJsonValue() { + { + let reviverCallIndex = 0; + const expectedKeys = ['a', 'b', 'c', '']; + const reviver = function(key, value, {source}) { + assert.sameValue(expectedKeys[reviverCallIndex++], key); + if (key == 'a') { + this.b = 2; + assert.sameValue('0', source); + } else if (key == 'b') { + this.c = 3; + assert.sameValue(2, value); + assert.sameValue(undefined, source); + } else if (key == 'c') { + assert.sameValue(3, value); + assert.sameValue(undefined, source); + } + return value; + } + assert.deepEqual({a: 0, b: 2, c: 3}, JSON.parse('{"a": 0, "b": 1, "c": [1, 2]}', reviver)); + } + { + let reviverCallIndex = 0; + const expectedKeys = ['0', '1', '2', '3', '']; + const reviver = function(key, value, {source}) { + assert.sameValue(expectedKeys[reviverCallIndex++], key); + if (key == '0') { + this[1] = 3; + assert.sameValue(1, value); + assert.sameValue('1', source); + } else if (key == '1') { + this[2] = 4; + assert.sameValue(3, value); + assert.sameValue(undefined, source); + } else if(key == '2') { + this[3] = 5; + assert.sameValue(4, value); + assert.sameValue(undefined, source); + } else if(key == '5'){ + assert.sameValue(5, value); + assert.sameValue(undefined, source); + } + return value; + } + assert.deepEqual([1, 3, 4, 5], JSON.parse('[1, 2, 3, {"a": 1}]', reviver)); + } +})(); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/JSON/shell.js b/js/src/tests/test262/staging/JSON/shell.js new file mode 100644 index 0000000000..276ee734ff --- /dev/null +++ b/js/src/tests/test262/staging/JSON/shell.js @@ -0,0 +1,329 @@ +// 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; +})(); diff --git a/js/src/tests/test262/staging/Temporal/Duration/browser.js b/js/src/tests/test262/staging/Temporal/Duration/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/add.js b/js/src/tests/test262/staging/Temporal/Duration/old/add.js new file mode 100644 index 0000000000..6e2220b32d --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/add.js @@ -0,0 +1,140 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.add() works as expected +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var oneDay = new Temporal.Duration(0, 0, 0, 1); +var hours24 = new Temporal.Duration(0, 0, 0, 0, 24); + +// relativeTo does not affect days if PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(`${ oneDay.add(hours24, { relativeTo }) }`, "P2D"); + +// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(`${ oneDay.add(hours24, { relativeTo }) }`, "P2D"); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var hours12 = new Temporal.Duration(0, 0, 0, 0, 12); +var hours25 = new Temporal.Duration(0, 0, 0, 0, 25); + +// start inside repeated hour, end after +assert.sameValue(`${ hours25.add(oneDay, { relativeTo: inRepeatedHour }) }`, "P2D"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo: inRepeatedHour }) }`, "P2DT1H"); + +// start after repeated hour, end inside (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-31T01:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2DT1H"); +assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2D"); + +// start inside repeated hour, end in skipped hour +assert.sameValue(`${ hours25.add(Temporal.Duration.from({ + days: 125, + hours: 1 +}), { relativeTo: inRepeatedHour }) }`, "P126DT1H"); +assert.sameValue(`${ oneDay.add(Temporal.Duration.from({ + days: 125, + hours: 1 +}), { relativeTo: inRepeatedHour }) }`, "P126DT1H"); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-03-31T02:30").toZonedDateTime(timeZone); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo }) }`, "P2DT1H"); +assert.sameValue(`${ hours25.add(oneDay, { relativeTo }) }`, "P2D"); + +// start before skipped hour, end >1 day after +assert.sameValue(`${ hours25.add(oneDay, { relativeTo: skippedHourDay }) }`, "P2DT2H"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo: skippedHourDay }) }`, "P2DT1H"); + +// start after skipped hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2DT2H"); +assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2DT1H"); + +// start before skipped hour, end <1 day after +assert.sameValue(`${ hours12.add(oneDay, { relativeTo: skippedHourDay }) }`, "P1DT13H"); +assert.sameValue(`${ oneDay.add(hours12, { relativeTo: skippedHourDay }) }`, "P1DT12H"); + +// start after skipped hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().add(oneDay.negated(), { relativeTo }) }`, "-P1DT13H"); +assert.sameValue(`${ oneDay.negated().add(hours12.negated(), { relativeTo }) }`, "-P1DT12H"); + +// start before repeated hour, end >1 day after +assert.sameValue(`${ hours25.add(oneDay, { relativeTo: repeatedHourDay }) }`, "P2D"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo: repeatedHourDay }) }`, "P2DT1H"); + +// start after repeated hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2D"); +assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2DT1H"); + +// start before repeated hour, end <1 day after +assert.sameValue(`${ hours12.add(oneDay, { relativeTo: repeatedHourDay }) }`, "P1DT11H"); +assert.sameValue(`${ oneDay.add(hours12, { relativeTo: repeatedHourDay }) }`, "P1DT12H"); + +// start after repeated hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().add(oneDay.negated(), { relativeTo }) }`, "-P1DT11H"); +assert.sameValue(`${ oneDay.negated().add(hours12.negated(), { relativeTo }) }`, "-P1DT12H"); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +assert.sameValue(`${ hours25.add(oneDay, { relativeTo }) }`, "P3DT1H"); +assert.sameValue(`${ oneDay.add(hours25, { relativeTo }) }`, "P3DT1H"); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(`${ oneDay.add(hours24, { + relativeTo: { + year: 2000, + month: 10, + day: 28, + timeZone + } +}) }`, "P1DT24H"); + +// casts relativeTo to PlainDate if possible +assert.sameValue(`${ oneDay.add(hours24, { relativeTo: "2019-11-02" }) }`, "P2D"); +assert.sameValue(`${ oneDay.add(hours24, { + relativeTo: { + year: 2019, + month: 11, + day: 2 + } +}) }`, "P2D"); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => oneDay.add(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" })); + +// at least the required properties must be present in relativeTo +assert.throws(TypeError, () => oneDay.add(hours24, { + relativeTo: { + month: 11, + day: 3 + } +})); +assert.throws(TypeError, () => oneDay.add(hours24, { + relativeTo: { + year: 2019, + month: 11 + } +})); +assert.throws(TypeError, () => oneDay.add(hours24, { + relativeTo: { + year: 2019, + day: 3 + } +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/browser.js b/js/src/tests/test262/staging/Temporal/Duration/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js b/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js new file mode 100644 index 0000000000..e5137e4452 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.compare() does not lose precision when totaling everything down to nanoseconds +features: [Temporal] +---*/ + +assert.notSameValue(Temporal.Duration.compare({ days: 200 }, { + days: 200, + nanoseconds: 1 +}), 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/limits.js b/js/src/tests/test262/staging/Temporal/Duration/old/limits.js new file mode 100644 index 0000000000..5c1e275430 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/limits.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: min/max values +features: [Temporal] +---*/ + +var units = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds" +]; + +// minimum is zero +assert.sameValue(`${ new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }`, "PT0S"); +units.forEach(unit => assert.sameValue(`${ Temporal.Duration.from({ [unit]: 0 }) }`, "PT0S")); +[ + "P0Y", + "P0M", + "P0W", + "P0D", + "PT0H", + "PT0M", + "PT0S" +].forEach(str => assert.sameValue(`${ Temporal.Duration.from(str) }`, "PT0S")); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/round.js b/js/src/tests/test262/staging/Temporal/Duration/old/round.js new file mode 100644 index 0000000000..ccb04aeffc --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/round.js @@ -0,0 +1,868 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.round() works as expected +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); +var d2 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5); +var relativeTo = Temporal.PlainDate.from("2020-01-01"); + +// succeeds with largestUnit: 'auto' +assert.sameValue(`${ Temporal.Duration.from({ hours: 25 }).round({ largestUnit: "auto" }) }`, "PT25H"); +var hours25 = new Temporal.Duration(0, 0, 0, 0, 25); + +// days are 24 hours if relativeTo not given +assert.sameValue(`${ hours25.round({ largestUnit: "days" }) }`, "P1DT1H"); + +// days are 24 hours if relativeTo is PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P1DT1H"); + +// days are 24 hours if relativeTo is ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P1DT1H"); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var oneDay = new Temporal.Duration(0, 0, 0, 1); +var hours12 = new Temporal.Duration(0, 0, 0, 0, 12); + +// start inside repeated hour, end after +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: inRepeatedHour +}) }`, "P1D"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo: inRepeatedHour +}) }`, "PT25H"); + +// start after repeated hour, end inside (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T01:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-P1D"); +assert.sameValue(`${ oneDay.negated().round({ + largestUnit: "hours", + relativeTo +}) }`, "-PT25H"); + +// start inside repeated hour, end in skipped hour +assert.sameValue(`${ Temporal.Duration.from({ + days: 126, + hours: 1 +}).round({ + largestUnit: "days", + relativeTo: inRepeatedHour +}) }`, "P126DT1H"); +assert.sameValue(`${ Temporal.Duration.from({ + days: 126, + hours: 1 +}).round({ + largestUnit: "hours", + relativeTo: inRepeatedHour +}) }`, "PT3026H"); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P1DT1H"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo +}) }`, "PT24H"); + +// start before skipped hour, end >1 day after +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: skippedHourDay +}) }`, "P1DT2H"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo: skippedHourDay +}) }`, "PT23H"); + +// start after skipped hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-P1DT2H"); +assert.sameValue(`${ oneDay.negated().round({ + largestUnit: "hours", + relativeTo +}) }`, "-PT23H"); + +// start before skipped hour, end <1 day after +assert.sameValue(`${ hours12.round({ + largestUnit: "days", + relativeTo: skippedHourDay +}) }`, "PT12H"); + +// start after skipped hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-PT12H"); + +// start before repeated hour, end >1 day after +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: repeatedHourDay +}) }`, "P1D"); +assert.sameValue(`${ oneDay.round({ + largestUnit: "hours", + relativeTo: repeatedHourDay +}) }`, "PT25H"); + +// start after repeated hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours25.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-P1D"); +assert.sameValue(`${ oneDay.negated().round({ + largestUnit: "hours", + relativeTo +}) }`, "-PT25H"); + +// start before repeated hour, end <1 day after +assert.sameValue(`${ hours12.round({ + largestUnit: "days", + relativeTo: repeatedHourDay +}) }`, "PT12H"); + +// start after repeated hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone); +assert.sameValue(`${ hours12.negated().round({ + largestUnit: "days", + relativeTo +}) }`, "-PT12H"); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo +}) }`, "P2DT1H"); +assert.sameValue(`${ Temporal.Duration.from({ hours: 48 }).round({ + largestUnit: "days", + relativeTo +}) }`, "P3D"); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2000, + month: 10, + day: 29, + timeZone + } +}) }`, "P1D"); + +// casts relativeTo to PlainDate if possible +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: "2019-11-02" +}) }`, "P1DT1H"); +assert.sameValue(`${ hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2019, + month: 11, + day: 2 + } +}) }`, "P1DT1H"); + +// accepts datetime strings or fields for relativeTo +[ + "2020-01-01", + "20200101", + "2020-01-01T00:00:00.000000000", + { + year: 2020, + month: 1, + day: 1 + } +].forEach(relativeTo => { + assert.sameValue(`${ d.round({ + smallestUnit: "seconds", + relativeTo + }) }`, "P5Y6M10DT5H5M5S"); +}); + +// does not accept non-string primitives for relativeTo +[ + 20200101, + 20200101n, + null, + true, +].forEach(relativeTo => { + assert.throws( + TypeError, () => d.round({ smallestUnit: "seconds", relativeTo}) + ); +}); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => d.round({ + smallestUnit: "seconds", + relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" +})); + +// relativeTo object must contain at least the required correctly-spelled properties +assert.throws(TypeError, () => hours25.round({ + largestUnit: "days", + relativeTo: { + month: 11, + day: 3 + } +})); +assert.throws(TypeError, () => hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2019, + month: 11 + } +})); +assert.throws(TypeError, () => hours25.round({ + largestUnit: "days", + relativeTo: { + year: 2019, + day: 3 + } +})); + +// incorrectly-spelled properties are ignored in relativeTo +var oneMonth = Temporal.Duration.from({ months: 1 }); +assert.sameValue(`${ oneMonth.round({ + largestUnit: "days", + relativeTo: { + year: 2020, + month: 1, + day: 1, + months: 2 + } +}) }`, "P31D"); + +// throws if neither one of largestUnit or smallestUnit is given +var hoursOnly = new Temporal.Duration(0, 0, 0, 0, 1); +[ + {}, + () => { + }, + { roundingMode: "ceil" } +].forEach(roundTo => { + assert.throws(RangeError, () => d.round(roundTo)); + assert.throws(RangeError, () => hoursOnly.round(roundTo)); +}); + +// relativeTo not required to round non-calendar units in durations w/o calendar units (string param) +assert.sameValue(`${ d2.round("days") }`, "P5D"); +assert.sameValue(`${ d2.round("hours") }`, "P5DT5H"); +assert.sameValue(`${ d2.round("minutes") }`, "P5DT5H5M"); +assert.sameValue(`${ d2.round("seconds") }`, "P5DT5H5M5S"); +assert.sameValue(`${ d2.round("milliseconds") }`, "P5DT5H5M5.005S"); +assert.sameValue(`${ d2.round("microseconds") }`, "P5DT5H5M5.005005S"); +assert.sameValue(`${ d2.round("nanoseconds") }`, "P5DT5H5M5.005005005S"); + +// relativeTo is required to round calendar units even in durations w/o calendar units (string param) +assert.throws(RangeError, () => d2.round("years")); +assert.throws(RangeError, () => d2.round("months")); +assert.throws(RangeError, () => d2.round("weeks")); + +// relativeTo not required to round non-calendar units in durations w/o calendar units (object param) +assert.sameValue(`${ d2.round({ smallestUnit: "days" }) }`, "P5D"); +assert.sameValue(`${ d2.round({ smallestUnit: "hours" }) }`, "P5DT5H"); +assert.sameValue(`${ d2.round({ smallestUnit: "minutes" }) }`, "P5DT5H5M"); +assert.sameValue(`${ d2.round({ smallestUnit: "seconds" }) }`, "P5DT5H5M5S"); +assert.sameValue(`${ d2.round({ smallestUnit: "milliseconds" }) }`, "P5DT5H5M5.005S"); +assert.sameValue(`${ d2.round({ smallestUnit: "microseconds" }) }`, "P5DT5H5M5.005005S"); +assert.sameValue(`${ d2.round({ smallestUnit: "nanoseconds" }) }`, "P5DT5H5M5.005005005S"); + +// relativeTo is required to round calendar units even in durations w/o calendar units (object param) +assert.throws(RangeError, () => d2.round({ smallestUnit: "years" })); +assert.throws(RangeError, () => d2.round({ smallestUnit: "months" })); +assert.throws(RangeError, () => d2.round({ smallestUnit: "weeks" })); + +// relativeTo is required for rounding durations with calendar units +assert.throws(RangeError, () => d.round({ largestUnit: "years" })); +assert.throws(RangeError, () => d.round({ largestUnit: "months" })); +assert.throws(RangeError, () => d.round({ largestUnit: "weeks" })); +assert.throws(RangeError, () => d.round({ largestUnit: "days" })); +assert.throws(RangeError, () => d.round({ largestUnit: "hours" })); +assert.throws(RangeError, () => d.round({ largestUnit: "minutes" })); +assert.throws(RangeError, () => d.round({ largestUnit: "seconds" })); +assert.throws(RangeError, () => d.round({ largestUnit: "milliseconds" })); +assert.throws(RangeError, () => d.round({ largestUnit: "microseconds" })); +assert.throws(RangeError, () => d.round({ largestUnit: "nanoseconds" })); + +// durations do not balance beyond their current largest unit by default +var relativeTo = Temporal.PlainDate.from("2020-01-01"); +var fortyDays = Temporal.Duration.from({ days: 40 }); +assert.sameValue(`${ fortyDays.round({ smallestUnit: "seconds" }) }`, "P40D"); +var roundAndBalanceResults = { + years: { + years: "P6Y", + months: "P5Y6M", + weeks: "P5Y5M6W", + days: "P5Y6M10D", + hours: "P5Y6M10DT5H", + minutes: "P5Y6M10DT5H5M", + seconds: "P5Y6M10DT5H5M5S", + milliseconds: "P5Y6M10DT5H5M5.005S", + microseconds: "P5Y6M10DT5H5M5.005005S", + nanoseconds: "P5Y6M10DT5H5M5.005005005S" + }, + months: { + months: "P66M", + weeks: "P65M6W", + days: "P66M10D", + hours: "P66M10DT5H", + minutes: "P66M10DT5H5M", + seconds: "P66M10DT5H5M5S", + milliseconds: "P66M10DT5H5M5.005S", + microseconds: "P66M10DT5H5M5.005005S", + nanoseconds: "P66M10DT5H5M5.005005005S" + }, + weeks: { + weeks: "P288W", + days: "P288W2D", + hours: "P288W2DT5H", + minutes: "P288W2DT5H5M", + seconds: "P288W2DT5H5M5S", + milliseconds: "P288W2DT5H5M5.005S", + microseconds: "P288W2DT5H5M5.005005S", + nanoseconds: "P288W2DT5H5M5.005005005S" + }, + days: { + days: "P2018D", + hours: "P2018DT5H", + minutes: "P2018DT5H5M", + seconds: "P2018DT5H5M5S", + milliseconds: "P2018DT5H5M5.005S", + microseconds: "P2018DT5H5M5.005005S", + nanoseconds: "P2018DT5H5M5.005005005S" + }, + hours: { + hours: "PT48437H", + minutes: "PT48437H5M", + seconds: "PT48437H5M5S", + milliseconds: "PT48437H5M5.005S", + microseconds: "PT48437H5M5.005005S", + nanoseconds: "PT48437H5M5.005005005S" + }, + minutes: { + minutes: "PT2906225M", + seconds: "PT2906225M5S", + milliseconds: "PT2906225M5.005S", + microseconds: "PT2906225M5.005005S", + nanoseconds: "PT2906225M5.005005005S" + }, + seconds: { + seconds: "PT174373505S", + milliseconds: "PT174373505.005S", + microseconds: "PT174373505.005005S", + nanoseconds: "PT174373505.005005005S" + }, + milliseconds: { + milliseconds: "PT174373505.005S", + microseconds: "PT174373505.005005S", + nanoseconds: "PT174373505.005005005S" + } +}; +for (var [largestUnit, entry] of Object.entries(roundAndBalanceResults)) { + for (var [smallestUnit, expected] of Object.entries(entry)) { + assert.sameValue(`${ d.round({ + largestUnit, + smallestUnit, + relativeTo + }) }`, expected); + } +} +var balanceLosePrecisionResults = { + microseconds: [ + "microseconds", + "nanoseconds" + ], + nanoseconds: ["nanoseconds"] +}; + +// Round may lose precision below ms +for (var [largestUnit, entry] of Object.entries(balanceLosePrecisionResults)) { + for (var smallestUnit of entry) { + assert(`${ d.round({ + largestUnit, + smallestUnit, + relativeTo + }) }`.startsWith("PT174373505.005")); + } +} + +// halfExpand is the default +assert.sameValue(`${ d.round({ + smallestUnit: "years", + relativeTo +}) }`, "P6Y"); +assert.sameValue(`${ d.negated().round({ + smallestUnit: "years", + relativeTo +}) }`, "-P6Y"); + +// balances up differently depending on relativeTo +var fortyDays = Temporal.Duration.from({ days: 40 }); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-01-01" +}) }`, "P1M9D"); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-02-01" +}) }`, "P1M11D"); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-03-01" +}) }`, "P1M9D"); +assert.sameValue(`${ fortyDays.round({ + largestUnit: "years", + relativeTo: "2020-04-01" +}) }`, "P1M10D"); +var minusForty = Temporal.Duration.from({ days: -40 }); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-02-01" +}) }`, "-P1M9D"); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-01-01" +}) }`, "-P1M9D"); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-03-01" +}) }`, "-P1M11D"); +assert.sameValue(`${ minusForty.round({ + largestUnit: "years", + relativeTo: "2020-04-01" +}) }`, "-P1M9D"); + +// balances up to the next unit after rounding +var almostWeek = Temporal.Duration.from({ + days: 6, + hours: 20 +}); +assert.sameValue(`${ almostWeek.round({ + largestUnit: "weeks", + smallestUnit: "days", + relativeTo: "2020-01-01" +}) }`, "P1W"); + +// balances days up to both years and months +var twoYears = Temporal.Duration.from({ + months: 11, + days: 396 +}); +assert.sameValue(`${ twoYears.round({ + largestUnit: "years", + relativeTo: "2017-01-01" +}) }`, "P2Y"); + +// does not balance up to weeks if largestUnit is larger than weeks +var monthAlmostWeek = Temporal.Duration.from({ + months: 1, + days: 6, + hours: 20 +}); +assert.sameValue(`${ monthAlmostWeek.round({ + smallestUnit: "days", + relativeTo: "2020-01-01" +}) }`, "P1M7D"); + +// balances down differently depending on relativeTo +var oneYear = Temporal.Duration.from({ years: 1 }); +assert.sameValue(`${ oneYear.round({ + largestUnit: "days", + relativeTo: "2019-01-01" +}) }`, "P365D"); +assert.sameValue(`${ oneYear.round({ + largestUnit: "days", + relativeTo: "2019-07-01" +}) }`, "P366D"); +var minusYear = Temporal.Duration.from({ years: -1 }); +assert.sameValue(`${ minusYear.round({ + largestUnit: "days", + relativeTo: "2020-01-01" +}) }`, "-P365D"); +assert.sameValue(`${ minusYear.round({ + largestUnit: "days", + relativeTo: "2020-07-01" +}) }`, "-P366D"); + +// rounds to an increment of hours +assert.sameValue(`${ d.round({ + smallestUnit: "hours", + roundingIncrement: 3, + relativeTo +}) }`, "P5Y6M10DT6H"); + +// rounds to an increment of minutes +assert.sameValue(`${ d.round({ + smallestUnit: "minutes", + roundingIncrement: 30, + relativeTo +}) }`, "P5Y6M10DT5H"); + +// rounds to an increment of seconds +assert.sameValue(`${ d.round({ + smallestUnit: "seconds", + roundingIncrement: 15, + relativeTo +}) }`, "P5Y6M10DT5H5M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ d.round({ + smallestUnit: "milliseconds", + roundingIncrement: 10, + relativeTo +}) }`, "P5Y6M10DT5H5M5.01S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ d.round({ + smallestUnit: "microseconds", + roundingIncrement: 10, + relativeTo +}) }`, "P5Y6M10DT5H5M5.00501S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ d.round({ + smallestUnit: "nanoseconds", + roundingIncrement: 10, + relativeTo +}) }`, "P5Y6M10DT5H5M5.00500501S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + smallestUnit: "hours", + roundingIncrement, + relativeTo + }; + assert(d.round(options) instanceof Temporal.Duration); +}); +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + // valid minutes/seconds increments divide into 60 + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var roundTo = { + smallestUnit, + roundingIncrement, + relativeTo + }; + assert(d.round(roundTo) instanceof Temporal.Duration); + }); + }); +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + // valid milliseconds/microseconds/nanoseconds increments divide into 1000 + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var roundTo = { + smallestUnit, + roundingIncrement, + relativeTo + }; + assert(d.round(roundTo) instanceof Temporal.Duration); + }); + }); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => d.round({ + relativeTo, + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +// accepts singular units +assert.sameValue(`${ d.round({ + largestUnit: "year", + relativeTo +}) }`, `${ d.round({ + largestUnit: "years", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "year", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "years", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "month", + relativeTo +}) }`, `${ d.round({ + largestUnit: "months", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "month", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "months", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "day", + relativeTo +}) }`, `${ d.round({ + largestUnit: "days", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "day", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "days", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "hour", + relativeTo +}) }`, `${ d.round({ + largestUnit: "hours", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "hour", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "hours", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "minute", + relativeTo +}) }`, `${ d.round({ + largestUnit: "minutes", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "minute", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "minutes", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "second", + relativeTo +}) }`, `${ d.round({ + largestUnit: "seconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "second", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "seconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "millisecond", + relativeTo +}) }`, `${ d.round({ + largestUnit: "milliseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "millisecond", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "milliseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "microsecond", + relativeTo +}) }`, `${ d.round({ + largestUnit: "microseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "microsecond", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "microseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + largestUnit: "nanosecond", + relativeTo +}) }`, `${ d.round({ + largestUnit: "nanoseconds", + relativeTo +}) }`); +assert.sameValue(`${ d.round({ + smallestUnit: "nanosecond", + relativeTo +}) }`, `${ d.round({ + smallestUnit: "nanoseconds", + relativeTo +}) }`); + +// counts the correct number of days when rounding relative to a date +var days = Temporal.Duration.from({ days: 45 }); +assert.sameValue(`${ days.round({ + relativeTo: "2019-01-01", + smallestUnit: "months" +}) }`, "P2M"); +assert.sameValue(`${ days.negated().round({ + relativeTo: "2019-02-15", + smallestUnit: "months" +}) }`, "-P1M"); +var yearAndHalf = Temporal.Duration.from({ + days: 547, + hours: 12 +}); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2018-01-01", + smallestUnit: "years" +}) }`, "P2Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2018-07-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2019-01-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2019-07-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2020-01-01", + smallestUnit: "years" +}) }`, "P1Y"); +assert.sameValue(`${ yearAndHalf.round({ + relativeTo: "2020-07-01", + smallestUnit: "years" +}) }`, "P2Y"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/shell.js b/js/src/tests/test262/staging/Temporal/Duration/old/shell.js new file mode 100644 index 0000000000..60f74c2518 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/shell.js @@ -0,0 +1,2158 @@ +// GENERATED, DO NOT EDIT +// file: temporalHelpers.js +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + This defines helper objects and functions for testing Temporal. +defines: [TemporalHelpers] +features: [Symbol.species, Symbol.iterator, Temporal] +---*/ + +const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; + +function formatPropertyName(propertyKey, objectName = "") { + switch (typeof propertyKey) { + case "symbol": + if (Symbol.keyFor(propertyKey) !== undefined) { + return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; + } else if (propertyKey.description.startsWith('Symbol.')) { + return `${objectName}[${propertyKey.description}]`; + } else { + return `${objectName}[Symbol('${propertyKey.description}')]` + } + case "string": + if (propertyKey !== String(Number(propertyKey))) { + if (ASCII_IDENTIFIER.test(propertyKey)) { + return objectName ? `${objectName}.${propertyKey}` : propertyKey; + } + return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']` + } + // fall through + default: + // integer or string integer-index + return `${objectName}[${propertyKey}]`; + } +} + +const SKIP_SYMBOL = Symbol("Skip"); + +var TemporalHelpers = { + /* + * Codes and maximum lengths of months in the ISO 8601 calendar. + */ + ISOMonths: [ + { month: 1, monthCode: "M01", daysInMonth: 31 }, + { month: 2, monthCode: "M02", daysInMonth: 29 }, + { month: 3, monthCode: "M03", daysInMonth: 31 }, + { month: 4, monthCode: "M04", daysInMonth: 30 }, + { month: 5, monthCode: "M05", daysInMonth: 31 }, + { month: 6, monthCode: "M06", daysInMonth: 30 }, + { month: 7, monthCode: "M07", daysInMonth: 31 }, + { month: 8, monthCode: "M08", daysInMonth: 31 }, + { month: 9, monthCode: "M09", daysInMonth: 30 }, + { month: 10, monthCode: "M10", daysInMonth: 31 }, + { month: 11, monthCode: "M11", daysInMonth: 30 }, + { month: 12, monthCode: "M12", daysInMonth: 31 } + ], + + /* + * assertDuration(duration, years, ..., nanoseconds[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * an expected value. + */ + assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, hours, `${prefix}hours result:`); + assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); + assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); + assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); + assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); + assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); + }, + + /* + * assertDateDuration(duration, years, months, weeks, days, [, description]): + * + * Shorthand for asserting that each date field of a Temporal.Duration is + * equal to an expected value. + */ + assertDateDuration(duration, years, months, weeks, days, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); + assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); + assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); + assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); + assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); + assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); + }, + + /* + * assertDurationsEqual(actual, expected[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * the corresponding field in another Temporal.Duration. + */ + assertDurationsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); + TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); + }, + + /* + * assertInstantsEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.Instants are of the correct type + * and equal according to their equals() methods. + */ + assertInstantsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); + assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDate is equal to + * an expected value. (Except the `calendar` property, since callers may want + * to assert either object equality with an object they put in there, or the + * value of date.calendarId.) + */ + assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert.sameValue(date.era, era, `${prefix}era result:`); + assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(date.year, year, `${prefix}year result:`); + assert.sameValue(date.month, month, `${prefix}month result:`); + assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(date.day, day, `${prefix}day result:`); + }, + + /* + * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDateTime is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of datetime.calendarId.) + */ + assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert.sameValue(datetime.era, era, `${prefix}era result:`); + assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(datetime.year, year, `${prefix}year result:`); + assert.sameValue(datetime.month, month, `${prefix}month result:`); + assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(datetime.day, day, `${prefix}day result:`); + assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); + assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); + assert.sameValue(datetime.second, second, `${prefix}second result:`); + assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); + assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainMonthDay is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of monthDay.calendarId().) + */ + assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { + const prefix = description ? `${description}: ` : ""; + assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); + assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(monthDay.day, day, `${prefix}day result:`); + assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); + }, + + /* + * assertPlainTime(time, hour, ..., nanosecond[, description]): + * + * Shorthand for asserting that each field of a Temporal.PlainTime is equal to + * an expected value. + */ + assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert.sameValue(time.hour, hour, `${prefix}hour result:`); + assert.sameValue(time.minute, minute, `${prefix}minute result:`); + assert.sameValue(time.second, second, `${prefix}second result:`); + assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainTimes are of the correct + * type and equal according to their equals() methods. + */ + assertPlainTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); + assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): + * + * Shorthand for asserting that each field of a Temporal.PlainYearMonth is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of yearMonth.calendarId.) + */ + assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { + const prefix = description ? `${description}: ` : ""; + assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); + assert.sameValue(yearMonth.era, era, `${prefix}era result:`); + assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(yearMonth.year, year, `${prefix}year result:`); + assert.sameValue(yearMonth.month, month, `${prefix}month result:`); + assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`); + }, + + /* + * assertZonedDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their time zones and calendar internal slots are the same value. + */ + assertZonedDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); + assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertUnreachable(description): + * + * Helper for asserting that code is not executed. This is useful for + * assertions that methods of user calendars and time zones are not called. + */ + assertUnreachable(description) { + let message = "This code should not be executed"; + if (description) { + message = `${message}: ${description}`; + } + throw new Test262Error(message); + }, + + /* + * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls): + * + * When an options object with a largestUnit property is synthesized inside + * Temporal and passed to user code such as calendar.dateUntil(), the value of + * the largestUnit property should be in the singular form, even if the input + * was given in the plural form. + * (This doesn't apply when the options object is passed through verbatim.) + * + * func(calendar, largestUnit, index) is the operation under test. It's called + * with an instance of a calendar that keeps track of which largestUnit is + * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and + * the key's numerical index in case the function needs to generate test data + * based on the index. At the end, the actual values passed to dateUntil() are + * compared with the array values of expectedLargestUnitCalls. + */ + checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) { + const actual = []; + + class DateUntilOptionsCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(earlier, later, options) { + actual.push(options.largestUnit); + return super.dateUntil(earlier, later, options); + } + + toString() { + return "date-until-options"; + } + } + + const calendar = new DateUntilOptionsCalendar(); + Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => { + func(calendar, largestUnit, index); + assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`); + actual.splice(0); // empty it for the next check + }); + }, + + /* + * checkPlainDateTimeConversionFastPath(func): + * + * ToTemporalDate and ToTemporalTime should both, if given a + * Temporal.PlainDateTime instance, convert to the desired type by reading the + * PlainDateTime's internal slots, rather than calling any getters. + * + * func(datetime, calendar) is the actual operation to test, that must + * internally call the abstract operation ToTemporalDate or ToTemporalTime. + * It is passed a Temporal.PlainDateTime instance, as well as the instance's + * calendar object (so that it doesn't have to call the calendar getter itself + * if it wants to make any assertions about the calendar.) + */ + checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); + ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(datetime, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${formatPropertyName(property)}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${formatPropertyName(property)}`); + return value; + }, + }; + }, + }); + }); + Object.defineProperty(datetime, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(datetime, calendar); + assert.compareArray(actual, expected, `${message}: property getters not called`); + }, + + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: 'years', + month: 'months', + week: 'weeks', + day: 'days', + hour: 'hours', + minute: 'minutes', + second: 'seconds', + millisecond: 'milliseconds', + microsecond: 'microseconds', + nanosecond: 'nanoseconds', + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; + if (singularValue instanceof Temporal.Duration) { + TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.Instant) { + TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainDateTime) { + TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainTime) { + TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.ZonedDateTime) { + TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + + /* + * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): + * + * Checks the type handling of the roundingIncrement option. + * checkFunc(roundingIncrement) is a function which takes the value of + * roundingIncrement to test, and calls the method under test with it, + * returning the result. assertTrueResultFunc(result, description) should + * assert that result is the expected result with roundingIncrement: true, and + * assertObjectResultFunc(result, description) should assert that result is + * the expected result with roundingIncrement being an object with a valueOf() + * method. + */ + checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { + // null converts to 0, which is out of range + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to either 0 or 1, and 1 is allowed + const trueResult = checkFunc(true); + assertTrueResultFunc(trueResult, "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols and BigInts cannot convert to numbers + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + assert.throws(TypeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their valueOf() methods when converting to a number + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + "get roundingIncrement.valueOf", + "call roundingIncrement.valueOf", + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); + const objectResult = checkFunc(observer); + assertObjectResultFunc(objectResult, "object with valueOf"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): + * + * Checks the type handling of a string option, of which there are several in + * Temporal. + * propertyName is the name of the option, and value is the value that + * assertFunc should expect it to have. + * checkFunc(value) is a function which takes the value of the option to test, + * and calls the method under test with it, returning the result. + * assertFunc(result, description) should assert that result is the expected + * result with the option value being an object with a toString() method + * which returns the given value. + */ + checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { + // null converts to the string "null", which is an invalid string value + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to the strings "true" or "false", which are invalid + assert.throws(RangeError, () => checkFunc(true), "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols cannot convert to strings + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + // Numbers convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2), "number"); + // BigInts convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their toString() methods when converting to a string + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + `get ${propertyName}.toString`, + `call ${propertyName}.toString`, + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); + const result = checkFunc(observer); + assertFunc(result, "object with toString"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, + * resultAssertions): + * + * Methods of Temporal classes that return a new instance of the same class, + * must not take the constructor of a subclass into account, nor the @@species + * property. This helper runs tests to ensure this. + * + * construct(...constructArgs) must yield a valid instance of the Temporal + * class. instance[method](...methodArgs) is the method call under test, which + * must also yield a valid instance of the same Temporal class, not a + * subclass. See below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnored(...args) { + this.checkSubclassConstructorNotObject(...args); + this.checkSubclassConstructorUndefined(...args); + this.checkSubclassConstructorThrows(...args); + this.checkSubclassConstructorNotCalled(...args); + this.checkSubclassSpeciesInvalidResult(...args); + this.checkSubclassSpeciesNotAConstructor(...args); + this.checkSubclassSpeciesNull(...args); + this.checkSubclassSpeciesUndefined(...args); + this.checkSubclassSpeciesThrows(...args); + }, + + /* + * Checks that replacing the 'constructor' property of the instance with + * various primitive values does not affect the returned new instance. + */ + checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = value; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + }, + + /* + * Checks that replacing the 'constructor' property of the subclass with + * undefined does not affect the returned new instance. + */ + checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = undefined; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that making the 'constructor' property of the instance throw when + * called does not affect the returned new instance. + */ + checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + const instance = new construct(...constructArgs); + Object.defineProperty(instance, "constructor", { + get() { + throw new CustomError(); + } + }); + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that when subclassing, the subclass constructor is not called by + * the method under test. + */ + checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's a + * constructor that returns a non-object value. + */ + checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: function() { + return value; + }, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's not a + * constructor. + */ + checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: value, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's null. + */ + checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: null, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's + * undefined. + */ + checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: undefined, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it throws, + * i.e. it is not called at all. + */ + checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + + const instance = new construct(...constructArgs); + instance.constructor = { + get [Symbol.species]() { + throw new CustomError(); + }, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + }, + + /* + * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): + * + * Static methods of Temporal classes that return a new instance of the class, + * must not use the this-value as a constructor. This helper runs tests to + * ensure this. + * + * construct[method](...methodArgs) is the static method call under test, and + * must yield a valid instance of the Temporal class, not a subclass. See + * below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnoredStatic(...args) { + this.checkStaticInvalidReceiver(...args); + this.checkStaticReceiverNotCalled(...args); + this.checkThisValueNotCalled(...args); + }, + + /* + * Check that calling the static method with a receiver that's not callable, + * still calls the intrinsic constructor. + */ + checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const result = construct[method].apply(value, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that calling the static method with a receiver that returns a value + * that's not callable, still calls the intrinsic constructor. + */ + checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const receiver = function () { + return value; + }; + const result = construct[method].apply(receiver, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that the receiver isn't called. + */ + checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { + let called = false; + + class MySubclass extends construct { + constructor(...args) { + called = true; + super(...args); + } + } + + const result = MySubclass[method](...methodArgs); + assert.sameValue(called, false); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that any iterable returned from a custom time zone's + * getPossibleInstantsFor() method is exhausted. + * The custom time zone object is passed in to func(). + * expected is an array of strings representing the expected calls to the + * getPossibleInstantsFor() method. The PlainDateTimes that it is called with, + * are compared (using their toString() results) with the array. + */ + checkTimeZonePossibleInstantsIterable(func, expected) { + // A custom time zone that returns an iterable instead of an array from its + // getPossibleInstantsFor() method, and for testing purposes skips + // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on + // January 3, 2030. Otherwise identical to the UTC time zone. + class TimeZonePossibleInstantsIterable extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.getPossibleInstantsForCallCount = 0; + this.getPossibleInstantsForCalledWith = []; + this.getPossibleInstantsForReturns = []; + this.iteratorExhausted = []; + } + + toString() { + return "Custom/Iterable"; + } + + getOffsetNanosecondsFor(instant) { + if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 && + Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) { + return 3600_000_000_000; + } else { + return 0; + } + } + + getPossibleInstantsFor(dateTime) { + this.getPossibleInstantsForCallCount++; + this.getPossibleInstantsForCalledWith.push(dateTime); + + // Fake DST transition + let retval = super.getPossibleInstantsFor(dateTime); + if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) { + retval = []; + } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) { + retval.push(retval[0].subtract({ hours: 1 })); + } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) { + retval[0] = retval[0].subtract({ hours: 1 }); + } + + this.getPossibleInstantsForReturns.push(retval); + this.iteratorExhausted.push(false); + return { + callIndex: this.getPossibleInstantsForCallCount - 1, + timeZone: this, + *[Symbol.iterator]() { + yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex]; + this.timeZone.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + + const timeZone = new TimeZonePossibleInstantsIterable(); + func(timeZone); + + assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times"); + + for (let index = 0; index < expected.length; index++) { + assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime"); + assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable"); + } + }, + + /* + * Check that any calendar-carrying Temporal object has its [[Calendar]] + * internal slot read by ToTemporalCalendar, and does not fetch the calendar + * by calling getters. + * The custom calendar object is passed in to func() so that it can do its + * own additional assertions involving the calendar if necessary. (Sometimes + * there is nothing to assert as the calendar isn't stored anywhere that can + * be asserted about.) + */ + checkToTemporalCalendarFastPath(func) { + class CalendarFastPathCheck extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + monthDayFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + + yearMonthFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + + toString() { + return "fast-path-check"; + } + } + const calendar = new CalendarFastPathCheck(); + + const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar); + const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar); + const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar); + const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + + [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { + const actual = []; + const expected = []; + + Object.defineProperty(temporalObject, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(temporalObject, calendar); + assert.compareArray(actual, expected, "calendar getter not called"); + }); + }, + + checkToTemporalInstantFastPath(func) { + const actual = []; + const expected = []; + + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + Object.defineProperty(datetime, 'toString', { + get() { + actual.push("get toString"); + return function (options) { + actual.push("call toString"); + return Temporal.ZonedDateTime.prototype.toString.call(this, options); + }; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, "toString not called"); + }, + + checkToTemporalPlainDateTimeFastPath(func) { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const date = new Temporal.PlainDate(2000, 5, 2, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); + ["year", "month", "monthCode", "day"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return TemporalHelpers.toPrimitiveObserver(actual, value, property); + }, + }); + }); + ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + return undefined; + }, + }); + }); + Object.defineProperty(date, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(date, calendar); + assert.compareArray(actual, expected, "property getters not called"); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * fromFields methods are always called with a null-prototype fields object. + */ + calendarCheckFieldsPrototypePollution() { + class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + } + + // toString must remain "iso8601", so that some methods don't throw due to + // incompatible calendars + + dateFromFields(fields, options = {}) { + this.dateFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options = {}) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options = {}) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object"); + return super.monthDayFromFields(fields, options); + } + } + + return new CalendarCheckFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * mergeFields() method is always called with null-prototype fields objects. + */ + calendarCheckMergeFieldsPrototypePollution() { + class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsCallCount = 0; + } + + toString() { + return "merge-fields-null-proto"; + } + + mergeFields(fields, additionalFields) { + this.mergeFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)"); + assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)"); + return super.mergeFields(fields, additionalFields); + } + } + + return new CalendarCheckMergeFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that methods + * are always called with a null-prototype options object. + */ + calendarCheckOptionsPrototypePollution() { + class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.yearMonthFromFieldsCallCount = 0; + this.dateUntilCallCount = 0; + } + + toString() { + return "options-null-proto"; + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options"); + return super.yearMonthFromFields(fields, options); + } + + dateUntil(one, two, options) { + this.dateUntilCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options"); + return super.dateUntil(one, two, options); + } + } + + return new CalendarCheckOptionsPrototypePollution(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with the + * options parameter having the value undefined. + */ + calendarDateAddUndefinedOptions() { + class CalendarDateAddUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + } + + toString() { + return "dateadd-undef-options"; + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert.sameValue(options, undefined, "dateAdd shouldn't be called with options"); + return super.dateAdd(date, duration, options); + } + } + return new CalendarDateAddUndefinedOptions(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with a + * PlainDate instance. Optionally, it also asserts that the PlainDate instance + * is the specific object `this.specificPlainDate`, if it is set by the + * calling code. + */ + calendarDateAddPlainDateInstance() { + class CalendarDateAddPlainDateInstance extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + this.specificPlainDate = undefined; + } + + toString() { + return "dateadd-plain-date-instance"; + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance"); + if (this.dateAddCallCount === 1 && this.specificPlainDate) { + assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`); + } + return super.dateAdd(date, duration, options).withCalendar(this); + } + } + return new CalendarDateAddPlainDateInstance(); + }, + + /* + * A custom calendar that returns an iterable instead of an array from its + * fields() method, otherwise identical to the ISO calendar. + */ + calendarFieldsIterable() { + class CalendarFieldsIterable extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.fieldsCallCount = 0; + this.fieldsCalledWith = []; + this.iteratorExhausted = []; + } + + toString() { + return "fields-iterable"; + } + + fields(fieldNames) { + this.fieldsCallCount++; + this.fieldsCalledWith.push(fieldNames.slice()); + this.iteratorExhausted.push(false); + return { + callIndex: this.fieldsCallCount - 1, + calendar: this, + *[Symbol.iterator]() { + yield* this.calendar.fieldsCalledWith[this.callIndex]; + this.calendar.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + return new CalendarFieldsIterable(); + }, + + /* + * A custom calendar that asserts its ...FromFields() methods are called with + * the options parameter having the value undefined. + */ + calendarFromFieldsUndefinedOptions() { + class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "from-fields-undef-options"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options"); + return super.monthDayFromFields(fields, options); + } + } + return new CalendarFromFieldsUndefinedOptions(); + }, + + /* + * A custom calendar that modifies the fields object passed in to + * dateFromFields, sabotaging its time properties. + */ + calendarMakeInfinityTime() { + class CalendarMakeInfinityTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + fields.hour = Infinity; + fields.minute = Infinity; + fields.second = Infinity; + fields.millisecond = Infinity; + fields.microsecond = Infinity; + fields.nanosecond = Infinity; + return retval; + } + } + return new CalendarMakeInfinityTime(); + }, + + /* + * A custom calendar that defines getters on the fields object passed into + * dateFromFields that throw, sabotaging its time properties. + */ + calendarMakeInvalidGettersTime() { + class CalendarMakeInvalidGettersTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + const throwingDescriptor = { + get() { + throw new Test262Error("reading a sabotaged time field"); + }, + }; + Object.defineProperties(fields, { + hour: throwingDescriptor, + minute: throwingDescriptor, + second: throwingDescriptor, + millisecond: throwingDescriptor, + microsecond: throwingDescriptor, + nanosecond: throwingDescriptor, + }); + return retval; + } + } + return new CalendarMakeInvalidGettersTime(); + }, + + /* + * A custom calendar whose mergeFields() method returns a proxy object with + * all of its Get and HasProperty operations observable, as well as adding a + * "shouldNotBeCopied": true property. + */ + calendarMergeFieldsGetters() { + class CalendarMergeFieldsGetters extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsReturnOperations = []; + } + + toString() { + return "merge-fields-getters"; + } + + dateFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.monthDayFromFields(fields, options); + } + + mergeFields(fields, additionalFields) { + const retval = super.mergeFields(fields, additionalFields); + retval._calendar = this; + retval.shouldNotBeCopied = true; + return new Proxy(retval, { + get(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`get ${key}`); + const result = target[key]; + if (result === undefined) { + return undefined; + } + return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key); + }, + has(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`has ${key}`); + return key in target; + }, + }); + } + } + return new CalendarMergeFieldsGetters(); + }, + + /* + * A custom calendar whose mergeFields() method returns a primitive value, + * given by @primitive, and which records the number of calls made to its + * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods. + */ + calendarMergeFieldsReturnsPrimitive(primitive) { + class CalendarMergeFieldsPrimitive extends Temporal.Calendar { + constructor(mergeFieldsReturnValue) { + super("iso8601"); + this._mergeFieldsReturnValue = mergeFieldsReturnValue; + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "merge-fields-primitive"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + return super.monthDayFromFields(fields, options); + } + + mergeFields() { + return this._mergeFieldsReturnValue; + } + } + return new CalendarMergeFieldsPrimitive(primitive); + }, + + /* + * A custom calendar whose fields() method returns the same value as the + * iso8601 calendar, with the addition of extraFields provided as parameter. + */ + calendarWithExtraFields(fields) { + class CalendarWithExtraFields extends Temporal.Calendar { + constructor(extraFields) { + super("iso8601"); + this._extraFields = extraFields; + } + + fields(fieldNames) { + return super.fields(fieldNames).concat(this._extraFields); + } + } + + return new CalendarWithExtraFields(fields); + }, + + /* + * crossDateLineTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single transition where the time zone moves from one side of the + * International Date Line to the other, for the purpose of testing time zone + * calculations without depending on system time zone data. + * + * The transition occurs at epoch second 1325239200 and goes from offset + * -10:00 to +14:00. In other words, the time zone skips the whole calendar + * day of 2011-12-30. This is the same as the real-life transition in the + * Pacific/Apia time zone. + */ + crossDateLineTimeZone() { + const { compare } = Temporal.PlainDate; + const skippedDay = new Temporal.PlainDate(2011, 12, 30); + const transitionEpoch = 1325239200_000_000_000n; + const beforeOffset = new Temporal.TimeZone("-10:00"); + const afterOffset = new Temporal.TimeZone("+14:00"); + + class CrossDateLineTimeZone extends Temporal.TimeZone { + constructor() { + super("+14:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < transitionEpoch) { + return beforeOffset.getOffsetNanosecondsFor(instant); + } + return afterOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + const comparison = compare(datetime.toPlainDate(), skippedDay); + if (comparison === 0) { + return []; + } + if (comparison < 0) { + return [beforeOffset.getInstantFor(datetime)]; + } + return [afterOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + toString() { + return "Custom/Date_Line"; + } + } + return new CrossDateLineTimeZone(); + }, + + /* + * observeProperty(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls to its accessors to the array @calls. + */ + observeProperty(calls, object, propertyName, value, objectName = "") { + Object.defineProperty(object, propertyName, { + get() { + calls.push(`get ${formatPropertyName(propertyName, objectName)}`); + return value; + }, + set(v) { + calls.push(`set ${formatPropertyName(propertyName, objectName)}`); + } + }); + }, + + /* + * observeMethod(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls of @value to the array @calls. + */ + observeMethod(calls, object, propertyName, objectName = "") { + const method = object[propertyName]; + object[propertyName] = function () { + calls.push(`call ${formatPropertyName(propertyName, objectName)}`); + return method.apply(object, arguments); + }; + }, + + /* + * Used for substituteMethod to indicate default behavior instead of a + * substituted value + */ + SUBSTITUTE_SKIP: SKIP_SYMBOL, + + /* + * substituteMethod(object, propertyName, values): + * + * Defines an own property @object.@propertyName that will, for each + * subsequent call to the method previously defined as + * @object.@propertyName: + * - Call the method, if no more values remain + * - Call the method, if the value in @values for the corresponding call + * is SUBSTITUTE_SKIP + * - Otherwise, return the corresponding value in @value + */ + substituteMethod(object, propertyName, values) { + let calls = 0; + const method = object[propertyName]; + object[propertyName] = function () { + if (calls >= values.length) { + return method.apply(object, arguments); + } else if (values[calls] === SKIP_SYMBOL) { + calls++; + return method.apply(object, arguments); + } else { + return values[calls++]; + } + }; + }, + + /* + * calendarObserver: + * A custom calendar that behaves exactly like the ISO 8601 calendar but + * tracks calls to any of its methods, and Get/Has operations on its + * properties, by appending messages to an array. This is for the purpose of + * testing order of operations that are observable from user code. + * objectName is used in the log. + */ + calendarObserver(calls, objectName, methodOverrides = {}) { + function removeExtraHasPropertyChecks(objectName, calls) { + // Inserting the tracking calendar into the return values of methods + // that we chain up into the ISO calendar for, causes extra HasProperty + // checks, which we observe. This removes them so that we don't leak + // implementation details of the helper into the test code. + assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.year`); + assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.monthCode`); + assert.sameValue(calls.pop(), `has ${objectName}.month`); + assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`); + assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`); + assert.sameValue(calls.pop(), `has ${objectName}.id`); + assert.sameValue(calls.pop(), `has ${objectName}.fields`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.day`); + assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`); + assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`); + } + + const iso8601 = new Temporal.Calendar("iso8601"); + const trackingMethods = { + dateFromFields(...args) { + calls.push(`call ${objectName}.dateFromFields`); + if ('dateFromFields' in methodOverrides) { + const value = methodOverrides.dateFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + yearMonthFromFields(...args) { + calls.push(`call ${objectName}.yearMonthFromFields`); + if ('yearMonthFromFields' in methodOverrides) { + const value = methodOverrides.yearMonthFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.yearMonthFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + monthDayFromFields(...args) { + calls.push(`call ${objectName}.monthDayFromFields`); + if ('monthDayFromFields' in methodOverrides) { + const value = methodOverrides.monthDayFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.monthDayFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + dateAdd(...args) { + calls.push(`call ${objectName}.dateAdd`); + if ('dateAdd' in methodOverrides) { + const value = methodOverrides.dateAdd; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateAdd(...args); + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + id: "iso8601", + }; + // Automatically generate the other methods that don't need any custom code + [ + "dateUntil", + "day", + "dayOfWeek", + "dayOfYear", + "daysInMonth", + "daysInWeek", + "daysInYear", + "era", + "eraYear", + "fields", + "inLeapYear", + "mergeFields", + "month", + "monthCode", + "monthsInYear", + "toString", + "weekOfYear", + "year", + "yearOfWeek", + ].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return iso8601[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom calendar that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + calendarThrowEverything() { + class CalendarThrowEverything extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + dateFromFields() { + TemporalHelpers.assertUnreachable("dateFromFields should not be called"); + } + yearMonthFromFields() { + TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called"); + } + monthDayFromFields() { + TemporalHelpers.assertUnreachable("monthDayFromFields should not be called"); + } + dateAdd() { + TemporalHelpers.assertUnreachable("dateAdd should not be called"); + } + dateUntil() { + TemporalHelpers.assertUnreachable("dateUntil should not be called"); + } + era() { + TemporalHelpers.assertUnreachable("era should not be called"); + } + eraYear() { + TemporalHelpers.assertUnreachable("eraYear should not be called"); + } + year() { + TemporalHelpers.assertUnreachable("year should not be called"); + } + month() { + TemporalHelpers.assertUnreachable("month should not be called"); + } + monthCode() { + TemporalHelpers.assertUnreachable("monthCode should not be called"); + } + day() { + TemporalHelpers.assertUnreachable("day should not be called"); + } + fields() { + TemporalHelpers.assertUnreachable("fields should not be called"); + } + mergeFields() { + TemporalHelpers.assertUnreachable("mergeFields should not be called"); + } + } + + return new CalendarThrowEverything(); + }, + + /* + * oneShiftTimeZone(shiftInstant, shiftNanoseconds): + * + * In the case of a spring-forward time zone offset transition (skipped time), + * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a + * negative number of nanoseconds from a PlainDateTime, which should balance + * with the microseconds field. + * + * This returns an instance of a custom time zone class which skips a length + * of time equal to shiftNanoseconds (a number), at the Temporal.Instant + * shiftInstant. Before shiftInstant, it's identical to UTC, and after + * shiftInstant it's a constant-offset time zone. + * + * It provides a getPossibleInstantsForCalledWith member which is an array + * with the result of calling toString() on any PlainDateTimes passed to + * getPossibleInstantsFor(). + */ + oneShiftTimeZone(shiftInstant, shiftNanoseconds) { + class OneShiftTimeZone extends Temporal.TimeZone { + constructor(shiftInstant, shiftNanoseconds) { + super("+00:00"); + this._shiftInstant = shiftInstant; + this._epoch1 = shiftInstant.epochNanoseconds; + this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds); + this._shiftNanoseconds = shiftNanoseconds; + this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds); + this.getPossibleInstantsForCalledWith = []; + } + + _isBeforeShift(instant) { + return instant.epochNanoseconds < this._epoch1; + } + + getOffsetNanosecondsFor(instant) { + return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds; + } + + getPossibleInstantsFor(plainDateTime) { + this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" })); + const [instant] = super.getPossibleInstantsFor(plainDateTime); + if (this._shiftNanoseconds > 0) { + if (this._isBeforeShift(instant)) return [instant]; + if (instant.epochNanoseconds < this._epoch2) return []; + return [instant.subtract(this._shift)]; + } + if (instant.epochNanoseconds < this._epoch2) return [instant]; + const shifted = instant.subtract(this._shift); + if (this._isBeforeShift(instant)) return [instant, shifted]; + return [shifted]; + } + + getNextTransition(instant) { + return this._isBeforeShift(instant) ? this._shiftInstant : null; + } + + getPreviousTransition(instant) { + return this._isBeforeShift(instant) ? null : this._shiftInstant; + } + + toString() { + return "Custom/One_Shift"; + } + } + return new OneShiftTimeZone(shiftInstant, shiftNanoseconds); + }, + + /* + * propertyBagObserver(): + * Returns an object that behaves like the given propertyBag but tracks Get + * and Has operations on any of its properties, by appending messages to an + * array. If the value of a property in propertyBag is a primitive, the value + * of the returned object's property will additionally be a + * TemporalHelpers.toPrimitiveObserver that will track calls to its toString + * and valueOf methods in the same array. This is for the purpose of testing + * order of operations that are observable from user code. objectName is used + * in the log. + */ + propertyBagObserver(calls, propertyBag, objectName) { + return new Proxy(propertyBag, { + ownKeys(target) { + calls.push(`ownKeys ${objectName}`); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(target, key) { + calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); + return Reflect.getOwnPropertyDescriptor(target, key); + }, + get(target, key, receiver) { + calls.push(`get ${formatPropertyName(key, objectName)}`); + const result = Reflect.get(target, key, receiver); + if (result === undefined) { + return undefined; + } + if ((result !== null && typeof result === "object") || typeof result === "function") { + return result; + } + return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * specificOffsetTimeZone(): + * + * This returns an instance of a custom time zone class, which returns a + * specific custom value from its getOffsetNanosecondsFrom() method. This is + * for the purpose of testing the validation of what this method returns. + * + * It also returns an empty array from getPossibleInstantsFor(), so as to + * trigger calls to getOffsetNanosecondsFor() when used from the + * BuiltinTimeZoneGetInstantFor operation. + */ + specificOffsetTimeZone(offsetValue) { + class SpecificOffsetTimeZone extends Temporal.TimeZone { + constructor(offsetValue) { + super("UTC"); + this._offsetValue = offsetValue; + } + + getOffsetNanosecondsFor() { + return this._offsetValue; + } + + getPossibleInstantsFor(dt) { + if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return []; + const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue }); + return [zdt.toInstant()]; + } + + get id() { + return this.getOffsetStringFor(new Temporal.Instant(0n)); + } + } + return new SpecificOffsetTimeZone(offsetValue); + }, + + /* + * springForwardFallBackTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single spring-forward/fall-back transition, for the purpose of testing the + * disambiguation option, without depending on system time zone data. + * + * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00 + * local) and goes from offset -08:00 to -07:00. + * + * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and + * goes from offset -07:00 to -08:00. + */ + springForwardFallBackTimeZone() { + const { compare } = Temporal.PlainDateTime; + const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2); + const springForwardEpoch = 954669600_000_000_000n; + const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1); + const fallBackEpoch = 972810000_000_000_000n; + const winterOffset = new Temporal.TimeZone('-08:00'); + const summerOffset = new Temporal.TimeZone('-07:00'); + + class SpringForwardFallBackTimeZone extends Temporal.TimeZone { + constructor() { + super("-08:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < springForwardEpoch || + instant.epochNanoseconds >= fallBackEpoch) { + return winterOffset.getOffsetNanosecondsFor(instant); + } + return summerOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) { + return []; + } + if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) { + return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)]; + } + if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) { + return [winterOffset.getInstantFor(datetime)]; + } + return [summerOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + return null; + } + + get id() { + return "Custom/Spring_Fall"; + } + + toString() { + return "Custom/Spring_Fall"; + } + } + return new SpringForwardFallBackTimeZone(); + }, + + /* + * timeZoneObserver: + * A custom calendar that behaves exactly like the UTC time zone but tracks + * calls to any of its methods, and Get/Has operations on its properties, by + * appending messages to an array. This is for the purpose of testing order of + * operations that are observable from user code. objectName is used in the + * log. methodOverrides is an optional object containing properties with the + * same name as Temporal.TimeZone methods. If the property value is a function + * it will be called with the proper arguments instead of the UTC method. + * Otherwise, the property value will be returned directly. + */ + timeZoneObserver(calls, objectName, methodOverrides = {}) { + const utc = new Temporal.TimeZone("UTC"); + const trackingMethods = { + id: "UTC", + }; + // Automatically generate the methods + ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return utc[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom time zone that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + timeZoneThrowEverything() { + class TimeZoneThrowEverything extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + getOffsetNanosecondsFor() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called"); + } + getPossibleInstantsFor() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + } + + return new TimeZoneThrowEverything(); + }, + + /* + * Returns an object that will append logs of any Gets or Calls of its valueOf + * or toString properties to the array calls. Both valueOf and toString will + * return the actual primitiveValue. propertyName is used in the log. + */ + toPrimitiveObserver(calls, primitiveValue, propertyName) { + return { + get valueOf() { + calls.push(`get ${propertyName}.valueOf`); + return function () { + calls.push(`call ${propertyName}.valueOf`); + return primitiveValue; + }; + }, + get toString() { + calls.push(`get ${propertyName}.toString`); + return function () { + calls.push(`call ${propertyName}.toString`); + if (primitiveValue === undefined) return undefined; + return primitiveValue.toString(); + }; + }, + }; + }, + + /* + * An object containing further methods that return arrays of ISO strings, for + * testing parsers. + */ + ISO: { + /* + * PlainMonthDay strings that are not valid. + */ + plainMonthDayStringsInvalid() { + return [ + "11-18junk", + "11-18[u-ca=gregory]", + "11-18[u-ca=hebrew]", + ]; + }, + + /* + * PlainMonthDay strings that are valid and that should produce October 1st. + */ + plainMonthDayStringsValid() { + return [ + "10-01", + "1001", + "1965-10-01", + "1976-10-01T152330.1+00:00", + "19761001T15:23:30.1+00:00", + "1976-10-01T15:23:30.1+0000", + "1976-10-01T152330.1+0000", + "19761001T15:23:30.1+0000", + "19761001T152330.1+00:00", + "19761001T152330.1+0000", + "+001976-10-01T152330.1+00:00", + "+0019761001T15:23:30.1+00:00", + "+001976-10-01T15:23:30.1+0000", + "+001976-10-01T152330.1+0000", + "+0019761001T15:23:30.1+0000", + "+0019761001T152330.1+00:00", + "+0019761001T152330.1+0000", + "1976-10-01T15:23:00", + "1976-10-01T15:23", + "1976-10-01T15", + "1976-10-01", + "--10-01", + "--1001", + ]; + }, + + /* + * PlainTime strings that may be mistaken for PlainMonthDay or + * PlainYearMonth strings, and so require a time designator. + */ + plainTimeStringsAmbiguous() { + const ambiguousStrings = [ + "2021-12", // ambiguity between YYYY-MM and HHMM-UU + "2021-12[-12:00]", // ditto, TZ does not disambiguate + "1214", // ambiguity between MMDD and HHMM + "0229", // ditto, including MMDD that doesn't occur every year + "1130", // ditto, including DD that doesn't occur in every month + "12-14", // ambiguity between MM-DD and HH-UU + "12-14[-14:00]", // ditto, TZ does not disambiguate + "202112", // ambiguity between YYYYMM and HHMMSS + "202112[UTC]", // ditto, TZ does not disambiguate + ]; + // Adding a calendar annotation to one of these strings must not cause + // disambiguation in favour of time. + const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]'); + return ambiguousStrings.concat(stringsWithCalendar); + }, + + /* + * PlainTime strings that are of similar form to PlainMonthDay and + * PlainYearMonth strings, but are not ambiguous due to components that + * aren't valid as months or days. + */ + plainTimeStringsUnambiguous() { + return [ + "2021-13", // 13 is not a month + "202113", // ditto + "2021-13[-13:00]", // ditto + "202113[-13:00]", // ditto + "0000-00", // 0 is not a month + "000000", // ditto + "0000-00[UTC]", // ditto + "000000[UTC]", // ditto + "1314", // 13 is not a month + "13-14", // ditto + "1232", // 32 is not a day + "0230", // 30 is not a day in February + "0631", // 31 is not a day in June + "0000", // 0 is neither a month nor a day + "00-00", // ditto + ]; + }, + + /* + * PlainYearMonth-like strings that are not valid. + */ + plainYearMonthStringsInvalid() { + return [ + "2020-13", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November + * 1976 in the ISO 8601 calendar. + */ + plainYearMonthStringsValid() { + return [ + "1976-11", + "1976-11-10", + "1976-11-01T09:00:00+00:00", + "1976-11-01T00:00:00+05:00", + "197611", + "+00197611", + "1976-11-18T15:23:30.1\u221202:00", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "1976-11-18T15:23", + "1976-11-18T15", + "1976-11-18", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November of + * the ISO year -9999. + */ + plainYearMonthStringsValidNegativeYear() { + return [ + "\u2212009999-11", + ]; + }, + } +}; diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js b/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js new file mode 100644 index 0000000000..619dd54e9b --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js @@ -0,0 +1,105 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.subtract() works as expected +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var oneDay = new Temporal.Duration(0, 0, 0, 1); +var hours24 = new Temporal.Duration(0, 0, 0, 0, 24); + +// relativeTo does not affect days if PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S"); + +// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S"); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var twoDays = new Temporal.Duration(0, 0, 0, 2); +var threeDays = new Temporal.Duration(0, 0, 0, 3); + +// start inside repeated hour, end after +assert.sameValue(`${ hours24.subtract(oneDay, { relativeTo: inRepeatedHour }) }`, "-PT1H"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo: inRepeatedHour }) }`, "PT1H"); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone); +assert.sameValue(`${ hours24.subtract(oneDay, { relativeTo }) }`, "PT1H"); +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S"); + +// start before skipped hour, end >1 day after +assert.sameValue(`${ threeDays.subtract(hours24, { relativeTo: skippedHourDay }) }`, "P2D"); +assert.sameValue(`${ hours24.subtract(threeDays, { relativeTo: skippedHourDay }) }`, "-P1DT23H"); + +// start before skipped hour, end <1 day after +assert.sameValue(`${ twoDays.subtract(hours24, { relativeTo: skippedHourDay }) }`, "P1D"); +assert.sameValue(`${ hours24.subtract(twoDays, { relativeTo: skippedHourDay }) }`, "-PT23H"); + +// start before repeated hour, end >1 day after +assert.sameValue(`${ threeDays.subtract(hours24, { relativeTo: repeatedHourDay }) }`, "P2D"); +assert.sameValue(`${ hours24.subtract(threeDays, { relativeTo: repeatedHourDay }) }`, "-P2DT1H"); + +// start before repeated hour, end <1 day after +assert.sameValue(`${ twoDays.subtract(hours24, { relativeTo: repeatedHourDay }) }`, "P1D"); +assert.sameValue(`${ hours24.subtract(twoDays, { relativeTo: repeatedHourDay }) }`, "-P1DT1H"); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +assert.sameValue(`${ twoDays.subtract(Temporal.Duration.from({ hours: 48 }), { relativeTo }) }`, "-P1D"); +assert.sameValue(`${ Temporal.Duration.from({ hours: 48 }).subtract(twoDays, { relativeTo }) }`, "P2D"); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(`${ oneDay.subtract(hours24, { + relativeTo: { + year: 2000, + month: 10, + day: 29, + timeZone + } +}) }`, "PT1H"); + +// casts relativeTo to PlainDate if possible +assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo: "2019-11-02" }) }`, "PT0S"); +assert.sameValue(`${ oneDay.subtract(hours24, { + relativeTo: { + year: 2019, + month: 11, + day: 2 + } +}) }`, "PT0S"); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => oneDay.subtract(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" })); + +// at least the required properties must be present in relativeTo +assert.throws(TypeError, () => oneDay.subtract(hours24, { + relativeTo: { + month: 11, + day: 3 + } +})); +assert.throws(TypeError, () => oneDay.subtract(hours24, { + relativeTo: { + year: 2019, + month: 11 + } +})); +assert.throws(TypeError, () => oneDay.subtract(hours24, { + relativeTo: { + year: 2019, + day: 3 + } +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/toString.js b/js/src/tests/test262/staging/Temporal/Duration/old/toString.js new file mode 100644 index 0000000000..1f7e91fb06 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/toString.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: toString() works as expected +features: [Temporal] +---*/ + +// serializing balance doesn't lose precision when values are precise +var d = Temporal.Duration.from({ + milliseconds: Number.MAX_SAFE_INTEGER, + microseconds: Number.MAX_SAFE_INTEGER +}); +assert.sameValue(`${ d }`, "PT9016206453995.731991S"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/total.js b/js/src/tests/test262/staging/Temporal/Duration/old/total.js new file mode 100644 index 0000000000..75f4d526db --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/old/total.js @@ -0,0 +1,453 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration.prototype.total() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); +var d2 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5); +var relativeTo = Temporal.PlainDate.from("2020-01-01"); + +// throws on disallowed or invalid unit (object param) +[ + "era", + "nonsense" +].forEach(unit => { + assert.throws(RangeError, () => d.total({ unit })); +}); + +// throws on disallowed or invalid unit (string param) +[ + "era", + "nonsense" +].forEach(unit => { + assert.throws(RangeError, () => d.total(unit)); +}); + +// does not lose precision for seconds and smaller units +var s = Temporal.Duration.from({ + milliseconds: 2, + microseconds: 31 +}).total({ unit: "seconds" }); +assert.sameValue(s, 0.002031); + +// accepts datetime strings or fields for relativeTo +[ + "2020-01-01", + "20200101", + "2020-01-01T00:00:00.000000000", + { + year: 2020, + month: 1, + day: 1 + } +].forEach(relativeTo => { + var daysPastJuly1 = 5 * 7 + 5 - 30; + var partialDayNanos = d.hours * 3600000000000 + d.minutes * 60000000000 + d.seconds * 1000000000 + d.milliseconds * 1000000 + d.microseconds * 1000 + d.nanoseconds; + var partialDay = partialDayNanos / (3600000000000 * 24); + var partialMonth = (daysPastJuly1 + partialDay) / 31; + var totalMonths = 5 * 12 + 5 + 1 + partialMonth; + var total = d.total({ + unit: "months", + relativeTo + }); + assert.sameValue(total.toPrecision(15), totalMonths.toPrecision(15)); +}); + +// does not accept non-string primitives for relativeTo +[ + 20200101, + 20200101n, + null, + true, +].forEach(relativeTo => { + assert.throws( + TypeError, () => d.total({ unit: "months", relativeTo}) + ); +}); + +// throws on wrong offset for ZonedDateTime relativeTo string +assert.throws(RangeError, () => d.total({ + unit: "months", + relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" +})); + +// relativeTo object must contain at least the required correctly-spelled properties +assert.throws(TypeError, () => d.total({ + unit: "months", + relativeTo: {} +})); +assert.throws(TypeError, () => d.total({ + unit: "months", + relativeTo: { + years: 2020, + month: 1, + day: 1 + } +})); + +// incorrectly-spelled properties are ignored in relativeTo +var oneMonth = Temporal.Duration.from({ months: 1 }); +assert.sameValue(oneMonth.total({ + unit: "months", + relativeTo: { + year: 2020, + month: 1, + day: 1, + months: 2 + } +}), 1); + +// throws RangeError if unit property is missing +[ + {}, + () => { + }, + { roundingMode: "ceil" } +].forEach(roundTo => assert.throws(RangeError, () => d.total(roundTo))); + +// relativeTo required to round calendar units even in durations w/o calendar units (object param) +assert.throws(RangeError, () => d2.total({ unit: "years" })); +assert.throws(RangeError, () => d2.total({ unit: "months" })); +assert.throws(RangeError, () => d2.total({ unit: "weeks" })); + +// relativeTo required to round calendar units even in durations w/o calendar units (string param) +assert.throws(RangeError, () => d2.total("years")); +assert.throws(RangeError, () => d2.total("months")); +assert.throws(RangeError, () => d2.total("weeks")); + +// relativeTo is required to round durations with calendar units (object param) +assert.throws(RangeError, () => d.total({ unit: "years" })); +assert.throws(RangeError, () => d.total({ unit: "months" })); +assert.throws(RangeError, () => d.total({ unit: "weeks" })); +assert.throws(RangeError, () => d.total({ unit: "days" })); +assert.throws(RangeError, () => d.total({ unit: "hours" })); +assert.throws(RangeError, () => d.total({ unit: "minutes" })); +assert.throws(RangeError, () => d.total({ unit: "seconds" })); +assert.throws(RangeError, () => d.total({ unit: "milliseconds" })); +assert.throws(RangeError, () => d.total({ unit: "microseconds" })); +assert.throws(RangeError, () => d.total({ unit: "nanoseconds" })); + +// relativeTo is required to round durations with calendar units (string param) +assert.throws(RangeError, () => d.total("years")); +assert.throws(RangeError, () => d.total("months")); +assert.throws(RangeError, () => d.total("weeks")); +assert.throws(RangeError, () => d.total("days")); +assert.throws(RangeError, () => d.total("hours")); +assert.throws(RangeError, () => d.total("minutes")); +assert.throws(RangeError, () => d.total("seconds")); +assert.throws(RangeError, () => d.total("milliseconds")); +assert.throws(RangeError, () => d.total("microseconds")); +assert.throws(RangeError, () => d.total("nanoseconds")); +var d2Nanoseconds = d2.days * 24 * 3600000000000 + d2.hours * 3600000000000 + d2.minutes * 60000000000 + d2.seconds * 1000000000 + d2.milliseconds * 1000000 + d2.microseconds * 1000 + d2.nanoseconds; +var totalD2 = { + days: d2Nanoseconds / (24 * 3600000000000), + hours: d2Nanoseconds / 3600000000000, + minutes: d2Nanoseconds / 60000000000, + seconds: d2Nanoseconds / 1000000000, + milliseconds: d2Nanoseconds / 1000000, + microseconds: d2Nanoseconds / 1000, + nanoseconds: d2Nanoseconds +}; + +// relativeTo not required to round fixed-length units in durations without variable units +assert(Math.abs(d2.total({ unit: "days" }) - totalD2.days) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "hours" }) - totalD2.hours) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "minutes" }) - totalD2.minutes) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "seconds" }) - totalD2.seconds) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "milliseconds" }) - totalD2.milliseconds) < Number.EPSILON); +assert(Math.abs(d2.total({ unit: "microseconds" }) - totalD2.microseconds) < Number.EPSILON); +assert.sameValue(d2.total({ unit: "nanoseconds" }), totalD2.nanoseconds); + +// relativeTo not required to round fixed-length units in durations without variable units (negative) +var negativeD2 = d2.negated(); +assert(Math.abs(negativeD2.total({ unit: "days" }) - -totalD2.days) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "hours" }) - -totalD2.hours) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "minutes" }) - -totalD2.minutes) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "seconds" }) - -totalD2.seconds) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "milliseconds" }) - -totalD2.milliseconds) < Number.EPSILON); +assert(Math.abs(negativeD2.total({ unit: "microseconds" }) - -totalD2.microseconds) < Number.EPSILON); +assert.sameValue(negativeD2.total({ unit: "nanoseconds" }), -totalD2.nanoseconds); +var endpoint = relativeTo.toPlainDateTime().add(d); +var options = unit => ({ + largestUnit: unit, + smallestUnit: unit, + roundingMode: "trunc" +}); +var fullYears = 5; +var fullDays = endpoint.since(relativeTo, options("days")).days; +var fullMilliseconds = endpoint.since(relativeTo, options("milliseconds")).milliseconds; +var partialDayMilliseconds = fullMilliseconds - fullDays * 24 * 3600000 + 0.005005; +var fractionalDay = partialDayMilliseconds / (24 * 3600000); +var partialYearDays = fullDays - (fullYears * 365 + 2); +var fractionalYear = partialYearDays / 365 + fractionalDay / 365; +var fractionalMonths = ((endpoint.day - 1) * (24 * 3600000) + partialDayMilliseconds) / (31 * 24 * 3600000); +var totalResults = { + years: fullYears + fractionalYear, + months: 66 + fractionalMonths, + weeks: (fullDays + fractionalDay) / 7, + days: fullDays + fractionalDay, + hours: fullDays * 24 + partialDayMilliseconds / 3600000, + minutes: fullDays * 24 * 60 + partialDayMilliseconds / 60000, + seconds: fullDays * 24 * 60 * 60 + partialDayMilliseconds / 1000, + milliseconds: fullMilliseconds + 0.005005, + microseconds: fullMilliseconds * 1000 + 5.005, + nanoseconds: fullMilliseconds * 1000000 + 5005 +}; +for (var [unit, expected] of Object.entries(totalResults)) { + assert.sameValue(d.total({ + unit, + relativeTo + }).toPrecision(15), expected.toPrecision(15)); +} +for (var unit of [ + "microseconds", + "nanoseconds" + ]) { + assert(d.total({ + unit, + relativeTo + }).toString().startsWith("174373505005")); +} + +// balances differently depending on relativeTo +var fortyDays = Temporal.Duration.from({ days: 40 }); +assert.sameValue(fortyDays.total({ + unit: "months", + relativeTo: "2020-02-01" +}).toPrecision(16), (1 + 11 / 31).toPrecision(16)); +assert.sameValue(fortyDays.total({ + unit: "months", + relativeTo: "2020-01-01" +}).toPrecision(16), (1 + 9 / 29).toPrecision(16)); + +// balances differently depending on relativeTo (negative) +var negativeFortyDays = Temporal.Duration.from({ days: -40 }); +assert.sameValue(negativeFortyDays.total({ + unit: "months", + relativeTo: "2020-03-01" +}).toPrecision(16), (-(1 + 11 / 31)).toPrecision(16)); +assert.sameValue(negativeFortyDays.total({ + unit: "months", + relativeTo: "2020-04-01" +}).toPrecision(16), (-(1 + 9 / 29)).toPrecision(16)); + +var oneDay = new Temporal.Duration(0, 0, 0, 1); +// relativeTo does not affect days if PlainDate +var relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo +}), 24); + +// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change +var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]"); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo +}), 24); + +// relativeTo affects days if ZonedDateTime, and duration encompasses DST change" +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone); +var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone); +var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone); +var hours12 = new Temporal.Duration(0, 0, 0, 0, 12); +var hours25 = new Temporal.Duration(0, 0, 0, 0, 25); + +// start inside repeated hour, end after +assert.sameValue(hours25.total({ + unit: "days", + relativeTo: inRepeatedHour +}), 1); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: inRepeatedHour +}), 25); + +// start after repeated hour, end inside (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T01:00").toZonedDateTime(timeZone); +assert.sameValue(hours25.negated().total({ + unit: "days", + relativeTo +}), -1); +assert.sameValue(oneDay.negated().total({ + unit: "hours", + relativeTo +}), -25); + +// start in normal hour, end in skipped hour +var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone); +var totalDays = hours25.total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (1 + 1 / 24)) < Number.EPSILON); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo +}), 24); + +// start before skipped hour, end >1 day after +var totalDays = hours25.total({ + unit: "days", + relativeTo: skippedHourDay +}); +assert(Math.abs(totalDays - (1 + 2 / 24)) < Number.EPSILON); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: skippedHourDay +}), 23); + +// start after skipped hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone); +var totalDays = hours25.negated().total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (-1 - 2 / 24)) < Number.EPSILON); +assert.sameValue(oneDay.negated().total({ + unit: "hours", + relativeTo +}), -23); + +// start before skipped hour, end <1 day after +var totalDays = hours12.total({ + unit: "days", + relativeTo: skippedHourDay +}); +assert(Math.abs(totalDays - 12 / 23) < Number.EPSILON); + +// start after skipped hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone); +var totalDays = hours12.negated().total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - -12 / 23) < Number.EPSILON); + +// start before repeated hour, end >1 day after +assert.sameValue(hours25.total({ + unit: "days", + relativeTo: repeatedHourDay +}), 1); +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: repeatedHourDay +}), 25); + +// start after repeated hour, end >1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone); +assert.sameValue(hours25.negated().total({ + unit: "days", + relativeTo +}), -1); +assert.sameValue(oneDay.negated().total({ + unit: "hours", + relativeTo +}), -25); + +// start before repeated hour, end <1 day after +var totalDays = hours12.total({ + unit: "days", + relativeTo: repeatedHourDay +}); +assert(Math.abs(totalDays - 12 / 25) < Number.EPSILON); + +// start after repeated hour, end <1 day before (negative) +var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone); +var totalDays = hours12.negated().total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - -12 / 25) < Number.EPSILON); + +// Samoa skipped 24 hours +var fakeSamoa = TemporalHelpers.crossDateLineTimeZone(); +var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa); +var totalDays = hours25.total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (2 + 1 / 24)) < Number.EPSILON); +assert.sameValue(Temporal.Duration.from({ hours: 48 }).total({ + unit: "days", + relativeTo +}), 3); +assert.sameValue(Temporal.Duration.from({ days: 2 }).total({ + unit: "hours", + relativeTo +}), 24); +assert.sameValue(Temporal.Duration.from({ days: 3 }).total({ + unit: "hours", + relativeTo +}), 48); + +// totaling back up to days +var relativeTo = Temporal.PlainDateTime.from("2000-10-28T00:00").toZonedDateTime(timeZone); +assert.sameValue(Temporal.Duration.from({ hours: 48 }).total({ unit: "days" }), 2); +var totalDays = Temporal.Duration.from({ hours: 48 }).total({ + unit: "days", + relativeTo +}); +assert(Math.abs(totalDays - (1 + 24 / 25)) < Number.EPSILON); + +// casts relativeTo to ZonedDateTime if possible +assert.sameValue(oneDay.total({ + unit: "hours", + relativeTo: { + year: 2000, + month: 10, + day: 29, + timeZone + } +}), 25); + +// balances up to the next unit after rounding +var almostWeek = Temporal.Duration.from({ + days: 6, + hours: 20 +}); +var totalWeeks = almostWeek.total({ + unit: "weeks", + relativeTo: "2020-01-01" +}); +assert(Math.abs(totalWeeks - (6 + 20 / 24) / 7) < Number.EPSILON); + +// balances up to the next unit after rounding (negative) +var almostWeek = Temporal.Duration.from({ + days: -6, + hours: -20 +}); +var totalWeeks = almostWeek.total({ + unit: "weeks", + relativeTo: "2020-01-01" +}); +assert(Math.abs(totalWeeks - -((6 + 20 / 24) / 7)) < Number.EPSILON); + +// balances days up to both years and months +var twoYears = Temporal.Duration.from({ + months: 11, + days: 396 +}); +assert.sameValue(twoYears.total({ + unit: "years", + relativeTo: "2017-01-01" +}), 2); + +// balances days up to both years and months (negative) +var twoYears = Temporal.Duration.from({ + months: -11, + days: -396 +}); +assert.sameValue(twoYears.total({ + unit: "years", + relativeTo: "2017-01-01" +}), -2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Duration/shell.js b/js/src/tests/test262/staging/Temporal/Duration/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Duration/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/browser.js b/js/src/tests/test262/staging/Temporal/Instant/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/add.js b/js/src/tests/test262/staging/Temporal/Instant/old/add.js new file mode 100644 index 0000000000..b2e2b05831 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/add.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.add works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1969-12-25T12:23:45.678901234Z"); + +// cross epoch in ms +var one = inst.subtract({ + hours: 240, + nanoseconds: 800 +}); +var two = inst.add({ + hours: 240, + nanoseconds: 800 +}); +var three = two.subtract({ + hours: 480, + nanoseconds: 1600 +}); +var four = one.add({ + hours: 480, + nanoseconds: 1600 +}); +assert.sameValue(`${ one }`, "1969-12-15T12:23:45.678900434Z", `(${ inst }).subtract({ hours: 240, nanoseconds: 800 }) = ${ one }`); +assert.sameValue(`${ two }`, "1970-01-04T12:23:45.678902034Z", `(${ inst }).add({ hours: 240, nanoseconds: 800 }) = ${ two }`); +assert(three.equals(one), `(${ two }).subtract({ hours: 480, nanoseconds: 1600 }) = ${ one }`); +assert(four.equals(two), `(${ one }).add({ hours: 480, nanoseconds: 1600 }) = ${ two }`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/browser.js b/js/src/tests/test262/staging/Temporal/Instant/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/compare.js b/js/src/tests/test262/staging/Temporal/Instant/old/compare.js new file mode 100644 index 0000000000..aab0b9bcfc --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/compare.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.compare works +features: [Temporal] +---*/ + +var i1 = Temporal.Instant.from("1963-02-13T09:36:29.123456789Z"); +var i2 = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z"); +var i3 = Temporal.Instant.from("1981-12-15T14:34:31.987654321Z"); + +// pre epoch equal +assert.sameValue(Temporal.Instant.compare(i1, Temporal.Instant.from(i1)), 0) + +// epoch equal +assert.sameValue(Temporal.Instant.compare(i2, Temporal.Instant.from(i2)), 0) + +// cross epoch smaller/larger +assert.sameValue(Temporal.Instant.compare(i1, i2), -1) + +// cross epoch larger/smaller +assert.sameValue(Temporal.Instant.compare(i2, i1), 1) + +// epoch smaller/larger +assert.sameValue(Temporal.Instant.compare(i2, i3), -1) + +// epoch larger/smaller +assert.sameValue(Temporal.Instant.compare(i3, i2), 1) + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/equals.js b/js/src/tests/test262/staging/Temporal/Instant/old/equals.js new file mode 100644 index 0000000000..2df0d8db50 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/equals.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.equals works +features: [Temporal] +---*/ + +var i1 = Temporal.Instant.from("1963-02-13T09:36:29.123456789Z"); +var i2 = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z"); +var i3 = Temporal.Instant.from("1981-12-15T14:34:31.987654321Z"); + +// pre epoch equal +assert(i1.equals(i1)) + +// epoch equal +assert(i2.equals(i2)) + +// cross epoch unequal +assert(!i1.equals(i2)) + +// epoch unequal +assert(!i2.equals(i3)) + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/limits.js b/js/src/tests/test262/staging/Temporal/Instant/old/limits.js new file mode 100644 index 0000000000..adeeaaf508 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/limits.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Min/max range +features: [Temporal] +---*/ + + +// constructing from ns +var limit = 8640000000000000000000n; +assert.throws(RangeError, () => new Temporal.Instant(-limit - 1n)); +assert.throws(RangeError, () => new Temporal.Instant(limit + 1n)); +assert.sameValue(`${ new Temporal.Instant(-limit) }`, "-271821-04-20T00:00:00Z"); +assert.sameValue(`${ new Temporal.Instant(limit) }`, "+275760-09-13T00:00:00Z"); + +// constructing from ms +var limit = 8640000000000000; +assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(-limit - 1)); +assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(limit + 1)); +assert.sameValue(`${ Temporal.Instant.fromEpochMilliseconds(-limit) }`, "-271821-04-20T00:00:00Z"); +assert.sameValue(`${ Temporal.Instant.fromEpochMilliseconds(limit) }`, "+275760-09-13T00:00:00Z"); + +// converting from DateTime +var min = Temporal.PlainDateTime.from("-271821-04-19T00:00:00.000000001"); +var max = Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999"); +var utc = Temporal.TimeZone.from("UTC"); +assert.throws(RangeError, () => utc.getInstantFor(min)); +assert.throws(RangeError, () => utc.getInstantFor(max)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/round.js b/js/src/tests/test262/staging/Temporal/Instant/old/round.js new file mode 100644 index 0000000000..6a5d41c878 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/round.js @@ -0,0 +1,131 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.round works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z"); + +// throws without required smallestUnit parameter +assert.throws(RangeError, () => inst.round({})); +assert.throws(RangeError, () => inst.round({ + roundingIncrement: 1, + roundingMode: "ceil" +})); + +// rounds to an increment of hours +assert.sameValue(`${ inst.round({ + smallestUnit: "hour", + roundingIncrement: 4 +}) }`, "1976-11-18T16:00:00Z"); + +// rounds to an increment of minutes +assert.sameValue(`${ inst.round({ + smallestUnit: "minute", + roundingIncrement: 15 +}) }`, "1976-11-18T14:30:00Z"); + +// rounds to an increment of seconds +assert.sameValue(`${ inst.round({ + smallestUnit: "second", + roundingIncrement: 30 +}) }`, "1976-11-18T14:23:30Z"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ inst.round({ + smallestUnit: "millisecond", + roundingIncrement: 10 +}) }`, "1976-11-18T14:23:30.12Z"); + +// rounds to an increment of microseconds +assert.sameValue(`${ inst.round({ + smallestUnit: "microsecond", + roundingIncrement: 10 +}) }`, "1976-11-18T14:23:30.12346Z"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ inst.round({ + smallestUnit: "nanosecond", + roundingIncrement: 10 +}) }`, "1976-11-18T14:23:30.12345679Z"); + +// rounds to days by specifying increment of 86400 seconds in various units +var expected = "1976-11-19T00:00:00Z"; +assert.sameValue(`${ inst.round({ + smallestUnit: "hour", + roundingIncrement: 24 +}) }`, expected); +assert.sameValue(`${ inst.round({ + smallestUnit: "minute", + roundingIncrement: 1440 +}) }`, expected); +assert.sameValue(`${ inst.round({ + smallestUnit: "second", + roundingIncrement: 86400 +}) }`, expected); +assert.sameValue(`${ inst.round({ + smallestUnit: "millisecond", + roundingIncrement: 86400000 +}) }`, expected); + +// allows increments that divide evenly into solar days +assert(inst.round({ + smallestUnit: "second", + roundingIncrement: 864 +}) instanceof Temporal.Instant); + +// throws on increments that do not divide evenly into solar days +assert.throws(RangeError, () => inst.round({ + smallestUnit: "hour", + roundingIncrement: 7 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "minute", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "second", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "millisecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "microsecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => inst.round({ + smallestUnit: "nanosecond", + roundingIncrement: 29 +})); + +// accepts plural units +[ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond" +].forEach(smallestUnit => { + assert(inst.round({ smallestUnit }).equals(inst.round({ smallestUnit: `${ smallestUnit }s` }))); +}); + +// accepts string parameter as shortcut for {smallestUnit} +[ + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond" +].forEach(smallestUnit => { + assert(inst.round(smallestUnit).equals(inst.round({ smallestUnit }))); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/shell.js b/js/src/tests/test262/staging/Temporal/Instant/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/since.js b/js/src/tests/test262/staging/Temporal/Instant/old/since.js new file mode 100644 index 0000000000..6dbc8bc0e1 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/since.js @@ -0,0 +1,249 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.since() works +features: [Temporal] +---*/ + +var earlier = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z"); +var later = Temporal.Instant.from("2019-10-29T10:46:38.271986102Z"); +var diff = later.since(earlier); +assert.sameValue(`${ earlier.since(later) }`, `${ diff.negated() }`) +assert.sameValue(`${ earlier.until(later) }`, `${ diff }`) +assert(earlier.add(diff).equals(later)) +assert(later.subtract(diff).equals(earlier)) +var feb20 = Temporal.Instant.from("2020-02-01T00:00Z"); +var feb21 = Temporal.Instant.from("2021-02-01T00:00Z"); + +// can return minutes and hours +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "minutes" }) }`, "PT527040M"); + +// can return subseconds +var latersub = feb20.add({ + hours: 24, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = latersub.since(feb20, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = latersub.since(feb20, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = latersub.since(feb20, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// options may be a function object +assert.sameValue(`${ feb21.since(feb20, () => { +}) }`, "PT31622400S"); + +// assumes a different default for largestUnit if smallestUnit is larger than seconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "hours", + roundingMode: "halfExpand" +}) }`, "PT376435H"); +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "minutes", + roundingMode: "halfExpand" +}) }`, "PT22586123M"); +var largestUnit = "hours"; + +// rounds to an increment of hours +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 3, + roundingMode: "halfExpand" +}) }`, "PT376434H"); + +// rounds to an increment of minutes +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT376435H30M"); + +// rounds to an increment of seconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT376435H23M15S"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT376435H23M8.15S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT376435H23M8.14853S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ later.since(earlier, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT376435H23M8.14852931S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit: "hours", + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); +}); + +// valid increments divide into 60 +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); + +// valid increments divide into 1000 +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js new file mode 100644 index 0000000000..85a7432434 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.toZonedDateTime() works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z"); + +// throws without parameter +assert.throws(TypeError, () => inst.toZonedDateTime()); + +// throws with a string parameter +assert.throws(TypeError, () => inst.toZonedDateTime("UTC")); + +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +// time zone parameter UTC +var timeZone = Temporal.TimeZone.from("UTC"); +var zdt = inst.toZonedDateTime({ + timeZone, + calendar: fakeGregorian, +}); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T14:23:30.123456789+00:00[UTC][u-ca=gregory]"); + +// time zone parameter non-UTC +var timeZone = Temporal.TimeZone.from("-05:00"); +var zdt = inst.toZonedDateTime({ + timeZone, + calendar: fakeGregorian, +}); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T09:23:30.123456789-05:00[-05:00][u-ca=gregory]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js new file mode 100644 index 0000000000..2fbaad8b9d --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.toZonedDateTimeISO() works +features: [Temporal] +---*/ + +var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z"); + +// throws without parameter +assert.throws(TypeError, () => inst.toZonedDateTimeISO()); + +// time zone parameter UTC +var tz = Temporal.TimeZone.from("UTC"); +var zdt = inst.toZonedDateTimeISO(tz); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T14:23:30.123456789+00:00[UTC]"); + +// time zone parameter non-UTC +var tz = Temporal.TimeZone.from("-05:00"); +var zdt = inst.toZonedDateTimeISO(tz); +assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds); +assert.sameValue(`${ zdt }`, "1976-11-18T09:23:30.123456789-05:00[-05:00]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/until.js b/js/src/tests/test262/staging/Temporal/Instant/old/until.js new file mode 100644 index 0000000000..78f3c11189 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/old/until.js @@ -0,0 +1,249 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant.until() works +features: [Temporal] +---*/ + +var earlier = Temporal.Instant.from("1969-07-24T16:50:35.123456789Z"); +var later = Temporal.Instant.from("2019-10-29T10:46:38.271986102Z"); +var diff = earlier.until(later); +assert.sameValue(`${ later.until(earlier) }`, `${ diff.negated() }`) +assert.sameValue(`${ later.since(earlier) }`, `${ diff }`) +assert(earlier.add(diff).equals(later)) +assert(later.subtract(diff).equals(earlier)) +var feb20 = Temporal.Instant.from("2020-02-01T00:00Z"); +var feb21 = Temporal.Instant.from("2021-02-01T00:00Z"); + +// can return minutes and hours +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "minutes" }) }`, "PT527040M"); + +// can return subseconds +var latersub = feb20.add({ + hours: 24, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = feb20.until(latersub, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = feb20.until(latersub, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = feb20.until(latersub, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// options may be a function object +assert.sameValue(`${ feb20.until(feb21, () => { +}) }`, "PT31622400S"); + +// assumes a different default for largestUnit if smallestUnit is larger than seconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "hours", + roundingMode: "halfExpand" +}) }`, "PT440610H"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "minutes", + roundingMode: "halfExpand" +}) }`, "PT26436596M"); +var largestUnit = "hours"; + +// rounds to an increment of hours +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 4, + roundingMode: "halfExpand" +}) }`, "PT440608H"); + +// rounds to an increment of minutes +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT440610H"); + +// rounds to an increment of seconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT440609H56M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT440609H56M3.15S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT440609H56M3.14853S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ earlier.until(later, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT440609H56M3.14852931S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit: "hours", + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); +}); + +// valid increments divide into 60 +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); + +// valid increments divide into 1000 +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + largestUnit, + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + largestUnit, + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Instant/shell.js b/js/src/tests/test262/staging/Temporal/Instant/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Instant/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/browser.js b/js/src/tests/test262/staging/Temporal/Regex/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/browser.js b/js/src/tests/test262/staging/Temporal/Regex/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/duration.js b/js/src/tests/test262/staging/Temporal/Regex/old/duration.js new file mode 100644 index 0000000000..8ea46a4202 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/duration.js @@ -0,0 +1,271 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-objects +description: Temporal.Duration works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var {y = 0, mon = 0, w = 0, d = 0, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0} = components; + var duration = Temporal.Duration.from(isoString); + assert.sameValue(duration.years, y); + assert.sameValue(duration.months, mon); + assert.sameValue(duration.weeks, w); + assert.sameValue(duration.days, d); + assert.sameValue(duration.hours, h); + assert.sameValue(duration.minutes, min); + assert.sameValue(duration.seconds, s); + assert.sameValue(duration.milliseconds, ms); + assert.sameValue(duration.microseconds, µs); + assert.sameValue(duration.nanoseconds, ns); +} +var day = [ + [ + "", + {} + ], + [ + "1Y", + { y: 1 } + ], + [ + "2M", + { mon: 2 } + ], + [ + "4W", + { w: 4 } + ], + [ + "3D", + { d: 3 } + ], + [ + "1Y2M", + { + y: 1, + mon: 2 + } + ], + [ + "1Y3D", + { + y: 1, + d: 3 + } + ], + [ + "2M3D", + { + mon: 2, + d: 3 + } + ], + [ + "4W3D", + { + w: 4, + d: 3 + } + ], + [ + "1Y2M3D", + { + y: 1, + mon: 2, + d: 3 + } + ], + [ + "1Y2M4W3D", + { + y: 1, + mon: 2, + w: 4, + d: 3 + } + ] +]; +var times = [ + [ + "", + {} + ], + [ + "4H", + { h: 4 } + ], + [ + "5M", + { min: 5 } + ], + [ + "4H5M", + { + h: 4, + min: 5 + } + ] +]; +var sec = [ + [ + "", + {} + ], + [ + "6S", + { s: 6 } + ], + [ + "7.1S", + { + s: 7, + ms: 100 + } + ], + [ + "7.12S", + { + s: 7, + ms: 120 + } + ], + [ + "7.123S", + { + s: 7, + ms: 123 + } + ], + [ + "8.1234S", + { + s: 8, + ms: 123, + µs: 400 + } + ], + [ + "8.12345S", + { + s: 8, + ms: 123, + µs: 450 + } + ], + [ + "8.123456S", + { + s: 8, + ms: 123, + µs: 456 + } + ], + [ + "9.1234567S", + { + s: 9, + ms: 123, + µs: 456, + ns: 700 + } + ], + [ + "9.12345678S", + { + s: 9, + ms: 123, + µs: 456, + ns: 780 + } + ], + [ + "9.123456789S", + { + s: 9, + ms: 123, + µs: 456, + ns: 789 + } + ], + [ + "0.123S", + { ms: 123 } + ], + [ + "0,123S", + { ms: 123 } + ], + [ + "0.123456S", + { + ms: 123, + µs: 456 + } + ], + [ + "0,123456S", + { + ms: 123, + µs: 456 + } + ], + [ + "0.123456789S", + { + ms: 123, + µs: 456, + ns: 789 + } + ], + [ + "0,123456789S", + { + ms: 123, + µs: 456, + ns: 789 + } + ] +]; +var tim = sec.reduce((arr, [s, add]) => arr.concat(times.map(([p, expect]) => [ + `${ p }${ s }`, + { + ...expect, + ...add + } +])), []).slice(1); +day.slice(1).forEach(([p, expect]) => { + test(`P${ p }`, expect); + test(`p${ p }`, expect); + test(`p${ p.toLowerCase() }`, expect); +}); +tim.forEach(([p, expect]) => { + test(`PT${ p }`, expect); + test(`Pt${ p }`, expect); + test(`pt${ p.toLowerCase() }`, expect); +}); +for (var [d, dexpect] of day) { + for (var [t, texpect] of tim) { + test(`P${ d }T${ t }`, { + ...dexpect, + ...texpect + }); + test(`p${ d }T${ t.toLowerCase() }`, { + ...dexpect, + ...texpect + }); + test(`P${ d.toLowerCase() }t${ t }`, { + ...dexpect, + ...texpect + }); + test(`p${ d.toLowerCase() }t${ t.toLowerCase() }`, { + ...dexpect, + ...texpect + }); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/instant.js b/js/src/tests/test262/staging/Temporal/Regex/old/instant.js new file mode 100644 index 0000000000..39b03d4d1b --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/instant.js @@ -0,0 +1,255 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-instant-objects +description: Temporal.Instant works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = components; + var instant = Temporal.Instant.from(isoString); + var utc = Temporal.TimeZone.from("UTC"); + var datetime = utc.getPlainDateTimeFor(instant); + assert.sameValue(datetime.year, y); + assert.sameValue(datetime.month, mon); + assert.sameValue(datetime.day, d); + assert.sameValue(datetime.hour, h); + assert.sameValue(datetime.minute, min); + assert.sameValue(datetime.second, s); + assert.sameValue(datetime.millisecond, ms); + assert.sameValue(datetime.microsecond, µs); + assert.sameValue(datetime.nanosecond, ns); +} +function generateTest(dateTimeString, zoneString, components) { + test(`${ dateTimeString }${ zoneString }`, components.slice(0, 5)); + test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 6)); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +// valid strings +[ + "+01:00", + "+01", + "+0100", +].forEach(zoneString => { + generateTest("1976-11-18T15:23", `${ zoneString }[Europe/Vienna]`, [ + 1976, + 11, + 18, + 14, + 23, + 30, + 123, + 456, + 789 + ]); + generateTest("1976-11-18T15:23", `+01:00[${ zoneString }]`, [ + 1976, + 11, + 18, + 14, + 23, + 30, + 123, + 456, + 789 + ]); +}); +[ + "-04:00", + "-04", + "-0400", + "-04:00:00", + "-040000", + "-04:00:00.000000000", + "-040000.0" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString, [ + 1976, + 11, + 18, + 19, + 23, + 30, + 123, + 456, + 789 +])); +test("1976-11-18T15:23:30.1Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 100 +]); +test("1976-11-18T15:23:30.12Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 120 +]); +test("1976-11-18T15:23:30.123Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123 +]); +test("1976-11-18T15:23:30.1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("1976-11-18T15:23:30.12345Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 450 +]); +test("1976-11-18T15:23:30.123456Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456 +]); +test("1976-11-18T15:23:30.1234567Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 700 +]); +test("1976-11-18T15:23:30.12345678Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 780 +]); +generateTest("1976-11-18T15:23", "z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 789 +]); +test("1976-11-18T15:23:30,1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +[ + "\u221204:00", + "\u221204", + "\u22120400" +].forEach(offset => test(`1976-11-18T15:23:30.1234${ offset }`, [ + 1976, + 11, + 18, + 19, + 23, + 30, + 123, + 400 +])); +test("\u2212009999-11-18T15:23:30.1234Z", [ + -9999, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("1976-11-18T152330Z", [ + 1976, + 11, + 18, + 15, + 23, + 30 +]); +test("1976-11-18T152330.1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("19761118T15:23:30Z", [ + 1976, + 11, + 18, + 15, + 23, + 30 +]); +test("19761118T152330Z", [ + 1976, + 11, + 18, + 15, + 23, + 30 +]); +test("19761118T152330.1234Z", [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 400 +]); +test("1976-11-18T15Z", [ + 1976, + 11, + 18, + 15 +]); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js new file mode 100644 index 0000000000..d757efe5be --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js @@ -0,0 +1,84 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plainedate-objects +description: Temporal.PlainDate works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, m, d, cid = "iso8601"] = components; + var date = Temporal.PlainDate.from(isoString); + assert.sameValue(date.year, y); + assert.sameValue(date.month, m); + assert.sameValue(date.day, d); + assert.sameValue(date.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 1976, + 11, + 18 + ]; + test(`${ dateTimeString }${ zoneString }`, components); + test(`${ dateTimeString }:30${ zoneString }`, components); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [ + 1976, + 11, + 18 +])); +test("1976-11-18T15:23:30,1234", [ + 1976, + 11, + 18 +]); +test("\u2212009999-11-18", [ + -9999, + 11, + 18 +]); +test("1976-11-18T15", [ + 1976, + 11, + 18 +]); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 1976, + 11, + 18 +])); +test("1976-11-18[u-ca=iso8601]", [ + 1976, + 11, + 18 +]); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js new file mode 100644 index 0000000000..2e08edf249 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js @@ -0,0 +1,74 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plaindatetime-objects +description: Temporal.PlainDateTime works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0, cid = "iso8601"] = components; + var datetime = Temporal.PlainDateTime.from(isoString); + assert.sameValue(datetime.year, y); + assert.sameValue(datetime.month, mon); + assert.sameValue(datetime.day, d); + assert.sameValue(datetime.hour, h); + assert.sameValue(datetime.minute, min); + assert.sameValue(datetime.second, s); + assert.sameValue(datetime.millisecond, ms); + assert.sameValue(datetime.microsecond, µs); + assert.sameValue(datetime.nanosecond, ns); + assert.sameValue(datetime.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 789 + ]; + test(`${ dateTimeString }${ zoneString }`, components.slice(0, 5)); + test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 6)); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} + +//valid strings +[ + "+0100[Europe/Vienna]", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00", + "-04:00:00.000000000", + "+010000.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "", + "+01:00[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "[Europe/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 1976, + 11, + 18, + 15, + 23, + 30, + 123, + 456, + 789 +])); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js b/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js new file mode 100644 index 0000000000..c6969e45d1 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js @@ -0,0 +1,78 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plainmonthday-objects +description: Temporal.PlainMonthDay works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [m, d, cid = "iso8601"] = components; + var monthDay = Temporal.PlainMonthDay.from(isoString); + assert.sameValue(monthDay.monthCode, `M${ m.toString().padStart(2, "0") }`); + assert.sameValue(monthDay.day, d); + assert.sameValue(monthDay.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 11, + 18 + ]; + test(`${ dateTimeString }${ zoneString }`, components); + test(`${ dateTimeString }:30${ zoneString }`, components); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+010000.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [ + 11, + 18 +])); +[ + "1976-11-18T15:23:30,1234", + "\u2212009999-11-18", + "19761118", + "+199999-11-18", + "+1999991118", + "-000300-11-18", + "-0003001118", + "15121118" +].forEach(str => test(str, [ + 11, + 18 +])); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 11, + 18 +])); +test("1972-11-18[u-ca=iso8601]", [ + 11, + 18 +]); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js new file mode 100644 index 0000000000..def35d7af2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js @@ -0,0 +1,65 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plaintime-objects +description: Temporal.PlainTime works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [h = 0, m = 0, s = 0, ms = 0, µs = 0, ns = 0] = components; + var time = Temporal.PlainTime.from(isoString); + assert.sameValue(time.hour, h); + assert.sameValue(time.minute, m); + assert.sameValue(time.second, s); + assert.sameValue(time.millisecond, ms); + assert.sameValue(time.microsecond, µs); + assert.sameValue(time.nanosecond, ns); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 15, + 23, + 30, + 123, + 456, + 789 + ]; + test(`${ dateTimeString }${ zoneString }`, components.slice(0, 2)); + test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 3)); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+010000.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-04:00", + "" +].forEach(zoneStr => test(`15${ zoneStr }`, [15])); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 15, + 23, + 30, + 123, + 456, + 789 +])); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js b/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js new file mode 100644 index 0000000000..54fbd85e93 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js @@ -0,0 +1,109 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-plainyearmonth-objects +description: Temporal.PlainYearMonth works as expected +features: [Temporal] +---*/ + +function test(isoString, components) { + var [y, m, cid = "iso8601"] = components; + var yearMonth = Temporal.PlainYearMonth.from(isoString); + assert.sameValue(yearMonth.year, y); + assert.sameValue(yearMonth.month, m); + assert.sameValue(yearMonth.calendarId, cid); +} +function generateTest(dateTimeString, zoneString) { + var components = [ + 1976, + 11 + ]; + test(`${ dateTimeString }${ zoneString }`, components); + test(`${ dateTimeString }:30${ zoneString }`, components); + test(`${ dateTimeString }:30.123456789${ zoneString }`, components); +} +[ + "+0100[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]", + "-0400", + "-04:00:00.000000000", + "+01:00:00.0[Europe/Vienna]", + "+01:00[+01:00]", + "+01:00[+0100]", + "" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString)); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [ + 1976, + 11 +])); +test("1976-11-18T15:23:30,1234", [ + 1976, + 11 +]); +test("19761118", [ + 1976, + 11 +]); +test("+199999-11-18", [ + 199999, + 11 +]); +test("+1999991118", [ + 199999, + 11 +]); +test("-000300-11-18", [ + -300, + 11 +]); +test("-0003001118", [ + -300, + 11 +]); +test("1512-11-18", [ + 1512, + 11 +]); +test("+199999-11", [ + 199999, + 11 +]); +test("+19999911", [ + 199999, + 11 +]); +test("-000300-11", [ + -300, + 11 +]); +test("-00030011", [ + -300, + 11 +]); +[ + "", + "+01:00[Europe/Vienna]", + "[Europe/Vienna]", + "+01:00[Custom/Vienna]" +].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [ + 1976, + 11 +])); +test("1976-11-01[u-ca=iso8601]", [ + 1976, + 11 +]); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/shell.js b/js/src/tests/test262/staging/Temporal/Regex/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js b/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js new file mode 100644 index 0000000000..60ffd043bf --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js @@ -0,0 +1,91 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone works as expected +features: [Temporal] +---*/ + +function test(offsetString, expectedName) { + var timeZone = Temporal.TimeZone.from(offsetString); + assert.sameValue(timeZone.id, expectedName); +} +function generateTest(dateTimeString, zoneString, expectedName) { + test(`${ dateTimeString }${ zoneString }`, expectedName); + test(`${ dateTimeString }:30${ zoneString }`, expectedName); + test(`${ dateTimeString }:30.123456789${ zoneString }`, expectedName); +} +[ + "+00:00", + "+00", + "+0000" +].forEach(zoneString => { + generateTest("1976-11-18T15:23", `${ zoneString }[UTC]`, "UTC"); + generateTest("1976-11-18T15:23", `+00:00[${ zoneString }]`, "+00:00"); +}); +[ + "-04:00", + "-04", + "-0400" +].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString, "-04:00")); +[ + "1", + "12", + "123", + "1234", + "12345", + "123456", + "1234567", + "12345678" +].forEach(decimals => { + test(`1976-11-18T15:23:30.${ decimals }Z`, "UTC"); +}); +generateTest("1976-11-18T15:23", "z", "UTC"); +test("1976-11-18T15:23:30,1234Z", "UTC"); +test("1976-11-18T15:23+000000,0[UTC]", "UTC"); +[ + "\u221204:00", + "\u221204", + "\u22120400" +].forEach(offset => test(`1976-11-18T15:23${ offset }`, "-04:00")); +[ + "1976-11-18T152330", + "1976-11-18T152330.1234", + "19761118T15:23:30", + "19761118T152330", + "19761118T152330.1234", + "1976-11-18T15" +].forEach(dateTimeString => { + [ + "+00:00", + "+00", + "+0000", + "" + ].forEach(zoneString => test(`${ dateTimeString }${ zoneString }[UTC]`, "UTC")); + [ + "-04:00", + "-04", + "-0400" + ].forEach(zoneString => test(`${ dateTimeString }${ zoneString }`, "-04:00")); + test(`${ dateTimeString }Z`, "UTC"); +}); +test("-0000", "+00:00"); +test("-00:00", "+00:00"); +test("+00", "+00:00"); +test("-00", "+00:00"); +test("+03", "+03:00"); +test("-03", "-03:00"); +test("\u22120000", "+00:00"); +test("\u221200:00", "+00:00"); +test("\u221200", "+00:00"); +test("\u22120300", "-03:00"); +test("\u221203:00", "-03:00"); +test("\u221203", "-03:00"); +test("1976-11-18T15:23:30.123456789Z[u-ca=iso8601]", "UTC"); +test("1976-11-18T15:23:30.123456789-04:00[u-ca=iso8601]", "-04:00"); +test("1976-11-18T15:23:30.123456789[UTC][u-ca=iso8601]", "UTC"); +test("1976-11-18T15:23:30.123456789+00:00[UTC][u-ca=iso8601]", "UTC"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/Regex/shell.js b/js/src/tests/test262/staging/Temporal/Regex/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/Regex/shell.js diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/browser.js b/js/src/tests/test262/staging/Temporal/TimeZone/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/browser.js diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js new file mode 100644 index 0000000000..8db7aa7e03 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: with DST change +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// clock moving forward +var zone = TemporalHelpers.springForwardFallBackTimeZone(); +var dtm = new Temporal.PlainDateTime(2000, 4, 2, 2, 45); +assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-04-02T10:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-04-02T09:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-04-02T10:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-04-02T10:45:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" })); + +// clock moving backward +var dtm = new Temporal.PlainDateTime(2000, 10, 29, 1, 45); +assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-10-29T09:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-10-29T08:45:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js new file mode 100644 index 0000000000..101a7d32a1 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: getInstantFor disambiguation +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var dtm = new Temporal.PlainDateTime(2000, 10, 29, 1, 45); + +// with constant offset +var zone = Temporal.TimeZone.from("+03:30"); +for (var disambiguation of [ + undefined, + "compatible", + "earlier", + "later", + "reject" + ]) { + assert(zone.getInstantFor(dtm, { disambiguation }) instanceof Temporal.Instant); +} + +// with daylight saving change - Fall +var zone = TemporalHelpers.springForwardFallBackTimeZone(); +assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-10-29T08:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-10-29T09:45:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-10-29T08:45:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" })); + +// with daylight saving change - Spring +var dtmLA = new Temporal.PlainDateTime(2000, 4, 2, 2, 30); +assert.sameValue(`${ zone.getInstantFor(dtmLA) }`, "2000-04-02T10:30:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "earlier" }) }`, "2000-04-02T09:30:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "later" }) }`, "2000-04-02T10:30:00Z"); +assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "compatible" }) }`, "2000-04-02T10:30:00Z"); +assert.throws(RangeError, () => zone.getInstantFor(dtmLA, { disambiguation: "reject" })); + +// throws on bad disambiguation +var zone = Temporal.TimeZone.from("+03:30"); +[ + "", + "EARLIER", + "test", +].forEach(disambiguation => assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation }))); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js new file mode 100644 index 0000000000..37c2be715f --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getInstantFor() works +features: [Temporal] +---*/ + + +// recent date +var dt = Temporal.PlainDateTime.from("2019-10-29T10:46:38.271986102"); +var tz = Temporal.TimeZone.from("+01:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "2019-10-29T09:46:38.271986102Z"); + +// year ≤ 99 +var dt = Temporal.PlainDateTime.from("0098-10-29T10:46:38.271986102"); +var tz = Temporal.TimeZone.from("+06:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0098-10-29T04:46:38.271986102Z"); +dt = Temporal.PlainDateTime.from("+000098-10-29T10:46:38.271986102"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0098-10-29T04:46:38.271986102Z"); + +// year < 1 +var dt = Temporal.PlainDateTime.from("0000-10-29T10:46:38.271986102"); +var tz = Temporal.TimeZone.from("+06:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-10-29T04:46:38.271986102Z"); +dt = Temporal.PlainDateTime.from("+000000-10-29T10:46:38.271986102"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-10-29T04:46:38.271986102Z"); +dt = Temporal.PlainDateTime.from("-001000-10-29T10:46:38.271986102"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "-001000-10-29T04:46:38.271986102Z"); + +// year 0 leap day +var dt = Temporal.PlainDateTime.from("0000-02-29T00:00"); +var tz = Temporal.TimeZone.from("-00:01"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z"); +dt = Temporal.PlainDateTime.from("+000000-02-29T00:00"); +assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z"); + +// outside of Instant range +var max = Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999"); +var offsetTz = Temporal.TimeZone.from("-01:00"); +assert.throws(RangeError, () => offsetTz.getInstantFor(max)); +var namedTz = Temporal.TimeZone.from("Etc/GMT+12"); +assert.throws(RangeError, () => namedTz.getInstantFor(max)); + +// casts argument +var tz = Temporal.TimeZone.from("+01:00"); +assert.sameValue(`${ tz.getInstantFor("2019-10-29T10:46:38.271986102") }`, "2019-10-29T09:46:38.271986102Z"); +assert.sameValue(`${ tz.getInstantFor({ + year: 2019, + month: 10, + day: 29, + hour: 10, + minute: 46, + second: 38 +}) }`, "2019-10-29T09:46:38Z"); + + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js new file mode 100644 index 0000000000..50e043ff83 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getNextTransition() works as expected +features: [Temporal] +---*/ + +var noTransitionTZ = Temporal.TimeZone.from("Etc/GMT+10"); + +// should work for timezones with no scheduled transitions in the near future +var start = Temporal.Instant.from("1945-10-15T13:00:00Z"); +assert.sameValue(noTransitionTZ.getNextTransition(start), null); + +// accepts string as argument +assert.sameValue(noTransitionTZ.getNextTransition("2019-04-16T21:01Z"), null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js new file mode 100644 index 0000000000..6eda88fdb0 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getPossibleInstantsFor() works as expected +includes: [deepEqual.js, temporalHelpers.js] +features: [Temporal] +---*/ + + +// with constant offset +var zone = Temporal.TimeZone.from("+03:30"); +var dt = Temporal.PlainDateTime.from("2019-02-16T23:45"); +assert.deepEqual(zone.getPossibleInstantsFor(dt).map(a => `${ a }`), ["2019-02-16T20:15:00Z"]); + +// with clock moving forward +var zone = TemporalHelpers.springForwardFallBackTimeZone(); +var dt = Temporal.PlainDateTime.from("2000-04-02T02:45"); +assert.deepEqual(zone.getPossibleInstantsFor(dt), []); + +// with clock moving backward +var dt = Temporal.PlainDateTime.from("2000-10-29T01:45"); +assert.deepEqual(zone.getPossibleInstantsFor(dt).map(a => `${ a }`), [ + "2000-10-29T08:45:00Z", + "2000-10-29T09:45:00Z" +]); + +// casts argument +var tz = Temporal.TimeZone.from("+03:30"); +assert.deepEqual(tz.getPossibleInstantsFor({ + year: 2019, + month: 2, + day: 16, + hour: 23, + minute: 45, + second: 30 +}).map(a => `${ a }`), ["2019-02-16T20:15:30Z"]); +assert.deepEqual(tz.getPossibleInstantsFor("2019-02-16T23:45:30").map(a => `${ a }`), ["2019-02-16T20:15:30Z"]); + +// object must contain at least the required properties +var tz = Temporal.TimeZone.from("UTC"); +assert.throws(TypeError, () => tz.getPossibleInstantsFor({ year: 2019 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js new file mode 100644 index 0000000000..6c6b703b58 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: Temporal.TimeZone.prototype.getPreviousTransition() works +features: [Temporal] +---*/ + +var utc = Temporal.TimeZone.from("UTC"); + +// no transitions without a TZDB +var instant = Temporal.Instant.from("2020-06-11T21:01Z"); +assert.sameValue(utc.getPreviousTransition(instant), null); + +// accepts string as argument +assert.sameValue(utc.getPreviousTransition("2020-06-11T21:01Z"), null); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js new file mode 100644 index 0000000000..eaa8761415 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js @@ -0,0 +1,2487 @@ +// 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: temporalHelpers.js +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + This defines helper objects and functions for testing Temporal. +defines: [TemporalHelpers] +features: [Symbol.species, Symbol.iterator, Temporal] +---*/ + +const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; + +function formatPropertyName(propertyKey, objectName = "") { + switch (typeof propertyKey) { + case "symbol": + if (Symbol.keyFor(propertyKey) !== undefined) { + return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; + } else if (propertyKey.description.startsWith('Symbol.')) { + return `${objectName}[${propertyKey.description}]`; + } else { + return `${objectName}[Symbol('${propertyKey.description}')]` + } + case "string": + if (propertyKey !== String(Number(propertyKey))) { + if (ASCII_IDENTIFIER.test(propertyKey)) { + return objectName ? `${objectName}.${propertyKey}` : propertyKey; + } + return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']` + } + // fall through + default: + // integer or string integer-index + return `${objectName}[${propertyKey}]`; + } +} + +const SKIP_SYMBOL = Symbol("Skip"); + +var TemporalHelpers = { + /* + * Codes and maximum lengths of months in the ISO 8601 calendar. + */ + ISOMonths: [ + { month: 1, monthCode: "M01", daysInMonth: 31 }, + { month: 2, monthCode: "M02", daysInMonth: 29 }, + { month: 3, monthCode: "M03", daysInMonth: 31 }, + { month: 4, monthCode: "M04", daysInMonth: 30 }, + { month: 5, monthCode: "M05", daysInMonth: 31 }, + { month: 6, monthCode: "M06", daysInMonth: 30 }, + { month: 7, monthCode: "M07", daysInMonth: 31 }, + { month: 8, monthCode: "M08", daysInMonth: 31 }, + { month: 9, monthCode: "M09", daysInMonth: 30 }, + { month: 10, monthCode: "M10", daysInMonth: 31 }, + { month: 11, monthCode: "M11", daysInMonth: 30 }, + { month: 12, monthCode: "M12", daysInMonth: 31 } + ], + + /* + * assertDuration(duration, years, ..., nanoseconds[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * an expected value. + */ + assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, hours, `${prefix}hours result:`); + assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); + assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); + assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); + assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); + assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); + }, + + /* + * assertDateDuration(duration, years, months, weeks, days, [, description]): + * + * Shorthand for asserting that each date field of a Temporal.Duration is + * equal to an expected value. + */ + assertDateDuration(duration, years, months, weeks, days, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); + assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); + assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); + assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); + assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); + assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); + }, + + /* + * assertDurationsEqual(actual, expected[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * the corresponding field in another Temporal.Duration. + */ + assertDurationsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); + TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); + }, + + /* + * assertInstantsEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.Instants are of the correct type + * and equal according to their equals() methods. + */ + assertInstantsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); + assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDate is equal to + * an expected value. (Except the `calendar` property, since callers may want + * to assert either object equality with an object they put in there, or the + * value of date.calendarId.) + */ + assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert.sameValue(date.era, era, `${prefix}era result:`); + assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(date.year, year, `${prefix}year result:`); + assert.sameValue(date.month, month, `${prefix}month result:`); + assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(date.day, day, `${prefix}day result:`); + }, + + /* + * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDateTime is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of datetime.calendarId.) + */ + assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert.sameValue(datetime.era, era, `${prefix}era result:`); + assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(datetime.year, year, `${prefix}year result:`); + assert.sameValue(datetime.month, month, `${prefix}month result:`); + assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(datetime.day, day, `${prefix}day result:`); + assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); + assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); + assert.sameValue(datetime.second, second, `${prefix}second result:`); + assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); + assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainMonthDay is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of monthDay.calendarId().) + */ + assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { + const prefix = description ? `${description}: ` : ""; + assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); + assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(monthDay.day, day, `${prefix}day result:`); + assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); + }, + + /* + * assertPlainTime(time, hour, ..., nanosecond[, description]): + * + * Shorthand for asserting that each field of a Temporal.PlainTime is equal to + * an expected value. + */ + assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert.sameValue(time.hour, hour, `${prefix}hour result:`); + assert.sameValue(time.minute, minute, `${prefix}minute result:`); + assert.sameValue(time.second, second, `${prefix}second result:`); + assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainTimes are of the correct + * type and equal according to their equals() methods. + */ + assertPlainTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); + assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): + * + * Shorthand for asserting that each field of a Temporal.PlainYearMonth is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of yearMonth.calendarId.) + */ + assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { + const prefix = description ? `${description}: ` : ""; + assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); + assert.sameValue(yearMonth.era, era, `${prefix}era result:`); + assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(yearMonth.year, year, `${prefix}year result:`); + assert.sameValue(yearMonth.month, month, `${prefix}month result:`); + assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`); + }, + + /* + * assertZonedDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their time zones and calendar internal slots are the same value. + */ + assertZonedDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); + assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertUnreachable(description): + * + * Helper for asserting that code is not executed. This is useful for + * assertions that methods of user calendars and time zones are not called. + */ + assertUnreachable(description) { + let message = "This code should not be executed"; + if (description) { + message = `${message}: ${description}`; + } + throw new Test262Error(message); + }, + + /* + * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls): + * + * When an options object with a largestUnit property is synthesized inside + * Temporal and passed to user code such as calendar.dateUntil(), the value of + * the largestUnit property should be in the singular form, even if the input + * was given in the plural form. + * (This doesn't apply when the options object is passed through verbatim.) + * + * func(calendar, largestUnit, index) is the operation under test. It's called + * with an instance of a calendar that keeps track of which largestUnit is + * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and + * the key's numerical index in case the function needs to generate test data + * based on the index. At the end, the actual values passed to dateUntil() are + * compared with the array values of expectedLargestUnitCalls. + */ + checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) { + const actual = []; + + class DateUntilOptionsCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(earlier, later, options) { + actual.push(options.largestUnit); + return super.dateUntil(earlier, later, options); + } + + toString() { + return "date-until-options"; + } + } + + const calendar = new DateUntilOptionsCalendar(); + Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => { + func(calendar, largestUnit, index); + assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`); + actual.splice(0); // empty it for the next check + }); + }, + + /* + * checkPlainDateTimeConversionFastPath(func): + * + * ToTemporalDate and ToTemporalTime should both, if given a + * Temporal.PlainDateTime instance, convert to the desired type by reading the + * PlainDateTime's internal slots, rather than calling any getters. + * + * func(datetime, calendar) is the actual operation to test, that must + * internally call the abstract operation ToTemporalDate or ToTemporalTime. + * It is passed a Temporal.PlainDateTime instance, as well as the instance's + * calendar object (so that it doesn't have to call the calendar getter itself + * if it wants to make any assertions about the calendar.) + */ + checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); + ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(datetime, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${formatPropertyName(property)}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${formatPropertyName(property)}`); + return value; + }, + }; + }, + }); + }); + Object.defineProperty(datetime, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(datetime, calendar); + assert.compareArray(actual, expected, `${message}: property getters not called`); + }, + + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: 'years', + month: 'months', + week: 'weeks', + day: 'days', + hour: 'hours', + minute: 'minutes', + second: 'seconds', + millisecond: 'milliseconds', + microsecond: 'microseconds', + nanosecond: 'nanoseconds', + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; + if (singularValue instanceof Temporal.Duration) { + TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.Instant) { + TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainDateTime) { + TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainTime) { + TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.ZonedDateTime) { + TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + + /* + * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): + * + * Checks the type handling of the roundingIncrement option. + * checkFunc(roundingIncrement) is a function which takes the value of + * roundingIncrement to test, and calls the method under test with it, + * returning the result. assertTrueResultFunc(result, description) should + * assert that result is the expected result with roundingIncrement: true, and + * assertObjectResultFunc(result, description) should assert that result is + * the expected result with roundingIncrement being an object with a valueOf() + * method. + */ + checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { + // null converts to 0, which is out of range + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to either 0 or 1, and 1 is allowed + const trueResult = checkFunc(true); + assertTrueResultFunc(trueResult, "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols and BigInts cannot convert to numbers + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + assert.throws(TypeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their valueOf() methods when converting to a number + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + "get roundingIncrement.valueOf", + "call roundingIncrement.valueOf", + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); + const objectResult = checkFunc(observer); + assertObjectResultFunc(objectResult, "object with valueOf"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): + * + * Checks the type handling of a string option, of which there are several in + * Temporal. + * propertyName is the name of the option, and value is the value that + * assertFunc should expect it to have. + * checkFunc(value) is a function which takes the value of the option to test, + * and calls the method under test with it, returning the result. + * assertFunc(result, description) should assert that result is the expected + * result with the option value being an object with a toString() method + * which returns the given value. + */ + checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { + // null converts to the string "null", which is an invalid string value + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to the strings "true" or "false", which are invalid + assert.throws(RangeError, () => checkFunc(true), "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols cannot convert to strings + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + // Numbers convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2), "number"); + // BigInts convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their toString() methods when converting to a string + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + `get ${propertyName}.toString`, + `call ${propertyName}.toString`, + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); + const result = checkFunc(observer); + assertFunc(result, "object with toString"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, + * resultAssertions): + * + * Methods of Temporal classes that return a new instance of the same class, + * must not take the constructor of a subclass into account, nor the @@species + * property. This helper runs tests to ensure this. + * + * construct(...constructArgs) must yield a valid instance of the Temporal + * class. instance[method](...methodArgs) is the method call under test, which + * must also yield a valid instance of the same Temporal class, not a + * subclass. See below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnored(...args) { + this.checkSubclassConstructorNotObject(...args); + this.checkSubclassConstructorUndefined(...args); + this.checkSubclassConstructorThrows(...args); + this.checkSubclassConstructorNotCalled(...args); + this.checkSubclassSpeciesInvalidResult(...args); + this.checkSubclassSpeciesNotAConstructor(...args); + this.checkSubclassSpeciesNull(...args); + this.checkSubclassSpeciesUndefined(...args); + this.checkSubclassSpeciesThrows(...args); + }, + + /* + * Checks that replacing the 'constructor' property of the instance with + * various primitive values does not affect the returned new instance. + */ + checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = value; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + }, + + /* + * Checks that replacing the 'constructor' property of the subclass with + * undefined does not affect the returned new instance. + */ + checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = undefined; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that making the 'constructor' property of the instance throw when + * called does not affect the returned new instance. + */ + checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + const instance = new construct(...constructArgs); + Object.defineProperty(instance, "constructor", { + get() { + throw new CustomError(); + } + }); + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that when subclassing, the subclass constructor is not called by + * the method under test. + */ + checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's a + * constructor that returns a non-object value. + */ + checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: function() { + return value; + }, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's not a + * constructor. + */ + checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: value, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's null. + */ + checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: null, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's + * undefined. + */ + checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: undefined, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it throws, + * i.e. it is not called at all. + */ + checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + + const instance = new construct(...constructArgs); + instance.constructor = { + get [Symbol.species]() { + throw new CustomError(); + }, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + }, + + /* + * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): + * + * Static methods of Temporal classes that return a new instance of the class, + * must not use the this-value as a constructor. This helper runs tests to + * ensure this. + * + * construct[method](...methodArgs) is the static method call under test, and + * must yield a valid instance of the Temporal class, not a subclass. See + * below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnoredStatic(...args) { + this.checkStaticInvalidReceiver(...args); + this.checkStaticReceiverNotCalled(...args); + this.checkThisValueNotCalled(...args); + }, + + /* + * Check that calling the static method with a receiver that's not callable, + * still calls the intrinsic constructor. + */ + checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const result = construct[method].apply(value, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that calling the static method with a receiver that returns a value + * that's not callable, still calls the intrinsic constructor. + */ + checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const receiver = function () { + return value; + }; + const result = construct[method].apply(receiver, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that the receiver isn't called. + */ + checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { + let called = false; + + class MySubclass extends construct { + constructor(...args) { + called = true; + super(...args); + } + } + + const result = MySubclass[method](...methodArgs); + assert.sameValue(called, false); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that any iterable returned from a custom time zone's + * getPossibleInstantsFor() method is exhausted. + * The custom time zone object is passed in to func(). + * expected is an array of strings representing the expected calls to the + * getPossibleInstantsFor() method. The PlainDateTimes that it is called with, + * are compared (using their toString() results) with the array. + */ + checkTimeZonePossibleInstantsIterable(func, expected) { + // A custom time zone that returns an iterable instead of an array from its + // getPossibleInstantsFor() method, and for testing purposes skips + // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on + // January 3, 2030. Otherwise identical to the UTC time zone. + class TimeZonePossibleInstantsIterable extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.getPossibleInstantsForCallCount = 0; + this.getPossibleInstantsForCalledWith = []; + this.getPossibleInstantsForReturns = []; + this.iteratorExhausted = []; + } + + toString() { + return "Custom/Iterable"; + } + + getOffsetNanosecondsFor(instant) { + if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 && + Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) { + return 3600_000_000_000; + } else { + return 0; + } + } + + getPossibleInstantsFor(dateTime) { + this.getPossibleInstantsForCallCount++; + this.getPossibleInstantsForCalledWith.push(dateTime); + + // Fake DST transition + let retval = super.getPossibleInstantsFor(dateTime); + if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) { + retval = []; + } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) { + retval.push(retval[0].subtract({ hours: 1 })); + } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) { + retval[0] = retval[0].subtract({ hours: 1 }); + } + + this.getPossibleInstantsForReturns.push(retval); + this.iteratorExhausted.push(false); + return { + callIndex: this.getPossibleInstantsForCallCount - 1, + timeZone: this, + *[Symbol.iterator]() { + yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex]; + this.timeZone.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + + const timeZone = new TimeZonePossibleInstantsIterable(); + func(timeZone); + + assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times"); + + for (let index = 0; index < expected.length; index++) { + assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime"); + assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable"); + } + }, + + /* + * Check that any calendar-carrying Temporal object has its [[Calendar]] + * internal slot read by ToTemporalCalendar, and does not fetch the calendar + * by calling getters. + * The custom calendar object is passed in to func() so that it can do its + * own additional assertions involving the calendar if necessary. (Sometimes + * there is nothing to assert as the calendar isn't stored anywhere that can + * be asserted about.) + */ + checkToTemporalCalendarFastPath(func) { + class CalendarFastPathCheck extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + monthDayFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + + yearMonthFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + + toString() { + return "fast-path-check"; + } + } + const calendar = new CalendarFastPathCheck(); + + const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar); + const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar); + const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar); + const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + + [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { + const actual = []; + const expected = []; + + Object.defineProperty(temporalObject, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(temporalObject, calendar); + assert.compareArray(actual, expected, "calendar getter not called"); + }); + }, + + checkToTemporalInstantFastPath(func) { + const actual = []; + const expected = []; + + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + Object.defineProperty(datetime, 'toString', { + get() { + actual.push("get toString"); + return function (options) { + actual.push("call toString"); + return Temporal.ZonedDateTime.prototype.toString.call(this, options); + }; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, "toString not called"); + }, + + checkToTemporalPlainDateTimeFastPath(func) { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const date = new Temporal.PlainDate(2000, 5, 2, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); + ["year", "month", "monthCode", "day"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return TemporalHelpers.toPrimitiveObserver(actual, value, property); + }, + }); + }); + ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + return undefined; + }, + }); + }); + Object.defineProperty(date, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(date, calendar); + assert.compareArray(actual, expected, "property getters not called"); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * fromFields methods are always called with a null-prototype fields object. + */ + calendarCheckFieldsPrototypePollution() { + class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + } + + // toString must remain "iso8601", so that some methods don't throw due to + // incompatible calendars + + dateFromFields(fields, options = {}) { + this.dateFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options = {}) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options = {}) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object"); + return super.monthDayFromFields(fields, options); + } + } + + return new CalendarCheckFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * mergeFields() method is always called with null-prototype fields objects. + */ + calendarCheckMergeFieldsPrototypePollution() { + class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsCallCount = 0; + } + + toString() { + return "merge-fields-null-proto"; + } + + mergeFields(fields, additionalFields) { + this.mergeFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)"); + assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)"); + return super.mergeFields(fields, additionalFields); + } + } + + return new CalendarCheckMergeFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that methods + * are always called with a null-prototype options object. + */ + calendarCheckOptionsPrototypePollution() { + class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.yearMonthFromFieldsCallCount = 0; + this.dateUntilCallCount = 0; + } + + toString() { + return "options-null-proto"; + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options"); + return super.yearMonthFromFields(fields, options); + } + + dateUntil(one, two, options) { + this.dateUntilCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options"); + return super.dateUntil(one, two, options); + } + } + + return new CalendarCheckOptionsPrototypePollution(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with the + * options parameter having the value undefined. + */ + calendarDateAddUndefinedOptions() { + class CalendarDateAddUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + } + + toString() { + return "dateadd-undef-options"; + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert.sameValue(options, undefined, "dateAdd shouldn't be called with options"); + return super.dateAdd(date, duration, options); + } + } + return new CalendarDateAddUndefinedOptions(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with a + * PlainDate instance. Optionally, it also asserts that the PlainDate instance + * is the specific object `this.specificPlainDate`, if it is set by the + * calling code. + */ + calendarDateAddPlainDateInstance() { + class CalendarDateAddPlainDateInstance extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + this.specificPlainDate = undefined; + } + + toString() { + return "dateadd-plain-date-instance"; + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance"); + if (this.dateAddCallCount === 1 && this.specificPlainDate) { + assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`); + } + return super.dateAdd(date, duration, options).withCalendar(this); + } + } + return new CalendarDateAddPlainDateInstance(); + }, + + /* + * A custom calendar that returns an iterable instead of an array from its + * fields() method, otherwise identical to the ISO calendar. + */ + calendarFieldsIterable() { + class CalendarFieldsIterable extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.fieldsCallCount = 0; + this.fieldsCalledWith = []; + this.iteratorExhausted = []; + } + + toString() { + return "fields-iterable"; + } + + fields(fieldNames) { + this.fieldsCallCount++; + this.fieldsCalledWith.push(fieldNames.slice()); + this.iteratorExhausted.push(false); + return { + callIndex: this.fieldsCallCount - 1, + calendar: this, + *[Symbol.iterator]() { + yield* this.calendar.fieldsCalledWith[this.callIndex]; + this.calendar.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + return new CalendarFieldsIterable(); + }, + + /* + * A custom calendar that asserts its ...FromFields() methods are called with + * the options parameter having the value undefined. + */ + calendarFromFieldsUndefinedOptions() { + class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "from-fields-undef-options"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options"); + return super.monthDayFromFields(fields, options); + } + } + return new CalendarFromFieldsUndefinedOptions(); + }, + + /* + * A custom calendar that modifies the fields object passed in to + * dateFromFields, sabotaging its time properties. + */ + calendarMakeInfinityTime() { + class CalendarMakeInfinityTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + fields.hour = Infinity; + fields.minute = Infinity; + fields.second = Infinity; + fields.millisecond = Infinity; + fields.microsecond = Infinity; + fields.nanosecond = Infinity; + return retval; + } + } + return new CalendarMakeInfinityTime(); + }, + + /* + * A custom calendar that defines getters on the fields object passed into + * dateFromFields that throw, sabotaging its time properties. + */ + calendarMakeInvalidGettersTime() { + class CalendarMakeInvalidGettersTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + const throwingDescriptor = { + get() { + throw new Test262Error("reading a sabotaged time field"); + }, + }; + Object.defineProperties(fields, { + hour: throwingDescriptor, + minute: throwingDescriptor, + second: throwingDescriptor, + millisecond: throwingDescriptor, + microsecond: throwingDescriptor, + nanosecond: throwingDescriptor, + }); + return retval; + } + } + return new CalendarMakeInvalidGettersTime(); + }, + + /* + * A custom calendar whose mergeFields() method returns a proxy object with + * all of its Get and HasProperty operations observable, as well as adding a + * "shouldNotBeCopied": true property. + */ + calendarMergeFieldsGetters() { + class CalendarMergeFieldsGetters extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsReturnOperations = []; + } + + toString() { + return "merge-fields-getters"; + } + + dateFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.monthDayFromFields(fields, options); + } + + mergeFields(fields, additionalFields) { + const retval = super.mergeFields(fields, additionalFields); + retval._calendar = this; + retval.shouldNotBeCopied = true; + return new Proxy(retval, { + get(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`get ${key}`); + const result = target[key]; + if (result === undefined) { + return undefined; + } + return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key); + }, + has(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`has ${key}`); + return key in target; + }, + }); + } + } + return new CalendarMergeFieldsGetters(); + }, + + /* + * A custom calendar whose mergeFields() method returns a primitive value, + * given by @primitive, and which records the number of calls made to its + * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods. + */ + calendarMergeFieldsReturnsPrimitive(primitive) { + class CalendarMergeFieldsPrimitive extends Temporal.Calendar { + constructor(mergeFieldsReturnValue) { + super("iso8601"); + this._mergeFieldsReturnValue = mergeFieldsReturnValue; + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "merge-fields-primitive"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + return super.monthDayFromFields(fields, options); + } + + mergeFields() { + return this._mergeFieldsReturnValue; + } + } + return new CalendarMergeFieldsPrimitive(primitive); + }, + + /* + * A custom calendar whose fields() method returns the same value as the + * iso8601 calendar, with the addition of extraFields provided as parameter. + */ + calendarWithExtraFields(fields) { + class CalendarWithExtraFields extends Temporal.Calendar { + constructor(extraFields) { + super("iso8601"); + this._extraFields = extraFields; + } + + fields(fieldNames) { + return super.fields(fieldNames).concat(this._extraFields); + } + } + + return new CalendarWithExtraFields(fields); + }, + + /* + * crossDateLineTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single transition where the time zone moves from one side of the + * International Date Line to the other, for the purpose of testing time zone + * calculations without depending on system time zone data. + * + * The transition occurs at epoch second 1325239200 and goes from offset + * -10:00 to +14:00. In other words, the time zone skips the whole calendar + * day of 2011-12-30. This is the same as the real-life transition in the + * Pacific/Apia time zone. + */ + crossDateLineTimeZone() { + const { compare } = Temporal.PlainDate; + const skippedDay = new Temporal.PlainDate(2011, 12, 30); + const transitionEpoch = 1325239200_000_000_000n; + const beforeOffset = new Temporal.TimeZone("-10:00"); + const afterOffset = new Temporal.TimeZone("+14:00"); + + class CrossDateLineTimeZone extends Temporal.TimeZone { + constructor() { + super("+14:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < transitionEpoch) { + return beforeOffset.getOffsetNanosecondsFor(instant); + } + return afterOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + const comparison = compare(datetime.toPlainDate(), skippedDay); + if (comparison === 0) { + return []; + } + if (comparison < 0) { + return [beforeOffset.getInstantFor(datetime)]; + } + return [afterOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + toString() { + return "Custom/Date_Line"; + } + } + return new CrossDateLineTimeZone(); + }, + + /* + * observeProperty(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls to its accessors to the array @calls. + */ + observeProperty(calls, object, propertyName, value, objectName = "") { + Object.defineProperty(object, propertyName, { + get() { + calls.push(`get ${formatPropertyName(propertyName, objectName)}`); + return value; + }, + set(v) { + calls.push(`set ${formatPropertyName(propertyName, objectName)}`); + } + }); + }, + + /* + * observeMethod(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls of @value to the array @calls. + */ + observeMethod(calls, object, propertyName, objectName = "") { + const method = object[propertyName]; + object[propertyName] = function () { + calls.push(`call ${formatPropertyName(propertyName, objectName)}`); + return method.apply(object, arguments); + }; + }, + + /* + * Used for substituteMethod to indicate default behavior instead of a + * substituted value + */ + SUBSTITUTE_SKIP: SKIP_SYMBOL, + + /* + * substituteMethod(object, propertyName, values): + * + * Defines an own property @object.@propertyName that will, for each + * subsequent call to the method previously defined as + * @object.@propertyName: + * - Call the method, if no more values remain + * - Call the method, if the value in @values for the corresponding call + * is SUBSTITUTE_SKIP + * - Otherwise, return the corresponding value in @value + */ + substituteMethod(object, propertyName, values) { + let calls = 0; + const method = object[propertyName]; + object[propertyName] = function () { + if (calls >= values.length) { + return method.apply(object, arguments); + } else if (values[calls] === SKIP_SYMBOL) { + calls++; + return method.apply(object, arguments); + } else { + return values[calls++]; + } + }; + }, + + /* + * calendarObserver: + * A custom calendar that behaves exactly like the ISO 8601 calendar but + * tracks calls to any of its methods, and Get/Has operations on its + * properties, by appending messages to an array. This is for the purpose of + * testing order of operations that are observable from user code. + * objectName is used in the log. + */ + calendarObserver(calls, objectName, methodOverrides = {}) { + function removeExtraHasPropertyChecks(objectName, calls) { + // Inserting the tracking calendar into the return values of methods + // that we chain up into the ISO calendar for, causes extra HasProperty + // checks, which we observe. This removes them so that we don't leak + // implementation details of the helper into the test code. + assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.year`); + assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.monthCode`); + assert.sameValue(calls.pop(), `has ${objectName}.month`); + assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`); + assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`); + assert.sameValue(calls.pop(), `has ${objectName}.id`); + assert.sameValue(calls.pop(), `has ${objectName}.fields`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.day`); + assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`); + assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`); + } + + const iso8601 = new Temporal.Calendar("iso8601"); + const trackingMethods = { + dateFromFields(...args) { + calls.push(`call ${objectName}.dateFromFields`); + if ('dateFromFields' in methodOverrides) { + const value = methodOverrides.dateFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + yearMonthFromFields(...args) { + calls.push(`call ${objectName}.yearMonthFromFields`); + if ('yearMonthFromFields' in methodOverrides) { + const value = methodOverrides.yearMonthFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.yearMonthFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + monthDayFromFields(...args) { + calls.push(`call ${objectName}.monthDayFromFields`); + if ('monthDayFromFields' in methodOverrides) { + const value = methodOverrides.monthDayFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.monthDayFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + dateAdd(...args) { + calls.push(`call ${objectName}.dateAdd`); + if ('dateAdd' in methodOverrides) { + const value = methodOverrides.dateAdd; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateAdd(...args); + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + id: "iso8601", + }; + // Automatically generate the other methods that don't need any custom code + [ + "dateUntil", + "day", + "dayOfWeek", + "dayOfYear", + "daysInMonth", + "daysInWeek", + "daysInYear", + "era", + "eraYear", + "fields", + "inLeapYear", + "mergeFields", + "month", + "monthCode", + "monthsInYear", + "toString", + "weekOfYear", + "year", + "yearOfWeek", + ].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return iso8601[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom calendar that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + calendarThrowEverything() { + class CalendarThrowEverything extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + dateFromFields() { + TemporalHelpers.assertUnreachable("dateFromFields should not be called"); + } + yearMonthFromFields() { + TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called"); + } + monthDayFromFields() { + TemporalHelpers.assertUnreachable("monthDayFromFields should not be called"); + } + dateAdd() { + TemporalHelpers.assertUnreachable("dateAdd should not be called"); + } + dateUntil() { + TemporalHelpers.assertUnreachable("dateUntil should not be called"); + } + era() { + TemporalHelpers.assertUnreachable("era should not be called"); + } + eraYear() { + TemporalHelpers.assertUnreachable("eraYear should not be called"); + } + year() { + TemporalHelpers.assertUnreachable("year should not be called"); + } + month() { + TemporalHelpers.assertUnreachable("month should not be called"); + } + monthCode() { + TemporalHelpers.assertUnreachable("monthCode should not be called"); + } + day() { + TemporalHelpers.assertUnreachable("day should not be called"); + } + fields() { + TemporalHelpers.assertUnreachable("fields should not be called"); + } + mergeFields() { + TemporalHelpers.assertUnreachable("mergeFields should not be called"); + } + } + + return new CalendarThrowEverything(); + }, + + /* + * oneShiftTimeZone(shiftInstant, shiftNanoseconds): + * + * In the case of a spring-forward time zone offset transition (skipped time), + * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a + * negative number of nanoseconds from a PlainDateTime, which should balance + * with the microseconds field. + * + * This returns an instance of a custom time zone class which skips a length + * of time equal to shiftNanoseconds (a number), at the Temporal.Instant + * shiftInstant. Before shiftInstant, it's identical to UTC, and after + * shiftInstant it's a constant-offset time zone. + * + * It provides a getPossibleInstantsForCalledWith member which is an array + * with the result of calling toString() on any PlainDateTimes passed to + * getPossibleInstantsFor(). + */ + oneShiftTimeZone(shiftInstant, shiftNanoseconds) { + class OneShiftTimeZone extends Temporal.TimeZone { + constructor(shiftInstant, shiftNanoseconds) { + super("+00:00"); + this._shiftInstant = shiftInstant; + this._epoch1 = shiftInstant.epochNanoseconds; + this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds); + this._shiftNanoseconds = shiftNanoseconds; + this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds); + this.getPossibleInstantsForCalledWith = []; + } + + _isBeforeShift(instant) { + return instant.epochNanoseconds < this._epoch1; + } + + getOffsetNanosecondsFor(instant) { + return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds; + } + + getPossibleInstantsFor(plainDateTime) { + this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" })); + const [instant] = super.getPossibleInstantsFor(plainDateTime); + if (this._shiftNanoseconds > 0) { + if (this._isBeforeShift(instant)) return [instant]; + if (instant.epochNanoseconds < this._epoch2) return []; + return [instant.subtract(this._shift)]; + } + if (instant.epochNanoseconds < this._epoch2) return [instant]; + const shifted = instant.subtract(this._shift); + if (this._isBeforeShift(instant)) return [instant, shifted]; + return [shifted]; + } + + getNextTransition(instant) { + return this._isBeforeShift(instant) ? this._shiftInstant : null; + } + + getPreviousTransition(instant) { + return this._isBeforeShift(instant) ? null : this._shiftInstant; + } + + toString() { + return "Custom/One_Shift"; + } + } + return new OneShiftTimeZone(shiftInstant, shiftNanoseconds); + }, + + /* + * propertyBagObserver(): + * Returns an object that behaves like the given propertyBag but tracks Get + * and Has operations on any of its properties, by appending messages to an + * array. If the value of a property in propertyBag is a primitive, the value + * of the returned object's property will additionally be a + * TemporalHelpers.toPrimitiveObserver that will track calls to its toString + * and valueOf methods in the same array. This is for the purpose of testing + * order of operations that are observable from user code. objectName is used + * in the log. + */ + propertyBagObserver(calls, propertyBag, objectName) { + return new Proxy(propertyBag, { + ownKeys(target) { + calls.push(`ownKeys ${objectName}`); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(target, key) { + calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); + return Reflect.getOwnPropertyDescriptor(target, key); + }, + get(target, key, receiver) { + calls.push(`get ${formatPropertyName(key, objectName)}`); + const result = Reflect.get(target, key, receiver); + if (result === undefined) { + return undefined; + } + if ((result !== null && typeof result === "object") || typeof result === "function") { + return result; + } + return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * specificOffsetTimeZone(): + * + * This returns an instance of a custom time zone class, which returns a + * specific custom value from its getOffsetNanosecondsFrom() method. This is + * for the purpose of testing the validation of what this method returns. + * + * It also returns an empty array from getPossibleInstantsFor(), so as to + * trigger calls to getOffsetNanosecondsFor() when used from the + * BuiltinTimeZoneGetInstantFor operation. + */ + specificOffsetTimeZone(offsetValue) { + class SpecificOffsetTimeZone extends Temporal.TimeZone { + constructor(offsetValue) { + super("UTC"); + this._offsetValue = offsetValue; + } + + getOffsetNanosecondsFor() { + return this._offsetValue; + } + + getPossibleInstantsFor(dt) { + if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return []; + const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue }); + return [zdt.toInstant()]; + } + + get id() { + return this.getOffsetStringFor(new Temporal.Instant(0n)); + } + } + return new SpecificOffsetTimeZone(offsetValue); + }, + + /* + * springForwardFallBackTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single spring-forward/fall-back transition, for the purpose of testing the + * disambiguation option, without depending on system time zone data. + * + * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00 + * local) and goes from offset -08:00 to -07:00. + * + * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and + * goes from offset -07:00 to -08:00. + */ + springForwardFallBackTimeZone() { + const { compare } = Temporal.PlainDateTime; + const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2); + const springForwardEpoch = 954669600_000_000_000n; + const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1); + const fallBackEpoch = 972810000_000_000_000n; + const winterOffset = new Temporal.TimeZone('-08:00'); + const summerOffset = new Temporal.TimeZone('-07:00'); + + class SpringForwardFallBackTimeZone extends Temporal.TimeZone { + constructor() { + super("-08:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < springForwardEpoch || + instant.epochNanoseconds >= fallBackEpoch) { + return winterOffset.getOffsetNanosecondsFor(instant); + } + return summerOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) { + return []; + } + if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) { + return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)]; + } + if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) { + return [winterOffset.getInstantFor(datetime)]; + } + return [summerOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + return null; + } + + get id() { + return "Custom/Spring_Fall"; + } + + toString() { + return "Custom/Spring_Fall"; + } + } + return new SpringForwardFallBackTimeZone(); + }, + + /* + * timeZoneObserver: + * A custom calendar that behaves exactly like the UTC time zone but tracks + * calls to any of its methods, and Get/Has operations on its properties, by + * appending messages to an array. This is for the purpose of testing order of + * operations that are observable from user code. objectName is used in the + * log. methodOverrides is an optional object containing properties with the + * same name as Temporal.TimeZone methods. If the property value is a function + * it will be called with the proper arguments instead of the UTC method. + * Otherwise, the property value will be returned directly. + */ + timeZoneObserver(calls, objectName, methodOverrides = {}) { + const utc = new Temporal.TimeZone("UTC"); + const trackingMethods = { + id: "UTC", + }; + // Automatically generate the methods + ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return utc[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom time zone that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + timeZoneThrowEverything() { + class TimeZoneThrowEverything extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + getOffsetNanosecondsFor() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called"); + } + getPossibleInstantsFor() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + } + + return new TimeZoneThrowEverything(); + }, + + /* + * Returns an object that will append logs of any Gets or Calls of its valueOf + * or toString properties to the array calls. Both valueOf and toString will + * return the actual primitiveValue. propertyName is used in the log. + */ + toPrimitiveObserver(calls, primitiveValue, propertyName) { + return { + get valueOf() { + calls.push(`get ${propertyName}.valueOf`); + return function () { + calls.push(`call ${propertyName}.valueOf`); + return primitiveValue; + }; + }, + get toString() { + calls.push(`get ${propertyName}.toString`); + return function () { + calls.push(`call ${propertyName}.toString`); + if (primitiveValue === undefined) return undefined; + return primitiveValue.toString(); + }; + }, + }; + }, + + /* + * An object containing further methods that return arrays of ISO strings, for + * testing parsers. + */ + ISO: { + /* + * PlainMonthDay strings that are not valid. + */ + plainMonthDayStringsInvalid() { + return [ + "11-18junk", + "11-18[u-ca=gregory]", + "11-18[u-ca=hebrew]", + ]; + }, + + /* + * PlainMonthDay strings that are valid and that should produce October 1st. + */ + plainMonthDayStringsValid() { + return [ + "10-01", + "1001", + "1965-10-01", + "1976-10-01T152330.1+00:00", + "19761001T15:23:30.1+00:00", + "1976-10-01T15:23:30.1+0000", + "1976-10-01T152330.1+0000", + "19761001T15:23:30.1+0000", + "19761001T152330.1+00:00", + "19761001T152330.1+0000", + "+001976-10-01T152330.1+00:00", + "+0019761001T15:23:30.1+00:00", + "+001976-10-01T15:23:30.1+0000", + "+001976-10-01T152330.1+0000", + "+0019761001T15:23:30.1+0000", + "+0019761001T152330.1+00:00", + "+0019761001T152330.1+0000", + "1976-10-01T15:23:00", + "1976-10-01T15:23", + "1976-10-01T15", + "1976-10-01", + "--10-01", + "--1001", + ]; + }, + + /* + * PlainTime strings that may be mistaken for PlainMonthDay or + * PlainYearMonth strings, and so require a time designator. + */ + plainTimeStringsAmbiguous() { + const ambiguousStrings = [ + "2021-12", // ambiguity between YYYY-MM and HHMM-UU + "2021-12[-12:00]", // ditto, TZ does not disambiguate + "1214", // ambiguity between MMDD and HHMM + "0229", // ditto, including MMDD that doesn't occur every year + "1130", // ditto, including DD that doesn't occur in every month + "12-14", // ambiguity between MM-DD and HH-UU + "12-14[-14:00]", // ditto, TZ does not disambiguate + "202112", // ambiguity between YYYYMM and HHMMSS + "202112[UTC]", // ditto, TZ does not disambiguate + ]; + // Adding a calendar annotation to one of these strings must not cause + // disambiguation in favour of time. + const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]'); + return ambiguousStrings.concat(stringsWithCalendar); + }, + + /* + * PlainTime strings that are of similar form to PlainMonthDay and + * PlainYearMonth strings, but are not ambiguous due to components that + * aren't valid as months or days. + */ + plainTimeStringsUnambiguous() { + return [ + "2021-13", // 13 is not a month + "202113", // ditto + "2021-13[-13:00]", // ditto + "202113[-13:00]", // ditto + "0000-00", // 0 is not a month + "000000", // ditto + "0000-00[UTC]", // ditto + "000000[UTC]", // ditto + "1314", // 13 is not a month + "13-14", // ditto + "1232", // 32 is not a day + "0230", // 30 is not a day in February + "0631", // 31 is not a day in June + "0000", // 0 is neither a month nor a day + "00-00", // ditto + ]; + }, + + /* + * PlainYearMonth-like strings that are not valid. + */ + plainYearMonthStringsInvalid() { + return [ + "2020-13", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November + * 1976 in the ISO 8601 calendar. + */ + plainYearMonthStringsValid() { + return [ + "1976-11", + "1976-11-10", + "1976-11-01T09:00:00+00:00", + "1976-11-01T00:00:00+05:00", + "197611", + "+00197611", + "1976-11-18T15:23:30.1\u221202:00", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "1976-11-18T15:23", + "1976-11-18T15", + "1976-11-18", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November of + * the ISO year -9999. + */ + plainYearMonthStringsValidNegativeYear() { + return [ + "\u2212009999-11", + ]; + }, + } +}; diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js new file mode 100644 index 0000000000..422507917a --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: +01:00 +features: [Temporal] +---*/ + +var zone = new Temporal.TimeZone("+01:00"); +var inst = Temporal.Instant.fromEpochSeconds(Math.floor(Math.random() * 1000000000)); +var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); +assert.sameValue(zone.id, `${ zone }`) +assert.sameValue(zone.getOffsetNanosecondsFor(inst), 3600000000000) +assert(zone.getPlainDateTimeFor(inst) instanceof Temporal.PlainDateTime) +assert(zone.getInstantFor(dtm) instanceof Temporal.Instant) +assert.sameValue(zone.getNextTransition(inst), null) +assert.sameValue(zone.getPreviousTransition(inst), null) + +// wraps around to the next day +assert.sameValue(`${ zone.getPlainDateTimeFor(Temporal.Instant.from("2020-02-06T23:59Z")) }`, "2020-02-07T00:59:00") + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js new file mode 100644 index 0000000000..7d8a75e581 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-timezone-objects +description: UTC +features: [Temporal] +---*/ + +var zone = new Temporal.TimeZone("UTC"); +var inst = Temporal.Instant.fromEpochSeconds(Math.floor(Math.random() * 1000000000)); +var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); +assert.sameValue(zone.id, `${ zone }`) +assert.sameValue(zone.getOffsetNanosecondsFor(inst), 0) +assert(zone.getPlainDateTimeFor(inst) instanceof Temporal.PlainDateTime) +assert(zone.getInstantFor(dtm) instanceof Temporal.Instant) +assert.sameValue(zone.getNextTransition(inst), null) +assert.sameValue(zone.getPreviousTransition(inst), null) + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/shell.js b/js/src/tests/test262/staging/Temporal/TimeZone/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/TimeZone/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js b/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js new file mode 100644 index 0000000000..b9b3c0bc56 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js @@ -0,0 +1,160 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: calendar with extra fields +features: [Temporal, Array.prototype.includes] +---*/ + +class SeasonCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + get id() { + return "season"; + } + toString() { + return "season"; + } + month(date) { + var {isoMonth} = date.getISOFields(); + return (isoMonth - 1) % 3 + 1; + } + monthCode(date) { + return `M${ this.month(date).toString().padStart(2, "0") }`; + } + season(date) { + var {isoMonth} = date.getISOFields(); + return Math.floor((isoMonth - 1) / 3) + 1; + } + _isoMonthCode(fields) { + var month = fields.month || +fields.monthCode.slice(1); + return `M${ ((fields.season - 1) * 3 + month).toString().padStart(2, "0") }`; + } + dateFromFields(fields, options) { + var monthCode = this._isoMonthCode(fields); + delete fields.month; + return super.dateFromFields({ + ...fields, + monthCode + }, options).withCalendar(this); + } + yearMonthFromFields(fields, options) { + var monthCode = this._isoMonthCode(fields); + delete fields.month; + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({ + ...fields, + monthCode + }, options).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + monthDayFromFields(fields, options) { + var monthCode = this._isoMonthCode(fields); + delete fields.month; + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({ + ...fields, + monthCode + }, options).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + fields(fields) { + fields = fields.slice(); + if (fields.includes("month") || fields.includes("monthCode")) + fields.push("season"); + return fields; + } +} +var calendar = new SeasonCalendar(); +var datetime = new Temporal.PlainDateTime(2019, 9, 15, 0, 0, 0, 0, 0, 0, calendar); +var date = new Temporal.PlainDate(2019, 9, 15, calendar); +var yearmonth = new Temporal.PlainYearMonth(2019, 9, calendar); +var monthday = new Temporal.PlainMonthDay(9, 15, calendar); +var zoned = new Temporal.ZonedDateTime(1568505600000000000n, "UTC", calendar); +var propDesc = { + get() { + return this.getCalendar().season(this); + }, + configurable: true +}; +Object.defineProperty(Temporal.PlainDateTime.prototype, "season", propDesc); +Object.defineProperty(Temporal.PlainDate.prototype, "season", propDesc); +Object.defineProperty(Temporal.PlainYearMonth.prototype, "season", propDesc); +Object.defineProperty(Temporal.PlainMonthDay.prototype, "season", propDesc); +Object.defineProperty(Temporal.ZonedDateTime.prototype, "season", propDesc); + +// property getter works +assert.sameValue(datetime.season, 3); +assert.sameValue(datetime.month, 3); +assert.sameValue(datetime.monthCode, "M03"); +assert.sameValue(date.season, 3); +assert.sameValue(date.month, 3); +assert.sameValue(date.monthCode, "M03"); +assert.sameValue(yearmonth.season, 3); +assert.sameValue(yearmonth.month, 3); +assert.sameValue(yearmonth.monthCode, "M03"); +assert.sameValue(monthday.season, 3); +assert.sameValue(monthday.monthCode, "M03"); +assert.sameValue(zoned.season, 3); +assert.sameValue(zoned.month, 3); +assert.sameValue(zoned.monthCode, "M03"); + +// accepts season in from() +assert.sameValue(`${ Temporal.PlainDateTime.from({ + year: 2019, + season: 3, + month: 3, + day: 15, + calendar +}) }`, "2019-09-15T00:00:00[u-ca=season]"); +assert.sameValue(`${ Temporal.PlainDate.from({ + year: 2019, + season: 3, + month: 3, + day: 15, + calendar +}) }`, "2019-09-15[u-ca=season]"); +assert.sameValue(`${ Temporal.PlainYearMonth.from({ + year: 2019, + season: 3, + month: 3, + calendar +}) }`, "2019-09-01[u-ca=season]"); +assert.sameValue(`${ Temporal.PlainMonthDay.from({ + season: 3, + monthCode: "M03", + day: 15, + calendar +}) }`, "1972-09-15[u-ca=season]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 2019, + season: 3, + month: 3, + day: 15, + timeZone: "UTC", + calendar +}) }`, "2019-09-15T00:00:00+00:00[UTC][u-ca=season]"); + +// accepts season in with() +assert.sameValue(`${ datetime.with({ season: 2 }) }`, "2019-06-15T00:00:00[u-ca=season]"); +assert.sameValue(`${ date.with({ season: 2 }) }`, "2019-06-15[u-ca=season]"); +assert.sameValue(`${ yearmonth.with({ season: 2 }) }`, "2019-06-01[u-ca=season]"); +assert.sameValue(`${ monthday.with({ season: 2 }) }`, "1972-06-15[u-ca=season]"); +assert.sameValue(`${ zoned.with({ season: 2 }) }`, "2019-06-15T00:00:00+00:00[UTC][u-ca=season]"); + +// translates month correctly in with() +assert.sameValue(`${ datetime.with({ month: 2 }) }`, "2019-08-15T00:00:00[u-ca=season]"); +assert.sameValue(`${ date.with({ month: 2 }) }`, "2019-08-15[u-ca=season]"); +assert.sameValue(`${ yearmonth.with({ month: 2 }) }`, "2019-08-01[u-ca=season]"); +assert.sameValue(`${ monthday.with({ monthCode: "M02" }) }`, "1972-08-15[u-ca=season]"); +assert.sameValue(`${ zoned.with({ month: 2 }) }`, "2019-08-15T00:00:00+00:00[UTC][u-ca=season]"); + +delete Temporal.PlainDateTime.prototype.season; +delete Temporal.PlainDate.prototype.season; +delete Temporal.PlainYearMonth.prototype.season; +delete Temporal.PlainMonthDay.prototype.season; +delete Temporal.ZonedDateTime.prototype.season; + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js new file mode 100644 index 0000000000..a3a836525f --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js @@ -0,0 +1,154 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: calendar with nontrivial mergeFields implementation +features: [Temporal, Array.prototype.includes] +---*/ + +class CenturyCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + get id() { + return 'century'; + } + toString() { + return "century"; + } + century(date) { + var {isoYear} = date.getISOFields(); + return Math.ceil(isoYear / 100); + } + centuryYear(date) { + var {isoYear} = date.getISOFields(); + return isoYear % 100; + } + _validateFields(fields) { + var {year, century, centuryYear} = fields; + if (century === undefined !== (centuryYear === undefined)) { + throw new TypeError("pass either both or neither of century and centuryYear"); + } + if (year === undefined) + return (century - 1) * 100 + centuryYear; + if (century !== undefined) { + var centuryCalculatedYear = (century - 1) * 100 + centuryYear; + if (year !== centuryCalculatedYear) { + throw new RangeError("year must agree with century/centuryYear if both given"); + } + } + return year; + } + dateFromFields(fields, options) { + var isoYear = this._validateFields(fields); + return super.dateFromFields({ + ...fields, + year: isoYear + }, options).withCalendar(this); + } + yearMonthFromFields(fields, options) { + var year = this._validateFields(fields); + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({ + ...fields, + year, + }, options).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + monthDayFromFields(fields, options) { + var year = this._validateFields(fields); + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({ + ...fields, + year, + }, options).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + fields(fields) { + fields = fields.slice(); + if (fields.includes("year")) + fields.push("century", "centuryYear"); + return fields; + } + mergeFields(fields, additionalFields) { + var {year, century, centuryYear, ...original} = fields; + var { + year: newYear, + century: newCentury, + centuryYear: newCenturyYear + } = additionalFields; + if (newYear === undefined) { + original.century = century; + original.centuryYear = centuryYear; + } + if (newCentury === undefined && newCenturyYear === undefined) { + original.year === year; + } + return { + ...original, + ...additionalFields + }; + } +} +var calendar = new CenturyCalendar(); +var datetime = new Temporal.PlainDateTime(2019, 9, 15, 0, 0, 0, 0, 0, 0, calendar); +var date = new Temporal.PlainDate(2019, 9, 15, calendar); +var yearmonth = new Temporal.PlainYearMonth(2019, 9, calendar); +var zoned = new Temporal.ZonedDateTime(1568505600000000000n, "UTC", calendar); +var propDesc = { + century: { + get() { + return this.getCalendar().century(this); + }, + configurable: true + }, + centuryYear: { + get() { + return this.getCalendar().centuryYear(this); + }, + configurable: true + } +}; +Object.defineProperties(Temporal.PlainDateTime.prototype, propDesc); +Object.defineProperties(Temporal.PlainDate.prototype, propDesc); +Object.defineProperties(Temporal.PlainYearMonth.prototype, propDesc); +Object.defineProperties(Temporal.ZonedDateTime.prototype, propDesc); + +// property getters work +assert.sameValue(datetime.century, 21); +assert.sameValue(datetime.centuryYear, 19); +assert.sameValue(date.century, 21); +assert.sameValue(date.centuryYear, 19); +assert.sameValue(yearmonth.century, 21); +assert.sameValue(yearmonth.centuryYear, 19); +assert.sameValue(zoned.century, 21); +assert.sameValue(zoned.centuryYear, 19); + +// correctly resolves century in with() +assert.sameValue(`${ datetime.with({ century: 20 }) }`, "1919-09-15T00:00:00[u-ca=century]"); +assert.sameValue(`${ date.with({ century: 20 }) }`, "1919-09-15[u-ca=century]"); +assert.sameValue(`${ yearmonth.with({ century: 20 }) }`, "1919-09-01[u-ca=century]"); +assert.sameValue(`${ zoned.with({ century: 20 }) }`, "1919-09-15T00:00:00+00:00[UTC][u-ca=century]"); + +// correctly resolves centuryYear in with() +assert.sameValue(`${ datetime.with({ centuryYear: 5 }) }`, "2005-09-15T00:00:00[u-ca=century]"); +assert.sameValue(`${ date.with({ centuryYear: 5 }) }`, "2005-09-15[u-ca=century]"); +assert.sameValue(`${ yearmonth.with({ centuryYear: 5 }) }`, "2005-09-01[u-ca=century]"); +assert.sameValue(`${ zoned.with({ centuryYear: 5 }) }`, "2005-09-15T00:00:00+00:00[UTC][u-ca=century]"); + +// correctly resolves year in with() +assert.sameValue(`${ datetime.with({ year: 1974 }) }`, "1974-09-15T00:00:00[u-ca=century]"); +assert.sameValue(`${ date.with({ year: 1974 }) }`, "1974-09-15[u-ca=century]"); +assert.sameValue(`${ yearmonth.with({ year: 1974 }) }`, "1974-09-01[u-ca=century]"); +assert.sameValue(`${ zoned.with({ year: 1974 }) }`, "1974-09-15T00:00:00+00:00[UTC][u-ca=century]"); + +delete Temporal.PlainDateTime.prototype.century; +delete Temporal.PlainDateTime.prototype.centuryYear; +delete Temporal.PlainDate.prototype.century; +delete Temporal.PlainDate.prototype.centuryYear; +delete Temporal.PlainYearMonth.prototype.century; +delete Temporal.PlainYearMonth.prototype.centuryYear; +delete Temporal.ZonedDateTime.prototype.century; +delete Temporal.ZonedDateTime.prototype.centuryYear; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js new file mode 100644 index 0000000000..b3bfd43e09 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js @@ -0,0 +1,231 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial protocol implementation +features: [Temporal] +---*/ + +function decimalToISO(year, month, day, overflow = "constrain") { + if (overflow === "constrain") { + if (month < 1) + month = 1; + if (month > 10) + month = 10; + if (day < 1) + day = 1; + if (day > 10) + day = 10; + } else if (overflow === "reject") { + if (month < 1 || month > 10 || day < 1 || day > 10) { + throw new RangeError("invalid value"); + } + } + var days = year * 100 + (month - 1) * 10 + (day - 1); + return new Temporal.PlainDate(1970, 1, 1, "iso8601").add({ days }); +} +function isoToDecimal(date) { + var {isoYear, isoMonth, isoDay} = date.getISOFields(); + var isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay); + var {days} = isoDate.since(new Temporal.PlainDate(1970, 1, 1), { largestUnit: "days" }); + var year = Math.floor(days / 100); + days %= 100; + return { + year, + days + }; +} +var obj = { + id: 'decimal', + toString() { + return "decimal"; + }, + fields(fieldNames) { return fieldNames; }, + dateFromFields(fields, options) { + var { + overflow = "constrain" + } = options ? options : {}; + var {month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + var isoDate = decimalToISO(fields.year, month, fields.day, 0, 0, 0, overflow); + return new Temporal.PlainDate(isoDate.year, isoDate.month, isoDate.day, this); + }, + yearMonthFromFields(fields, options) { + var { + overflow = "constrain" + } = options ? options : {}; + var {month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + var isoDate = decimalToISO(fields.year, month, 1, 0, 0, 0, overflow); + return new Temporal.PlainYearMonth(isoDate.year, isoDate.month, this, isoDate.day); + }, + monthDayFromFields(fields, options) { + var { + overflow = "constrain" + } = options ? options : {}; + var {month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + var isoDate = decimalToISO(0, month, fields.day, 0, 0, 0, overflow); + return new Temporal.PlainMonthDay(isoDate.month, isoDate.day, this, isoDate.year); + }, + year(date) { + return isoToDecimal(date).year; + }, + month(date) { + var {days} = isoToDecimal(date); + return Math.floor(days / 10) + 1; + }, + monthCode(date) { + return `M${ this.month(date).toString().padStart(2, "0") }`; + }, + day(date) { + var {days} = isoToDecimal(date); + return days % 10 + 1; + }, + dateAdd() {}, // left as an exercise for the reader + dateUntil() {}, // ditto + dayOfWeek() { + throw new Error('no weeks'); + }, + dayOfYear(date) { + return isoToDecimal(date).days; + }, + daysInMonth() { + return 10; + }, + daysInWeek() { + throw new Error('no weeks'); + }, + daysInYear() { + return 100; + }, + fields(fields) { + return fields; + }, + inLeapYear() { + return false; + }, + mergeFields(fields, additional) { + return new Temporal.Calendar("iso8601").mergeFields(fields, additional) + }, + monthsInYear() { + return 10; + }, + weekOfYear() { + throw new Error('no weeks'); + }, + yearOfWeek(date) { + throw new Error('no weeks'); + }, +}; +var date = Temporal.PlainDate.from({ + year: 184, + month: 2, + day: 9, + calendar: obj +}); +var dt = Temporal.PlainDateTime.from({ + year: 184, + month: 2, + day: 9, + hour: 12, + calendar: obj +}); +var ym = Temporal.PlainYearMonth.from({ + year: 184, + month: 2, + calendar: obj +}); +var md = Temporal.PlainMonthDay.from({ + monthCode: "M02", + day: 9, + calendar: obj +}); + +// is a calendar +assert.sameValue(typeof obj, "object") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.Calendar.from("decimal")); +assert.throws(RangeError, () => Temporal.Calendar.from("2020-06-05T09:34-00:00[UTC][u-ca=decimal]")); + +// Temporal.PlainDate.from() +assert.sameValue(`${ date }`, "2020-06-05[u-ca=decimal]") + +// Temporal.PlainDate fields +assert.sameValue(date.year, 184); +assert.sameValue(date.month, 2); +assert.sameValue(date.day, 9); + +// date.with() +var date2 = date.with({ year: 0 }); +assert.sameValue(date2.year, 0); + +// date.withCalendar() +var date2 = Temporal.PlainDate.from("2020-06-05T12:00"); +assert(date2.withCalendar(obj).equals(date)); + +// Temporal.PlainDateTime.from() +assert.sameValue(`${ dt }`, "2020-06-05T12:00:00[u-ca=decimal]") + +// Temporal.PlainDateTime fields +assert.sameValue(dt.year, 184); +assert.sameValue(dt.month, 2); +assert.sameValue(dt.day, 9); +assert.sameValue(dt.hour, 12); +assert.sameValue(dt.minute, 0); +assert.sameValue(dt.second, 0); +assert.sameValue(dt.millisecond, 0); +assert.sameValue(dt.microsecond, 0); +assert.sameValue(dt.nanosecond, 0); + +// datetime.with() +var dt2 = dt.with({ year: 0 }); +assert.sameValue(dt2.year, 0); + +// datetime.withCalendar() +var dt2 = Temporal.PlainDateTime.from("2020-06-05T12:00"); +assert(dt2.withCalendar(obj).equals(dt)); + +// Temporal.PlainYearMonth.from() +assert.sameValue(`${ ym }`, "2020-05-28[u-ca=decimal]") + +// Temporal.PlainYearMonth fields +assert.sameValue(dt.year, 184); +assert.sameValue(dt.month, 2); + +// yearmonth.with() +var ym2 = ym.with({ year: 0 }); +assert.sameValue(ym2.year, 0); + +// Temporal.PlainMonthDay.from() +assert.sameValue(`${ md }`, "1970-01-19[u-ca=decimal]") + +// Temporal.PlainMonthDay fields +assert.sameValue(md.monthCode, "M02"); +assert.sameValue(md.day, 9); + +// monthday.with() +var md2 = md.with({ monthCode: "M01" }); +assert.sameValue(md2.monthCode, "M01"); + +// timezone.getPlainDateTimeFor() +var tz = Temporal.TimeZone.from("UTC"); +var inst = Temporal.Instant.fromEpochSeconds(0); +var dt = tz.getPlainDateTimeFor(inst, obj); +assert.sameValue(dt.getCalendar(), obj); + +// Temporal.Now.plainDateTime() +var nowDateTime = Temporal.Now.plainDateTime(obj, "UTC"); +assert.sameValue(nowDateTime.getCalendar(), obj); + +// Temporal.Now.plainDate() +var nowDate = Temporal.Now.plainDate(obj, "UTC"); +assert.sameValue(nowDate.getCalendar(), obj); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js new file mode 100644 index 0000000000..b96e75c95c --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js @@ -0,0 +1,167 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial subclass +features: [Temporal] +---*/ + +class TwoBasedCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + get id() { + return "two-based"; + } + toString() { + return "two-based"; + } + dateFromFields(fields, options) { + var {year, month, monthCode, day} = fields; + if (month === undefined) + month = +monthCode.slice(1); + return super.dateFromFields({ + year, + monthCode: `M${ (month - 1).toString().padStart(2, "0") }`, + day + }, options).withCalendar(this); + } + yearMonthFromFields(fields, options) { + var {year, month, monthCode} = fields; + if (month === undefined) + month = +monthCode.slice(1); + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({ + year, + monthCode: `M${ (month - 1).toString().padStart(2, "0") }` + }, options).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + monthDayFromFields(fields, options) { + var {month, monthCode, day} = fields; + if (month === undefined) + month = +monthCode.slice(1); + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({ + monthCode: `M${ (month - 1).toString().padStart(2, "0") }`, + day + }, options).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + month(date) { + return date.getISOFields().isoMonth + 1; + } + monthCode(date) { + return `M${ this.month(date).toString().padStart(2, "0") }`; + } +} +var obj = new TwoBasedCalendar(); +var date = Temporal.PlainDate.from({ + year: 2020, + month: 5, + day: 5, + calendar: obj +}); +var dt = Temporal.PlainDateTime.from({ + year: 2020, + month: 5, + day: 5, + hour: 12, + calendar: obj +}); +var ym = Temporal.PlainYearMonth.from({ + year: 2020, + month: 5, + calendar: obj +}); +var md = Temporal.PlainMonthDay.from({ + monthCode: "M05", + day: 5, + calendar: obj +}); + +// is a calendar +assert.sameValue(typeof obj, "object") + +// .id property +assert.sameValue(obj.id, "two-based") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.Calendar.from("two-based")); +assert.throws(RangeError, () => Temporal.Calendar.from("2020-06-05T09:34-00:00[UTC][u-ca=two-based]")); + +// Temporal.PlainDate.from() +assert.sameValue(`${ date }`, "2020-04-05[u-ca=two-based]") + +// Temporal.PlainDate fields +assert.sameValue(date.year, 2020); +assert.sameValue(date.month, 5); +assert.sameValue(date.day, 5); + +// date.with() +var date2 = date.with({ month: 2 }); +assert.sameValue(date2.month, 2); + +// date.withCalendar() +var date2 = Temporal.PlainDate.from("2020-04-05"); +assert(date2.withCalendar(obj).equals(date)); + +// Temporal.PlainDateTime.from() +assert.sameValue(`${ dt }`, "2020-04-05T12:00:00[u-ca=two-based]") + +// Temporal.PlainDateTime fields +assert.sameValue(dt.year, 2020); +assert.sameValue(dt.month, 5); +assert.sameValue(dt.day, 5); +assert.sameValue(dt.hour, 12); +assert.sameValue(dt.minute, 0); +assert.sameValue(dt.second, 0); +assert.sameValue(dt.millisecond, 0); +assert.sameValue(dt.microsecond, 0); +assert.sameValue(dt.nanosecond, 0); + +// datetime.with() +var dt2 = dt.with({ month: 2 }); +assert.sameValue(dt2.month, 2); + +// datetime.withCalendar() +var dt2 = Temporal.PlainDateTime.from("2020-04-05T12:00"); +assert(dt2.withCalendar(obj).equals(dt)); + +// Temporal.PlainYearMonth.from() +assert.sameValue(`${ ym }`, "2020-04-01[u-ca=two-based]") + +// Temporal.PlainYearMonth fields +assert.sameValue(dt.year, 2020); +assert.sameValue(dt.month, 5); + +// yearmonth.with() +var ym2 = ym.with({ month: 2 }); +assert.sameValue(ym2.month, 2); + +// Temporal.PlainMonthDay.from() +assert.sameValue(`${ md }`, "1972-04-05[u-ca=two-based]") + +// Temporal.PlainMonthDay fields +assert.sameValue(md.monthCode, "M05"); +assert.sameValue(md.day, 5); + +// monthday.with() +var md2 = md.with({ monthCode: "M02" }); +assert.sameValue(md2.monthCode, "M02"); + +// timezone.getPlainDateTimeFor() +var tz = Temporal.TimeZone.from("UTC"); +var instant = Temporal.Instant.fromEpochSeconds(0); +var dt = tz.getPlainDateTimeFor(instant, obj); +assert.sameValue(dt.getCalendar(), obj); + +// Temporal.Now.plainDateTime() +var nowDateTime = Temporal.Now.plainDateTime(obj, "UTC"); +assert.sameValue(nowDateTime.getCalendar(), obj); + +// Temporal.Now.plainDate() +var nowDate = Temporal.Now.plainDate(obj, "UTC"); +assert.sameValue(nowDate.getCalendar(), obj); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js b/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js b/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js new file mode 100644 index 0000000000..52d11f3548 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js @@ -0,0 +1,106 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: sub-minute offset +features: [Temporal] +---*/ + +class SubminuteTimeZone extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + get id() { + return "Custom/Subminute"; + } + toString() { + return "Custom/Subminute"; + } + getOffsetNanosecondsFor() { + return -1111111111; + } + getPossibleInstantsFor(dateTime) { + var utc = Temporal.TimeZone.from("UTC"); + var instant = utc.getInstantFor(dateTime); + return [instant.add({ nanoseconds: 1111111111 })]; + } + getNextTransition() { + return null; + } + getPreviousTransition() { + return null; + } +} +var obj = new SubminuteTimeZone(); +var inst = Temporal.Instant.fromEpochNanoseconds(0n); +var dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); + +// is a time zone +assert.sameValue(typeof obj, "object") + +// .id property +assert.sameValue(obj.id, "Custom/Subminute") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.TimeZone.from("Custom/Subminute")); +assert.throws(RangeError, () => Temporal.TimeZone.from("2020-05-26T16:02:46.251163036-00:00:01.111111111[Custom/Subminute]")); + +// has offset string -00:00:01.111111111 +assert.sameValue(obj.getOffsetStringFor(inst), "-00:00:01.111111111") + +// converts to DateTime +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ obj.getPlainDateTimeFor(inst) }`, "1969-12-31T23:59:58.888888889"); +assert.sameValue(`${ obj.getPlainDateTimeFor(inst, fakeGregorian) }`, "1969-12-31T23:59:58.888888889[u-ca=gregory]"); + +// converts to Instant +assert.sameValue(`${ obj.getInstantFor(dt) }`, "1976-11-18T15:23:31.2345679Z"); + +// converts to string +assert.sameValue(`${ obj }`, obj.id) + +// offset prints with minute precision in instant.toString +assert.sameValue(inst.toString({ timeZone: obj }), "1969-12-31T23:59:58.888888889+00:00") + +// offset prints with minute precision prints in zdt.toString +var zdt = new Temporal.ZonedDateTime(0n, obj); +assert.sameValue(zdt.toString(), "1969-12-31T23:59:58.888888889+00:00[Custom/Subminute]"); + +// has no next transitions +assert.sameValue(obj.getNextTransition(), null) + +// has no previous transitions +assert.sameValue(obj.getPreviousTransition(), null) + +// works in Temporal.Now +assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js new file mode 100644 index 0000000000..e11e798f68 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial protocol implementation +features: [Temporal] +---*/ + +var obj = { + getOffsetNanosecondsFor() { + return 0; + }, + getPossibleInstantsFor(dateTime) { + var {year, month, day, hour, minute, second, millisecond, microsecond, nanosecond} = dateTime; + var dayNum = MakeDay(year, month, day); + var time = MakeTime(hour, minute, second, millisecond, microsecond, nanosecond); + var epochNs = MakeDate(dayNum, time); + return [new Temporal.Instant(epochNs)]; + }, + id: "Etc/Custom/UTC_Protocol", +}; +var inst = Temporal.Instant.fromEpochNanoseconds(0n); + +// offset prints in instant.toString +assert.sameValue(inst.toString({ timeZone: obj }), "1970-01-01T00:00:00+00:00") + +// prints in zdt.toString +var zdt = new Temporal.ZonedDateTime(0n, obj); +assert.sameValue(zdt.toString(), "1970-01-01T00:00:00+00:00[Etc/Custom/UTC_Protocol]"); + +// works in Temporal.Now +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js new file mode 100644 index 0000000000..6b6db93e35 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js @@ -0,0 +1,144 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Trivial subclass +features: [Temporal] +---*/ + +class CustomUTCSubclass extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + get id() { + return "Etc/Custom/UTC_Subclass"; + } + toString() { + return "Etc/Custom/UTC_Subclass"; + } + getOffsetNanosecondsFor() { + return 0; + } + getPossibleInstantsFor(dateTime) { + var {year, month, day, hour, minute, second, millisecond, microsecond, nanosecond} = dateTime; + var dayNum = MakeDay(year, month, day); + var time = MakeTime(hour, minute, second, millisecond, microsecond, nanosecond); + var epochNs = MakeDate(dayNum, time); + return [new Temporal.Instant(epochNs)]; + } + getNextTransition() { + return null; + } + getPreviousTransition() { + return null; + } +} + +const nsPerDay = 86400_000_000_000n; +const nsPerMillisecond = 1_000_000n; + +function Day(t) { + return t / nsPerDay; +} + +function MakeDate(day, time) { + return day * nsPerDay + time; +} + +function MakeDay(year, month, day) { + const m = month - 1; + const ym = year + Math.floor(m / 12); + const mn = m % 12; + const t = BigInt(Date.UTC(ym, mn, 1)) * nsPerMillisecond; + return Day(t) + BigInt(day) - 1n; +} + +function MakeTime(h, min, s, ms, µs, ns) { + const MinutesPerHour = 60n; + const SecondsPerMinute = 60n; + const nsPerSecond = 1_000_000_000n; + const nsPerMinute = nsPerSecond * SecondsPerMinute; + const nsPerHour = nsPerMinute * MinutesPerHour; + return ( + BigInt(h) * nsPerHour + + BigInt(min) * nsPerMinute + + BigInt(s) * nsPerSecond + + BigInt(ms) * nsPerMillisecond + + BigInt(µs) * 1000n + + BigInt(ns) + ); +} + +var obj = new CustomUTCSubclass(); +var inst = Temporal.Instant.fromEpochNanoseconds(0n); +var dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789); + +// is a time zone +assert.sameValue(typeof obj, "object") + +// .id property +assert.sameValue(obj.id, "Etc/Custom/UTC_Subclass") + +// .id is not available in from() +assert.throws(RangeError, () => Temporal.TimeZone.from("Etc/Custom/UTC_Subclass")); +assert.throws(RangeError, () => Temporal.TimeZone.from("2020-05-26T16:02:46.251163036+00:00[Etc/Custom/UTC_Subclass]")); + +// has offset string +00:00 +assert.sameValue(obj.getOffsetStringFor(inst), "+00:00") + +// converts to DateTime +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ obj.getPlainDateTimeFor(inst) }`, "1970-01-01T00:00:00"); +assert.sameValue(`${ obj.getPlainDateTimeFor(inst, fakeGregorian) }`, "1970-01-01T00:00:00[u-ca=gregory]"); + +// converts to Instant +assert.sameValue(`${ obj.getInstantFor(dt) }`, "1976-11-18T15:23:30.123456789Z"); + +// converts to string +assert.sameValue(`${ obj }`, obj.id) + +// offset prints in instant.toString +assert.sameValue(inst.toString({ timeZone: obj }), "1970-01-01T00:00:00+00:00") + +// prints in zdt.toString +var zdt = new Temporal.ZonedDateTime(0n, obj); +assert.sameValue(zdt.toString(), "1970-01-01T00:00:00+00:00[Etc/Custom/UTC_Subclass]"); + +// has no next transitions +assert.sameValue(obj.getNextTransition(), null) + +// has no previous transitions +assert.sameValue(obj.getPreviousTransition(), null) + +// works in Temporal.Now +assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime); +assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate); +assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js b/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js new file mode 100644 index 0000000000..e1fdbd9ac5 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.add() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1969-12-25T12:23:45.678901234+00:00[UTC]"); +// cross epoch in ms + var one = zdt.subtract({ + hours: 240, + nanoseconds: 800 + }); + var two = zdt.add({ + hours: 240, + nanoseconds: 800 + }); + var three = two.subtract({ + hours: 480, + nanoseconds: 1600 + }); + var four = one.add({ + hours: 480, + nanoseconds: 1600 + }); +assert.sameValue(`${ one }`, "1969-12-15T12:23:45.678900434+00:00[UTC]"); +assert.sameValue(`${ two }`, "1970-01-04T12:23:45.678902034+00:00[UTC]"); +assert(three.equals(one)); +assert(four.equals(two)); + +// zdt.add(durationObj) +var later = zdt.add(Temporal.Duration.from("PT240H0.000000800S")); +assert.sameValue(`${ later }`, "1970-01-04T12:23:45.678902034+00:00[UTC]"); + +// casts argument +assert.sameValue(`${ zdt.add("PT240H0.000000800S") }`, "1970-01-04T12:23:45.678902034+00:00[UTC]"); +var jan31 = Temporal.ZonedDateTime.from("2020-01-31T15:00-08:00[-08:00]"); + +// constrain when ambiguous result +assert.sameValue(`${ jan31.add({ months: 1 }) }`, "2020-02-29T15:00:00-08:00[-08:00]"); +assert.sameValue(`${ jan31.add({ months: 1 }, { overflow: "constrain" }) }`, "2020-02-29T15:00:00-08:00[-08:00]"); + +// symmetrical with regard to negative durations in the time part +assert.sameValue(`${ jan31.add({ minutes: -30 }) }`, "2020-01-31T14:30:00-08:00[-08:00]"); +assert.sameValue(`${ jan31.add({ seconds: -30 }) }`, "2020-01-31T14:59:30-08:00[-08:00]"); + +// throw when ambiguous result with reject +assert.throws(RangeError, () => jan31.add({ months: 1 }, { overflow: "reject" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js new file mode 100644 index 0000000000..7345c962b5 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js @@ -0,0 +1,146 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.compare() +features: [Temporal] +---*/ + +var zdt1 = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); +var zdt2 = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102+01:00[+01:00]"); + +// equal +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1), 0) + +// smaller/larger +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt2), -1) + +// larger/smaller +assert.sameValue(Temporal.ZonedDateTime.compare(zdt2, zdt1), 1) + +// casts first argument +assert.sameValue(Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + day: 18, + hour: 15, + timeZone: "+01:00" +}, zdt2), -1); +assert.sameValue(Temporal.ZonedDateTime.compare("1976-11-18T15:23:30.123456789+01:00[+01:00]", zdt2), -1); + +// casts second argument +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + day: 29, + hour: 10, + timeZone: "+01:00" +}), -1); +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, "2019-10-29T10:46:38.271986102+01:00[+01:00]"), -1); + +// object must contain at least the required properties +assert.sameValue(Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + day: 18, + timeZone: "+01:00" +}, zdt2), -1); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + month: 11, + day: 18, + timeZone: "+01:00" +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + year: 1976, + day: 18, + timeZone: "+01:00" +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + timeZone: "+01:00" +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + year: 1976, + month: 11, + day: 18 +}, zdt2)); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ + years: 1976, + months: 11, + days: 19, + hours: 15, + timeZone: "+01:00" +}, zdt2)); +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + day: 29, + timeZone: "+01:00" +}), -1); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + month: 10, + day: 29, + timeZone: "+01:00" +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + day: 29, + timeZone: "+01:00" +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + timeZone: "+01:00" +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + year: 2019, + month: 10, + day: 29 +})); +assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, { + years: 2019, + months: 10, + days: 29, + hours: 10, + timeZone: "+01:00" +})); + +// disregards time zone IDs if exact times are equal +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1.withTimeZone("+05:30")), 0); + +// disregards calendar IDs if exact times and time zones are equal +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1.withCalendar(fakeJapanese)), 0); + +// compares exact time, not clock time +var clockBefore = Temporal.ZonedDateTime.from("1999-12-31T23:30-08:00[-08:00]"); +var clockAfter = Temporal.ZonedDateTime.from("2000-01-01T01:30-04:00[-04:00]"); +assert.sameValue(Temporal.ZonedDateTime.compare(clockBefore, clockAfter), 1); +assert.sameValue(Temporal.PlainDateTime.compare(clockBefore.toPlainDateTime(), clockAfter.toPlainDateTime()), -1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js new file mode 100644 index 0000000000..eb039a9ee5 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js @@ -0,0 +1,117 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Construction and properties +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-08:00"); +var epochMillis = Date.UTC(1976, 10, 18, 15, 23, 30, 123); +var epochNanos = BigInt(epochMillis) * BigInt(1000000) + BigInt(456789); + +// works +var zdt = new Temporal.ZonedDateTime(epochNanos, tz); +assert(zdt instanceof Temporal.ZonedDateTime); +assert.sameValue(typeof zdt, "object"); +assert.sameValue(zdt.toInstant().epochSeconds, Math.floor(Date.UTC(1976, 10, 18, 15, 23, 30, 123) / 1000), "epochSeconds"); +assert.sameValue(zdt.toInstant().epochMilliseconds, Date.UTC(1976, 10, 18, 15, 23, 30, 123), "epochMilliseconds"); + +// Temporal.ZonedDateTime for (1976, 11, 18, 15, 23, 30, 123, 456, 789)" + var zdt = new Temporal.ZonedDateTime(epochNanos, "UTC"); +// can be constructed +assert(zdt instanceof Temporal.ZonedDateTime); +assert.sameValue(typeof zdt, "object"); + +assert.sameValue(zdt.year, 1976) +assert.sameValue(zdt.month, 11); +assert.sameValue(zdt.monthCode, "M11"); +assert.sameValue(zdt.day, 18); +assert.sameValue(zdt.hour, 15); +assert.sameValue(zdt.minute, 23); +assert.sameValue(zdt.second, 30); +assert.sameValue(zdt.millisecond, 123); +assert.sameValue(zdt.microsecond, 456); +assert.sameValue(zdt.nanosecond, 789); +assert.sameValue(zdt.epochSeconds, 217178610); +assert.sameValue(zdt.epochMilliseconds, 217178610123); +assert.sameValue(zdt.epochMicroseconds, 217178610123456n); +assert.sameValue(zdt.epochNanoseconds, 217178610123456789n); +assert.sameValue(zdt.dayOfWeek, 4); +assert.sameValue(zdt.dayOfYear, 323); +assert.sameValue(zdt.weekOfYear, 47); +assert.sameValue(zdt.daysInWeek, 7); +assert.sameValue(zdt.daysInMonth, 30); +assert.sameValue(zdt.daysInYear, 366); +assert.sameValue(zdt.monthsInYear, 12); +assert.sameValue(zdt.inLeapYear, true); +assert.sameValue(zdt.offset, "+00:00"); +assert.sameValue(zdt.offsetNanoseconds, 0); +assert.sameValue(`${ zdt }`, "1976-11-18T15:23:30.123456789+00:00[UTC]"); + +// Temporal.ZonedDateTime with non-UTC time zone and non-ISO calendar +// can be constructed +var fakeGregorian = { + era() { return "ce"; }, + year(date) { return date.withCalendar("iso8601").year; }, + month(date) { return date.withCalendar("iso8601").month; }, + monthCode(date) { return date.withCalendar("iso8601").monthCode; }, + day(date) { return date.withCalendar("iso8601").day; }, + dayOfWeek(date) { return date.withCalendar("iso8601").dayOfWeek; }, + dayOfYear(date) { return date.withCalendar("iso8601").dayOfYear; }, + weekOfYear(date) { return date.withCalendar("iso8601").weekOfYear; }, + daysInWeek(date) { return date.withCalendar("iso8601").daysInWeek; }, + daysInMonth(date) { return date.withCalendar("iso8601").daysInMonth; }, + daysInYear(date) { return date.withCalendar("iso8601").daysInYear; }, + monthsInYear(date) { return date.withCalendar("iso8601").monthsInYear; }, + inLeapYear(date) { return date.withCalendar("iso8601").inLeapYear; }, + id: "gregory", + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + fields() {}, + mergeFields() {}, + monthDayFromFields() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var fakeVienna = { + getOffsetNanosecondsFor() { return 3600_000_000_000; }, + getPossibleInstantsFor(datetime) { return [datetime.toZonedDateTime("+01:00").toInstant()]; }, + id: "Europe/Vienna", +} +var zdt = new Temporal.ZonedDateTime(epochNanos, fakeVienna, fakeGregorian); +assert(zdt instanceof Temporal.ZonedDateTime); +assert.sameValue(typeof zdt, "object"); + +assert.sameValue(zdt.era, "ce"); +assert.sameValue(zdt.year, 1976); +assert.sameValue(zdt.month, 11); +assert.sameValue(zdt.monthCode, "M11"); +assert.sameValue(zdt.day, 18); +assert.sameValue(zdt.hour, 16); +assert.sameValue(zdt.minute, 23); +assert.sameValue(zdt.second, 30); +assert.sameValue(zdt.millisecond, 123); +assert.sameValue(zdt.microsecond, 456); +assert.sameValue(zdt.nanosecond, 789); +assert.sameValue(zdt.epochSeconds, 217178610); +assert.sameValue(zdt.epochMilliseconds, 217178610123); +assert.sameValue(zdt.epochMicroseconds, 217178610123456n); +assert.sameValue(zdt.epochNanoseconds, 217178610123456789n); +assert.sameValue(zdt.dayOfWeek, 4); +assert.sameValue(zdt.dayOfYear, 323); +assert.sameValue(zdt.weekOfYear, 47); +assert.sameValue(zdt.daysInWeek, 7); +assert.sameValue(zdt.daysInMonth, 30); +assert.sameValue(zdt.daysInYear, 366); +assert.sameValue(zdt.monthsInYear, 12); +assert.sameValue(zdt.inLeapYear, true); +assert.sameValue(zdt.offset, "+01:00"); +assert.sameValue(zdt.offsetNanoseconds, 3600000000000); +assert.sameValue(`${ zdt }`, "1976-11-18T16:23:30.123456789+01:00[Europe/Vienna][u-ca=gregory]"); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js new file mode 100644 index 0000000000..5c1f644c1b --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: hours overflow +features: [Temporal] +---*/ + + +// subtract result +var later = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]"); +var earlier = later.subtract({ hours: 12 }); +assert.sameValue(`${ earlier }`, "2019-10-28T22:46:38.271986102-03:00[-03:00]"); + +// add result +var earlier = Temporal.ZonedDateTime.from("2020-05-31T23:12:38.271986102-04:00[-04:00]"); +var later = earlier.add({ hours: 2 }); +assert.sameValue(`${ later }`, "2020-06-01T01:12:38.271986102-04:00[-04:00]"); + +// symmetrical with regard to negative durations +assert.sameValue(`${ Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]").add({ hours: -12 }) }`, "2019-10-28T22:46:38.271986102-03:00[-03:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-05-31T23:12:38.271986102-04:00[-04:00]").subtract({ hours: -2 }) }`, "2020-06-01T01:12:38.271986102-04:00[-04:00]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js new file mode 100644 index 0000000000..6e8879ee65 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js @@ -0,0 +1,279 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: math around DST +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var tz = TemporalHelpers.springForwardFallBackTimeZone(); +var hourBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 2, 1).toZonedDateTime(tz); +var dayBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 1, 2, 30).toZonedDateTime(tz); + +// add 1 hour to get to DST start +var added = hourBeforeDstStart.add({ hours: 1 }); +assert.sameValue(added.hour, 3); +var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" }); +assert.sameValue(`${ diff }`, "PT1H"); +assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`); + +// add 2 hours to get to DST start +1 +var added = hourBeforeDstStart.add({ hours: 2 }); +assert.sameValue(added.hour, 4); +var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" }); +assert.sameValue(`${ diff }`, "PT2H"); +assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`); + +// add 1.5 hours to get to 0.5 hours after DST start +var added = hourBeforeDstStart.add({ + hours: 1, + minutes: 30 +}); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 30); +var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" }); +assert.sameValue(`${ diff }`, "PT1H30M"); +assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`); + +// Samoa date line change (add): 10:00PM 29 Dec 2011 -> 11:00PM 31 Dec 2011 +var timeZone = TemporalHelpers.crossDateLineTimeZone(); +var dayBeforeSamoaDateLineChangeAbs = timeZone.getInstantFor(new Temporal.PlainDateTime(2011, 12, 29, 22)); +var start = dayBeforeSamoaDateLineChangeAbs.toZonedDateTimeISO(timeZone); +var added = start.add({ + days: 1, + hours: 1 +}); +assert.sameValue(added.day, 31); +assert.sameValue(added.hour, 23); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P2DT1H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// Samoa date line change (subtract): 11:00PM 31 Dec 2011 -> 10:00PM 29 Dec 2011 +var dayAfterSamoaDateLineChangeAbs = timeZone.getInstantFor(new Temporal.PlainDateTime(2011, 12, 31, 23)); +var start = dayAfterSamoaDateLineChangeAbs.toZonedDateTimeISO(timeZone); +var skipped = start.subtract({ + days: 1, + hours: 1 +}); +assert.sameValue(skipped.day, 31); +assert.sameValue(skipped.hour, 22); +assert.sameValue(skipped.minute, 0); +var end = start.subtract({ + days: 2, + hours: 1 +}); +assert.sameValue(end.day, 29); +assert.sameValue(end.hour, 22); +assert.sameValue(end.minute, 0); +var diff = end.since(start, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "-P2DT1H"); +var undo = start.add(diff); +assert.sameValue(`${ undo }`, `${ end }`); + +// 3:30 day before DST start -> 3:30 day of DST start +var start = dayBeforeDstStart.add({ hours: 1 }); +var added = start.add({ days: 1 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 30); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1D"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// 2:30 day before DST start -> 3:30 day of DST start +var added = dayBeforeDstStart.add({ days: 1 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 30); +var diff = dayBeforeDstStart.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1D"); +var undo = dayBeforeDstStart.add(diff); +assert.sameValue(`${ undo }`, `${ added }`); + +// 1:30 day DST starts -> 4:30 day DST starts +var start = dayBeforeDstStart.add({ hours: 23 }); +var added = start.add({ hours: 2 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 4); +assert.sameValue(added.minute, 30); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "PT2H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// 2:00 day before DST starts -> 3:00 day DST starts +var start = hourBeforeDstStart.subtract({ days: 1 }).add({ hours: 1 }); +var added = start.add({ days: 1 }); +assert.sameValue(added.day, 2); +assert.sameValue(added.hour, 3); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1D"); +var undo = start.add(diff); +assert.sameValue(`${ undo }`, `${ added }`); + +// 1:00AM day DST starts -> (add 24 hours) -> 2:00AM day after DST starts +var start = hourBeforeDstStart; +var added = start.add({ hours: 24 }); +assert.sameValue(added.day, 3); +assert.sameValue(added.hour, 2); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1DT1H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// 12:00AM day DST starts -> (add 24 hours) -> 1:00AM day after DST starts +var start = hourBeforeDstStart.subtract({ hours: 1 }); +var added = start.add({ hours: 24 }); +assert.sameValue(added.day, 3); +assert.sameValue(added.hour, 1); +assert.sameValue(added.minute, 0); +var diff = start.until(added, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1DT1H"); +var undo = added.subtract(diff); +assert.sameValue(`${ undo }`, `${ start }`); + +// Difference can return day length > 24 hours +var start = Temporal.PlainDateTime.from("2000-10-27T01:45").toZonedDateTime(tz); +var end = Temporal.PlainDateTime.from("2000-10-30T01:15").toZonedDateTime(tz); +var diff = start.until(end, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P2DT24H30M"); +var undo = start.add(diff); +assert.sameValue(`${ undo }`, `${ end }`); + +// Difference rounding (nearest day) is DST-aware +var start = Temporal.PlainDateTime.from("2000-04-04T02:30").toZonedDateTime(tz); +var end = Temporal.PlainDateTime.from("2000-04-01T14:15").toZonedDateTime(tz); +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "-P3D"); + +// Difference rounding (ceil day) is DST-aware +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "ceil" +}); +assert.sameValue(`${ diff }`, "-P2D"); + +// Difference rounding (trunc day) is DST-aware +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "trunc" +}); +assert.sameValue(`${ diff }`, "-P2D"); + +// Difference rounding (floor day) is DST-aware +var diff = start.until(end, { + smallestUnit: "days", + roundingMode: "floor" +}); +assert.sameValue(`${ diff }`, "-P3D"); + +// Difference rounding (nearest hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "-P2DT12H"); + +// Difference rounding (ceil hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "ceil" +}); +assert.sameValue(`${ diff }`, "-P2DT12H"); + +// Difference rounding (trunc hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "trunc" +}); +assert.sameValue(`${ diff }`, "-P2DT12H"); + +// Difference rounding (floor hour) is DST-aware +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "floor" +}); +assert.sameValue(`${ diff }`, "-P2DT13H"); + +// Difference when date portion ends inside a DST-skipped period +var start = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(tz); +var end = Temporal.PlainDateTime.from("2000-04-02T03:15").toZonedDateTime(tz); +var diff = start.until(end, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "PT23H45M"); + +// Difference when date portion ends inside day skipped by Samoa's 24hr 2011 transition +var end = Temporal.PlainDateTime.from("2011-12-31T05:00").toZonedDateTime(timeZone); +var start = Temporal.PlainDateTime.from("2011-12-28T10:00").toZonedDateTime(timeZone); +var diff = start.until(end, { largestUnit: "days" }); +assert.sameValue(`${ diff }`, "P1DT19H"); + +// Rounding up to hours causes one more day of overflow (positive) +var start = Temporal.ZonedDateTime.from("2020-01-01T00:00-08:00[-08:00]"); +var end = Temporal.ZonedDateTime.from("2020-01-03T23:59-08:00[-08:00]"); +var diff = start.until(end, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "P3D"); + +// Rounding up to hours causes one more day of overflow (negative) +var start = Temporal.ZonedDateTime.from("2020-01-01T00:00-08:00[-08:00]"); +var end = Temporal.ZonedDateTime.from("2020-01-03T23:59-08:00[-08:00]"); +var diff = end.until(start, { + largestUnit: "days", + smallestUnit: "hours", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ diff }`, "-P3D"); + +// addition and difference work near DST start +var stepsPerHour = 2; +var minutesPerStep = 60 / stepsPerHour; +var hoursUntilEnd = 26; +var startHourRange = 3; +for (var i = 0; i < startHourRange * stepsPerHour; i++) { + var start = hourBeforeDstStart.add({ minutes: minutesPerStep * i }); + for (var j = 0; j < hoursUntilEnd * stepsPerHour; j++) { + var end = start.add({ minutes: j * minutesPerStep }); + var diff = start.until(end, { largestUnit: "days" }); + var expectedMinutes = minutesPerStep * (j % stepsPerHour); + assert.sameValue(diff.minutes, expectedMinutes); + var diff60 = Math.floor(j / stepsPerHour); + if (i >= stepsPerHour) { + var expectedDays = diff60 < 24 ? 0 : diff60 < 48 ? 1 : 2; + var expectedHours = diff60 < 24 ? diff60 : diff60 < 48 ? diff60 - 24 : diff60 - 48; + assert.sameValue(diff.hours, expectedHours); + assert.sameValue(diff.days, expectedDays); + } else { + var expectedDays = diff60 < 23 ? 0 : diff60 < 47 ? 1 : 2; + var expectedHours = diff60 < 23 ? diff60 : diff60 < 47 ? diff60 - 23 : diff60 - 47; + assert.sameValue(diff.hours, expectedHours); + assert.sameValue(diff.days, expectedDays); + } + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js new file mode 100644 index 0000000000..fbd5d37197 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: properties around DST +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var tz = TemporalHelpers.springForwardFallBackTimeZone(); +var hourBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 2, 1).toZonedDateTime(tz); +var dayBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 1, 2, 30).toZonedDateTime(tz); + +// hoursInDay works with DST start +assert.sameValue(hourBeforeDstStart.hoursInDay, 23); + +// hoursInDay works with non-DST days +assert.sameValue(dayBeforeDstStart.hoursInDay, 24); + +// hoursInDay works with DST end +var dstEnd = Temporal.PlainDateTime.from("2000-10-29T01:00").toZonedDateTime(tz); +assert.sameValue(dstEnd.hoursInDay, 25); + +// startOfDay works +var start = dayBeforeDstStart.startOfDay(); +assert.sameValue(`${ start.toPlainDate() }`, `${ dayBeforeDstStart.toPlainDate() }`); +assert.sameValue(`${ start.toPlainTime() }`, "00:00:00"); + +var samoa = TemporalHelpers.crossDateLineTimeZone(); +var dayAfterSamoaDateLineChange = Temporal.PlainDateTime.from("2011-12-31T22:00").toZonedDateTime(samoa); +var dayBeforeSamoaDateLineChange = Temporal.PlainDateTime.from("2011-12-29T22:00").toZonedDateTime(samoa); + +// startOfDay works after Samoa date line change +var start = dayAfterSamoaDateLineChange.startOfDay(); +assert.sameValue(`${ start.toPlainTime() }`, "00:00:00"); + +// hoursInDay works after Samoa date line change +assert.sameValue(dayAfterSamoaDateLineChange.hoursInDay, 24); + +// hoursInDay works before Samoa date line change +assert.sameValue(dayBeforeSamoaDateLineChange.hoursInDay, 24); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js new file mode 100644 index 0000000000..9f3eb5515e --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js @@ -0,0 +1,111 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.equals() +features: [Temporal] +---*/ + +var tz = { + getOffsetNanosecondsFor() { return -5 * 3600_000_000_000; }, + getPossibleInstantsFor(pdt) { return Temporal.TimeZone.from("-05:00").getPossibleInstantsFor(pdt); }, + id: "America/New_York", +}; +var cal = { + dateFromFields(...args) { return Temporal.Calendar.from("iso8601").dateFromFields(...args); }, + id: "gregory", + dateAdd() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields(fieldNames) { return fieldNames; }, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = new Temporal.ZonedDateTime(0n, tz, cal); + +// constructed from equivalent parameters are equal +var zdt2 = Temporal.ZonedDateTime.from({ + year: 1969, + month: 12, + day: 31, + hour: 19, + timeZone: tz, + calendar: cal, +}); +assert(zdt.equals(zdt2)); +assert(zdt2.equals(zdt)); + +// different instant not equal +var zdt2 = new Temporal.ZonedDateTime(1n, tz, cal); +assert(!zdt.equals(zdt2)); + +// different time zone not equal +var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", cal); +assert(!zdt.equals(zdt2)); + +// different calendar not equal +var zdt2 = new Temporal.ZonedDateTime(0n, tz, "iso8601"); +assert(!zdt.equals(zdt2)); + +// casts its argument +var instance = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); +assert(instance.equals("1970-01-01T00:00+00:00[UTC][u-ca=iso8601]")); +assert(instance.equals({ + year: 1970, + month: 1, + day: 1, + timeZone: "UTC", + calendar: "iso8601", +})); + +// at least the required properties must be present +assert(!zdt.equals({ + year: 1969, + month: 12, + day: 31, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + month: 12, + day: 31, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + year: 1969, + day: 31, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + year: 1969, + month: 12, + timeZone: tz +})); +assert.throws(TypeError, () => zdt.equals({ + year: 1969, + month: 12, + day: 31 +})); +assert.throws(TypeError, () => zdt.equals({ + years: 1969, + months: 12, + days: 31, + timeZone: tz, + calendarName: "gregory" +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js new file mode 100644 index 0000000000..406ba04865 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js @@ -0,0 +1,82 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: math order of operations and options +features: [Temporal] +---*/ + +var breakoutUnits = (op, zdt, d, options) => zdt[op]({ years: d.years }, options)[op]({ months: d.months }, options)[op]({ weeks: d.weeks }, options)[op]({ days: d.days }, options)[op]({ + hours: d.hours, + minutes: d.minutes, + seconds: d.seconds, + milliseconds: d.milliseconds, + microseconds: d.microseconds, + nanoseconds: d.nanoseconds +}, options); + +// order of operations: add / none +var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = undefined; +var result = zdt.add(d, options); +assert.sameValue(result.toString(), "2020-03-01T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("add", zdt, d, options).toString(), result.toString()); + +// order of operations: add / constrain +var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "constrain" }; +var result = zdt.add(d, options); +assert.sameValue(result.toString(), "2020-03-01T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("add", zdt, d, options).toString(), result.toString()); + +// order of operations: add / reject +var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "reject" }; +assert.throws(RangeError, () => zdt.add(d, options)); + +// order of operations: subtract / none +var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = undefined; +var result = zdt.subtract(d, options); +assert.sameValue(result.toString(), "2020-02-28T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("subtract", zdt, d, options).toString(), result.toString()); + +// order of operations: subtract / constrain +var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "constrain" }; +var result = zdt.subtract(d, options); +assert.sameValue(result.toString(), "2020-02-28T00:00:00-08:00[-08:00]"); +assert.sameValue(breakoutUnits("subtract", zdt, d, options).toString(), result.toString()); + +// order of operations: subtract / reject +var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]"); +var d = Temporal.Duration.from({ + months: 1, + days: 1 +}); +var options = { overflow: "reject" }; +assert.throws(RangeError, () => zdt.subtract(d, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js new file mode 100644 index 0000000000..e308c33e23 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js @@ -0,0 +1,296 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: property bags +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var lagos = Temporal.TimeZone.from("+01:00"); + +// can be constructed with monthCode and without month +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + monthCode: "M11", + day: 18, + timeZone: lagos +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// can be constructed with month and without monthCode +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + timeZone: lagos +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// month and monthCode must agree +assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + monthCode: "M12", + day: 18, + timeZone: lagos +})); + +// Temporal.ZonedDateTime.from({}) throws +assert.throws(TypeError, () => Temporal.ZonedDateTime.from({})) + +// Temporal.ZonedDateTime.from(required prop undefined) throws +assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ + year: 1976, + month: undefined, + monthCode: undefined, + day: 18, + timeZone: lagos +})) + +// options may be a function object +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + timeZone: lagos +}, () => { +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// object must contain at least the required correctly-spelled properties +assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ + years: 1976, + months: 11, + days: 18, + timeZone: lagos +})); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + timeZone: lagos, + hours: 12 +}) }`, "1976-11-18T00:00:00+01:00[+01:00]"); + +// does not accept non-string offset property +[ + null, + true, + 1000, + 1000n, + Symbol(), + {} +].forEach(offset => { + assert.throws( + typeof offset === "string" || (typeof offset === "object" && offset !== null) || typeof offset === "function" + ? RangeError + : TypeError, + () => Temporal.ZonedDateTime.from({ + year: 1976, + month: 11, + day: 18, + offset: offset, + timeZone: Temporal.TimeZone.from("+10:00") + }) + ) +}); + + +// overflow options +var bad = { + year: 2019, + month: 1, + day: 32, + timeZone: lagos +}; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(bad, { overflow: "reject" })); +assert.sameValue(`${ Temporal.ZonedDateTime.from(bad) }`, "2019-01-31T00:00:00+01:00[+01:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(bad, { overflow: "constrain" }) }`, "2019-01-31T00:00:00+01:00[+01:00]"); + +// Offset options + +// { offset: 'reject' } throws if offset does not match offset time zone +var obj = { + year: 2020, + month: 3, + day: 8, + hour: 1, + offset: "-04:00", + timeZone: "-08:00" +}; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj)); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { offset: "reject" })); + +// { offset: 'reject' } throws if offset does not match IANA time zone +var obj = { + year: 2020, + month: 3, + day: 8, + hour: 1, + offset: "-04:00", + timeZone: "UTC" +}; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj)); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { offset: "reject" })); + +var cali = TemporalHelpers.springForwardFallBackTimeZone(); +var date = { +year: 2000, +month: 10, +day: 29, +timeZone: cali +}; +// { offset: 'prefer' } if offset matches time zone (first 1:30 when DST ends) +var obj = { + ...date, + hour: 1, + minute: 30, + offset: "-07:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T01:30:00-07:00[Custom/Spring_Fall]"); + +// { offset: 'prefer' } if offset matches time zone (second 1:30 when DST ends) +var obj = { + ...date, + hour: 1, + minute: 30, + offset: "-08:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T01:30:00-08:00[Custom/Spring_Fall]"); + +// { offset: 'prefer' } if offset does not match time zone" +var obj = { + ...date, + hour: 4, + offset: "-07:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T04:00:00-08:00[Custom/Spring_Fall]"); + +// { offset: 'ignore' } uses time zone only +var obj = { + ...date, + hour: 4, + offset: "-12:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "ignore" }) }`, "2000-10-29T04:00:00-08:00[Custom/Spring_Fall]"); + +// { offset: 'use' } uses offset only +var obj = { + ...date, + hour: 4, + offset: "-07:00" +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "use" }) }`, "2000-10-29T03:00:00-08:00[Custom/Spring_Fall]"); + +// Disambiguation options + +// plain datetime with multiple instants - Fall DST +var obj = { + year: 2000, + month: 10, + day: 29, + hour: 1, + minute: 45, + timeZone: cali +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "compatible" }) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "earlier" }) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "later" }) }`, "2000-10-29T01:45:00-08:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" })); + +// plain datetime with multiple instants - Spring DST +var obj = { + year: 2000, + month: 4, + day: 2, + hour: 2, + minute: 30, + timeZone: cali +}; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "compatible" }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "earlier" }) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "later" }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" })); + +// uses disambiguation if offset is ignored +var obj = { + year: 2000, + month: 4, + day: 2, + hour: 2, + minute: 30, + timeZone: cali +}; +var offset = "ignore"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "compatible" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" })); + +// uses disambiguation if offset is wrong and option is prefer +var obj = { + year: 2000, + month: 4, + day: 2, + hour: 2, + minute: 30, + offset: "-23:59", + timeZone: cali +}; +var offset = "prefer"; +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "compatible" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { + offset, + disambiguation: "reject" +})); + +// throw when bad disambiguation +[ + "", + "EARLIER", + "balance", + 3, + null +].forEach(disambiguation => { + assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-11-01T04:00[UTC]", { disambiguation })); +}); + +// sub-minute time zone offsets + +// does not truncate offset property to minutes +var zdt = Temporal.ZonedDateTime.from({ + year: 1971, + month: 1, + day: 1, + hour: 12, + timeZone: TemporalHelpers.specificOffsetTimeZone(-2.67e12) // -00:44:30 in nanoseconds +}); +assert.sameValue(zdt.offset, "-00:44:30"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js new file mode 100644 index 0000000000..909d7b6b61 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Reversibility of differences +features: [Temporal] +---*/ + +var earlier = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-03:00[-03:00]"); +var later = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]"); +[ + "hours", + "minutes", + "seconds" +].forEach(largestUnit => { + var diff = later.since(earlier, { largestUnit }); +assert.sameValue(`${ earlier.since(later, { largestUnit }) }`, `${ diff.negated() }`); +assert.sameValue(`${ earlier.until(later, { largestUnit }) }`, `${ diff }`); +// difference symmetrical with regard to negative durations + assert(earlier.subtract(diff.negated()).equals(later)); + assert(later.add(diff.negated()).equals(earlier)); + }); +[ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds" +].forEach(largestUnit => { + var diff1 = earlier.until(later, { largestUnit }); + var diff2 = later.since(earlier, { largestUnit }); + assert(earlier.add(diff1).equals(later)); + assert(later.subtract(diff2).equals(earlier)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js new file mode 100644 index 0000000000..3dfb21989d --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js @@ -0,0 +1,242 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.round() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); + +// throws without parameter +assert.throws(TypeError, () => zdt.round()); + +// throws without required smallestUnit parameter +assert.throws(RangeError, () => zdt.round({})); +assert.throws(RangeError, () => zdt.round({ + roundingIncrement: 1, + roundingMode: "ceil" +})); + +// throws on disallowed or invalid smallestUnit (string param) +[ + "era", + "year", + "month", + "week", + "years", + "months", + "weeks", + "nonsense" +].forEach(smallestUnit => { + assert.throws(RangeError, () => zdt.round(smallestUnit)); +}); + +// rounds to an increment of hours +assert.sameValue(`${ zdt.round({ + smallestUnit: "hour", + roundingIncrement: 4 +}) }`, "1976-11-18T16:00:00+01:00[+01:00]"); + +// rounds to an increment of minutes +assert.sameValue(`${ zdt.round({ + smallestUnit: "minute", + roundingIncrement: 15 +}) }`, "1976-11-18T15:30:00+01:00[+01:00]"); + +// rounds to an increment of seconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "second", + roundingIncrement: 30 +}) }`, "1976-11-18T15:23:30+01:00[+01:00]"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "millisecond", + roundingIncrement: 10 +}) }`, "1976-11-18T15:23:30.12+01:00[+01:00]"); + +// rounds to an increment of microseconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "microsecond", + roundingIncrement: 10 +}) }`, "1976-11-18T15:23:30.12346+01:00[+01:00]"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ zdt.round({ + smallestUnit: "nanosecond", + roundingIncrement: 10 +}) }`, "1976-11-18T15:23:30.12345679+01:00[+01:00]"); + +// 1 day is a valid increment +assert.sameValue(`${ zdt.round({ + smallestUnit: "day", + roundingIncrement: 1 +}) }`, "1976-11-19T00:00:00+01:00[+01:00]"); + +// valid hour increments divide into 24 +var smallestUnit = "hour"; +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + assert(zdt.round({ + smallestUnit, + roundingIncrement + }) instanceof Temporal.ZonedDateTime); +}); +[ + "minute", + "second" +].forEach(smallestUnit => { + // valid minutes/seconds increments divide into 60`, () => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + assert(zdt.round({ + smallestUnit, + roundingIncrement + }) instanceof Temporal.ZonedDateTime); + }); + }); +[ + "millisecond", + "microsecond", + "nanosecond" +].forEach(smallestUnit => { + // valid increments divide into 1000` + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + assert(zdt.round({ + smallestUnit, + roundingIncrement + }) instanceof Temporal.ZonedDateTime); + }); + }); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "day", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "hour", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "minute", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "second", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "millisecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "microsecond", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "nanosecond", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "hour", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "minute", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "second", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "millisecond", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "microsecond", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => zdt.round({ + smallestUnit: "nanosecond", + roundingIncrement: 1000 +})); +var bal = Temporal.ZonedDateTime.from("1976-11-18T23:59:59.999999999+01:00[+01:00]"); +[ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond" +].forEach(smallestUnit => { + assert.sameValue(`${ bal.round({ smallestUnit }) }`, "1976-11-19T00:00:00+01:00[+01:00]"); +}); + +var timeZone = TemporalHelpers.springForwardFallBackTimeZone(); + +// rounds correctly to a 25-hour day +var roundTo = { smallestUnit: "day" }; +var roundMeDown = Temporal.PlainDateTime.from("2000-10-29T12:29:59").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeDown.round(roundTo) }`, "2000-10-29T00:00:00-07:00[Custom/Spring_Fall]"); +var roundMeUp = Temporal.PlainDateTime.from("2000-10-29T12:30:01").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeUp.round(roundTo) }`, "2000-10-30T00:00:00-08:00[Custom/Spring_Fall]"); + +// rounds correctly to a 23-hour day +var roundTo = { smallestUnit: "day" }; +var roundMeDown = Temporal.PlainDateTime.from("2000-04-02T11:29:59").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeDown.round(roundTo) }`, "2000-04-02T00:00:00-08:00[Custom/Spring_Fall]"); +var roundMeUp = Temporal.PlainDateTime.from("2000-04-02T11:30:01").toZonedDateTime(timeZone); +assert.sameValue(`${ roundMeUp.round(roundTo) }`, "2000-04-03T00:00:00-07:00[Custom/Spring_Fall]"); + +// rounding up to a nonexistent wall-clock time +var almostSkipped = Temporal.PlainDateTime.from("2000-04-02T01:59:59.999999999").toZonedDateTime(timeZone); +var rounded = almostSkipped.round({ + smallestUnit: "microsecond", + roundingMode: "halfExpand" +}); +assert.sameValue(`${ rounded }`, "2000-04-02T03:00:00-07:00[Custom/Spring_Fall]"); +assert.sameValue(rounded.epochNanoseconds - almostSkipped.epochNanoseconds, 1n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js new file mode 100644 index 0000000000..60f74c2518 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js @@ -0,0 +1,2158 @@ +// GENERATED, DO NOT EDIT +// file: temporalHelpers.js +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + This defines helper objects and functions for testing Temporal. +defines: [TemporalHelpers] +features: [Symbol.species, Symbol.iterator, Temporal] +---*/ + +const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u; + +function formatPropertyName(propertyKey, objectName = "") { + switch (typeof propertyKey) { + case "symbol": + if (Symbol.keyFor(propertyKey) !== undefined) { + return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`; + } else if (propertyKey.description.startsWith('Symbol.')) { + return `${objectName}[${propertyKey.description}]`; + } else { + return `${objectName}[Symbol('${propertyKey.description}')]` + } + case "string": + if (propertyKey !== String(Number(propertyKey))) { + if (ASCII_IDENTIFIER.test(propertyKey)) { + return objectName ? `${objectName}.${propertyKey}` : propertyKey; + } + return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']` + } + // fall through + default: + // integer or string integer-index + return `${objectName}[${propertyKey}]`; + } +} + +const SKIP_SYMBOL = Symbol("Skip"); + +var TemporalHelpers = { + /* + * Codes and maximum lengths of months in the ISO 8601 calendar. + */ + ISOMonths: [ + { month: 1, monthCode: "M01", daysInMonth: 31 }, + { month: 2, monthCode: "M02", daysInMonth: 29 }, + { month: 3, monthCode: "M03", daysInMonth: 31 }, + { month: 4, monthCode: "M04", daysInMonth: 30 }, + { month: 5, monthCode: "M05", daysInMonth: 31 }, + { month: 6, monthCode: "M06", daysInMonth: 30 }, + { month: 7, monthCode: "M07", daysInMonth: 31 }, + { month: 8, monthCode: "M08", daysInMonth: 31 }, + { month: 9, monthCode: "M09", daysInMonth: 30 }, + { month: 10, monthCode: "M10", daysInMonth: 31 }, + { month: 11, monthCode: "M11", daysInMonth: 30 }, + { month: 12, monthCode: "M12", daysInMonth: 31 } + ], + + /* + * assertDuration(duration, years, ..., nanoseconds[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * an expected value. + */ + assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, hours, `${prefix}hours result:`); + assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`); + assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`); + assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`); + assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`); + assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`); + }, + + /* + * assertDateDuration(duration, years, months, weeks, days, [, description]): + * + * Shorthand for asserting that each date field of a Temporal.Duration is + * equal to an expected value. + */ + assertDateDuration(duration, years, months, weeks, days, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(duration instanceof Temporal.Duration, `${prefix}instanceof`); + assert.sameValue(duration.years, years, `${prefix}years result:`); + assert.sameValue(duration.months, months, `${prefix}months result:`); + assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`); + assert.sameValue(duration.days, days, `${prefix}days result:`); + assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`); + assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`); + assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`); + assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`); + assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`); + assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`); + }, + + /* + * assertDurationsEqual(actual, expected[, description]): + * + * Shorthand for asserting that each field of a Temporal.Duration is equal to + * the corresponding field in another Temporal.Duration. + */ + assertDurationsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`); + TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description); + }, + + /* + * assertInstantsEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.Instants are of the correct type + * and equal according to their equals() methods. + */ + assertInstantsEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`); + assert(actual instanceof Temporal.Instant, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDate is equal to + * an expected value. (Except the `calendar` property, since callers may want + * to assert either object equality with an object they put in there, or the + * value of date.calendarId.) + */ + assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`); + assert.sameValue(date.era, era, `${prefix}era result:`); + assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(date.year, year, `${prefix}year result:`); + assert.sameValue(date.month, month, `${prefix}month result:`); + assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(date.day, day, `${prefix}day result:`); + }, + + /* + * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainDateTime is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of datetime.calendarId.) + */ + assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) { + const prefix = description ? `${description}: ` : ""; + assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert.sameValue(datetime.era, era, `${prefix}era result:`); + assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(datetime.year, year, `${prefix}year result:`); + assert.sameValue(datetime.month, month, `${prefix}month result:`); + assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(datetime.day, day, `${prefix}day result:`); + assert.sameValue(datetime.hour, hour, `${prefix}hour result:`); + assert.sameValue(datetime.minute, minute, `${prefix}minute result:`); + assert.sameValue(datetime.second, second, `${prefix}second result:`); + assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their calendar internal slots are the same value. + */ + assertPlainDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`); + assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]): + * + * Shorthand for asserting that each field of a Temporal.PlainMonthDay is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of monthDay.calendarId().) + */ + assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) { + const prefix = description ? `${description}: ` : ""; + assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`); + assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(monthDay.day, day, `${prefix}day result:`); + assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`); + }, + + /* + * assertPlainTime(time, hour, ..., nanosecond[, description]): + * + * Shorthand for asserting that each field of a Temporal.PlainTime is equal to + * an expected value. + */ + assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert.sameValue(time.hour, hour, `${prefix}hour result:`); + assert.sameValue(time.minute, minute, `${prefix}minute result:`); + assert.sameValue(time.second, second, `${prefix}second result:`); + assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`); + assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`); + assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`); + }, + + /* + * assertPlainTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.PlainTimes are of the correct + * type and equal according to their equals() methods. + */ + assertPlainTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`); + assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + }, + + /* + * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]): + * + * Shorthand for asserting that each field of a Temporal.PlainYearMonth is + * equal to an expected value. (Except the `calendar` property, since callers + * may want to assert either object equality with an object they put in there, + * or the value of yearMonth.calendarId.) + */ + assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) { + const prefix = description ? `${description}: ` : ""; + assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`); + assert.sameValue(yearMonth.era, era, `${prefix}era result:`); + assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`); + assert.sameValue(yearMonth.year, year, `${prefix}year result:`); + assert.sameValue(yearMonth.month, month, `${prefix}month result:`); + assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`); + assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`); + }, + + /* + * assertZonedDateTimesEqual(actual, expected[, description]): + * + * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct + * type, equal according to their equals() methods, and additionally that + * their time zones and calendar internal slots are the same value. + */ + assertZonedDateTimesEqual(actual, expected, description = "") { + const prefix = description ? `${description}: ` : ""; + assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`); + assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`); + assert(actual.equals(expected), `${prefix}equals method`); + assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`); + assert.sameValue( + actual.getISOFields().calendar, + expected.getISOFields().calendar, + `${prefix}calendar same value:` + ); + }, + + /* + * assertUnreachable(description): + * + * Helper for asserting that code is not executed. This is useful for + * assertions that methods of user calendars and time zones are not called. + */ + assertUnreachable(description) { + let message = "This code should not be executed"; + if (description) { + message = `${message}: ${description}`; + } + throw new Test262Error(message); + }, + + /* + * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls): + * + * When an options object with a largestUnit property is synthesized inside + * Temporal and passed to user code such as calendar.dateUntil(), the value of + * the largestUnit property should be in the singular form, even if the input + * was given in the plural form. + * (This doesn't apply when the options object is passed through verbatim.) + * + * func(calendar, largestUnit, index) is the operation under test. It's called + * with an instance of a calendar that keeps track of which largestUnit is + * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and + * the key's numerical index in case the function needs to generate test data + * based on the index. At the end, the actual values passed to dateUntil() are + * compared with the array values of expectedLargestUnitCalls. + */ + checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) { + const actual = []; + + class DateUntilOptionsCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(earlier, later, options) { + actual.push(options.largestUnit); + return super.dateUntil(earlier, later, options); + } + + toString() { + return "date-until-options"; + } + } + + const calendar = new DateUntilOptionsCalendar(); + Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => { + func(calendar, largestUnit, index); + assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`); + actual.splice(0); // empty it for the next check + }); + }, + + /* + * checkPlainDateTimeConversionFastPath(func): + * + * ToTemporalDate and ToTemporalTime should both, if given a + * Temporal.PlainDateTime instance, convert to the desired type by reading the + * PlainDateTime's internal slots, rather than calling any getters. + * + * func(datetime, calendar) is the actual operation to test, that must + * internally call the abstract operation ToTemporalDate or ToTemporalTime. + * It is passed a Temporal.PlainDateTime instance, as well as the instance's + * calendar object (so that it doesn't have to call the calendar getter itself + * if it wants to make any assertions about the calendar.) + */ + checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype); + ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(datetime, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${formatPropertyName(property)}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${formatPropertyName(property)}`); + return value; + }, + }; + }, + }); + }); + Object.defineProperty(datetime, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(datetime, calendar); + assert.compareArray(actual, expected, `${message}: property getters not called`); + }, + + /* + * Check that an options bag that accepts units written in the singular form, + * also accepts the same units written in the plural form. + * func(unit) should call the method with the appropriate options bag + * containing unit as a value. This will be called twice for each element of + * validSingularUnits, once with singular and once with plural, and the + * results of each pair should be the same (whether a Temporal object or a + * primitive value.) + */ + checkPluralUnitsAccepted(func, validSingularUnits) { + const plurals = { + year: 'years', + month: 'months', + week: 'weeks', + day: 'days', + hour: 'hours', + minute: 'minutes', + second: 'seconds', + millisecond: 'milliseconds', + microsecond: 'microseconds', + nanosecond: 'nanoseconds', + }; + + validSingularUnits.forEach((unit) => { + const singularValue = func(unit); + const pluralValue = func(plurals[unit]); + const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`; + if (singularValue instanceof Temporal.Duration) { + TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.Instant) { + TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainDateTime) { + TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.PlainTime) { + TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc); + } else if (singularValue instanceof Temporal.ZonedDateTime) { + TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc); + } else { + assert.sameValue(pluralValue, singularValue); + } + }); + }, + + /* + * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc): + * + * Checks the type handling of the roundingIncrement option. + * checkFunc(roundingIncrement) is a function which takes the value of + * roundingIncrement to test, and calls the method under test with it, + * returning the result. assertTrueResultFunc(result, description) should + * assert that result is the expected result with roundingIncrement: true, and + * assertObjectResultFunc(result, description) should assert that result is + * the expected result with roundingIncrement being an object with a valueOf() + * method. + */ + checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) { + // null converts to 0, which is out of range + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to either 0 or 1, and 1 is allowed + const trueResult = checkFunc(true); + assertTrueResultFunc(trueResult, "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols and BigInts cannot convert to numbers + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + assert.throws(TypeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their valueOf() methods when converting to a number + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + "get roundingIncrement.valueOf", + "call roundingIncrement.valueOf", + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement"); + const objectResult = checkFunc(observer); + assertObjectResultFunc(objectResult, "object with valueOf"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc): + * + * Checks the type handling of a string option, of which there are several in + * Temporal. + * propertyName is the name of the option, and value is the value that + * assertFunc should expect it to have. + * checkFunc(value) is a function which takes the value of the option to test, + * and calls the method under test with it, returning the result. + * assertFunc(result, description) should assert that result is the expected + * result with the option value being an object with a toString() method + * which returns the given value. + */ + checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) { + // null converts to the string "null", which is an invalid string value + assert.throws(RangeError, () => checkFunc(null), "null"); + // Booleans convert to the strings "true" or "false", which are invalid + assert.throws(RangeError, () => checkFunc(true), "true"); + assert.throws(RangeError, () => checkFunc(false), "false"); + // Symbols cannot convert to strings + assert.throws(TypeError, () => checkFunc(Symbol()), "symbol"); + // Numbers convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2), "number"); + // BigInts convert to strings which are invalid + assert.throws(RangeError, () => checkFunc(2n), "bigint"); + + // Objects prefer their toString() methods when converting to a string + assert.throws(RangeError, () => checkFunc({}), "plain object"); + + const expected = [ + `get ${propertyName}.toString`, + `call ${propertyName}.toString`, + ]; + const actual = []; + const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName); + const result = checkFunc(observer); + assertFunc(result, "object with toString"); + assert.compareArray(actual, expected, "order of operations"); + }, + + /* + * checkSubclassingIgnored(construct, constructArgs, method, methodArgs, + * resultAssertions): + * + * Methods of Temporal classes that return a new instance of the same class, + * must not take the constructor of a subclass into account, nor the @@species + * property. This helper runs tests to ensure this. + * + * construct(...constructArgs) must yield a valid instance of the Temporal + * class. instance[method](...methodArgs) is the method call under test, which + * must also yield a valid instance of the same Temporal class, not a + * subclass. See below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnored(...args) { + this.checkSubclassConstructorNotObject(...args); + this.checkSubclassConstructorUndefined(...args); + this.checkSubclassConstructorThrows(...args); + this.checkSubclassConstructorNotCalled(...args); + this.checkSubclassSpeciesInvalidResult(...args); + this.checkSubclassSpeciesNotAConstructor(...args); + this.checkSubclassSpeciesNull(...args); + this.checkSubclassSpeciesUndefined(...args); + this.checkSubclassSpeciesThrows(...args); + }, + + /* + * Checks that replacing the 'constructor' property of the instance with + * various primitive values does not affect the returned new instance. + */ + checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = value; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + }, + + /* + * Checks that replacing the 'constructor' property of the subclass with + * undefined does not affect the returned new instance. + */ + checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = undefined; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that making the 'constructor' property of the instance throw when + * called does not affect the returned new instance. + */ + checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + const instance = new construct(...constructArgs); + Object.defineProperty(instance, "constructor", { + get() { + throw new CustomError(); + } + }); + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Checks that when subclassing, the subclass constructor is not called by + * the method under test. + */ + checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's a + * constructor that returns a non-object value. + */ + checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: function() { + return value; + }, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's not a + * constructor. + */ + checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) { + function check(value, description) { + const instance = new construct(...constructArgs); + instance.constructor = { + [Symbol.species]: value, + }; + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description); + resultAssertions(result); + } + + check(true, "true"); + check("test", "string"); + check(Symbol(), "Symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "plain object"); + }, + + /* + * Check that the constructor's @@species property is ignored when it's null. + */ + checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: null, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it's + * undefined. + */ + checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) { + let called = 0; + + class MySubclass extends construct { + constructor() { + ++called; + super(...constructArgs); + } + } + + const instance = new MySubclass(); + assert.sameValue(called, 1); + + MySubclass.prototype.constructor = { + [Symbol.species]: undefined, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(called, 1); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that the constructor's @@species property is ignored when it throws, + * i.e. it is not called at all. + */ + checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) { + function CustomError() {} + + const instance = new construct(...constructArgs); + instance.constructor = { + get [Symbol.species]() { + throw new CustomError(); + }, + }; + + const result = instance[method](...methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + }, + + /* + * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions): + * + * Static methods of Temporal classes that return a new instance of the class, + * must not use the this-value as a constructor. This helper runs tests to + * ensure this. + * + * construct[method](...methodArgs) is the static method call under test, and + * must yield a valid instance of the Temporal class, not a subclass. See + * below for the individual tests that this runs. + * resultAssertions() is a function that performs additional assertions on the + * instance returned by the method under test. + */ + checkSubclassingIgnoredStatic(...args) { + this.checkStaticInvalidReceiver(...args); + this.checkStaticReceiverNotCalled(...args); + this.checkThisValueNotCalled(...args); + }, + + /* + * Check that calling the static method with a receiver that's not callable, + * still calls the intrinsic constructor. + */ + checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const result = construct[method].apply(value, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that calling the static method with a receiver that returns a value + * that's not callable, still calls the intrinsic constructor. + */ + checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) { + function check(value, description) { + const receiver = function () { + return value; + }; + const result = construct[method].apply(receiver, methodArgs); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + } + + check(undefined, "undefined"); + check(null, "null"); + check(true, "true"); + check("test", "string"); + check(Symbol(), "symbol"); + check(7, "number"); + check(7n, "bigint"); + check({}, "Non-callable object"); + }, + + /* + * Check that the receiver isn't called. + */ + checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) { + let called = false; + + class MySubclass extends construct { + constructor(...args) { + called = true; + super(...args); + } + } + + const result = MySubclass[method](...methodArgs); + assert.sameValue(called, false); + assert.sameValue(Object.getPrototypeOf(result), construct.prototype); + resultAssertions(result); + }, + + /* + * Check that any iterable returned from a custom time zone's + * getPossibleInstantsFor() method is exhausted. + * The custom time zone object is passed in to func(). + * expected is an array of strings representing the expected calls to the + * getPossibleInstantsFor() method. The PlainDateTimes that it is called with, + * are compared (using their toString() results) with the array. + */ + checkTimeZonePossibleInstantsIterable(func, expected) { + // A custom time zone that returns an iterable instead of an array from its + // getPossibleInstantsFor() method, and for testing purposes skips + // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on + // January 3, 2030. Otherwise identical to the UTC time zone. + class TimeZonePossibleInstantsIterable extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.getPossibleInstantsForCallCount = 0; + this.getPossibleInstantsForCalledWith = []; + this.getPossibleInstantsForReturns = []; + this.iteratorExhausted = []; + } + + toString() { + return "Custom/Iterable"; + } + + getOffsetNanosecondsFor(instant) { + if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 && + Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) { + return 3600_000_000_000; + } else { + return 0; + } + } + + getPossibleInstantsFor(dateTime) { + this.getPossibleInstantsForCallCount++; + this.getPossibleInstantsForCalledWith.push(dateTime); + + // Fake DST transition + let retval = super.getPossibleInstantsFor(dateTime); + if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) { + retval = []; + } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) { + retval.push(retval[0].subtract({ hours: 1 })); + } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) { + retval[0] = retval[0].subtract({ hours: 1 }); + } + + this.getPossibleInstantsForReturns.push(retval); + this.iteratorExhausted.push(false); + return { + callIndex: this.getPossibleInstantsForCallCount - 1, + timeZone: this, + *[Symbol.iterator]() { + yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex]; + this.timeZone.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + + const timeZone = new TimeZonePossibleInstantsIterable(); + func(timeZone); + + assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times"); + + for (let index = 0; index < expected.length; index++) { + assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime"); + assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable"); + } + }, + + /* + * Check that any calendar-carrying Temporal object has its [[Calendar]] + * internal slot read by ToTemporalCalendar, and does not fetch the calendar + * by calling getters. + * The custom calendar object is passed in to func() so that it can do its + * own additional assertions involving the calendar if necessary. (Sometimes + * there is nothing to assert as the calendar isn't stored anywhere that can + * be asserted about.) + */ + checkToTemporalCalendarFastPath(func) { + class CalendarFastPathCheck extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + monthDayFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + } + + yearMonthFromFields(...args) { + const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + } + + toString() { + return "fast-path-check"; + } + } + const calendar = new CalendarFastPathCheck(); + + const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar); + const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar); + const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar); + const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + + [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => { + const actual = []; + const expected = []; + + Object.defineProperty(temporalObject, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(temporalObject, calendar); + assert.compareArray(actual, expected, "calendar getter not called"); + }); + }, + + checkToTemporalInstantFastPath(func) { + const actual = []; + const expected = []; + + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + Object.defineProperty(datetime, 'toString', { + get() { + actual.push("get toString"); + return function (options) { + actual.push("call toString"); + return Temporal.ZonedDateTime.prototype.toString.call(this, options); + }; + }, + }); + + func(datetime); + assert.compareArray(actual, expected, "toString not called"); + }, + + checkToTemporalPlainDateTimeFastPath(func) { + const actual = []; + const expected = []; + + const calendar = new Temporal.Calendar("iso8601"); + const date = new Temporal.PlainDate(2000, 5, 2, calendar); + const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype); + ["year", "month", "monthCode", "day"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + const value = prototypeDescrs[property].get.call(this); + return TemporalHelpers.toPrimitiveObserver(actual, value, property); + }, + }); + }); + ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => { + Object.defineProperty(date, property, { + get() { + actual.push(`get ${formatPropertyName(property)}`); + return undefined; + }, + }); + }); + Object.defineProperty(date, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + func(date, calendar); + assert.compareArray(actual, expected, "property getters not called"); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * fromFields methods are always called with a null-prototype fields object. + */ + calendarCheckFieldsPrototypePollution() { + class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + } + + // toString must remain "iso8601", so that some methods don't throw due to + // incompatible calendars + + dateFromFields(fields, options = {}) { + this.dateFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options = {}) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options = {}) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object"); + return super.monthDayFromFields(fields, options); + } + } + + return new CalendarCheckFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that the + * mergeFields() method is always called with null-prototype fields objects. + */ + calendarCheckMergeFieldsPrototypePollution() { + class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsCallCount = 0; + } + + toString() { + return "merge-fields-null-proto"; + } + + mergeFields(fields, additionalFields) { + this.mergeFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)"); + assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)"); + return super.mergeFields(fields, additionalFields); + } + } + + return new CalendarCheckMergeFieldsPrototypePollution(); + }, + + /* + * A custom calendar used in prototype pollution checks. Verifies that methods + * are always called with a null-prototype options object. + */ + calendarCheckOptionsPrototypePollution() { + class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.yearMonthFromFieldsCallCount = 0; + this.dateUntilCallCount = 0; + } + + toString() { + return "options-null-proto"; + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options"); + return super.yearMonthFromFields(fields, options); + } + + dateUntil(one, two, options) { + this.dateUntilCallCount++; + assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options"); + return super.dateUntil(one, two, options); + } + } + + return new CalendarCheckOptionsPrototypePollution(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with the + * options parameter having the value undefined. + */ + calendarDateAddUndefinedOptions() { + class CalendarDateAddUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + } + + toString() { + return "dateadd-undef-options"; + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert.sameValue(options, undefined, "dateAdd shouldn't be called with options"); + return super.dateAdd(date, duration, options); + } + } + return new CalendarDateAddUndefinedOptions(); + }, + + /* + * A custom calendar that asserts its dateAdd() method is called with a + * PlainDate instance. Optionally, it also asserts that the PlainDate instance + * is the specific object `this.specificPlainDate`, if it is set by the + * calling code. + */ + calendarDateAddPlainDateInstance() { + class CalendarDateAddPlainDateInstance extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateAddCallCount = 0; + this.specificPlainDate = undefined; + } + + toString() { + return "dateadd-plain-date-instance"; + } + + dateFromFields(...args) { + return super.dateFromFields(...args).withCalendar(this); + } + + dateAdd(date, duration, options) { + this.dateAddCallCount++; + assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance"); + if (this.dateAddCallCount === 1 && this.specificPlainDate) { + assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`); + } + return super.dateAdd(date, duration, options).withCalendar(this); + } + } + return new CalendarDateAddPlainDateInstance(); + }, + + /* + * A custom calendar that returns an iterable instead of an array from its + * fields() method, otherwise identical to the ISO calendar. + */ + calendarFieldsIterable() { + class CalendarFieldsIterable extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.fieldsCallCount = 0; + this.fieldsCalledWith = []; + this.iteratorExhausted = []; + } + + toString() { + return "fields-iterable"; + } + + fields(fieldNames) { + this.fieldsCallCount++; + this.fieldsCalledWith.push(fieldNames.slice()); + this.iteratorExhausted.push(false); + return { + callIndex: this.fieldsCallCount - 1, + calendar: this, + *[Symbol.iterator]() { + yield* this.calendar.fieldsCalledWith[this.callIndex]; + this.calendar.iteratorExhausted[this.callIndex] = true; + }, + }; + } + } + return new CalendarFieldsIterable(); + }, + + /* + * A custom calendar that asserts its ...FromFields() methods are called with + * the options parameter having the value undefined. + */ + calendarFromFieldsUndefinedOptions() { + class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "from-fields-undef-options"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options"); + return super.monthDayFromFields(fields, options); + } + } + return new CalendarFromFieldsUndefinedOptions(); + }, + + /* + * A custom calendar that modifies the fields object passed in to + * dateFromFields, sabotaging its time properties. + */ + calendarMakeInfinityTime() { + class CalendarMakeInfinityTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + fields.hour = Infinity; + fields.minute = Infinity; + fields.second = Infinity; + fields.millisecond = Infinity; + fields.microsecond = Infinity; + fields.nanosecond = Infinity; + return retval; + } + } + return new CalendarMakeInfinityTime(); + }, + + /* + * A custom calendar that defines getters on the fields object passed into + * dateFromFields that throw, sabotaging its time properties. + */ + calendarMakeInvalidGettersTime() { + class CalendarMakeInvalidGettersTime extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateFromFields(fields, options) { + const retval = super.dateFromFields(fields, options); + const throwingDescriptor = { + get() { + throw new Test262Error("reading a sabotaged time field"); + }, + }; + Object.defineProperties(fields, { + hour: throwingDescriptor, + minute: throwingDescriptor, + second: throwingDescriptor, + millisecond: throwingDescriptor, + microsecond: throwingDescriptor, + nanosecond: throwingDescriptor, + }); + return retval; + } + } + return new CalendarMakeInvalidGettersTime(); + }, + + /* + * A custom calendar whose mergeFields() method returns a proxy object with + * all of its Get and HasProperty operations observable, as well as adding a + * "shouldNotBeCopied": true property. + */ + calendarMergeFieldsGetters() { + class CalendarMergeFieldsGetters extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.mergeFieldsReturnOperations = []; + } + + toString() { + return "merge-fields-getters"; + } + + dateFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied"); + return super.monthDayFromFields(fields, options); + } + + mergeFields(fields, additionalFields) { + const retval = super.mergeFields(fields, additionalFields); + retval._calendar = this; + retval.shouldNotBeCopied = true; + return new Proxy(retval, { + get(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`get ${key}`); + const result = target[key]; + if (result === undefined) { + return undefined; + } + return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key); + }, + has(target, key) { + target._calendar.mergeFieldsReturnOperations.push(`has ${key}`); + return key in target; + }, + }); + } + } + return new CalendarMergeFieldsGetters(); + }, + + /* + * A custom calendar whose mergeFields() method returns a primitive value, + * given by @primitive, and which records the number of calls made to its + * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods. + */ + calendarMergeFieldsReturnsPrimitive(primitive) { + class CalendarMergeFieldsPrimitive extends Temporal.Calendar { + constructor(mergeFieldsReturnValue) { + super("iso8601"); + this._mergeFieldsReturnValue = mergeFieldsReturnValue; + this.dateFromFieldsCallCount = 0; + this.monthDayFromFieldsCallCount = 0; + this.yearMonthFromFieldsCallCount = 0; + } + + toString() { + return "merge-fields-primitive"; + } + + dateFromFields(fields, options) { + this.dateFromFieldsCallCount++; + return super.dateFromFields(fields, options); + } + + yearMonthFromFields(fields, options) { + this.yearMonthFromFieldsCallCount++; + return super.yearMonthFromFields(fields, options); + } + + monthDayFromFields(fields, options) { + this.monthDayFromFieldsCallCount++; + return super.monthDayFromFields(fields, options); + } + + mergeFields() { + return this._mergeFieldsReturnValue; + } + } + return new CalendarMergeFieldsPrimitive(primitive); + }, + + /* + * A custom calendar whose fields() method returns the same value as the + * iso8601 calendar, with the addition of extraFields provided as parameter. + */ + calendarWithExtraFields(fields) { + class CalendarWithExtraFields extends Temporal.Calendar { + constructor(extraFields) { + super("iso8601"); + this._extraFields = extraFields; + } + + fields(fieldNames) { + return super.fields(fieldNames).concat(this._extraFields); + } + } + + return new CalendarWithExtraFields(fields); + }, + + /* + * crossDateLineTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single transition where the time zone moves from one side of the + * International Date Line to the other, for the purpose of testing time zone + * calculations without depending on system time zone data. + * + * The transition occurs at epoch second 1325239200 and goes from offset + * -10:00 to +14:00. In other words, the time zone skips the whole calendar + * day of 2011-12-30. This is the same as the real-life transition in the + * Pacific/Apia time zone. + */ + crossDateLineTimeZone() { + const { compare } = Temporal.PlainDate; + const skippedDay = new Temporal.PlainDate(2011, 12, 30); + const transitionEpoch = 1325239200_000_000_000n; + const beforeOffset = new Temporal.TimeZone("-10:00"); + const afterOffset = new Temporal.TimeZone("+14:00"); + + class CrossDateLineTimeZone extends Temporal.TimeZone { + constructor() { + super("+14:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < transitionEpoch) { + return beforeOffset.getOffsetNanosecondsFor(instant); + } + return afterOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + const comparison = compare(datetime.toPlainDate(), skippedDay); + if (comparison === 0) { + return []; + } + if (comparison < 0) { + return [beforeOffset.getInstantFor(datetime)]; + } + return [afterOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch); + return null; + } + + toString() { + return "Custom/Date_Line"; + } + } + return new CrossDateLineTimeZone(); + }, + + /* + * observeProperty(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls to its accessors to the array @calls. + */ + observeProperty(calls, object, propertyName, value, objectName = "") { + Object.defineProperty(object, propertyName, { + get() { + calls.push(`get ${formatPropertyName(propertyName, objectName)}`); + return value; + }, + set(v) { + calls.push(`set ${formatPropertyName(propertyName, objectName)}`); + } + }); + }, + + /* + * observeMethod(calls, object, propertyName, value): + * + * Defines an own property @object.@propertyName with value @value, that + * will log any calls of @value to the array @calls. + */ + observeMethod(calls, object, propertyName, objectName = "") { + const method = object[propertyName]; + object[propertyName] = function () { + calls.push(`call ${formatPropertyName(propertyName, objectName)}`); + return method.apply(object, arguments); + }; + }, + + /* + * Used for substituteMethod to indicate default behavior instead of a + * substituted value + */ + SUBSTITUTE_SKIP: SKIP_SYMBOL, + + /* + * substituteMethod(object, propertyName, values): + * + * Defines an own property @object.@propertyName that will, for each + * subsequent call to the method previously defined as + * @object.@propertyName: + * - Call the method, if no more values remain + * - Call the method, if the value in @values for the corresponding call + * is SUBSTITUTE_SKIP + * - Otherwise, return the corresponding value in @value + */ + substituteMethod(object, propertyName, values) { + let calls = 0; + const method = object[propertyName]; + object[propertyName] = function () { + if (calls >= values.length) { + return method.apply(object, arguments); + } else if (values[calls] === SKIP_SYMBOL) { + calls++; + return method.apply(object, arguments); + } else { + return values[calls++]; + } + }; + }, + + /* + * calendarObserver: + * A custom calendar that behaves exactly like the ISO 8601 calendar but + * tracks calls to any of its methods, and Get/Has operations on its + * properties, by appending messages to an array. This is for the purpose of + * testing order of operations that are observable from user code. + * objectName is used in the log. + */ + calendarObserver(calls, objectName, methodOverrides = {}) { + function removeExtraHasPropertyChecks(objectName, calls) { + // Inserting the tracking calendar into the return values of methods + // that we chain up into the ISO calendar for, causes extra HasProperty + // checks, which we observe. This removes them so that we don't leak + // implementation details of the helper into the test code. + assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.year`); + assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.monthCode`); + assert.sameValue(calls.pop(), `has ${objectName}.month`); + assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`); + assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`); + assert.sameValue(calls.pop(), `has ${objectName}.id`); + assert.sameValue(calls.pop(), `has ${objectName}.fields`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`); + assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`); + assert.sameValue(calls.pop(), `has ${objectName}.day`); + assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`); + assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`); + assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`); + } + + const iso8601 = new Temporal.Calendar("iso8601"); + const trackingMethods = { + dateFromFields(...args) { + calls.push(`call ${objectName}.dateFromFields`); + if ('dateFromFields' in methodOverrides) { + const value = methodOverrides.dateFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + yearMonthFromFields(...args) { + calls.push(`call ${objectName}.yearMonthFromFields`); + if ('yearMonthFromFields' in methodOverrides) { + const value = methodOverrides.yearMonthFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.yearMonthFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + monthDayFromFields(...args) { + calls.push(`call ${objectName}.monthDayFromFields`); + if ('monthDayFromFields' in methodOverrides) { + const value = methodOverrides.monthDayFromFields; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.monthDayFromFields(...args); + // Replace the calendar in the result with the call-tracking calendar + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + dateAdd(...args) { + calls.push(`call ${objectName}.dateAdd`); + if ('dateAdd' in methodOverrides) { + const value = methodOverrides.dateAdd; + return typeof value === "function" ? value(...args) : value; + } + const originalResult = iso8601.dateAdd(...args); + const {isoYear, isoMonth, isoDay} = originalResult.getISOFields(); + const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this); + removeExtraHasPropertyChecks(objectName, calls); + return result; + }, + id: "iso8601", + }; + // Automatically generate the other methods that don't need any custom code + [ + "dateUntil", + "day", + "dayOfWeek", + "dayOfYear", + "daysInMonth", + "daysInWeek", + "daysInYear", + "era", + "eraYear", + "fields", + "inLeapYear", + "mergeFields", + "month", + "monthCode", + "monthsInYear", + "toString", + "weekOfYear", + "year", + "yearOfWeek", + ].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return iso8601[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom calendar that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + calendarThrowEverything() { + class CalendarThrowEverything extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + dateFromFields() { + TemporalHelpers.assertUnreachable("dateFromFields should not be called"); + } + yearMonthFromFields() { + TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called"); + } + monthDayFromFields() { + TemporalHelpers.assertUnreachable("monthDayFromFields should not be called"); + } + dateAdd() { + TemporalHelpers.assertUnreachable("dateAdd should not be called"); + } + dateUntil() { + TemporalHelpers.assertUnreachable("dateUntil should not be called"); + } + era() { + TemporalHelpers.assertUnreachable("era should not be called"); + } + eraYear() { + TemporalHelpers.assertUnreachable("eraYear should not be called"); + } + year() { + TemporalHelpers.assertUnreachable("year should not be called"); + } + month() { + TemporalHelpers.assertUnreachable("month should not be called"); + } + monthCode() { + TemporalHelpers.assertUnreachable("monthCode should not be called"); + } + day() { + TemporalHelpers.assertUnreachable("day should not be called"); + } + fields() { + TemporalHelpers.assertUnreachable("fields should not be called"); + } + mergeFields() { + TemporalHelpers.assertUnreachable("mergeFields should not be called"); + } + } + + return new CalendarThrowEverything(); + }, + + /* + * oneShiftTimeZone(shiftInstant, shiftNanoseconds): + * + * In the case of a spring-forward time zone offset transition (skipped time), + * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a + * negative number of nanoseconds from a PlainDateTime, which should balance + * with the microseconds field. + * + * This returns an instance of a custom time zone class which skips a length + * of time equal to shiftNanoseconds (a number), at the Temporal.Instant + * shiftInstant. Before shiftInstant, it's identical to UTC, and after + * shiftInstant it's a constant-offset time zone. + * + * It provides a getPossibleInstantsForCalledWith member which is an array + * with the result of calling toString() on any PlainDateTimes passed to + * getPossibleInstantsFor(). + */ + oneShiftTimeZone(shiftInstant, shiftNanoseconds) { + class OneShiftTimeZone extends Temporal.TimeZone { + constructor(shiftInstant, shiftNanoseconds) { + super("+00:00"); + this._shiftInstant = shiftInstant; + this._epoch1 = shiftInstant.epochNanoseconds; + this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds); + this._shiftNanoseconds = shiftNanoseconds; + this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds); + this.getPossibleInstantsForCalledWith = []; + } + + _isBeforeShift(instant) { + return instant.epochNanoseconds < this._epoch1; + } + + getOffsetNanosecondsFor(instant) { + return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds; + } + + getPossibleInstantsFor(plainDateTime) { + this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" })); + const [instant] = super.getPossibleInstantsFor(plainDateTime); + if (this._shiftNanoseconds > 0) { + if (this._isBeforeShift(instant)) return [instant]; + if (instant.epochNanoseconds < this._epoch2) return []; + return [instant.subtract(this._shift)]; + } + if (instant.epochNanoseconds < this._epoch2) return [instant]; + const shifted = instant.subtract(this._shift); + if (this._isBeforeShift(instant)) return [instant, shifted]; + return [shifted]; + } + + getNextTransition(instant) { + return this._isBeforeShift(instant) ? this._shiftInstant : null; + } + + getPreviousTransition(instant) { + return this._isBeforeShift(instant) ? null : this._shiftInstant; + } + + toString() { + return "Custom/One_Shift"; + } + } + return new OneShiftTimeZone(shiftInstant, shiftNanoseconds); + }, + + /* + * propertyBagObserver(): + * Returns an object that behaves like the given propertyBag but tracks Get + * and Has operations on any of its properties, by appending messages to an + * array. If the value of a property in propertyBag is a primitive, the value + * of the returned object's property will additionally be a + * TemporalHelpers.toPrimitiveObserver that will track calls to its toString + * and valueOf methods in the same array. This is for the purpose of testing + * order of operations that are observable from user code. objectName is used + * in the log. + */ + propertyBagObserver(calls, propertyBag, objectName) { + return new Proxy(propertyBag, { + ownKeys(target) { + calls.push(`ownKeys ${objectName}`); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(target, key) { + calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`); + return Reflect.getOwnPropertyDescriptor(target, key); + }, + get(target, key, receiver) { + calls.push(`get ${formatPropertyName(key, objectName)}`); + const result = Reflect.get(target, key, receiver); + if (result === undefined) { + return undefined; + } + if ((result !== null && typeof result === "object") || typeof result === "function") { + return result; + } + return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`); + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * specificOffsetTimeZone(): + * + * This returns an instance of a custom time zone class, which returns a + * specific custom value from its getOffsetNanosecondsFrom() method. This is + * for the purpose of testing the validation of what this method returns. + * + * It also returns an empty array from getPossibleInstantsFor(), so as to + * trigger calls to getOffsetNanosecondsFor() when used from the + * BuiltinTimeZoneGetInstantFor operation. + */ + specificOffsetTimeZone(offsetValue) { + class SpecificOffsetTimeZone extends Temporal.TimeZone { + constructor(offsetValue) { + super("UTC"); + this._offsetValue = offsetValue; + } + + getOffsetNanosecondsFor() { + return this._offsetValue; + } + + getPossibleInstantsFor(dt) { + if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return []; + const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue }); + return [zdt.toInstant()]; + } + + get id() { + return this.getOffsetStringFor(new Temporal.Instant(0n)); + } + } + return new SpecificOffsetTimeZone(offsetValue); + }, + + /* + * springForwardFallBackTimeZone(): + * + * This returns an instance of a custom time zone class that implements one + * single spring-forward/fall-back transition, for the purpose of testing the + * disambiguation option, without depending on system time zone data. + * + * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00 + * local) and goes from offset -08:00 to -07:00. + * + * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and + * goes from offset -07:00 to -08:00. + */ + springForwardFallBackTimeZone() { + const { compare } = Temporal.PlainDateTime; + const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2); + const springForwardEpoch = 954669600_000_000_000n; + const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1); + const fallBackEpoch = 972810000_000_000_000n; + const winterOffset = new Temporal.TimeZone('-08:00'); + const summerOffset = new Temporal.TimeZone('-07:00'); + + class SpringForwardFallBackTimeZone extends Temporal.TimeZone { + constructor() { + super("-08:00"); + } + + getOffsetNanosecondsFor(instant) { + if (instant.epochNanoseconds < springForwardEpoch || + instant.epochNanoseconds >= fallBackEpoch) { + return winterOffset.getOffsetNanosecondsFor(instant); + } + return summerOffset.getOffsetNanosecondsFor(instant); + } + + getPossibleInstantsFor(datetime) { + if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) { + return []; + } + if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) { + return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)]; + } + if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) { + return [winterOffset.getInstantFor(datetime)]; + } + return [summerOffset.getInstantFor(datetime)]; + } + + getPreviousTransition(instant) { + if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + return null; + } + + getNextTransition(instant) { + if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch); + if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch); + return null; + } + + get id() { + return "Custom/Spring_Fall"; + } + + toString() { + return "Custom/Spring_Fall"; + } + } + return new SpringForwardFallBackTimeZone(); + }, + + /* + * timeZoneObserver: + * A custom calendar that behaves exactly like the UTC time zone but tracks + * calls to any of its methods, and Get/Has operations on its properties, by + * appending messages to an array. This is for the purpose of testing order of + * operations that are observable from user code. objectName is used in the + * log. methodOverrides is an optional object containing properties with the + * same name as Temporal.TimeZone methods. If the property value is a function + * it will be called with the proper arguments instead of the UTC method. + * Otherwise, the property value will be returned directly. + */ + timeZoneObserver(calls, objectName, methodOverrides = {}) { + const utc = new Temporal.TimeZone("UTC"); + const trackingMethods = { + id: "UTC", + }; + // Automatically generate the methods + ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => { + trackingMethods[methodName] = function (...args) { + calls.push(`call ${formatPropertyName(methodName, objectName)}`); + if (methodName in methodOverrides) { + const value = methodOverrides[methodName]; + return typeof value === "function" ? value(...args) : value; + } + return utc[methodName](...args); + }; + }); + return new Proxy(trackingMethods, { + get(target, key, receiver) { + const result = Reflect.get(target, key, receiver); + calls.push(`get ${formatPropertyName(key, objectName)}`); + return result; + }, + has(target, key) { + calls.push(`has ${formatPropertyName(key, objectName)}`); + return Reflect.has(target, key); + }, + }); + }, + + /* + * A custom time zone that does not allow any of its methods to be called, for + * the purpose of asserting that a particular operation does not call into + * user code. + */ + timeZoneThrowEverything() { + class TimeZoneThrowEverything extends Temporal.TimeZone { + constructor() { + super("UTC"); + } + getOffsetNanosecondsFor() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called"); + } + getPossibleInstantsFor() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called"); + } + toString() { + TemporalHelpers.assertUnreachable("toString should not be called"); + } + } + + return new TimeZoneThrowEverything(); + }, + + /* + * Returns an object that will append logs of any Gets or Calls of its valueOf + * or toString properties to the array calls. Both valueOf and toString will + * return the actual primitiveValue. propertyName is used in the log. + */ + toPrimitiveObserver(calls, primitiveValue, propertyName) { + return { + get valueOf() { + calls.push(`get ${propertyName}.valueOf`); + return function () { + calls.push(`call ${propertyName}.valueOf`); + return primitiveValue; + }; + }, + get toString() { + calls.push(`get ${propertyName}.toString`); + return function () { + calls.push(`call ${propertyName}.toString`); + if (primitiveValue === undefined) return undefined; + return primitiveValue.toString(); + }; + }, + }; + }, + + /* + * An object containing further methods that return arrays of ISO strings, for + * testing parsers. + */ + ISO: { + /* + * PlainMonthDay strings that are not valid. + */ + plainMonthDayStringsInvalid() { + return [ + "11-18junk", + "11-18[u-ca=gregory]", + "11-18[u-ca=hebrew]", + ]; + }, + + /* + * PlainMonthDay strings that are valid and that should produce October 1st. + */ + plainMonthDayStringsValid() { + return [ + "10-01", + "1001", + "1965-10-01", + "1976-10-01T152330.1+00:00", + "19761001T15:23:30.1+00:00", + "1976-10-01T15:23:30.1+0000", + "1976-10-01T152330.1+0000", + "19761001T15:23:30.1+0000", + "19761001T152330.1+00:00", + "19761001T152330.1+0000", + "+001976-10-01T152330.1+00:00", + "+0019761001T15:23:30.1+00:00", + "+001976-10-01T15:23:30.1+0000", + "+001976-10-01T152330.1+0000", + "+0019761001T15:23:30.1+0000", + "+0019761001T152330.1+00:00", + "+0019761001T152330.1+0000", + "1976-10-01T15:23:00", + "1976-10-01T15:23", + "1976-10-01T15", + "1976-10-01", + "--10-01", + "--1001", + ]; + }, + + /* + * PlainTime strings that may be mistaken for PlainMonthDay or + * PlainYearMonth strings, and so require a time designator. + */ + plainTimeStringsAmbiguous() { + const ambiguousStrings = [ + "2021-12", // ambiguity between YYYY-MM and HHMM-UU + "2021-12[-12:00]", // ditto, TZ does not disambiguate + "1214", // ambiguity between MMDD and HHMM + "0229", // ditto, including MMDD that doesn't occur every year + "1130", // ditto, including DD that doesn't occur in every month + "12-14", // ambiguity between MM-DD and HH-UU + "12-14[-14:00]", // ditto, TZ does not disambiguate + "202112", // ambiguity between YYYYMM and HHMMSS + "202112[UTC]", // ditto, TZ does not disambiguate + ]; + // Adding a calendar annotation to one of these strings must not cause + // disambiguation in favour of time. + const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]'); + return ambiguousStrings.concat(stringsWithCalendar); + }, + + /* + * PlainTime strings that are of similar form to PlainMonthDay and + * PlainYearMonth strings, but are not ambiguous due to components that + * aren't valid as months or days. + */ + plainTimeStringsUnambiguous() { + return [ + "2021-13", // 13 is not a month + "202113", // ditto + "2021-13[-13:00]", // ditto + "202113[-13:00]", // ditto + "0000-00", // 0 is not a month + "000000", // ditto + "0000-00[UTC]", // ditto + "000000[UTC]", // ditto + "1314", // 13 is not a month + "13-14", // ditto + "1232", // 32 is not a day + "0230", // 30 is not a day in February + "0631", // 31 is not a day in June + "0000", // 0 is neither a month nor a day + "00-00", // ditto + ]; + }, + + /* + * PlainYearMonth-like strings that are not valid. + */ + plainYearMonthStringsInvalid() { + return [ + "2020-13", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November + * 1976 in the ISO 8601 calendar. + */ + plainYearMonthStringsValid() { + return [ + "1976-11", + "1976-11-10", + "1976-11-01T09:00:00+00:00", + "1976-11-01T00:00:00+05:00", + "197611", + "+00197611", + "1976-11-18T15:23:30.1\u221202:00", + "1976-11-18T152330.1+00:00", + "19761118T15:23:30.1+00:00", + "1976-11-18T15:23:30.1+0000", + "1976-11-18T152330.1+0000", + "19761118T15:23:30.1+0000", + "19761118T152330.1+00:00", + "19761118T152330.1+0000", + "+001976-11-18T152330.1+00:00", + "+0019761118T15:23:30.1+00:00", + "+001976-11-18T15:23:30.1+0000", + "+001976-11-18T152330.1+0000", + "+0019761118T15:23:30.1+0000", + "+0019761118T152330.1+00:00", + "+0019761118T152330.1+0000", + "1976-11-18T15:23", + "1976-11-18T15", + "1976-11-18", + ]; + }, + + /* + * PlainYearMonth-like strings that are valid and should produce November of + * the ISO year -9999. + */ + plainYearMonthStringsValidNegativeYear() { + return [ + "\u2212009999-11", + ]; + }, + } +}; diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js new file mode 100644 index 0000000000..ff64fa3160 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js @@ -0,0 +1,310 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.since() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); + +// zdt.since(earlier) === earlier.until(zdt) with default options +var earlier = Temporal.ZonedDateTime.from({ + year: 1966, + month: 3, + day: 3, + hour: 18, + timeZone: "+01:00" +}); +assert.sameValue(`${ zdt.since(earlier) }`, `${ earlier.until(zdt) }`); + +// casts argument +assert.sameValue(`${ zdt.since({ + year: 2019, + month: 10, + day: 29, + hour: 10, + timeZone: "+01:00" +}) }`, "-PT376434H36M29.876543211S"); +assert.sameValue(`${ zdt.since("2019-10-29T10:46:38.271986102+01:00[+01:00]") }`, "-PT376435H23M8.148529313S"); +var feb20 = Temporal.ZonedDateTime.from("2020-02-01T00:00+01:00[+01:00]"); +var feb21 = Temporal.ZonedDateTime.from("2021-02-01T00:00+01:00[+01:00]"); + +// defaults to returning hours +assert.sameValue(`${ feb21.since(feb20) }`, "PT8784H"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "auto" }) }`, "PT8784H"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2021-02-01T00:00:00.000000001+01:00[+01:00]").since(feb20) }`, "PT8784H0.000000001S"); +assert.sameValue(`${ feb21.since(Temporal.ZonedDateTime.from("2020-02-01T00:00:00.000000001+01:00[+01:00]")) }`, "PT8783H59M59.999999999S"); + +// can return lower or higher units +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "years" }) }`, "P1Y"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "months" }) }`, "P12M"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "weeks" }) }`, "P52W2D"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "days" }) }`, "P366D"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "minutes" }) }`, "PT527040M"); +assert.sameValue(`${ feb21.since(feb20, { largestUnit: "seconds" }) }`, "PT31622400S"); + +// can return subseconds +var later = feb20.add({ + days: 1, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = later.since(feb20, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = later.since(feb20, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = later.since(feb20, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// does not include higher units than necessary +var lastFeb20 = Temporal.ZonedDateTime.from("2020-02-29T00:00+01:00[+01:00]"); +var lastFeb21 = Temporal.ZonedDateTime.from("2021-02-28T00:00+01:00[+01:00]"); +assert.sameValue(`${ lastFeb21.since(lastFeb20) }`, "PT8760H"); +assert.sameValue(`${ lastFeb21.since(lastFeb20, { largestUnit: "months" }) }`, "P11M28D"); +assert.sameValue(`${ lastFeb21.since(lastFeb20, { largestUnit: "years" }) }`, "P11M28D"); + +// weeks and months are mutually exclusive +var laterDateTime = zdt.add({ + days: 42, + hours: 3 +}); +var weeksDifference = laterDateTime.since(zdt, { largestUnit: "weeks" }); +assert.notSameValue(weeksDifference.weeks, 0); +assert.sameValue(weeksDifference.months, 0); +var monthsDifference = laterDateTime.since(zdt, { largestUnit: "months" }); +assert.sameValue(monthsDifference.weeks, 0); +assert.notSameValue(monthsDifference.months, 0); + +// no two different calendars +var zdt1 = new Temporal.ZonedDateTime(0n, "UTC"); +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", fakeJapanese); +assert.throws(RangeError, () => zdt1.since(zdt2)); + +var earlier = Temporal.ZonedDateTime.from('2019-01-08T09:22:36.123456789+01:00[+01:00]'); +var later = Temporal.ZonedDateTime.from('2021-09-07T13:39:40.987654321+01:00[+01:00]'); +// assumes a different default for largestUnit if smallestUnit is larger than days +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P3Y"); +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "months", + roundingMode: "halfExpand" +}) }`, "P32M"); +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "weeks", + roundingMode: "halfExpand" +}) }`, "P139W"); + +// rounds to an increment of hours +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "hours", + roundingIncrement: 3, + roundingMode: "halfExpand" +}) }`, "PT23355H"); + +// rounds to an increment of minutes +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT23356H30M"); + +// rounds to an increment of seconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT23356H17M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.8642S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ later.since(earlier, { + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86419753S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + smallestUnit: "hours", + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); +}); +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(later.since(earlier, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => later.since(earlier, { + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +// rounds relative to the receiver +var dt1 = Temporal.ZonedDateTime.from("2019-01-01T00:00+00:00[UTC]"); +var dt2 = Temporal.ZonedDateTime.from("2020-07-02T00:00+00:00[UTC]"); +assert.sameValue(`${ dt2.since(dt1, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P1Y"); +assert.sameValue(`${ dt1.since(dt2, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "-P2Y"); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js new file mode 100644 index 0000000000..f17eca11fd --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js @@ -0,0 +1,71 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: string parsing +features: [Temporal] +---*/ + +// any number of decimal places +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1-08:00[-08:00]") }`, "1976-11-18T15:23:30.1-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12-08:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123-08:00[-08:00]") }`, "1976-11-18T15:23:30.123-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1234-08:00[-08:00]") }`, "1976-11-18T15:23:30.1234-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12345-08:00[-08:00]") }`, "1976-11-18T15:23:30.12345-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456-08:00[-08:00]") }`, "1976-11-18T15:23:30.123456-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1234567-08:00[-08:00]") }`, "1976-11-18T15:23:30.1234567-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12345678-08:00[-08:00]") }`, "1976-11-18T15:23:30.12345678-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-08:00[-08:00]") }`, "1976-11-18T15:23:30.123456789-08:00[-08:00]"); + +// variant decimal separator +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30,12-08:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]"); + +// variant minus sign +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12\u221208:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("\u2212009999-11-18T15:23:30.12+00:00[UTC]") }`, "-009999-11-18T15:23:30.12+00:00[UTC]"); + +// mixture of basic and extended format +[ + "1976-11-18T152330.1-08:00[-08:00]", + "19761118T15:23:30.1-08:00[-08:00]", + "1976-11-18T15:23:30.1-0800[-08:00]", + "1976-11-18T152330.1-0800[-08:00]", + "19761118T15:23:30.1-0800[-08:00]", + "19761118T152330.1-08:00[-08:00]", + "19761118T152330.1-0800[-08:00]", + "+001976-11-18T152330.1-08:00[-08:00]", + "+0019761118T15:23:30.1-08:00[-08:00]", + "+001976-11-18T15:23:30.1-0800[-08:00]", + "+001976-11-18T152330.1-0800[-08:00]", + "+0019761118T15:23:30.1-0800[-08:00]", + "+0019761118T152330.1-08:00[-08:00]", + "+0019761118T152330.1-0800[-08:00]" +].forEach(input => assert.sameValue(`${ Temporal.ZonedDateTime.from(input) }`, "1976-11-18T15:23:30.1-08:00[-08:00]")); + +// optional parts +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30-08[-08:00]") }`, "1976-11-18T15:23:30-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15-08:00[-08:00]") }`, "1976-11-18T15:00:00-08:00[-08:00]"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-01-01[+09:00]") }`, "2020-01-01T00:00:00+09:00[+09:00]"); + +// no junk at end of string +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-08:00[-08:00]junk")) + +// constrain has no effect on invalid ISO string +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-13-34T24:60[-08:00]", { overflow: "constrain" })); + +// { offset: 'reject' } throws if offset does not match IANA time zone +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-03-08T01:00-04:00[UTC]")); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-03-08T01:00-04:00[UTC]", { offset: "reject" })); + +// throw when bad disambiguation +[ + "", + "EARLIER", + "balance" +].forEach(disambiguation => { + assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-11-01T04:00[-08:00]", { disambiguation })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js new file mode 100644 index 0000000000..790dfbb9cb --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.subtract() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1969-12-25T12:23:45.678901234+00:00[UTC]"); + +// inst.subtract(durationObj) +var earlier = zdt.subtract(Temporal.Duration.from("PT240H0.000000800S")); +assert.sameValue(`${ earlier }`, "1969-12-15T12:23:45.678900434+00:00[UTC]"); + +// casts argument +assert.sameValue(`${ zdt.subtract("PT240H0.000000800S") }`, "1969-12-15T12:23:45.678900434+00:00[UTC]"); +var mar31 = Temporal.ZonedDateTime.from("2020-03-31T15:00+00:00[UTC]"); + +// constrain when ambiguous result +assert.sameValue(`${ mar31.subtract({ months: 1 }) }`, "2020-02-29T15:00:00+00:00[UTC]"); +assert.sameValue(`${ mar31.subtract({ months: 1 }, { overflow: "constrain" }) }`, "2020-02-29T15:00:00+00:00[UTC]"); + +// symmetrical with regard to negative durations in the time part +assert.sameValue(`${ mar31.subtract({ minutes: -30 }) }`, "2020-03-31T15:30:00+00:00[UTC]"); +assert.sameValue(`${ mar31.subtract({ seconds: -30 }) }`, "2020-03-31T15:00:30+00:00[UTC]"); + +// throw when ambiguous result with reject +assert.throws(RangeError, () => mar31.subtract({ months: 1 }, { overflow: "reject" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js new file mode 100644 index 0000000000..0fcc34bb13 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toInstant() +features: [Temporal] +---*/ + + +// recent date +var zdt = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102+01:00[+01:00]"); +assert.sameValue(`${ zdt.toInstant() }`, "2019-10-29T09:46:38.271986102Z"); + +// year ≤ 99 +var zdt = Temporal.ZonedDateTime.from("0098-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0098-10-29T10:46:38.271986102Z"); +zdt = Temporal.ZonedDateTime.from("+000098-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0098-10-29T10:46:38.271986102Z"); + +// year < 1 +var zdt = Temporal.ZonedDateTime.from("0000-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-10-29T10:46:38.271986102Z"); +zdt = Temporal.ZonedDateTime.from("+000000-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-10-29T10:46:38.271986102Z"); +zdt = Temporal.ZonedDateTime.from("-001000-10-29T10:46:38.271986102+00:00[UTC]"); +assert.sameValue(`${ zdt.toInstant() }`, "-001000-10-29T10:46:38.271986102Z"); + +// year 0 leap day +var zdt = Temporal.ZonedDateTime.from("0000-02-29T00:00-00:01[-00:01]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z"); +zdt = Temporal.ZonedDateTime.from("+000000-02-29T00:00-00:01[-00:01]"); +assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js new file mode 100644 index 0000000000..4b8e6aa54e --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainDate() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-07:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainDate() }`, "2019-10-29"); + +// preserves the calendar +const fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({ + timeZone: tz, + calendar: fakeGregorian +}); +assert.sameValue(zdt.toPlainDate().getCalendar(), fakeGregorian); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js new file mode 100644 index 0000000000..23d1248aec --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainMonthDay() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-08:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainMonthDay() }`, "10-29"); + +// preserves the calendar +var fakeGregorian = { + id: 'gregory', + monthDayFromFields(fields) { + var md = Temporal.Calendar.from("iso8601").monthDayFromFields(fields); + var {isoYear, isoMonth, isoDay} = md.getISOFields(); + return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); + }, + monthCode(date) { return date.withCalendar("iso8601").monthCode; }, + day(date) { return date.withCalendar("iso8601").day; }, + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields(fieldNames) { return fieldNames; }, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({ + timeZone: tz, + calendar: fakeGregorian +}); +assert.sameValue(zdt.toPlainMonthDay().getCalendar(), fakeGregorian); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js new file mode 100644 index 0000000000..78e6e0bbae --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainTime() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-07:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainTime() }`, "02:46:38.271986102"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js new file mode 100644 index 0000000000..983bc21bec --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toPlainYearMonth() +features: [Temporal] +---*/ + +var tz = new Temporal.TimeZone("-08:00"); + +// works +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz); +assert.sameValue(`${ zdt.toPlainYearMonth() }`, "2019-10"); + +// preserves the calendar +var fakeGregorian = { + id: 'gregory', + yearMonthFromFields(fields) { + var ym = Temporal.Calendar.from("iso8601").yearMonthFromFields(fields); + var {isoYear, isoMonth, isoDay} = ym.getISOFields(); + return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); + }, + year(date) { return date.withCalendar("iso8601").year; }, + monthCode(date) { return date.withCalendar("iso8601").monthCode; }, + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields(fieldNames) { return fieldNames; }, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({ + timeZone: tz, + calendar: fakeGregorian +}); +assert.sameValue(zdt.toPlainYearMonth().getCalendar(), fakeGregorian); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js new file mode 100644 index 0000000000..2e3751b520 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js @@ -0,0 +1,74 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.toString() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var zdt1 = Temporal.ZonedDateTime.from("1976-11-18T15:23+00:00[UTC]"); +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +// shows offset if offset = auto +assert.sameValue(zdt1.toString({ offset: "auto" }), "1976-11-18T15:23:00+00:00[UTC]"); + +// omits offset if offset = never +assert.sameValue(zdt1.toString({ offset: "never" }), "1976-11-18T15:23:00[UTC]"); + +// combinations of calendar, time zone, and offset +var zdt = zdt1.withCalendar(fakeGregorian); +assert.sameValue(zdt.toString({ + timeZoneName: "never", + calendarName: "never" +}), "1976-11-18T15:23:00+00:00"); +assert.sameValue(zdt.toString({ + offset: "never", + calendarName: "never" +}), "1976-11-18T15:23:00[UTC]"); +assert.sameValue(zdt.toString({ + offset: "never", + timeZoneName: "never" +}), "1976-11-18T15:23:00[u-ca=gregory]"); +assert.sameValue(zdt.toString({ + offset: "never", + timeZoneName: "never", + calendarName: "never" +}), "1976-11-18T15:23:00"); + +// rounding up to a nonexistent wall-clock time +var dst = TemporalHelpers.springForwardFallBackTimeZone(); +var zdt5 = Temporal.PlainDateTime.from("2000-04-02T01:59:59.999999999").toZonedDateTime(dst); +var roundedString = zdt5.toString({ + fractionalSecondDigits: 8, + roundingMode: "halfExpand" +}); +assert.sameValue(roundedString, "2000-04-02T03:00:00.00000000-07:00[Custom/Spring_Fall]"); +var zdt6 = Temporal.Instant.from(roundedString); +assert.sameValue(zdt6.epochNanoseconds - zdt5.epochNanoseconds, 1n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js new file mode 100644 index 0000000000..65fdcf2ffc --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js @@ -0,0 +1,314 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.until() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]"); + +// zdt.until(later) === later.since(zdt) with default options +var later = Temporal.ZonedDateTime.from({ + year: 2016, + month: 3, + day: 3, + hour: 18, + timeZone: "+01:00" +}); +assert.sameValue(`${ zdt.until(later) }`, `${ later.since(zdt) }`); + +// casts argument +assert.sameValue(`${ zdt.until({ + year: 2019, + month: 10, + day: 29, + hour: 10, + timeZone: "+01:00" +}) }`, "PT376434H36M29.876543211S"); +assert.sameValue(`${ zdt.until("2019-10-29T10:46:38.271986102+01:00[+01:00]") }`, "PT376435H23M8.148529313S"); +var feb20 = Temporal.ZonedDateTime.from("2020-02-01T00:00+01:00[+01:00]"); +var feb21 = Temporal.ZonedDateTime.from("2021-02-01T00:00+01:00[+01:00]"); + +// defaults to returning hours +assert.sameValue(`${ feb20.until(feb21) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "auto" }) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "hours" }) }`, "PT8784H"); +assert.sameValue(`${ feb20.until(Temporal.ZonedDateTime.from("2021-02-01T00:00:00.000000001+01:00[+01:00]")) }`, "PT8784H0.000000001S"); +assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-02-01T00:00:00.000000001+01:00[+01:00]").until(feb21) }`, "PT8783H59M59.999999999S"); + +// can return lower or higher units +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "years" }) }`, "P1Y"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "months" }) }`, "P12M"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "weeks" }) }`, "P52W2D"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "days" }) }`, "P366D"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "minutes" }) }`, "PT527040M"); +assert.sameValue(`${ feb20.until(feb21, { largestUnit: "seconds" }) }`, "PT31622400S"); + +// can return subseconds +var later = feb20.add({ + days: 1, + milliseconds: 250, + microseconds: 250, + nanoseconds: 250 +}); +var msDiff = feb20.until(later, { largestUnit: "milliseconds" }); +assert.sameValue(msDiff.seconds, 0); +assert.sameValue(msDiff.milliseconds, 86400250); +assert.sameValue(msDiff.microseconds, 250); +assert.sameValue(msDiff.nanoseconds, 250); +var µsDiff = feb20.until(later, { largestUnit: "microseconds" }); +assert.sameValue(µsDiff.milliseconds, 0); +assert.sameValue(µsDiff.microseconds, 86400250250); +assert.sameValue(µsDiff.nanoseconds, 250); +var nsDiff = feb20.until(later, { largestUnit: "nanoseconds" }); +assert.sameValue(nsDiff.microseconds, 0); +assert.sameValue(nsDiff.nanoseconds, 86400250250250); + +// does not include higher units than necessary +var lastFeb20 = Temporal.ZonedDateTime.from("2020-02-29T00:00+01:00[+01:00]"); +var lastJan21 = Temporal.ZonedDateTime.from("2021-01-31T00:00+01:00[+01:00]"); +assert.sameValue(`${ lastFeb20.until(lastJan21) }`, "PT8088H"); +assert.sameValue(`${ lastFeb20.until(lastJan21, { largestUnit: "months" }) }`, "P11M2D"); +assert.sameValue(`${ lastFeb20.until(lastJan21, { largestUnit: "years" }) }`, "P11M2D"); + +// weeks and months are mutually exclusive +var laterDateTime = zdt.add({ + days: 42, + hours: 3 +}); +var weeksDifference = zdt.until(laterDateTime, { largestUnit: "weeks" }); +assert.notSameValue(weeksDifference.weeks, 0); +assert.sameValue(weeksDifference.months, 0); +var monthsDifference = zdt.until(laterDateTime, { largestUnit: "months" }); +assert.sameValue(monthsDifference.weeks, 0); +assert.notSameValue(monthsDifference.months, 0); + +// no two different calendars +var zdt1 = new Temporal.ZonedDateTime(0n, "UTC"); +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", fakeJapanese); +assert.throws(RangeError, () => zdt1.until(zdt2)); + +var earlier = Temporal.ZonedDateTime.from('2019-01-08T09:22:36.123456789+01:00[+01:00]'); +var later = Temporal.ZonedDateTime.from('2021-09-07T13:39:40.987654321+01:00[+01:00]'); +// assumes a different default for largestUnit if smallestUnit is larger than hours +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P3Y"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "months", + roundingMode: "halfExpand" +}) }`, "P32M"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "weeks", + roundingMode: "halfExpand" +}) }`, "P139W"); +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "days", + roundingMode: "halfExpand" +}) }`, "P973D"); + +// rounds to an increment of hours +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "hours", + roundingIncrement: 3, + roundingMode: "halfExpand" +}) }`, "PT23355H"); + +// rounds to an increment of minutes +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "minutes", + roundingIncrement: 30, + roundingMode: "halfExpand" +}) }`, "PT23356H30M"); + +// rounds to an increment of seconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "seconds", + roundingIncrement: 15, + roundingMode: "halfExpand" +}) }`, "PT23356H17M"); + +// rounds to an increment of milliseconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "milliseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86S"); + +// rounds to an increment of microseconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "microseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.8642S"); + +// rounds to an increment of nanoseconds +assert.sameValue(`${ earlier.until(later, { + smallestUnit: "nanoseconds", + roundingIncrement: 10, + roundingMode: "halfExpand" +}) }`, "PT23356H17M4.86419753S"); + +// valid hour increments divide into 24 +[ + 1, + 2, + 3, + 4, + 6, + 8, + 12 +].forEach(roundingIncrement => { + var options = { + smallestUnit: "hours", + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); +}); +[ + "minutes", + "seconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 3, + 4, + 5, + 6, + 10, + 12, + 15, + 20, + 30 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); +[ + "milliseconds", + "microseconds", + "nanoseconds" +].forEach(smallestUnit => { + [ + 1, + 2, + 4, + 5, + 8, + 10, + 20, + 25, + 40, + 50, + 100, + 125, + 200, + 250, + 500 + ].forEach(roundingIncrement => { + var options = { + smallestUnit, + roundingIncrement + }; + assert(earlier.until(later, options) instanceof Temporal.Duration); + }); +}); + +// throws on increments that do not divide evenly into the next highest +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "hours", + roundingIncrement: 11 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "minutes", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "seconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "milliseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "microseconds", + roundingIncrement: 29 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "nanoseconds", + roundingIncrement: 29 +})); + +// throws on increments that are equal to the next highest +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "hours", + roundingIncrement: 24 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "minutes", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "seconds", + roundingIncrement: 60 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "milliseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "microseconds", + roundingIncrement: 1000 +})); +assert.throws(RangeError, () => earlier.until(later, { + smallestUnit: "nanoseconds", + roundingIncrement: 1000 +})); + +// rounds relative to the receiver +var dt1 = Temporal.ZonedDateTime.from("2019-01-01T00:00+00:00[UTC]"); +var dt2 = Temporal.ZonedDateTime.from("2020-07-02T00:00+00:00[UTC]"); +assert.sameValue(`${ dt1.until(dt2, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "P2Y"); +assert.sameValue(`${ dt2.until(dt1, { + smallestUnit: "years", + roundingMode: "halfExpand" +}) }`, "-P1Y"); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js new file mode 100644 index 0000000000..42eede22f9 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js @@ -0,0 +1,272 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.with() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var zdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC"); + +// zdt.with({ year: 2019 } works +assert.sameValue(`${ zdt.with({ year: 2019 }) }`, "2019-11-18T15:23:30.123456789+00:00[UTC]"); + +// zdt.with({ month: 5 } works +assert.sameValue(`${ zdt.with({ month: 5 }) }`, "1976-05-18T15:23:30.123456789+00:00[UTC]"); + +// zdt.with({ monthCode: "M05" }) works +assert.sameValue(`${ zdt.with({ monthCode: "M05" }) }`, "1976-05-18T15:23:30.123456789+00:00[UTC]"); + +// month and monthCode must agree +assert.throws(RangeError, () => zdt.with({ + month: 5, + monthCode: "M06" +})); + +// zdt.with({ day: 5 } works +assert.sameValue(`${ zdt.with({ day: 5 }) }`, "1976-11-05T15:23:30.123456789+00:00[UTC]"); + +// zdt.with({ hour: 5 } works +assert.sameValue(`${ zdt.with({ hour: 5 }) }`, "1976-11-18T05:23:30.123456789+00:00[UTC]"); + +// zdt.with({ minute: 5 } works +assert.sameValue(`${ zdt.with({ minute: 5 }) }`, "1976-11-18T15:05:30.123456789+00:00[UTC]"); + +// zdt.with({ second: 5 } works +assert.sameValue(`${ zdt.with({ second: 5 }) }`, "1976-11-18T15:23:05.123456789+00:00[UTC]"); + +// zdt.with({ millisecond: 5 } works +assert.sameValue(`${ zdt.with({ millisecond: 5 }) }`, "1976-11-18T15:23:30.005456789+00:00[UTC]"); + +// zdt.with({ microsecond: 5 } works +assert.sameValue(`${ zdt.with({ microsecond: 5 }) }`, "1976-11-18T15:23:30.123005789+00:00[UTC]"); + +// zdt.with({ nanosecond: 5 } works +assert.sameValue(`${ zdt.with({ nanosecond: 5 }) }`, "1976-11-18T15:23:30.123456005+00:00[UTC]"); + +// zdt.with({ month: 5, second: 15 } works +assert.sameValue(`${ zdt.with({ + month: 5, + second: 15 +}) }`, "1976-05-18T15:23:15.123456789+00:00[UTC]"); + +// Overflow options +// constrain +var overflow = "constrain"; +assert.sameValue(`${ zdt.with({ month: 29 }, { overflow }) }`, "1976-12-18T15:23:30.123456789+00:00[UTC]"); +assert.sameValue(`${ zdt.with({ day: 31 }, { overflow }) }`, "1976-11-30T15:23:30.123456789+00:00[UTC]"); +assert.sameValue(`${ zdt.with({ hour: 29 }, { overflow }) }`, "1976-11-18T23:23:30.123456789+00:00[UTC]"); +assert.sameValue(`${ zdt.with({ nanosecond: 9000 }, { overflow }) }`, "1976-11-18T15:23:30.123456999+00:00[UTC]"); + +// reject +var overflow = "reject"; +assert.throws(RangeError, () => zdt.with({ month: 29 }, { overflow })); +assert.throws(RangeError, () => zdt.with({ day: 31 }, { overflow })); +assert.throws(RangeError, () => zdt.with({ hour: 29 }, { overflow })); +assert.throws(RangeError, () => zdt.with({ nanosecond: 9000 }, { overflow })); + +var dst = TemporalHelpers.springForwardFallBackTimeZone(); +var dstStartDay = Temporal.PlainDateTime.from("2000-04-02T12:00:01").toZonedDateTime(dst); +var dstEndDay = Temporal.PlainDateTime.from("2000-10-29T12:00:01").toZonedDateTime(dst); +var oneThirty = { +hour: 1, +minute: 30 +}; +var twoThirty = { +hour: 2, +minute: 30 +}; + +// Disambiguation options +var offset = "ignore"; +// compatible, skipped wall time +assert.sameValue(`${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "compatible" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// earlier, skipped wall time +assert.sameValue(`${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]"); + +// later, skipped wall time +assert.sameValue(`${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// compatible, repeated wall time +assert.sameValue(`${ dstEndDay.with(oneThirty, { + offset, + disambiguation: "compatible" +}) }`, "2000-10-29T01:30:01-07:00[Custom/Spring_Fall]"); + +// earlier, repeated wall time +assert.sameValue(`${ dstEndDay.with(oneThirty, { + offset, + disambiguation: "earlier" +}) }`, "2000-10-29T01:30:01-07:00[Custom/Spring_Fall]"); + +// later, repeated wall time +assert.sameValue(`${ dstEndDay.with(oneThirty, { + offset, + disambiguation: "later" +}) }`, "2000-10-29T01:30:01-08:00[Custom/Spring_Fall]"); + +// reject +assert.throws(RangeError, () => dstStartDay.with(twoThirty, { + offset, + disambiguation: "reject" +})); +assert.throws(RangeError, () => dstEndDay.with(oneThirty, { + offset, + disambiguation: "reject" +})); + +// compatible is the default +assert.sameValue(`${ dstStartDay.with(twoThirty, { offset }) }`, `${ dstStartDay.with(twoThirty, { + offset, + disambiguation: "compatible" +}) }`); +assert.sameValue(`${ dstEndDay.with(twoThirty, { offset }) }`, `${ dstEndDay.with(twoThirty, { + offset, + disambiguation: "compatible" +}) }`); + +// invalid disambiguation +[ + "", + "EARLIER", + "balance" +].forEach(disambiguation => assert.throws(RangeError, () => zdt.with({ day: 5 }, { disambiguation }))); + +// Offset options +var bogus = { + ...twoThirty, + offset: "+23:59" +}; +// use, with bogus offset, changes to the exact time with the offset +var preserveExact = dstStartDay.with(bogus, { offset: "use" }); +assert.sameValue(`${ preserveExact }`, "2000-03-31T18:31:01-08:00[Custom/Spring_Fall]"); +assert.sameValue(preserveExact.epochNanoseconds, Temporal.Instant.from("2000-04-02T02:30:01+23:59").epochNanoseconds); + +// ignore, with bogus offset, defers to disambiguation option +var offset = "ignore"; +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// prefer, with bogus offset, defers to disambiguation option +var offset = "prefer"; +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "earlier" +}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]"); +assert.sameValue(`${ dstStartDay.with(bogus, { + offset, + disambiguation: "later" +}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]"); + +// reject, with bogus offset, throws +assert.throws(RangeError, () => dstStartDay.with({ + ...twoThirty, + offset: "+23:59" +}, { offset: "reject" })); + +var doubleTime = new Temporal.ZonedDateTime(972811801_000_000_000n, dst); +// use changes to the exact time with the offset +var preserveExact = doubleTime.with({ offset: "-07:00" }, { offset: "use" }); +assert.sameValue(preserveExact.offset, "-07:00"); +assert.sameValue(preserveExact.epochNanoseconds, Temporal.Instant.from("2000-10-29T01:30:01-07:00").epochNanoseconds); + +// ignore defers to disambiguation option +var offset = "ignore"; +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { + offset, + disambiguation: "earlier" +}).offset, "-07:00"); +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { + offset, + disambiguation: "later" +}).offset, "-08:00"); + +// prefer adjusts offset of repeated clock time +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { offset: "prefer" }).offset, "-07:00"); + +// reject adjusts offset of repeated clock time +assert.sameValue(doubleTime.with({ offset: "-07:00" }, { offset: "reject" }).offset, "-07:00"); + +// use does not cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "use" }).offset, "-08:00"); + +// ignore may cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "ignore" }).offset, "-07:00"); + +// prefer does not cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "prefer" }).offset, "-08:00"); + +// reject does not cause the offset to change when adjusting repeated clock time +assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "reject" }).offset, "-08:00"); + +// prefer is the default +assert.sameValue(`${ dstStartDay.with(twoThirty) }`, `${ dstStartDay.with(twoThirty, { offset: "prefer" }) }`); +assert.sameValue(`${ dstEndDay.with(twoThirty) }`, `${ dstEndDay.with(twoThirty, { offset: "prefer" }) }`); +assert.sameValue(`${ doubleTime.with({ minute: 31 }) }`, `${ doubleTime.with({ minute: 31 }, { offset: "prefer" }) }`); + +// invalid offset +[ + "", + "PREFER", + "balance" +].forEach(offset => assert.throws(RangeError, () => zdt.with({ day: 5 }, { offset }))); + +// object must contain at least one correctly-spelled property +assert.throws(TypeError, () => zdt.with({})); +assert.throws(TypeError, () => zdt.with({ months: 12 })); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ zdt.with({ + month: 12, + days: 15 +}) }`, "1976-12-18T15:23:30.123456789+00:00[UTC]"); + +// throws if timeZone is included +assert.throws(TypeError, () => zdt.with({ + month: 2, + timeZone: "UTC" +})); + +// throws if given a Temporal object with a time zone +assert.throws(TypeError, () => zdt.with(dstStartDay)); + +// throws if calendarName is included +assert.throws(TypeError, () => zdt.with({ + month: 2, + calendar: "iso8601" +})); + +// throws if given a Temporal object with a calendar +assert.throws(TypeError, () => zdt.with(Temporal.PlainDateTime.from("1976-11-18T12:00"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainDate.from("1976-11-18"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainTime.from("12:00"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainYearMonth.from("1976-11"))); +assert.throws(TypeError, () => zdt.with(Temporal.PlainMonthDay.from("11-18"))); + +// throws if given a string +assert.throws(TypeError, () => zdt.with("1976-11-18T12:00+00:00[UTC]")); +assert.throws(TypeError, () => zdt.with("1976-11-18")); +assert.throws(TypeError, () => zdt.with("12:00")); +assert.throws(TypeError, () => zdt.with("invalid")); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js new file mode 100644 index 0000000000..380cbeb664 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.withCalendar() +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789-08:00[-08:00]"); + +// zonedDateTime.withCalendar(japanese) works +var cal = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ zdt.withCalendar(cal) }`, "2019-11-18T15:23:30.123456789-08:00[-08:00][u-ca=japanese]"); + +// keeps instant and time zone the same +var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789+01:00[+01:00][u-ca=iso8601]"); +var zdt2 = zdt.withCalendar(cal); +assert.sameValue(zdt.epochNanoseconds, zdt2.epochNanoseconds); +assert.sameValue(zdt2.getCalendar(), cal); +assert.sameValue(zdt2.timeZoneId, "+01:00"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js new file mode 100644 index 0000000000..810ebd3350 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js @@ -0,0 +1,98 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: .withPlainDate manipulation +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +var dst = TemporalHelpers.springForwardFallBackTimeZone(); +var zdt = Temporal.PlainDateTime.from("1995-12-07T03:24:30").toZonedDateTime(dst); + +// withPlainDate({ year: 2000, month: 6, day: 1 }) works +// and keeps wall time constant despite the UTC offset change +assert.sameValue(`${ zdt.withPlainDate({ + year: 2000, + month: 6, + day: 1 +}) }`, "2000-06-01T03:24:30-07:00[Custom/Spring_Fall]"); + +// withPlainDate(plainDate) works +var date = Temporal.PlainDate.from("2020-01-23"); +assert.sameValue(`${ zdt.withPlainDate(date) }`, "2020-01-23T03:24:30-08:00[Custom/Spring_Fall]"); + +// withPlainDate('2018-09-15') works +assert.sameValue(`${ zdt.withPlainDate("2018-09-15") }`, "2018-09-15T03:24:30-08:00[Custom/Spring_Fall]"); + +// result contains a non-ISO calendar if present in the input +var fakeJapanese = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "japanese", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.sameValue(`${ zdt.withCalendar(fakeJapanese).withPlainDate("2008-09-06") }`, "2008-09-06T03:24:30-08:00[Custom/Spring_Fall][u-ca=japanese]"); + +// calendar is unchanged if input has ISO calendar +var date = new Temporal.PlainDate(2008, 9, 6, fakeJapanese); +assert.sameValue(`${ zdt.withPlainDate(date) }`, "2008-09-06T03:24:30-08:00[Custom/Spring_Fall][u-ca=japanese]"); + +// throws if both `this` and `other` have a non-ISO calendar +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +assert.throws(RangeError, () => zdt.withCalendar(fakeGregorian).withPlainDate(date)); + +// object must contain at least one correctly-spelled property +assert.throws(TypeError, () => zdt.withPlainDate({})); +assert.throws(TypeError, () => zdt.withPlainDate({ months: 12 })); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ zdt.withPlainDate({ + year: 2000, + month: 6, + day: 1, + months: 123 +}) }`, "2000-06-01T03:24:30-07:00[Custom/Spring_Fall]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js new file mode 100644 index 0000000000..9bd03c02fd --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: .withPlainTime manipulation +features: [Temporal] +---*/ + +var zdt = Temporal.ZonedDateTime.from("2015-12-07T03:24:30.000003500[-08:00]"); + +// withPlainTime({ hour: 10 }) works +assert.sameValue(`${ zdt.withPlainTime({ hour: 10 }) }`, "2015-12-07T10:00:00-08:00[-08:00]"); + +// withPlainTime(time) works +var time = Temporal.PlainTime.from("11:22"); +assert.sameValue(`${ zdt.withPlainTime(time) }`, "2015-12-07T11:22:00-08:00[-08:00]"); + +// withPlainTime('12:34') works +assert.sameValue(`${ zdt.withPlainTime("12:34") }`, "2015-12-07T12:34:00-08:00[-08:00]"); + +// incorrectly-spelled properties are ignored +assert.sameValue(`${ zdt.withPlainTime({ + hour: 10, + seconds: 55 +}) }`, "2015-12-07T10:00:00-08:00[-08:00]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js new file mode 100644 index 0000000000..ae5951ff64 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2018 Bloomberg LP. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-objects +description: Temporal.ZonedDateTime.prototype.withTimeZone() +features: [Temporal] +---*/ + +// keeps instant and calendar the same +var fakeGregorian = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "gregory", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789+01:00[+01:00]").withCalendar(fakeGregorian); +var zdt2 = zdt.withTimeZone("-08:00"); +assert.sameValue(zdt.epochNanoseconds, zdt2.epochNanoseconds); +assert.sameValue(zdt2.getCalendar(), fakeGregorian); +assert.sameValue(zdt2.timeZoneId, "-08:00"); +assert.notSameValue(`${ zdt.toPlainDateTime() }`, `${ zdt2.toPlainDateTime() }`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js diff --git a/js/src/tests/test262/staging/Temporal/browser.js b/js/src/tests/test262/staging/Temporal/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/browser.js diff --git a/js/src/tests/test262/staging/Temporal/shell.js b/js/src/tests/test262/staging/Temporal/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/Temporal/shell.js diff --git a/js/src/tests/test262/staging/browser.js b/js/src/tests/test262/staging/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/browser.js diff --git a/js/src/tests/test262/staging/set-is-subset-on-set-like.js b/js/src/tests/test262/staging/set-is-subset-on-set-like.js new file mode 100644 index 0000000000..dac869b822 --- /dev/null +++ b/js/src/tests/test262/staging/set-is-subset-on-set-like.js @@ -0,0 +1,27 @@ +// |reftest| skip -- set-methods is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: Test isSubsetOf set method on a set like with equal size. +features: [set-methods] +---*/ + +const SetLike = { + arr: [42, 44, 45], + size: 3, + keys() { + return this.arr[Symbol.iterator](); + }, + has(key) { + return this.arr.indexOf(key) != -1; + } +}; + +const firstSet = new Set(); +firstSet.add(42); +firstSet.add(43); +firstSet.add(45); + +assert.sameValue(firstSet.isSubsetOf(SetLike), false); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/set-is-subset-table-receiver-cleared.js b/js/src/tests/test262/staging/set-is-subset-table-receiver-cleared.js new file mode 100644 index 0000000000..927f57ac53 --- /dev/null +++ b/js/src/tests/test262/staging/set-is-subset-table-receiver-cleared.js @@ -0,0 +1,28 @@ +// |reftest| skip -- set-methods is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: Test isSubsetOf set method receiver is cleared. +features: [set-methods] +---*/ + +const firstSet = new Set(); +firstSet.add(42); +firstSet.add(43); + +const otherSet = new Set(); +otherSet.add(42); +otherSet.add(43); +otherSet.add(47); + +Object.defineProperty(otherSet, 'size', { + get: function() { + firstSet.clear(); + return 3; + }, + +}); + +assert.sameValue(firstSet.isSubsetOf(otherSet), true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/set-is-subset-table-transition.js b/js/src/tests/test262/staging/set-is-subset-table-transition.js new file mode 100644 index 0000000000..4ef9bf407e --- /dev/null +++ b/js/src/tests/test262/staging/set-is-subset-table-transition.js @@ -0,0 +1,32 @@ +// |reftest| skip -- set-methods is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: Test isSubsetOf set method after table transition in receiver. +features: [set-methods] +---*/ + +const firstSet = new Set(); +firstSet.add(42); +firstSet.add(43); +firstSet.add(44); + +const setLike = { + size: 5, + keys() { + return [1, 2, 3, 4, 5].keys(); + }, + has(key) { + if (key == 42) { + // Cause a table transition in the receiver. + firstSet.clear(); + } + // Return true so we keep iterating the transitioned receiver. + return true; + } +}; + +assert.sameValue(firstSet.isSubsetOf(setLike), true); +assert.sameValue(firstSet.size, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/set-methods/browser.js b/js/src/tests/test262/staging/set-methods/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/set-methods/browser.js diff --git a/js/src/tests/test262/staging/set-methods/set-intersect-other-is-set-like.js b/js/src/tests/test262/staging/set-methods/set-intersect-other-is-set-like.js new file mode 100644 index 0000000000..8d31996491 --- /dev/null +++ b/js/src/tests/test262/staging/set-methods/set-intersect-other-is-set-like.js @@ -0,0 +1,34 @@ +// |reftest| skip -- set-methods is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: test intersection method when `other` is a set-like. +features: [set-methods] +includes: [compareArray.js] +---*/ + +const SetLike = { + arr: [42, 43, 45], + size: 3, + keys() { + return this.arr[Symbol.iterator](); + }, + has(key) { + return this.arr.indexOf(key) != -1; + } + }; + +const firstSet = new Set(); +firstSet.add(42); +firstSet.add(43); + +const resultSet = new Set(); +resultSet.add(42); +resultSet.add(43); + +const resultArray = Array.from(resultSet); +const intersectionArray = Array.from(firstSet.intersection(SetLike)); + +assert.compareArray(resultArray, intersectionArray); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/set-methods/set-intersection-other-is-map.js b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-map.js new file mode 100644 index 0000000000..e53671fa8a --- /dev/null +++ b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-map.js @@ -0,0 +1,27 @@ +// |reftest| skip -- set-methods is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: test intersection method when `other` is a map. +features: [set-methods] +includes: [compareArray.js] +---*/ + +const firstSet = new Set(); +firstSet.add(42); +firstSet.add(43); + +const other = new Map(); +other.set(42); +other.set(46); +other.set(47); + +const resultSet = new Set(); +resultSet.add(42); + +const resultArray = Array.from(resultSet); +const intersectionArray = Array.from(firstSet.intersection(other)); + +assert.compareArray(resultArray, intersectionArray); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/set-methods/set-intersection-other-is-set.js b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-set.js new file mode 100644 index 0000000000..b2cb955a11 --- /dev/null +++ b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-set.js @@ -0,0 +1,27 @@ +// |reftest| skip -- set-methods is not supported +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: test intersection method when `other` is a set. +features: [set-methods] +includes: [compareArray.js] +---*/ + +const firstSet = new Set(); +firstSet.add(42); +firstSet.add(43); +firstSet.add(44); + +const otherSet = new Set(); +otherSet.add(42); +otherSet.add(45); + +const resultSet = new Set(); +resultSet.add(42); + +const resultArray = Array.from(resultSet); +const intersectionArray = Array.from(firstSet.intersection(otherSet)); + +assert.compareArray(resultArray, intersectionArray); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/staging/set-methods/shell.js b/js/src/tests/test262/staging/set-methods/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/set-methods/shell.js diff --git a/js/src/tests/test262/staging/shell.js b/js/src/tests/test262/staging/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/staging/shell.js |