Commit 36559d91 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[rab/gsab] Fix length-tracking handling in TA#subarray

The normative change in
https://github.com/tc39/proposal-resizablearraybuffer/pull/93 changed
the behavior of TypedArray.prototype.subarray(begin, end) such that if
the receiver is a length-tracking TA and end is undefined, the result
TypedArray is also length-tracking.

This change reached consensus in the March 2022 TC39.

Bug: v8:11111
Change-Id: If1a84cc3134f3ce8046196d6cc36683b6996dec0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3888382
Commit-Queue: Marja Hölttä <marja@chromium.org>
Auto-Submit: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83147}
parent 002ac416
...@@ -478,12 +478,12 @@ transitioning macro TypedArraySpeciesCreateByLength(implicit context: Context)( ...@@ -478,12 +478,12 @@ transitioning macro TypedArraySpeciesCreateByLength(implicit context: Context)(
transitioning macro TypedArraySpeciesCreateByBuffer(implicit context: Context)( transitioning macro TypedArraySpeciesCreateByBuffer(implicit context: Context)(
methodName: constexpr string, exemplar: JSTypedArray, buffer: JSArrayBuffer, methodName: constexpr string, exemplar: JSTypedArray, buffer: JSArrayBuffer,
beginByteOffset: uintptr, newLength: uintptr): JSTypedArray { beginByteOffset: uintptr, newLength: NumberOrUndefined): JSTypedArray {
const numArgs: constexpr int31 = 3; const numArgs: constexpr int31 = 3;
// TODO(v8:4153): pass length further as uintptr. // TODO(v8:4153): pass length further as uintptr.
const typedArray: JSTypedArray = TypedArraySpeciesCreate( const typedArray: JSTypedArray = TypedArraySpeciesCreate(
methodName, numArgs, exemplar, buffer, Convert<Number>(beginByteOffset), methodName, numArgs, exemplar, buffer, Convert<Number>(beginByteOffset),
Convert<Number>(newLength)); newLength);
return typedArray; return typedArray;
} }
......
...@@ -10,16 +10,18 @@ transitioning javascript builtin TypedArrayPrototypeSubArray( ...@@ -10,16 +10,18 @@ transitioning javascript builtin TypedArrayPrototypeSubArray(
const methodName: constexpr string = '%TypedArray%.prototype.subarray'; const methodName: constexpr string = '%TypedArray%.prototype.subarray';
// 1. Let O be the this value. // 1. Let O be the this value.
// 3. If O does not have a [[TypedArrayName]] internal slot, throw a // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
// TypeError exception.
const source = Cast<JSTypedArray>(receiver) const source = Cast<JSTypedArray>(receiver)
otherwise ThrowTypeError( otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName); MessageTemplate::kIncompatibleMethodReceiver, methodName);
// 5. Let buffer be O.[[ViewedArrayBuffer]]. // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
// 4. Let buffer be O.[[ViewedArrayBuffer]].
const buffer = typed_array::GetTypedArrayBuffer(source); const buffer = typed_array::GetTypedArrayBuffer(source);
// 6. Let srcLength be O.[[ArrayLength]]. // 5. Let getSrcBufferByteLength be
// MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
// 6. Let srcLength be IntegerIndexedObjectLength(O, getSrcBufferByteLength).
let srcLength: uintptr; let srcLength: uintptr;
try { try {
srcLength = LoadJSTypedArrayLengthAndCheckDetached(source) srcLength = LoadJSTypedArrayLengthAndCheckDetached(source)
...@@ -29,41 +31,57 @@ transitioning javascript builtin TypedArrayPrototypeSubArray( ...@@ -29,41 +31,57 @@ transitioning javascript builtin TypedArrayPrototypeSubArray(
srcLength = 0; srcLength = 0;
} }
// 8. Let relativeBegin be ? ToInteger(begin). // 8. Let relativeBegin be ? ToIntegerOrInfinity(begin).
// 9. If relativeBegin < 0, let beginIndex be max((srcLength + // 9. If relativeBegin is -∞, let beginIndex be 0.
// relativeBegin), 0); else let beginIndex be min(relativeBegin, // 10. Else if relativeBegin < 0, let beginIndex be max(srcLength +
// srcLength). // relativeBegin, 0).
// 11. Else, let beginIndex be min(relativeBegin, srcLength).
const arg0 = arguments[0]; const arg0 = arguments[0];
const begin: uintptr = const begin: uintptr =
arg0 != Undefined ? ConvertAndClampRelativeIndex(arg0, srcLength) : 0; arg0 != Undefined ? ConvertAndClampRelativeIndex(arg0, srcLength) : 0;
// 10. If end is undefined, let relativeEnd be srcLength; // 12. If O.[[ArrayLength]] is auto and end is undefined, then
// 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 arg1 = arguments[1];
const end: uintptr = arg1 != Undefined ? const endIsDefined = arg1 != Undefined;
let newLength: NumberOrUndefined;
if (IsLengthTrackingJSArrayBufferView(source) && !endIsDefined) {
// a. Let newLength be undefined.
newLength = Undefined;
} else {
// 13. Else,
// a. If end is undefined, let relativeEnd be srcLength; else let
// relativeEnd be ? ToIntegerOrInfinity(end).
// b. If relativeEnd is -∞, let endIndex be 0.
// c. Else if relativeEnd < 0, let endIndex be max(srcLength +
// relativeEnd, 0).
// d. Else, let endIndex be min(relativeEnd, srcLength).
const end: uintptr = endIsDefined ?
ConvertAndClampRelativeIndex(arg1, srcLength) : ConvertAndClampRelativeIndex(arg1, srcLength) :
srcLength; srcLength;
// 12. Let newLength be max(endIndex - beginIndex, 0). // e. Let newLength be max(endIndex - beginIndex, 0).
const newLength: uintptr = Unsigned(IntPtrMax(Signed(end - begin), 0)); newLength = Convert<Number>(Unsigned(IntPtrMax(Signed(end - begin), 0)));
}
// 13. Let constructorName be the String value of O.[[TypedArrayName]]. // 14. Let constructorName be the String value of O.[[TypedArrayName]].
// 14. Let elementSize be the Number value of the Element Size value // 15. Let elementSize be the Number value of the Element Size value
// specified in Table 52 for constructorName. // specified in Table 52 for constructorName.
const elementsInfo = typed_array::GetTypedArrayElementsInfo(source); const elementsInfo = typed_array::GetTypedArrayElementsInfo(source);
// 15. Let srcByteOffset be O.[[ByteOffset]]. // 16. Let srcByteOffset be O.[[ByteOffset]].
const srcByteOffset: uintptr = source.byte_offset; const srcByteOffset: uintptr = source.byte_offset;
// 16. Let beginByteOffset be srcByteOffset + beginIndex × elementSize. // 17. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
const beginByteOffset = const beginByteOffset =
srcByteOffset + elementsInfo.CalculateByteLength(begin) srcByteOffset + elementsInfo.CalculateByteLength(begin)
otherwise ThrowRangeError(MessageTemplate::kInvalidArrayBufferLength); otherwise ThrowRangeError(MessageTemplate::kInvalidArrayBufferLength);
// 17. Let argumentsList be « buffer, beginByteOffset, newLength ». // 18. If newLength is undefined, then
// 18. Return ? TypedArraySpeciesCreate(O, argumentsList). // a. Let argumentsList be « buffer, 𝔽(beginByteOffset) ».
// 19. Else,
// a. Let argumentsList be « buffer, 𝔽(beginByteOffset), 𝔽(newLength) ».
// 20. Return ? TypedArraySpeciesCreate(O, argumentsList).
return TypedArraySpeciesCreateByBuffer( return TypedArraySpeciesCreateByBuffer(
methodName, source, buffer, beginByteOffset, newLength); methodName, source, buffer, beginByteOffset, newLength);
} }
......
...@@ -3437,11 +3437,11 @@ Reverse(ArrayReverseHelper); ...@@ -3437,11 +3437,11 @@ Reverse(ArrayReverseHelper);
assertEquals(4, fixedLengthSubFull.length); assertEquals(4, fixedLengthSubFull.length);
assertEquals(2, fixedLengthWithOffsetSubFull.length); assertEquals(2, fixedLengthWithOffsetSubFull.length);
// TODO(v8:11111): Are subarrays of length-tracking TAs also // Subarrays of length-tracking TAs that don't pass an explicit end argument
// length-tracking? See // are also length-tracking.
// https://github.com/tc39/proposal-resizablearraybuffer/issues/91 assertEquals(lengthTracking.length, lengthTrackingSubFull.length);
assertEquals(4, lengthTrackingSubFull.length); assertEquals(lengthTrackingWithOffset.length,
assertEquals(2, lengthTrackingWithOffsetSubFull.length); lengthTrackingWithOffsetSubFull.length);
} }
})(); })();
...@@ -3479,7 +3479,8 @@ Reverse(ArrayReverseHelper); ...@@ -3479,7 +3479,8 @@ Reverse(ArrayReverseHelper);
const evil = { valueOf: () => { gsab.grow(6 * ctor.BYTES_PER_ELEMENT); const evil = { valueOf: () => { gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
return 0;}}; return 0;}};
assertEquals([0, 2, 4, 6], ToNumbers(lengthTracking.subarray(evil))); assertEquals([0, 2, 4, 6], ToNumbers(
lengthTracking.subarray(evil, lengthTracking.length)));
} }
})(); })();
......
...@@ -6682,11 +6682,11 @@ Reverse(ArrayReverseHelper, false); ...@@ -6682,11 +6682,11 @@ Reverse(ArrayReverseHelper, false);
assertEquals(4, fixedLengthSubFull.length); assertEquals(4, fixedLengthSubFull.length);
assertEquals(2, fixedLengthWithOffsetSubFull.length); assertEquals(2, fixedLengthWithOffsetSubFull.length);
// TODO(v8:11111): Are subarrays of length-tracking TAs also // Subarrays of length-tracking TAs that don't pass an explicit end argument
// length-tracking? See // are also length-tracking.
// https://github.com/tc39/proposal-resizablearraybuffer/issues/91 assertEquals(lengthTracking.length, lengthTrackingSubFull.length);
assertEquals(4, lengthTrackingSubFull.length); assertEquals(lengthTrackingWithOffset.length,
assertEquals(2, lengthTrackingWithOffsetSubFull.length); lengthTrackingWithOffsetSubFull.length);
} }
})(); })();
...@@ -6779,7 +6779,9 @@ Reverse(ArrayReverseHelper, false); ...@@ -6779,7 +6779,9 @@ Reverse(ArrayReverseHelper, false);
rab.resize(2 * ctor.BYTES_PER_ELEMENT); rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 0; return 0;
}}; }};
assertThrows(() => { lengthTracking.subarray(evil); }); assertThrows(() => {
lengthTracking.subarray(evil, lengthTracking.length);
});
} }
// Like the previous test, but now we construct a smaller subarray and it // Like the previous test, but now we construct a smaller subarray and it
...@@ -6874,7 +6876,8 @@ Reverse(ArrayReverseHelper, false); ...@@ -6874,7 +6876,8 @@ Reverse(ArrayReverseHelper, false);
const evil = { valueOf: () => { rab.resize(6 * ctor.BYTES_PER_ELEMENT); const evil = { valueOf: () => { rab.resize(6 * ctor.BYTES_PER_ELEMENT);
return 0;}}; return 0;}};
assertEquals([0, 2, 4, 6], ToNumbers(lengthTracking.subarray(evil))); assertEquals([0, 2, 4, 6], ToNumbers(
lengthTracking.subarray(evil, lengthTracking.length)));
} }
})(); })();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment