diff --git a/polyfill.js b/polyfill.js index 0829ffd..d5bf378 100644 --- a/polyfill.js +++ b/polyfill.js @@ -129,6 +129,27 @@ } } + /** + * @param {TypedArray} example + * @param {unknown} value + * @description convert `value` to bigint or number based on the the type of array + * @returns {bigint | number} + * @throws if one of the override methods throws. e.g. `@@toPrimitive`, `valueOf`, `toString` + */ + function typedArrayNumberConversion(example, value) { + let asNumber; + { + if (isBigIntArray(example)) { + asNumber = 0n; + } else { + asNumber = -0; // important to use `-0` and not `0` + } + // @ts-ignore : using `+=` to emulate ToBigInt or ToNumber + asNumber += value; + } + return asNumber; + } + defineArrayMethods({ toReversed() { const o = toObject(this); @@ -239,8 +260,11 @@ const o = assertTypedArray(this); const len = typedArrayLength(o); const { actualStart, actualDeleteCount, newLen } = calculateSplice({ start, deleteCount, len, values, argsCount: arguments.length }); + const convertedValues = values.map(v => { + return typedArrayNumberConversion(o, v); + }) const a = typedArrayCreate(o, newLen); - doSplice({ src: o, target: a, actualStart, actualDeleteCount, values, newLen }); + doSplice({ src: o, target: a, actualStart, actualDeleteCount, values: convertedValues, newLen }); return a; } }); @@ -269,16 +293,7 @@ const len = typedArrayLength(o); const relativeIndex = toIntegerOrInfinity(index); const actualIndex = relativeIndex < 0 ? len + relativeIndex : relativeIndex; - let asNumber; - { - if (isBigIntArray(o)) { - asNumber = 0n; - } else { - asNumber = -0; // important to use `-0` and not `0` - } - // @ts-ignore : using `+=` to emulate ToBigInt or ToNumber - asNumber += value; - } + const asNumber = typedArrayNumberConversion(o, value); if (actualIndex < 0 || actualIndex >= len) { throw new RangeError(); } diff --git a/polyfill.test.js b/polyfill.test.js index 55e79f1..5b3ab84 100644 --- a/polyfill.test.js +++ b/polyfill.test.js @@ -219,6 +219,34 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); + tape(`${TypedArray.name}.prototype.toSpliced performs type conversion early`, (t) => { + const orig = new TypedArray([1]); + const valueUserCodeWillInsert = 4; + const userCodeReturnValue = 5; + const expected = new TypedArray([valueUserCodeWillInsert, userCodeReturnValue]); + + let userCodeExecuted = false; + /** @type any */ + const val = { + valueOf() { + userCodeExecuted = true; + orig[0] = valueUserCodeWillInsert; + return userCodeReturnValue; + } + }; + + const idx = 1; + const delNum = 0; + const ins = [val]; + + const copy = orig.toSpliced(idx, delNum, ...ins); + + t.deepEqual(copy, expected); + t.notEqual(orig, copy); + t.notDeepEqual(orig, copy); + t.end(); + }); + tape(`${TypedArray.name}.prototype.with`, (t) => { const orig = new TypedArray([1, 1, 3]); const expected = new TypedArray([1, 2, 3]); @@ -306,10 +334,10 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { [ BigInt64Array, BigUint64Array -].forEach((TypedArray) => { - tape(`${TypedArray.name}.prototype.toReversed`, (t) => { - const orig = new TypedArray([3n, 2n, 1n]); - const expected = new TypedArray([1n, 2n, 3n]); +].forEach((BigIntArray) => { + tape(`${BigIntArray.name}.prototype.toReversed`, (t) => { + const orig = new BigIntArray([3n, 2n, 1n]); + const expected = new BigIntArray([1n, 2n, 3n]); const copy = orig.toReversed(); @@ -319,9 +347,9 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.toSorted`, (t) => { - const orig = new TypedArray([3n, 1n, 2n]); - const expected = new TypedArray([1n, 2n, 3n]); + tape(`${BigIntArray.name}.prototype.toSorted`, (t) => { + const orig = new BigIntArray([3n, 1n, 2n]); + const expected = new BigIntArray([1n, 2n, 3n]); const copy = orig.toSorted(); @@ -331,9 +359,9 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.toSorted(compareFn)`, (t) => { - const orig = new TypedArray([3n, 1n, 2n]); - const expected = new TypedArray([3n, 2n, 1n]); + tape(`${BigIntArray.name}.prototype.toSorted(compareFn)`, (t) => { + const orig = new BigIntArray([3n, 1n, 2n]); + const expected = new BigIntArray([3n, 2n, 1n]); function compareFn(a, b) { return a > b ? -1 : 1; } @@ -346,9 +374,9 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.toSpliced`, (t) => { - const orig = new TypedArray([1n, -1n, 0n, -1n, 4n]); - const expected = new TypedArray([1n, 2n, 3n, 4n]); + tape(`${BigIntArray.name}.prototype.toSpliced`, (t) => { + const orig = new BigIntArray([1n, -1n, 0n, -1n, 4n]); + const expected = new BigIntArray([1n, 2n, 3n, 4n]); const idx = 1; const delNum = 3; const ins = [2n, 3n]; @@ -361,9 +389,37 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.with`, (t) => { - const orig = new TypedArray([1n, 1n, 3n]); - const expected = new TypedArray([1n, 2n, 3n]); + tape(`${BigIntArray.name}.prototype.toSpliced performs type conversion early`, (t) => { + const orig = new BigIntArray([1n]); + const valueUserCodeWillInsert = 4n; + const userCodeReturnValue = 5n; + const expected = new BigIntArray([valueUserCodeWillInsert, userCodeReturnValue]); + + let userCodeExecuted = false; + /** @type any */ + const val = { + valueOf() { + userCodeExecuted = true; + orig[0] = valueUserCodeWillInsert; + return userCodeReturnValue; + } + }; + + const idx = 1; + const delNum = 0; + const ins = [val]; + + const copy = orig.toSpliced(idx, delNum, ...ins); + + t.deepEqual(copy, expected); + t.notEqual(orig, copy); + t.notDeepEqual(orig, copy); + t.end(); + }); + + tape(`${BigIntArray.name}.prototype.with`, (t) => { + const orig = new BigIntArray([1n, 1n, 3n]); + const expected = new BigIntArray([1n, 2n, 3n]); const idx = 1; const val = 2n; @@ -375,8 +431,8 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.with non bigint throws`, (t) => { - const orig = new TypedArray([1n, 2n, 2n]); + tape(`${BigIntArray.name}.prototype.with non bigint throws`, (t) => { + const orig = new BigIntArray([1n, 2n, 2n]); const idx = 3; const val = 4; @@ -388,9 +444,9 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.with negativeIndex`, (t) => { - const orig = new TypedArray([1n, 2n, 2n]); - const expected = new TypedArray([1n, 2n, 3n]); + tape(`${BigIntArray.name}.prototype.with negativeIndex`, (t) => { + const orig = new BigIntArray([1n, 2n, 2n]); + const expected = new BigIntArray([1n, 2n, 3n]); const idx = -1; const val = 3n; @@ -402,8 +458,8 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.with out of bounds`, (t) => { - const orig = new TypedArray([1n, 2n, 2n]); + tape(`${BigIntArray.name}.prototype.with out of bounds`, (t) => { + const orig = new BigIntArray([1n, 2n, 2n]); const idx = 3; const val = 4n; @@ -414,12 +470,12 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name}.prototype.with executes 'user code' before starting copy`, (t) => { - const orig = new TypedArray([1n, 2n, 3n]); + tape(`${BigIntArray.name}.prototype.with executes 'user code' before starting copy`, (t) => { + const orig = new BigIntArray([1n, 2n, 3n]); const idx = 1; const valueUserCodeWillInsert = 4n; const userCodeReturnValue = 5n; - const expected = new TypedArray([valueUserCodeWillInsert, userCodeReturnValue, 3n]); + const expected = new BigIntArray([valueUserCodeWillInsert, userCodeReturnValue, 3n]); let userCodeExecuted = false; /** @type any */ const val = { @@ -437,12 +493,12 @@ tape("Array.prototype[Symbol.unscopables]", (t) => { t.end(); }); - tape(`${TypedArray.name} does not use Symbol.species for the new methods`, (t) => { - class SubClass extends TypedArray { } + tape(`${BigIntArray.name} does not use Symbol.species for the new methods`, (t) => { + class SubClass extends BigIntArray { } function assertType(arr) { t.equal(arr instanceof SubClass, false); - t.equal(arr instanceof TypedArray, true); + t.equal(arr instanceof BigIntArray, true); } /** @type {BigInt64Array} */ diff --git a/spec.html b/spec.html index 4a12d9e..d693007 100644 --- a/spec.html +++ b/spec.html @@ -425,6 +425,11 @@

%TypedArray%.prototype.toSpliced ( _start_, _deleteCount_, ..._items_ )

1. Else if _relativeStart_ < 0, let _actualStart_ be max(_len_ + _relativeStart_, 0). 1. Else, let _actualStart_ be min(_relativeStart_, _len_). 1. Let _insertCount_ be the number of elements in _items_. + 1. Let _convertedItems_ be a new empty List. + 1. For each element _E_ of _items_, do + 1. If _O_.[[ContentType]] is ~BigInt~, let _convertedValue_ be ? ToBigInt(_E_). + 1. Else, let _convertedValue_ be ? ToNumber(_E_). + 1. Append _convertedValue_ as the last element of _convertedItems_. 1. If _start_ is not present, then 1. Let _actualDeleteCount_ be 0. 1. Else if _deleteCount_ is not present, then @@ -441,9 +446,9 @@

%TypedArray%.prototype.toSpliced ( _start_, _deleteCount_, ..._items_ )

1. Let _iValue_ be ! Get(_src_, _Pi_). 1. Perform ! Set(_target_, _Pi_, _iValue_, *true*). 1. Set _i_ to _i_ + 1. - 1. For each element _E_ of _items_, do + 1. For each element _E_ of _convertedItems_, do 1. Let _Pi_ be ! ToString(𝔽(_i_)). - 1. [id="step-typedarray-tospliced-set"] Perform ? Set(_A_, _Pi_, _E_, *true*). + 1. Perform ! Set(_A_, _Pi_, _E_, *true*). 1. Set _i_ to _i_ + 1. 1. Repeat, while _r_ < _newLen_, 1. Let _Pi_ be ! ToString(𝔽(_i_)). @@ -454,9 +459,6 @@

%TypedArray%.prototype.toSpliced ( _start_, _deleteCount_, ..._items_ )

1. Set _r_ to _r_ + 1. 1. Return _A_. - - Step may return an abrupt completion because _E_ is a value of any ECMAScript language type and is passed to ToBigInt or ToNumber. -