Commit e99a1092 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

Revert "[builtins] Reland Array.prototype.splice() Torque implementation."

This reverts commit cdaaa311.

Reason for revert: chromium:876445 chromium:876453 chromium:876443

Original change's description:
> [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}

TBR=mvstanton@chromium.org,jgruber@chromium.org,tebbi@chromium.org

Change-Id: I5b750a98e671b7284474ffcabc6b4d37a9d1219e
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/1184741Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55289}
parent 91041c12
......@@ -888,7 +888,6 @@ 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,8 +1741,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
1, false);
SimpleInstallFunction(isolate_, proto, "slice",
Builtins::kArrayPrototypeSlice, 2, false);
SimpleInstallFunction(isolate_, proto, "splice", Builtins::kArraySplice, 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, "includes", Builtins::kArrayIncludes,
1, false);
SimpleInstallFunction(isolate_, proto, "indexOf", Builtins::kArrayIndexOf,
......
......@@ -62,10 +62,6 @@ 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);
......
// 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,197 +13,293 @@ module array {
}
}
macro IsArray(o: Object): bool {
try {
let array: JSArray = cast<JSArray>(o) otherwise NotArray;
return true;
}
label NotArray {
return false;
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 StoreArrayHole(elements: FixedDoubleArray, k: Smi): void {
StoreFixedDoubleArrayHoleSmi(elements, k);
}
// 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;
macro StoreArrayHole(elements: FixedArray, k: Smi): void {
elements[k] = Hole;
}
let deletedResult: JSArray =
ExtractFastJSArray(context, a, actualStart, actualDeleteCount);
macro CopyArrayElement(
elements: FixedArray, newElements: FixedArray, from: Smi, to: Smi): void {
let e: Object = elements[from];
newElements[to] = e;
if (newLength == 0) {
a.elements = kEmptyFixedArray;
a.length = 0;
return deletedResult;
}
macro CopyArrayElement(
elements: FixedDoubleArray, newElements: FixedDoubleArray, from: Smi,
to: Smi): void {
try {
let floatValue: float64 = LoadDoubleWithHoleCheck(elements, from)
otherwise FoundHole;
newElements[to] = floatValue;
}
label FoundHole {
StoreArrayHole(newElements, to);
}
}
let elements: FixedArray = cast<FixedArray>(a.elements) otherwise Bailout;
let elementsMap: Map = elements.map;
///////////////////////////////////////////////////////////////////////////////////
// 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;
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;
// 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;
}
return false;
}
label ReturnFalse {
return false;
// 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--;
}
label ReturnTrue {
return true;
// 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;
}
}
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);
// 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);
// 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 {
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);
}
}
}
// 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 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;
try {
return FastArraySplice(
context, arguments, o, len, actualStart, insertCount,
actualDeleteCount) otherwise Bailout;
}
// 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));
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);
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) {
let fromValue: Object = GetProperty(context, o, i);
CreateDataProperty(context, new_array, i, fromValue);
// 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;
}
for (let i: Number = actualStart + actualDeleteCount; i < limit; i++) {
let fromPresent: Boolean = HasProperty(context, o, i);
// 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) {
let fromValue: Object = GetProperty(context, o, i);
let toIndex: Number = i - actualDeleteCount + additionalArgumentCount;
CreateDataProperty(context, new_array, toIndex, fromValue);
// 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;
}
} 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);
// 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 if (key >= (actualStart + actualDeleteCount)) {
let fromPresent: Boolean = HasProperty(context, o, key);
// 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) {
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);
// 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 big_indices_array: JSArray =
unsafe_cast<JSArray>(big_indices);
CreateDataProperty(
context, big_indices_array, big_indices_array.length,
new_key);
}
}
// vi. Decrease k by 1.
k = k - 1;
}
}
}
// 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);
// 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,10 +107,6 @@ 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';
......@@ -173,7 +169,6 @@ 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;
......@@ -192,8 +187,7 @@ 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): JSReceiver;
extern macro InternalArrayCreate(Context, Number): JSArray;
extern macro ArraySpeciesCreate(Context, Object, Number): Object;
extern macro EnsureArrayPushable(Map): ElementsKind labels Bailout;
extern builtin ToObject(Context, Object): JSReceiver;
......@@ -203,9 +197,7 @@ extern macro IsTheHole(Object): bool;
extern macro IsString(HeapObject): bool;
extern builtin ToString(Context, Object): String;
extern runtime NormalizeElements(Context, JSObject);
extern runtime TransitionElementsKindWithKind(Context, JSObject, Smi);
extern runtime CreateDataProperty(Context, JSReceiver, Object, Object);
extern runtime CreateDataProperty(Context, Object, Object, Object);
extern macro LoadRoot(constexpr RootListIndex): Object;
extern macro StoreRoot(constexpr RootListIndex, Object): Object;
......@@ -232,10 +224,6 @@ 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;
......@@ -254,14 +242,11 @@ 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;
......@@ -328,8 +313,8 @@ extern operator '.length' macro GetArgumentsLength(constexpr Arguments): intptr;
extern operator
'[]' macro GetArgumentValue(constexpr Arguments, intptr): Object;
extern macro TaggedIsSmi(Object): bool;
extern macro TaggedIsNotSmi(Object): bool;
extern operator 'is<Smi>' macro TaggedIsSmi(Object): bool;
extern operator 'isnt<Smi>' macro TaggedIsNotSmi(Object): bool;
extern macro TaggedIsPositiveSmi(Object): bool;
extern macro HeapObjectToJSDataView(HeapObject): JSDataView labels CastError;
......@@ -337,20 +322,18 @@ 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 {
......@@ -370,8 +353,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;
......@@ -477,13 +460,6 @@ 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);
......@@ -570,9 +546,6 @@ 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);
}
......@@ -655,13 +628,10 @@ 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;
......@@ -690,28 +660,6 @@ 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;
......@@ -727,13 +675,8 @@ 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;
......@@ -744,7 +687,8 @@ extern macro Call(
extern macro Call(
Context, Callable, Object, Object, Object, Object, Object, Object): Object;
extern macro ExtractFixedArray(FixedArrayBase, Smi, Smi, Smi): FixedArrayBase;
extern macro ExtractFixedArray(
FixedArray, Smi, Smi, Smi, constexpr ExtractFixedArrayFlags): FixedArray;
extern builtin ExtractFastJSArray(Context, JSArray, Smi, Smi): JSArray;
......@@ -779,17 +723,11 @@ 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,6 +23,34 @@ 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);
......@@ -502,6 +530,67 @@ 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,6 +318,8 @@ 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 */ \
......
......@@ -4232,33 +4232,6 @@ 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)));
......@@ -10066,10 +10039,6 @@ 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);
......@@ -10116,10 +10085,6 @@ 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);
......@@ -12028,15 +11993,6 @@ 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));
......
......@@ -1201,19 +1201,6 @@ 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,
......@@ -1449,7 +1436,6 @@ 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,
......@@ -1525,10 +1511,6 @@ 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,
......@@ -1569,11 +1551,12 @@ 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) {
return ExtractFixedArray(source, first, count, capacity,
ExtractFixedArrayFlag::kAllFixedArrays,
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,
SMI_PARAMETERS);
}
......@@ -2564,12 +2547,6 @@ 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);
......@@ -2661,32 +2638,26 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
void BranchIfNumberRelationalComparison(Operation op, 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) {
void BranchIfNumberLessThan(Node* left, Node* right, Label* if_true,
Label* if_false) {
BranchIfNumberRelationalComparison(Operation::kLessThan, left, right,
if_true, if_false);
}
void BranchIfNumberLessThanOrEqual(TNode<Number> left, TNode<Number> right,
Label* if_true, Label* if_false) {
void BranchIfNumberLessThanOrEqual(Node* left, Node* right, Label* if_true,
Label* if_false) {
BranchIfNumberRelationalComparison(Operation::kLessThanOrEqual, left, right,
if_true, if_false);
}
void BranchIfNumberGreaterThan(TNode<Number> left, TNode<Number> right,
Label* if_true, Label* if_false) {
void BranchIfNumberGreaterThan(Node* left, Node* right, Label* if_true,
Label* if_false) {
BranchIfNumberRelationalComparison(Operation::kGreaterThan, left, right,
if_true, if_false);
}
void BranchIfNumberGreaterThanOrEqual(TNode<Number> left, TNode<Number> right,
Label* if_true, Label* if_false) {
void BranchIfNumberGreaterThanOrEqual(Node* left, Node* right, Label* if_true,
Label* if_false) {
BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, left,
right, if_true, if_false);
}
......
......@@ -72,6 +72,7 @@ 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,6 +873,8 @@ 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")
......
......@@ -200,6 +200,35 @@ 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) {
......@@ -536,6 +565,83 @@ 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.
......@@ -836,6 +942,7 @@ utils.Export(function(to) {
"array_values_iterator", ArrayValues,
// Fallback implementations of Array builtins.
"array_shift", ArrayShiftFallback,
"array_splice", ArraySpliceFallback,
"array_unshift", ArrayUnshiftFallback,
]);
......
......@@ -30,16 +30,6 @@ 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,22 +36,21 @@ 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) \
F(TransitionElementsKindWithKind, 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(TrySliceSimpleNonFastElements, 3, 1)
#define FOR_EACH_INTRINSIC_ATOMICS(F) \
......
......@@ -445,21 +445,3 @@
"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,6 +474,9 @@
# 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],
......@@ -515,6 +518,8 @@
# 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],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment