[builtins] Reland Array.prototype.splice() Torque implementation.
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. Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng Change-Id: Ia7334a30b401988309e9909cfa0069da0bb6fb9f Reviewed-on: https://chromium-review.googlesource.com/1169466 Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#55263}
This commit is contained in:
parent
41819b39c4
commit
cdaaa31151
1
BUILD.gn
1
BUILD.gn
@ -888,6 +888,7 @@ torque_files = [
|
||||
"src/builtins/array-copywithin.tq",
|
||||
"src/builtins/array-foreach.tq",
|
||||
"src/builtins/array-shift.tq",
|
||||
"src/builtins/array-splice.tq",
|
||||
"src/builtins/typed-array.tq",
|
||||
"src/builtins/data-view.tq",
|
||||
"test/torque/test-torque.tq",
|
||||
|
@ -1741,13 +1741,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
1, false);
|
||||
SimpleInstallFunction(isolate_, proto, "slice",
|
||||
Builtins::kArrayPrototypeSlice, 2, 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,
|
||||
|
@ -62,6 +62,10 @@ module array {
|
||||
cast<Callable>(callback) otherwise Unexpected;
|
||||
const k: Smi = cast<Smi>(initialK) otherwise Unexpected;
|
||||
const number_length: Number = cast<Number>(length) otherwise Unexpected;
|
||||
// The unsafe cast is safe because all continuation points in forEach are
|
||||
// after the ToObject(O) call that ensures we are dealing with a
|
||||
// JSReceiver.
|
||||
let jsreceiver: JSReceiver = unsafe_cast<JSReceiver>(object);
|
||||
|
||||
return ArrayForEachTorqueContinuation(
|
||||
context, receiver, number_length, callbackfn, thisArg, k);
|
||||
|
416
src/builtins/array-splice.tq
Normal file
416
src/builtins/array-splice.tq
Normal file
@ -0,0 +1,416 @@
|
||||
// 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;
|
||||
|
||||
if (UseSparseVariant(context, o, len, affected)) {
|
||||
// UseSparseVariant returns true only if {o} is a JSArray.
|
||||
assert(IsJSArray(o));
|
||||
let array: JSArray = unsafe_cast<JSArray>(o);
|
||||
NormalizeElements(context, array);
|
||||
|
||||
try {
|
||||
let a_array: JSArray = cast<JSArray>(a) otherwise NotArray;
|
||||
NormalizeElements(context, a_array);
|
||||
}
|
||||
label NotArray {}
|
||||
|
||||
SparseSlice(context, array, actualStart, actualDeleteCount, len, a);
|
||||
let additionalArgumentCount: Smi = 0;
|
||||
if (arguments.length > 2) {
|
||||
additionalArgumentCount = convert<Smi>(arguments.length - 2);
|
||||
}
|
||||
SparseMove(
|
||||
context, array, actualStart, actualDeleteCount, len,
|
||||
additionalArgumentCount);
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
}
|
@ -13,293 +13,197 @@ 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;
|
||||
}
|
||||
label NotArray {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
macro StoreArrayHole(elements: FixedDoubleArray, k: Smi): void {
|
||||
StoreFixedDoubleArrayHoleSmi(elements, k);
|
||||
}
|
||||
|
||||
// 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: FixedArray, k: Smi): void {
|
||||
elements[k] = Hole;
|
||||
}
|
||||
|
||||
// 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.
|
||||
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 FoundHole {
|
||||
StoreArrayHole(newElements, to);
|
||||
}
|
||||
}
|
||||
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);
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Sparse array support
|
||||
// This is a port of the sparse array support in src/js/array.js.
|
||||
//
|
||||
extern runtime HasComplexElements(Context, JSArray): Boolean;
|
||||
extern runtime EstimateNumberOfElements(Context, JSArray): Number;
|
||||
extern runtime GetArrayKeys(Context, Object, Object): JSArray|Number;
|
||||
extern runtime MoveArrayContents(Context, JSArray, JSArray): void;
|
||||
|
||||
// 10. Let k be 0.
|
||||
let k: Number = 0;
|
||||
macro UseSparseVariant(
|
||||
context: Context, o: JSReceiver, length: Number, touched: Number): bool {
|
||||
try {
|
||||
if (length < 1000) {
|
||||
return false;
|
||||
}
|
||||
let array: JSArray = cast<JSArray>(o) otherwise ReturnFalse;
|
||||
if (HasComplexElements(context, array) == True) {
|
||||
return false;
|
||||
}
|
||||
let smi_length: Smi = cast<Smi>(length) otherwise ReturnTrue;
|
||||
let elements_threshold: Smi = smi_length >>> 2; // = length / 4.
|
||||
let estimated_elements_number: Number =
|
||||
EstimateNumberOfElements(context, array);
|
||||
let estimated_elements: Smi =
|
||||
cast<Smi>(estimated_elements_number) otherwise ReturnFalse;
|
||||
if (estimated_elements < elements_threshold) {
|
||||
if (touched > (estimated_elements >>> 2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
label ReturnFalse {
|
||||
return false;
|
||||
}
|
||||
label ReturnTrue {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Repeat, while k < actualDeleteCount
|
||||
while (k < actualDeleteCount) {
|
||||
// a. Let from be ! ToString(actualStart + k).
|
||||
let from: String = ToString_Inline(context, actualStart + k);
|
||||
macro SparseSlice(
|
||||
context: Context, o: JSReceiver, actualStart: Number,
|
||||
actualDeleteCount: Number, len: Number, a: JSReceiver): void {
|
||||
let indices: JSArray|Number =
|
||||
GetArrayKeys(context, o, actualStart + actualDeleteCount);
|
||||
if (IsNumber(indices)) {
|
||||
let limit: Number = unsafe_cast<Number>(indices);
|
||||
for (let i: Number = actualStart; i < limit; i++) {
|
||||
let fromPresent: Boolean = HasProperty(context, o, i);
|
||||
if (fromPresent == True) {
|
||||
let fromValue: Object = GetProperty(context, o, i);
|
||||
CreateDataProperty(context, a, i - actualStart, fromValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let indices_array: JSArray = unsafe_cast<JSArray>(indices);
|
||||
assert(IsFixedArray(indices_array.elements));
|
||||
let indices_elements: FixedArray =
|
||||
unsafe_cast<FixedArray>(indices_array.elements);
|
||||
let length: Smi = unsafe_cast<Smi>(indices_array.length);
|
||||
for (let k: Smi = 0; k < length; k++) {
|
||||
let key: Number = unsafe_cast<Number>(indices_elements[k]);
|
||||
if (key >= actualStart) {
|
||||
let fromPresent: Boolean = HasProperty(context, o, key);
|
||||
if (fromPresent == True) {
|
||||
let fromValue: Object = GetProperty(context, o, key);
|
||||
CreateDataProperty(context, a, key - actualStart, fromValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// b. Let fromPresent be ? HasProperty(O, from).
|
||||
let fromPresent: Oddball = HasProperty(context, o, from);
|
||||
macro SparseMove(
|
||||
context: Context, o: JSArray, actualStart: Number,
|
||||
actualDeleteCount: Number, len: Number,
|
||||
additionalArgumentCount: Number): void {
|
||||
// Bail out if no moving is necessary.
|
||||
if (additionalArgumentCount == actualDeleteCount) {
|
||||
return;
|
||||
}
|
||||
// Move data to new array.
|
||||
let new_array: JSArray = InternalArrayCreate(
|
||||
context,
|
||||
// Clamp array length to 2^32-1 to avoid early RangeError.
|
||||
NumberMin(
|
||||
len - actualDeleteCount + additionalArgumentCount, 0xffffffff));
|
||||
|
||||
// c. If fromPresent is true, then
|
||||
let big_indices: Object = Undefined;
|
||||
let indices: JSArray|Number = GetArrayKeys(context, o, len);
|
||||
if (IsNumber(indices)) {
|
||||
let limit: Number = unsafe_cast<Number>(indices);
|
||||
for (let i: Number = 0; i < actualStart && i < limit; i++) {
|
||||
let fromPresent: Boolean = HasProperty(context, o, i);
|
||||
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);
|
||||
let fromValue: Object = GetProperty(context, o, i);
|
||||
CreateDataProperty(context, new_array, i, fromValue);
|
||||
}
|
||||
|
||||
// d. Increment k by 1.
|
||||
k = k + 1;
|
||||
}
|
||||
|
||||
// 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
|
||||
for (let i: Number = actualStart + actualDeleteCount; i < limit; i++) {
|
||||
let fromPresent: Boolean = HasProperty(context, o, i);
|
||||
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);
|
||||
let fromValue: Object = GetProperty(context, o, i);
|
||||
let toIndex: Number = i - actualDeleteCount + additionalArgumentCount;
|
||||
CreateDataProperty(context, new_array, toIndex, fromValue);
|
||||
}
|
||||
// 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;
|
||||
} else {
|
||||
assert(IsJSArray(unsafe_cast<HeapObject>(indices)));
|
||||
let indices_array: JSArray = unsafe_cast<JSArray>(indices);
|
||||
let indices_elements: FixedArray =
|
||||
unsafe_cast<FixedArray>(indices_array.elements);
|
||||
let length: Smi = unsafe_cast<Smi>(indices_array.length);
|
||||
for (let k: Smi = 0; k < length; ++k) {
|
||||
let key: Number = unsafe_cast<Number>(indices_elements[k]);
|
||||
if (key < actualStart) {
|
||||
let fromPresent: Boolean = HasProperty(context, o, key);
|
||||
if (fromPresent == True) {
|
||||
let fromValue: Object = GetProperty(context, o, key);
|
||||
CreateDataProperty(context, new_array, key, fromValue);
|
||||
}
|
||||
// 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
|
||||
} else if (key >= (actualStart + actualDeleteCount)) {
|
||||
let fromPresent: Boolean = HasProperty(context, o, key);
|
||||
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);
|
||||
let fromValue: Object = GetProperty(context, o, key);
|
||||
let new_key: Number =
|
||||
key - actualDeleteCount + additionalArgumentCount;
|
||||
CreateDataProperty(context, new_array, new_key, fromValue);
|
||||
if (new_key > 0xfffffffe) {
|
||||
if (big_indices == Undefined) {
|
||||
big_indices = InternalArrayCreate(context, 0);
|
||||
}
|
||||
|
||||
// vi. Decrease k by 1.
|
||||
k = k - 1;
|
||||
let big_indices_array: JSArray =
|
||||
unsafe_cast<JSArray>(big_indices);
|
||||
CreateDataProperty(
|
||||
context, big_indices_array, big_indices_array.length,
|
||||
new_key);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Move contents of new_array into this array
|
||||
MoveArrayContents(context, new_array, o);
|
||||
// Add any moved values that aren't elements anymore.
|
||||
if (big_indices != Undefined) {
|
||||
let big_indices_array: JSArray = unsafe_cast<JSArray>(big_indices);
|
||||
let length: Smi = unsafe_cast<Smi>(big_indices_array.length);
|
||||
for (let i: Smi = 0; i < length; ++i) {
|
||||
let key: Object = GetProperty(context, big_indices_array, i);
|
||||
let value: Object = GetProperty(context, new_array, key);
|
||||
CreateDataProperty(context, o, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +107,10 @@ type FixedUint8ClampedArray extends FixedTypedArray;
|
||||
type FixedBigUint64Array extends FixedTypedArray;
|
||||
type FixedBigInt64Array extends FixedTypedArray;
|
||||
|
||||
const kFixedArrays: constexpr ExtractFixedArrayFlags generates
|
||||
'ExtractFixedArrayFlag::kFixedArrays';
|
||||
const kFixedDoubleArrays: constexpr ExtractFixedArrayFlags generates
|
||||
'ExtractFixedArrayFlag::kFixedDoubleArrays';
|
||||
const kAllFixedArrays: constexpr ExtractFixedArrayFlags generates
|
||||
'ExtractFixedArrayFlag::kAllFixedArrays';
|
||||
|
||||
@ -169,6 +173,7 @@ extern macro Is64(): constexpr bool;
|
||||
|
||||
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;
|
||||
@ -187,7 +192,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;
|
||||
@ -197,7 +203,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;
|
||||
@ -224,6 +232,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;
|
||||
|
||||
@ -242,11 +254,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;
|
||||
@ -313,8 +328,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;
|
||||
@ -322,18 +337,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 {
|
||||
@ -353,8 +370,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;
|
||||
@ -460,6 +477,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);
|
||||
@ -546,6 +570,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);
|
||||
}
|
||||
@ -628,10 +655,13 @@ 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;
|
||||
@ -660,6 +690,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;
|
||||
|
||||
@ -675,8 +727,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;
|
||||
@ -687,8 +744,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;
|
||||
|
||||
@ -723,11 +779,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 Typeof(Object): Object;
|
||||
|
@ -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);
|
||||
@ -530,67 +502,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 {
|
||||
|
@ -318,8 +318,6 @@ namespace internal {
|
||||
CPP(ArrayShift) \
|
||||
/* 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 */ \
|
||||
|
@ -4166,6 +4166,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)));
|
||||
@ -9973,6 +10000,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);
|
||||
@ -10019,6 +10050,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);
|
||||
@ -11927,6 +11962,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));
|
||||
|
||||
|
@ -1191,6 +1191,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,
|
||||
@ -1426,6 +1439,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,
|
||||
@ -1501,6 +1515,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,
|
||||
@ -1541,12 +1559,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);
|
||||
}
|
||||
|
||||
@ -2534,6 +2551,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);
|
||||
@ -2625,26 +2648,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);
|
||||
}
|
||||
|
@ -72,7 +72,6 @@ enum ContextLookupFlags {
|
||||
|
||||
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
||||
V(ARRAY_SHIFT_INDEX, JSFunction, array_shift) \
|
||||
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) \
|
||||
|
@ -873,8 +873,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) {
|
||||
@ -565,83 +536,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.
|
||||
@ -942,7 +836,6 @@ utils.Export(function(to) {
|
||||
"array_values_iterator", ArrayValues,
|
||||
// Fallback implementations of Array builtins.
|
||||
"array_shift", ArrayShiftFallback,
|
||||
"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) \
|
||||
|
@ -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);
|
||||
})();
|
||||
|
@ -474,9 +474,6 @@
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=7993
|
||||
'intl402/RelativeTimeFormat/prototype/toStringTag/toStringTag': [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],
|
||||
|
||||
@ -518,8 +515,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/reverse/length-exceeding-integer-limit-with-proxy': [FAIL],
|
||||
'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],
|
||||
|
Loading…
Reference in New Issue
Block a user