• 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-gen.h'
6
7namespace runtime {
8extern transitioning runtime
9ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny;
10}
11
12namespace promise {
13extern macro ConstructorStringConstant(): String;
14const kConstructorString: String = ConstructorStringConstant();
15
16// https://tc39.es/ecma262/#sec-promise.resolve
17transitioning javascript builtin
18PromiseResolveTrampoline(
19    js-implicit context: NativeContext, receiver: JSAny)(value: JSAny): JSAny {
20  // 1. Let C be the this value.
21  // 2. If Type(C) is not Object, throw a TypeError exception.
22  const receiver = Cast<JSReceiver>(receiver) otherwise
23  ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseResolve');
24
25  // 3. Return ? PromiseResolve(C, x).
26  return PromiseResolve(receiver, value);
27}
28
29transitioning builtin
30PromiseResolve(implicit context: Context)(
31    constructor: JSReceiver, value: JSAny): JSAny {
32  const nativeContext = LoadNativeContext(context);
33  const promiseFun = *NativeContextSlot(
34      nativeContext, ContextSlot::PROMISE_FUNCTION_INDEX);
35  try {
36    // Check if {value} is a JSPromise.
37    const value = Cast<JSPromise>(value) otherwise NeedToAllocate;
38
39    // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
40    // is the (initial) Promise.prototype and the @@species protector is
41    // intact, as that guards the lookup path for "constructor" on
42    // JSPromise instances which have the (initial) Promise.prototype.
43    const promisePrototype =
44        *NativeContextSlot(
45        nativeContext, ContextSlot::PROMISE_PROTOTYPE_INDEX);
46    // Check that Torque load elimination works.
47    static_assert(nativeContext == LoadNativeContext(context));
48    if (value.map.prototype != promisePrototype) {
49      goto SlowConstructor;
50    }
51
52    if (IsPromiseSpeciesProtectorCellInvalid()) goto SlowConstructor;
53
54    // If the {constructor} is the Promise function, we just immediately
55    // return the {value} here and don't bother wrapping it into a
56    // native Promise.
57    if (promiseFun != constructor) goto SlowConstructor;
58    return value;
59  } label SlowConstructor deferred {
60    // At this point, value or/and constructor are not native promises, but
61    // they could be of the same subclass.
62    const valueConstructor = GetProperty(value, kConstructorString);
63    if (valueConstructor != constructor) goto NeedToAllocate;
64    return value;
65  } label NeedToAllocate {
66    if (promiseFun == constructor) {
67      // This adds a fast path for native promises that don't need to
68      // create NewPromiseCapability.
69      const result = NewJSPromise();
70      ResolvePromise(context, result, value);
71      return result;
72    } else
73      deferred {
74        const capability = NewPromiseCapability(constructor, True);
75        const resolve = UnsafeCast<Callable>(capability.resolve);
76        Call(context, resolve, Undefined, value);
77        return capability.promise;
78      }
79  }
80}
81
82extern macro IsJSReceiverMap(Map): bool;
83
84extern macro IsPromiseThenProtectorCellInvalid(): bool;
85
86extern macro ThenStringConstant(): String;
87
88const kThenString: String = ThenStringConstant();
89
90// https://tc39.es/ecma262/#sec-promise-resolve-functions
91transitioning builtin
92ResolvePromise(implicit context: Context)(
93    promise: JSPromise, resolution: JSAny): JSAny {
94  // 7. If SameValue(resolution, promise) is true, then
95  // If promise hook is enabled or the debugger is active, let
96  // the runtime handle this operation, which greatly reduces
97  // the complexity here and also avoids a couple of back and
98  // forth between JavaScript and C++ land.
99  // We also let the runtime handle it if promise == resolution.
100  // We can use pointer comparison here, since the {promise} is guaranteed
101  // to be a JSPromise inside this function and thus is reference comparable.
102  if (IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
103      TaggedEqual(promise, resolution))
104    deferred {
105      return runtime::ResolvePromise(promise, resolution);
106    }
107
108  let then: Object = Undefined;
109  try {
110    // 8. If Type(resolution) is not Object, then
111    // 8.a Return FulfillPromise(promise, resolution).
112    if (TaggedIsSmi(resolution)) {
113      return FulfillPromise(promise, resolution);
114    }
115
116    const heapResolution = UnsafeCast<HeapObject>(resolution);
117    const resolutionMap = heapResolution.map;
118    if (!IsJSReceiverMap(resolutionMap)) {
119      return FulfillPromise(promise, resolution);
120    }
121
122    // We can skip the "then" lookup on {resolution} if its [[Prototype]]
123    // is the (initial) Promise.prototype and the Promise#then protector
124    // is intact, as that guards the lookup path for the "then" property
125    // on JSPromise instances which have the (initial) %PromisePrototype%.
126    if (IsForceSlowPath()) {
127      goto Slow;
128    }
129
130    if (IsPromiseThenProtectorCellInvalid()) {
131      goto Slow;
132    }
133
134    const nativeContext = LoadNativeContext(context);
135    if (!IsJSPromiseMap(resolutionMap)) {
136      // We can skip the lookup of "then" if the {resolution} is a (newly
137      // created) IterResultObject, as the Promise#then() protector also
138      // ensures that the intrinsic %ObjectPrototype% doesn't contain any
139      // "then" property. This helps to avoid negative lookups on iterator
140      // results from async generators.
141      dcheck(IsJSReceiverMap(resolutionMap));
142      dcheck(!IsPromiseThenProtectorCellInvalid());
143      if (resolutionMap ==
144          *NativeContextSlot(
145              nativeContext, ContextSlot::ITERATOR_RESULT_MAP_INDEX)) {
146        return FulfillPromise(promise, resolution);
147      } else {
148        goto Slow;
149      }
150    }
151
152    const promisePrototype =
153        *NativeContextSlot(
154        nativeContext, ContextSlot::PROMISE_PROTOTYPE_INDEX);
155    if (resolutionMap.prototype == promisePrototype) {
156      // The {resolution} is a native Promise in this case.
157      then = *NativeContextSlot(nativeContext, ContextSlot::PROMISE_THEN_INDEX);
158      // Check that Torque load elimination works.
159      static_assert(nativeContext == LoadNativeContext(context));
160      goto Enqueue;
161    }
162    goto Slow;
163  } label Slow deferred {
164    // 9. Let then be Get(resolution, "then").
165    // 10. If then is an abrupt completion, then
166    try {
167      then = GetProperty(resolution, kThenString);
168    } catch (e, _message) {
169      // a. Return RejectPromise(promise, then.[[Value]]).
170      return RejectPromise(promise, e, False);
171    }
172
173    // 11. Let thenAction be then.[[Value]].
174    // 12. If IsCallable(thenAction) is false, then
175    if (!Is<Callable>(then)) {
176      // a. Return FulfillPromise(promise, resolution).
177      return FulfillPromise(promise, resolution);
178    }
179    goto Enqueue;
180  } label Enqueue {
181    // 13. Let job be NewPromiseResolveThenableJob(promise, resolution,
182    //                                             thenAction).
183    const task = NewPromiseResolveThenableJobTask(
184        promise, UnsafeCast<JSReceiver>(resolution),
185        UnsafeCast<Callable>(then));
186
187    // 14. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
188    // 15. Return undefined.
189    return EnqueueMicrotask(task.context, task);
190  }
191}
192}
193