promise-resolve.tq 7.15 KB
Newer Older
1 2 3 4 5 6
// 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-gen.h'

7
namespace runtime {
8 9
extern transitioning runtime
ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny;
10 11
}

12
namespace promise {
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
extern macro ConstructorStringConstant(): String;
const kConstructorString: String = ConstructorStringConstant();

// https://tc39.es/ecma262/#sec-promise.resolve
transitioning javascript builtin
PromiseResolveTrampoline(
    js-implicit context: NativeContext, receiver: JSAny)(value: 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, 'PromiseResolve');

  // 3. Return ? PromiseResolve(C, x).
  return PromiseResolve(receiver, value);
}
28

29 30 31 32
transitioning builtin
PromiseResolve(implicit context: Context)(
    constructor: JSReceiver, value: JSAny): JSAny {
  const nativeContext = LoadNativeContext(context);
33 34
  const promiseFun = *NativeContextSlot(
      nativeContext, ContextSlot::PROMISE_FUNCTION_INDEX);
35 36 37 38 39 40 41 42 43
  try {
    // Check if {value} is a JSPromise.
    const value = Cast<JSPromise>(value) otherwise NeedToAllocate;

    // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
    // is the (initial) Promise.prototype and the @@species protector is
    // intact, as that guards the lookup path for "constructor" on
    // JSPromise instances which have the (initial) Promise.prototype.
    const promisePrototype =
44 45
        *NativeContextSlot(
        nativeContext, ContextSlot::PROMISE_PROTOTYPE_INDEX);
46 47
    // Check that Torque load elimination works.
    static_assert(nativeContext == LoadNativeContext(context));
48 49
    if (value.map.prototype != promisePrototype) {
      goto SlowConstructor;
50
    }
51

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
    if (IsPromiseSpeciesProtectorCellInvalid()) goto SlowConstructor;

    // If the {constructor} is the Promise function, we just immediately
    // return the {value} here and don't bother wrapping it into a
    // native Promise.
    if (promiseFun != constructor) goto SlowConstructor;
    return value;
  } label SlowConstructor deferred {
    // At this point, value or/and constructor are not native promises, but
    // they could be of the same subclass.
    const valueConstructor = GetProperty(value, kConstructorString);
    if (valueConstructor != constructor) goto NeedToAllocate;
    return value;
  } label NeedToAllocate {
    if (promiseFun == constructor) {
      // This adds a fast path for native promises that don't need to
      // create NewPromiseCapability.
      const result = NewJSPromise();
      ResolvePromise(context, result, value);
      return result;
    } else
73
      deferred {
74 75 76 77
        const capability = NewPromiseCapability(constructor, True);
        const resolve = UnsafeCast<Callable>(capability.resolve);
        Call(context, resolve, Undefined, value);
        return capability.promise;
78
      }
79 80
  }
}
81

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
extern macro IsJSReceiverMap(Map): bool;

extern macro IsPromiseThenProtectorCellInvalid(): bool;

extern macro ThenStringConstant(): String;

const kThenString: String = ThenStringConstant();

// https://tc39.es/ecma262/#sec-promise-resolve-functions
transitioning builtin
ResolvePromise(implicit context: Context)(
    promise: JSPromise, resolution: JSAny): JSAny {
  // 7. If SameValue(resolution, promise) is true, then
  // 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.
  // We also let the runtime handle it if promise == resolution.
  // We can use pointer comparison here, since the {promise} is guaranteed
  // to be a JSPromise inside this function and thus is reference comparable.
102
  if (IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
103 104 105 106
      TaggedEqual(promise, resolution))
    deferred {
      return runtime::ResolvePromise(promise, resolution);
    }
107

108 109 110 111 112 113 114
  let then: Object = Undefined;
  try {
    // 8. If Type(resolution) is not Object, then
    // 8.a Return FulfillPromise(promise, resolution).
    if (TaggedIsSmi(resolution)) {
      return FulfillPromise(promise, resolution);
    }
115

116 117 118 119 120
    const heapResolution = UnsafeCast<HeapObject>(resolution);
    const resolutionMap = heapResolution.map;
    if (!IsJSReceiverMap(resolutionMap)) {
      return FulfillPromise(promise, resolution);
    }
121

122 123 124 125 126 127 128
    // We can skip the "then" lookup on {resolution} if its [[Prototype]]
    // is the (initial) Promise.prototype and the Promise#then protector
    // is intact, as that guards the lookup path for the "then" property
    // on JSPromise instances which have the (initial) %PromisePrototype%.
    if (IsForceSlowPath()) {
      goto Slow;
    }
129

130
    if (IsPromiseThenProtectorCellInvalid()) {
131
      goto Slow;
132
    }
133

134 135 136 137 138 139 140
    const nativeContext = LoadNativeContext(context);
    if (!IsJSPromiseMap(resolutionMap)) {
      // We can skip the lookup of "then" if the {resolution} is a (newly
      // created) IterResultObject, as the Promise#then() protector also
      // ensures that the intrinsic %ObjectPrototype% doesn't contain any
      // "then" property. This helps to avoid negative lookups on iterator
      // results from async generators.
141 142
      dcheck(IsJSReceiverMap(resolutionMap));
      dcheck(!IsPromiseThenProtectorCellInvalid());
143
      if (resolutionMap ==
144 145
          *NativeContextSlot(
              nativeContext, ContextSlot::ITERATOR_RESULT_MAP_INDEX)) {
146
        return FulfillPromise(promise, resolution);
147 148
      } else {
        goto Slow;
149
      }
150 151 152
    }

    const promisePrototype =
153 154
        *NativeContextSlot(
        nativeContext, ContextSlot::PROMISE_PROTOTYPE_INDEX);
155 156
    if (resolutionMap.prototype == promisePrototype) {
      // The {resolution} is a native Promise in this case.
157
      then = *NativeContextSlot(nativeContext, ContextSlot::PROMISE_THEN_INDEX);
158 159
      // Check that Torque load elimination works.
      static_assert(nativeContext == LoadNativeContext(context));
160 161
      goto Enqueue;
    }
162 163 164 165 166 167
    goto Slow;
  } label Slow deferred {
    // 9. Let then be Get(resolution, "then").
    // 10. If then is an abrupt completion, then
    try {
      then = GetProperty(resolution, kThenString);
168
    } catch (e, _message) {
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
      // a. Return RejectPromise(promise, then.[[Value]]).
      return RejectPromise(promise, e, False);
    }

    // 11. Let thenAction be then.[[Value]].
    // 12. If IsCallable(thenAction) is false, then
    if (!Is<Callable>(then)) {
      // a. Return FulfillPromise(promise, resolution).
      return FulfillPromise(promise, resolution);
    }
    goto Enqueue;
  } label Enqueue {
    // 13. Let job be NewPromiseResolveThenableJob(promise, resolution,
    //                                             thenAction).
    const task = NewPromiseResolveThenableJobTask(
        promise, UnsafeCast<JSReceiver>(resolution),
        UnsafeCast<Callable>(then));

    // 14. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
    // 15. Return undefined.
    return EnqueueMicrotask(task.context, task);
190
  }
191
}
192
}