blob: 5c9c4b5ceba4931aded31ef3d98ac27502415718 [file] [log] [blame]
// Copyright 2016 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.
(function(global, utils, extrasUtils) {
"use strict";
%CheckIsBootstrapping();
// -------------------------------------------------------------------
// Imports
var AsyncFunctionNext;
var AsyncFunctionThrow;
var CreateInternalPromiseCapability;
var PromiseCreate;
var PromiseNextMicrotaskID;
var RejectPromise;
utils.Import(function(from) {
AsyncFunctionNext = from.AsyncFunctionNext;
AsyncFunctionThrow = from.AsyncFunctionThrow;
CreateInternalPromiseCapability = from.CreateInternalPromiseCapability;
PromiseCreate = from.PromiseCreate;
RejectPromise = from.RejectPromise;
});
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 promiseHandledHintSymbol =
utils.ImportNow("promise_handled_hint_symbol");
// -------------------------------------------------------------------
function PromiseCastResolved(value) {
// TODO(caitp): This is non spec compliant. See v8:5694.
if (%is_promise(value)) {
return value;
} else {
var promise = PromiseCreate();
%promise_resolve(promise, value);
return promise;
}
}
// ES#abstract-ops-async-function-await
// AsyncFunctionAwait ( value )
// Shared logic for the core of await. The parser desugars
// await awaited
// into
// yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise)
// The 'awaited' parameter is the value; the generator stands in
// for the asyncContext, and .promise is the larger promise under
// construction by the enclosing async function.
function AsyncFunctionAwait(generator, awaited, outerPromise) {
// Promise.resolve(awaited).then(
// value => AsyncFunctionNext(value),
// error => AsyncFunctionThrow(error)
// );
var promise = PromiseCastResolved(awaited);
var onFulfilled = sentValue => {
%_Call(AsyncFunctionNext, generator, sentValue);
// The resulting Promise is a throwaway, so it doesn't matter what it
// resolves to. What is important is that we don't end up keeping the
// whole chain of intermediate Promises alive by returning the value
// of AsyncFunctionNext, as that would create a memory leak.
return;
};
var onRejected = sentError => {
%_Call(AsyncFunctionThrow, generator, sentError);
// Similarly, returning the huge Promise here would cause a long
// resolution chain to find what the exception to throw is, and
// create a similar memory leak, and it does not matter what
// sort of rejection this intermediate Promise becomes.
return;
}
// Just forwarding the exception, so no debugEvent for throwawayCapability.
var throwawayCapability = CreateInternalPromiseCapability(promise);
// The Promise will be thrown away and not handled, but it shouldn't trigger
// unhandled reject events as its work is done
%PromiseMarkAsHandled(throwawayCapability.promise);
if (DEBUG_IS_ACTIVE) {
if (%is_promise(awaited)) {
// Mark the reject handler callback to be a forwarding edge, rather
// than a meaningful catch handler
SET_PRIVATE(onRejected, promiseForwardingHandlerSymbol, true);
}
// Mark the dependency to outerPromise in case the throwaway Promise is
// found on the Promise stack
SET_PRIVATE(throwawayCapability.promise, promiseHandledBySymbol,
outerPromise);
}
%perform_promise_then(promise, onFulfilled, onRejected, throwawayCapability);
}
// Called by the parser from the desugaring of 'await' when catch
// prediction indicates no locally surrounding catch block
function AsyncFunctionAwaitUncaught(generator, awaited, outerPromise) {
AsyncFunctionAwait(generator, awaited, outerPromise);
}
// Called by the parser from the desugaring of 'await' when catch
// prediction indicates that there is a locally surrounding catch block
function AsyncFunctionAwaitCaught(generator, awaited, outerPromise) {
if (DEBUG_IS_ACTIVE && %is_promise(awaited)) {
SET_PRIVATE(awaited, promiseHandledHintSymbol, true);
}
AsyncFunctionAwait(generator, awaited, outerPromise);
}
// How the parser rejects promises from async/await desugaring
function RejectPromiseNoDebugEvent(promise, reason) {
return RejectPromise(promise, reason, false);
}
function AsyncFunctionPromiseCreate() {
var promise = PromiseCreate();
if (DEBUG_IS_ACTIVE) {
// Push the Promise under construction in an async function on
// the catch prediction stack to handle exceptions thrown before
// the first await.
%DebugPushPromise(promise);
// Assign ID and create a recurring task to save stack for future
// resumptions from await.
var id = %DebugNextMicrotaskId();
SET_PRIVATE(promise, promiseAsyncStackIDSymbol, id);
%DebugAsyncTaskEvent("enqueueRecurring", id, "async function");
}
return promise;
}
function AsyncFunctionPromiseRelease(promise) {
if (DEBUG_IS_ACTIVE) {
// Cancel
var id = GET_PRIVATE(promise, promiseAsyncStackIDSymbol);
// Don't send invalid events when catch prediction is turned on in
// the middle of some async operation.
if (!IS_UNDEFINED(id)) {
%DebugAsyncTaskEvent("cancel", id, "async function");
}
// Pop the Promise under construction in an async function on
// from catch prediction stack.
%DebugPopPromise();
}
}
%InstallToContext([
"async_function_await_caught", AsyncFunctionAwaitCaught,
"async_function_await_uncaught", AsyncFunctionAwaitUncaught,
"reject_promise_no_debug_event", RejectPromiseNoDebugEvent,
"async_function_promise_create", AsyncFunctionPromiseCreate,
"async_function_promise_release", AsyncFunctionPromiseRelease,
]);
})