[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: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83147}
This commit is contained in:
Shu-yu Guo 2022-09-09 14:52:34 -07:00 committed by V8 LUCI CQ
parent 002ac4168c
commit 36559d91ca
4 changed files with 60 additions and 38 deletions

View File

@ -478,12 +478,12 @@ transitioning macro TypedArraySpeciesCreateByLength(implicit context: Context)(
transitioning macro TypedArraySpeciesCreateByBuffer(implicit context: Context)(
methodName: constexpr string, exemplar: JSTypedArray, buffer: JSArrayBuffer,
beginByteOffset: uintptr, newLength: uintptr): JSTypedArray {
beginByteOffset: uintptr, newLength: NumberOrUndefined): JSTypedArray {
const numArgs: constexpr int31 = 3;
// TODO(v8:4153): pass length further as uintptr.
const typedArray: JSTypedArray = TypedArraySpeciesCreate(
methodName, numArgs, exemplar, buffer, Convert<Number>(beginByteOffset),
Convert<Number>(newLength));
newLength);
return typedArray;
}

View File

@ -10,16 +10,18 @@ transitioning javascript builtin TypedArrayPrototypeSubArray(
const methodName: constexpr string = '%TypedArray%.prototype.subarray';
// 1. Let O be the this value.
// 3. If O does not have a [[TypedArrayName]] internal slot, throw a
// TypeError exception.
// 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
const source = Cast<JSTypedArray>(receiver)
otherwise ThrowTypeError(
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);
// 6. Let srcLength be O.[[ArrayLength]].
// 5. Let getSrcBufferByteLength be
// MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
// 6. Let srcLength be IntegerIndexedObjectLength(O, getSrcBufferByteLength).
let srcLength: uintptr;
try {
srcLength = LoadJSTypedArrayLengthAndCheckDetached(source)
@ -29,41 +31,57 @@ transitioning javascript builtin TypedArrayPrototypeSubArray(
srcLength = 0;
}
// 8. Let relativeBegin be ? ToInteger(begin).
// 9. If relativeBegin < 0, let beginIndex be max((srcLength +
// relativeBegin), 0); else let beginIndex be min(relativeBegin,
// srcLength).
// 8. Let relativeBegin be ? ToIntegerOrInfinity(begin).
// 9. If relativeBegin is -∞, let beginIndex be 0.
// 10. Else if relativeBegin < 0, let beginIndex be max(srcLength +
// relativeBegin, 0).
// 11. Else, let beginIndex be min(relativeBegin, srcLength).
const arg0 = arguments[0];
const begin: uintptr =
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).
// 12. If O.[[ArrayLength]] is auto and end is undefined, then
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) :
srcLength;
// 12. Let newLength be max(endIndex - beginIndex, 0).
const newLength: uintptr = Unsigned(IntPtrMax(Signed(end - begin), 0));
// e. Let newLength be max(endIndex - beginIndex, 0).
newLength = Convert<Number>(Unsigned(IntPtrMax(Signed(end - begin), 0)));
}
// 13. Let constructorName be the String value of O.[[TypedArrayName]].
// 14. Let elementSize be the Number value of the Element Size value
// 14. Let constructorName be the String value of O.[[TypedArrayName]].
// 15. Let elementSize be the Number value of the Element Size value
// specified in Table 52 for constructorName.
const elementsInfo = typed_array::GetTypedArrayElementsInfo(source);
// 15. Let srcByteOffset be O.[[ByteOffset]].
// 16. Let srcByteOffset be O.[[ByteOffset]].
const srcByteOffset: uintptr = source.byte_offset;
// 16. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
// 17. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
const beginByteOffset =
srcByteOffset + elementsInfo.CalculateByteLength(begin)
otherwise ThrowRangeError(MessageTemplate::kInvalidArrayBufferLength);
// 17. Let argumentsList be « buffer, beginByteOffset, newLength ».
// 18. Return ? TypedArraySpeciesCreate(O, argumentsList).
// 18. If newLength is undefined, then
// a. Let argumentsList be « buffer, 𝔽(beginByteOffset) ».
// 19. Else,
// a. Let argumentsList be « buffer, 𝔽(beginByteOffset), 𝔽(newLength) ».
// 20. Return ? TypedArraySpeciesCreate(O, argumentsList).
return TypedArraySpeciesCreateByBuffer(
methodName, source, buffer, beginByteOffset, newLength);
}

View File

@ -3437,11 +3437,11 @@ Reverse(ArrayReverseHelper);
assertEquals(4, fixedLengthSubFull.length);
assertEquals(2, fixedLengthWithOffsetSubFull.length);
// TODO(v8:11111): Are subarrays of length-tracking TAs also
// length-tracking? See
// https://github.com/tc39/proposal-resizablearraybuffer/issues/91
assertEquals(4, lengthTrackingSubFull.length);
assertEquals(2, lengthTrackingWithOffsetSubFull.length);
// Subarrays of length-tracking TAs that don't pass an explicit end argument
// are also length-tracking.
assertEquals(lengthTracking.length, lengthTrackingSubFull.length);
assertEquals(lengthTrackingWithOffset.length,
lengthTrackingWithOffsetSubFull.length);
}
})();
@ -3479,7 +3479,8 @@ Reverse(ArrayReverseHelper);
const evil = { valueOf: () => { gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
return 0;}};
assertEquals([0, 2, 4, 6], ToNumbers(lengthTracking.subarray(evil)));
assertEquals([0, 2, 4, 6], ToNumbers(
lengthTracking.subarray(evil, lengthTracking.length)));
}
})();

View File

@ -6682,11 +6682,11 @@ Reverse(ArrayReverseHelper, false);
assertEquals(4, fixedLengthSubFull.length);
assertEquals(2, fixedLengthWithOffsetSubFull.length);
// TODO(v8:11111): Are subarrays of length-tracking TAs also
// length-tracking? See
// https://github.com/tc39/proposal-resizablearraybuffer/issues/91
assertEquals(4, lengthTrackingSubFull.length);
assertEquals(2, lengthTrackingWithOffsetSubFull.length);
// Subarrays of length-tracking TAs that don't pass an explicit end argument
// are also length-tracking.
assertEquals(lengthTracking.length, lengthTrackingSubFull.length);
assertEquals(lengthTrackingWithOffset.length,
lengthTrackingWithOffsetSubFull.length);
}
})();
@ -6779,7 +6779,9 @@ Reverse(ArrayReverseHelper, false);
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
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
@ -6874,7 +6876,8 @@ Reverse(ArrayReverseHelper, false);
const evil = { valueOf: () => { rab.resize(6 * ctor.BYTES_PER_ELEMENT);
return 0;}};
assertEquals([0, 2, 4, 6], ToNumbers(lengthTracking.subarray(evil)));
assertEquals([0, 2, 4, 6], ToNumbers(
lengthTracking.subarray(evil, lengthTracking.length)));
}
})();