1// Copyright 2016 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(function(global, utils, extrasUtils) {
6
7"use strict";
8
9%CheckIsBootstrapping();
10
11// -------------------------------------------------------------------
12// Imports
13
14var AsyncFunctionNext;
15var AsyncFunctionThrow;
16var GlobalPromise;
17var IsPromise;
18var NewPromiseCapability;
19var PerformPromiseThen;
20var PromiseCreate;
21var PromiseNextMicrotaskID;
22var RejectPromise;
23var ResolvePromise;
24
25utils.Import(function(from) {
26  AsyncFunctionNext = from.AsyncFunctionNext;
27  AsyncFunctionThrow = from.AsyncFunctionThrow;
28  GlobalPromise = from.GlobalPromise;
29  IsPromise = from.IsPromise;
30  NewPromiseCapability = from.NewPromiseCapability;
31  PerformPromiseThen = from.PerformPromiseThen;
32  PromiseCreate = from.PromiseCreate;
33  RejectPromise = from.RejectPromise;
34  ResolvePromise = from.ResolvePromise;
35});
36
37var promiseAsyncStackIDSymbol =
38    utils.ImportNow("promise_async_stack_id_symbol");
39var promiseHandledBySymbol =
40    utils.ImportNow("promise_handled_by_symbol");
41var promiseForwardingHandlerSymbol =
42    utils.ImportNow("promise_forwarding_handler_symbol");
43var promiseHandledHintSymbol =
44    utils.ImportNow("promise_handled_hint_symbol");
45var promiseHasHandlerSymbol =
46    utils.ImportNow("promise_has_handler_symbol");
47
48// -------------------------------------------------------------------
49
50function PromiseCastResolved(value) {
51  if (IsPromise(value)) {
52    return value;
53  } else {
54    var promise = PromiseCreate();
55    ResolvePromise(promise, value);
56    return promise;
57  }
58}
59
60// ES#abstract-ops-async-function-await
61// AsyncFunctionAwait ( value )
62// Shared logic for the core of await. The parser desugars
63//   await awaited
64// into
65//   yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise)
66// The 'awaited' parameter is the value; the generator stands in
67// for the asyncContext, and .promise is the larger promise under
68// construction by the enclosing async function.
69function AsyncFunctionAwait(generator, awaited, outerPromise) {
70  // Promise.resolve(awaited).then(
71  //     value => AsyncFunctionNext(value),
72  //     error => AsyncFunctionThrow(error)
73  // );
74  var promise = PromiseCastResolved(awaited);
75
76  var onFulfilled = sentValue => {
77    %_Call(AsyncFunctionNext, generator, sentValue);
78    // The resulting Promise is a throwaway, so it doesn't matter what it
79    // resolves to. What is important is that we don't end up keeping the
80    // whole chain of intermediate Promises alive by returning the value
81    // of AsyncFunctionNext, as that would create a memory leak.
82    return;
83  };
84  var onRejected = sentError => {
85    %_Call(AsyncFunctionThrow, generator, sentError);
86    // Similarly, returning the huge Promise here would cause a long
87    // resolution chain to find what the exception to throw is, and
88    // create a similar memory leak, and it does not matter what
89    // sort of rejection this intermediate Promise becomes.
90    return;
91  }
92
93  // Just forwarding the exception, so no debugEvent for throwawayCapability
94  var throwawayCapability = NewPromiseCapability(GlobalPromise, false);
95
96  // The Promise will be thrown away and not handled, but it shouldn't trigger
97  // unhandled reject events as its work is done
98  SET_PRIVATE(throwawayCapability.promise, promiseHasHandlerSymbol, true);
99
100  if (DEBUG_IS_ACTIVE) {
101    if (IsPromise(awaited)) {
102      // Mark the reject handler callback to be a forwarding edge, rather
103      // than a meaningful catch handler
104      SET_PRIVATE(onRejected, promiseForwardingHandlerSymbol, true);
105    }
106
107    // Mark the dependency to outerPromise in case the throwaway Promise is
108    // found on the Promise stack
109    SET_PRIVATE(throwawayCapability.promise, promiseHandledBySymbol,
110                outerPromise);
111  }
112
113  PerformPromiseThen(promise, onFulfilled, onRejected, throwawayCapability);
114}
115
116// Called by the parser from the desugaring of 'await' when catch
117// prediction indicates no locally surrounding catch block
118function AsyncFunctionAwaitUncaught(generator, awaited, outerPromise) {
119  AsyncFunctionAwait(generator, awaited, outerPromise);
120}
121
122// Called by the parser from the desugaring of 'await' when catch
123// prediction indicates that there is a locally surrounding catch block
124function AsyncFunctionAwaitCaught(generator, awaited, outerPromise) {
125  if (DEBUG_IS_ACTIVE && IsPromise(awaited)) {
126    SET_PRIVATE(awaited, promiseHandledHintSymbol, true);
127  }
128  AsyncFunctionAwait(generator, awaited, outerPromise);
129}
130
131// How the parser rejects promises from async/await desugaring
132function RejectPromiseNoDebugEvent(promise, reason) {
133  return RejectPromise(promise, reason, false);
134}
135
136function AsyncFunctionPromiseCreate() {
137  var promise = PromiseCreate();
138  if (DEBUG_IS_ACTIVE) {
139    // Push the Promise under construction in an async function on
140    // the catch prediction stack to handle exceptions thrown before
141    // the first await.
142    %DebugPushPromise(promise);
143    // Assign ID and create a recurring task to save stack for future
144    // resumptions from await.
145    var id = %DebugNextMicrotaskId();
146    SET_PRIVATE(promise, promiseAsyncStackIDSymbol, id);
147    %DebugAsyncTaskEvent("enqueueRecurring", id, "async function");
148  }
149  return promise;
150}
151
152function AsyncFunctionPromiseRelease(promise) {
153  if (DEBUG_IS_ACTIVE) {
154    // Cancel
155    var id = GET_PRIVATE(promise, promiseAsyncStackIDSymbol);
156
157    // Don't send invalid events when catch prediction is turned on in
158    // the middle of some async operation.
159    if (!IS_UNDEFINED(id)) {
160      %DebugAsyncTaskEvent("cancel", id, "async function");
161    }
162    // Pop the Promise under construction in an async function on
163    // from catch prediction stack.
164    %DebugPopPromise();
165  }
166}
167
168%InstallToContext([
169  "async_function_await_caught", AsyncFunctionAwaitCaught,
170  "async_function_await_uncaught", AsyncFunctionAwaitUncaught,
171  "reject_promise_no_debug_event", RejectPromiseNoDebugEvent,
172  "async_function_promise_create", AsyncFunctionPromiseCreate,
173  "async_function_promise_release", AsyncFunctionPromiseRelease,
174]);
175
176})
177