Commit b3596483 authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[rab/gsab] RAB/GSAB support for Object.DefinePropert(y|ies)

Bug: v8:11111,chromium:1306929
Change-Id: I26e4c5d7e87f75844e60952f30e8fe20189910c4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3535783Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79577}
parent 2c05f264
......@@ -236,52 +236,52 @@ Maybe<bool> JSTypedArray::DefineOwnProperty(Isolate* isolate,
Handle<Object> key,
PropertyDescriptor* desc,
Maybe<ShouldThrow> should_throw) {
// 1. Assert: IsPropertyKey(P) is true.
DCHECK(key->IsName() || key->IsNumber());
// 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
// 3. If Type(P) is String, then
// 1. If Type(P) is String, then
PropertyKey lookup_key(isolate, key);
if (lookup_key.is_element() || key->IsSmi() || key->IsString()) {
// 3a. Let numericIndex be ! CanonicalNumericIndexString(P)
// 3b. If numericIndex is not undefined, then
// 1a. Let numericIndex be ! CanonicalNumericIndexString(P)
// 1b. If numericIndex is not undefined, then
bool is_minus_zero = false;
if (key->IsSmi() || // Smi keys are definitely canonical
CanonicalNumericIndexString(isolate, lookup_key, &is_minus_zero)) {
// 3b i. If IsInteger(numericIndex) is false, return false.
// 3b ii. If numericIndex = -0, return false.
// 3b iii. If numericIndex < 0, return false.
if (!lookup_key.is_element() || is_minus_zero) {
// 1b i. If IsValidIntegerIndex(O, numericIndex) is false, return false.
// IsValidIntegerIndex:
size_t index = lookup_key.index();
bool out_of_bounds = false;
size_t length = o->GetLengthOrOutOfBounds(out_of_bounds);
if (o->WasDetached() || out_of_bounds || index >= length) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
}
size_t index = lookup_key.index();
// 3b iv. Let length be O.[[ArrayLength]].
size_t length = o->length();
// 3b v. If numericIndex ≥ length, return false.
if (o->WasDetached() || index >= length) {
if (!lookup_key.is_element() || is_minus_zero) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
}
// 3b vi. If IsAccessorDescriptor(Desc) is true, return false.
// 1b ii. If Desc has a [[Configurable]] field and if
// Desc.[[Configurable]] is false, return false.
// 1b iii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]]
// is false, return false.
// 1b iv. If IsAccessorDescriptor(Desc) is true, return false.
// 1b v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is
// false, return false.
if (PropertyDescriptor::IsAccessorDescriptor(desc)) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed, key));
}
// 3b vii. If Desc has a [[Configurable]] field and if
// Desc.[[Configurable]] is false, return false.
// 3b viii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]]
// is false, return false.
// 3b ix. If Desc has a [[Writable]] field and if Desc.[[Writable]] is
// false, return false.
if ((desc->has_configurable() && !desc->configurable()) ||
(desc->has_enumerable() && !desc->enumerable()) ||
(desc->has_writable() && !desc->writable())) {
RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
NewTypeError(MessageTemplate::kRedefineDisallowed, key));
}
// 3b x. If Desc has a [[Value]] field, then
// 3b x 1. Let value be Desc.[[Value]].
// 3b x 2. Return ? IntegerIndexedElementSet(O, numericIndex, value).
// 1b vi. If Desc has a [[Value]] field, perform
// ? IntegerIndexedElementSet(O, numericIndex, Desc.[[Value]]).
if (desc->has_value()) {
if (!desc->has_configurable()) desc->set_configurable(true);
if (!desc->has_enumerable()) desc->set_enumerable(true);
......@@ -293,7 +293,7 @@ Maybe<bool> JSTypedArray::DefineOwnProperty(Isolate* isolate,
DefineOwnPropertyIgnoreAttributes(&it, value, desc->ToAttributes()),
Nothing<bool>());
}
// 3b xi. Return true.
// 1b vii. Return true.
return Just(true);
}
}
......
// Copyright 2022 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: --harmony-rab-gsab
const gsab = new SharedArrayBuffer(1024, {maxByteLength: 11337});
const ta = new Float64Array(gsab);
Object.defineProperty(ta, 0, {});
......@@ -3565,3 +3565,86 @@ function TestIterationAndGrow(ta, expected, gsab, grow_after,
assertEquals([7, 8, 9, 10, 0, 0], ToNumbers(taFull));
}
})();
(function ObjectDefinePropertyDefineProperties() {
for (let helper of
[ObjectDefinePropertyHelper, ObjectDefinePropertiesHelper]) {
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(gsab, 0, 4);
const fixedLengthWithOffset = new ctor(
gsab, 2 * ctor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new ctor(gsab, 0);
const lengthTrackingWithOffset = new ctor(
gsab, 2 * ctor.BYTES_PER_ELEMENT);
const taFull = new ctor(gsab, 0);
// Orig. array: [0, 0, 0, 0]
// [0, 0, 0, 0] << fixedLength
// [0, 0] << fixedLengthWithOffset
// [0, 0, 0, 0, ...] << lengthTracking
// [0, 0, ...] << lengthTrackingWithOffset
helper(fixedLength, 0, 1);
assertEquals([1, 0, 0, 0], ToNumbers(taFull));
helper(fixedLengthWithOffset, 0, 2);
assertEquals([1, 0, 2, 0], ToNumbers(taFull));
helper(lengthTracking, 1, 3);
assertEquals([1, 3, 2, 0], ToNumbers(taFull));
helper(lengthTrackingWithOffset, 1, 4);
assertEquals([1, 3, 2, 4], ToNumbers(taFull));
assertThrows(() => { helper(fixedLength, 4, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 2, 8); }, TypeError);
assertThrows(() => { helper(lengthTracking, 4, 8); }, TypeError);
assertThrows(() => { helper(lengthTrackingWithOffset, 2, 8); },
TypeError);
// Grow.
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
helper(fixedLength, 0, 9);
assertEquals([9, 3, 2, 4, 0, 0], ToNumbers(taFull));
helper(fixedLengthWithOffset, 0, 10);
assertEquals([9, 3, 10, 4, 0, 0], ToNumbers(taFull));
helper(lengthTracking, 1, 11);
assertEquals([9, 11, 10, 4, 0, 0], ToNumbers(taFull));
helper(lengthTrackingWithOffset, 2, 12);
assertEquals([9, 11, 10, 4, 12, 0], ToNumbers(taFull));
// Trying to define properties out of the fixed-length bounds throws.
assertThrows(() => { helper(fixedLength, 5, 13); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 3, 13); }, TypeError);
assertEquals([9, 11, 10, 4, 12, 0], ToNumbers(taFull));
helper(lengthTracking, 4, 14);
assertEquals([9, 11, 10, 4, 14, 0], ToNumbers(taFull));
helper(lengthTrackingWithOffset, 3, 15);
assertEquals([9, 11, 10, 4, 14, 15], ToNumbers(taFull));
assertThrows(() => { helper(fixedLength, 6, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 4, 8); }, TypeError);
assertThrows(() => { helper(lengthTracking, 6, 8); }, TypeError);
assertThrows(() => { helper(lengthTrackingWithOffset, 4, 8); },
TypeError);
}
}
})();
(function ObjectDefinePropertyParameterConversionGrows() {
const helper = ObjectDefinePropertyHelper;
// Length tracking.
for (let ctor of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(gsab, 0);
const evil = {toString: () => {
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
return 4; // Index valid after resize.
}};
helper(lengthTracking, evil, 8);
assertEquals([0, 0, 0, 0, 8, 0], ToNumbers(lengthTracking));
}
})();
......@@ -234,3 +234,21 @@ function assertAllDataViewMethodsThrow(view, index, errorType) {
assertThrows(() => { getter.call(view, index); }, errorType);
}
}
function ObjectDefinePropertyHelper(ta, index, value) {
if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
Object.defineProperty(ta, index, {value: BigInt(value)});
} else {
Object.defineProperty(ta, index, {value: value});
}
}
function ObjectDefinePropertiesHelper(ta, index, value) {
const values = {};
if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
values[index] = {value: BigInt(value)};
} else {
values[index] = {value: value};
}
Object.defineProperties(ta, values);
}
......@@ -1480,3 +1480,59 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js');
assertThrows(() => { lengthTracking.sort(CustomComparison); });
}
})();
(function ObjectDefineProperty() {
for (let helper of
[ObjectDefinePropertyHelper, ObjectDefinePropertiesHelper]) {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const fixedLengthWithOffset = new ctor(
rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new ctor(rab, 0);
const lengthTrackingWithOffset = new ctor(
rab, 2 * ctor.BYTES_PER_ELEMENT);
// Orig. array: [0, 0, 0, 0]
// [0, 0, 0, 0] << fixedLength
// [0, 0] << fixedLengthWithOffset
// [0, 0, 0, 0, ...] << lengthTracking
// [0, 0, ...] << lengthTrackingWithOffset
%ArrayBufferDetach(rab);
assertThrows(() => { helper(fixedLength, 0, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 0, 8); }, TypeError);
assertThrows(() => { helper(lengthTracking, 0, 8); }, TypeError);
assertThrows(() => { helper(lengthTrackingWithOffset, 0, 8); },
TypeError);
}
}
})();
(function ObjectDefinePropertyParameterConversionDetaches() {
const helper = ObjectDefinePropertyHelper;
// Fixed length.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const evil = {toString: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { helper(fixedLength, evil, 8); }, TypeError);
}
// Length tracking.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab, 0);
const evil = {toString: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { helper(lengthTracking, evil, 8); }, TypeError);
}
})();
......@@ -6674,3 +6674,157 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
assertEquals([7, 8, 9, 10, 0, 0], ToNumbers(taFull));
}
})();
(function ObjectDefinePropertyDefineProperties() {
for (let helper of
[ObjectDefinePropertyHelper, ObjectDefinePropertiesHelper]) {
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const fixedLengthWithOffset = new ctor(
rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
const lengthTracking = new ctor(rab, 0);
const lengthTrackingWithOffset = new ctor(
rab, 2 * ctor.BYTES_PER_ELEMENT);
const taFull = new ctor(rab, 0);
// Orig. array: [0, 0, 0, 0]
// [0, 0, 0, 0] << fixedLength
// [0, 0] << fixedLengthWithOffset
// [0, 0, 0, 0, ...] << lengthTracking
// [0, 0, ...] << lengthTrackingWithOffset
helper(fixedLength, 0, 1);
assertEquals([1, 0, 0, 0], ToNumbers(taFull));
helper(fixedLengthWithOffset, 0, 2);
assertEquals([1, 0, 2, 0], ToNumbers(taFull));
helper(lengthTracking, 1, 3);
assertEquals([1, 3, 2, 0], ToNumbers(taFull));
helper(lengthTrackingWithOffset, 1, 4);
assertEquals([1, 3, 2, 4], ToNumbers(taFull));
assertThrows(() => { helper(fixedLength, 4, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 2, 8); }, TypeError);
assertThrows(() => { helper(lengthTracking, 4, 8); }, TypeError);
assertThrows(() => { helper(lengthTrackingWithOffset, 2, 8); },
TypeError);
// Shrink so that fixed length TAs go out of bounds.
rab.resize(3 * ctor.BYTES_PER_ELEMENT);
// Orig. array: [1, 3, 2]
// [1, 3, 2, ...] << lengthTracking
// [2, ...] << lengthTrackingWithOffset
assertThrows(() => { helper(fixedLength, 0, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 0, 8); }, TypeError);
assertEquals([1, 3, 2], ToNumbers(taFull));
helper(lengthTracking, 0, 5);
assertEquals([5, 3, 2], ToNumbers(taFull));
helper(lengthTrackingWithOffset, 0, 6);
assertEquals([5, 3, 6], ToNumbers(taFull));
// Shrink so that the TAs with offset go out of bounds.
rab.resize(1 * ctor.BYTES_PER_ELEMENT);
assertThrows(() => { helper(fixedLength, 0, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 0, 8); }, TypeError);
assertThrows(() => { helper(lengthTrackingWithOffset, 0, 8); },
TypeError);
assertEquals([5], ToNumbers(taFull));
helper(lengthTracking, 0, 7);
assertEquals([7], ToNumbers(taFull));
// Shrink to zero.
rab.resize(0);
assertThrows(() => { helper(fixedLength, 0, 8); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 0, 8); }, TypeError);
assertThrows(() => { helper(lengthTracking, 0, 8); }, TypeError);
assertThrows(() => { helper(lengthTrackingWithOffset, 0, 8); },
TypeError);
assertEquals([], ToNumbers(taFull));
// Grow so that all TAs are back in-bounds.
rab.resize(6 * ctor.BYTES_PER_ELEMENT);
helper(fixedLength, 0, 9);
assertEquals([9, 0, 0, 0, 0, 0], ToNumbers(taFull));
helper(fixedLengthWithOffset, 0, 10);
assertEquals([9, 0, 10, 0, 0, 0], ToNumbers(taFull));
helper(lengthTracking, 1, 11);
assertEquals([9, 11, 10, 0, 0, 0], ToNumbers(taFull));
helper(lengthTrackingWithOffset, 2, 12);
assertEquals([9, 11, 10, 0, 12, 0], ToNumbers(taFull));
// Trying to define properties out of the fixed-length bounds throws.
assertThrows(() => { helper(fixedLength, 5, 13); }, TypeError);
assertThrows(() => { helper(fixedLengthWithOffset, 3, 13); }, TypeError);
assertEquals([9, 11, 10, 0, 12, 0], ToNumbers(taFull));
helper(lengthTracking, 4, 14);
assertEquals([9, 11, 10, 0, 14, 0], ToNumbers(taFull));
helper(lengthTrackingWithOffset, 3, 15);
assertEquals([9, 11, 10, 0, 14, 15], ToNumbers(taFull));
}
}
})();
(function ObjectDefinePropertyParameterConversionShrinks() {
const helper = ObjectDefinePropertyHelper;
// Fixed length.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
const evil = {toString: () => {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 0;
}};
assertThrows(() => { helper(fixedLength, evil, 8); }, TypeError);
}
// Length tracking.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab, 0);
const evil = {toString: () => {
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
return 3; // Index too large after resize.
}};
assertThrows(() => { helper(lengthTracking, evil, 8); }, TypeError);
}
})();
(function ObjectDefinePropertyParameterConversionGrows() {
const helper = ObjectDefinePropertyHelper;
// Fixed length.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const fixedLength = new ctor(rab, 0, 4);
// Make fixedLength go OOB.
rab.resize(2 * ctor.BYTES_PER_ELEMENT);
const evil = {toString: () => {
rab.resize(6 * ctor.BYTES_PER_ELEMENT);
return 0;
}};
helper(fixedLength, evil, 8);
assertEquals([8, 0, 0, 0], ToNumbers(fixedLength));
}
// Length tracking.
for (let ctor of ctors) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
const lengthTracking = new ctor(rab, 0);
const evil = {toString: () => {
rab.resize(6 * ctor.BYTES_PER_ELEMENT);
return 4; // Index valid after resize.
}};
helper(lengthTracking, evil, 8);
assertEquals([0, 0, 0, 0, 8, 0], ToNumbers(lengthTracking));
}
})();
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