blob: 5fc6853644846f5b579d93af4b6c046b6ef7f009 [file] [log] [blame]
// 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 {
macro GetLengthProperty(context: Context, o: Object): Number {
if (BranchIfFastJSArray(o, context)) {
let a: JSArray = unsafe_cast<JSArray>(o);
return a.length_fast;
} else
deferred {
return ToLength_Inline(context, GetProperty(context, o, 'length'));
}
}
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;
}
}
// 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;
}
// 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 {
// 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);
}
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.
// 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 = TorqueHasProperty(context, o, from);
// c. If fromPresent is true, then
if (fromPresent == True) {
// i. Let fromValue be ? Get(O, from).
let fromValue: Object = GetProperty(context, o, from);
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue).
CreateDataProperty(context, a, ToString_Inline(context, k), fromValue);
}
// d. Increment k by 1.
k = k + 1;
}
// 12. Perform ? Set(A, "length", actualDeleteCount, true).
SetProperty(context, a, 'length', actualDeleteCount, kStrict);
// 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 = TorqueHasProperty(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, kStrict);
// v. Else fromPresent is false,
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(context, o, to, kStrict);
}
// vi. Increase k by 1.
k = k + 1;
}
// c. Let k be len.
k = len;
// d. Repeat, while k > (len - actualDeleteCount + itemCount)
while (k > (len - actualDeleteCount + itemCount)) {
// i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)).
DeleteProperty(context, o, ToString_Inline(context, k - 1), kStrict);
// ii. Decrease k by 1.
k = k - 1;
}
// 16. Else if itemCount > actualDeleteCount, then
} else if (itemCount > actualDeleteCount) {
// a. Let k be (len - actualDeleteCount).
let k: Number = len - actualDeleteCount;
// b. Repeat, while k > actualStart
while (k > actualStart) {
// i. Let from be ! ToString(k + actualDeleteCount - 1).
let from: String = ToString_Inline(context, k + actualDeleteCount - 1);
// ii. Let to be ! ToString(k + itemCount - 1).
let to: String = ToString_Inline(context, k + itemCount - 1);
// iii. Let fromPresent be ? HasProperty(O, from).
let fromPresent: Oddball = TorqueHasProperty(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, kStrict);
// v. Else fromPresent is false,
} else {
// 1. Perform ? DeletePropertyOrThrow(O, to).
DeleteProperty(context, o, to, kStrict);
}
// vi. Decrease k by 1.
k = k - 1;
}
}
// 17. Let k be actualStart.
k = actualStart;
// 18. Repeat, while items is not empty
// a. Remove the first element from items and let E be the value of that
// element.
if (arguments.length > 2) {
for (let e: Object of arguments [2: ]) {
// b. Perform ? Set(O, ! ToString(k), E, true).
SetProperty(context, o, ToString_Inline(context, k), e, kStrict);
// c. Increase k by 1.
k = k + 1;
}
}
// 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount,
// true).
SetProperty(
context, o, 'length', len - actualDeleteCount + itemCount, kStrict);
return a;
}
}