1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5module array { 6 macro ArrayForEachTorqueContinuation( 7 context: Context, o: JSReceiver, len: Number, callbackfn: Callable, 8 thisArg: Object, initial_k: Smi): Object { 9 // 5. Let k be 0. 10 // 6. Repeat, while k < len 11 for (let k: Smi = initial_k; k < len; k = k + 1) { 12 // 6a. Let Pk be ! ToString(k). 13 const pK: String = ToString_Inline(context, k); 14 15 // 6b. Let kPresent be ? HasProperty(O, Pk). 16 const kPresent: Boolean = HasProperty(context, o, pK); 17 18 // 6c. If kPresent is true, then 19 if (kPresent == True) { 20 // 6c. i. Let kValue be ? Get(O, Pk). 21 const kValue: Object = GetProperty(context, o, pK); 22 23 // 6c. ii. Perform ? Call(callbackfn, T, <kValue, k, O>). 24 Call(context, callbackfn, thisArg, kValue, k, o); 25 } 26 27 // 6d. Increase k by 1. (done by the loop). 28 } 29 return Undefined; 30 } 31 32 javascript builtin ArrayForEachLoopEagerDeoptContinuation( 33 context: Context, receiver: Object, callback: Object, thisArg: Object, 34 initialK: Object, length: Object): Object { 35 // The unsafe cast is safe because all continuation points in forEach are 36 // after the ToObject(O) call that ensures we are dealing with a 37 // JSReceiver. 38 const jsreceiver: JSReceiver = unsafe_cast<JSReceiver>(receiver); 39 return ArrayForEachLoopContinuation( 40 context, jsreceiver, callback, thisArg, Undefined, jsreceiver, initialK, 41 length, Undefined); 42 } 43 44 javascript builtin ArrayForEachLoopLazyDeoptContinuation( 45 context: Context, receiver: Object, callback: Object, thisArg: Object, 46 initialK: Object, length: Object, result: Object): Object { 47 // The unsafe cast is safe because all continuation points in forEach are 48 // after the ToObject(O) call that ensures we are dealing with a 49 // JSReceiver. 50 const jsreceiver: JSReceiver = unsafe_cast<JSReceiver>(receiver); 51 return ArrayForEachLoopContinuation( 52 context, jsreceiver, callback, thisArg, Undefined, jsreceiver, initialK, 53 length, Undefined); 54 } 55 56 builtin ArrayForEachLoopContinuation( 57 context: Context, receiver: JSReceiver, callback: Object, thisArg: Object, 58 array: Object, object: Object, initialK: Object, length: Object, 59 to: Object): Object { 60 try { 61 const callbackfn: Callable = 62 cast<Callable>(callback) otherwise Unexpected; 63 const k: Smi = cast<Smi>(initialK) otherwise Unexpected; 64 const number_length: Number = cast<Number>(length) otherwise Unexpected; 65 66 return ArrayForEachTorqueContinuation( 67 context, receiver, number_length, callbackfn, thisArg, k); 68 } 69 label Unexpected { 70 unreachable; 71 } 72 } 73 74 macro VisitAllElements<FixedArrayType : type>( 75 context: Context, a: JSArray, len: Smi, callbackfn: Callable, 76 thisArg: Object): void labels 77 Bailout(Smi) { 78 let k: Smi = 0; 79 const map: Map = a.map; 80 81 try { 82 // Build a fast loop over the smi array. 83 for (; k < len; k = k + 1) { 84 // Ensure that the map didn't change. 85 if (map != a.map) goto Slow; 86 // Ensure that we haven't walked beyond a possibly updated length. 87 if (k >= a.length) goto Slow; 88 89 try { 90 const value: Object = 91 LoadElementNoHole<FixedArrayType>(a, k) otherwise FoundHole; 92 Call(context, callbackfn, thisArg, value, k, a); 93 } 94 label FoundHole { 95 // If we found the hole, we need to bail out if the initial 96 // array prototype has had elements inserted. This is preferable 97 // to walking the prototype chain looking for elements. 98 99 if (IsNoElementsProtectorCellInvalid()) goto Bailout(k); 100 } 101 } 102 } 103 label Slow { 104 goto Bailout(k); 105 } 106 } 107 108 macro FastArrayForEach( 109 context: Context, o: JSReceiver, len: Number, callbackfn: Callable, 110 thisArg: Object): Object labels 111 Bailout(Smi) { 112 let k: Smi = 0; 113 try { 114 const smi_len: Smi = cast<Smi>(len) otherwise Slow; 115 const a: JSArray = cast<JSArray>(o) otherwise Slow; 116 const map: Map = a.map; 117 118 if (!IsPrototypeInitialArrayPrototype(context, map)) goto Slow; 119 const elementsKind: ElementsKind = map.elements_kind; 120 if (!IsFastElementsKind(elementsKind)) goto Slow; 121 122 if (IsElementsKindGreaterThan(elementsKind, HOLEY_ELEMENTS)) { 123 VisitAllElements<FixedDoubleArray>( 124 context, a, smi_len, callbackfn, thisArg) 125 otherwise Bailout; 126 } else { 127 VisitAllElements<FixedArray>(context, a, smi_len, callbackfn, thisArg) 128 otherwise Bailout; 129 } 130 } 131 label Slow { 132 goto Bailout(k); 133 } 134 return Undefined; 135 } 136 137 // https://tc39.github.io/ecma262/#sec-array.prototype.foreach 138 javascript builtin ArrayForEach( 139 context: Context, receiver: Object, ...arguments): Object { 140 try { 141 if (IsNullOrUndefined(receiver)) { 142 goto NullOrUndefinedError; 143 } 144 145 // 1. Let O be ? ToObject(this value). 146 const o: JSReceiver = ToObject_Inline(context, receiver); 147 148 // 2. Let len be ? ToLength(? Get(O, "length")). 149 const len: Number = GetLengthProperty(context, o); 150 151 // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. 152 if (arguments.length == 0) { 153 goto TypeError; 154 } 155 const callbackfn: Callable = 156 cast<Callable>(arguments[0]) otherwise TypeError; 157 158 // 4. If thisArg is present, let T be thisArg; else let T be undefined. 159 const thisArg: Object = arguments.length > 1 ? arguments[1] : Undefined; 160 161 // Special cases. 162 let k: Smi = 0; 163 try { 164 return FastArrayForEach(context, o, len, callbackfn, thisArg) 165 otherwise Bailout; 166 } 167 label Bailout(k_value: Smi) { 168 k = k_value; 169 } 170 171 return ArrayForEachTorqueContinuation( 172 context, o, len, callbackfn, thisArg, k); 173 } 174 label TypeError { 175 ThrowTypeError(context, kCalledNonCallable, arguments[0]); 176 } 177 label NullOrUndefinedError { 178 ThrowTypeError( 179 context, kCalledOnNullOrUndefined, 'Array.prototype.forEach'); 180 } 181 } 182} 183