• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "src/builtins/builtins-async.h"
6 #include "src/builtins/builtins-utils.h"
7 #include "src/builtins/builtins.h"
8 #include "src/code-factory.h"
9 #include "src/code-stub-assembler.h"
10 #include "src/frames-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 namespace {
16 
17 // Describe fields of Context associated with the AsyncIterator unwrap closure.
18 class ValueUnwrapContext {
19  public:
20   enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
21 };
22 
23 class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
24  public:
AsyncFromSyncBuiltinsAssembler(CodeAssemblerState * state)25   explicit AsyncFromSyncBuiltinsAssembler(CodeAssemblerState* state)
26       : AsyncBuiltinsAssembler(state) {}
27 
28   void ThrowIfNotAsyncFromSyncIterator(Node* const context, Node* const object,
29                                        Label* if_exception,
30                                        Variable* var_exception,
31                                        const char* method_name);
32 
33   typedef std::function<void(Node* const context, Node* const promise,
34                              Label* if_exception)>
35       UndefinedMethodHandler;
36   void Generate_AsyncFromSyncIteratorMethod(
37       Node* const context, Node* const iterator, Node* const sent_value,
38       Handle<Name> method_name, UndefinedMethodHandler&& if_method_undefined,
39       const char* operation_name,
40       Label::Type reject_label_type = Label::kDeferred,
41       Node* const initial_exception_value = nullptr);
42 
43   Node* AllocateAsyncIteratorValueUnwrapContext(Node* native_context,
44                                                 Node* done);
45 
46   // Load "value" and "done" from an iterator result object. If an exception
47   // is thrown at any point, jumps to te `if_exception` label with exception
48   // stored in `var_exception`.
49   //
50   // Returns a Pair of Nodes, whose first element is the value of the "value"
51   // property, and whose second element is the value of the "done" property,
52   // converted to a Boolean if needed.
53   std::pair<Node*, Node*> LoadIteratorResult(Node* const context,
54                                              Node* const native_context,
55                                              Node* const iter_result,
56                                              Label* if_exception,
57                                              Variable* var_exception);
58 
59   Node* CreateUnwrapClosure(Node* const native_context, Node* const done);
60 };
61 
ThrowIfNotAsyncFromSyncIterator(Node * const context,Node * const object,Label * if_exception,Variable * var_exception,const char * method_name)62 void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator(
63     Node* const context, Node* const object, Label* if_exception,
64     Variable* var_exception, const char* method_name) {
65   Label if_receiverisincompatible(this, Label::kDeferred), done(this);
66 
67   GotoIf(TaggedIsSmi(object), &if_receiverisincompatible);
68   Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done,
69          &if_receiverisincompatible);
70 
71   Bind(&if_receiverisincompatible);
72   {
73     // If Type(O) is not Object, or if O does not have a [[SyncIterator]]
74     // internal slot, then
75 
76     // Let badIteratorError be a new TypeError exception.
77     Node* const error =
78         MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
79                       CStringConstant(method_name), object);
80 
81     // Perform ! Call(promiseCapability.[[Reject]], undefined,
82     //                « badIteratorError »).
83     var_exception->Bind(error);
84     Goto(if_exception);
85   }
86 
87   Bind(&done);
88 }
89 
Generate_AsyncFromSyncIteratorMethod(Node * const context,Node * const iterator,Node * const sent_value,Handle<Name> method_name,UndefinedMethodHandler && if_method_undefined,const char * operation_name,Label::Type reject_label_type,Node * const initial_exception_value)90 void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
91     Node* const context, Node* const iterator, Node* const sent_value,
92     Handle<Name> method_name, UndefinedMethodHandler&& if_method_undefined,
93     const char* operation_name, Label::Type reject_label_type,
94     Node* const initial_exception_value) {
95   Node* const native_context = LoadNativeContext(context);
96   Node* const promise = AllocateAndInitJSPromise(context);
97 
98   Variable var_exception(this, MachineRepresentation::kTagged,
99                          initial_exception_value == nullptr
100                              ? UndefinedConstant()
101                              : initial_exception_value);
102   Label reject_promise(this, reject_label_type);
103 
104   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
105                                   &var_exception, operation_name);
106 
107   Node* const sync_iterator =
108       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
109 
110   Node* const method = GetProperty(context, sync_iterator, method_name);
111 
112   if (if_method_undefined) {
113     Label if_isnotundefined(this);
114 
115     GotoIfNot(IsUndefined(method), &if_isnotundefined);
116     if_method_undefined(native_context, promise, &reject_promise);
117 
118     Bind(&if_isnotundefined);
119   }
120 
121   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
122                                    method, sync_iterator, sent_value);
123   GotoIfException(iter_result, &reject_promise, &var_exception);
124 
125   Node* value;
126   Node* done;
127   std::tie(value, done) = LoadIteratorResult(
128       context, native_context, iter_result, &reject_promise, &var_exception);
129   Node* const wrapper = AllocateAndInitJSPromise(context);
130 
131   // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
132   // throwValue »).
133   InternalResolvePromise(context, wrapper, value);
134 
135   // Let onFulfilled be a new built-in function object as defined in
136   // Async Iterator Value Unwrap Functions.
137   // Set onFulfilled.[[Done]] to throwDone.
138   Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
139 
140   // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
141   //     onFulfilled, undefined, promiseCapability).
142   Node* const undefined = UndefinedConstant();
143   InternalPerformPromiseThen(context, wrapper, on_fulfilled, undefined, promise,
144                              undefined, undefined);
145   Return(promise);
146 
147   Bind(&reject_promise);
148   {
149     Node* const exception = var_exception.value();
150     InternalPromiseReject(context, promise, exception, TrueConstant());
151 
152     Return(promise);
153   }
154 }
155 
LoadIteratorResult(Node * const context,Node * const native_context,Node * const iter_result,Label * if_exception,Variable * var_exception)156 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
157     Node* const context, Node* const native_context, Node* const iter_result,
158     Label* if_exception, Variable* var_exception) {
159   Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this),
160       done(this), if_notanobject(this, Label::kDeferred);
161   GotoIf(TaggedIsSmi(iter_result), &if_notanobject);
162 
163   Node* const iter_result_map = LoadMap(iter_result);
164   GotoIfNot(IsJSReceiverMap(iter_result_map), &if_notanobject);
165 
166   Node* const fast_iter_result_map =
167       LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
168 
169   Variable var_value(this, MachineRepresentation::kTagged);
170   Variable var_done(this, MachineRepresentation::kTagged);
171   Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath,
172          &if_slowpath);
173 
174   Bind(&if_fastpath);
175   {
176     var_value.Bind(
177         LoadObjectField(iter_result, JSIteratorResult::kValueOffset));
178     var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset));
179     Goto(&merge);
180   }
181 
182   Bind(&if_slowpath);
183   {
184     // Let nextValue be IteratorValue(nextResult).
185     // IfAbruptRejectPromise(nextValue, promiseCapability).
186     Node* const value =
187         GetProperty(context, iter_result, factory()->value_string());
188     GotoIfException(value, if_exception, var_exception);
189 
190     // Let nextDone be IteratorComplete(nextResult).
191     // IfAbruptRejectPromise(nextDone, promiseCapability).
192     Node* const done =
193         GetProperty(context, iter_result, factory()->done_string());
194     GotoIfException(done, if_exception, var_exception);
195 
196     var_value.Bind(value);
197     var_done.Bind(done);
198     Goto(&merge);
199   }
200 
201   Bind(&if_notanobject);
202   {
203     // Sync iterator result is not an object --- Produce a TypeError and jump
204     // to the `if_exception` path.
205     Node* const error = MakeTypeError(
206         MessageTemplate::kIteratorResultNotAnObject, context, iter_result);
207     var_exception->Bind(error);
208     Goto(if_exception);
209   }
210 
211   Bind(&merge);
212   // Ensure `iterResult.done` is a Boolean.
213   GotoIf(TaggedIsSmi(var_done.value()), &to_boolean);
214   Branch(IsBoolean(var_done.value()), &done, &to_boolean);
215 
216   Bind(&to_boolean);
217   {
218     Node* const result =
219         CallStub(CodeFactory::ToBoolean(isolate()), context, var_done.value());
220     var_done.Bind(result);
221     Goto(&done);
222   }
223 
224   Bind(&done);
225   return std::make_pair(var_value.value(), var_done.value());
226 }
227 
CreateUnwrapClosure(Node * native_context,Node * done)228 Node* AsyncFromSyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
229                                                           Node* done) {
230   Node* const map = LoadContextElement(
231       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
232   Node* const on_fulfilled_shared = LoadContextElement(
233       native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
234   CSA_ASSERT(this,
235              HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
236   Node* const closure_context =
237       AllocateAsyncIteratorValueUnwrapContext(native_context, done);
238   return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
239                                            closure_context);
240 }
241 
AllocateAsyncIteratorValueUnwrapContext(Node * native_context,Node * done)242 Node* AsyncFromSyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
243     Node* native_context, Node* done) {
244   CSA_ASSERT(this, IsNativeContext(native_context));
245   CSA_ASSERT(this, IsBoolean(done));
246 
247   Node* const context =
248       CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
249   StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
250                                     done);
251   return context;
252 }
253 }  // namespace
254 
255 // https://tc39.github.io/proposal-async-iteration/
256 // Section #sec-%asyncfromsynciteratorprototype%.next
TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext,AsyncFromSyncBuiltinsAssembler)257 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
258   Node* const iterator = Parameter(0);
259   Node* const value = Parameter(1);
260   Node* const context = Parameter(4);
261 
262   Generate_AsyncFromSyncIteratorMethod(
263       context, iterator, value, factory()->next_string(),
264       UndefinedMethodHandler(), "[Async-from-Sync Iterator].prototype.next");
265 }
266 
267 // https://tc39.github.io/proposal-async-iteration/
268 // Section #sec-%asyncfromsynciteratorprototype%.return
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,AsyncFromSyncBuiltinsAssembler)269 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
270            AsyncFromSyncBuiltinsAssembler) {
271   Node* const iterator = Parameter(0);
272   Node* const value = Parameter(1);
273   Node* const context = Parameter(4);
274 
275   auto if_return_undefined = [=](Node* const native_context,
276                                  Node* const promise, Label* if_exception) {
277     // If return is undefined, then
278     // Let iterResult be ! CreateIterResultObject(value, true)
279     Node* const iter_result =
280         CallStub(CodeFactory::CreateIterResultObject(isolate()), context, value,
281                  TrueConstant());
282 
283     // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
284     // IfAbruptRejectPromise(nextDone, promiseCapability).
285     // Return promiseCapability.[[Promise]].
286     PromiseFulfill(context, promise, iter_result, v8::Promise::kFulfilled);
287     Return(promise);
288   };
289 
290   Generate_AsyncFromSyncIteratorMethod(
291       context, iterator, value, factory()->return_string(), if_return_undefined,
292       "[Async-from-Sync Iterator].prototype.return");
293 }
294 
295 // https://tc39.github.io/proposal-async-iteration/
296 // Section #sec-%asyncfromsynciteratorprototype%.throw
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,AsyncFromSyncBuiltinsAssembler)297 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
298            AsyncFromSyncBuiltinsAssembler) {
299   Node* const iterator = Parameter(0);
300   Node* const reason = Parameter(1);
301   Node* const context = Parameter(4);
302 
303   auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
304                                 Label* if_exception) { Goto(if_exception); };
305 
306   Generate_AsyncFromSyncIteratorMethod(
307       context, iterator, reason, factory()->throw_string(), if_throw_undefined,
308       "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
309       reason);
310 }
311 
TF_BUILTIN(AsyncIteratorValueUnwrap,AsyncFromSyncBuiltinsAssembler)312 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncFromSyncBuiltinsAssembler) {
313   Node* const value = Parameter(1);
314   Node* const context = Parameter(4);
315 
316   Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
317   CSA_ASSERT(this, IsBoolean(done));
318 
319   Node* const unwrapped_value = CallStub(
320       CodeFactory::CreateIterResultObject(isolate()), context, value, done);
321 
322   Return(unwrapped_value);
323 }
324 
325 }  // namespace internal
326 }  // namespace v8
327