1// Copyright 2019 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include 'src/builtins/builtins-promise.h' 6 7// https://tc39.es/ecma262/#sec-promise-jobs 8namespace promise { 9extern macro IsJSPromiseMap(Map): bool; 10extern macro NeedsAnyPromiseHooks(): bool; 11 12// https://tc39.es/ecma262/#sec-promiseresolvethenablejob 13transitioning builtin 14PromiseResolveThenableJob(implicit context: Context)( 15 promiseToResolve: JSPromise, thenable: JSReceiver, then: JSAny): JSAny { 16 // We can use a simple optimization here if we know that {then} is the 17 // initial Promise.prototype.then method, and {thenable} is a JSPromise 18 // whose 19 // @@species lookup chain is intact: We can connect {thenable} and 20 // {promise_to_resolve} directly in that case and avoid the allocation of a 21 // temporary JSPromise and the closures plus context. 22 // 23 // We take the generic (slow-)path if a PromiseHook is enabled or the 24 // debugger is active, to make sure we expose spec compliant behavior. 25 const nativeContext = LoadNativeContext(context); 26 const promiseThen = *NativeContextSlot(ContextSlot::PROMISE_THEN_INDEX); 27 const thenableMap = thenable.map; 28 if (TaggedEqual(then, promiseThen) && IsJSPromiseMap(thenableMap) && 29 !NeedsAnyPromiseHooks() && 30 IsPromiseSpeciesLookupChainIntact(nativeContext, thenableMap)) { 31 // We know that the {thenable} is a JSPromise, which doesn't require 32 // any special treatment and that {then} corresponds to the initial 33 // Promise.prototype.then method. So instead of allocating a temporary 34 // JSPromise to connect the {thenable} with the {promise_to_resolve}, 35 // we can directly schedule the {promise_to_resolve} with default 36 // handlers onto the {thenable} promise. This does not only save the 37 // JSPromise allocation, but also avoids the allocation of the two 38 // resolving closures and the shared context. 39 // 40 // What happens normally in this case is 41 // 42 // resolve, reject = CreateResolvingFunctions(promise_to_resolve) 43 // result_capability = NewPromiseCapability(%Promise%) 44 // PerformPromiseThen(thenable, resolve, reject, result_capability) 45 // 46 // which means that PerformPromiseThen will either schedule a new 47 // PromiseReaction with resolve and reject or a PromiseReactionJob 48 // with resolve or reject based on the state of {thenable}. And 49 // resolve or reject will just invoke the default [[Resolve]] or 50 // [[Reject]] functions on the {promise_to_resolve}. 51 // 52 // This is the same as just doing 53 // 54 // PerformPromiseThen(thenable, undefined, undefined, 55 // promise_to_resolve) 56 // 57 // which performs exactly the same (observable) steps. 58 return PerformPromiseThen( 59 UnsafeCast<JSPromise>(thenable), UndefinedConstant(), 60 UndefinedConstant(), promiseToResolve); 61 } else { 62 const funcs = 63 CreatePromiseResolvingFunctions(promiseToResolve, False, nativeContext); 64 const resolve = funcs.resolve; 65 const reject = funcs.reject; 66 try { 67 return Call( 68 context, UnsafeCast<Callable>(then), thenable, resolve, reject); 69 } catch (e, _message) { 70 return Call(context, UnsafeCast<Callable>(reject), Undefined, e); 71 } 72 } 73} 74} 75