• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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