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.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:
     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 
     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 
     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 
    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 
    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 
    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
    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
    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
    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 
    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