[change-array-by-copy] Implement with

Bug: v8:12764
Change-Id: I67b9b0e4f3c7ca6a2719c234b7f7605f07f86b28
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3671760
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80874}
This commit is contained in:
Shu-yu Guo 2022-05-31 17:06:42 -07:00 committed by V8 LUCI CQ
parent 5c445ebbea
commit 0f510c4ab1
13 changed files with 688 additions and 26 deletions

View File

@ -783,6 +783,7 @@ filegroup(
"src/builtins/array-splice.tq",
"src/builtins/array-to-reversed.tq",
"src/builtins/array-unshift.tq",
"src/builtins/array-with.tq",
"src/builtins/array.tq",
"src/builtins/arraybuffer.tq",
"src/builtins/base.tq",
@ -883,6 +884,7 @@ filegroup(
"src/builtins/typed-array-subarray.tq",
"src/builtins/typed-array-to-reversed.tq",
"src/builtins/typed-array-values.tq",
"src/builtins/typed-array-with.tq",
"src/builtins/typed-array.tq",
"src/builtins/weak-ref.tq",
"src/ic/handler-configuration.tq",

View File

@ -1683,6 +1683,7 @@ torque_files = [
"src/builtins/array-splice.tq",
"src/builtins/array-to-reversed.tq",
"src/builtins/array-unshift.tq",
"src/builtins/array-with.tq",
"src/builtins/array.tq",
"src/builtins/arraybuffer.tq",
"src/builtins/base.tq",
@ -1783,6 +1784,7 @@ torque_files = [
"src/builtins/typed-array-subarray.tq",
"src/builtins/typed-array-to-reversed.tq",
"src/builtins/typed-array-values.tq",
"src/builtins/typed-array-with.tq",
"src/builtins/typed-array.tq",
"src/builtins/weak-ref.tq",
"src/ic/handler-configuration.tq",

View File

@ -0,0 +1,89 @@
// 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.
namespace array {
transitioning macro TryFastPackedArrayWith(implicit context: Context)(
receiver: JSReceiver, len: Number, actualIndex: Number,
value: JSAny): JSArray labels Slow {
// Array#with does not preserve holes and always creates packed Arrays. Holes
// in the source array-like are treated like any other element and the value
// is computed with Get. So, there are only fast paths for packed elements.
const array: FastJSArray = Cast<FastJSArray>(receiver) otherwise Slow;
if (IsFastPackedElementsKind(array.map.elements_kind)) {
const lenSmi = Cast<Smi>(len) otherwise Slow;
// It is possible that the index coercion shrunk the source array, in which
// case go to the slow case.
if (lenSmi > array.length) goto Slow;
// Steps 7-9 done by copying and overriding the value at index.
const copy = ExtractFastJSArray(context, array, 0, lenSmi);
FastCreateDataProperty(copy, actualIndex, value);
// 10. Return A.
return copy;
}
goto Slow;
}
transitioning macro GenericArrayWith(
context: Context, receiver: JSReceiver, len: Number, actualIndex: Number,
value: JSAny): JSArray {
// 7. Let A be ? ArrayCreate(𝔽(len)).
const copy = ArrayCreate(len);
// 8. Let k be 0.
let k: Number = 0;
// 9. Repeat, while k < len,
while (k < len) {
// a. Let Pk be ! ToString(𝔽(k)).
// b. If k is actualIndex, let fromValue be value.
// c. Else, let fromValue be ? Get(O, Pk).
const fromValue = k == actualIndex ? value : GetProperty(receiver, k);
// d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
FastCreateDataProperty(copy, k, fromValue);
// e. Set k to k + 1.
++k;
}
// 10. Return A.
return copy;
}
// https://tc39.es/proposal-change-array-by-copy/#sec-array.prototype.with
transitioning javascript builtin ArrayPrototypeWith(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
const index = arguments[0];
const value = arguments[1];
// 1. Let O be ? ToObject(this value).
const object: JSReceiver = ToObject_Inline(context, receiver);
// 2. Let len be ? LengthOfArrayLike(O).
const len: Number = GetLengthProperty(object);
try {
// 3. Let relativeIndex be ? ToIntegerOrInfinity(index).
const relativeIndex = ToInteger_Inline(index);
// 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
// 5. Else, let actualIndex be len + relativeIndex.
// 6. If actualIndex ≥ len or actualIndex < 0, throw a RangeError exception.
const actualIndex =
ConvertRelativeIndex(relativeIndex, len) otherwise OutOfBounds,
OutOfBounds;
try {
return TryFastPackedArrayWith(object, len, actualIndex, value)
otherwise Slow;
} label Slow {
return GenericArrayWith(context, object, len, actualIndex, value);
}
} label OutOfBounds deferred {
ThrowRangeError(MessageTemplate::kInvalid, 'index', index);
}
}
}

View File

@ -360,9 +360,11 @@ const kBigIntMaxLength: constexpr intptr generates 'BigInt::kMaxLength';
extern enum MessageTemplate {
kAllPromisesRejected,
kInvalid,
kInvalidArrayBufferLength,
kInvalidArrayLength,
kInvalidIndex,
kInvalidTypedArrayIndex,
kNotConstructor,
kNotGeneric,
kCalledNonCallable,
@ -644,6 +646,8 @@ transitioning macro ToInteger_Inline(implicit context: Context)(input: JSAny):
}
}
extern macro ToBigInt(Context, JSAny): BigInt;
extern enum BigIntHandling extends int32
constexpr 'CodeStubAssembler::BigIntHandling' { kConvertToNumber, kThrow }
@ -683,6 +687,8 @@ extern macro ThrowRangeError(implicit context: Context)(
constexpr MessageTemplate): never;
extern macro ThrowRangeError(implicit context: Context)(
constexpr MessageTemplate, Object): never;
extern macro ThrowRangeError(implicit context: Context)(
constexpr MessageTemplate, Object, Object): never;
extern macro ThrowTypeError(implicit context: Context)(
constexpr MessageTemplate): never;
extern macro ThrowTypeError(implicit context: Context)(
@ -828,6 +834,7 @@ extern macro IsElementsKindInRange(
ElementsKind, constexpr ElementsKind, constexpr ElementsKind): bool;
extern macro IsFastElementsKind(constexpr ElementsKind): constexpr bool;
extern macro IsFastPackedElementsKind(constexpr ElementsKind): constexpr bool;
extern macro IsDoubleElementsKind(constexpr ElementsKind): constexpr bool;
extern macro GetNonRabGsabElementsKind(ElementsKind): ElementsKind;
@ -1306,6 +1313,7 @@ extern macro BasicLoadNumberDictionaryElement(NumberDictionary, intptr): JSAny
labels NotData, IfHole;
extern macro IsFastElementsKind(ElementsKind): bool;
extern macro IsFastPackedElementsKind(ElementsKind): bool;
extern macro IsDoubleElementsKind(ElementsKind): bool;
extern macro IsFastSmiOrTaggedElementsKind(ElementsKind): bool;
extern macro IsFastSmiElementsKind(ElementsKind): bool;

View File

@ -536,7 +536,6 @@ transitioning javascript builtin DataViewPrototypeGetBigInt64(
}
extern macro ToNumber(Context, JSAny): Number;
extern macro ToBigInt(Context, JSAny): BigInt;
extern macro TruncateFloat64ToWord32(float64): uint32;
extern macro DataViewBuiltinsAssembler::StoreWord8(

View File

@ -0,0 +1,96 @@
// 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.
namespace typed_array {
const kBuiltinNameWith: constexpr string = '%TypedArray%.prototype.with';
// https://tc39.es/proposal-change-array-by-copy/#sec-%typedarray%.prototype.with
transitioning javascript builtin TypedArrayPrototypeWith(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
const index = arguments[0];
let value: JSAny = arguments[1];
try {
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
// 3. Let len be O.[[ArrayLength]].
const array: JSTypedArray =
Cast<JSTypedArray>(receiver) otherwise NotTypedArray;
let attachedArrayAndLength = EnsureAttachedAndReadLength(array)
otherwise IsDetachedOrOutOfBounds;
const originalLength = attachedArrayAndLength.length;
// TODO(v8:12764): This change is waiting on upstream
// https://github.com/tc39/proposal-change-array-by-copy/issues/85
if (IsBigInt64ElementsKind(array.elements_kind)) {
// 4. If O.[[ContentType]] is BigInt, set value to ? ToBigInt(value).
value = ToBigInt(context, value);
} else {
// 5. Else, set value to ? ToNumber(value).
value = ToNumber_Inline(value);
}
// 6. Let relativeIndex be ? ToIntegerOrInfinity(index).
const relativeIndex = ToInteger_Inline(index);
// 7. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
// 8. Else, let actualIndex be len + relativeIndex.
const actualIndex: uintptr = ConvertRelativeIndex(
relativeIndex, originalLength) otherwise IndexOutOfBounds,
IndexOutOfBounds;
// 9. If ! IsValidIntegerIndex(O, 𝔽(actualIndex)) is false, throw a
// RangeError exception.
attachedArrayAndLength = EnsureAttachedAndReadLength(array)
otherwise IndexOutOfBounds;
if (actualIndex >= attachedArrayAndLength.length) goto IndexOutOfBounds;
// 10. Let A be ? TypedArrayCreateSameType(O, « 𝔽(len) »).
const copy = TypedArrayCreateSameType(array, originalLength);
const fastCopyableLength =
UintPtrMin(originalLength, attachedArrayAndLength.length);
// Steps 11-12's copy loop implemented by memmove.
const info = GetTypedArrayElementsInfo(copy);
const countBytes: uintptr =
info.CalculateByteLength(fastCopyableLength) otherwise unreachable;
// TypedArrayCreateSameType always use built-in constructors, and so cannot
// cause the source TypedArray to become detached or OOB.
const srcPtr: RawPtr = array.data_ptr;
if (IsSharedArrayBuffer(array.buffer)) {
CallCRelaxedMemmove(copy.data_ptr, srcPtr, countBytes);
} else {
CallCMemmove(copy.data_ptr, srcPtr, countBytes);
}
// b. If k is actualIndex, then
// i. Perform ? Set(A, Pk, value, true).
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(copy.elements_kind);
accessor.StoreJSAny(context, copy, actualIndex, value)
otherwise unreachable;
// Fill the remainder with undefined, in case of resize during parameter
// conversion. This is not the same as doing nothing because:
// - Undefined convert to NaN, which is observable when stored into
// Float32 and Float64Arrays
// - Undefined cannot convert to BigInt and throws
let k: uintptr = fastCopyableLength;
while (k < copy.length) {
accessor.StoreJSAny(context, copy, k, Undefined) otherwise unreachable;
++k;
}
// 11. Return A.
return copy;
} label IndexOutOfBounds deferred {
ThrowRangeError(MessageTemplate::kInvalidTypedArrayIndex);
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameWith);
} label IsDetachedOrOutOfBounds deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameWith);
}
}
}

View File

@ -298,7 +298,7 @@ builtin StoreTypedElementNumeric<T : type extends ElementsKind>(
return kStoreSucceded;
}
// Returns True on sucess or False if the typedArrays was detached.
// Returns True on success or False if the typedArrays was detached.
builtin StoreTypedElementJSAny<T : type extends ElementsKind>(
context: Context, typedArray: JSTypedArray, index: uintptr,
value: JSAny): Smi {

View File

@ -14492,6 +14492,18 @@ TNode<BoolT> CodeStubAssembler::IsFastElementsKind(
Int32Constant(LAST_FAST_ELEMENTS_KIND));
}
TNode<BoolT> CodeStubAssembler::IsFastPackedElementsKind(
TNode<Int32T> elements_kind) {
static_assert(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND);
// ElementsKind values that are even are packed. See
// internal::IsFastPackedElementsKind.
static_assert((~PACKED_SMI_ELEMENTS & 1) == 1);
static_assert((~PACKED_ELEMENTS & 1) == 1);
static_assert((~PACKED_DOUBLE_ELEMENTS & 1) == 1);
return Word32And(IsNotSetWord32(elements_kind, 1),
IsFastElementsKind(elements_kind));
}
TNode<BoolT> CodeStubAssembler::IsFastOrNonExtensibleOrSealedElementsKind(
TNode<Int32T> elements_kind) {
static_assert(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND);

View File

@ -2719,6 +2719,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
bool IsFastElementsKind(ElementsKind kind) {
return v8::internal::IsFastElementsKind(kind);
}
TNode<BoolT> IsFastPackedElementsKind(TNode<Int32T> elements_kind);
bool IsFastPackedElementsKind(ElementsKind kind) {
return v8::internal::IsFastPackedElementsKind(kind);
}
TNode<BoolT> IsFastOrNonExtensibleOrSealedElementsKind(
TNode<Int32T> elements_kind);

View File

@ -4497,6 +4497,8 @@ void Genesis::InitializeGlobal_harmony_change_array_by_copy() {
SimpleInstallFunction(isolate_, array_prototype, "toReversed",
Builtin::kArrayPrototypeToReversed, 0, true);
SimpleInstallFunction(isolate_, array_prototype, "with",
Builtin::kArrayPrototypeWith, 2, true);
Handle<JSObject> unscopables = Handle<JSObject>::cast(
JSObject::GetProperty(isolate(), array_prototype,
@ -4511,6 +4513,8 @@ void Genesis::InitializeGlobal_harmony_change_array_by_copy() {
isolate());
SimpleInstallFunction(isolate_, prototype, "toReversed",
Builtin::kTypedArrayPrototypeToReversed, 0, true);
SimpleInstallFunction(isolate_, prototype, "with",
Builtin::kTypedArrayPrototypeWith, 2, true);
}
}

View File

@ -0,0 +1,136 @@
// 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-change-array-by-copy
"use strict";
assertEquals(2, Array.prototype.with.length);
assertEquals("with", Array.prototype.with.name);
const values = [1, 1.1, true, false, undefined, null, "foo", {}];
(function TestSmiPacked() {
let a = [1,2,3,4];
for (let v of values) {
let b = a.with(1, v);
assertEquals([1,2,3,4], a);
assertEquals([1,v,3,4], b);
assertFalse(a === b);
let c = a.with(-1, v);
assertEquals([1,2,3,4], a);
assertEquals([1,2,3,v], c);
assertFalse(a === c);
}
})();
(function TestDoublePacked() {
let a = [1.1,2.2,3.3,4.4];
for (let v of values) {
let b = a.with(1, v);
assertEquals([1.1,2.2,3.3,4.4], a);
assertEquals([1.1,v,3.3,4.4], b);
assertFalse(a === b);
let c = a.with(-1, v);
assertEquals([1.1,2.2,3.3,4.4], a);
assertEquals([1.1,2.2,3.3,v], c);
assertFalse(a === c);
}
})();
(function TestPacked() {
let a = [true,false,1,42.42];
for (let v of values) {
let b = a.with(1, v);
assertEquals([true,false,1,42.42], a);
assertEquals([true,v,1,42.42], b);
assertFalse(a === b);
let c = a.with(-1, v);
assertEquals([true,false,1,42.42], a);
assertEquals([true,false,1,v], c);
assertFalse(a === c);
}
})();
(function TestGeneric() {
let a = { length: 4,
get "0"() { return "hello"; },
get "1"() { return "cursed"; },
get "2"() { return "java"; },
get "3"() { return "script" } };
for (let v of values) {
let b = Array.prototype.with.call(a, 1, v);
assertEquals("cursed", a[1]);
assertEquals(["hello",v,"java","script"], b);
assertFalse(a === b);
assertTrue(Array.isArray(b));
assertEquals(Array, b.constructor);
let c = Array.prototype.with.call(a, -1, v);
assertEquals("script", a[a.length-1]);
assertEquals(["hello","cursed","java",v], c);
assertFalse(a === c);
assertTrue(Array.isArray(c));
assertEquals(Array, c.constructor);
}
})();
(function TestTooBig() {
let a = { length: Math.pow(2, 32) };
assertThrows(() => Array.prototype.with.call(a, 1, 42), RangeError);
})();
(function TestNoSpecies() {
class MyArray extends Array {
static get [Symbol.species]() { return MyArray; }
}
let a = new MyArray();
a.length = 4;
assertEquals(Array, a.with(1,42).constructor);
})();
(function TestOutOfBounds() {
let a = [1,2,3,4];
assertThrows(() => { a.with(a.length, 42); }, RangeError);
assertThrows(() => { a.with(-a.length - 1, 42); }, RangeError);
})();
(function TestIndexShenanigans() {
let a = [1,2,3,4];
// The index can resize and do other shenanigans since it's coerced before
// iteration.
let b = a.with({ valueOf() { a.length = 2; return 1; } }, 42);
assertEquals([1,2], a);
assertEquals([1,42,undefined,undefined], b);
// Holey.
a = [1,2,3,4,,,];
let c = a.with({ valueOf() { a.length = 2; return 1; } }, 42);
assertEquals([1,2], a);
assertEquals([1,42,undefined,undefined,undefined,undefined], c);
a = [1,2,3,4];
let d = a.with({ valueOf() { a[2] = "barf"; return 1; } }, 42);
assertEquals([1,2,"barf",4], a);
assertEquals([1,42,"barf",4], d);
})();
(function TestValuePassthrough() {
let a = [1,2,3,4];
// The value isn't used in any meaningful sense by with.
a.with(1, { valueOf() { assertUnreachable(); } });
})();
// All tests after this have an invalidated elements-on-prototype protector.
(function TestNoHoles() {
let a = [,,,,];
Array.prototype[3] = "on proto";
for (let v of values) {
let b = a.with(1,v);
assertEquals([undefined,v,undefined,"on proto"], b);
assertTrue(b.hasOwnProperty('0'));
assertTrue(b.hasOwnProperty('1'));
assertTrue(b.hasOwnProperty('2'));
assertTrue(b.hasOwnProperty('3'));
}
})();

View File

@ -0,0 +1,310 @@
// 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-change-array-by-copy --harmony-rab-gsab
// Flags: --allow-natives-syntax
"use strict";
d8.file.execute('test/mjsunit/typedarray-helpers.js');
function NormalizeValue(array, value) {
if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
return BigInt(value);
}
return value;
}
function CheckWith(orig, index, indexNotEvil, v, vNotEvil) {
const origLen = orig.length;
const absoluteIndex = indexNotEvil < 0
? indexNotEvil + orig.length
: indexNotEvil;
const origVAtIndex = orig[absoluteIndex];
const copy = orig.with(index, v);
assertEquals(origLen, copy.length);
for (let i = 0; i < origLen; i++) {
assertEquals(i == absoluteIndex ? vNotEvil : orig[i], copy[i]);
}
assertEquals(origVAtIndex, orig[absoluteIndex]);
assertFalse(copy === orig);
assertFalse(copy.buffer === orig.buffer);
}
function CheckWithShrunkOrDetached(orig, v, vNotEvil) {
const origLen = orig.length;
if (orig instanceof BigInt64Array ||
orig instanceof BigUint64Array) {
// This is funny. For BigInt TAs this throws because when shrinking the
// source array, the remainder of the copy is filled with undefineds because
// OOB TA loads return undefined, but undefined can't be coerced to BigInt
// and throw a TypeError.
assertThrows(() => { orig.with(1, v); }, TypeError);
return;
}
const copy = orig.with(1, v);
assertEquals(origLen, copy.length);
assertEquals(NormalizeValue(copy, 0), copy[0]);
assertEquals(vNotEvil, copy[1]);
for (let i = 2; i < copy.length; i++) {
if (copy instanceof Float32Array || copy instanceof Float64Array) {
assertEquals(NaN, copy[i]);
} else {
assertEquals(0, copy[i]);
}
}
}
(function TestSurface() {
for (let TA of ctors) {
assertEquals(2, TA.prototype.with.length);
assertEquals("with", TA.prototype.with.name);
}
})();
(function TestBasic() {
for (let TA of ctors) {
let a = new TA(4);
const v = NormalizeValue(a, 42);
for (let i = 0; i < 4; i++) {
WriteToTypedArray(a, i, i);
}
CheckWith(a, 1, 1, v, v);
CheckWith(a, -1, -1, v, v);
}
})();
(function TestOutOfBounds() {
for (let TA of ctors) {
let a = new TA(4);
const v = NormalizeValue(a, 42);
assertThrows(() => { a.with(a.length, v); }, RangeError);
assertThrows(() => { a.with(-a.length - 1, v); }, RangeError);
}
})();
(function TestNonTypedArray() {
for (let TA of ctors) {
assertThrows(() => { TA.prototype.with.call([1,2,3,4], 1, 42); }, TypeError);
}
})();
(function TestResizableBuffer() {
for (let TA of ctors) {
const rab = CreateResizableArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
const fixedLength = new TA(rab, 0, 4);
const fixedLengthWithOffset = new TA(rab, 2 * TA.BYTES_PER_ELEMENT, 2);
const lengthTracking = new TA(rab, 0);
const lengthTrackingWithOffset = new TA(rab, 2 * TA.BYTES_PER_ELEMENT);
const v = NormalizeValue(fixedLength, 42);
// Write some data into the array.
const taWrite = new TA(rab);
for (let i = 0; i < 4; i++) {
WriteToTypedArray(taWrite, i, i);
}
// Orig. array: [0, 1, 2, 3]
// [0, 1, 2, 3] << fixedLength
// [2, 3] << fixedLengthWithOffset
// [0, 1, 2, 3, ...] << lengthTracking
// [2, 3, ...] << lengthTrackingWithOffset
CheckWith(fixedLength, 1, 1, v, v);
CheckWith(fixedLengthWithOffset, 1, 1, v, v);
CheckWith(lengthTracking, 1, 1, v, v);
CheckWith(lengthTrackingWithOffset, 1, 1, v, v);
CheckWith(fixedLength, -1, -1, v, v);
CheckWith(fixedLengthWithOffset, -1, -1, v, v);
CheckWith(lengthTracking, -1, -1, v, v);
CheckWith(lengthTrackingWithOffset, -1, -1, v, v);
// Shrink so that the TAs with offset go out of bounds.
rab.resize(1 * TA.BYTES_PER_ELEMENT);
WriteToTypedArray(taWrite, 0, 0);
assertThrows(() => { fixedLength.with(0, v); }, TypeError);
assertThrows(() => { fixedLengthWithOffset.with(0, v); }, TypeError);
CheckWith(lengthTracking, 0, 0, v, v);
assertThrows(() => { lengthTrackingWithOffset.with(0, v); }, TypeError);
// Shrink to zero.
rab.resize(0);
assertThrows(() => { fixedLength.with(0, v); }, TypeError);
assertThrows(() => { fixedLengthWithOffset.with(0, v); }, TypeError);
assertThrows(() => { lengthTracking.with(0, v); }, RangeError);
assertThrows(() => { lengthTrackingWithOffset.copyWithin(0, 1, 1); },
TypeError);
// Grow so that all TAs are back in-bounds.
rab.resize(6 * TA.BYTES_PER_ELEMENT);
for (let i = 0; i < 6; ++i) {
WriteToTypedArray(taWrite, i, i);
}
// Orig. array: [0, 1, 2, 3, 4, 5]
// [0, 1, 2, 3] << fixedLength
// [2, 3] << fixedLengthWithOffset
// [0, 1, 2, 3, 4, 5, ...] << lengthTracking
// [2, 3, 4, 5, ...] << lengthTrackingWithOffset
CheckWith(fixedLength, 1, 1, v, v);
CheckWith(fixedLengthWithOffset, 1, 1, v, v);
CheckWith(lengthTracking, 4, 4, v, v);
CheckWith(lengthTrackingWithOffset, 1, 1, v, v);
CheckWith(fixedLength, -1, -1, v, v);
CheckWith(fixedLengthWithOffset, -1, -1, v, v);
CheckWith(lengthTracking, -4, -4, v, v);
CheckWith(lengthTrackingWithOffset, -3, -3, v, v);
}
})();
(function TestParameterConversionShrinks() {
for (let TA of ctors) {
const rab = CreateResizableArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
const fixedLength = new TA(rab, 0, 4);
const v = NormalizeValue(fixedLength, 42);
const evilIndex = { valueOf: () => { rab.resize(2 * TA.BYTES_PER_ELEMENT);
return 2; }};
assertThrows(() => { fixedLength.with(evilIndex, v); },
RangeError);
const evilV = { valueOf: () => { rab.resize(2 * TA.BYTES_PER_ELEMENT);
return v; }};
rab.resize(4 * TA.BYTES_PER_ELEMENT);
const lengthTracking = new TA(rab, 0);
CheckWithShrunkOrDetached(lengthTracking, evilV, v);
}
})();
(function TestParameterConversionGrows() {
for (let TA of ctors) {
const rab = CreateResizableArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
const lengthTracking = new TA(rab, 0);
const v = NormalizeValue(lengthTracking, 42);
const evilIndex = { valueOf: () => { rab.resize(6 * TA.BYTES_PER_ELEMENT);
return 1; }};
const evilV = { valueOf: () => { rab.resize(6 * TA.BYTES_PER_ELEMENT);
return v; }};
CheckWith(lengthTracking, evilIndex, 1, v, v);
rab.resize(4 * TA.BYTES_PER_ELEMENT);
CheckWith(lengthTracking, 1, 1, evilV, v);
}
})();
(function TestParameterConversionDetaches() {
for (let TA of ctors) {
let rab = CreateResizableArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
const fixedLength = new TA(rab, 0, 4);
const v = NormalizeValue(fixedLength, 42);
const evilIndex = { valueOf: () => { %ArrayBufferDetach(rab);
return 2; }};
assertThrows(() => { fixedLength.with(evilIndex, v); },
RangeError);
rab = CreateResizableArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
const lengthTracking = new TA(rab, 0);
const evilV = { valueOf: () => { %ArrayBufferDetach(rab);
return v; }};
assertThrows(() => { fixedLength.with(1, evilV); }, TypeError);
}
})();
(function TestGrowableSAB() {
for (let TA of ctors) {
const gsab = CreateGrowableSharedArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
const fixedLength = new TA(gsab, 0, 4);
const fixedLengthWithOffset = new TA(gsab, 2 * TA.BYTES_PER_ELEMENT, 2);
const lengthTracking = new TA(gsab, 0);
const lengthTrackingWithOffset = new TA(gsab, 2 * TA.BYTES_PER_ELEMENT);
const v = NormalizeValue(fixedLength, 42);
// Write some data into the array.
const taWrite = new TA(gsab);
for (let i = 0; i < 4; i++) {
WriteToTypedArray(taWrite, i, 2 * i);
}
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
CheckWith(fixedLength, 1, 1, v, v);
CheckWith(fixedLengthWithOffset, 1, 1, v, v);
CheckWith(lengthTracking, 1, 1, v, v);
CheckWith(lengthTrackingWithOffset, 1, 1, v, v);
CheckWith(fixedLength, -1, -1, v, v);
CheckWith(fixedLengthWithOffset, -1, -1, v, v);
CheckWith(lengthTracking, -1, -1, v, v);
CheckWith(lengthTrackingWithOffset, -1, -1, v, v);
// Grow.
gsab.grow(6 * TA.BYTES_PER_ELEMENT);
for (let i = 0; i < 6; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
// Orig. array: [0, 2, 4, 6, 8, 10]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, 8, 10, ...] << lengthTracking
// [4, 6, 8, 10, ...] << lengthTrackingWithOffset
CheckWith(fixedLength, 1, 1, v, v);
CheckWith(fixedLengthWithOffset, 1, 1, v, v);
CheckWith(lengthTracking, 4, 4, v, v);
CheckWith(lengthTrackingWithOffset, 1, 1, v, v);
CheckWith(fixedLength, -1, -1, v, v);
CheckWith(fixedLengthWithOffset, -1, -1, v, v);
CheckWith(lengthTracking, -4, -4, v, v);
CheckWith(lengthTrackingWithOffset, -3, -3, v, v);
}
})();
(function TestParameterConversionGrowsSAB() {
for (let TA of ctors) {
let gsab = CreateGrowableSharedArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
let lengthTracking = new TA(gsab, 0);
const v = NormalizeValue(lengthTracking, 42);
const evilIndex = { valueOf: () => { gsab.grow(6 * TA.BYTES_PER_ELEMENT);
return 1; }};
const evilV = { valueOf: () => { gsab.grow(6 * TA.BYTES_PER_ELEMENT);
return v; }};
CheckWith(lengthTracking, evilIndex, 1, v, v);
gsab = CreateGrowableSharedArrayBuffer(4 * TA.BYTES_PER_ELEMENT,
8 * TA.BYTES_PER_ELEMENT);
lengthTracking = new TA(gsab, 0);
CheckWith(lengthTracking, 1, 1, evilV, v);
}
})();
(function TestNoSpecies() {
class MyUint8Array extends Uint8Array {
constructor(len) { super(len); }
static get [Symbol.species]() { return MyUint8Array; }
}
assertEquals(Uint8Array, (new MyUint8Array(4)).with(0, 42).constructor);
})();

View File

@ -536,30 +536,30 @@ KNOWN_OBJECTS = {
("old_space", 0x04b35): "StringSplitCache",
("old_space", 0x04f3d): "RegExpMultipleCache",
("old_space", 0x05345): "BuiltinsConstantsTable",
("old_space", 0x0577d): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x057a1): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x057c5): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x057e9): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x0580d): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x05831): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x05855): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x05879): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x0589d): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x058c1): "PromiseAllResolveElementSharedFun",
("old_space", 0x058e5): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x05909): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x0592d): "PromiseAnyRejectElementSharedFun",
("old_space", 0x05951): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x05975): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x05999): "PromiseCatchFinallySharedFun",
("old_space", 0x059bd): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x059e1): "PromiseThenFinallySharedFun",
("old_space", 0x05a05): "PromiseThrowerFinallySharedFun",
("old_space", 0x05a29): "PromiseValueThunkFinallySharedFun",
("old_space", 0x05a4d): "ProxyRevokeSharedFun",
("old_space", 0x05a71): "ShadowRealmImportValueFulfilledSFI",
("old_space", 0x05a95): "SourceTextModuleExecuteAsyncModuleFulfilledSFI",
("old_space", 0x05ab9): "SourceTextModuleExecuteAsyncModuleRejectedSFI",
("old_space", 0x05781): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x057a5): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x057c9): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x057ed): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x05811): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x05835): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x05859): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x0587d): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x058a1): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x058c5): "PromiseAllResolveElementSharedFun",
("old_space", 0x058e9): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x0590d): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x05931): "PromiseAnyRejectElementSharedFun",
("old_space", 0x05955): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x05979): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x0599d): "PromiseCatchFinallySharedFun",
("old_space", 0x059c1): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x059e5): "PromiseThenFinallySharedFun",
("old_space", 0x05a09): "PromiseThrowerFinallySharedFun",
("old_space", 0x05a2d): "PromiseValueThunkFinallySharedFun",
("old_space", 0x05a51): "ProxyRevokeSharedFun",
("old_space", 0x05a75): "ShadowRealmImportValueFulfilledSFI",
("old_space", 0x05a99): "SourceTextModuleExecuteAsyncModuleFulfilledSFI",
("old_space", 0x05abd): "SourceTextModuleExecuteAsyncModuleRejectedSFI",
}
# Lower 32 bits of first page addresses for various heap spaces.