OLD | NEW |
1 // Copyright 2017 the V8 project authors. All rights reserved. | 1 // Copyright 2017 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/builtins/builtins-promise.h" | 5 #include "src/builtins/builtins-async.h" |
6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils.h" |
7 #include "src/builtins/builtins.h" | 7 #include "src/builtins/builtins.h" |
8 #include "src/code-stub-assembler.h" | 8 #include "src/code-stub-assembler.h" |
9 | 9 |
10 namespace v8 { | 10 namespace v8 { |
11 namespace internal { | 11 namespace internal { |
12 | 12 |
13 typedef compiler::Node Node; | 13 typedef compiler::Node Node; |
14 typedef CodeStubAssembler::ParameterMode ParameterMode; | 14 typedef CodeStubAssembler::ParameterMode ParameterMode; |
15 typedef compiler::CodeAssemblerState CodeAssemblerState; | 15 typedef compiler::CodeAssemblerState CodeAssemblerState; |
16 | 16 |
17 class AsyncFunctionBuiltinsAssembler : public PromiseBuiltinsAssembler { | 17 class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler { |
18 public: | 18 public: |
19 explicit AsyncFunctionBuiltinsAssembler(CodeAssemblerState* state) | 19 explicit AsyncFunctionBuiltinsAssembler(CodeAssemblerState* state) |
20 : PromiseBuiltinsAssembler(state) {} | 20 : AsyncBuiltinsAssembler(state) {} |
| 21 |
| 22 protected: |
| 23 void AsyncFunctionAwait(Node* const context, Node* const generator, |
| 24 Node* const awaited, Node* const outer_promise, |
| 25 const bool is_predicted_as_caught); |
| 26 |
| 27 void AsyncFunctionAwaitResumeClosure( |
| 28 Node* const context, Node* const sent_value, |
| 29 JSGeneratorObject::ResumeMode resume_mode); |
21 }; | 30 }; |
22 | 31 |
| 32 namespace { |
| 33 |
| 34 // Describe fields of Context associated with AsyncFunctionAwait resume |
| 35 // closures. |
| 36 // TODO(jgruber): Refactor to reuse code for upcoming async-generators. |
| 37 class AwaitContext { |
| 38 public: |
| 39 enum Fields { kGeneratorSlot = Context::MIN_CONTEXT_SLOTS, kLength }; |
| 40 }; |
| 41 |
| 42 } // anonymous namespace |
| 43 |
| 44 void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure( |
| 45 Node* context, Node* sent_value, |
| 46 JSGeneratorObject::ResumeMode resume_mode) { |
| 47 DCHECK(resume_mode == JSGeneratorObject::kNext || |
| 48 resume_mode == JSGeneratorObject::kThrow); |
| 49 |
| 50 Node* const generator = |
| 51 LoadContextElement(context, AwaitContext::kGeneratorSlot); |
| 52 CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE)); |
| 53 |
| 54 // Inline version of GeneratorPrototypeNext / GeneratorPrototypeReturn with |
| 55 // unnecessary runtime checks removed. |
| 56 // TODO(jgruber): Refactor to reuse code from builtins-generator.cc. |
| 57 |
| 58 // Ensure that the generator is neither closed nor running. |
| 59 CSA_SLOW_ASSERT( |
| 60 this, |
| 61 SmiGreaterThan( |
| 62 LoadObjectField(generator, JSGeneratorObject::kContinuationOffset), |
| 63 SmiConstant(JSGeneratorObject::kGeneratorClosed))); |
| 64 |
| 65 // Resume the {receiver} using our trampoline. |
| 66 Callable callable = CodeFactory::ResumeGenerator(isolate()); |
| 67 CallStub(callable, context, sent_value, generator, SmiConstant(resume_mode)); |
| 68 |
| 69 // The resulting Promise is a throwaway, so it doesn't matter what it |
| 70 // resolves to. What is important is that we don't end up keeping the |
| 71 // whole chain of intermediate Promises alive by returning the return value |
| 72 // of ResumeGenerator, as that would create a memory leak. |
| 73 } |
| 74 |
| 75 TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) { |
| 76 CSA_ASSERT_JS_ARGC_EQ(this, 1); |
| 77 Node* const sentError = Parameter(1); |
| 78 Node* const context = Parameter(4); |
| 79 |
| 80 AsyncFunctionAwaitResumeClosure(context, sentError, |
| 81 JSGeneratorObject::kThrow); |
| 82 Return(UndefinedConstant()); |
| 83 } |
| 84 |
| 85 TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) { |
| 86 CSA_ASSERT_JS_ARGC_EQ(this, 1); |
| 87 Node* const sentValue = Parameter(1); |
| 88 Node* const context = Parameter(4); |
| 89 |
| 90 AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext); |
| 91 Return(UndefinedConstant()); |
| 92 } |
| 93 |
| 94 // ES#abstract-ops-async-function-await |
| 95 // AsyncFunctionAwait ( value ) |
| 96 // Shared logic for the core of await. The parser desugars |
| 97 // await awaited |
| 98 // into |
| 99 // yield AsyncFunctionAwait{Caught,Uncaught}(.generator, awaited, .promise) |
| 100 // The 'awaited' parameter is the value; the generator stands in |
| 101 // for the asyncContext, and .promise is the larger promise under |
| 102 // construction by the enclosing async function. |
| 103 void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( |
| 104 Node* const context, Node* const generator, Node* const awaited, |
| 105 Node* const outer_promise, const bool is_predicted_as_caught) { |
| 106 CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE)); |
| 107 CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE)); |
| 108 |
| 109 NodeGenerator1 create_closure_context = [&](Node* native_context) -> Node* { |
| 110 Node* const context = |
| 111 CreatePromiseContext(native_context, AwaitContext::kLength); |
| 112 StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot, |
| 113 generator); |
| 114 return context; |
| 115 }; |
| 116 |
| 117 // TODO(jgruber): AsyncBuiltinsAssembler::Await currently does not reuse |
| 118 // the awaited promise if it is already a promise. Reuse is non-spec compliant |
| 119 // but part of our old behavior gives us a couple of percent |
| 120 // performance boost. |
| 121 // TODO(jgruber): Use a faster specialized version of |
| 122 // InternalPerformPromiseThen. |
| 123 |
| 124 Node* const result = Await( |
| 125 context, generator, awaited, outer_promise, create_closure_context, |
| 126 Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN, |
| 127 Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught); |
| 128 |
| 129 Return(result); |
| 130 } |
| 131 |
| 132 // Called by the parser from the desugaring of 'await' when catch |
| 133 // prediction indicates that there is a locally surrounding catch block. |
| 134 TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) { |
| 135 CSA_ASSERT_JS_ARGC_EQ(this, 3); |
| 136 Node* const generator = Parameter(1); |
| 137 Node* const awaited = Parameter(2); |
| 138 Node* const outer_promise = Parameter(3); |
| 139 Node* const context = Parameter(6); |
| 140 |
| 141 static const bool kIsPredictedAsCaught = true; |
| 142 |
| 143 AsyncFunctionAwait(context, generator, awaited, outer_promise, |
| 144 kIsPredictedAsCaught); |
| 145 } |
| 146 |
| 147 // Called by the parser from the desugaring of 'await' when catch |
| 148 // prediction indicates no locally surrounding catch block. |
| 149 TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) { |
| 150 CSA_ASSERT_JS_ARGC_EQ(this, 3); |
| 151 Node* const generator = Parameter(1); |
| 152 Node* const awaited = Parameter(2); |
| 153 Node* const outer_promise = Parameter(3); |
| 154 Node* const context = Parameter(6); |
| 155 |
| 156 static const bool kIsPredictedAsCaught = false; |
| 157 |
| 158 AsyncFunctionAwait(context, generator, awaited, outer_promise, |
| 159 kIsPredictedAsCaught); |
| 160 } |
| 161 |
23 TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) { | 162 TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) { |
24 CSA_ASSERT_JS_ARGC_EQ(this, 0); | 163 CSA_ASSERT_JS_ARGC_EQ(this, 0); |
25 Node* const context = Parameter(3); | 164 Node* const context = Parameter(3); |
26 | 165 |
27 Node* const promise = AllocateAndInitJSPromise(context); | 166 Node* const promise = AllocateAndInitJSPromise(context); |
28 | 167 |
29 Label if_is_debug_active(this, Label::kDeferred); | 168 Label if_is_debug_active(this, Label::kDeferred); |
30 GotoIf(IsDebugActive(), &if_is_debug_active); | 169 GotoIf(IsDebugActive(), &if_is_debug_active); |
31 | 170 |
32 // Early exit if debug is not active. | 171 // Early exit if debug is not active. |
(...skipping 26 matching lines...) Expand all Loading... |
59 { | 198 { |
60 // Pop the Promise under construction in an async function on | 199 // Pop the Promise under construction in an async function on |
61 // from catch prediction stack. | 200 // from catch prediction stack. |
62 CallRuntime(Runtime::kDebugPopPromise, context); | 201 CallRuntime(Runtime::kDebugPopPromise, context); |
63 Return(promise); | 202 Return(promise); |
64 } | 203 } |
65 } | 204 } |
66 | 205 |
67 } // namespace internal | 206 } // namespace internal |
68 } // namespace v8 | 207 } // namespace v8 |
OLD | NEW |