blob: e51d37fef2ccdb3a1face5dc9ab8c1e429c3e749 [file] [log] [blame]
// 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);
}
}
}