| // Copyright 2019 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. |
| |
| namespace array { |
| // Array.from( items [, mapfn [, thisArg ] ] ) |
| // ES #sec-array.from |
| transitioning javascript builtin |
| ArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(...arguments): |
| JSReceiver { |
| // Use fast path if: |
| // * |items| is the only argument, and |
| // * the receiver is the Array function. |
| if (arguments.length == 1 && receiver == GetArrayFunction()) { |
| try { |
| return iterator::FastIterableToList(arguments[0]) otherwise Slow; |
| } label Slow { |
| // fall through |
| } |
| } |
| |
| const items = arguments[0]; |
| const mapfn = arguments[1]; |
| const thisArg = arguments[2]; |
| |
| // 1. Let C be the this value. |
| const c = receiver; |
| |
| let mapping: bool; |
| // 2. If mapfn is undefined, let mapping be false. |
| if (mapfn == Undefined) { |
| mapping = false; |
| } else { |
| // a. If IsCallable(mapfn) is false, throw a TypeError exception. |
| if (!Is<Callable>(mapfn)) deferred { |
| ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfn); |
| } |
| // b. Let mapping be true. |
| mapping = true; |
| } |
| |
| // 4. Let usingIterator be ? GetMethod(items, @@iterator). |
| // 5. If usingIterator is not undefined, then |
| try { |
| const usingIterator = GetMethod(items, IteratorSymbolConstant()) |
| otherwise IteratorIsUndefined, IteratorNotCallable; |
| |
| let a: JSReceiver; |
| // a. If IsConstructor(C) is true, then |
| typeswitch (c) { |
| case (c: Constructor): { |
| // i. Let A be ? Construct(C). |
| a = Construct(c); |
| } |
| case (JSAny): { |
| // i. Let A be ? ArrayCreate(0). |
| a = ArrayCreate(0); |
| } |
| } |
| |
| // c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator). |
| const iteratorRecord = iterator::GetIterator(items, usingIterator); |
| |
| const fastIteratorResultMap = GetIteratorResultMap(); |
| |
| // d. Let k be 0. |
| let k: Smi = 0; |
| // e. Repeat, |
| while (true) { |
| // i. If k ≥ 2^53-1, then |
| // 1. Let error be ThrowCompletion(a newly created TypeError object). |
| // 2. Return ? IteratorClose(iteratorRecord, error). |
| // The spec requires that we throw an exception if index reaches 2^53-1, |
| // but an empty loop would take >100 days to do this many iterations. To |
| // actually run for that long would require an iterator that never set |
| // done to true and a target array which somehow never ran out of |
| // memory, e.g. a proxy that discarded the values. Ignoring this case |
| // just means we would repeatedly call CreateDataProperty with index = |
| // 2^53 |
| assert(k < kMaxSafeInteger); |
| |
| // ii. Let Pk be ! ToString(k). |
| |
| // iii. Let next be ? IteratorStep(iteratorRecord). |
| let next: JSReceiver; |
| try { |
| next = iterator::IteratorStep(iteratorRecord, fastIteratorResultMap) |
| otherwise NextIsFalse; |
| } |
| // iv. If next is false, then |
| label NextIsFalse { |
| // 1. Perform ? Set(A, "length", k, true). |
| array::SetPropertyLength(a, k); |
| // 2. Return A. |
| return a; |
| } |
| |
| // v. Let nextValue be ? IteratorValue(next). |
| const nextValue = iterator::IteratorValue(next, fastIteratorResultMap); |
| |
| let mappedValue: JSAny; |
| // vi. If mapping is true, then |
| if (mapping) { |
| // 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, k »). |
| // 2. If mappedValue is an abrupt completion, |
| // return ? IteratorClose(iteratorRecord, mappedValue). |
| // 3. Set mappedValue to mappedValue.[[Value]]. |
| try { |
| mappedValue = |
| Call(context, UnsafeCast<Callable>(mapfn), thisArg, nextValue, k); |
| } catch (e) { |
| iterator::IteratorCloseOnException(iteratorRecord); |
| ReThrow(context, e); |
| } |
| } else { |
| mappedValue = nextValue; |
| } |
| // viii. Let defineStatus be |
| // CreateDataPropertyOrThrow(A, Pk, mappedValue). |
| // ix. If defineStatus is an abrupt completion, |
| // return ? IteratorClose(iteratorRecord, defineStatus). |
| try { |
| FastCreateDataProperty(a, k, mappedValue); |
| } catch (e) deferred { |
| iterator::IteratorCloseOnException(iteratorRecord); |
| ReThrow(context, e); |
| } |
| // x. Set k to k + 1. |
| k += 1; |
| } |
| unreachable; |
| } label IteratorIsUndefined { |
| // 6. NOTE: items is not an Iterable so assume it is an array-like object. |
| // 7. Let arrayLike be ! ToObject(items). |
| const arrayLike = ToObject_Inline(context, items); |
| // 8. Let len be ? LengthOfArrayLike(arrayLike). |
| const len = GetLengthProperty(arrayLike); |
| |
| let a: JSReceiver; |
| // 9. If IsConstructor(C) is true, then |
| typeswitch (c) { |
| case (c: Constructor): { |
| // a. Let A be ? Construct(C, « len »). |
| a = Construct(c, len); |
| } |
| case (JSAny): { |
| // a. Let A be ? ArrayCreate(len). |
| a = ArrayCreate(len); |
| } |
| } |
| |
| // 11. Let k be 0. |
| let k: Smi = 0; |
| // 12. Repeat, while k < len |
| while (k < len) { |
| // a. Let Pk be ! ToString(k). |
| // b. Let kValue be ? Get(arrayLike, Pk). |
| const kValue = GetProperty(arrayLike, k); |
| let mappedValue: JSAny; |
| // c. If mapping is true, then |
| if (mapping) { |
| // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). |
| mappedValue = |
| Call(context, UnsafeCast<Callable>(mapfn), thisArg, kValue, k); |
| } else { |
| // d. Else, let mappedValue be kValue. |
| mappedValue = kValue; |
| } |
| // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). |
| FastCreateDataProperty(a, k, mappedValue); |
| // f. Set k to k + 1. |
| k += 1; |
| } |
| |
| // 13. Perform ? Set(A, "length", len, true). |
| array::SetPropertyLength(a, len); |
| // 14. Return A. |
| return a; |
| } label IteratorNotCallable(_value: JSAny) deferred { |
| ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); |
| } |
| } |
| } |