summaryrefslogtreecommitdiff
path: root/deps/v8/src/js/promise.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/js/promise.js')
-rw-r--r--deps/v8/src/js/promise.js524
1 files changed, 7 insertions, 517 deletions
diff --git a/deps/v8/src/js/promise.js b/deps/v8/src/js/promise.js
index 0b37c643d1..95ab793591 100644
--- a/deps/v8/src/js/promise.js
+++ b/deps/v8/src/js/promise.js
@@ -12,193 +12,16 @@
// Imports
var InternalArray = utils.InternalArray;
-var promiseAsyncStackIDSymbol =
- utils.ImportNow("promise_async_stack_id_symbol");
var promiseHandledBySymbol =
utils.ImportNow("promise_handled_by_symbol");
var promiseForwardingHandlerSymbol =
utils.ImportNow("promise_forwarding_handler_symbol");
-var promiseHasHandlerSymbol =
- utils.ImportNow("promise_has_handler_symbol");
-var promiseRejectReactionsSymbol =
- utils.ImportNow("promise_reject_reactions_symbol");
-var promiseFulfillReactionsSymbol =
- utils.ImportNow("promise_fulfill_reactions_symbol");
-var promiseDeferredReactionSymbol =
- utils.ImportNow("promise_deferred_reaction_symbol");
-var promiseHandledHintSymbol =
- utils.ImportNow("promise_handled_hint_symbol");
-var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
-var promiseStateSymbol = utils.ImportNow("promise_state_symbol");
-var promiseResultSymbol = utils.ImportNow("promise_result_symbol");
-var SpeciesConstructor;
-var speciesSymbol = utils.ImportNow("species_symbol");
-var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
-var ObjectHasOwnProperty;
-
-utils.Import(function(from) {
- ObjectHasOwnProperty = from.ObjectHasOwnProperty;
- SpeciesConstructor = from.SpeciesConstructor;
-});
+var GlobalPromise = global.Promise;
// -------------------------------------------------------------------
-// [[PromiseState]] values:
-// These values should be kept in sync with PromiseStatus in globals.h
-const kPending = 0;
-const kFulfilled = +1;
-const kRejected = +2;
-
-const kResolveCallback = 0;
-const kRejectCallback = 1;
-
-// ES#sec-promise-executor
-// Promise ( executor )
-var GlobalPromise = function Promise(executor) {
- if (executor === promiseRawSymbol) {
- return %_NewObject(GlobalPromise, new.target);
- }
- if (IS_UNDEFINED(new.target)) throw %make_type_error(kNotAPromise, this);
- if (!IS_CALLABLE(executor)) {
- throw %make_type_error(kResolverNotAFunction, executor);
- }
-
- var promise = PromiseInit(%_NewObject(GlobalPromise, new.target));
- // Calling the reject function would be a new exception, so debugEvent = true
- // TODO(gsathya): Remove container for callbacks when this is moved
- // to CPP/TF.
- var callbacks = %create_resolving_functions(promise, true);
- var debug_is_active = DEBUG_IS_ACTIVE;
- try {
- if (debug_is_active) %DebugPushPromise(promise);
- executor(callbacks[kResolveCallback], callbacks[kRejectCallback]);
- } %catch (e) { // Natives syntax to mark this catch block.
- %_Call(callbacks[kRejectCallback], UNDEFINED, e);
- } finally {
- if (debug_is_active) %DebugPopPromise();
- }
-
- return promise;
-}
-
// Core functionality.
-function PromiseSet(promise, status, value) {
- SET_PRIVATE(promise, promiseStateSymbol, status);
- SET_PRIVATE(promise, promiseResultSymbol, value);
-
- // There are 3 possible states for the resolve, reject symbols when we add
- // a new callback --
- // 1) UNDEFINED -- This is the zero state where there is no callback
- // registered. When we see this state, we directly attach the callbacks to
- // the symbol.
- // 2) !IS_ARRAY -- There is a single callback directly attached to the
- // symbols. We need to create a new array to store additional callbacks.
- // 3) IS_ARRAY -- There are multiple callbacks already registered,
- // therefore we can just push the new callback to the existing array.
- SET_PRIVATE(promise, promiseFulfillReactionsSymbol, UNDEFINED);
- SET_PRIVATE(promise, promiseRejectReactionsSymbol, UNDEFINED);
-
- // This symbol is used only when one deferred needs to be attached. When more
- // than one deferred need to be attached the promise, we attach them directly
- // to the promiseFulfillReactionsSymbol and promiseRejectReactionsSymbol and
- // reset this back to UNDEFINED.
- SET_PRIVATE(promise, promiseDeferredReactionSymbol, UNDEFINED);
-
- return promise;
-}
-
-function PromiseCreateAndSet(status, value) {
- var promise = new GlobalPromise(promiseRawSymbol);
- // If debug is active, notify about the newly created promise first.
- if (DEBUG_IS_ACTIVE) PromiseSet(promise, kPending, UNDEFINED);
- return PromiseSet(promise, status, value);
-}
-
-function PromiseInit(promise) {
- return PromiseSet(promise, kPending, UNDEFINED);
-}
-
-function PromiseHandle(value, handler, deferred) {
- var debug_is_active = DEBUG_IS_ACTIVE;
- try {
- if (debug_is_active) %DebugPushPromise(deferred.promise);
- var result = handler(value);
- if (IS_UNDEFINED(deferred.resolve)) {
- ResolvePromise(deferred.promise, result);
- } else {
- %_Call(deferred.resolve, UNDEFINED, result);
- }
- } %catch (exception) { // Natives syntax to mark this catch block.
- try {
- if (IS_UNDEFINED(deferred.reject)) {
- // Pass false for debugEvent so .then chaining does not trigger
- // redundant ExceptionEvents.
- %PromiseReject(deferred.promise, exception, false);
- PromiseSet(deferred.promise, kRejected, exception);
- } else {
- %_Call(deferred.reject, UNDEFINED, exception);
- }
- } catch (e) { }
- } finally {
- if (debug_is_active) %DebugPopPromise();
- }
-}
-
-function PromiseDebugGetInfo(deferreds, status) {
- var id, name, instrumenting = DEBUG_IS_ACTIVE;
-
- if (instrumenting) {
- // In an async function, reuse the existing stack related to the outer
- // Promise. Otherwise, e.g. in a direct call to then, save a new stack.
- // Promises with multiple reactions with one or more of them being async
- // functions will not get a good stack trace, as async functions require
- // different stacks from direct Promise use, but we save and restore a
- // stack once for all reactions. TODO(littledan): Improve this case.
- if (!IS_UNDEFINED(deferreds) &&
- HAS_PRIVATE(deferreds.promise, promiseHandledBySymbol) &&
- HAS_PRIVATE(GET_PRIVATE(deferreds.promise, promiseHandledBySymbol),
- promiseAsyncStackIDSymbol)) {
- id = GET_PRIVATE(GET_PRIVATE(deferreds.promise, promiseHandledBySymbol),
- promiseAsyncStackIDSymbol);
- name = "async function";
- } else {
- id = %DebugNextMicrotaskId();
- name = status === kFulfilled ? "Promise.resolve" : "Promise.reject";
- %DebugAsyncTaskEvent("enqueue", id, name);
- }
- }
- return [id, name];
-}
-
-function PromiseAttachCallbacks(promise, deferred, onResolve, onReject) {
- var maybeResolveCallbacks =
- GET_PRIVATE(promise, promiseFulfillReactionsSymbol);
- if (IS_UNDEFINED(maybeResolveCallbacks)) {
- SET_PRIVATE(promise, promiseFulfillReactionsSymbol, onResolve);
- SET_PRIVATE(promise, promiseRejectReactionsSymbol, onReject);
- SET_PRIVATE(promise, promiseDeferredReactionSymbol, deferred);
- } else if (!IS_ARRAY(maybeResolveCallbacks)) {
- var resolveCallbacks = new InternalArray();
- var rejectCallbacks = new InternalArray();
- var existingDeferred = GET_PRIVATE(promise, promiseDeferredReactionSymbol);
-
- resolveCallbacks.push(
- maybeResolveCallbacks, existingDeferred, onResolve, deferred);
- rejectCallbacks.push(GET_PRIVATE(promise, promiseRejectReactionsSymbol),
- existingDeferred,
- onReject,
- deferred);
-
- SET_PRIVATE(promise, promiseFulfillReactionsSymbol, resolveCallbacks);
- SET_PRIVATE(promise, promiseRejectReactionsSymbol, rejectCallbacks);
- SET_PRIVATE(promise, promiseDeferredReactionSymbol, UNDEFINED);
- } else {
- maybeResolveCallbacks.push(onResolve, deferred);
- GET_PRIVATE(promise, promiseRejectReactionsSymbol).push(onReject, deferred);
- }
-}
-
function PromiseIdResolveHandler(x) { return x; }
function PromiseIdRejectHandler(r) { %_ReThrow(r); }
SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true);
@@ -206,229 +29,8 @@ SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true);
// -------------------------------------------------------------------
// Define exported functions.
-// For bootstrapper.
-
-// ES#sec-ispromise IsPromise ( x )
-function IsPromise(x) {
- return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStateSymbol);
-}
-
-function PromiseCreate() {
- return PromiseInit(new GlobalPromise(promiseRawSymbol));
-}
-
-// ES#sec-promise-resolve-functions
-// Promise Resolve Functions, steps 6-13
-function ResolvePromise(promise, resolution) {
- if (resolution === promise) {
- var exception = %make_type_error(kPromiseCyclic, resolution);
- %PromiseReject(promise, exception, true);
- PromiseSet(promise, kRejected, exception);
- return;
- }
- if (IS_RECEIVER(resolution)) {
- // 25.4.1.3.2 steps 8-12
- try {
- var then = resolution.then;
- } catch (e) {
- %PromiseReject(promise, e, true);
- PromiseSet(promise, kRejected, e);
- return;
- }
-
- // Resolution is a native promise and if it's already resolved or
- // rejected, shortcircuit the resolution procedure by directly
- // reusing the value from the promise.
- if (IsPromise(resolution) && then === PromiseThen) {
- var thenableState = GET_PRIVATE(resolution, promiseStateSymbol);
- if (thenableState === kFulfilled) {
- // This goes inside the if-else to save one symbol lookup in
- // the slow path.
- var thenableValue = GET_PRIVATE(resolution, promiseResultSymbol);
- %PromiseFulfill(promise, kFulfilled, thenableValue,
- promiseFulfillReactionsSymbol);
- PromiseSet(promise, kFulfilled, thenableValue);
- SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
- return;
- } else if (thenableState === kRejected) {
- var thenableValue = GET_PRIVATE(resolution, promiseResultSymbol);
- if (!HAS_DEFINED_PRIVATE(resolution, promiseHasHandlerSymbol)) {
- // Promise has already been rejected, but had no handler.
- // Revoke previously triggered reject event.
- %PromiseRevokeReject(resolution);
- }
- // Don't cause a debug event as this case is forwarding a rejection
- %PromiseReject(promise, thenableValue, false);
- PromiseSet(promise, kRejected, thenableValue);
- SET_PRIVATE(resolution, promiseHasHandlerSymbol, true);
- return;
- }
- }
-
- if (IS_CALLABLE(then)) {
- if (DEBUG_IS_ACTIVE && IsPromise(resolution)) {
- // Mark the dependency of the new promise on the resolution
- SET_PRIVATE(resolution, promiseHandledBySymbol, promise);
- }
- %EnqueuePromiseResolveThenableJob(promise, resolution, then);
- return;
- }
- }
- %PromiseFulfill(promise, kFulfilled, resolution,
- promiseFulfillReactionsSymbol);
- PromiseSet(promise, kFulfilled, resolution);
-}
-
-// Only used by async-await.js
-function RejectPromise(promise, reason, debugEvent) {
- %PromiseReject(promise, reason, debugEvent);
- PromiseSet(promise, kRejected, reason);
-}
-
-// Export to bindings
-function DoRejectPromise(promise, reason) {
- %PromiseReject(promise, reason, true);
- PromiseSet(promise, kRejected, reason);
-}
-
-// ES#sec-newpromisecapability
-// NewPromiseCapability ( C )
-function NewPromiseCapability(C, debugEvent) {
- if (C === GlobalPromise) {
- // Optimized case, avoid extra closure.
- var promise = PromiseCreate();
- // TODO(gsathya): Remove container for callbacks when this is
- // moved to CPP/TF.
- var callbacks = %create_resolving_functions(promise, debugEvent);
- return {
- promise: promise,
- resolve: callbacks[kResolveCallback],
- reject: callbacks[kRejectCallback]
- };
- }
-
- var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED };
- result.promise = new C((resolve, reject) => {
- if (!IS_UNDEFINED(result.resolve) || !IS_UNDEFINED(result.reject))
- throw %make_type_error(kPromiseExecutorAlreadyInvoked);
- result.resolve = resolve;
- result.reject = reject;
- });
-
- if (!IS_CALLABLE(result.resolve) || !IS_CALLABLE(result.reject))
- throw %make_type_error(kPromiseNonCallable);
-
- return result;
-}
-
-// ES#sec-promise.reject
-// Promise.reject ( x )
-function PromiseReject(r) {
- if (!IS_RECEIVER(this)) {
- throw %make_type_error(kCalledOnNonObject, PromiseResolve);
- }
- if (this === GlobalPromise) {
- // Optimized case, avoid extra closure.
- var promise = PromiseCreateAndSet(kRejected, r);
- // Trigger debug events if the debugger is on, as Promise.reject is
- // equivalent to throwing an exception directly.
- %PromiseRejectEventFromStack(promise, r);
- return promise;
- } else {
- var promiseCapability = NewPromiseCapability(this, true);
- %_Call(promiseCapability.reject, UNDEFINED, r);
- return promiseCapability.promise;
- }
-}
-
-function PerformPromiseThen(promise, onResolve, onReject, resultCapability) {
- if (!IS_CALLABLE(onResolve)) onResolve = PromiseIdResolveHandler;
- if (!IS_CALLABLE(onReject)) onReject = PromiseIdRejectHandler;
-
- var status = GET_PRIVATE(promise, promiseStateSymbol);
- switch (status) {
- case kPending:
- PromiseAttachCallbacks(promise, resultCapability, onResolve, onReject);
- break;
- case kFulfilled:
- %EnqueuePromiseReactionJob(GET_PRIVATE(promise, promiseResultSymbol),
- onResolve, resultCapability, kFulfilled);
- break;
- case kRejected:
- if (!HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) {
- // Promise has already been rejected, but had no handler.
- // Revoke previously triggered reject event.
- %PromiseRevokeReject(promise);
- }
- %EnqueuePromiseReactionJob(GET_PRIVATE(promise, promiseResultSymbol),
- onReject, resultCapability, kRejected);
- break;
- }
-
- // Mark this promise as having handler.
- SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
- return resultCapability.promise;
-}
-
-// ES#sec-promise.prototype.then
-// Promise.prototype.then ( onFulfilled, onRejected )
-// Multi-unwrapped chaining with thenable coercion.
-function PromiseThen(onResolve, onReject) {
- var status = GET_PRIVATE(this, promiseStateSymbol);
- if (IS_UNDEFINED(status)) {
- throw %make_type_error(kNotAPromise, this);
- }
-
- var constructor = SpeciesConstructor(this, GlobalPromise);
- var resultCapability;
-
- // The resultCapability.promise is only ever fulfilled internally,
- // so we don't need the closures to protect against accidentally
- // calling them multiple times.
- if (constructor === GlobalPromise) {
- // TODO(gsathya): Combine this into NewPromiseCapability.
- resultCapability = {
- promise: PromiseCreate(),
- resolve: UNDEFINED,
- reject: UNDEFINED
- };
- } else {
- // Pass false for debugEvent so .then chaining does not trigger
- // redundant ExceptionEvents.
- resultCapability = NewPromiseCapability(constructor, false);
- }
- return PerformPromiseThen(this, onResolve, onReject, resultCapability);
-}
-
-// ES#sec-promise.prototype.catch
-// Promise.prototype.catch ( onRejected )
-function PromiseCatch(onReject) {
- return this.then(UNDEFINED, onReject);
-}
-
// Combinators.
-// ES#sec-promise.resolve
-// Promise.resolve ( x )
-function PromiseResolve(x) {
- if (!IS_RECEIVER(this)) {
- throw %make_type_error(kCalledOnNonObject, PromiseResolve);
- }
- if (IsPromise(x) && x.constructor === this) return x;
-
- // Avoid creating resolving functions.
- if (this === GlobalPromise) {
- var promise = PromiseCreate();
- ResolvePromise(promise, x);
- return promise;
- }
-
- // debugEvent is not so meaningful here as it will be resolved
- var promiseCapability = NewPromiseCapability(this, true);
- %_Call(promiseCapability.resolve, UNDEFINED, x);
- return promiseCapability.promise;
-}
-
// ES#sec-promise.all
// Promise.all ( iterable )
function PromiseAll(iterable) {
@@ -438,7 +40,7 @@ function PromiseAll(iterable) {
// false debugEvent so that forwarding the rejection through all does not
// trigger redundant ExceptionEvents
- var deferred = NewPromiseCapability(this, false);
+ var deferred = %new_promise_capability(this, false);
var resolutions = new InternalArray();
var count;
@@ -474,7 +76,7 @@ function PromiseAll(iterable) {
deferred.reject);
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
- if (instrumenting && IsPromise(throwawayPromise)) {
+ if (instrumenting && %is_promise(throwawayPromise)) {
SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, deferred.promise);
}
++i;
@@ -502,7 +104,7 @@ function PromiseRace(iterable) {
// false debugEvent so that forwarding the rejection through race does not
// trigger redundant ExceptionEvents
- var deferred = NewPromiseCapability(this, false);
+ var deferred = %new_promise_capability(this, false);
// For catch prediction, don't treat the .then calls as handling it;
// instead, recurse outwards.
@@ -517,7 +119,7 @@ function PromiseRace(iterable) {
deferred.reject);
// For catch prediction, mark that rejections here are semantically
// handled by the combined Promise.
- if (instrumenting && IsPromise(throwawayPromise)) {
+ if (instrumenting && %is_promise(throwawayPromise)) {
SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, deferred.promise);
}
}
@@ -527,129 +129,17 @@ function PromiseRace(iterable) {
return deferred.promise;
}
-
-// Utility for debugger
-
-function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) {
- // Recurse to the forwarding Promise, if any. This may be due to
- // - await reaction forwarding to the throwaway Promise, which has
- // a dependency edge to the outer Promise.
- // - PromiseIdResolveHandler forwarding to the output of .then
- // - Promise.all/Promise.race forwarding to a throwaway Promise, which
- // has a dependency edge to the generated outer Promise.
- if (GET_PRIVATE(handler, promiseForwardingHandlerSymbol)) {
- return PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise);
- }
-
- // Otherwise, this is a real reject handler for the Promise
- return true;
-}
-
-function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
- // If this promise was marked as being handled by a catch block
- // in an async function, then it has a user-defined reject handler.
- if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true;
-
- // If this Promise is subsumed by another Promise (a Promise resolved
- // with another Promise, or an intermediate, hidden, throwaway Promise
- // within async/await), then recurse on the outer Promise.
- // In this case, the dependency is one possible way that the Promise
- // could be resolved, so it does not subsume the other following cases.
- var outerPromise = GET_PRIVATE(promise, promiseHandledBySymbol);
- if (outerPromise &&
- PromiseHasUserDefinedRejectHandlerRecursive(outerPromise)) {
- return true;
- }
-
- var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol);
- var deferred = GET_PRIVATE(promise, promiseDeferredReactionSymbol);
-
- if (IS_UNDEFINED(queue)) return false;
-
- if (!IS_ARRAY(queue)) {
- return PromiseHasUserDefinedRejectHandlerCheck(queue, deferred);
- }
-
- for (var i = 0; i < queue.length; i += 2) {
- if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], queue[i + 1])) {
- return true;
- }
- }
- return false;
-}
-
-// Return whether the promise will be handled by a user-defined reject
-// handler somewhere down the promise chain. For this, we do a depth-first
-// search for a reject handler that's not the default PromiseIdRejectHandler.
-// This function also traverses dependencies of one Promise on another,
-// set up through async/await and Promises resolved with Promises.
-function PromiseHasUserDefinedRejectHandler() {
- return PromiseHasUserDefinedRejectHandlerRecursive(this);
-};
-
-function MarkPromiseAsHandled(promise) {
- SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
-}
-
-
-function PromiseSpecies() {
- return this;
-}
-
// -------------------------------------------------------------------
// Install exported functions.
-%AddNamedProperty(global, 'Promise', GlobalPromise, DONT_ENUM);
-%AddNamedProperty(GlobalPromise.prototype, toStringTagSymbol, "Promise",
- DONT_ENUM | READ_ONLY);
-
utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
- "reject", PromiseReject,
"all", PromiseAll,
"race", PromiseRace,
- "resolve", PromiseResolve
-]);
-
-utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies);
-
-utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
- "then", PromiseThen,
- "catch", PromiseCatch
]);
%InstallToContext([
- "promise_catch", PromiseCatch,
- "promise_create", PromiseCreate,
- "promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
- "promise_reject", DoRejectPromise,
- // TODO(gsathya): Remove this once we update the promise builtin.
- "promise_internal_reject", RejectPromise,
- "promise_resolve", ResolvePromise,
- "promise_then", PromiseThen,
- "promise_handle", PromiseHandle,
- "promise_debug_get_info", PromiseDebugGetInfo
+ "promise_id_resolve_handler", PromiseIdResolveHandler,
+ "promise_id_reject_handler", PromiseIdRejectHandler
]);
-// This allows extras to create promises quickly without building extra
-// resolve/reject closures, and allows them to later resolve and reject any
-// promise without having to hold on to those closures forever.
-utils.InstallFunctions(extrasUtils, 0, [
- "createPromise", PromiseCreate,
- "resolvePromise", ResolvePromise,
- "rejectPromise", DoRejectPromise,
- "markPromiseAsHandled", MarkPromiseAsHandled
-]);
-
-utils.Export(function(to) {
- to.IsPromise = IsPromise;
- to.PromiseCreate = PromiseCreate;
- to.PromiseThen = PromiseThen;
-
- to.GlobalPromise = GlobalPromise;
- to.NewPromiseCapability = NewPromiseCapability;
- to.PerformPromiseThen = PerformPromiseThen;
- to.ResolvePromise = ResolvePromise;
- to.RejectPromise = RejectPromise;
-});
-
})