| // 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. |
| |
| #include 'src/builtins/builtins-promise.h' |
| #include 'src/builtins/builtins-promise-gen.h' |
| |
| namespace runtime { |
| extern transitioning runtime |
| RejectPromise(implicit context: Context)(JSPromise, JSAny, Boolean): JSAny; |
| |
| extern transitioning runtime |
| PromiseRevokeReject(implicit context: Context)(JSPromise): JSAny; |
| |
| extern transitioning runtime |
| PromiseRejectAfterResolved(implicit context: Context)(JSPromise, JSAny): JSAny; |
| |
| extern transitioning runtime |
| PromiseResolveAfterResolved(implicit context: Context)(JSPromise, JSAny): JSAny; |
| |
| extern transitioning runtime |
| PromiseRejectEventFromStack(implicit context: Context)(JSPromise, JSAny): JSAny; |
| } |
| |
| // https://tc39.es/ecma262/#sec-promise-abstract-operations |
| namespace promise { |
| |
| extern macro PromiseForwardingHandlerSymbolConstant(): Symbol; |
| const kPromiseForwardingHandlerSymbol: Symbol = |
| PromiseForwardingHandlerSymbolConstant(); |
| extern macro PromiseHandledBySymbolConstant(): Symbol; |
| const kPromiseHandledBySymbol: Symbol = PromiseHandledBySymbolConstant(); |
| extern macro ResolveStringConstant(): String; |
| const kResolveString: String = ResolveStringConstant(); |
| extern macro IsPromiseResolveProtectorCellInvalid(): bool; |
| |
| extern macro AllocateFunctionWithMapAndContext( |
| Map, SharedFunctionInfo, FunctionContext): JSFunction; |
| |
| extern macro PromiseReactionMapConstant(): Map; |
| extern macro PromiseFulfillReactionJobTaskMapConstant(): Map; |
| extern macro PromiseRejectReactionJobTaskMapConstant(): Map; |
| extern transitioning builtin |
| ResolvePromise(Context, JSPromise, JSAny): JSAny; |
| |
| extern transitioning builtin |
| EnqueueMicrotask(Context, Microtask): Undefined; |
| |
| macro |
| ExtractHandlerContextInternal(implicit context: Context)( |
| handler: Callable|Undefined): Context labels NotFound { |
| let iter: JSAny = handler; |
| while (true) { |
| typeswitch (iter) { |
| case (b: JSBoundFunction): { |
| iter = b.bound_target_function; |
| } |
| case (p: JSProxy): { |
| iter = p.target; |
| } |
| case (f: JSFunction): { |
| return f.context; |
| } |
| case (JSAny): { |
| break; |
| } |
| } |
| } |
| goto NotFound; |
| } |
| |
| macro |
| ExtractHandlerContext(implicit context: Context)(handler: Callable| |
| Undefined): Context { |
| try { |
| return ExtractHandlerContextInternal(handler) otherwise NotFound; |
| } label NotFound deferred { |
| return context; |
| } |
| } |
| |
| macro |
| ExtractHandlerContext(implicit context: Context)( |
| primary: Callable|Undefined, secondary: Callable|Undefined): Context { |
| try { |
| return ExtractHandlerContextInternal(primary) otherwise NotFound; |
| } label NotFound deferred { |
| return ExtractHandlerContextInternal(secondary) otherwise Default; |
| } label Default deferred { |
| return context; |
| } |
| } |
| |
| transitioning macro MorphAndEnqueuePromiseReaction(implicit context: Context)( |
| promiseReaction: PromiseReaction, argument: JSAny, |
| reactionType: constexpr PromiseReactionType): void { |
| let primaryHandler: Callable|Undefined; |
| let secondaryHandler: Callable|Undefined; |
| if constexpr (reactionType == kPromiseReactionFulfill) { |
| primaryHandler = promiseReaction.fulfill_handler; |
| secondaryHandler = promiseReaction.reject_handler; |
| } else { |
| static_assert(reactionType == kPromiseReactionReject); |
| primaryHandler = promiseReaction.reject_handler; |
| secondaryHandler = promiseReaction.fulfill_handler; |
| } |
| |
| // According to HTML, we use the context of the appropriate handler as the |
| // context of the microtask. See step 3 of HTML's EnqueueJob: |
| // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments) |
| const handlerContext: Context = |
| ExtractHandlerContext(primaryHandler, secondaryHandler); |
| |
| // Morph {current} from a PromiseReaction into a PromiseReactionJobTask |
| // and schedule that on the microtask queue. We try to minimize the number |
| // of stores here to avoid screwing up the store buffer. |
| static_assert( |
| kPromiseReactionSize == |
| kPromiseReactionJobTaskSizeOfAllPromiseReactionJobTasks); |
| if constexpr (reactionType == kPromiseReactionFulfill) { |
| *UnsafeConstCast(&promiseReaction.map) = |
| PromiseFulfillReactionJobTaskMapConstant(); |
| const promiseReactionJobTask = |
| UnsafeCast<PromiseFulfillReactionJobTask>(promiseReaction); |
| promiseReactionJobTask.argument = argument; |
| promiseReactionJobTask.context = handlerContext; |
| EnqueueMicrotask(handlerContext, promiseReactionJobTask); |
| static_assert( |
| kPromiseReactionFulfillHandlerOffset == |
| kPromiseReactionJobTaskHandlerOffset); |
| static_assert( |
| kPromiseReactionPromiseOrCapabilityOffset == |
| kPromiseReactionJobTaskPromiseOrCapabilityOffset); |
| } else { |
| static_assert(reactionType == kPromiseReactionReject); |
| *UnsafeConstCast(&promiseReaction.map) = |
| PromiseRejectReactionJobTaskMapConstant(); |
| const promiseReactionJobTask = |
| UnsafeCast<PromiseRejectReactionJobTask>(promiseReaction); |
| promiseReactionJobTask.argument = argument; |
| promiseReactionJobTask.context = handlerContext; |
| promiseReactionJobTask.handler = primaryHandler; |
| EnqueueMicrotask(handlerContext, promiseReactionJobTask); |
| static_assert( |
| kPromiseReactionPromiseOrCapabilityOffset == |
| kPromiseReactionJobTaskPromiseOrCapabilityOffset); |
| } |
| } |
| |
| // https://tc39.es/ecma262/#sec-triggerpromisereactions |
| transitioning macro TriggerPromiseReactions(implicit context: Context)( |
| reactions: Zero|PromiseReaction, argument: JSAny, |
| reactionType: constexpr PromiseReactionType): void { |
| // We need to reverse the {reactions} here, since we record them on the |
| // JSPromise in the reverse order. |
| let current = reactions; |
| let reversed: Zero|PromiseReaction = kZero; |
| |
| // As an additional safety net against misuse of the V8 Extras API, we |
| // sanity check the {reactions} to make sure that they are actually |
| // PromiseReaction instances and not actual JavaScript values (which |
| // would indicate that we're rejecting or resolving an already settled |
| // promise), see https://crbug.com/931640 for details on this. |
| while (true) { |
| typeswitch (current) { |
| case (Zero): { |
| break; |
| } |
| case (currentReaction: PromiseReaction): { |
| current = currentReaction.next; |
| currentReaction.next = reversed; |
| reversed = currentReaction; |
| } |
| } |
| } |
| // Morph the {reactions} into PromiseReactionJobTasks and push them |
| // onto the microtask queue. |
| current = reversed; |
| while (true) { |
| typeswitch (current) { |
| case (Zero): { |
| break; |
| } |
| case (currentReaction: PromiseReaction): { |
| current = currentReaction.next; |
| MorphAndEnqueuePromiseReaction(currentReaction, argument, reactionType); |
| } |
| } |
| } |
| } |
| |
| // https://tc39.es/ecma262/#sec-fulfillpromise |
| transitioning builtin |
| FulfillPromise(implicit context: Context)( |
| promise: JSPromise, value: JSAny): Undefined { |
| // Assert: The value of promise.[[PromiseState]] is "pending". |
| assert(promise.Status() == PromiseState::kPending); |
| |
| // 2. Let reactions be promise.[[PromiseFulfillReactions]]. |
| const reactions = |
| UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); |
| |
| // 3. Set promise.[[PromiseResult]] to value. |
| // 4. Set promise.[[PromiseFulfillReactions]] to undefined. |
| // 5. Set promise.[[PromiseRejectReactions]] to undefined. |
| promise.reactions_or_result = value; |
| |
| // 6. Set promise.[[PromiseState]] to "fulfilled". |
| promise.SetStatus(PromiseState::kFulfilled); |
| |
| // 7. Return TriggerPromiseReactions(reactions, value). |
| TriggerPromiseReactions(reactions, value, kPromiseReactionFulfill); |
| return Undefined; |
| } |
| |
| extern macro PromiseBuiltinsAssembler:: |
| IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(): bool; |
| |
| // https://tc39.es/ecma262/#sec-rejectpromise |
| transitioning builtin |
| RejectPromise(implicit context: Context)( |
| promise: JSPromise, reason: JSAny, debugEvent: Boolean): JSAny { |
| // If promise hook is enabled or the debugger is active, let |
| // the runtime handle this operation, which greatly reduces |
| // the complexity here and also avoids a couple of back and |
| // forth between JavaScript and C++ land. |
| if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() || |
| !promise.HasHandler()) { |
| // 7. If promise.[[PromiseIsHandled]] is false, perform |
| // HostPromiseRejectionTracker(promise, "reject"). |
| // We don't try to handle rejecting {promise} without handler |
| // here, but we let the C++ code take care of this completely. |
| return runtime::RejectPromise(promise, reason, debugEvent); |
| } |
| |
| // 2. Let reactions be promise.[[PromiseRejectReactions]]. |
| const reactions = |
| UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); |
| |
| // 3. Set promise.[[PromiseResult]] to reason. |
| // 4. Set promise.[[PromiseFulfillReactions]] to undefined. |
| // 5. Set promise.[[PromiseRejectReactions]] to undefined. |
| promise.reactions_or_result = reason; |
| |
| // 6. Set promise.[[PromiseState]] to "rejected". |
| promise.SetStatus(PromiseState::kRejected); |
| |
| // 8. Return TriggerPromiseReactions(reactions, reason). |
| TriggerPromiseReactions(reactions, reason, kPromiseReactionReject); |
| return Undefined; |
| } |
| |
| const kPromiseCapabilitySize: |
| constexpr int31 generates 'PromiseCapability::kSize'; |
| |
| type PromiseResolvingFunctionContext extends FunctionContext; |
| extern enum PromiseResolvingFunctionContextSlot extends intptr |
| constexpr 'PromiseBuiltins::PromiseResolvingFunctionContextSlot' { |
| kPromiseSlot: Slot<PromiseResolvingFunctionContext, JSPromise>, |
| kAlreadyResolvedSlot: Slot<PromiseResolvingFunctionContext, Boolean>, |
| kDebugEventSlot: Slot<PromiseResolvingFunctionContext, Boolean>, |
| kPromiseContextLength |
| } |
| |
| type PromiseCapabilitiesExecutorContext extends FunctionContext; |
| extern enum FunctionContextSlot extends intptr |
| constexpr 'PromiseBuiltins::FunctionContextSlot' { |
| kCapabilitySlot: Slot<PromiseCapabilitiesExecutorContext, PromiseCapability>, |
| kCapabilitiesContextLength |
| } |
| |
| @export |
| macro CreatePromiseCapabilitiesExecutorContext( |
| nativeContext: NativeContext, capability: PromiseCapability): |
| PromiseCapabilitiesExecutorContext { |
| const executorContext = %RawDownCast<PromiseCapabilitiesExecutorContext>( |
| AllocateSyntheticFunctionContext( |
| nativeContext, FunctionContextSlot::kCapabilitiesContextLength)); |
| |
| InitContextSlot( |
| executorContext, FunctionContextSlot::kCapabilitySlot, capability); |
| return executorContext; |
| } |
| |
| @export |
| macro CreatePromiseCapability( |
| promise: JSReceiver|Undefined, resolve: JSFunction|Undefined, |
| reject: JSFunction|Undefined): PromiseCapability { |
| return new PromiseCapability{ |
| map: kPromiseCapabilityMap, |
| promise: promise, |
| resolve: resolve, |
| reject: reject |
| }; |
| } |
| |
| @export |
| struct PromiseResolvingFunctions { |
| resolve: JSFunction; |
| reject: JSFunction; |
| } |
| |
| @export |
| macro CreatePromiseResolvingFunctions(implicit context: Context)( |
| promise: JSPromise, debugEvent: Boolean, nativeContext: NativeContext): |
| PromiseResolvingFunctions { |
| const promiseContext = CreatePromiseResolvingFunctionsContext( |
| promise, debugEvent, nativeContext); |
| const map = *NativeContextSlot( |
| nativeContext, ContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| const resolveInfo = PromiseCapabilityDefaultResolveSharedFunConstant(); |
| |
| const resolve: JSFunction = |
| AllocateFunctionWithMapAndContext(map, resolveInfo, promiseContext); |
| const rejectInfo = PromiseCapabilityDefaultRejectSharedFunConstant(); |
| const reject: JSFunction = |
| AllocateFunctionWithMapAndContext(map, rejectInfo, promiseContext); |
| return PromiseResolvingFunctions{resolve: resolve, reject: reject}; |
| } |
| |
| transitioning macro |
| InnerNewPromiseCapability(implicit context: Context)( |
| constructor: HeapObject, debugEvent: Boolean): PromiseCapability { |
| const nativeContext = LoadNativeContext(context); |
| if (constructor == |
| *NativeContextSlot(nativeContext, ContextSlot::PROMISE_FUNCTION_INDEX)) { |
| const promise = NewJSPromise(); |
| |
| const pair = |
| CreatePromiseResolvingFunctions(promise, debugEvent, nativeContext); |
| |
| return CreatePromiseCapability(promise, pair.resolve, pair.reject); |
| } else { |
| // We have to create the capability before the associated promise |
| // because the builtin PromiseConstructor uses the executor. |
| const capability = CreatePromiseCapability(Undefined, Undefined, Undefined); |
| const executorContext = |
| CreatePromiseCapabilitiesExecutorContext(nativeContext, capability); |
| |
| const executorInfo = PromiseGetCapabilitiesExecutorSharedFunConstant(); |
| const functionMap = |
| *NativeContextSlot( |
| nativeContext, |
| ContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); |
| const executor = AllocateFunctionWithMapAndContext( |
| functionMap, executorInfo, executorContext); |
| |
| const promiseConstructor = UnsafeCast<Constructor>(constructor); |
| const promise = Construct(promiseConstructor, executor); |
| capability.promise = promise; |
| |
| if (!Is<Callable>(capability.resolve) || !Is<Callable>(capability.reject)) { |
| ThrowTypeError(MessageTemplate::kPromiseNonCallable); |
| } |
| return capability; |
| } |
| } |
| |
| // https://tc39.es/ecma262/#sec-newpromisecapability |
| transitioning builtin |
| NewPromiseCapability(implicit context: Context)( |
| maybeConstructor: Object, debugEvent: Boolean): PromiseCapability { |
| typeswitch (maybeConstructor) { |
| case (Smi): { |
| ThrowTypeError(MessageTemplate::kNotConstructor, maybeConstructor); |
| } |
| case (constructor: HeapObject): { |
| if (!IsConstructor(constructor)) { |
| ThrowTypeError(MessageTemplate::kNotConstructor, maybeConstructor); |
| } |
| return InnerNewPromiseCapability(constructor, debugEvent); |
| } |
| } |
| } |
| |
| // https://tc39.es/ecma262/#sec-promise-reject-functions |
| transitioning javascript builtin |
| PromiseCapabilityDefaultReject( |
| js-implicit context: Context, receiver: JSAny)(reason: JSAny): JSAny { |
| const context = %RawDownCast<PromiseResolvingFunctionContext>(context); |
| // 2. Let promise be F.[[Promise]]. |
| const promise = |
| *ContextSlot(context, PromiseResolvingFunctionContextSlot::kPromiseSlot); |
| |
| // 3. Let alreadyResolved be F.[[AlreadyResolved]]. |
| const alreadyResolved = *ContextSlot( |
| context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot); |
| |
| // 4. If alreadyResolved.[[Value]] is true, return undefined. |
| if (alreadyResolved == True) { |
| return runtime::PromiseRejectAfterResolved(promise, reason); |
| } |
| |
| // 5. Set alreadyResolved.[[Value]] to true. |
| *ContextSlot( |
| context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot) = |
| True; |
| |
| // 6. Return RejectPromise(promise, reason). |
| const debugEvent = *ContextSlot( |
| context, PromiseResolvingFunctionContextSlot::kDebugEventSlot); |
| return RejectPromise(promise, reason, debugEvent); |
| } |
| |
| // https://tc39.es/ecma262/#sec-promise-resolve-functions |
| transitioning javascript builtin |
| PromiseCapabilityDefaultResolve( |
| js-implicit context: Context, receiver: JSAny)(resolution: JSAny): JSAny { |
| const context = %RawDownCast<PromiseResolvingFunctionContext>(context); |
| // 2. Let promise be F.[[Promise]]. |
| const promise: JSPromise = |
| *ContextSlot(context, PromiseResolvingFunctionContextSlot::kPromiseSlot); |
| |
| // 3. Let alreadyResolved be F.[[AlreadyResolved]]. |
| const alreadyResolved: Boolean = *ContextSlot( |
| context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot); |
| |
| // 4. If alreadyResolved.[[Value]] is true, return undefined. |
| if (alreadyResolved == True) { |
| return runtime::PromiseResolveAfterResolved(promise, resolution); |
| } |
| |
| // 5. Set alreadyResolved.[[Value]] to true. |
| *ContextSlot( |
| context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot) = |
| True; |
| |
| // The rest of the logic (and the catch prediction) is |
| // encapsulated in the dedicated ResolvePromise builtin. |
| return ResolvePromise(context, promise, resolution); |
| } |
| |
| @export |
| transitioning macro PerformPromiseThenImpl(implicit context: Context)( |
| promise: JSPromise, onFulfilled: Callable|Undefined, |
| onRejected: Callable|Undefined, |
| resultPromiseOrCapability: JSPromise|PromiseCapability|Undefined): void { |
| if (promise.Status() == PromiseState::kPending) { |
| // The {promise} is still in "Pending" state, so we just record a new |
| // PromiseReaction holding both the onFulfilled and onRejected callbacks. |
| // Once the {promise} is resolved we decide on the concrete handler to |
| // push onto the microtask queue. |
| const handlerContext = ExtractHandlerContext(onFulfilled, onRejected); |
| const promiseReactions = |
| UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); |
| const reaction = NewPromiseReaction( |
| handlerContext, promiseReactions, resultPromiseOrCapability, |
| onFulfilled, onRejected); |
| promise.reactions_or_result = reaction; |
| } else { |
| const reactionsOrResult = promise.reactions_or_result; |
| let microtask: PromiseReactionJobTask; |
| let handlerContext: Context; |
| if (promise.Status() == PromiseState::kFulfilled) { |
| handlerContext = ExtractHandlerContext(onFulfilled, onRejected); |
| microtask = NewPromiseFulfillReactionJobTask( |
| handlerContext, reactionsOrResult, onFulfilled, |
| resultPromiseOrCapability); |
| } else |
| deferred { |
| assert(promise.Status() == PromiseState::kRejected); |
| handlerContext = ExtractHandlerContext(onRejected, onFulfilled); |
| microtask = NewPromiseRejectReactionJobTask( |
| handlerContext, reactionsOrResult, onRejected, |
| resultPromiseOrCapability); |
| if (!promise.HasHandler()) { |
| runtime::PromiseRevokeReject(promise); |
| } |
| } |
| EnqueueMicrotask(handlerContext, microtask); |
| } |
| promise.SetHasHandler(); |
| } |
| |
| // https://tc39.es/ecma262/#sec-performpromisethen |
| transitioning builtin |
| PerformPromiseThen(implicit context: Context)( |
| promise: JSPromise, onFulfilled: Callable|Undefined, |
| onRejected: Callable|Undefined, resultPromise: JSPromise|Undefined): JSAny { |
| PerformPromiseThenImpl(promise, onFulfilled, onRejected, resultPromise); |
| return resultPromise; |
| } |
| |
| // https://tc39.es/ecma262/#sec-promise-reject-functions |
| transitioning javascript builtin |
| PromiseReject( |
| js-implicit context: NativeContext, receiver: JSAny)(reason: JSAny): JSAny { |
| // 1. Let C be the this value. |
| // 2. If Type(C) is not Object, throw a TypeError exception. |
| const receiver = Cast<JSReceiver>(receiver) otherwise |
| ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseReject'); |
| |
| const promiseFun = *NativeContextSlot(ContextSlot::PROMISE_FUNCTION_INDEX); |
| if (promiseFun == receiver) { |
| const promise = NewJSPromise(PromiseState::kRejected, reason); |
| runtime::PromiseRejectEventFromStack(promise, reason); |
| return promise; |
| } else { |
| // 3. Let promiseCapability be ? NewPromiseCapability(C). |
| const capability = NewPromiseCapability(receiver, True); |
| |
| // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). |
| const reject = UnsafeCast<Callable>(capability.reject); |
| Call(context, reject, Undefined, reason); |
| |
| // 5. Return promiseCapability.[[Promise]]. |
| return capability.promise; |
| } |
| } |
| |
| const kPromiseExecutorAlreadyInvoked: constexpr MessageTemplate |
| generates 'MessageTemplate::kPromiseExecutorAlreadyInvoked'; |
| |
| // https://tc39.es/ecma262/#sec-getcapabilitiesexecutor-functions |
| transitioning javascript builtin |
| PromiseGetCapabilitiesExecutor(js-implicit context: Context, receiver: JSAny)( |
| resolve: JSAny, reject: JSAny): JSAny { |
| const context = %RawDownCast<PromiseCapabilitiesExecutorContext>(context); |
| const capability: PromiseCapability = |
| *ContextSlot(context, FunctionContextSlot::kCapabilitySlot); |
| if (capability.resolve != Undefined || capability.reject != Undefined) |
| deferred { |
| ThrowTypeError(kPromiseExecutorAlreadyInvoked); |
| } |
| |
| capability.resolve = resolve; |
| capability.reject = reject; |
| return Undefined; |
| } |
| |
| macro IsPromiseResolveLookupChainIntact(implicit context: Context)( |
| nativeContext: NativeContext, constructor: JSReceiver): bool { |
| if (IsForceSlowPath()) return false; |
| const promiseFun = |
| *NativeContextSlot(nativeContext, ContextSlot::PROMISE_FUNCTION_INDEX); |
| return promiseFun == constructor && !IsPromiseResolveProtectorCellInvalid(); |
| } |
| |
| // https://tc39.es/ecma262/#sec-getpromiseresolve |
| transitioning macro GetPromiseResolve(implicit context: Context)( |
| nativeContext: NativeContext, constructor: Constructor): JSAny { |
| // 1. Assert: IsConstructor(constructor) is true. |
| |
| // We can skip the "resolve" lookup on {constructor} if it's the |
| // Promise constructor and the Promise.resolve protector is intact, |
| // as that guards the lookup path for the "resolve" property on the |
| // Promise constructor. In this case, promiseResolveFunction is undefined, |
| // and when CallResolve is called with it later, it will call Promise.resolve. |
| let promiseResolveFunction: JSAny = Undefined; |
| |
| if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) { |
| let promiseResolve: JSAny; |
| |
| // 2. Let promiseResolve be ? Get(constructor, "resolve"). |
| promiseResolve = GetProperty(constructor, kResolveString); |
| |
| // 3. If IsCallable(promiseResolve) is false, throw a TypeError exception. |
| promiseResolveFunction = |
| Cast<Callable>(promiseResolve) otherwise ThrowTypeError( |
| MessageTemplate::kCalledNonCallable, 'resolve'); |
| } |
| // 4. return promiseResolve. |
| return promiseResolveFunction; |
| } |
| |
| transitioning macro CallResolve(implicit context: Context)( |
| constructor: Constructor, resolve: JSAny, value: JSAny): JSAny { |
| // Undefined can never be a valid value for the resolve function, |
| // instead it is used as a special marker for the fast path. |
| if (resolve == Undefined) { |
| return PromiseResolve(constructor, value); |
| } else |
| deferred { |
| return Call(context, UnsafeCast<Callable>(resolve), constructor, value); |
| } |
| } |
| |
| transitioning javascript builtin |
| PromiseConstructorLazyDeoptContinuation( |
| js-implicit context: NativeContext, receiver: JSAny)( |
| promise: JSAny, reject: JSAny, exception: JSAny|TheHole, |
| _result: JSAny): JSAny { |
| typeswitch (exception) { |
| case (TheHole): { |
| } |
| case (e: JSAny): { |
| Call(context, reject, Undefined, e); |
| } |
| } |
| return promise; |
| } |
| |
| extern macro PromiseCapabilityDefaultRejectSharedFunConstant(): |
| SharedFunctionInfo; |
| extern macro PromiseCapabilityDefaultResolveSharedFunConstant(): |
| SharedFunctionInfo; |
| extern macro PromiseGetCapabilitiesExecutorSharedFunConstant(): |
| SharedFunctionInfo; |
| } |