aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/js/async-await.js
blob: a1cac0d5cd62a6bb6850197db285460cf06681ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// 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 GlobalPromise;
var IsPromise;
var NewPromiseCapability;
var PerformPromiseThen;
var PromiseCreate;
var PromiseNextMicrotaskID;
var RejectPromise;
var ResolvePromise;

utils.Import(function(from) {
  AsyncFunctionNext = from.AsyncFunctionNext;
  AsyncFunctionThrow = from.AsyncFunctionThrow;
  GlobalPromise = from.GlobalPromise;
  IsPromise = from.IsPromise;
  NewPromiseCapability = from.NewPromiseCapability;
  PerformPromiseThen = from.PerformPromiseThen;
  PromiseCreate = from.PromiseCreate;
  RejectPromise = from.RejectPromise;
  ResolvePromise = from.ResolvePromise;
});

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");
var promiseHasHandlerSymbol =
    utils.ImportNow("promise_has_handler_symbol");

// -------------------------------------------------------------------

function PromiseCastResolved(value) {
  if (IsPromise(value)) {
    return value;
  } else {
    var promise = PromiseCreate();
    ResolvePromise(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 = NewPromiseCapability(GlobalPromise, false);

  // The Promise will be thrown away and not handled, but it shouldn't trigger
  // unhandled reject events as its work is done
  SET_PRIVATE(throwawayCapability.promise, promiseHasHandlerSymbol, true);

  if (DEBUG_IS_ACTIVE) {
    if (IsPromise(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);
  }

  PerformPromiseThen(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 && IsPromise(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,
]);

})