blob: ecc9145cbda734afb7dd4096926a6d0b64f06a60 [file] [log] [blame]
// Copyright 2023 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 {
extern enum ArrayFromAsyncLabels extends uint31
constexpr 'ArrayBuiltins::ArrayFromAsyncLabels' {
kGetIteratorStep,
kCheckIteratorValueAndMapping,
kIteratorMapping,
kGetIteratorValueWithMapping,
kAddIteratorValueToTheArray,
kGetArrayLikeValue,
kCheckArrayLikeValueAndMapping,
kGetArrayLikeValueWithMapping,
kAddArrayLikeValueToTheArray,
kDoneAndResolvePromise,
kCloseAsyncIterator,
kRejectPromise
}
transitioning macro ArrayFromAsyncAwaitPoint<T : type extends FunctionContext>(
implicit context: Context)(resolveContext: T, stepSlot: Slot<T, Smi>,
promiseFunSlot: Slot<T, JSReceiver>,
resolveSlot: Slot<T, Undefined|JSFunction>,
rejectSlot: Slot<T, Undefined|JSFunction>, step: ArrayFromAsyncLabels,
value: JSAny): JSAny {
*ContextSlot(resolveContext, stepSlot) = SmiTag<ArrayFromAsyncLabels>(step);
const promiseFun = *ContextSlot(resolveContext, promiseFunSlot);
const resolve = *ContextSlot(resolveContext, resolveSlot);
const reject = *ContextSlot(resolveContext, rejectSlot);
const resultPromise = promise::PromiseResolve(promiseFun, value);
promise::PerformPromiseThenImpl(
UnsafeCast<JSPromise>(resultPromise), resolve, reject, Undefined);
return Undefined;
}
// This macro reject the promise if any exception occurs in the execution of
// the asynchronous code.
transitioning macro
RejectArrayFromAsyncPromise<T : type extends FunctionContext>(
implicit context: Context)(resolveContext: T, errorSlot: Slot<T, JSAny>,
promiseSlot: Slot<T, JSPromise>): JSAny {
const error = *ContextSlot(resolveContext, errorSlot);
const promise = *ContextSlot(resolveContext, promiseSlot);
return promise::RejectPromise(promise, error, False);
}
// --- Iterable path
struct ArrayFromAsyncIterableResumeState {
step: ArrayFromAsyncLabels;
awaitedValue: JSAny;
index: Smi;
}
type ArrayFromAsyncIterableResolveContext extends FunctionContext;
extern enum ArrayFromAsyncIterableResolveContextSlots extends intptr
constexpr 'ArrayBuiltins::ArrayFromAsyncIterableResolveContextSlots' {
kArrayFromAsyncIterableResolveResumeStateStepSlot:
Slot<ArrayFromAsyncIterableResolveContext, Smi>,
kArrayFromAsyncIterableResolveResumeStateAwaitedValueSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSAny>,
kArrayFromAsyncIterableResolveResumeStateIndexSlot:
Slot<ArrayFromAsyncIterableResolveContext, Smi>,
kArrayFromAsyncIterableResolvePromiseSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSPromise>,
kArrayFromAsyncIterableResolvePromiseFunctionSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSReceiver>,
kArrayFromAsyncIterableResolveOnFulfilledFunctionSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSFunction|Undefined>,
kArrayFromAsyncIterableResolveOnRejectedFunctionSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSFunction|Undefined>,
kArrayFromAsyncIterableResolveResultArraySlot:
Slot<ArrayFromAsyncIterableResolveContext, JSReceiver>,
kArrayFromAsyncIterableResolveIteratorSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSReceiver>,
kArrayFromAsyncIterableResolveNextMethodSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSAny>,
kArrayFromAsyncIterableResolveErrorSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSAny>,
kArrayFromAsyncIterableResolveMapfnSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSAny>,
kArrayFromAsyncIterableResolveThisArgSlot:
Slot<ArrayFromAsyncIterableResolveContext, JSAny>,
kArrayFromAsyncIterableResolveLength
}
extern macro AllocateRootFunctionWithContext(
constexpr intptr, FunctionContext, NativeContext): JSFunction;
const kArrayFromAsyncIterableOnFulfilledSharedFun: constexpr intptr
generates 'RootIndex::kArrayFromAsyncIterableOnFulfilledSharedFun';
const kArrayFromAsyncIterableOnRejectedSharedFun: constexpr intptr
generates 'RootIndex::kArrayFromAsyncIterableOnRejectedSharedFun';
macro CreateArrayFromAsyncIterableResolveContext(
implicit context: Context)(resumeState: ArrayFromAsyncIterableResumeState,
promise: JSPromise, promiseFun: JSReceiver, iterator: JSReceiver,
next: JSAny, arr: JSReceiver, error: JSAny, mapfn: JSAny, thisArg: JSAny,
nativeContext: NativeContext): ArrayFromAsyncIterableResolveContext {
const resolveContext = %RawDownCast<ArrayFromAsyncIterableResolveContext>(
AllocateSyntheticFunctionContext(
nativeContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveLength));
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateStepSlot,
SmiTag<ArrayFromAsyncLabels>(resumeState.step));
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateAwaitedValueSlot,
resumeState.awaitedValue);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateIndexSlot,
resumeState.index);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolvePromiseSlot,
promise);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolvePromiseFunctionSlot,
promiseFun);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveOnFulfilledFunctionSlot,
AllocateRootFunctionWithContext(
kArrayFromAsyncIterableOnFulfilledSharedFun, resolveContext,
nativeContext));
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveOnRejectedFunctionSlot,
AllocateRootFunctionWithContext(
kArrayFromAsyncIterableOnRejectedSharedFun, resolveContext,
nativeContext));
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResultArraySlot,
arr);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveIteratorSlot,
iterator);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveNextMethodSlot,
next);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveErrorSlot,
error);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveMapfnSlot,
mapfn);
InitContextSlot(
resolveContext,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveThisArgSlot,
thisArg);
return resolveContext;
}
macro GetIteratorRecordFromArrayFromAsyncIterableResolveContext(
context: ArrayFromAsyncIterableResolveContext): iterator::IteratorRecord {
const iterator = *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveIteratorSlot);
const nextMethod = *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveNextMethodSlot);
return iterator::IteratorRecord{object: iterator, next: nextMethod};
}
transitioning macro CreateArrayFromIterableAsynchronously(
context: ArrayFromAsyncIterableResolveContext): JSAny {
try {
const fastIteratorResultMap = GetIteratorResultMap();
const mapfn = *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveMapfnSlot);
const thisArg = *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveThisArgSlot);
const arr = *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResultArraySlot);
let resumeState = ArrayFromAsyncIterableResumeState{
step: SmiUntag<ArrayFromAsyncLabels>(
%RawDownCast<SmiTagged<ArrayFromAsyncLabels>>(*ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateStepSlot))),
awaitedValue: *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateAwaitedValueSlot),
index: *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateIndexSlot)
};
let mappedValue: JSAny = Undefined;
let nextValue: JSAny = Undefined;
// TODO(v8:14290): Replace `if/else` with `switch/case` when the support
// for `switch` is added.
while (true) {
if (resumeState.step == ArrayFromAsyncLabels::kGetIteratorStep) {
const iteratorRecord =
GetIteratorRecordFromArrayFromAsyncIterableResolveContext(context);
let next: JSAny;
// https://github.com/tc39/proposal-array-from-async/issues/33#issuecomment-1279296963
// 3. Let nextResult be ? Call(iteratorRecord.[[NextMethod]],
// iteratorRecord.[[Iterator]]).
// 4. Set nextResult to ? Await(nextResult).
next = Call(context, iteratorRecord.next, iteratorRecord.object);
return ArrayFromAsyncIterableAwaitPoint(
context, ArrayFromAsyncLabels::kCheckIteratorValueAndMapping, next);
} else if (
resumeState.step ==
ArrayFromAsyncLabels::kCheckIteratorValueAndMapping) {
// 5. If nextResult is not an Object, throw a TypeError exception.
const nextJSReceiver = Cast<JSReceiver>(resumeState.awaitedValue)
otherwise ThrowTypeError(
MessageTemplate::kIteratorResultNotAnObject, 'Array.fromAsync');
try {
// 6. Let done be ? IteratorComplete(nextResult).
iterator::IteratorComplete(nextJSReceiver, fastIteratorResultMap)
otherwise Done;
// 8. Let nextValue be ? IteratorValue(nextResult).
nextValue =
iterator::IteratorValue(nextJSReceiver, fastIteratorResultMap);
// When mapfn is not undefined, it is guaranteed to be callable as
// checked upon entry.
const mapping: bool = (mapfn != Undefined);
// 9. If mapping is true, then
if (mapping) {
resumeState.step = ArrayFromAsyncLabels::kIteratorMapping;
} else {
// 10. Else, let mappedValue be nextValue.
mappedValue = nextValue;
resumeState.step =
ArrayFromAsyncLabels::kAddIteratorValueToTheArray;
}
} label Done {
// 7. If done is true,
// a. Perform ? Set(A, "length", 𝔽(k), true).
// b. Return Completion Record { [[Type]]: return, [[Value]]: A,
// [[Target]]: empty }.
resumeState.step = ArrayFromAsyncLabels::kDoneAndResolvePromise;
}
} else if (resumeState.step == ArrayFromAsyncLabels::kIteratorMapping) {
// a. Let mappedValue be Call(mapfn, thisArg, « nextValue, 𝔽(k)
// »).
// b. IfAbruptCloseAsyncIterator(mappedValue,
// iteratorRecord).
const mapResult = Call(
context, UnsafeCast<Callable>(mapfn), thisArg, nextValue,
resumeState.index);
// c. Set mappedValue to Await(mappedValue).
// d. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
return ArrayFromAsyncIterableAwaitPoint(
context, ArrayFromAsyncLabels::kGetIteratorValueWithMapping,
mapResult);
} else if (
resumeState.step ==
ArrayFromAsyncLabels::kGetIteratorValueWithMapping) {
mappedValue = resumeState.awaitedValue;
resumeState.step = ArrayFromAsyncLabels::kAddIteratorValueToTheArray;
} else if (
resumeState.step ==
ArrayFromAsyncLabels::kAddIteratorValueToTheArray) {
// 11. Let defineStatus be CreateDataPropertyOrThrow(A, Pk,
// mappedValue).
// 12. If defineStatus is an abrupt completion, return ?
// AsyncIteratorClose(iteratorRecord, defineStatus).
FastCreateDataProperty(arr, resumeState.index, mappedValue);
// 13. Set k to k + 1.
resumeState.index++;
*ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateIndexSlot) =
resumeState.index;
resumeState.step = ArrayFromAsyncLabels::kGetIteratorStep;
} else if (
resumeState.step == ArrayFromAsyncLabels::kDoneAndResolvePromise) {
array::SetPropertyLength(arr, resumeState.index);
const promise = *ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolvePromiseSlot);
promise::ResolvePromise(promise, arr);
return Undefined;
} else if (
resumeState.step == ArrayFromAsyncLabels::kCloseAsyncIterator) {
resumeState.step = ArrayFromAsyncLabels::kRejectPromise;
const iteratorRecord =
GetIteratorRecordFromArrayFromAsyncIterableResolveContext(context);
try {
ArrayFromAsyncAsyncIteratorCloseOnException(iteratorRecord)
otherwise RejectPromise;
return Undefined;
} label RejectPromise {
// Do nothing so the codeflow continues to the kRejectPromise label.
}
} else if (resumeState.step == ArrayFromAsyncLabels::kRejectPromise) {
return RejectArrayFromAsyncPromise<
ArrayFromAsyncIterableResolveContext>(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveErrorSlot,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolvePromiseSlot);
}
}
} catch (e, _message) {
*ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveErrorSlot) = e;
const iteratorRecord =
GetIteratorRecordFromArrayFromAsyncIterableResolveContext(context);
try {
ArrayFromAsyncAsyncIteratorCloseOnException(iteratorRecord)
otherwise RejectPromise;
} label RejectPromise {
return RejectArrayFromAsyncPromise<ArrayFromAsyncIterableResolveContext>(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveErrorSlot,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolvePromiseSlot);
}
}
return Undefined;
}
transitioning macro ArrayFromAsyncIterableAwaitPoint(
context: ArrayFromAsyncIterableResolveContext, step: ArrayFromAsyncLabels,
value: JSAny): JSAny {
return ArrayFromAsyncAwaitPoint<ArrayFromAsyncIterableResolveContext>(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateStepSlot,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolvePromiseFunctionSlot,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveOnFulfilledFunctionSlot,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveOnRejectedFunctionSlot,
step, value);
}
// `ArrayFromAsyncIterableOnFulfilled` is the callback function for the
// fulfilled case of the promise in `then` handler.
transitioning javascript builtin ArrayFromAsyncIterableOnFulfilled(
js-implicit context: Context, receiver: JSAny, target: JSFunction)(
result: JSAny): JSAny {
const context = %RawDownCast<ArrayFromAsyncIterableResolveContext>(context);
*ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateAwaitedValueSlot) = result;
return CreateArrayFromIterableAsynchronously(context);
}
// `ArrayFromAsyncIterableOnRejected` is the callback function for the rejected
// case of the promise in `then` handler.
transitioning javascript builtin ArrayFromAsyncIterableOnRejected(
js-implicit context: Context, receiver: JSAny, target: JSFunction)(
result: JSAny): JSAny {
const context = %RawDownCast<ArrayFromAsyncIterableResolveContext>(context);
*ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveResumeStateStepSlot) =
SmiTag<ArrayFromAsyncLabels>(ArrayFromAsyncLabels::kCloseAsyncIterator);
*ContextSlot(
context,
ArrayFromAsyncIterableResolveContextSlots::
kArrayFromAsyncIterableResolveErrorSlot) = result;
return CreateArrayFromIterableAsynchronously(context);
}
// This is the specialized implementation of `IfAbruptCloseAsyncIterator` for
// Array.fromAsync
// https://tc39.es/proposal-array-from-async/#sec-ifabruptcloseasynciterator
transitioning macro ArrayFromAsyncAsyncIteratorCloseOnException(
implicit context: Context)(
iterator: iterator::IteratorRecord): void labels RejectPromise {
try {
const context = %RawDownCast<ArrayFromAsyncIterableResolveContext>(context);
// 3. Let innerResult be GetMethod(iterator, "return").
const method = GetProperty(iterator.object, kReturnString);
// 4. If innerResult.[[Type]] is normal, then
// a. Let return be innerResult.[[Value]].
// b. If return is undefined, return Completion(completion).
if (method == Undefined || method == Null) {
goto RejectPromise;
}
// c. Set innerResult to Call(return, iterator).
// If an exception occurs, the original exception remains bound
const innerResult = Call(context, method, iterator.object);
// d. If innerResult.[[Type]] is normal, set innerResult to
// Completion(Await(innerResult.[[Value]])).
const step = ArrayFromAsyncLabels::kRejectPromise;
ArrayFromAsyncIterableAwaitPoint(context, step, innerResult);
} catch (_e, _message) {
// Swallow the exception.
}
// (5. If completion.[[Type]] is throw) return Completion(completion).
}
extern macro ArrayFromAsyncIterableOnFulfilledSharedFunConstant():
SharedFunctionInfo;
extern macro ArrayFromAsyncIterableOnRejectedSharedFunConstant():
SharedFunctionInfo;
// --- Array-like path
struct ArrayFromAsyncArrayLikeResumeState {
step: ArrayFromAsyncLabels;
awaitedValue: JSAny;
len: Number;
index: Smi;
}
type ArrayFromAsyncArrayLikeResolveContext extends FunctionContext;
extern enum ArrayFromAsyncArrayLikeResolveContextSlots extends intptr
constexpr 'ArrayBuiltins::ArrayFromAsyncArrayLikeResolveContextSlots' {
kArrayFromAsyncArrayLikeResolveResumeStateStepSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, Smi>,
kArrayFromAsyncArrayLikeResolveResumeStateAwaitedValueSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSAny>,
kArrayFromAsyncArrayLikeResolveResumeStateLenSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, Number>,
kArrayFromAsyncArrayLikeResolveResumeStateIndexSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, Smi>,
kArrayFromAsyncArrayLikeResolvePromiseSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSPromise>,
kArrayFromAsyncArrayLikeResolvePromiseFunctionSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSReceiver>,
kArrayFromAsyncArrayLikeResolveOnFulfilledFunctionSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSFunction|Undefined>,
kArrayFromAsyncArrayLikeResolveOnRejectedFunctionSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSFunction|Undefined>,
kArrayFromAsyncArrayLikeResolveResultArraySlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSReceiver>,
kArrayFromAsyncArrayLikeResolveArrayLikeSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSReceiver>,
kArrayFromAsyncArrayLikeResolveErrorSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSAny>,
kArrayFromAsyncArrayLikeResolveMapfnSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSAny>,
kArrayFromAsyncArrayLikeResolveThisArgSlot:
Slot<ArrayFromAsyncArrayLikeResolveContext, JSAny>,
kArrayFromAsyncArrayLikeResolveLength
}
const kArrayFromAsyncArrayLikeOnFulfilledSharedFun: constexpr intptr
generates 'RootIndex::kArrayFromAsyncArrayLikeOnFulfilledSharedFun';
const kArrayFromAsyncArrayLikeOnRejectedSharedFun: constexpr intptr
generates 'RootIndex::kArrayFromAsyncArrayLikeOnRejectedSharedFun';
macro CreateArrayFromAsyncArrayLikeResolveContext(
implicit context: Context)(resumeState: ArrayFromAsyncArrayLikeResumeState,
promise: JSPromise, promiseFun: JSReceiver, arrayLike: JSReceiver,
arr: JSReceiver, error: JSAny, mapfn: JSAny, thisArg: JSAny,
nativeContext: NativeContext): ArrayFromAsyncArrayLikeResolveContext {
const resolveContext = %RawDownCast<ArrayFromAsyncArrayLikeResolveContext>(
AllocateSyntheticFunctionContext(
nativeContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveLength));
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateStepSlot,
SmiTag<ArrayFromAsyncLabels>(resumeState.step));
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateAwaitedValueSlot,
resumeState.awaitedValue);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateLenSlot,
resumeState.len);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateIndexSlot,
resumeState.index);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolvePromiseSlot,
promise);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolvePromiseFunctionSlot,
promiseFun);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveOnFulfilledFunctionSlot,
AllocateRootFunctionWithContext(
kArrayFromAsyncArrayLikeOnFulfilledSharedFun, resolveContext,
nativeContext));
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveOnRejectedFunctionSlot,
AllocateRootFunctionWithContext(
kArrayFromAsyncArrayLikeOnRejectedSharedFun, resolveContext,
nativeContext));
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResultArraySlot,
arr);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveArrayLikeSlot,
arrayLike);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveErrorSlot,
error);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveMapfnSlot,
mapfn);
InitContextSlot(
resolveContext,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveThisArgSlot,
thisArg);
return resolveContext;
}
transitioning macro CreateArrayFromArrayLikeAsynchronously(
context: ArrayFromAsyncArrayLikeResolveContext): JSAny {
try {
const mapfn = *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveMapfnSlot);
const thisArg = *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveThisArgSlot);
const arr = *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResultArraySlot);
let resumeState = ArrayFromAsyncArrayLikeResumeState{
step: SmiUntag<ArrayFromAsyncLabels>(
%RawDownCast<SmiTagged<ArrayFromAsyncLabels>>(*ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateStepSlot))),
awaitedValue: *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateAwaitedValueSlot),
len: *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateLenSlot),
index: *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateIndexSlot)
};
let mappedValue: JSAny = Undefined;
// TODO(v8:14290): Replace `if/else` with `switch/case` when the support
// for `switch` is added.
while (true) {
if (resumeState.step == ArrayFromAsyncLabels::kGetArrayLikeValue) {
const arrayLike = *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveArrayLikeSlot);
// vii. Repeat, while k < len,
// 1. Let Pk be ! ToString(𝔽(k)).
if (resumeState.index < resumeState.len) {
// 2. Let kValue be ? Get(arrayLike, Pk).
const kValue = GetProperty(arrayLike, resumeState.index);
// 3. Set kValue to ? Await(kValue).
return ArrayFromAsyncArrayLikeAwaitPoint(
context, ArrayFromAsyncLabels::kCheckArrayLikeValueAndMapping,
kValue);
}
// viii. Perform ? Set(A, "length", 𝔽(len), true).
// ix. Return Completion Record { [[Type]]: return, [[Value]]: A,
// [[Target]]: empty }.
resumeState.step = ArrayFromAsyncLabels::kDoneAndResolvePromise;
} else if (
resumeState.step ==
ArrayFromAsyncLabels::kCheckArrayLikeValueAndMapping) {
// When mapfn is not undefined, it is guaranteed to be callable as
// checked upon entry.
const mapping: bool = (mapfn != Undefined);
// 4. If mapping is true, then
if (mapping) {
resumeState.step =
ArrayFromAsyncLabels::kGetArrayLikeValueWithMapping;
} else {
resumeState.step = ArrayFromAsyncLabels::kAddArrayLikeValueToTheArray;
}
} else if (
resumeState.step ==
ArrayFromAsyncLabels::kGetArrayLikeValueWithMapping) {
// a. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k)
// »).
// b. Set mappedValue to ? Await(mappedValue).
const mapResult = Call(
context, UnsafeCast<Callable>(mapfn), thisArg,
resumeState.awaitedValue, resumeState.index);
return ArrayFromAsyncArrayLikeAwaitPoint(
context, ArrayFromAsyncLabels::kAddArrayLikeValueToTheArray,
mapResult);
} else if (
resumeState.step ==
ArrayFromAsyncLabels::kAddArrayLikeValueToTheArray) {
// 5. Else, let mappedValue be kValue.
mappedValue = resumeState.awaitedValue;
// 6. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
FastCreateDataProperty(arr, resumeState.index, mappedValue);
resumeState.index++;
*ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateIndexSlot) =
resumeState.index;
resumeState.step = ArrayFromAsyncLabels::kGetArrayLikeValue;
} else if (
resumeState.step == ArrayFromAsyncLabels::kDoneAndResolvePromise) {
array::SetPropertyLength(arr, resumeState.index);
const promise = *ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolvePromiseSlot);
promise::ResolvePromise(promise, arr);
return Undefined;
} else if (resumeState.step == ArrayFromAsyncLabels::kRejectPromise) {
return RejectArrayFromAsyncPromise<
ArrayFromAsyncArrayLikeResolveContext>(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveErrorSlot,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolvePromiseSlot);
}
}
} catch (e, _message) {
*ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveErrorSlot) = e;
return RejectArrayFromAsyncPromise<ArrayFromAsyncArrayLikeResolveContext>(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveErrorSlot,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolvePromiseSlot);
}
return Undefined;
}
transitioning macro ArrayFromAsyncArrayLikeAwaitPoint(
context: ArrayFromAsyncArrayLikeResolveContext, step: ArrayFromAsyncLabels,
value: JSAny): JSAny {
return ArrayFromAsyncAwaitPoint<ArrayFromAsyncArrayLikeResolveContext>(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateStepSlot,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolvePromiseFunctionSlot,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveOnFulfilledFunctionSlot,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveOnRejectedFunctionSlot,
step, value);
}
// `ArrayFromAsyncArrayLikeOnFulfilled` is the callback function for the
// fulfilled case of the promise in `then` handler.
transitioning javascript builtin ArrayFromAsyncArrayLikeOnFulfilled(
js-implicit context: Context, receiver: JSAny, target: JSFunction)(
result: JSAny): JSAny {
const context = %RawDownCast<ArrayFromAsyncArrayLikeResolveContext>(context);
*ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateAwaitedValueSlot) = result;
return CreateArrayFromArrayLikeAsynchronously(context);
}
// `ArrayFromAsyncArrayLikeOnRejected` is the callback function for the rejected
// case of the promise in `then` handler.
transitioning javascript builtin ArrayFromAsyncArrayLikeOnRejected(
js-implicit context: Context, receiver: JSAny, target: JSFunction)(
result: JSAny): JSAny {
const context = %RawDownCast<ArrayFromAsyncArrayLikeResolveContext>(context);
*ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveResumeStateStepSlot) =
SmiTag<ArrayFromAsyncLabels>(ArrayFromAsyncLabels::kRejectPromise);
*ContextSlot(
context,
ArrayFromAsyncArrayLikeResolveContextSlots::
kArrayFromAsyncArrayLikeResolveErrorSlot) = result;
return CreateArrayFromArrayLikeAsynchronously(context);
}
extern macro ArrayFromAsyncArrayLikeOnFulfilledSharedFunConstant():
SharedFunctionInfo;
extern macro ArrayFromAsyncArrayLikeOnRejectedSharedFunConstant():
SharedFunctionInfo;
// --- Array.fromAsync builtin
// https://tc39.es/proposal-array-from-async/#sec-array.fromAsync
// Array.fromAsync ( asyncItems [ , mapfn [ , thisArg ] ] )
// Since we do not have support for `await` in torque, we handled
// asynchronous execution flow manually in torque. More information
// is available in go/array-from-async-implementation.
@incrementUseCounter('v8::Isolate::kArrayFromAsync')
transitioning javascript builtin ArrayFromAsync(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let C be the this value.
const c = HasBuiltinSubclassingFlag() ? receiver : GetArrayFunction();
const items = arguments[0];
const mapfn = arguments[1];
const thisArg = arguments[2];
// 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
const promise = promise::NewJSPromise();
const promiseFun = *NativeContextSlot(
context, ContextSlot::PROMISE_FUNCTION_INDEX);
// 3. Let fromAsyncClosure be a new Abstract Closure with no parameters that
// captures C, mapfn, and thisArg and performs the following steps when
// called:
let usingAsyncIterator: JSAny = Undefined;
let usingSyncIterator: JSAny = Undefined;
try {
if (mapfn != Undefined) {
// i. If IsCallable(mapfn) is false, throw a TypeError exception.
if (!Is<Callable>(mapfn)) deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfn);
}
}
try {
// c. Let usingAsyncIterator be ?
// GetMethod(asyncItems, @@asyncIterator).
usingAsyncIterator = GetMethod(items, AsyncIteratorSymbolConstant())
otherwise AsyncIteratorIsUndefined, AsyncIteratorNotCallable;
} label AsyncIteratorIsUndefined {
// d. If usingAsyncIterator is undefined, then
// i. Let usingSyncIterator be ?
// GetMethod(asyncItems, @@iterator).
usingSyncIterator = GetMethod(items, IteratorSymbolConstant())
otherwise SyncIteratorIsUndefined, SyncIteratorNotCallable;
} label SyncIteratorIsUndefined deferred {
// i. Else, (iteratorRecord is undefined)
// i. NOTE: asyncItems is neither an AsyncIterable nor an
// Iterable so assume it is an array-like object.
// ii. Let arrayLike be ! ToObject(asyncItems).
const arrayLike = ToObject_Inline(context, items);
// iii. Let len be ? LengthOfArrayLike(arrayLike).
const len = GetLengthProperty(arrayLike);
// TODO(v8:13321): Allocate an array with PACKED elements kind for
// fast-path rather than calling the constructor which creates an
// array with HOLEY kind.
let arr: JSReceiver;
typeswitch (c) {
case (c: Constructor): {
// iv. If IsConstructor(C) is
// true, then
// 1. Let A be ? Construct(C, « 𝔽(len) »).
arr = Construct(c, len);
}
case (JSAny): {
// v. Else,
// 1. Let A be ? ArrayCreate(len).
arr = ArrayCreate(len);
}
}
// vi. Let k be 0.
// Will be done when creating resumeState later.
let resumeState = ArrayFromAsyncArrayLikeResumeState{
step: ArrayFromAsyncLabels::kGetArrayLikeValue,
awaitedValue: Undefined,
len: len,
index: 0
};
const arrayLikeResolveContext =
CreateArrayFromAsyncArrayLikeResolveContext(
resumeState, promise, promiseFun, arrayLike, arr, Undefined,
mapfn, thisArg, context);
CreateArrayFromArrayLikeAsynchronously(arrayLikeResolveContext);
return promise;
} label SyncIteratorNotCallable(_value: JSAny)
deferred {
ThrowTypeError(
MessageTemplate::kFirstArgumentIteratorSymbolNonCallable,
'Array.fromAsync');
} label AsyncIteratorNotCallable(_value: JSAny)
deferred {
ThrowTypeError(
MessageTemplate::kFirstArgumentAsyncIteratorSymbolNonCallable,
'Array.fromAsync');
}
// e. Let iteratorRecord be undefined.
// f. If usingAsyncIterator is not undefined, then
// i. Set iteratorRecord to ? GetIterator(asyncItems, async,
// usingAsyncIterator).
// g. Else if usingSyncIterator is not undefined, then
// i. Set iteratorRecord to ?
// CreateAsyncFromSyncIterator(GetIterator(asyncItems, sync,
// usingSyncIterator)).
const iteratorRecord = (usingAsyncIterator != Undefined) ?
iterator::GetIterator(items, usingAsyncIterator) :
iterator::GetIteratorRecordAfterCreateAsyncFromSyncIterator(
context, iterator::GetIterator(items, usingSyncIterator));
let arr: JSReceiver;
// h. If iteratorRecord is not undefined, then
typeswitch (c) {
case (c: Constructor): {
// i. If IsConstructor(C) is true, then
// 1. Let A be ? Construct(C).
arr = Construct(c);
}
case (JSAny): {
// ii. Else,
// 1. Let A be ! ArrayCreate(0).
arr = ArrayCreate(0);
}
}
let iterableResumeState = ArrayFromAsyncIterableResumeState{
step: ArrayFromAsyncLabels::kGetIteratorStep,
awaitedValue: Undefined,
index: 0
};
const iterableResolveContext = CreateArrayFromAsyncIterableResolveContext(
iterableResumeState, promise, promiseFun, iteratorRecord.object,
iteratorRecord.next, arr, Undefined, mapfn, thisArg, context);
CreateArrayFromIterableAsynchronously(iterableResolveContext);
return promise;
} catch (e, _message) {
promise::RejectPromise(promise, e, False);
return promise;
}
}
}