[builtins] Enable Torque Array.prototype.splice
Before, splice was implemented with a C++ fast path and a comprehensive JavaScript version. This impl. is entirely in Torque with a fastpath for SMI, DOUBLE and OBJECT arrays, and a comprehensive slow path. The same level of "sparse" array support as given by the array.js implementation is included. This reland addresses several issues: * Removed "sparse" array support from splice. * Addressed ClusterFuzz issue 876443: The test and code that uses the fix is in this CL. The fix in isolation can be seen here: https://chromium-review.googlesource.com/c/v8/v8/+/1199403 * Removed dead code in elements.cc BUG=chromium:876443, v8:8131, v8:1956, v8:7221 Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng Change-Id: I2d4a66c24ba1edabeca34e27e6ff8ee6136ed5f1 Reviewed-on: https://chromium-review.googlesource.com/1201783 Commit-Queue: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#55610}
This commit is contained in:
parent
0e460c25a0
commit
fd334b3216
1
BUILD.gn
1
BUILD.gn
@ -883,6 +883,7 @@ torque_files = [
|
||||
"src/builtins/array-foreach.tq",
|
||||
"src/builtins/array-lastindexof.tq",
|
||||
"src/builtins/array-reverse.tq",
|
||||
"src/builtins/array-splice.tq",
|
||||
"src/builtins/typed-array.tq",
|
||||
"src/builtins/data-view.tq",
|
||||
"test/torque/test-torque.tq",
|
||||
|
@ -1747,13 +1747,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
Builtins::kArrayPrototypeSlice, 2, false);
|
||||
SimpleInstallFunction(isolate_, proto, "sort",
|
||||
Builtins::kArrayPrototypeSort, 1, false);
|
||||
if (FLAG_enable_experimental_builtins) {
|
||||
SimpleInstallFunction(isolate_, proto, "splice",
|
||||
Builtins::kArraySpliceTorque, 2, false);
|
||||
} else {
|
||||
SimpleInstallFunction(isolate_, proto, "splice", Builtins::kArraySplice,
|
||||
2, false);
|
||||
}
|
||||
SimpleInstallFunction(isolate_, proto, "splice", Builtins::kArraySplice, 2,
|
||||
false);
|
||||
SimpleInstallFunction(isolate_, proto, "includes", Builtins::kArrayIncludes,
|
||||
1, false);
|
||||
SimpleInstallFunction(isolate_, proto, "indexOf", Builtins::kArrayIndexOf,
|
||||
|
@ -152,8 +152,8 @@ module array {
|
||||
assert(IsFastSmiOrTaggedElementsKind(array.map.elements_kind));
|
||||
|
||||
const length: Smi = array.length_fast;
|
||||
array.elements = ExtractFixedArray(
|
||||
unsafe_cast<FixedArray>(elements), 0, length, length, kFixedArrays);
|
||||
array.elements =
|
||||
ExtractFixedArray(unsafe_cast<FixedArray>(elements), 0, length, length);
|
||||
}
|
||||
|
||||
macro TryFastPackedArrayReverse(receiver: Object) labels Slow {
|
||||
|
394
src/builtins/array-splice.tq
Normal file
394
src/builtins/array-splice.tq
Normal file
@ -0,0 +1,394 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
module array {
|
||||
// Given {elements}, we want to create a non-zero length array of type
|
||||
// FixedArrayType. Most of this behavior is outsourced to ExtractFixedArray(),
|
||||
// but the special case of wanting to have a FixedDoubleArray when given a
|
||||
// zero-length input FixedArray is handled here.
|
||||
macro Extract<FixedArrayType : type>(
|
||||
elements: FixedArrayBase, first: Smi, count: Smi,
|
||||
capacity: Smi): FixedArrayType {
|
||||
return unsafe_cast<FixedArrayType>(
|
||||
ExtractFixedArray(elements, first, count, capacity));
|
||||
}
|
||||
|
||||
Extract<FixedDoubleArray>(
|
||||
elements: FixedArrayBase, first: Smi, count: Smi,
|
||||
capacity: Smi): FixedDoubleArray {
|
||||
if (elements == kEmptyFixedArray) {
|
||||
let newArray: FixedDoubleArray =
|
||||
AllocateZeroedFixedDoubleArray(convert<intptr>(capacity));
|
||||
return newArray;
|
||||
}
|
||||
return unsafe_cast<FixedDoubleArray>(
|
||||
ExtractFixedArray(elements, first, count, capacity));
|
||||
}
|
||||
|
||||
macro FastSplice<FixedArrayType : type, ElementType : type>(
|
||||
args: constexpr Arguments, a: JSArray, length: Smi, newLength: Smi,
|
||||
lengthDelta: Smi, actualStart: Smi, insertCount: Smi,
|
||||
actualDeleteCount: Smi): void labels Bailout {
|
||||
let elements: FixedArrayBase = a.elements;
|
||||
let elementsMap: Map = elements.map;
|
||||
|
||||
// If the spliced array is larger then the
|
||||
// source array, then allocate a new FixedArrayType to hold the result.
|
||||
let newElements: FixedArrayBase = elements;
|
||||
if (elementsMap == kCOWMap || lengthDelta > 0) {
|
||||
newElements =
|
||||
Extract<FixedArrayType>(elements, 0, actualStart, newLength);
|
||||
if (elementsMap == kCOWMap) {
|
||||
newElements.map = elementsMap;
|
||||
}
|
||||
a.elements = newElements;
|
||||
}
|
||||
|
||||
// Copy over inserted elements.
|
||||
let k: Smi = actualStart;
|
||||
if (insertCount > 0) {
|
||||
let typedNewElements: FixedArrayType =
|
||||
unsafe_cast<FixedArrayType>(newElements);
|
||||
for (let e: Object of args [2: ]) {
|
||||
// The argument elements were already validated to be an appropriate
|
||||
// {ElementType} to store in {FixedArrayType}.
|
||||
typedNewElements[k++] = unsafe_cast<ElementType>(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over elements after deleted elements.
|
||||
let count: Smi = length - actualStart - actualDeleteCount;
|
||||
while (count > 0) {
|
||||
let typedElements: FixedArrayType = unsafe_cast<FixedArrayType>(elements);
|
||||
let typedNewElements: FixedArrayType =
|
||||
unsafe_cast<FixedArrayType>(newElements);
|
||||
CopyArrayElement(typedElements, typedNewElements, k - lengthDelta, k);
|
||||
k++;
|
||||
count--;
|
||||
}
|
||||
|
||||
// Fill rest of spliced FixedArray with the hole, but only if the
|
||||
// destination FixedArray is the original array's, since otherwise the array
|
||||
// is pre-filled with holes.
|
||||
if (elements == newElements) {
|
||||
let typedNewElements: FixedArrayType =
|
||||
unsafe_cast<FixedArrayType>(newElements);
|
||||
let limit: Smi = elements.length;
|
||||
while (k < limit) {
|
||||
StoreArrayHole(typedNewElements, k);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the array's length after all the FixedArray shuffling is done.
|
||||
a.length = newLength;
|
||||
}
|
||||
|
||||
macro FastArraySplice(
|
||||
context: Context, args: constexpr Arguments, o: JSReceiver,
|
||||
originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
|
||||
actualDeleteCountNumber: Number): Object
|
||||
labels Bailout {
|
||||
let originalLength: Smi = cast<Smi>(originalLengthNumber) otherwise Bailout;
|
||||
let actualStart: Smi = cast<Smi>(actualStartNumber) otherwise Bailout;
|
||||
let actualDeleteCount: Smi =
|
||||
cast<Smi>(actualDeleteCountNumber) otherwise Bailout;
|
||||
let lengthDelta: Smi = insertCount - actualDeleteCount;
|
||||
let newLength: Smi = originalLength + lengthDelta;
|
||||
|
||||
let a: JSArray = cast<JSArray>(o) otherwise Bailout;
|
||||
|
||||
let map: Map = a.map;
|
||||
if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout;
|
||||
if (IsNoElementsProtectorCellInvalid()) goto Bailout;
|
||||
if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
|
||||
|
||||
// Fast path only works on fast elements kind and with writable length.
|
||||
let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
|
||||
if (!IsFastElementsKind(elementsKind)) goto Bailout;
|
||||
|
||||
let oldElementsKind: ElementsKind = elementsKind;
|
||||
for (let e: Object of args [2: ]) {
|
||||
if (IsFastSmiElementsKind(elementsKind)) {
|
||||
if (TaggedIsNotSmi(e)) {
|
||||
let heapObject: HeapObject = unsafe_cast<HeapObject>(e);
|
||||
elementsKind = IsHeapNumber(heapObject) ?
|
||||
AllowDoubleElements(elementsKind) :
|
||||
AllowNonNumberElements(elementsKind);
|
||||
}
|
||||
} else if (IsDoubleElementsKind(elementsKind)) {
|
||||
if (!IsNumber(e)) {
|
||||
elementsKind = AllowNonNumberElements(elementsKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elementsKind != oldElementsKind) {
|
||||
let smi_elements_kind: Smi = convert<Smi>(convert<int32>(elementsKind));
|
||||
TransitionElementsKindWithKind(context, a, smi_elements_kind);
|
||||
}
|
||||
|
||||
// Make sure that the length hasn't been changed by side-effect.
|
||||
let length: Smi = cast<Smi>(a.length) otherwise Bailout;
|
||||
if (originalLength != length) goto Bailout;
|
||||
|
||||
let deletedResult: JSArray =
|
||||
ExtractFastJSArray(context, a, actualStart, actualDeleteCount);
|
||||
|
||||
if (newLength == 0) {
|
||||
a.elements = kEmptyFixedArray;
|
||||
a.length = 0;
|
||||
return deletedResult;
|
||||
}
|
||||
|
||||
if (IsFastSmiOrTaggedElementsKind(elementsKind)) {
|
||||
FastSplice<FixedArray, Object>(
|
||||
args, a, length, newLength, lengthDelta, actualStart, insertCount,
|
||||
actualDeleteCount) otherwise Bailout;
|
||||
} else {
|
||||
FastSplice<FixedDoubleArray, Number>(
|
||||
args, a, length, newLength, lengthDelta, actualStart, insertCount,
|
||||
actualDeleteCount) otherwise Bailout;
|
||||
}
|
||||
|
||||
return deletedResult;
|
||||
}
|
||||
|
||||
macro FillDeletedElementsArray(
|
||||
context: Context, o: JSReceiver, actualStart: Number,
|
||||
actualDeleteCount: Number, a: JSReceiver): Object {
|
||||
// 10. Let k be 0.
|
||||
let k: Number = 0;
|
||||
|
||||
// 11. Repeat, while k < actualDeleteCount
|
||||
while (k < actualDeleteCount) {
|
||||
// a. Let from be ! ToString(actualStart + k).
|
||||
let from: Number = actualStart + k;
|
||||
|
||||
// b. Let fromPresent be ? HasProperty(O, from).
|
||||
let fromPresent: Boolean = HasProperty(context, o, from);
|
||||
|
||||
// c. If fromPresent is true, then
|
||||
if (fromPresent == True) {
|
||||
// i. Let fromValue be ? Get(O, from).
|
||||
let fromValue: Object = GetProperty(context, o, from);
|
||||
|
||||
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
|
||||
CreateDataProperty(context, a, k, fromValue);
|
||||
}
|
||||
|
||||
// d. Increment k by 1.
|
||||
k++;
|
||||
}
|
||||
// 12. Perform ? Set(A, "length", actualDeleteCount, true).
|
||||
SetProperty(context, a, 'length', actualDeleteCount);
|
||||
return a;
|
||||
}
|
||||
|
||||
// HandleForwardCase implements step 15. "If itemCount < actualDeleteCount,
|
||||
// then...""
|
||||
macro HandleForwardCase(
|
||||
context: Context, o: JSReceiver, len: Number, itemCount: Number,
|
||||
actualStart: Number, actualDeleteCount: Number): void {
|
||||
// 15. If itemCount < actualDeleteCount, then
|
||||
// a. Let k be actualStart.
|
||||
let k: Number = actualStart;
|
||||
|
||||
// b. Repeat, while k < (len - actualDeleteCount)
|
||||
while (k < (len - actualDeleteCount)) {
|
||||
// i. Let from be ! ToString(k + actualDeleteCount).
|
||||
let from: Number = k + actualDeleteCount;
|
||||
// ii. Let to be ! ToString(k + itemCount).
|
||||
let to: Number = k + itemCount;
|
||||
|
||||
// iii. Let fromPresent be ? HasProperty(O, from).
|
||||
let fromPresent: Boolean = HasProperty(context, o, from);
|
||||
|
||||
// iv. If fromPresent is true, then
|
||||
if (fromPresent == True) {
|
||||
// 1. Let fromValue be ? Get(O, from).
|
||||
let fromValue: Object = GetProperty(context, o, from);
|
||||
|
||||
// 2. Perform ? Set(O, to, fromValue, true).
|
||||
SetProperty(context, o, to, fromValue);
|
||||
|
||||
// v. Else fromPresent is false,
|
||||
} else {
|
||||
// 1. Perform ? DeletePropertyOrThrow(O, to).
|
||||
DeleteProperty(context, o, to, kStrict);
|
||||
}
|
||||
// vi. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// c. Let k be len.
|
||||
k = len;
|
||||
|
||||
// d. Repeat, while k > (len - actualDeleteCount + itemCount)
|
||||
while (k > (len - actualDeleteCount + itemCount)) {
|
||||
// i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
|
||||
DeleteProperty(context, o, k - 1, kStrict);
|
||||
// ii. Decrease k by 1.
|
||||
k--;
|
||||
}
|
||||
}
|
||||
|
||||
// HandleBackwardCase implements step 16. "Else if itemCount >
|
||||
// actualDeleteCount, then..."
|
||||
macro HandleBackwardCase(
|
||||
context: Context, o: JSReceiver, len: Number, itemCount: Number,
|
||||
actualStart: Number, actualDeleteCount: Number): void {
|
||||
// 16. Else if itemCount > actualDeleteCount, then
|
||||
// a. Let k be (len - actualDeleteCount).
|
||||
let k: Number = len - actualDeleteCount;
|
||||
|
||||
// b. Repeat, while k > actualStart
|
||||
while (k > actualStart) {
|
||||
// i. Let from be ! ToString(k + actualDeleteCount - 1).
|
||||
let from: Number = k + actualDeleteCount - 1;
|
||||
|
||||
// ii. Let to be ! ToString(k + itemCount - 1).
|
||||
let to: Number = k + itemCount - 1;
|
||||
|
||||
// iii. Let fromPresent be ? HasProperty(O, from).
|
||||
let fromPresent: Boolean = HasProperty(context, o, from);
|
||||
|
||||
// iv. If fromPresent is true, then
|
||||
if (fromPresent == True) {
|
||||
// 1. Let fromValue be ? Get(O, from).
|
||||
let fromValue: Object = GetProperty(context, o, from);
|
||||
|
||||
// 2. Perform ? Set(O, to, fromValue, true).
|
||||
SetProperty(context, o, to, fromValue);
|
||||
|
||||
// v. Else fromPresent is false,
|
||||
} else {
|
||||
// 1. Perform ? DeletePropertyOrThrow(O, to).
|
||||
DeleteProperty(context, o, to, kStrict);
|
||||
}
|
||||
|
||||
// vi. Decrease k by 1.
|
||||
k--;
|
||||
}
|
||||
}
|
||||
|
||||
macro SlowSplice(
|
||||
context: Context, arguments: constexpr Arguments, o: JSReceiver,
|
||||
len: Number, actualStart: Number, insertCount: Smi,
|
||||
actualDeleteCount: Number): Object {
|
||||
let affected: Number = len - actualStart - actualDeleteCount;
|
||||
|
||||
// 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
|
||||
let a: JSReceiver = ArraySpeciesCreate(context, o, actualDeleteCount);
|
||||
let itemCount: Number = insertCount;
|
||||
|
||||
// Steps 9 through 12: creating the array of deleted elements.
|
||||
FillDeletedElementsArray(context, o, actualStart, actualDeleteCount, a);
|
||||
|
||||
// 13. Let items be a List whose elements are, in left-to-right order,
|
||||
// the portion of the actual argument list starting with the third
|
||||
// argument. The list is empty if fewer than three arguments were
|
||||
// passed.
|
||||
// 14. Let itemCount be the Number of elements in items.
|
||||
// (done above).
|
||||
|
||||
// 15. If itemCount < actualDeleteCount, then
|
||||
if (itemCount < actualDeleteCount) {
|
||||
HandleForwardCase(
|
||||
context, o, len, itemCount, actualStart, actualDeleteCount);
|
||||
// 16. Else if itemCount > actualDeleteCount, then
|
||||
} else if (itemCount > actualDeleteCount) {
|
||||
HandleBackwardCase(
|
||||
context, o, len, itemCount, actualStart, actualDeleteCount);
|
||||
}
|
||||
|
||||
// 17. Let k be actualStart.
|
||||
let k: Number = actualStart;
|
||||
|
||||
// 18. Repeat, while items is not empty
|
||||
// a. Remove the first element from items and let E be the value of that
|
||||
// element.
|
||||
if (arguments.length > 2) {
|
||||
for (let e: Object of arguments [2: ]) {
|
||||
// b. Perform ? Set(O, ! ToString(k), E, true).
|
||||
SetProperty(context, o, k, e);
|
||||
|
||||
// c. Increase k by 1.
|
||||
k = k + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
|
||||
// true).
|
||||
SetProperty(context, o, 'length', len - actualDeleteCount + itemCount);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.splice
|
||||
javascript builtin ArraySplice(
|
||||
context: Context, receiver: Object, ...arguments): Object {
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
let o: JSReceiver = ToObject(context, receiver);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
let len: Number = GetLengthProperty(context, o);
|
||||
|
||||
// 3. Let relativeStart be ? ToInteger(start).
|
||||
let start: Object = arguments[0];
|
||||
let relativeStart: Number = ToInteger_Inline(context, start);
|
||||
|
||||
// 4. If relativeStart < 0, let actualStart be max((len + relativeStart),
|
||||
// 0);
|
||||
// else let actualStart be min(relativeStart, len).
|
||||
let actualStart: Number = relativeStart < 0 ?
|
||||
max((len + relativeStart), 0) :
|
||||
min(relativeStart, len);
|
||||
|
||||
let insertCount: Smi;
|
||||
let actualDeleteCount: Number;
|
||||
// 5. If the Number of actual arguments is 0, then
|
||||
if (arguments.length == 0) {
|
||||
// a. Let insertCount be 0.
|
||||
insertCount = 0;
|
||||
// b. Let actualDeleteCount be 0.
|
||||
actualDeleteCount = 0;
|
||||
// 6. Else if the Number of actual arguments is 1, then
|
||||
} else if (arguments.length == 1) {
|
||||
// a. Let insertCount be 0.
|
||||
insertCount = 0;
|
||||
// b. Let actualDeleteCount be len - actualStart.
|
||||
actualDeleteCount = len - actualStart;
|
||||
// 7. Else,
|
||||
} else {
|
||||
// a. Let insertCount be the Number of actual arguments minus 2.
|
||||
insertCount = convert<Smi>(arguments.length) - 2;
|
||||
// b. Let dc be ? ToInteger(deleteCount).
|
||||
let deleteCount: Object = arguments[1];
|
||||
let dc: Number = ToInteger_Inline(context, deleteCount);
|
||||
// c. Let actualDeleteCount be min(max(dc, 0), len - actualStart).
|
||||
actualDeleteCount = min(max(dc, 0), len - actualStart);
|
||||
}
|
||||
|
||||
// 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a
|
||||
// Bailout exception.
|
||||
let new_length: Number = len + insertCount - actualDeleteCount;
|
||||
if (new_length > kMaxSafeInteger) {
|
||||
ThrowTypeError(context, kInvalidArrayLength, start);
|
||||
}
|
||||
|
||||
try {
|
||||
return FastArraySplice(
|
||||
context, arguments, o, len, actualStart, insertCount,
|
||||
actualDeleteCount) otherwise Bailout;
|
||||
}
|
||||
label Bailout {}
|
||||
|
||||
// If the fast case fails, just continue with the slow, correct,
|
||||
// spec-compliant case.
|
||||
return SlowSplice(
|
||||
context, arguments, o, len, actualStart, insertCount,
|
||||
actualDeleteCount);
|
||||
}
|
||||
}
|
@ -24,293 +24,40 @@ module array {
|
||||
}
|
||||
}
|
||||
|
||||
macro FastArraySplice(
|
||||
context: Context, args: constexpr Arguments, o: Object,
|
||||
originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
|
||||
actualDeleteCountNumber: Number): Object
|
||||
labels Bailout {
|
||||
let originalLength: Smi = cast<Smi>(originalLengthNumber) otherwise Bailout;
|
||||
let actualStart: Smi = cast<Smi>(actualStartNumber) otherwise Bailout;
|
||||
let actualDeleteCount: Smi =
|
||||
cast<Smi>(actualDeleteCountNumber) otherwise Bailout;
|
||||
let lengthDelta: Smi = insertCount - actualDeleteCount;
|
||||
let newLength: Smi = originalLength + lengthDelta;
|
||||
|
||||
let a: JSArray = cast<JSArray>(o) otherwise Bailout;
|
||||
|
||||
let map: Map = a.map;
|
||||
if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout;
|
||||
if (IsNoElementsProtectorCellInvalid()) goto Bailout;
|
||||
if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
|
||||
|
||||
// Fast path only works on fast elements kind and with writable length.
|
||||
let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
|
||||
if (!IsFastElementsKind(elementsKind)) goto Bailout;
|
||||
|
||||
// For now, only support non-double fast elements
|
||||
if (!IsFastSmiOrTaggedElementsKind(elementsKind)) goto Bailout;
|
||||
|
||||
if (IsFastSmiElementsKind(elementsKind)) {
|
||||
for (let e: Object of args [2: ]) {
|
||||
if (TaggedIsNotSmi(e)) goto Bailout;
|
||||
}
|
||||
macro IsArray(o: Object): bool {
|
||||
try {
|
||||
let array: JSArray = cast<JSArray>(o) otherwise NotArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Make sure that the length hasn't been changed by side-effect.
|
||||
let length: Smi = cast<Smi>(a.length) otherwise Bailout;
|
||||
if (originalLength != length) goto Bailout;
|
||||
|
||||
let deletedResult: JSArray =
|
||||
ExtractFastJSArray(context, a, actualStart, actualDeleteCount);
|
||||
|
||||
if (newLength == 0) {
|
||||
a.elements = kEmptyFixedArray;
|
||||
a.length = 0;
|
||||
return deletedResult;
|
||||
label NotArray {
|
||||
return false;
|
||||
}
|
||||
|
||||
let elements: FixedArray = cast<FixedArray>(a.elements) otherwise Bailout;
|
||||
let elementsMap: Map = elements.map;
|
||||
|
||||
// If the source is a COW array or the spliced array is larger then the
|
||||
// source array, then allocate a new FixedArray to hold the result.
|
||||
let newElements: FixedArray = elements;
|
||||
if ((elementsMap == kCOWMap) || (lengthDelta > 0)) {
|
||||
newElements = ExtractFixedArray(
|
||||
elements, 0, actualStart, newLength, kAllFixedArrays);
|
||||
newElements.map = elementsMap;
|
||||
a.elements = newElements;
|
||||
}
|
||||
|
||||
// Double check that the array is still in fast elements mode
|
||||
assert(IsFastSmiElementsKind(a.map.elements_kind));
|
||||
|
||||
// Copy over inserted elements.
|
||||
let k: Smi = actualStart;
|
||||
if (insertCount > 0) {
|
||||
for (let e: Object of args [2: ]) {
|
||||
newElements[k++] = e;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over elements after deleted elements.
|
||||
let count: Smi = length - actualStart - actualDeleteCount;
|
||||
while (count > 0) {
|
||||
let e: Object = elements[k - lengthDelta];
|
||||
newElements[k++] = e;
|
||||
count--;
|
||||
}
|
||||
|
||||
// Fill rest of spliced FixedArray with the hole, but only if the
|
||||
// destination FixedArray is the original array's, since otherwise the array
|
||||
// is pre-filled with holes.
|
||||
if (elements == newElements) {
|
||||
let limit: Smi = elements.length;
|
||||
while (k < limit) {
|
||||
newElements[k++] = Hole;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the array's length after all the FixedArray shuffling is done.
|
||||
a.length = newLength;
|
||||
|
||||
return deletedResult;
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.splice
|
||||
javascript builtin ArraySpliceTorque(
|
||||
context: Context, receiver: Object, ...arguments): Object {
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
let o: JSReceiver = ToObject(context, receiver);
|
||||
macro StoreArrayHole(elements: FixedDoubleArray, k: Smi): void {
|
||||
StoreFixedDoubleArrayHoleSmi(elements, k);
|
||||
}
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
let len: Number = GetLengthProperty(context, o);
|
||||
macro StoreArrayHole(elements: FixedArray, k: Smi): void {
|
||||
elements[k] = Hole;
|
||||
}
|
||||
|
||||
// 3. Let relativeStart be ? ToInteger(start).
|
||||
let start: Object = arguments[0];
|
||||
let relativeStart: Number = ToInteger_Inline(context, start);
|
||||
|
||||
// 4. If relativeStart < 0, let actualStart be max((len + relativeStart),
|
||||
// 0);
|
||||
// else let actualStart be min(relativeStart, len).
|
||||
let actualStart: Number = relativeStart < 0 ?
|
||||
max((len + relativeStart), 0) :
|
||||
min(relativeStart, len);
|
||||
|
||||
let insertCount: Smi;
|
||||
let actualDeleteCount: Number;
|
||||
// 5. If the Number of actual arguments is 0, then
|
||||
if (arguments.length == 0) {
|
||||
// a. Let insertCount be 0.
|
||||
insertCount = 0;
|
||||
// b. Let actualDeleteCount be 0.
|
||||
actualDeleteCount = 0;
|
||||
// 6. Else if the Number of actual arguments is 1, then
|
||||
} else if (arguments.length == 1) {
|
||||
// a. Let insertCount be 0.
|
||||
insertCount = 0;
|
||||
// b. Let actualDeleteCount be len - actualStart.
|
||||
actualDeleteCount = len - actualStart;
|
||||
// 7. Else,
|
||||
} else {
|
||||
// a. Let insertCount be the Number of actual arguments minus 2.
|
||||
insertCount = convert<Smi>(arguments.length) - 2;
|
||||
// b. Let dc be ? ToInteger(deleteCount).
|
||||
let deleteCount: Object = arguments[1];
|
||||
let dc: Number = ToInteger_Inline(context, deleteCount);
|
||||
// c. Let actualDeleteCount be min(max(dc, 0), len - actualStart).
|
||||
actualDeleteCount = min(max(dc, 0), len - actualStart);
|
||||
}
|
||||
|
||||
// 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a
|
||||
// Bailout exception.
|
||||
if (len + insertCount - actualDeleteCount > kMaxSafeInteger) {
|
||||
ThrowRangeError(context, kInvalidArrayLength);
|
||||
}
|
||||
macro CopyArrayElement(
|
||||
elements: FixedArray, newElements: FixedArray, from: Smi, to: Smi): void {
|
||||
let e: Object = elements[from];
|
||||
newElements[to] = e;
|
||||
}
|
||||
|
||||
macro CopyArrayElement(
|
||||
elements: FixedDoubleArray, newElements: FixedDoubleArray, from: Smi,
|
||||
to: Smi): void {
|
||||
try {
|
||||
return FastArraySplice(
|
||||
context, arguments, o, len, actualStart, insertCount,
|
||||
actualDeleteCount) otherwise Bailout;
|
||||
let floatValue: float64 = LoadDoubleWithHoleCheck(elements, from)
|
||||
otherwise FoundHole;
|
||||
newElements[to] = floatValue;
|
||||
}
|
||||
label Bailout {}
|
||||
// If the fast case fails, just continue with the slow, correct,
|
||||
// spec-compliant case.
|
||||
|
||||
// 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
|
||||
let a: Object = ArraySpeciesCreate(context, o, actualDeleteCount);
|
||||
|
||||
// 10. Let k be 0.
|
||||
let k: Number = 0;
|
||||
|
||||
// 11. Repeat, while k < actualDeleteCount
|
||||
while (k < actualDeleteCount) {
|
||||
// a. Let from be ! ToString(actualStart + k).
|
||||
let from: String = ToString_Inline(context, actualStart + k);
|
||||
|
||||
// b. Let fromPresent be ? HasProperty(O, from).
|
||||
let fromPresent: Oddball = HasProperty(context, o, from);
|
||||
|
||||
// c. If fromPresent is true, then
|
||||
if (fromPresent == True) {
|
||||
// i. Let fromValue be ? Get(O, from).
|
||||
let fromValue: Object = GetProperty(context, o, from);
|
||||
|
||||
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
|
||||
CreateDataProperty(context, a, ToString_Inline(context, k), fromValue);
|
||||
}
|
||||
|
||||
// d. Increment k by 1.
|
||||
k = k + 1;
|
||||
label FoundHole {
|
||||
StoreArrayHole(newElements, to);
|
||||
}
|
||||
|
||||
// 12. Perform ? Set(A, "length", actualDeleteCount, true).
|
||||
SetProperty(context, a, 'length', actualDeleteCount);
|
||||
|
||||
// 13. Let items be a List whose elements are, in left-to-right order,
|
||||
// the portion of the actual argument list starting with the third
|
||||
// argument. The list is empty if fewer than three arguments were
|
||||
// passed.
|
||||
// 14. Let itemCount be the Number of elements in items.
|
||||
let itemCount: Number = insertCount;
|
||||
|
||||
// 15. If itemCount < actualDeleteCount, then
|
||||
if (itemCount < actualDeleteCount) {
|
||||
// a. Let k be actualStart.
|
||||
let k: Number = actualStart;
|
||||
|
||||
// b. Repeat, while k < (len - actualDeleteCount)
|
||||
while (k < (len - actualDeleteCount)) {
|
||||
// i. Let from be ! ToString(k + actualDeleteCount).
|
||||
let from: String = ToString_Inline(context, k + actualDeleteCount);
|
||||
// ii. Let to be ! ToString(k + itemCount).
|
||||
let to: String = ToString_Inline(context, k + itemCount);
|
||||
|
||||
// iii. Let fromPresent be ? HasProperty(O, from).
|
||||
let fromPresent: Oddball = HasProperty(context, o, from);
|
||||
|
||||
// iv. If fromPresent is true, then
|
||||
if (fromPresent == True) {
|
||||
// 1. Let fromValue be ? Get(O, from).
|
||||
let fromValue: Object = GetProperty(context, o, from);
|
||||
|
||||
// 2. Perform ? Set(O, to, fromValue, true).
|
||||
SetProperty(context, o, to, fromValue);
|
||||
|
||||
// v. Else fromPresent is false,
|
||||
} else {
|
||||
// 1. Perform ? DeletePropertyOrThrow(O, to).
|
||||
DeleteProperty(context, o, to, kStrict);
|
||||
}
|
||||
// vi. Increase k by 1.
|
||||
k = k + 1;
|
||||
}
|
||||
|
||||
// c. Let k be len.
|
||||
k = len;
|
||||
// d. Repeat, while k > (len - actualDeleteCount + itemCount)
|
||||
while (k > (len - actualDeleteCount + itemCount)) {
|
||||
// i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
|
||||
DeleteProperty(context, o, ToString_Inline(context, k - 1), kStrict);
|
||||
|
||||
// ii. Decrease k by 1.
|
||||
k = k - 1;
|
||||
}
|
||||
// 16. Else if itemCount > actualDeleteCount, then
|
||||
} else if (itemCount > actualDeleteCount) {
|
||||
// a. Let k be (len - actualDeleteCount).
|
||||
let k: Number = len - actualDeleteCount;
|
||||
|
||||
// b. Repeat, while k > actualStart
|
||||
while (k > actualStart) {
|
||||
// i. Let from be ! ToString(k + actualDeleteCount - 1).
|
||||
let from: String = ToString_Inline(context, k + actualDeleteCount - 1);
|
||||
|
||||
// ii. Let to be ! ToString(k + itemCount - 1).
|
||||
let to: String = ToString_Inline(context, k + itemCount - 1);
|
||||
|
||||
// iii. Let fromPresent be ? HasProperty(O, from).
|
||||
let fromPresent: Oddball = HasProperty(context, o, from);
|
||||
|
||||
// iv. If fromPresent is true, then
|
||||
if (fromPresent == True) {
|
||||
// 1. Let fromValue be ? Get(O, from).
|
||||
let fromValue: Object = GetProperty(context, o, from);
|
||||
|
||||
// 2. Perform ? Set(O, to, fromValue, true).
|
||||
SetProperty(context, o, to, fromValue);
|
||||
|
||||
// v. Else fromPresent is false,
|
||||
} else {
|
||||
// 1. Perform ? DeletePropertyOrThrow(O, to).
|
||||
DeleteProperty(context, o, to, kStrict);
|
||||
}
|
||||
|
||||
// vi. Decrease k by 1.
|
||||
k = k - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Let k be actualStart.
|
||||
k = actualStart;
|
||||
|
||||
// 18. Repeat, while items is not empty
|
||||
// a. Remove the first element from items and let E be the value of that
|
||||
// element.
|
||||
if (arguments.length > 2) {
|
||||
for (let e: Object of arguments [2: ]) {
|
||||
// b. Perform ? Set(O, ! ToString(k), E, true).
|
||||
SetProperty(context, o, ToString_Inline(context, k), e);
|
||||
|
||||
// c. Increase k by 1.
|
||||
k = k + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
|
||||
// true).
|
||||
SetProperty(context, o, 'length', len - actualDeleteCount + itemCount);
|
||||
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +107,8 @@ type FixedUint8ClampedArray extends FixedTypedArray;
|
||||
type FixedBigUint64Array extends FixedTypedArray;
|
||||
type FixedBigInt64Array extends FixedTypedArray;
|
||||
|
||||
const kFixedDoubleArrays: constexpr ExtractFixedArrayFlags generates
|
||||
'ExtractFixedArrayFlag::kFixedDoubleArrays';
|
||||
const kAllFixedArrays: constexpr ExtractFixedArrayFlags generates
|
||||
'ExtractFixedArrayFlag::kAllFixedArrays';
|
||||
const kFixedArrays: constexpr ExtractFixedArrayFlags generates
|
||||
@ -167,9 +169,8 @@ const kSloppy: constexpr LanguageMode generates 'LanguageMode::kSloppy';
|
||||
const SMI_PARAMETERS: constexpr ParameterMode generates 'SMI_PARAMETERS';
|
||||
const INTPTR_PARAMETERS: constexpr ParameterMode generates 'INTPTR_PARAMETERS';
|
||||
|
||||
|
||||
const SKIP_WRITE_BARRIER: constexpr WriteBarrierMode
|
||||
generates 'SKIP_WRITE_BARRIER';
|
||||
generates 'SKIP_WRITE_BARRIER';
|
||||
|
||||
extern macro Is64(): constexpr bool;
|
||||
|
||||
@ -177,6 +178,7 @@ extern macro SelectBooleanConstant(bool): Boolean;
|
||||
|
||||
extern macro Print(constexpr string);
|
||||
extern macro Print(constexpr string, Object);
|
||||
extern macro Comment(constexpr string);
|
||||
extern macro Print(Object);
|
||||
extern macro DebugBreak();
|
||||
extern macro ToInteger_Inline(Context, Object): Number;
|
||||
@ -195,7 +197,8 @@ extern macro ThrowTypeError(Context, constexpr MessageTemplate): never;
|
||||
extern macro ThrowTypeError(Context, constexpr MessageTemplate, Object): never;
|
||||
extern macro ThrowTypeError(
|
||||
Context, constexpr MessageTemplate, Object, Object, Object): never;
|
||||
extern macro ArraySpeciesCreate(Context, Object, Number): Object;
|
||||
extern macro ArraySpeciesCreate(Context, Object, Number): JSReceiver;
|
||||
extern macro InternalArrayCreate(Context, Number): JSArray;
|
||||
extern macro EnsureArrayPushable(Map): ElementsKind labels Bailout;
|
||||
|
||||
extern builtin ToObject(Context, Object): JSReceiver;
|
||||
@ -205,7 +208,9 @@ extern macro IsTheHole(Object): bool;
|
||||
extern macro IsString(HeapObject): bool;
|
||||
extern builtin ToString(Context, Object): String;
|
||||
|
||||
extern runtime CreateDataProperty(Context, Object, Object, Object);
|
||||
extern runtime NormalizeElements(Context, JSObject);
|
||||
extern runtime TransitionElementsKindWithKind(Context, JSObject, Smi);
|
||||
extern runtime CreateDataProperty(Context, JSReceiver, Object, Object);
|
||||
|
||||
extern macro LoadRoot(constexpr RootListIndex): Object;
|
||||
extern macro StoreRoot(constexpr RootListIndex, Object): Object;
|
||||
@ -232,6 +237,10 @@ extern operator '>=' macro SmiGreaterThanOrEqual(Smi, Smi): bool;
|
||||
extern operator '==' macro ElementsKindEqual(
|
||||
constexpr ElementsKind, constexpr ElementsKind): constexpr bool;
|
||||
extern operator '==' macro ElementsKindEqual(ElementsKind, ElementsKind): bool;
|
||||
operator '!=' macro ElementsKindNotEqual(
|
||||
k1: ElementsKind, k2: ElementsKind): bool {
|
||||
return !ElementsKindEqual(k1, k2);
|
||||
}
|
||||
extern macro IsFastElementsKind(constexpr ElementsKind): constexpr bool;
|
||||
extern macro IsDoubleElementsKind(constexpr ElementsKind): constexpr bool;
|
||||
|
||||
@ -250,11 +259,14 @@ extern operator '>=' macro UintPtrGreaterThanOrEqual(uintptr, uintptr): bool;
|
||||
extern operator '==' macro Float64Equal(float64, float64): bool;
|
||||
extern operator '!=' macro Float64NotEqual(float64, float64): bool;
|
||||
|
||||
extern operator
|
||||
'==' macro BranchIfNumberEqual(Number, Number): never labels Taken, NotTaken;
|
||||
extern operator
|
||||
'<' macro BranchIfNumberLessThan(Number, Number): never labels Taken, NotTaken;
|
||||
extern operator
|
||||
'<=' macro BranchIfNumberLessThanOrEqual(Number, Number): never labels Taken,
|
||||
NotTaken;
|
||||
|
||||
extern operator
|
||||
'>' macro BranchIfNumberGreaterThan(Number, Number): never labels Taken,
|
||||
NotTaken;
|
||||
@ -327,8 +339,8 @@ extern operator '.length' macro GetArgumentsLength(constexpr Arguments): intptr;
|
||||
extern operator
|
||||
'[]' macro GetArgumentValue(constexpr Arguments, intptr): Object;
|
||||
|
||||
extern operator 'is<Smi>' macro TaggedIsSmi(Object): bool;
|
||||
extern operator 'isnt<Smi>' macro TaggedIsNotSmi(Object): bool;
|
||||
extern macro TaggedIsSmi(Object): bool;
|
||||
extern macro TaggedIsNotSmi(Object): bool;
|
||||
extern macro TaggedIsPositiveSmi(Object): bool;
|
||||
|
||||
extern macro HeapObjectToJSDataView(HeapObject): JSDataView labels CastError;
|
||||
@ -336,18 +348,20 @@ extern macro TaggedToHeapObject(Object): HeapObject labels CastError;
|
||||
extern macro TaggedToSmi(Object): Smi labels CastError;
|
||||
extern macro HeapObjectToJSArray(HeapObject): JSArray labels CastError;
|
||||
extern macro HeapObjectToCallable(HeapObject): Callable labels CastError;
|
||||
extern macro HeapObjectToFixedArray(HeapObject):
|
||||
FixedArray labels CastError;
|
||||
extern macro HeapObjectToFixedArray(HeapObject): FixedArray labels CastError;
|
||||
extern macro HeapObjectToFixedDoubleArray(HeapObject):
|
||||
FixedDoubleArray labels CastError;
|
||||
extern macro TaggedToNumber(Object): Number labels CastError;
|
||||
|
||||
macro cast_HeapObject<A : type>(o : HeapObject) : A labels CastError;
|
||||
cast_HeapObject<HeapObject>(o : HeapObject) : HeapObject labels CastError { return o; }
|
||||
macro cast_HeapObject<A : type>(o: HeapObject): A labels CastError;
|
||||
cast_HeapObject<HeapObject>(o: HeapObject): HeapObject labels CastError {
|
||||
return o;
|
||||
}
|
||||
cast_HeapObject<FixedArray>(o: HeapObject): FixedArray labels CastError {
|
||||
return HeapObjectToFixedArray(o) otherwise CastError;
|
||||
}
|
||||
cast_HeapObject<FixedDoubleArray>(o: HeapObject): FixedDoubleArray labels CastError {
|
||||
cast_HeapObject<FixedDoubleArray>(o: HeapObject):
|
||||
FixedDoubleArray labels CastError {
|
||||
return HeapObjectToFixedDoubleArray(o) otherwise CastError;
|
||||
}
|
||||
cast_HeapObject<JSDataView>(o: HeapObject): JSDataView labels CastError {
|
||||
@ -367,8 +381,8 @@ macro cast<A : type>(o: HeapObject): A labels CastError {
|
||||
// cast_HeapObject allows this default-implementation to be non-recursive.
|
||||
// Otherwise the generated CSA code might run into infinite recursion.
|
||||
macro cast<A : type>(o: Object): A labels CastError {
|
||||
return cast_HeapObject<A>(
|
||||
TaggedToHeapObject(o) otherwise CastError) otherwise CastError;
|
||||
return cast_HeapObject<A>(TaggedToHeapObject(o) otherwise CastError)
|
||||
otherwise CastError;
|
||||
}
|
||||
cast<Smi>(o: Object): Smi labels CastError {
|
||||
return TaggedToSmi(o) otherwise CastError;
|
||||
@ -474,6 +488,13 @@ from_constexpr<Object>(s: constexpr string): Object {
|
||||
macro convert<A : type>(i: constexpr int31): A {
|
||||
return i;
|
||||
}
|
||||
extern macro ConvertElementsKindToInt(ElementsKind): int32;
|
||||
|
||||
macro convert<A : type>(elements_kind: ElementsKind): A;
|
||||
convert<int32>(elements_kind: ElementsKind): int32 {
|
||||
return ConvertElementsKindToInt(elements_kind);
|
||||
}
|
||||
|
||||
macro convert<A : type>(i: int32): A;
|
||||
convert<Number>(i: int32): Number {
|
||||
return ChangeInt32ToTagged(i);
|
||||
@ -560,6 +581,9 @@ unsafe_cast<HeapNumber>(n: Number): HeapNumber {
|
||||
return UnsafeCastNumberToHeapNumber(n);
|
||||
}
|
||||
macro unsafe_cast<A : type>(o: Object): A;
|
||||
unsafe_cast<Object>(o: Object): Object {
|
||||
return o;
|
||||
}
|
||||
unsafe_cast<FixedArray>(o: Object): FixedArray {
|
||||
return UnsafeCastObjectToFixedArray(o);
|
||||
}
|
||||
@ -646,13 +670,16 @@ extern operator
|
||||
extern operator
|
||||
'[]=' macro StoreFixedArrayElement(FixedArray, intptr, Object): void;
|
||||
extern operator
|
||||
'[]=' macro StoreFixedArrayElement(
|
||||
FixedArray, constexpr int31, Object): void;
|
||||
'[]=' macro StoreFixedArrayElement(FixedArray, constexpr int31, Object): void;
|
||||
extern operator
|
||||
'[]=' macro StoreFixedArrayElementSmi(FixedArray, Smi, Object): void;
|
||||
operator '[]=' macro StoreFixedDoubleArrayNumber(
|
||||
a: FixedDoubleArray, index: Smi, value: Number): void {
|
||||
a[index] = convert<float64>(value);
|
||||
}
|
||||
|
||||
extern macro StoreFixedArrayElementSmi(FixedArray, Smi, Object,
|
||||
constexpr WriteBarrierMode): void;
|
||||
extern macro StoreFixedArrayElementSmi(
|
||||
FixedArray, Smi, Object, constexpr WriteBarrierMode): void;
|
||||
|
||||
extern operator '.instance_type' macro LoadMapInstanceType(Map): int32;
|
||||
|
||||
@ -678,6 +705,28 @@ extern macro IsFastSmiOrTaggedElementsKind(ElementsKind): bool;
|
||||
extern macro IsFastSmiElementsKind(ElementsKind): bool;
|
||||
extern macro IsHoleyFastElementsKind(ElementsKind): bool;
|
||||
|
||||
macro AllowDoubleElements(kind: ElementsKind): ElementsKind {
|
||||
if (kind == PACKED_SMI_ELEMENTS) {
|
||||
return PACKED_DOUBLE_ELEMENTS;
|
||||
} else if (kind == HOLEY_SMI_ELEMENTS) {
|
||||
return HOLEY_DOUBLE_ELEMENTS;
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
|
||||
macro AllowNonNumberElements(kind: ElementsKind): ElementsKind {
|
||||
if (kind == PACKED_SMI_ELEMENTS) {
|
||||
return PACKED_ELEMENTS;
|
||||
} else if (kind == HOLEY_SMI_ELEMENTS) {
|
||||
return HOLEY_ELEMENTS;
|
||||
} else if (kind == PACKED_DOUBLE_ELEMENTS) {
|
||||
return PACKED_ELEMENTS;
|
||||
} else if (kind == HOLEY_DOUBLE_ELEMENTS) {
|
||||
return HOLEY_ELEMENTS;
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
|
||||
extern macro AllocateZeroedFixedArray(intptr): FixedArray;
|
||||
extern macro AllocateZeroedFixedDoubleArray(intptr): FixedDoubleArray;
|
||||
|
||||
@ -693,8 +742,13 @@ extern macro AllocateJSArray(constexpr ElementsKind, Map, Smi, Smi): JSArray;
|
||||
extern macro IsElementsKindGreaterThan(
|
||||
ElementsKind, constexpr ElementsKind): bool;
|
||||
|
||||
extern operator
|
||||
'[]=' macro StoreFixedDoubleArrayElementSmi(
|
||||
FixedDoubleArray, Smi, float64): void;
|
||||
|
||||
extern macro LoadDoubleWithHoleCheck(FixedDoubleArray, Smi): float64
|
||||
labels IfHole;
|
||||
extern macro StoreFixedDoubleArrayHoleSmi(FixedDoubleArray, Smi): void;
|
||||
|
||||
extern macro Call(Context, Callable, Object): Object;
|
||||
extern macro Call(Context, Callable, Object, Object): Object;
|
||||
@ -705,8 +759,7 @@ extern macro Call(
|
||||
extern macro Call(
|
||||
Context, Callable, Object, Object, Object, Object, Object, Object): Object;
|
||||
|
||||
extern macro ExtractFixedArray(
|
||||
FixedArray, Smi, Smi, Smi, constexpr ExtractFixedArrayFlags): FixedArray;
|
||||
extern macro ExtractFixedArray(FixedArrayBase, Smi, Smi, Smi): FixedArrayBase;
|
||||
|
||||
extern builtin ExtractFastJSArray(Context, JSArray, Smi, Smi): JSArray;
|
||||
|
||||
@ -741,12 +794,17 @@ labels IfHole {
|
||||
}
|
||||
}
|
||||
|
||||
extern macro TransitionElementsKind(
|
||||
JSObject, Map, ElementsKind, ElementsKind): void labels Bailout;
|
||||
|
||||
extern macro IsCallable(HeapObject): bool;
|
||||
extern macro IsJSArray(HeapObject): bool;
|
||||
extern macro IsJSReceiver(HeapObject): bool;
|
||||
extern macro TaggedIsCallable(Object): bool;
|
||||
extern macro IsDetachedBuffer(JSArrayBuffer): bool;
|
||||
extern macro IsHeapNumber(HeapObject): bool;
|
||||
extern macro IsFixedArray(HeapObject): bool;
|
||||
extern macro IsNumber(Object): bool;
|
||||
extern macro IsExtensibleMap(Map): bool;
|
||||
extern macro IsCustomElementsReceiverInstanceType(int32): bool;
|
||||
extern macro IsFastJSArray(Object, Context): bool;
|
||||
@ -758,9 +816,9 @@ macro NumberIsNaN(number: Number): bool {
|
||||
case (Smi) {
|
||||
return false;
|
||||
} case (hn : HeapNumber) {
|
||||
let value: float64 = convert<float64>(hn);
|
||||
return value != value;
|
||||
}
|
||||
let value: float64 = convert<float64>(hn);
|
||||
return value != value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,34 +23,6 @@ namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
inline bool ClampedToInteger(Isolate* isolate, Object* object, int* out) {
|
||||
// This is an extended version of ECMA-262 7.1.11 handling signed values
|
||||
// Try to convert object to a number and clamp values to [kMinInt, kMaxInt]
|
||||
if (object->IsSmi()) {
|
||||
*out = Smi::ToInt(object);
|
||||
return true;
|
||||
} else if (object->IsHeapNumber()) {
|
||||
double value = HeapNumber::cast(object)->value();
|
||||
if (std::isnan(value)) {
|
||||
*out = 0;
|
||||
} else if (value > kMaxInt) {
|
||||
*out = kMaxInt;
|
||||
} else if (value < kMinInt) {
|
||||
*out = kMinInt;
|
||||
} else {
|
||||
*out = static_cast<int>(value);
|
||||
}
|
||||
return true;
|
||||
} else if (object->IsNullOrUndefined(isolate)) {
|
||||
*out = 0;
|
||||
return true;
|
||||
} else if (object->IsBoolean()) {
|
||||
*out = object->IsTrue(isolate);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate,
|
||||
JSArray* receiver) {
|
||||
return JSObject::PrototypeHasNoElements(isolate, receiver);
|
||||
@ -637,67 +609,6 @@ BUILTIN(ArrayUnshift) {
|
||||
return Smi::FromInt(new_length);
|
||||
}
|
||||
|
||||
BUILTIN(ArraySplice) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> receiver = args.receiver();
|
||||
if (V8_UNLIKELY(
|
||||
!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3,
|
||||
args.length() - 3) ||
|
||||
// If this is a subclass of Array, then call out to JS.
|
||||
!Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) ||
|
||||
// If anything with @@species has been messed with, call out to JS.
|
||||
!isolate->IsArraySpeciesLookupChainIntact())) {
|
||||
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
|
||||
}
|
||||
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
|
||||
|
||||
int argument_count = args.length() - 1;
|
||||
int relative_start = 0;
|
||||
if (argument_count > 0) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
if (!ClampedToInteger(isolate, args[1], &relative_start)) {
|
||||
AllowHeapAllocation allow_allocation;
|
||||
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
|
||||
}
|
||||
}
|
||||
int len = Smi::ToInt(array->length());
|
||||
// clip relative start to [0, len]
|
||||
int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
|
||||
: Min(relative_start, len);
|
||||
|
||||
int actual_delete_count;
|
||||
if (argument_count == 1) {
|
||||
// SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
|
||||
// given as a request to delete all the elements from the start.
|
||||
// And it differs from the case of undefined delete count.
|
||||
// This does not follow ECMA-262, but we do the same for compatibility.
|
||||
DCHECK_GE(len - actual_start, 0);
|
||||
actual_delete_count = len - actual_start;
|
||||
} else {
|
||||
int delete_count = 0;
|
||||
DisallowHeapAllocation no_gc;
|
||||
if (argument_count > 1) {
|
||||
if (!ClampedToInteger(isolate, args[2], &delete_count)) {
|
||||
AllowHeapAllocation allow_allocation;
|
||||
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
|
||||
}
|
||||
}
|
||||
actual_delete_count = Min(Max(delete_count, 0), len - actual_start);
|
||||
}
|
||||
|
||||
int add_count = (argument_count > 1) ? (argument_count - 2) : 0;
|
||||
int new_length = len - actual_delete_count + add_count;
|
||||
|
||||
if (new_length != len && JSArray::HasReadOnlyLength(array)) {
|
||||
AllowHeapAllocation allow_allocation;
|
||||
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
|
||||
}
|
||||
ElementsAccessor* accessor = array->GetElementsAccessor();
|
||||
Handle<JSArray> result_array = accessor->Splice(
|
||||
array, actual_start, actual_delete_count, &args, add_count);
|
||||
return *result_array;
|
||||
}
|
||||
|
||||
// Array Concat -------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
@ -319,8 +319,6 @@ namespace internal {
|
||||
TFJ(ArrayPrototypeShift, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 #sec-array.prototype.slice */ \
|
||||
TFJ(ArrayPrototypeSlice, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 #sec-array.prototype.splice */ \
|
||||
CPP(ArraySplice) \
|
||||
/* ES6 #sec-array.prototype.unshift */ \
|
||||
CPP(ArrayUnshift) \
|
||||
/* Support for Array.from and other array-copying idioms */ \
|
||||
|
@ -4246,6 +4246,33 @@ void CodeStubAssembler::FillFixedArrayWithValue(
|
||||
mode);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreFixedDoubleArrayHole(
|
||||
TNode<FixedDoubleArray> array, Node* index, ParameterMode parameter_mode) {
|
||||
CSA_SLOW_ASSERT(this, MatchesParameterMode(index, parameter_mode));
|
||||
Node* offset =
|
||||
ElementOffsetFromIndex(index, PACKED_DOUBLE_ELEMENTS, parameter_mode,
|
||||
FixedArray::kHeaderSize - kHeapObjectTag);
|
||||
CSA_ASSERT(this, IsOffsetInBounds(
|
||||
offset, LoadAndUntagFixedArrayBaseLength(array),
|
||||
FixedDoubleArray::kHeaderSize, PACKED_DOUBLE_ELEMENTS));
|
||||
Node* double_hole =
|
||||
Is64() ? ReinterpretCast<UintPtrT>(Int64Constant(kHoleNanInt64))
|
||||
: ReinterpretCast<UintPtrT>(Int32Constant(kHoleNanLower32));
|
||||
// TODO(danno): When we have a Float32/Float64 wrapper class that
|
||||
// preserves double bits during manipulation, remove this code/change
|
||||
// this to an indexed Float64 store.
|
||||
if (Is64()) {
|
||||
StoreNoWriteBarrier(MachineRepresentation::kWord64, array, offset,
|
||||
double_hole);
|
||||
} else {
|
||||
StoreNoWriteBarrier(MachineRepresentation::kWord32, array, offset,
|
||||
double_hole);
|
||||
StoreNoWriteBarrier(MachineRepresentation::kWord32, array,
|
||||
IntPtrAdd(offset, IntPtrConstant(kPointerSize)),
|
||||
double_hole);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::FillFixedArrayWithSmiZero(TNode<FixedArray> array,
|
||||
TNode<IntPtrT> length) {
|
||||
CSA_ASSERT(this, WordEqual(length, LoadAndUntagFixedArrayBaseLength(array)));
|
||||
@ -10050,6 +10077,10 @@ void CodeStubAssembler::BranchIfNumberRelationalComparison(
|
||||
// Both {left} and {right} are Smi, so just perform a fast
|
||||
// Smi comparison.
|
||||
switch (op) {
|
||||
case Operation::kEqual:
|
||||
BranchIfSmiEqual(smi_left, smi_right, if_true,
|
||||
if_false);
|
||||
break;
|
||||
case Operation::kLessThan:
|
||||
BranchIfSmiLessThan(smi_left, smi_right, if_true,
|
||||
if_false);
|
||||
@ -10096,6 +10127,10 @@ void CodeStubAssembler::BranchIfNumberRelationalComparison(
|
||||
BIND(&do_float_comparison);
|
||||
{
|
||||
switch (op) {
|
||||
case Operation::kEqual:
|
||||
Branch(Float64Equal(var_left_float.value(), var_right_float.value()),
|
||||
if_true, if_false);
|
||||
break;
|
||||
case Operation::kLessThan:
|
||||
Branch(Float64LessThan(var_left_float.value(), var_right_float.value()),
|
||||
if_true, if_false);
|
||||
@ -12001,6 +12036,15 @@ Node* CodeStubAssembler::ArraySpeciesCreate(TNode<Context> context,
|
||||
len);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::InternalArrayCreate(TNode<Context> context,
|
||||
TNode<Number> len) {
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Node* const constructor = LoadContextElement(
|
||||
native_context, Context::INTERNAL_ARRAY_FUNCTION_INDEX);
|
||||
return ConstructJS(CodeFactory::Construct(isolate()), context, constructor,
|
||||
len);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) {
|
||||
CSA_ASSERT(this, HasInstanceType(buffer, JS_ARRAY_BUFFER_TYPE));
|
||||
|
||||
|
@ -1210,6 +1210,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
TNode<FixedDoubleArray> object, Node* index, TNode<Float64T> value,
|
||||
ParameterMode parameter_mode = INTPTR_PARAMETERS);
|
||||
|
||||
void StoreFixedDoubleArrayElementSmi(TNode<FixedDoubleArray> object,
|
||||
TNode<Smi> index,
|
||||
TNode<Float64T> value) {
|
||||
StoreFixedDoubleArrayElement(object, index, value, SMI_PARAMETERS);
|
||||
}
|
||||
|
||||
void StoreFixedDoubleArrayHole(TNode<FixedDoubleArray> array, Node* index,
|
||||
ParameterMode mode = INTPTR_PARAMETERS);
|
||||
void StoreFixedDoubleArrayHoleSmi(TNode<FixedDoubleArray> array,
|
||||
TNode<Smi> index) {
|
||||
StoreFixedDoubleArrayHole(array, index, SMI_PARAMETERS);
|
||||
}
|
||||
|
||||
Node* StoreFeedbackVectorSlot(
|
||||
Node* object, Node* index, Node* value,
|
||||
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
|
||||
@ -1445,6 +1458,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
|
||||
Node* ArraySpeciesCreate(TNode<Context> context, TNode<Object> originalArray,
|
||||
TNode<Number> len);
|
||||
Node* InternalArrayCreate(TNode<Context> context, TNode<Number> len);
|
||||
|
||||
void FillFixedArrayWithValue(ElementsKind kind, Node* array, Node* from_index,
|
||||
Node* to_index,
|
||||
@ -1520,6 +1534,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
return UncheckedCast<FixedDoubleArray>(base);
|
||||
}
|
||||
|
||||
TNode<Int32T> ConvertElementsKindToInt(TNode<Int32T> elements_kind) {
|
||||
return UncheckedCast<Int32T>(elements_kind);
|
||||
}
|
||||
|
||||
enum class ExtractFixedArrayFlag {
|
||||
kFixedArrays = 1,
|
||||
kFixedDoubleArrays = 2,
|
||||
@ -1560,12 +1578,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
ExtractFixedArrayFlag::kAllFixedArrays,
|
||||
ParameterMode parameter_mode = INTPTR_PARAMETERS);
|
||||
|
||||
TNode<FixedArrayBase> ExtractFixedArray(
|
||||
TNode<FixedArrayBase> source, TNode<Smi> first, TNode<Smi> count,
|
||||
TNode<Smi> capacity,
|
||||
ExtractFixedArrayFlags extract_flags =
|
||||
ExtractFixedArrayFlag::kAllFixedArrays) {
|
||||
return ExtractFixedArray(source, first, count, capacity, extract_flags,
|
||||
TNode<FixedArrayBase> ExtractFixedArray(TNode<FixedArrayBase> source,
|
||||
TNode<Smi> first, TNode<Smi> count,
|
||||
TNode<Smi> capacity) {
|
||||
return ExtractFixedArray(source, first, count, capacity,
|
||||
ExtractFixedArrayFlag::kAllFixedArrays,
|
||||
SMI_PARAMETERS);
|
||||
}
|
||||
|
||||
@ -2561,6 +2578,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
ElementsKind to_kind, bool is_jsarray,
|
||||
Label* bailout);
|
||||
|
||||
void TransitionElementsKind(TNode<JSReceiver> object, TNode<Map> map,
|
||||
ElementsKind from_kind, ElementsKind to_kind,
|
||||
Label* bailout) {
|
||||
TransitionElementsKind(object, map, from_kind, to_kind, true, bailout);
|
||||
}
|
||||
|
||||
void TrapAllocationMemento(Node* object, Label* memento_found);
|
||||
|
||||
TNode<IntPtrT> PageFromAddress(TNode<IntPtrT> address);
|
||||
@ -2652,26 +2675,32 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
void BranchIfNumberRelationalComparison(Operation op, Node* left, Node* right,
|
||||
Label* if_true, Label* if_false);
|
||||
|
||||
void BranchIfNumberLessThan(Node* left, Node* right, Label* if_true,
|
||||
Label* if_false) {
|
||||
void BranchIfNumberEqual(TNode<Number> left, TNode<Number> right,
|
||||
Label* if_true, Label* if_false) {
|
||||
BranchIfNumberRelationalComparison(Operation::kEqual, left, right, if_true,
|
||||
if_false);
|
||||
}
|
||||
|
||||
void BranchIfNumberLessThan(TNode<Number> left, TNode<Number> right,
|
||||
Label* if_true, Label* if_false) {
|
||||
BranchIfNumberRelationalComparison(Operation::kLessThan, left, right,
|
||||
if_true, if_false);
|
||||
}
|
||||
|
||||
void BranchIfNumberLessThanOrEqual(Node* left, Node* right, Label* if_true,
|
||||
Label* if_false) {
|
||||
void BranchIfNumberLessThanOrEqual(TNode<Number> left, TNode<Number> right,
|
||||
Label* if_true, Label* if_false) {
|
||||
BranchIfNumberRelationalComparison(Operation::kLessThanOrEqual, left, right,
|
||||
if_true, if_false);
|
||||
}
|
||||
|
||||
void BranchIfNumberGreaterThan(Node* left, Node* right, Label* if_true,
|
||||
Label* if_false) {
|
||||
void BranchIfNumberGreaterThan(TNode<Number> left, TNode<Number> right,
|
||||
Label* if_true, Label* if_false) {
|
||||
BranchIfNumberRelationalComparison(Operation::kGreaterThan, left, right,
|
||||
if_true, if_false);
|
||||
}
|
||||
|
||||
void BranchIfNumberGreaterThanOrEqual(Node* left, Node* right, Label* if_true,
|
||||
Label* if_false) {
|
||||
void BranchIfNumberGreaterThanOrEqual(TNode<Number> left, TNode<Number> right,
|
||||
Label* if_true, Label* if_false) {
|
||||
BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, left,
|
||||
right, if_true, if_false);
|
||||
}
|
||||
|
@ -71,46 +71,46 @@ enum ContextLookupFlags {
|
||||
V(ASYNC_GENERATOR_AWAIT_CAUGHT, JSFunction, async_generator_await_caught) \
|
||||
V(ASYNC_GENERATOR_AWAIT_UNCAUGHT, JSFunction, async_generator_await_uncaught)
|
||||
|
||||
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
||||
V(ARRAY_SPLICE_INDEX, JSFunction, array_splice) \
|
||||
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
|
||||
V(ARRAY_ENTRIES_ITERATOR_INDEX, JSFunction, array_entries_iterator) \
|
||||
V(ARRAY_FOR_EACH_ITERATOR_INDEX, JSFunction, array_for_each_iterator) \
|
||||
V(ARRAY_KEYS_ITERATOR_INDEX, JSFunction, array_keys_iterator) \
|
||||
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
|
||||
V(CANONICALIZE_LOCALE_LIST_FUNCTION_INDEX, JSFunction, \
|
||||
canonicalize_locale_list) \
|
||||
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
|
||||
V(ERROR_TO_STRING, JSFunction, error_to_string) \
|
||||
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
|
||||
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
|
||||
V(GLOBAL_PROXY_FUNCTION_INDEX, JSFunction, global_proxy_function) \
|
||||
V(MAP_DELETE_INDEX, JSFunction, map_delete) \
|
||||
V(MAP_GET_INDEX, JSFunction, map_get) \
|
||||
V(MAP_HAS_INDEX, JSFunction, map_has) \
|
||||
V(MAP_SET_INDEX, JSFunction, map_set) \
|
||||
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
|
||||
V(OBJECT_VALUE_OF, JSFunction, object_value_of) \
|
||||
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
|
||||
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
|
||||
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
|
||||
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
|
||||
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
|
||||
V(CACHED_OR_NEW_SERVICE_LOCALE_FUNCTION_INDEX, JSFunction, \
|
||||
cached_or_new_service) \
|
||||
V(RESOLVE_LOCALE_FUNCTION_INDEX, JSFunction, resolve_locale) \
|
||||
V(SET_ADD_INDEX, JSFunction, set_add) \
|
||||
V(SET_DELETE_INDEX, JSFunction, set_delete) \
|
||||
V(SET_HAS_INDEX, JSFunction, set_has) \
|
||||
V(SYNTAX_ERROR_FUNCTION_INDEX, JSFunction, syntax_error_function) \
|
||||
V(TYPE_ERROR_FUNCTION_INDEX, JSFunction, type_error_function) \
|
||||
V(URI_ERROR_FUNCTION_INDEX, JSFunction, uri_error_function) \
|
||||
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
|
||||
wasm_compile_error_function) \
|
||||
V(WASM_LINK_ERROR_FUNCTION_INDEX, JSFunction, wasm_link_error_function) \
|
||||
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, \
|
||||
wasm_runtime_error_function) \
|
||||
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
|
||||
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
||||
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
|
||||
V(ARRAY_UNSHIFT_INDEX, JSFunction, array_unshift) \
|
||||
V(ARRAY_ENTRIES_ITERATOR_INDEX, JSFunction, array_entries_iterator) \
|
||||
V(ARRAY_FOR_EACH_ITERATOR_INDEX, JSFunction, array_for_each_iterator) \
|
||||
V(ARRAY_KEYS_ITERATOR_INDEX, JSFunction, array_keys_iterator) \
|
||||
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
|
||||
V(CANONICALIZE_LOCALE_LIST_FUNCTION_INDEX, JSFunction, \
|
||||
canonicalize_locale_list) \
|
||||
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
|
||||
V(ERROR_TO_STRING, JSFunction, error_to_string) \
|
||||
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
|
||||
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
|
||||
V(GLOBAL_PROXY_FUNCTION_INDEX, JSFunction, global_proxy_function) \
|
||||
V(MAP_DELETE_INDEX, JSFunction, map_delete) \
|
||||
V(MAP_GET_INDEX, JSFunction, map_get) \
|
||||
V(MAP_HAS_INDEX, JSFunction, map_has) \
|
||||
V(MAP_SET_INDEX, JSFunction, map_set) \
|
||||
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
|
||||
V(OBJECT_VALUE_OF, JSFunction, object_value_of) \
|
||||
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
|
||||
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
|
||||
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
|
||||
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
|
||||
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
|
||||
V(CACHED_OR_NEW_SERVICE_LOCALE_FUNCTION_INDEX, JSFunction, \
|
||||
cached_or_new_service) \
|
||||
V(RESOLVE_LOCALE_FUNCTION_INDEX, JSFunction, resolve_locale) \
|
||||
V(SET_ADD_INDEX, JSFunction, set_add) \
|
||||
V(SET_DELETE_INDEX, JSFunction, set_delete) \
|
||||
V(SET_HAS_INDEX, JSFunction, set_has) \
|
||||
V(SYNTAX_ERROR_FUNCTION_INDEX, JSFunction, syntax_error_function) \
|
||||
V(TYPE_ERROR_FUNCTION_INDEX, JSFunction, type_error_function) \
|
||||
V(URI_ERROR_FUNCTION_INDEX, JSFunction, uri_error_function) \
|
||||
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
|
||||
wasm_compile_error_function) \
|
||||
V(WASM_LINK_ERROR_FUNCTION_INDEX, JSFunction, wasm_link_error_function) \
|
||||
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, \
|
||||
wasm_runtime_error_function) \
|
||||
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
|
||||
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add)
|
||||
|
||||
#define NATIVE_CONTEXT_FIELDS(V) \
|
||||
|
108
src/elements.cc
108
src/elements.cc
@ -724,18 +724,6 @@ class ElementsAccessorBase : public InternalElementsAccessor {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Handle<JSArray> Splice(Handle<JSArray> receiver, uint32_t start,
|
||||
uint32_t delete_count, Arguments* args,
|
||||
uint32_t add_count) final {
|
||||
return Subclass::SpliceImpl(receiver, start, delete_count, args, add_count);
|
||||
}
|
||||
|
||||
static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver,
|
||||
uint32_t start, uint32_t delete_count,
|
||||
Arguments* args, uint32_t add_count) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Handle<Object> Pop(Handle<JSArray> receiver) final {
|
||||
return Subclass::PopImpl(receiver);
|
||||
}
|
||||
@ -2227,58 +2215,6 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
|
||||
return result_array;
|
||||
}
|
||||
|
||||
static Handle<JSArray> SpliceImpl(Handle<JSArray> receiver,
|
||||
uint32_t start, uint32_t delete_count,
|
||||
Arguments* args, uint32_t add_count) {
|
||||
Isolate* isolate = receiver->GetIsolate();
|
||||
Heap* heap = isolate->heap();
|
||||
uint32_t length = Smi::ToInt(receiver->length());
|
||||
uint32_t new_length = length - delete_count + add_count;
|
||||
|
||||
ElementsKind kind = KindTraits::Kind;
|
||||
if (new_length <= static_cast<uint32_t>(receiver->elements()->length()) &&
|
||||
IsSmiOrObjectElementsKind(kind)) {
|
||||
HandleScope scope(isolate);
|
||||
JSObject::EnsureWritableFastElements(receiver);
|
||||
}
|
||||
|
||||
Handle<FixedArrayBase> backing_store(receiver->elements(), isolate);
|
||||
|
||||
if (new_length == 0) {
|
||||
receiver->set_elements(ReadOnlyRoots(heap).empty_fixed_array());
|
||||
receiver->set_length(Smi::kZero);
|
||||
return isolate->factory()->NewJSArrayWithElements(
|
||||
backing_store, KindTraits::Kind, delete_count);
|
||||
}
|
||||
|
||||
// Construct the result array which holds the deleted elements.
|
||||
Handle<JSArray> deleted_elements = isolate->factory()->NewJSArray(
|
||||
KindTraits::Kind, delete_count, delete_count);
|
||||
if (delete_count > 0) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Subclass::CopyElementsImpl(isolate, *backing_store, start,
|
||||
deleted_elements->elements(), KindTraits::Kind,
|
||||
0, kPackedSizeNotKnown, delete_count);
|
||||
}
|
||||
|
||||
// Delete and move elements to make space for add_count new elements.
|
||||
if (add_count < delete_count) {
|
||||
Subclass::SpliceShrinkStep(isolate, receiver, backing_store, start,
|
||||
delete_count, add_count, length, new_length);
|
||||
} else if (add_count > delete_count) {
|
||||
backing_store =
|
||||
Subclass::SpliceGrowStep(isolate, receiver, backing_store, start,
|
||||
delete_count, add_count, length, new_length);
|
||||
}
|
||||
|
||||
// Copy over the arguments.
|
||||
Subclass::CopyArguments(args, backing_store, add_count, 3, start);
|
||||
|
||||
receiver->set_length(Smi::FromInt(new_length));
|
||||
Subclass::TryTransitionResultArrayToPacked(deleted_elements);
|
||||
return deleted_elements;
|
||||
}
|
||||
|
||||
static void MoveElements(Isolate* isolate, Handle<JSArray> receiver,
|
||||
Handle<FixedArrayBase> backing_store, int dst_index,
|
||||
int src_index, int len, int hole_start,
|
||||
@ -2503,50 +2439,6 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
// SpliceShrinkStep might modify the backing_store.
|
||||
static void SpliceShrinkStep(Isolate* isolate, Handle<JSArray> receiver,
|
||||
Handle<FixedArrayBase> backing_store,
|
||||
uint32_t start, uint32_t delete_count,
|
||||
uint32_t add_count, uint32_t len,
|
||||
uint32_t new_length) {
|
||||
const int move_left_count = len - delete_count - start;
|
||||
const int move_left_dst_index = start + add_count;
|
||||
Subclass::MoveElements(isolate, receiver, backing_store,
|
||||
move_left_dst_index, start + delete_count,
|
||||
move_left_count, new_length, len);
|
||||
}
|
||||
|
||||
// SpliceGrowStep might modify the backing_store.
|
||||
static Handle<FixedArrayBase> SpliceGrowStep(
|
||||
Isolate* isolate, Handle<JSArray> receiver,
|
||||
Handle<FixedArrayBase> backing_store, uint32_t start,
|
||||
uint32_t delete_count, uint32_t add_count, uint32_t length,
|
||||
uint32_t new_length) {
|
||||
// Check we do not overflow the new_length.
|
||||
DCHECK((add_count - delete_count) <= (Smi::kMaxValue - length));
|
||||
// Check if backing_store is big enough.
|
||||
if (new_length <= static_cast<uint32_t>(backing_store->length())) {
|
||||
Subclass::MoveElements(isolate, receiver, backing_store,
|
||||
start + add_count, start + delete_count,
|
||||
(length - delete_count - start), 0, 0);
|
||||
// MoveElements updates the backing_store in-place.
|
||||
return backing_store;
|
||||
}
|
||||
// New backing storage is needed.
|
||||
int capacity = JSObject::NewElementsCapacity(new_length);
|
||||
// Partially copy all elements up to start.
|
||||
Handle<FixedArrayBase> new_elms = Subclass::ConvertElementsWithCapacity(
|
||||
receiver, backing_store, KindTraits::Kind, capacity, start);
|
||||
// Copy the trailing elements after start + delete_count
|
||||
Subclass::CopyElementsImpl(isolate, *backing_store, start + delete_count,
|
||||
*new_elms, KindTraits::Kind, start + add_count,
|
||||
kPackedSizeNotKnown,
|
||||
ElementsAccessor::kCopyToEndAndInitializeToHole);
|
||||
receiver->set_elements(*new_elms);
|
||||
return new_elms;
|
||||
}
|
||||
|
||||
static Handle<Object> RemoveElement(Handle<JSArray> receiver,
|
||||
Where remove_position) {
|
||||
Isolate* isolate = receiver->GetIsolate();
|
||||
|
@ -135,10 +135,6 @@ class ElementsAccessor {
|
||||
virtual Handle<JSObject> Slice(Handle<JSObject> receiver, uint32_t start,
|
||||
uint32_t end) = 0;
|
||||
|
||||
virtual Handle<JSArray> Splice(Handle<JSArray> receiver,
|
||||
uint32_t start, uint32_t delete_count,
|
||||
Arguments* args, uint32_t add_count) = 0;
|
||||
|
||||
virtual Handle<Object> Pop(Handle<JSArray> receiver) = 0;
|
||||
|
||||
virtual Handle<Object> Shift(Handle<JSArray> receiver) = 0;
|
||||
|
@ -874,8 +874,6 @@ DEFINE_BOOL(expose_trigger_failure, false, "expose trigger-failure extension")
|
||||
DEFINE_INT(stack_trace_limit, 10, "number of stack frames to capture")
|
||||
DEFINE_BOOL(builtins_in_stack_traces, false,
|
||||
"show built-in functions in stack traces")
|
||||
DEFINE_BOOL(enable_experimental_builtins, false,
|
||||
"enable new csa-based experimental builtins")
|
||||
DEFINE_BOOL(disallow_code_generation_from_strings, false,
|
||||
"disallow eval and friends")
|
||||
DEFINE_BOOL(expose_async_hooks, false, "expose async_hooks object")
|
||||
|
107
src/js/array.js
107
src/js/array.js
@ -200,35 +200,6 @@ function ConvertToString(use_locale, x, locales, options) {
|
||||
return TO_STRING(x);
|
||||
}
|
||||
|
||||
|
||||
// This function implements the optimized splice implementation that can use
|
||||
// special array operations to handle sparse arrays in a sensible fashion.
|
||||
function SparseSlice(array, start_i, del_count, len, deleted_elements) {
|
||||
// Move deleted elements to a new array (the return value from splice).
|
||||
var indices = %GetArrayKeys(array, start_i + del_count);
|
||||
if (IS_NUMBER(indices)) {
|
||||
var limit = indices;
|
||||
for (var i = start_i; i < limit; ++i) {
|
||||
var current = array[i];
|
||||
if (!IS_UNDEFINED(current) || i in array) {
|
||||
%CreateDataProperty(deleted_elements, i - start_i, current);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var length = indices.length;
|
||||
for (var k = 0; k < length; ++k) {
|
||||
var key = indices[k];
|
||||
if (key >= start_i) {
|
||||
var current = array[key];
|
||||
if (!IS_UNDEFINED(current) || key in array) {
|
||||
%CreateDataProperty(deleted_elements, key - start_i, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This function implements the optimized splice implementation that can use
|
||||
// special array operations to handle sparse arrays in a sensible fashion.
|
||||
function SparseMove(array, start_i, del_count, len, num_additional_args) {
|
||||
@ -442,83 +413,6 @@ function ArraySliceFallback(start, end) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function ComputeSpliceStartIndex(start_i, len) {
|
||||
if (start_i < 0) {
|
||||
start_i += len;
|
||||
return start_i < 0 ? 0 : start_i;
|
||||
}
|
||||
|
||||
return start_i > len ? len : start_i;
|
||||
}
|
||||
|
||||
|
||||
function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
|
||||
// SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
|
||||
// given as a request to delete all the elements from the start.
|
||||
// And it differs from the case of undefined delete count.
|
||||
// This does not follow ECMA-262, but we do the same for
|
||||
// compatibility.
|
||||
var del_count = 0;
|
||||
if (num_arguments == 1)
|
||||
return len - start_i;
|
||||
|
||||
del_count = TO_INTEGER(delete_count);
|
||||
if (del_count < 0)
|
||||
return 0;
|
||||
|
||||
if (del_count > len - start_i)
|
||||
return len - start_i;
|
||||
|
||||
return del_count;
|
||||
}
|
||||
|
||||
|
||||
function ArraySpliceFallback(start, delete_count) {
|
||||
var num_arguments = arguments.length;
|
||||
var array = TO_OBJECT(this);
|
||||
var len = TO_LENGTH(array.length);
|
||||
var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
|
||||
var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
|
||||
start_i);
|
||||
var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
|
||||
|
||||
const new_len = len - del_count + num_elements_to_add;
|
||||
if (new_len >= 2**53) throw %make_type_error(kInvalidArrayLength);
|
||||
|
||||
var deleted_elements = ArraySpeciesCreate(array, del_count);
|
||||
deleted_elements.length = del_count;
|
||||
|
||||
var changed_elements = del_count;
|
||||
if (num_elements_to_add != del_count) {
|
||||
// If the slice needs to do a actually move elements after the insertion
|
||||
// point, then include those in the estimate of changed elements.
|
||||
changed_elements += len - start_i - del_count;
|
||||
}
|
||||
if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
|
||||
%NormalizeElements(array);
|
||||
if (IS_ARRAY(deleted_elements)) %NormalizeElements(deleted_elements);
|
||||
SparseSlice(array, start_i, del_count, len, deleted_elements);
|
||||
SparseMove(array, start_i, del_count, len, num_elements_to_add);
|
||||
} else {
|
||||
SimpleSlice(array, start_i, del_count, len, deleted_elements);
|
||||
SimpleMove(array, start_i, del_count, len, num_elements_to_add);
|
||||
}
|
||||
|
||||
// Insert the arguments into the resulting array in
|
||||
// place of the deleted elements.
|
||||
var i = start_i;
|
||||
var arguments_index = 2;
|
||||
var arguments_length = arguments.length;
|
||||
while (arguments_index < arguments_length) {
|
||||
array[i++] = arguments[arguments_index++];
|
||||
}
|
||||
array.length = new_len;
|
||||
|
||||
// Return the deleted elements.
|
||||
return deleted_elements;
|
||||
}
|
||||
|
||||
|
||||
function InnerArraySort(array, length, comparefn) {
|
||||
// In-place QuickSort algorithm.
|
||||
// For short (length <= 10) arrays, insertion sort is used for efficiency.
|
||||
@ -755,7 +649,6 @@ utils.Export(function(to) {
|
||||
"array_keys_iterator", ArrayKeys,
|
||||
"array_values_iterator", ArrayValues,
|
||||
// Fallback implementations of Array builtins.
|
||||
"array_splice", ArraySpliceFallback,
|
||||
"array_unshift", ArrayUnshiftFallback,
|
||||
]);
|
||||
|
||||
|
@ -30,6 +30,16 @@ RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
|
||||
return *object;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_TransitionElementsKindWithKind) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Smi, elements_kind_smi, 1);
|
||||
ElementsKind to_kind = static_cast<ElementsKind>(elements_kind_smi->value());
|
||||
JSObject::TransitionElementsKind(object, to_kind);
|
||||
return *object;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Find the next free position. undefined and holes are both considered
|
||||
// free spots. Returns "Nothing" if an exception occurred.
|
||||
|
@ -36,21 +36,22 @@ namespace internal {
|
||||
// A variable number of arguments is specified by a -1, additional restrictions
|
||||
// are specified by inline comments
|
||||
|
||||
#define FOR_EACH_INTRINSIC_ARRAY(F) \
|
||||
F(ArrayIncludes_Slow, 3, 1) \
|
||||
F(ArrayIndexOf, 3, 1) \
|
||||
F(ArrayIsArray, 1, 1) \
|
||||
F(ArraySpeciesConstructor, 1, 1) \
|
||||
F(EstimateNumberOfElements, 1, 1) \
|
||||
F(GetArrayKeys, 2, 1) \
|
||||
F(GrowArrayElements, 2, 1) \
|
||||
F(HasComplexElements, 1, 1) \
|
||||
F(IsArray, 1, 1) \
|
||||
F(MoveArrayContents, 2, 1) \
|
||||
F(NewArray, -1 /* >= 3 */, 1) \
|
||||
F(NormalizeElements, 1, 1) \
|
||||
F(PrepareElementsForSort, 2, 1) \
|
||||
F(TransitionElementsKind, 2, 1) \
|
||||
#define FOR_EACH_INTRINSIC_ARRAY(F) \
|
||||
F(ArrayIncludes_Slow, 3, 1) \
|
||||
F(ArrayIndexOf, 3, 1) \
|
||||
F(ArrayIsArray, 1, 1) \
|
||||
F(ArraySpeciesConstructor, 1, 1) \
|
||||
F(EstimateNumberOfElements, 1, 1) \
|
||||
F(GetArrayKeys, 2, 1) \
|
||||
F(GrowArrayElements, 2, 1) \
|
||||
F(HasComplexElements, 1, 1) \
|
||||
F(IsArray, 1, 1) \
|
||||
F(MoveArrayContents, 2, 1) \
|
||||
F(NewArray, -1 /* >= 3 */, 1) \
|
||||
F(NormalizeElements, 1, 1) \
|
||||
F(PrepareElementsForSort, 2, 1) \
|
||||
F(TransitionElementsKind, 2, 1) \
|
||||
F(TransitionElementsKindWithKind, 2, 1) \
|
||||
F(TrySliceSimpleNonFastElements, 3, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_ATOMICS(F) \
|
||||
|
@ -31,13 +31,8 @@
|
||||
* should work on other objects too, so we test that too.
|
||||
*/
|
||||
|
||||
var LARGE = 400000;
|
||||
var VERYLARGE = 4000000000;
|
||||
|
||||
// Nicer for firefox 1.5. Unless you uncomment the following two lines,
|
||||
// smjs will appear to hang on this file.
|
||||
//var LARGE = 40000;
|
||||
//var VERYLARGE = 40000;
|
||||
var LARGE = 40000;
|
||||
var VERYLARGE = 40000;
|
||||
|
||||
var fourhundredth = LARGE/400;
|
||||
|
||||
|
@ -445,3 +445,21 @@
|
||||
"array.hasOwnProperty(Math.pow(2, 32) - 2)");
|
||||
}
|
||||
})();
|
||||
|
||||
// Verify that fast implementations aren't confused by empty DOUBLE element arrays
|
||||
(function() {
|
||||
|
||||
function foo(dontAddAnything) {
|
||||
let a = [];
|
||||
if (dontAddAnything === undefined) {
|
||||
a[1] = 0.5;
|
||||
}
|
||||
return a.splice(0, 0, 3.5);
|
||||
}
|
||||
|
||||
// Learn via allocation site tracking to create double arrays in foo().
|
||||
foo();
|
||||
foo();
|
||||
// force splice to copy the input array.
|
||||
foo(true);
|
||||
})();
|
||||
|
9
test/mjsunit/regress/regress-crbug-876443.js
Normal file
9
test/mjsunit/regress/regress-crbug-876443.js
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2018 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:
|
||||
|
||||
var a = [5.65];
|
||||
a.splice(0);
|
||||
var b = a.splice(-4, 9, 10);
|
@ -32,10 +32,3 @@ assertEquals(0xffffffff, a.length);
|
||||
assertEquals(10, a[0xffffffff]);
|
||||
assertEquals(0xffffffff, a.length);
|
||||
assertEquals(undefined, a[0xfffffffe]);
|
||||
|
||||
a = [1,2,3];
|
||||
a[0xfffffffe] = 10;
|
||||
assertThrows("a.splice(1,1,7,7,7,7,7);", RangeError);
|
||||
assertEquals([1,7,7,7,7,7,3], a.slice(0, 7));
|
||||
assertEquals(0xffffffff, a.length);
|
||||
assertEquals(10, a[0xfffffffe + 5 - 1]);
|
||||
|
@ -184,6 +184,11 @@
|
||||
# characters. This takes a long time to run (~32 seconds).
|
||||
'js1_5/GC/regress-348532': [SKIP],
|
||||
|
||||
# Takes a really long time to run, creating an Array of length
|
||||
# 2^32 - 1. Related to removal of "sparse" array support for
|
||||
# splice and friends:
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=8131.
|
||||
'ecma_3/Array/regress-322135-03': [SKIP],
|
||||
|
||||
# Runs for too long: huge array with getters and setters. As it says
|
||||
# in the test: "This test will probably run out of memory".
|
||||
|
@ -598,9 +598,6 @@
|
||||
'intl402/Locale/prototype/toStringTag/toStringTag': [FAIL],
|
||||
'intl402/Locale/prototype/toStringTag/toString': [FAIL],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=7814
|
||||
'built-ins/Array/prototype/splice/property-traps-order-with-species': [FAIL],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=6705
|
||||
'built-ins/Object/assign/strings-and-symbol-order': [FAIL],
|
||||
|
||||
@ -683,8 +680,6 @@
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=6538
|
||||
'built-ins/Array/prototype/unshift/throws-if-integer-limit-exceeded': [SKIP],
|
||||
'built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit': [FAIL],
|
||||
'built-ins/Array/prototype/splice/throws-if-integer-limit-exceeded': [SKIP],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=6541
|
||||
'language/export/escaped-as-export-specifier': [FAIL],
|
||||
|
@ -54,8 +54,3 @@ shouldBe("arr.splice(2, -1)", "[]")
|
||||
shouldBe("arr", "['a','b','c']");
|
||||
shouldBe("arr.splice(2, 100)", "['c']")
|
||||
shouldBe("arr", "['a','b']");
|
||||
|
||||
// Check this doesn't crash.
|
||||
try {
|
||||
String(Array(0xFFFFFFFD).splice(0));
|
||||
} catch (e) { }
|
||||
|
Loading…
Reference in New Issue
Block a user