blob: c00dc843bc0a0f96dd2066ea8c7a1b7d5269adef [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 ArrayForEachTorqueContinuation(
context: Context, o: JSReceiver, len: Number, callbackfn: Callable,
thisArg: Object, initial_k: Smi): Object {
// 5. Let k be 0.
// 6. Repeat, while k < len
for (let k: Smi = initial_k; k < len; k = k + 1) {
// 6a. Let Pk be ! ToString(k).
let pK: String = ToString_Inline(context, k);
// 6b. Let kPresent be ? HasProperty(O, Pk).
let kPresent: Oddball = TorqueHasProperty(context, o, pK);
// 6c. If kPresent is true, then
if (kPresent == True) {
// 6c. i. Let kValue be ? Get(O, Pk).
let kValue: Object = GetProperty(context, o, pK);
// 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>).
Call(context, callbackfn, thisArg, kValue, k, o);
}
// 6d. Increase k by 1. (done by the loop).
}
return Undefined;
}
javascript builtin ArrayForEachLoopEagerDeoptContinuation(
context: Context, receiver: Object, callback: Object, thisArg: Object,
initialK: Object, length: Object): Object {
// 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>(receiver);
return ArrayForEachLoopContinuation(
context, jsreceiver, callback, thisArg, Undefined, jsreceiver, initialK,
length, Undefined);
}
javascript builtin ArrayForEachLoopLazyDeoptContinuation(
context: Context, receiver: Object, callback: Object, thisArg: Object,
initialK: Object, length: Object, result: Object): Object {
// 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>(receiver);
return ArrayForEachLoopContinuation(
context, jsreceiver, callback, thisArg, Undefined, jsreceiver, initialK,
length, Undefined);
}
builtin ArrayForEachLoopContinuation(
context: Context, receiver: JSReceiver, callback: Object, thisArg: Object,
array: Object, object: Object, initialK: Object, length: Object,
to: Object): Object {
try {
let callbackfn: Callable = cast<Callable>(callback) otherwise Unexpected;
let k: Smi = cast<Smi>(initialK) otherwise Unexpected;
let 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);
}
label Unexpected {
unreachable;
}
}
macro VisitAllElements<FixedArrayType : type>(
context: Context, a: JSArray, len: Smi, callbackfn: Callable,
thisArg: Object): void labels
Bailout(Smi) {
let k: Smi = 0;
let map: Map = a.map;
try {
// Build a fast loop over the smi array.
for (; k < len; k = k + 1) {
// Ensure that the map didn't change.
if (map != a.map) goto Slow;
// Ensure that we haven't walked beyond a possibly updated length.
if (k >= a.length) goto Slow;
try {
let value: Object =
LoadElementNoHole<FixedArrayType>(a, k) otherwise FoundHole;
Call(context, callbackfn, thisArg, value, k, a);
}
label FoundHole {
// If we found the hole, we need to bail out if the initial
// array prototype has had elements inserted. This is preferable
// to walking the prototype chain looking for elements.
if (IsNoElementsProtectorCellInvalid()) goto Bailout(k);
}
}
}
label Slow {
goto Bailout(k);
}
}
macro FastArrayForEach(
context: Context, o: JSReceiver, len: Number, callbackfn: Callable,
thisArg: Object): Object labels
Bailout(Smi) {
let k: Smi = 0;
try {
let smi_len: Smi = cast<Smi>(len) otherwise Slow;
let a: JSArray = cast<JSArray>(o) otherwise Slow;
let map: Map = a.map;
if (!IsPrototypeInitialArrayPrototype(context, map)) goto Slow;
let elementsKind: ElementsKind = map.elements_kind;
if (!IsFastElementsKind(elementsKind)) goto Slow;
if (IsElementsKindGreaterThan(elementsKind, HOLEY_ELEMENTS)) {
VisitAllElements<FixedDoubleArray>(
context, a, smi_len, callbackfn, thisArg)
otherwise Bailout;
} else {
VisitAllElements<FixedArray>(context, a, smi_len, callbackfn, thisArg)
otherwise Bailout;
}
}
label Slow {
goto Bailout(k);
}
return Undefined;
}
// https://tc39.github.io/ecma262/#sec-array.prototype.foreach
javascript builtin ArrayForEach(
context: Context, receiver: Object, ...arguments): Object {
try {
if (IsNullOrUndefined(receiver)) {
goto NullOrUndefinedError;
}
// 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. If IsCallable(callbackfn) is false, throw a TypeError exception.
if (arguments.length == 0) {
goto TypeError;
}
let callbackfn: Callable =
cast<Callable>(arguments[0]) otherwise TypeError;
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
let thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined;
// Special cases.
let k: Smi = 0;
try {
return FastArrayForEach(context, o, len, callbackfn, thisArg)
otherwise Bailout;
}
label Bailout(k_value: Smi) {
k = k_value;
}
return ArrayForEachTorqueContinuation(
context, o, len, callbackfn, thisArg, k);
}
label TypeError {
ThrowTypeError(context, kCalledNonCallable, arguments[0]);
}
label NullOrUndefinedError {
ThrowTypeError(
context, kCalledOnNullOrUndefined, 'Array.prototype.forEach');
}
}
}