| // 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). | |
| const pK: String = ToString_Inline(context, k); | |
| // 6b. Let kPresent be ? HasProperty(O, Pk). | |
| const kPresent: Boolean = HasProperty(context, o, pK); | |
| // 6c. If kPresent is true, then | |
| if (kPresent == True) { | |
| // 6c. i. Let kValue be ? Get(O, Pk). | |
| const 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. | |
| const 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. | |
| const 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 { | |
| const callbackfn: Callable = | |
| cast<Callable>(callback) otherwise Unexpected; | |
| const k: Smi = cast<Smi>(initialK) otherwise Unexpected; | |
| const number_length: Number = cast<Number>(length) otherwise Unexpected; | |
| 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; | |
| const 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 { | |
| const 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 { | |
| const smi_len: Smi = cast<Smi>(len) otherwise Slow; | |
| const a: JSArray = cast<JSArray>(o) otherwise Slow; | |
| const map: Map = a.map; | |
| if (!IsPrototypeInitialArrayPrototype(context, map)) goto Slow; | |
| const 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). | |
| const o: JSReceiver = ToObject_Inline(context, receiver); | |
| // 2. Let len be ? ToLength(? Get(O, "length")). | |
| const len: Number = GetLengthProperty(context, o); | |
| // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. | |
| if (arguments.length == 0) { | |
| goto TypeError; | |
| } | |
| const callbackfn: Callable = | |
| cast<Callable>(arguments[0]) otherwise TypeError; | |
| // 4. If thisArg is present, let T be thisArg; else let T be undefined. | |
| const 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'); | |
| } | |
| } | |
| } |