/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/builtins/array-from-async.tq
955 строк
37 KB
Michaël Zasso
deps: update V8 to 13.6.233.8
02 май 2025, 16:06
Не верифицирован
02 май 2025, 16:06
918fe04
Код
Авторство
О чём код?
// 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; } } }