diff --git a/src/builtins/array-at.tq b/src/builtins/array-at.tq index b1707745e8..f323032a10 100644 --- a/src/builtins/array-at.tq +++ b/src/builtins/array-at.tq @@ -3,25 +3,39 @@ // found in the LICENSE file. namespace array { +macro ConvertRelativeIndex(index: Number, length: Number): + Number labels OutOfBoundsLow, OutOfBoundsHigh { + const relativeIndex = index >= 0 ? index : length + index; + if (relativeIndex < 0) goto OutOfBoundsLow; + if (relativeIndex >= length) goto OutOfBoundsHigh; + return relativeIndex; +} + // https://tc39.es/proposal-item-method/#sec-array.prototype.at transitioning javascript builtin ArrayPrototypeAt( js-implicit context: NativeContext, receiver: JSAny)(index: JSAny): JSAny { // 1. Let O be ? ToObject(this value). const o = ToObject_Inline(context, receiver); + // 2. Let len be ? LengthOfArrayLike(O). const len = GetLengthProperty(o); - // 3. Let relativeIndex be ? ToInteger(index). - const relativeIndex = ToInteger_Inline(index); - // 4. If relativeIndex ≥ 0, then - // a. Let k be relativeIndex. - // 5. Else, - // a. Let k be len + relativeIndex. - const k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; - // 6. If k < 0 or k ≥ len, then return undefined. - if (k < 0 || k >= len) { + + try { + // 3. Let relativeIndex be ? ToInteger(index). + const relativeIndex = ToInteger_Inline(index); + + // 4. If relativeIndex ≥ 0, then + // a. Let k be relativeIndex. + // 5. Else, + // a. Let k be len + relativeIndex. + const k = ConvertRelativeIndex(relativeIndex, len) otherwise OutOfBounds, + OutOfBounds; + + // 7. Return ? Get(O, ! ToString(k)). + return GetProperty(o, k); + } label OutOfBounds { + // 6. If k < 0 or k ≥ len, then return undefined. return Undefined; } - // 7. Return ? Get(O, ! ToString(k)). - return GetProperty(o, k); } } diff --git a/src/builtins/array-copywithin.tq b/src/builtins/array-copywithin.tq index 3d2a456efb..afe10968a0 100644 --- a/src/builtins/array-copywithin.tq +++ b/src/builtins/array-copywithin.tq @@ -3,8 +3,15 @@ // found in the LICENSE file. namespace array { -macro ConvertToRelativeIndex(index: Number, length: Number): Number { - return index < 0 ? Max(index + length, 0) : Min(index, length); +macro ConvertAndClampRelativeIndex(index: Number, length: Number): Number { + try { + return ConvertRelativeIndex(index, length) otherwise OutOfBoundsLow, + OutOfBoundsHigh; + } label OutOfBoundsLow { + return 0; + } label OutOfBoundsHigh { + return length; + } } // https://tc39.github.io/ecma262/#sec-array.prototype.copyWithin @@ -21,14 +28,14 @@ transitioning javascript builtin ArrayPrototypeCopyWithin( // 4. If relativeTarget < 0, let to be max((len + relativeTarget), 0); // else let to be min(relativeTarget, len). - let to: Number = ConvertToRelativeIndex(relativeTarget, length); + let to: Number = ConvertAndClampRelativeIndex(relativeTarget, length); // 5. Let relativeStart be ? ToInteger(start). const relativeStart: Number = ToInteger_Inline(arguments[1]); // 6. If relativeStart < 0, let from be max((len + relativeStart), 0); // else let from be min(relativeStart, len). - let from: Number = ConvertToRelativeIndex(relativeStart, length); + let from: Number = ConvertAndClampRelativeIndex(relativeStart, length); // 7. If end is undefined, let relativeEnd be len; // else let relativeEnd be ? ToInteger(end). @@ -39,7 +46,7 @@ transitioning javascript builtin ArrayPrototypeCopyWithin( // 8. If relativeEnd < 0, let final be max((len + relativeEnd), 0); // else let final be min(relativeEnd, len). - const final: Number = ConvertToRelativeIndex(relativeEnd, length); + const final: Number = ConvertAndClampRelativeIndex(relativeEnd, length); // 9. Let count be min(final-from, len-to). let count: Number = Min(final - from, length - to); diff --git a/src/builtins/base.tq b/src/builtins/base.tq index ca11030e98..e95e37de69 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.tq @@ -1643,44 +1643,65 @@ extern macro IsOneByteStringInstanceType(InstanceType): bool; // After converting an index to an integer, calculate a relative index: // return index < 0 ? max(length + index, 0) : min(index, length) @export -transitioning macro ConvertToRelativeIndex(implicit context: Context)( +transitioning macro ConvertAndClampRelativeIndex(implicit context: Context)( index: JSAny, length: uintptr): uintptr { const indexNumber: Number = ToInteger_Inline(index); - return ConvertToRelativeIndex(indexNumber, length); + return ConvertAndClampRelativeIndex(indexNumber, length); } // Calculate a relative index: // return index < 0 ? max(length + index, 0) : min(index, length) @export -macro ConvertToRelativeIndex(indexNumber: Number, length: uintptr): uintptr { +macro ConvertAndClampRelativeIndex(indexNumber: Number, length: uintptr): + uintptr { + try { + return ConvertRelativeIndex(indexNumber, length) otherwise OutOfBoundsLow, + OutOfBoundsHigh; + } label OutOfBoundsLow { + return 0; + } label OutOfBoundsHigh { + return length; + } +} + +// Calculate a relative index with explicit out-of-bounds labels. +macro ConvertRelativeIndex(indexNumber: Number, length: uintptr): + uintptr labels OutOfBoundsLow, OutOfBoundsHigh { typeswitch (indexNumber) { case (indexSmi: Smi): { const indexIntPtr: intptr = Convert(indexSmi); // The logic is implemented using unsigned types. if (indexIntPtr < 0) { const relativeIndex: uintptr = Unsigned(indexIntPtr) + length; - return relativeIndex < length ? relativeIndex : 0; + if (relativeIndex < length) return relativeIndex; + goto OutOfBoundsLow; } else { const relativeIndex: uintptr = Unsigned(indexIntPtr); - return relativeIndex < length ? relativeIndex : length; + if (relativeIndex < length) return relativeIndex; + goto OutOfBoundsHigh; } } case (indexHeapNumber: HeapNumber): { dcheck(IsNumberNormalized(indexHeapNumber)); const indexDouble: float64 = Convert(indexHeapNumber); - // NaNs must already be handled by ConvertToRelativeIndex() version + // NaNs must already be handled by ConvertAndClampRelativeIndex() version // above accepting JSAny indices. dcheck(!Float64IsNaN(indexDouble)); const lengthDouble: float64 = Convert(length); dcheck(lengthDouble <= kMaxSafeInteger); if (indexDouble < 0) { const relativeIndex: float64 = lengthDouble + indexDouble; - return relativeIndex > 0 ? ChangeFloat64ToUintPtr(relativeIndex) : 0; + if (relativeIndex > 0) { + return ChangeFloat64ToUintPtr(relativeIndex); + } + goto OutOfBoundsLow; } else { - return ChangeFloat64ToUintPtr( - indexDouble < lengthDouble ? indexDouble : lengthDouble); + if (indexDouble < lengthDouble) { + return ChangeFloat64ToUintPtr(indexDouble); + } + goto OutOfBoundsHigh; } } } diff --git a/src/builtins/string-slice.tq b/src/builtins/string-slice.tq index 71442a28fa..7a953418e7 100644 --- a/src/builtins/string-slice.tq +++ b/src/builtins/string-slice.tq @@ -17,13 +17,13 @@ transitioning javascript builtin StringPrototypeSlice( // Convert {start} to a relative index. const arg0 = arguments[0]; const start: uintptr = - arg0 != Undefined ? ConvertToRelativeIndex(arg0, length) : 0; + arg0 != Undefined ? ConvertAndClampRelativeIndex(arg0, length) : 0; // 5. If end is undefined, let intEnd be len; // else Convert {end} to a relative index. const arg1 = arguments[1]; const end: uintptr = - arg1 != Undefined ? ConvertToRelativeIndex(arg1, length) : length; + arg1 != Undefined ? ConvertAndClampRelativeIndex(arg1, length) : length; if (end <= start) { return kEmptyString; diff --git a/src/builtins/string-substr.tq b/src/builtins/string-substr.tq index 9c0f63d085..81ea82ab7e 100644 --- a/src/builtins/string-substr.tq +++ b/src/builtins/string-substr.tq @@ -20,7 +20,7 @@ transitioning javascript builtin StringPrototypeSubstr( // 6. If intStart < 0, set intStart to max(size + intStart, 0). const start = arguments[0]; const initStart: uintptr = - start != Undefined ? ConvertToRelativeIndex(start, size) : 0; + start != Undefined ? ConvertAndClampRelativeIndex(start, size) : 0; // 4. If length is undefined, // let end be +∞; otherwise let end be ? ToInteger(length). diff --git a/src/builtins/typed-array-slice.tq b/src/builtins/typed-array-slice.tq index 356bf36d4c..a1dba47bac 100644 --- a/src/builtins/typed-array-slice.tq +++ b/src/builtins/typed-array-slice.tq @@ -76,7 +76,7 @@ transitioning javascript builtin TypedArrayPrototypeSlice( // else let k be min(relativeStart, len). const start = arguments[0]; const k: uintptr = - start != Undefined ? ConvertToRelativeIndex(start, len) : 0; + start != Undefined ? ConvertAndClampRelativeIndex(start, len) : 0; // 6. If end is undefined, let relativeEnd be len; // else let relativeEnd be ? ToInteger(end). @@ -84,7 +84,7 @@ transitioning javascript builtin TypedArrayPrototypeSlice( // else let final be min(relativeEnd, len). const end = arguments[1]; let final: uintptr = - end != Undefined ? ConvertToRelativeIndex(end, len) : len; + end != Undefined ? ConvertAndClampRelativeIndex(end, len) : len; // 8. Let count be max(final - k, 0). let count: uintptr = Unsigned(IntPtrMax(Signed(final - k), 0)); diff --git a/src/builtins/typed-array-subarray.tq b/src/builtins/typed-array-subarray.tq index 85ff74dc9c..1d815e84ea 100644 --- a/src/builtins/typed-array-subarray.tq +++ b/src/builtins/typed-array-subarray.tq @@ -35,15 +35,16 @@ transitioning javascript builtin TypedArrayPrototypeSubArray( // srcLength). const arg0 = arguments[0]; const begin: uintptr = - arg0 != Undefined ? ConvertToRelativeIndex(arg0, srcLength) : 0; + arg0 != Undefined ? ConvertAndClampRelativeIndex(arg0, srcLength) : 0; // 10. If end is undefined, let relativeEnd be srcLength; // else, let relativeEnd be ? ToInteger(end). // 11. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), // 0); else let endIndex be min(relativeEnd, srcLength). const arg1 = arguments[1]; - const end: uintptr = - arg1 != Undefined ? ConvertToRelativeIndex(arg1, srcLength) : srcLength; + const end: uintptr = arg1 != Undefined ? + ConvertAndClampRelativeIndex(arg1, srcLength) : + srcLength; // 12. Let newLength be max(endIndex - beginIndex, 0). const newLength: uintptr = Unsigned(IntPtrMax(Signed(end - begin), 0)); diff --git a/test/cctest/test-code-stub-assembler.cc b/test/cctest/test-code-stub-assembler.cc index 61d18fb2b7..6694fb6431 100644 --- a/test/cctest/test-code-stub-assembler.cc +++ b/test/cctest/test-code-stub-assembler.cc @@ -318,7 +318,7 @@ TEST(IsValidPositiveSmi) { #endif } -TEST(ConvertToRelativeIndex) { +TEST(ConvertAndClampRelativeIndex) { Isolate* isolate(CcTest::InitIsolateOnce()); const int kNumParams = 3; @@ -335,7 +335,7 @@ TEST(ConvertToRelativeIndex) { TNode expected = m.ChangeUintPtrNumberToUintPtr(expected_relative_index); - TNode result = m.ConvertToRelativeIndex(index, length); + TNode result = m.ConvertAndClampRelativeIndex(index, length); m.Return(m.SelectBooleanConstant(m.WordEqual(result, expected))); }