LCOV - code coverage report
Current view: top level - src/builtins - builtins-async-iterator-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 110 140 78.6 %
Date: 2019-02-19 Functions: 27 29 93.1 %

          Line data    Source code
       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         168 : class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
      19             :  public:
      20         168 :   explicit AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState* state)
      21         168 :       : 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         112 :   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         112 :     auto get_method = [=](Node* const sync_iterator) {
      54         224 :       return GetProperty(context, sync_iterator, name);
      55         336 :     };
      56             :     return Generate_AsyncFromSyncIteratorMethod(
      57             :         context, iterator, sent_value, get_method, if_method_undefined,
      58         112 :         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           0 :     auto get_method = [=](Node* const sync_iterator) {
      67           0 :       return GetProperty(context, sync_iterator, name);
      68           0 :     };
      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         168 : void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator(
      89             :     Node* const context, Node* const object, Label* if_exception,
      90             :     Variable* var_exception, const char* method_name) {
      91         336 :   Label if_receiverisincompatible(this, Label::kDeferred), done(this);
      92             : 
      93         168 :   GotoIf(TaggedIsSmi(object), &if_receiverisincompatible);
      94         336 :   Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done,
      95         168 :          &if_receiverisincompatible);
      96             : 
      97         168 :   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         168 :                       StringConstant(method_name), object);
     106             : 
     107             :     // Perform ! Call(promiseCapability.[[Reject]], undefined,
     108             :     //                « badIteratorError »).
     109         168 :     var_exception->Bind(error);
     110         168 :     Goto(if_exception);
     111             :   }
     112             : 
     113         336 :   BIND(&done);
     114         168 : }
     115             : 
     116         168 : 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         168 :   Node* const native_context = LoadNativeContext(context);
     123         168 :   Node* const promise = AllocateAndInitJSPromise(context);
     124             : 
     125         168 :   VARIABLE(var_exception, MachineRepresentation::kTagged,
     126             :            initial_exception_value == nullptr ? UndefinedConstant()
     127             :                                               : initial_exception_value);
     128         336 :   Label reject_promise(this, reject_label_type);
     129             : 
     130             :   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
     131         168 :                                   &var_exception, operation_name);
     132             : 
     133             :   Node* const sync_iterator =
     134         168 :       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
     135             : 
     136         168 :   Node* const method = get_method(sync_iterator);
     137             : 
     138         168 :   if (if_method_undefined) {
     139         112 :     Label if_isnotundefined(this);
     140             : 
     141         112 :     GotoIfNot(IsUndefined(method), &if_isnotundefined);
     142         112 :     if_method_undefined(native_context, promise, &reject_promise);
     143             : 
     144         112 :     BIND(&if_isnotundefined);
     145             :   }
     146             : 
     147             :   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
     148         168 :                                    method, sync_iterator, sent_value);
     149         168 :   GotoIfException(iter_result, &reject_promise, &var_exception);
     150             : 
     151             :   Node* value;
     152             :   Node* done;
     153         336 :   std::tie(value, done) = LoadIteratorResult(
     154         168 :       context, native_context, iter_result, &reject_promise, &var_exception);
     155         168 :   Node* const wrapper = AllocateAndInitJSPromise(context);
     156             : 
     157             :   // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
     158             :   // throwValue »).
     159         168 :   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         168 :   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         168 :                      on_fulfilled, UndefinedConstant(), promise));
     170             : 
     171         168 :   BIND(&reject_promise);
     172             :   {
     173         168 :     Node* const exception = var_exception.value();
     174             :     CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
     175         168 :                 TrueConstant());
     176         168 :     Return(promise);
     177         168 :   }
     178         168 : }
     179             : 
     180           0 : 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           0 :   Node* const native_context = LoadNativeContext(context);
     188           0 :   Node* const promise = AllocateAndInitJSPromise(context);
     189             : 
     190           0 :   VARIABLE(var_exception, MachineRepresentation::kTagged,
     191             :            initial_exception_value == nullptr ? UndefinedConstant()
     192             :                                               : initial_exception_value);
     193           0 :   Label reject_promise(this, reject_label_type);
     194             : 
     195             :   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
     196           0 :                                   &var_exception, operation_name);
     197             : 
     198             :   Node* const sync_iterator =
     199           0 :       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
     200             : 
     201           0 :   Node* const method = get_method(sync_iterator);
     202             : 
     203           0 :   if (if_method_undefined) {
     204           0 :     Label if_isnotundefined(this);
     205             : 
     206           0 :     GotoIfNot(IsUndefined(method), &if_isnotundefined);
     207           0 :     if_method_undefined(native_context, promise, &reject_promise);
     208             : 
     209           0 :     BIND(&if_isnotundefined);
     210             :   }
     211             : 
     212             :   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
     213           0 :                                    method, sync_iterator, sent_value);
     214           0 :   GotoIfException(iter_result, &reject_promise, &var_exception);
     215             : 
     216             :   Node* value;
     217             :   Node* done;
     218           0 :   std::tie(value, done) = LoadIteratorResult(
     219           0 :       context, native_context, iter_result, &reject_promise, &var_exception);
     220             : 
     221             :   Node* const promise_fun =
     222           0 :       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           0 :                                          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           0 :   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           0 :                      on_fulfilled, UndefinedConstant(), promise));
     238             : 
     239           0 :   BIND(&reject_promise);
     240             :   {
     241           0 :     Node* const exception = var_exception.value();
     242             :     CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
     243           0 :                 TrueConstant());
     244           0 :     Return(promise);
     245           0 :   }
     246           0 : }
     247         168 : 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         336 :   Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this),
     251         336 :       done(this), if_notanobject(this, Label::kDeferred);
     252         168 :   GotoIf(TaggedIsSmi(iter_result), &if_notanobject);
     253             : 
     254         168 :   Node* const iter_result_map = LoadMap(iter_result);
     255         168 :   GotoIfNot(IsJSReceiverMap(iter_result_map), &if_notanobject);
     256             : 
     257             :   Node* const fast_iter_result_map =
     258         168 :       LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
     259             : 
     260         336 :   VARIABLE(var_value, MachineRepresentation::kTagged);
     261         336 :   VARIABLE(var_done, MachineRepresentation::kTagged);
     262         336 :   Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath,
     263         168 :          &if_slowpath);
     264             : 
     265         168 :   BIND(&if_fastpath);
     266             :   {
     267         168 :     var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset));
     268             :     var_value.Bind(
     269         168 :         LoadObjectField(iter_result, JSIteratorResult::kValueOffset));
     270         168 :     Goto(&merge);
     271             :   }
     272             : 
     273         168 :   BIND(&if_slowpath);
     274             :   {
     275             :     // Let nextDone be IteratorComplete(nextResult).
     276             :     // IfAbruptRejectPromise(nextDone, promiseCapability).
     277             :     Node* const done =
     278         336 :         GetProperty(context, iter_result, factory()->done_string());
     279         168 :     GotoIfException(done, if_exception, var_exception);
     280             : 
     281             :     // Let nextValue be IteratorValue(nextResult).
     282             :     // IfAbruptRejectPromise(nextValue, promiseCapability).
     283             :     Node* const value =
     284         336 :         GetProperty(context, iter_result, factory()->value_string());
     285         168 :     GotoIfException(value, if_exception, var_exception);
     286             : 
     287         168 :     var_value.Bind(value);
     288         168 :     var_done.Bind(done);
     289         168 :     Goto(&merge);
     290             :   }
     291             : 
     292         168 :   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         168 :         MessageTemplate::kIteratorResultNotAnObject, context, iter_result);
     298         168 :     var_exception->Bind(error);
     299         168 :     Goto(if_exception);
     300             :   }
     301             : 
     302         168 :   BIND(&merge);
     303             :   // Ensure `iterResult.done` is a Boolean.
     304         168 :   GotoIf(TaggedIsSmi(var_done.value()), &to_boolean);
     305         168 :   Branch(IsBoolean(var_done.value()), &done, &to_boolean);
     306             : 
     307         168 :   BIND(&to_boolean);
     308             :   {
     309             :     Node* const result =
     310         168 :         CallBuiltin(Builtins::kToBoolean, context, var_done.value());
     311         168 :     var_done.Bind(result);
     312         168 :     Goto(&done);
     313             :   }
     314             : 
     315         168 :   BIND(&done);
     316         336 :   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         392 : TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
     324          56 :   Node* const iterator = Parameter(Descriptor::kReceiver);
     325          56 :   Node* const value = Parameter(Descriptor::kValue);
     326          56 :   Node* const context = Parameter(Descriptor::kContext);
     327             : 
     328          56 :   auto get_method = [=](Node* const unused) {
     329         112 :     return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
     330         168 :   };
     331             :   Generate_AsyncFromSyncIteratorMethod(
     332             :       context, iterator, value, get_method, UndefinedMethodHandler(),
     333          56 :       "[Async-from-Sync Iterator].prototype.next");
     334          56 : }
     335             : 
     336             : // https://tc39.github.io/proposal-async-iteration/
     337             : // Section #sec-%asyncfromsynciteratorprototype%.return
     338         392 : TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
     339             :            AsyncFromSyncBuiltinsAssembler) {
     340          56 :   Node* const iterator = Parameter(Descriptor::kReceiver);
     341          56 :   Node* const value = Parameter(Descriptor::kValue);
     342          56 :   Node* const context = Parameter(Descriptor::kContext);
     343             : 
     344             :   auto if_return_undefined = [=](Node* const native_context,
     345          56 :                                  Node* const promise, Label* if_exception) {
     346             :     // If return is undefined, then
     347             :     // Let iterResult be ! CreateIterResultObject(value, true)
     348             :     Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
     349          56 :                                           context, value, TrueConstant());
     350             : 
     351             :     // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
     352             :     // IfAbruptRejectPromise(nextDone, promiseCapability).
     353             :     // Return promiseCapability.[[Promise]].
     354          56 :     CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
     355          56 :     Return(promise);
     356         112 :   };
     357             : 
     358             :   Generate_AsyncFromSyncIteratorMethod(
     359             :       context, iterator, value, factory()->return_string(), if_return_undefined,
     360          56 :       "[Async-from-Sync Iterator].prototype.return");
     361          56 : }
     362             : 
     363             : // https://tc39.github.io/proposal-async-iteration/
     364             : // Section #sec-%asyncfromsynciteratorprototype%.throw
     365         392 : TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
     366             :            AsyncFromSyncBuiltinsAssembler) {
     367          56 :   Node* const iterator = Parameter(Descriptor::kReceiver);
     368          56 :   Node* const reason = Parameter(Descriptor::kReason);
     369          56 :   Node* const context = Parameter(Descriptor::kContext);
     370             : 
     371             :   auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
     372         112 :                                 Label* if_exception) { Goto(if_exception); };
     373             : 
     374             :   Generate_AsyncFromSyncIteratorMethod(
     375             :       context, iterator, reason, factory()->throw_string(), if_throw_undefined,
     376             :       "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
     377          56 :       reason);
     378          56 : }
     379             : 
     380             : }  // namespace internal
     381       86739 : }  // namespace v8

Generated by: LCOV version 1.10