1// Copyright 2019 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 5namespace array { 6// Array.from( items [, mapfn [, thisArg ] ] ) 7// ES #sec-array.from 8transitioning javascript builtin 9ArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(...arguments): 10 JSReceiver { 11 const c = HasBuiltinSubclassingFlag() ? receiver : GetArrayFunction(); 12 13 // Use fast path if: 14 // * |items| is the only argument, and 15 // * the receiver is the Array function. 16 if (arguments.length == 1 && c == GetArrayFunction()) { 17 try { 18 return iterator::FastIterableToList(arguments[0]) otherwise Slow; 19 } label Slow { 20 // fall through 21 } 22 } 23 24 const items = arguments[0]; 25 const mapfn = arguments[1]; 26 const thisArg = arguments[2]; 27 28 // 1. Let C be the this value. 29 // (Done above.) 30 31 let mapping: bool; 32 // 2. If mapfn is undefined, let mapping be false. 33 if (mapfn == Undefined) { 34 mapping = false; 35 } else { 36 // a. If IsCallable(mapfn) is false, throw a TypeError exception. 37 if (!Is<Callable>(mapfn)) deferred { 38 ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfn); 39 } 40 // b. Let mapping be true. 41 mapping = true; 42 } 43 44 // 4. Let usingIterator be ? GetMethod(items, @@iterator). 45 // 5. If usingIterator is not undefined, then 46 try { 47 const usingIterator = GetMethod(items, IteratorSymbolConstant()) 48 otherwise IteratorIsUndefined, IteratorNotCallable; 49 50 let a: JSReceiver; 51 // a. If IsConstructor(C) is true, then 52 typeswitch (c) { 53 case (c: Constructor): { 54 // i. Let A be ? Construct(C). 55 a = Construct(c); 56 } 57 case (JSAny): { 58 // i. Let A be ? ArrayCreate(0). 59 a = ArrayCreate(0); 60 } 61 } 62 63 // c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator). 64 const iteratorRecord = iterator::GetIterator(items, usingIterator); 65 66 const fastIteratorResultMap = GetIteratorResultMap(); 67 68 // d. Let k be 0. 69 let k: Smi = 0; 70 // e. Repeat, 71 while (true) { 72 // i. If k ≥ 2^53-1, then 73 // 1. Let error be ThrowCompletion(a newly created TypeError object). 74 // 2. Return ? IteratorClose(iteratorRecord, error). 75 // The spec requires that we throw an exception if index reaches 2^53-1, 76 // but an empty loop would take >100 days to do this many iterations. To 77 // actually run for that long would require an iterator that never set 78 // done to true and a target array which somehow never ran out of 79 // memory, e.g. a proxy that discarded the values. Ignoring this case 80 // just means we would repeatedly call CreateDataProperty with index = 81 // 2^53 82 dcheck(k < kMaxSafeInteger); 83 84 // ii. Let Pk be ! ToString(k). 85 86 // iii. Let next be ? IteratorStep(iteratorRecord). 87 let next: JSReceiver; 88 try { 89 next = iterator::IteratorStep(iteratorRecord, fastIteratorResultMap) 90 otherwise NextIsFalse; 91 } 92 // iv. If next is false, then 93 label NextIsFalse { 94 // 1. Perform ? Set(A, "length", k, true). 95 array::SetPropertyLength(a, k); 96 // 2. Return A. 97 return a; 98 } 99 100 // v. Let nextValue be ? IteratorValue(next). 101 const nextValue = iterator::IteratorValue(next, fastIteratorResultMap); 102 103 let mappedValue: JSAny; 104 // vi. If mapping is true, then 105 if (mapping) { 106 // 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, k »). 107 // 2. If mappedValue is an abrupt completion, 108 // return ? IteratorClose(iteratorRecord, mappedValue). 109 // 3. Set mappedValue to mappedValue.[[Value]]. 110 try { 111 mappedValue = 112 Call(context, UnsafeCast<Callable>(mapfn), thisArg, nextValue, k); 113 } catch (e, message) { 114 iterator::IteratorCloseOnException(iteratorRecord); 115 ReThrowWithMessage(context, e, message); 116 } 117 } else { 118 mappedValue = nextValue; 119 } 120 // viii. Let defineStatus be 121 // CreateDataPropertyOrThrow(A, Pk, mappedValue). 122 // ix. If defineStatus is an abrupt completion, 123 // return ? IteratorClose(iteratorRecord, defineStatus). 124 try { 125 FastCreateDataProperty(a, k, mappedValue); 126 } catch (e, message) deferred { 127 iterator::IteratorCloseOnException(iteratorRecord); 128 ReThrowWithMessage(context, e, message); 129 } 130 // x. Set k to k + 1. 131 k += 1; 132 } 133 unreachable; 134 } label IteratorIsUndefined { 135 // 6. NOTE: items is not an Iterable so assume it is an array-like object. 136 // 7. Let arrayLike be ! ToObject(items). 137 const arrayLike = ToObject_Inline(context, items); 138 // 8. Let len be ? LengthOfArrayLike(arrayLike). 139 const len = GetLengthProperty(arrayLike); 140 141 let a: JSReceiver; 142 // 9. If IsConstructor(C) is true, then 143 typeswitch (c) { 144 case (c: Constructor): { 145 // a. Let A be ? Construct(C, « len »). 146 a = Construct(c, len); 147 } 148 case (JSAny): { 149 // a. Let A be ? ArrayCreate(len). 150 a = ArrayCreate(len); 151 } 152 } 153 154 // 11. Let k be 0. 155 let k: Smi = 0; 156 // 12. Repeat, while k < len 157 while (k < len) { 158 // a. Let Pk be ! ToString(k). 159 // b. Let kValue be ? Get(arrayLike, Pk). 160 const kValue = GetProperty(arrayLike, k); 161 let mappedValue: JSAny; 162 // c. If mapping is true, then 163 if (mapping) { 164 // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). 165 mappedValue = 166 Call(context, UnsafeCast<Callable>(mapfn), thisArg, kValue, k); 167 } else { 168 // d. Else, let mappedValue be kValue. 169 mappedValue = kValue; 170 } 171 // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). 172 FastCreateDataProperty(a, k, mappedValue); 173 // f. Set k to k + 1. 174 k += 1; 175 } 176 177 // 13. Perform ? Set(A, "length", len, true). 178 array::SetPropertyLength(a, len); 179 // 14. Return A. 180 return a; 181 } label IteratorNotCallable(_value: JSAny) deferred { 182 ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); 183 } 184} 185} 186