Commit 908e7ac7 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[typedarray] Remove per-iteration detach check in TypedArray.prototype.set

Bug: v8:12750, v8:11111
Change-Id: I3e9947ec8e2883364178b497a49299a3a96332e4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3569879Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79792}
parent 636d2818
...@@ -114,18 +114,16 @@ TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)( ...@@ -114,18 +114,16 @@ TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
target: JSTypedArray, targetLength: uintptr, arrayArg: JSAny, target: JSTypedArray, targetLength: uintptr, arrayArg: JSAny,
targetOffset: uintptr, targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds { targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
// Steps 3-7 are not observable, do them later. // 4. Let src be ? ToObject(source).
// 8. Let src be ? ToObject(source).
const src: JSReceiver = ToObject_Inline(context, arrayArg); const src: JSReceiver = ToObject_Inline(context, arrayArg);
// 9. Let srcLength be ? LengthOfArrayLike(src). // 5. Let srcLength be ? LengthOfArrayLike(src).
const srcLengthNum: Number = GetLengthProperty(src); const srcLengthNum: Number = GetLengthProperty(src);
// 10. If targetOffset is +∞, throw a RangeError exception. // 6. If targetOffset is +∞, throw a RangeError exception.
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds; if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 11. If srcLength + targetOffset > targetLength, throw a RangeError // 7. If srcLength + targetOffset > targetLength, throw a RangeError
// exception. // exception.
const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum) const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum)
otherwise IfOffsetOutOfBounds; otherwise IfOffsetOutOfBounds;
...@@ -136,12 +134,6 @@ TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)( ...@@ -136,12 +134,6 @@ TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
// to do with the empty source array. // to do with the empty source array.
if (srcLength == 0) return; if (srcLength == 0) return;
// 4. Let targetName be the String value of target.[[TypedArrayName]].
// 5. Let targetElementSize be the Element Size value specified in
// Table 69 for targetName.
// 6. Let targetType be the Element Type value in Table 69 for
// targetName.
try { try {
// BigInt typed arrays are not handled by // BigInt typed arrays are not handled by
// CopyFastNumberJSArrayElementsToTypedArray. // CopyFastNumberJSArrayElementsToTypedArray.
......
...@@ -3822,42 +3822,52 @@ class TypedElementsAccessor ...@@ -3822,42 +3822,52 @@ class TypedElementsAccessor
return false; return false;
} }
// ES#sec-settypedarrayfromarraylike
static Object CopyElementsHandleSlow(Handle<Object> source, static Object CopyElementsHandleSlow(Handle<Object> source,
Handle<JSTypedArray> destination, Handle<JSTypedArray> destination,
size_t length, size_t offset) { size_t length, size_t offset) {
Isolate* isolate = destination->GetIsolate(); Isolate* isolate = destination->GetIsolate();
// 8. Let k be 0.
// 9. Repeat, while k < srcLength,
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
Handle<Object> elem; Handle<Object> elem;
// a. Let Pk be ! ToString(𝔽(k)).
// b. Let value be ? Get(src, Pk).
LookupIterator it(isolate, source, i); LookupIterator it(isolate, source, i);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
Object::GetProperty(&it)); Object::GetProperty(&it));
// c. Let targetIndex be 𝔽(targetOffset + k).
// d. Perform ? IntegerIndexedElementSet(target, targetIndex, value).
//
// Rest of loop body inlines ES#IntegerIndexedElementSet
if (IsBigIntTypedArrayElementsKind(Kind)) { if (IsBigIntTypedArrayElementsKind(Kind)) {
// 1. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
BigInt::FromObject(isolate, elem)); BigInt::FromObject(isolate, elem));
} else { } else {
// 2. Otherwise, let numValue be ? ToNumber(value).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
Object::ToNumber(isolate, elem)); Object::ToNumber(isolate, elem));
} }
// 3. If IsValidIntegerIndex(O, index) is true, then
// a. Let offset be O.[[ByteOffset]].
// b. Let elementSize be TypedArrayElementSize(O).
// c. Let indexedPosition be (ℝ(index) × elementSize) + offset.
// d. Let elementType be TypedArrayElementType(O).
// e. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]],
// indexedPosition, elementType, numValue, true, Unordered).
bool out_of_bounds = false; bool out_of_bounds = false;
size_t new_length = destination->GetLengthOrOutOfBounds(out_of_bounds); size_t new_length = destination->GetLengthOrOutOfBounds(out_of_bounds);
if (V8_UNLIKELY(out_of_bounds || destination->WasDetached())) { if (V8_UNLIKELY(out_of_bounds || destination->WasDetached() ||
const char* op = "set"; new_length <= offset + i)) {
const MessageTemplate message = MessageTemplate::kDetachedOperation;
Handle<String> operation =
isolate->factory()->NewStringFromAsciiChecked(op);
THROW_NEW_ERROR_RETURN_FAILURE(isolate,
NewTypeError(message, operation));
}
if (V8_UNLIKELY(new_length <= offset + i)) {
// Proceed with the loop so that we call get getters for the source even // Proceed with the loop so that we call get getters for the source even
// though we don't set the values in the target. // though we don't set the values in the target.
// TODO(v8:11111): Maybe change this, depending on how
// https://github.com/tc39/proposal-resizablearraybuffer/issues/86 is
// resolved.
continue; continue;
} }
SetImpl(destination, InternalIndex(offset + i), *elem); SetImpl(destination, InternalIndex(offset + i), *elem);
// e. Set k to k + 1.
} }
// 10. Return unused.
return *isolate->factory()->undefined_value(); return *isolate->factory()->undefined_value();
} }
......
...@@ -642,7 +642,7 @@ function TestTypedArraySet() { ...@@ -642,7 +642,7 @@ function TestTypedArraySet() {
return 1; return 1;
} }
}; };
assertThrows(() => a111.set(evilarr), TypeError); a111.set(evilarr);
assertEquals(true, detached); assertEquals(true, detached);
// Check if the target is a typed array before converting offset to integer // Check if the target is a typed array before converting offset to integer
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
let ta = new Int32Array(10);
assertThrows(() => {
ta.set({
get length() {
%ArrayBufferDetach(ta.buffer);
return 1;
},
get 0() {
return 100;
},
});
}, TypeError);
...@@ -1207,32 +1207,29 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js'); ...@@ -1207,32 +1207,29 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js');
}); });
} }
// Tests where the length getter returns a non-zero value -> these throw. // Tests where the length getter detaches -> these are no-op.
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4); const fixedLength = new ctor(rab, 0, 4);
assertThrows(() => { fixedLength.set(CreateSourceProxy(1)); }, TypeError); fixedLength.set(CreateSourceProxy(1));
} }
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(1)); }, fixedLengthWithOffset.set(CreateSourceProxy(1));
TypeError);
} }
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0); const lengthTracking = new ctor(rab, 0);
assertThrows(() => { lengthTracking.set(CreateSourceProxy(1)); }, lengthTracking.set(CreateSourceProxy(1));
TypeError);
} }
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(1)); }, lengthTrackingWithOffset.set(CreateSourceProxy(1));
TypeError);
} }
// Tests where the length getter returns a zero -> these don't throw. // Tests where the length getter returns a zero -> these don't throw.
...@@ -1302,31 +1299,28 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js'); ...@@ -1302,31 +1299,28 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js');
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4); const fixedLength = new ctor(rab, 0, 4);
detachAt = 2; detachAt = 2;
assertThrows(() => { fixedLength.set(CreateSourceProxy(4)); }, TypeError); fixedLength.set(CreateSourceProxy(4));
} }
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAt = 2; detachAt = 2;
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(2)); }, fixedLengthWithOffset.set(CreateSourceProxy(2));
TypeError);
} }
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0); const lengthTracking = new ctor(rab, 0);
detachAt = 2; detachAt = 2;
assertThrows(() => { lengthTracking.set(CreateSourceProxy(2)); }, lengthTracking.set(CreateSourceProxy(2));
TypeError);
} }
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAt = 2; detachAt = 2;
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(2)); }, lengthTrackingWithOffset.set(CreateSourceProxy(2));
TypeError);
} }
})(); })();
......
...@@ -5570,13 +5570,13 @@ function TestIterationAndResize(ta, expected, rab, resize_after, ...@@ -5570,13 +5570,13 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
}); });
} }
// Tests where the length getter returns a non-zero value -> these throw if // Tests where the length getter returns a non-zero value -> these are nop if
// the TA went OOB. // the TA went OOB.
for (let ctor of ctors) { for (let ctor of ctors) {
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4); const fixedLength = new ctor(rab, 0, 4);
resizeTo = 3 * ctor.BYTES_PER_ELEMENT; resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
assertThrows(() => { fixedLength.set(CreateSourceProxy(1)); }, TypeError); fixedLength.set(CreateSourceProxy(1));
assertEquals([0, 2, 4], ToNumbers(new ctor(rab))); assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
} }
...@@ -5584,8 +5584,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after, ...@@ -5584,8 +5584,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
resizeTo = 3 * ctor.BYTES_PER_ELEMENT; resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(1)); }, fixedLengthWithOffset.set(CreateSourceProxy(1));
TypeError);
assertEquals([0, 2, 4], ToNumbers(new ctor(rab))); assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
} }
...@@ -5612,8 +5611,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after, ...@@ -5612,8 +5611,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
rab = CreateRabForTest(ctor); rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
resizeTo = 1 * ctor.BYTES_PER_ELEMENT; resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(1)); }, lengthTrackingWithOffset.set(CreateSourceProxy(1));
TypeError);
assertEquals([0], ToNumbers(new ctor(rab))); assertEquals([0], ToNumbers(new ctor(rab)));
} }
...@@ -5757,7 +5755,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after, ...@@ -5757,7 +5755,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
const fixedLength = new ctor(rab, 0, 4); const fixedLength = new ctor(rab, 0, 4);
resizeAt = 2; resizeAt = 2;
resizeTo = 3 * ctor.BYTES_PER_ELEMENT; resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
assertThrows(() => { fixedLength.set(CreateSourceProxy(4)); }, TypeError); fixedLength.set(CreateSourceProxy(4));
assertEquals([1, 2, 4], ToNumbers(new ctor(rab))); assertEquals([1, 2, 4], ToNumbers(new ctor(rab)));
} }
...@@ -5766,8 +5764,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after, ...@@ -5766,8 +5764,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2); const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
resizeAt = 2; resizeAt = 2;
resizeTo = 3 * ctor.BYTES_PER_ELEMENT; resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(2)); }, fixedLengthWithOffset.set(CreateSourceProxy(2));
TypeError);
assertEquals([0, 2, 1], ToNumbers(new ctor(rab))); assertEquals([0, 2, 1], ToNumbers(new ctor(rab)));
} }
...@@ -5797,8 +5794,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after, ...@@ -5797,8 +5794,7 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
resizeAt = 1; resizeAt = 1;
resizeTo = 1 * ctor.BYTES_PER_ELEMENT; resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(2)); }, lengthTrackingWithOffset.set(CreateSourceProxy(2));
TypeError);
assertEquals([0], ToNumbers(new ctor(rab))); assertEquals([0], ToNumbers(new ctor(rab)));
} }
})(); })();
......
...@@ -2905,9 +2905,6 @@ ...@@ -2905,9 +2905,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=12744 # https://bugs.chromium.org/p/v8/issues/detail?id=12744
'built-ins/TypedArrayConstructors/ctors/no-species': [FAIL], 'built-ins/TypedArrayConstructors/ctors/no-species': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=12750
'built-ins/TypedArray/prototype/set/array-arg-targetbuffer-detached-on-get-src-value-no-throw': [FAIL],
######################## NEEDS INVESTIGATION ########################### ######################## NEEDS INVESTIGATION ###########################
# https://bugs.chromium.org/p/v8/issues/detail?id=7833 # https://bugs.chromium.org/p/v8/issues/detail?id=7833
......
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