Home | History | Annotate | Download | only in builtins
      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-gen.h"
      6 #include "src/builtins/builtins-utils-gen.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 using compiler::Node;
     16 
     17 namespace {
     18 class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
     19  public:
     20   explicit AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState* state)
     21       : AsyncBuiltinsAssembler(state) {}
     22 
     23   void ThrowIfNotAsyncFromSyncIterator(Node* const context, Node* const object,
     24                                        Label* if_exception,
     25                                        Variable* var_exception,
     26                                        const char* method_name);
     27 
     28   typedef std::function<void(Node* const context, Node* const promise,
     29                              Label* if_exception)>
     30       UndefinedMethodHandler;
     31   typedef std::function<Node*(Node*)> SyncIteratorNodeGenerator;
     32   void Generate_AsyncFromSyncIteratorMethod(
     33       Node* const context, Node* const iterator, Node* const sent_value,
     34       const SyncIteratorNodeGenerator& get_method,
     35       const UndefinedMethodHandler& if_method_undefined,
     36       const char* operation_name,
     37       Label::Type reject_label_type = Label::kDeferred,
     38       Node* const initial_exception_value = nullptr);
     39   void Generate_AsyncFromSyncIteratorMethodOptimized(
     40       Node* const context, Node* const iterator, Node* const sent_value,
     41       const SyncIteratorNodeGenerator& get_method,
     42       const UndefinedMethodHandler& if_method_undefined,
     43       const char* operation_name,
     44       Label::Type reject_label_type = Label::kDeferred,
     45       Node* const initial_exception_value = nullptr);
     46 
     47   void Generate_AsyncFromSyncIteratorMethod(
     48       Node* const context, Node* const iterator, Node* const sent_value,
     49       Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
     50       const char* operation_name,
     51       Label::Type reject_label_type = Label::kDeferred,
     52       Node* const initial_exception_value = nullptr) {
     53     auto get_method = [=](Node* const sync_iterator) {
     54       return GetProperty(context, sync_iterator, name);
     55     };
     56     return Generate_AsyncFromSyncIteratorMethod(
     57         context, iterator, sent_value, get_method, if_method_undefined,
     58         operation_name, reject_label_type, initial_exception_value);
     59   }
     60   void Generate_AsyncFromSyncIteratorMethodOptimized(
     61       Node* const context, Node* const iterator, Node* const sent_value,
     62       Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
     63       const char* operation_name,
     64       Label::Type reject_label_type = Label::kDeferred,
     65       Node* const initial_exception_value = nullptr) {
     66     auto get_method = [=](Node* const sync_iterator) {
     67       return GetProperty(context, sync_iterator, name);
     68     };
     69     return Generate_AsyncFromSyncIteratorMethodOptimized(
     70         context, iterator, sent_value, get_method, if_method_undefined,
     71         operation_name, reject_label_type, initial_exception_value);
     72   }
     73 
     74   // Load "value" and "done" from an iterator result object. If an exception
     75   // is thrown at any point, jumps to te `if_exception` label with exception
     76   // stored in `var_exception`.
     77   //
     78   // Returns a Pair of Nodes, whose first element is the value of the "value"
     79   // property, and whose second element is the value of the "done" property,
     80   // converted to a Boolean if needed.
     81   std::pair<Node*, Node*> LoadIteratorResult(Node* const context,
     82                                              Node* const native_context,
     83                                              Node* const iter_result,
     84                                              Label* if_exception,
     85                                              Variable* var_exception);
     86 };
     87 
     88 void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator(
     89     Node* const context, Node* const object, Label* if_exception,
     90     Variable* var_exception, const char* method_name) {
     91   Label if_receiverisincompatible(this, Label::kDeferred), done(this);
     92 
     93   GotoIf(TaggedIsSmi(object), &if_receiverisincompatible);
     94   Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done,
     95          &if_receiverisincompatible);
     96 
     97   BIND(&if_receiverisincompatible);
     98   {
     99     // If Type(O) is not Object, or if O does not have a [[SyncIterator]]
    100     // internal slot, then
    101 
    102     // Let badIteratorError be a new TypeError exception.
    103     Node* const error =
    104         MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
    105                       StringConstant(method_name), object);
    106 
    107     // Perform ! Call(promiseCapability.[[Reject]], undefined,
    108     //                 badIteratorError ).
    109     var_exception->Bind(error);
    110     Goto(if_exception);
    111   }
    112 
    113   BIND(&done);
    114 }
    115 
    116 void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
    117     Node* const context, Node* const iterator, Node* const sent_value,
    118     const SyncIteratorNodeGenerator& get_method,
    119     const UndefinedMethodHandler& if_method_undefined,
    120     const char* operation_name, Label::Type reject_label_type,
    121     Node* const initial_exception_value) {
    122   Node* const native_context = LoadNativeContext(context);
    123   Node* const promise = AllocateAndInitJSPromise(context);
    124 
    125   VARIABLE(var_exception, MachineRepresentation::kTagged,
    126            initial_exception_value == nullptr ? UndefinedConstant()
    127                                               : initial_exception_value);
    128   Label reject_promise(this, reject_label_type);
    129 
    130   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
    131                                   &var_exception, operation_name);
    132 
    133   Node* const sync_iterator =
    134       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
    135 
    136   Node* const method = get_method(sync_iterator);
    137 
    138   if (if_method_undefined) {
    139     Label if_isnotundefined(this);
    140 
    141     GotoIfNot(IsUndefined(method), &if_isnotundefined);
    142     if_method_undefined(native_context, promise, &reject_promise);
    143 
    144     BIND(&if_isnotundefined);
    145   }
    146 
    147   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
    148                                    method, sync_iterator, sent_value);
    149   GotoIfException(iter_result, &reject_promise, &var_exception);
    150 
    151   Node* value;
    152   Node* done;
    153   std::tie(value, done) = LoadIteratorResult(
    154       context, native_context, iter_result, &reject_promise, &var_exception);
    155   Node* const wrapper = AllocateAndInitJSPromise(context);
    156 
    157   // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, 
    158   // throwValue ).
    159   CallBuiltin(Builtins::kResolvePromise, context, wrapper, value);
    160 
    161   // Let onFulfilled be a new built-in function object as defined in
    162   // Async Iterator Value Unwrap Functions.
    163   // Set onFulfilled.[[Done]] to throwDone.
    164   Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
    165 
    166   // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
    167   //     onFulfilled, undefined, promiseCapability).
    168   Return(CallBuiltin(Builtins::kPerformPromiseThen, context, wrapper,
    169                      on_fulfilled, UndefinedConstant(), promise));
    170 
    171   BIND(&reject_promise);
    172   {
    173     Node* const exception = var_exception.value();
    174     CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
    175                 TrueConstant());
    176     Return(promise);
    177   }
    178 }
    179 
    180 void AsyncFromSyncBuiltinsAssembler::
    181     Generate_AsyncFromSyncIteratorMethodOptimized(
    182         Node* const context, Node* const iterator, Node* const sent_value,
    183         const SyncIteratorNodeGenerator& get_method,
    184         const UndefinedMethodHandler& if_method_undefined,
    185         const char* operation_name, Label::Type reject_label_type,
    186         Node* const initial_exception_value) {
    187   Node* const native_context = LoadNativeContext(context);
    188   Node* const promise = AllocateAndInitJSPromise(context);
    189 
    190   VARIABLE(var_exception, MachineRepresentation::kTagged,
    191            initial_exception_value == nullptr ? UndefinedConstant()
    192                                               : initial_exception_value);
    193   Label reject_promise(this, reject_label_type);
    194 
    195   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
    196                                   &var_exception, operation_name);
    197 
    198   Node* const sync_iterator =
    199       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
    200 
    201   Node* const method = get_method(sync_iterator);
    202 
    203   if (if_method_undefined) {
    204     Label if_isnotundefined(this);
    205 
    206     GotoIfNot(IsUndefined(method), &if_isnotundefined);
    207     if_method_undefined(native_context, promise, &reject_promise);
    208 
    209     BIND(&if_isnotundefined);
    210   }
    211 
    212   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
    213                                    method, sync_iterator, sent_value);
    214   GotoIfException(iter_result, &reject_promise, &var_exception);
    215 
    216   Node* value;
    217   Node* done;
    218   std::tie(value, done) = LoadIteratorResult(
    219       context, native_context, iter_result, &reject_promise, &var_exception);
    220 
    221   Node* const promise_fun =
    222       LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
    223   CSA_ASSERT(this, IsConstructor(promise_fun));
    224 
    225   // Let valueWrapper be ? PromiseResolve( value ).
    226   Node* const valueWrapper = CallBuiltin(Builtins::kPromiseResolve,
    227                                          native_context, promise_fun, value);
    228 
    229   // Let onFulfilled be a new built-in function object as defined in
    230   // Async Iterator Value Unwrap Functions.
    231   // Set onFulfilled.[[Done]] to throwDone.
    232   Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
    233 
    234   // Perform ! PerformPromiseThen(valueWrapper,
    235   //     onFulfilled, undefined, promiseCapability).
    236   Return(CallBuiltin(Builtins::kPerformPromiseThen, context, valueWrapper,
    237                      on_fulfilled, UndefinedConstant(), promise));
    238 
    239   BIND(&reject_promise);
    240   {
    241     Node* const exception = var_exception.value();
    242     CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
    243                 TrueConstant());
    244     Return(promise);
    245   }
    246 }
    247 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
    248     Node* const context, Node* const native_context, Node* const iter_result,
    249     Label* if_exception, Variable* var_exception) {
    250   Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this),
    251       done(this), if_notanobject(this, Label::kDeferred);
    252   GotoIf(TaggedIsSmi(iter_result), &if_notanobject);
    253 
    254   Node* const iter_result_map = LoadMap(iter_result);
    255   GotoIfNot(IsJSReceiverMap(iter_result_map), &if_notanobject);
    256 
    257   Node* const fast_iter_result_map =
    258       LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
    259 
    260   VARIABLE(var_value, MachineRepresentation::kTagged);
    261   VARIABLE(var_done, MachineRepresentation::kTagged);
    262   Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath,
    263          &if_slowpath);
    264 
    265   BIND(&if_fastpath);
    266   {
    267     var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset));
    268     var_value.Bind(
    269         LoadObjectField(iter_result, JSIteratorResult::kValueOffset));
    270     Goto(&merge);
    271   }
    272 
    273   BIND(&if_slowpath);
    274   {
    275     // Let nextDone be IteratorComplete(nextResult).
    276     // IfAbruptRejectPromise(nextDone, promiseCapability).
    277     Node* const done =
    278         GetProperty(context, iter_result, factory()->done_string());
    279     GotoIfException(done, if_exception, var_exception);
    280 
    281     // Let nextValue be IteratorValue(nextResult).
    282     // IfAbruptRejectPromise(nextValue, promiseCapability).
    283     Node* const value =
    284         GetProperty(context, iter_result, factory()->value_string());
    285     GotoIfException(value, if_exception, var_exception);
    286 
    287     var_value.Bind(value);
    288     var_done.Bind(done);
    289     Goto(&merge);
    290   }
    291 
    292   BIND(&if_notanobject);
    293   {
    294     // Sync iterator result is not an object --- Produce a TypeError and jump
    295     // to the `if_exception` path.
    296     Node* const error = MakeTypeError(
    297         MessageTemplate::kIteratorResultNotAnObject, context, iter_result);
    298     var_exception->Bind(error);
    299     Goto(if_exception);
    300   }
    301 
    302   BIND(&merge);
    303   // Ensure `iterResult.done` is a Boolean.
    304   GotoIf(TaggedIsSmi(var_done.value()), &to_boolean);
    305   Branch(IsBoolean(var_done.value()), &done, &to_boolean);
    306 
    307   BIND(&to_boolean);
    308   {
    309     Node* const result =
    310         CallBuiltin(Builtins::kToBoolean, context, var_done.value());
    311     var_done.Bind(result);
    312     Goto(&done);
    313   }
    314 
    315   BIND(&done);
    316   return std::make_pair(var_value.value(), var_done.value());
    317 }
    318 
    319 }  // namespace
    320 
    321 // https://tc39.github.io/proposal-async-iteration/
    322 // Section #sec-%asyncfromsynciteratorprototype%.next
    323 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
    324   Node* const iterator = Parameter(Descriptor::kReceiver);
    325   Node* const value = Parameter(Descriptor::kValue);
    326   Node* const context = Parameter(Descriptor::kContext);
    327 
    328   auto get_method = [=](Node* const unused) {
    329     return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
    330   };
    331   Generate_AsyncFromSyncIteratorMethod(
    332       context, iterator, value, get_method, UndefinedMethodHandler(),
    333       "[Async-from-Sync Iterator].prototype.next");
    334 }
    335 
    336 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNextOptimized,
    337            AsyncFromSyncBuiltinsAssembler) {
    338   Node* const iterator = Parameter(Descriptor::kReceiver);
    339   Node* const value = Parameter(Descriptor::kValue);
    340   Node* const context = Parameter(Descriptor::kContext);
    341 
    342   auto get_method = [=](Node* const unused) {
    343     return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
    344   };
    345   Generate_AsyncFromSyncIteratorMethodOptimized(
    346       context, iterator, value, get_method, UndefinedMethodHandler(),
    347       "[Async-from-Sync Iterator].prototype.next");
    348 }
    349 
    350 // https://tc39.github.io/proposal-async-iteration/
    351 // Section #sec-%asyncfromsynciteratorprototype%.return
    352 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
    353            AsyncFromSyncBuiltinsAssembler) {
    354   Node* const iterator = Parameter(Descriptor::kReceiver);
    355   Node* const value = Parameter(Descriptor::kValue);
    356   Node* const context = Parameter(Descriptor::kContext);
    357 
    358   auto if_return_undefined = [=](Node* const native_context,
    359                                  Node* const promise, Label* if_exception) {
    360     // If return is undefined, then
    361     // Let iterResult be ! CreateIterResultObject(value, true)
    362     Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
    363                                           context, value, TrueConstant());
    364 
    365     // Perform ! Call(promiseCapability.[[Resolve]], undefined,  iterResult ).
    366     // IfAbruptRejectPromise(nextDone, promiseCapability).
    367     // Return promiseCapability.[[Promise]].
    368     CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
    369     Return(promise);
    370   };
    371 
    372   Generate_AsyncFromSyncIteratorMethod(
    373       context, iterator, value, factory()->return_string(), if_return_undefined,
    374       "[Async-from-Sync Iterator].prototype.return");
    375 }
    376 
    377 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturnOptimized,
    378            AsyncFromSyncBuiltinsAssembler) {
    379   Node* const iterator = Parameter(Descriptor::kReceiver);
    380   Node* const value = Parameter(Descriptor::kValue);
    381   Node* const context = Parameter(Descriptor::kContext);
    382 
    383   auto if_return_undefined = [=](Node* const native_context,
    384                                  Node* const promise, Label* if_exception) {
    385     // If return is undefined, then
    386     // Let iterResult be ! CreateIterResultObject(value, true)
    387     Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
    388                                           context, value, TrueConstant());
    389 
    390     // Perform ! Call(promiseCapability.[[Resolve]], undefined,  iterResult ).
    391     // IfAbruptRejectPromise(nextDone, promiseCapability).
    392     // Return promiseCapability.[[Promise]].
    393     CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
    394     Return(promise);
    395   };
    396 
    397   Generate_AsyncFromSyncIteratorMethodOptimized(
    398       context, iterator, value, factory()->return_string(), if_return_undefined,
    399       "[Async-from-Sync Iterator].prototype.return");
    400 }
    401 
    402 // https://tc39.github.io/proposal-async-iteration/
    403 // Section #sec-%asyncfromsynciteratorprototype%.throw
    404 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
    405            AsyncFromSyncBuiltinsAssembler) {
    406   Node* const iterator = Parameter(Descriptor::kReceiver);
    407   Node* const reason = Parameter(Descriptor::kReason);
    408   Node* const context = Parameter(Descriptor::kContext);
    409 
    410   auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
    411                                 Label* if_exception) { Goto(if_exception); };
    412 
    413   Generate_AsyncFromSyncIteratorMethod(
    414       context, iterator, reason, factory()->throw_string(), if_throw_undefined,
    415       "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
    416       reason);
    417 }
    418 
    419 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrowOptimized,
    420            AsyncFromSyncBuiltinsAssembler) {
    421   Node* const iterator = Parameter(Descriptor::kReceiver);
    422   Node* const reason = Parameter(Descriptor::kReason);
    423   Node* const context = Parameter(Descriptor::kContext);
    424 
    425   auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
    426                                 Label* if_exception) { Goto(if_exception); };
    427 
    428   Generate_AsyncFromSyncIteratorMethodOptimized(
    429       context, iterator, reason, factory()->throw_string(), if_throw_undefined,
    430       "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
    431       reason);
    432 }
    433 
    434 }  // namespace internal
    435 }  // namespace v8
    436