summaryrefslogtreecommitdiff
path: root/deps/v8/src/builtins/builtins-async-gen.cc
blob: f8974acd98993c994e20bda9bc1a95b8a67c8f89 (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
// 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.

#include "src/builtins/builtins-async-gen.h"
#include "src/builtins/builtins-utils-gen.h"

namespace v8 {
namespace internal {

using compiler::Node;

namespace {
// Describe fields of Context associated with the AsyncIterator unwrap closure.
class ValueUnwrapContext {
 public:
  enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
};

}  // namespace

Node* AsyncBuiltinsAssembler::Await(
    Node* context, Node* generator, Node* value, Node* outer_promise,
    const NodeGenerator1& create_closure_context, int on_resolve_context_index,
    int on_reject_context_index, bool is_predicted_as_caught) {
  // Let promiseCapability be ! NewPromiseCapability(%Promise%).
  Node* const wrapped_value = AllocateAndInitJSPromise(context);

  // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
  CallBuiltin(Builtins::kResolveNativePromise, context, wrapped_value, value);

  Node* const native_context = LoadNativeContext(context);

  Node* const closure_context = create_closure_context(native_context);
  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);

  // Load and allocate on_resolve closure
  Node* const on_resolve_shared_fun =
      LoadContextElement(native_context, on_resolve_context_index);
  CSA_SLOW_ASSERT(
      this, HasInstanceType(on_resolve_shared_fun, SHARED_FUNCTION_INFO_TYPE));
  Node* const on_resolve = AllocateFunctionWithMapAndContext(
      map, on_resolve_shared_fun, closure_context);

  // Load and allocate on_reject closure
  Node* const on_reject_shared_fun =
      LoadContextElement(native_context, on_reject_context_index);
  CSA_SLOW_ASSERT(
      this, HasInstanceType(on_reject_shared_fun, SHARED_FUNCTION_INFO_TYPE));
  Node* const on_reject = AllocateFunctionWithMapAndContext(
      map, on_reject_shared_fun, closure_context);

  Node* const throwaway_promise =
      AllocateAndInitJSPromise(context, wrapped_value);

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

  Label do_perform_promise_then(this);
  GotoIfNot(IsDebugActive(), &do_perform_promise_then);
  {
    Label common(this);
    GotoIf(TaggedIsSmi(value), &common);
    GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
    {
      // Mark the reject handler callback to be a forwarding edge, rather
      // than a meaningful catch handler
      Node* const key =
          HeapConstant(factory()->promise_forwarding_handler_symbol());
      CallRuntime(Runtime::kSetProperty, context, on_reject, key,
                  TrueConstant(), SmiConstant(STRICT));

      if (is_predicted_as_caught) PromiseSetHandledHint(value);
    }

    Goto(&common);
    BIND(&common);
    // Mark the dependency to outer Promise in case the throwaway Promise is
    // found on the Promise stack
    CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));

    Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
    CallRuntime(Runtime::kSetProperty, context, throwaway_promise, key,
                outer_promise, SmiConstant(STRICT));
  }

  Goto(&do_perform_promise_then);
  BIND(&do_perform_promise_then);
  CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapped_value,
              on_resolve, on_reject, throwaway_promise);

  return wrapped_value;
}

Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
                                                  Node* done) {
  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
  Node* const on_fulfilled_shared = LoadContextElement(
      native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
  CSA_ASSERT(this,
             HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
  Node* const closure_context =
      AllocateAsyncIteratorValueUnwrapContext(native_context, done);
  return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
                                           closure_context);
}

Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
    Node* native_context, Node* done) {
  CSA_ASSERT(this, IsNativeContext(native_context));
  CSA_ASSERT(this, IsBoolean(done));

  Node* const context =
      CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
  StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
                                    done);
  return context;
}

TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
  Node* const value = Parameter(Descriptor::kValue);
  Node* const context = Parameter(Descriptor::kContext);

  Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
  CSA_ASSERT(this, IsBoolean(done));

  Node* const unwrapped_value = CallStub(
      CodeFactory::CreateIterResultObject(isolate()), context, value, done);

  Return(unwrapped_value);
}

}  // namespace internal
}  // namespace v8