async-await.js 4.89 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// 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;

utils.Import(function(from) {
  AsyncFunctionNext = from.AsyncFunctionNext;
  AsyncFunctionThrow = from.AsyncFunctionThrow;
});

22 23 24 25
var promiseHandledBySymbol =
    utils.ImportNow("promise_handled_by_symbol");
var promiseForwardingHandlerSymbol =
    utils.ImportNow("promise_forwarding_handler_symbol");
26

27 28
// -------------------------------------------------------------------

29
function PromiseCastResolved(value) {
30
  // TODO(caitp): This is non spec compliant. See v8:5694.
gsathya's avatar
gsathya committed
31
  if (%is_promise(value)) {
32 33
    return value;
  } else {
34
    var promise = %promise_internal_constructor(UNDEFINED);
35
    %promise_resolve(promise, value);
36 37 38 39 40 41 42 43 44
    return promise;
  }
}

// ES#abstract-ops-async-function-await
// AsyncFunctionAwait ( value )
// Shared logic for the core of await. The parser desugars
//   await awaited
// into
45
//   yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise)
46
// The 'awaited' parameter is the value; the generator stands in
47 48 49
// for the asyncContext, and .promise is the larger promise under
// construction by the enclosing async function.
function AsyncFunctionAwait(generator, awaited, outerPromise) {
50
  // Promise.resolve(awaited).then(
51 52 53
  //     value => AsyncFunctionNext(value),
  //     error => AsyncFunctionThrow(error)
  // );
54
  var promise = PromiseCastResolved(awaited);
55

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  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;
  }
72

73
  var throwawayPromise = %promise_internal_constructor(promise);
74 75 76

  // The Promise will be thrown away and not handled, but it shouldn't trigger
  // unhandled reject events as its work is done
77
  %PromiseMarkAsHandled(throwawayPromise);
78

79
  if (DEBUG_IS_ACTIVE) {
gsathya's avatar
gsathya committed
80
    if (%is_promise(awaited)) {
81 82 83 84 85 86 87
      // 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
88
    SET_PRIVATE(throwawayPromise, promiseHandledBySymbol, outerPromise);
89
  }
90

91
  %perform_promise_then(promise, onFulfilled, onRejected, throwawayPromise);
92 93
}

94 95
// Called by the parser from the desugaring of 'await' when catch
// prediction indicates no locally surrounding catch block
96 97
function AsyncFunctionAwaitUncaught(generator, awaited, outerPromise) {
  AsyncFunctionAwait(generator, awaited, outerPromise);
98 99 100 101
}

// Called by the parser from the desugaring of 'await' when catch
// prediction indicates that there is a locally surrounding catch block
102
function AsyncFunctionAwaitCaught(generator, awaited, outerPromise) {
gsathya's avatar
gsathya committed
103
  if (DEBUG_IS_ACTIVE && %is_promise(awaited)) {
104
    %PromiseMarkHandledHint(awaited);
105
  }
106
  AsyncFunctionAwait(generator, awaited, outerPromise);
107 108
}

109 110
// How the parser rejects promises from async/await desugaring
function RejectPromiseNoDebugEvent(promise, reason) {
111
  return %promise_internal_reject(promise, reason, false);
112 113
}

114
function AsyncFunctionPromiseCreate() {
115
  var promise = %promise_internal_constructor(UNDEFINED);
116 117 118 119 120 121
  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.
    // Assign ID and create a recurring task to save stack for future
    // resumptions from await.
122
    %DebugAsyncFunctionPromiseCreated(promise);
123 124 125 126 127 128 129 130 131 132 133 134
  }
  return promise;
}

function AsyncFunctionPromiseRelease(promise) {
  if (DEBUG_IS_ACTIVE) {
    // Pop the Promise under construction in an async function on
    // from catch prediction stack.
    %DebugPopPromise();
  }
}

135
%InstallToContext([
136 137
  "async_function_await_caught", AsyncFunctionAwaitCaught,
  "async_function_await_uncaught", AsyncFunctionAwaitUncaught,
138
  "reject_promise_no_debug_event", RejectPromiseNoDebugEvent,
139 140
  "async_function_promise_create", AsyncFunctionPromiseCreate,
  "async_function_promise_release", AsyncFunctionPromiseRelease,
141
]);
142 143

})