Home | History | Annotate | Download | only in builtins
      1 // Copyright 2016 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 
      7 #include "src/builtins/builtins-utils-gen.h"
      8 #include "src/heap/factory-inl.h"
      9 #include "src/objects/js-promise.h"
     10 #include "src/objects/shared-function-info.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 using compiler::Node;
     16 
     17 namespace {
     18 // Describe fields of Context associated with the AsyncIterator unwrap closure.
     19 class ValueUnwrapContext {
     20  public:
     21   enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
     22 };
     23 
     24 }  // namespace
     25 
     26 Node* AsyncBuiltinsAssembler::Await(
     27     Node* context, Node* generator, Node* value, Node* outer_promise,
     28     int context_length, const ContextInitializer& init_closure_context,
     29     Node* on_resolve_context_index, Node* on_reject_context_index,
     30     Node* is_predicted_as_caught) {
     31   DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
     32 
     33   Node* const native_context = LoadNativeContext(context);
     34 
     35   static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length);
     36   static const int kThrowawayPromiseOffset =
     37       kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
     38   static const int kResolveClosureOffset =
     39       kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
     40   static const int kRejectClosureOffset =
     41       kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
     42   static const int kTotalSize =
     43       kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
     44 
     45   Node* const base = AllocateInNewSpace(kTotalSize);
     46   Node* const closure_context = base;
     47   {
     48     // Initialize closure context
     49     InitializeFunctionContext(native_context, closure_context, context_length);
     50     init_closure_context(closure_context);
     51   }
     52 
     53   // Let promiseCapability be ! NewPromiseCapability(%Promise%).
     54   Node* const promise_fun =
     55       LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
     56   CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
     57   Node* const promise_map =
     58       LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
     59   // Assert that the JSPromise map has an instance size is
     60   // JSPromise::kSizeWithEmbedderFields.
     61   CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
     62                              IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
     63                                             kPointerSize)));
     64   Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
     65   {
     66     // Initialize Promise
     67     StoreMapNoWriteBarrier(wrapped_value, promise_map);
     68     InitializeJSObjectFromMap(
     69         wrapped_value, promise_map,
     70         IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
     71     PromiseInit(wrapped_value);
     72   }
     73 
     74   Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
     75   {
     76     // Initialize throwawayPromise
     77     StoreMapNoWriteBarrier(throwaway, promise_map);
     78     InitializeJSObjectFromMap(
     79         throwaway, promise_map,
     80         IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
     81     PromiseInit(throwaway);
     82   }
     83 
     84   Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
     85   {
     86     // Initialize resolve handler
     87     InitializeNativeClosure(closure_context, native_context, on_resolve,
     88                             on_resolve_context_index);
     89   }
     90 
     91   Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
     92   {
     93     // Initialize reject handler
     94     InitializeNativeClosure(closure_context, native_context, on_reject,
     95                             on_reject_context_index);
     96   }
     97 
     98   {
     99     // Add PromiseHooks if needed
    100     Label next(this);
    101     GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
    102     CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value,
    103                 outer_promise, throwaway);
    104     Goto(&next);
    105     BIND(&next);
    106   }
    107 
    108   // Perform ! Call(promiseCapability.[[Resolve]], undefined,  promise ).
    109   CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
    110 
    111   // The Promise will be thrown away and not handled, but it shouldn't trigger
    112   // unhandled reject events as its work is done
    113   PromiseSetHasHandler(throwaway);
    114 
    115   Label do_perform_promise_then(this);
    116   GotoIfNot(IsDebugActive(), &do_perform_promise_then);
    117   {
    118     Label common(this);
    119     GotoIf(TaggedIsSmi(value), &common);
    120     GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
    121     {
    122       // Mark the reject handler callback to be a forwarding edge, rather
    123       // than a meaningful catch handler
    124       Node* const key =
    125           HeapConstant(factory()->promise_forwarding_handler_symbol());
    126       SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
    127                         TrueConstant());
    128 
    129       GotoIf(IsFalse(is_predicted_as_caught), &common);
    130       PromiseSetHandledHint(value);
    131     }
    132 
    133     Goto(&common);
    134     BIND(&common);
    135     // Mark the dependency to outer Promise in case the throwaway Promise is
    136     // found on the Promise stack
    137     CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
    138 
    139     Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
    140     SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
    141                       CAST(outer_promise));
    142   }
    143 
    144   Goto(&do_perform_promise_then);
    145   BIND(&do_perform_promise_then);
    146   return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
    147                      on_resolve, on_reject, throwaway);
    148 }
    149 
    150 Node* AsyncBuiltinsAssembler::AwaitOptimized(
    151     Node* context, Node* generator, Node* value, Node* outer_promise,
    152     int context_length, const ContextInitializer& init_closure_context,
    153     Node* on_resolve_context_index, Node* on_reject_context_index,
    154     Node* is_predicted_as_caught) {
    155   DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
    156 
    157   Node* const native_context = LoadNativeContext(context);
    158   Node* const promise_fun =
    159       LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
    160   CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
    161   CSA_ASSERT(this, IsConstructor(promise_fun));
    162 
    163   static const int kThrowawayPromiseOffset =
    164       FixedArray::SizeFor(context_length);
    165   static const int kResolveClosureOffset =
    166       kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
    167   static const int kRejectClosureOffset =
    168       kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
    169   static const int kTotalSize =
    170       kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
    171 
    172   // 2. Let promise be ? PromiseResolve( promise ).
    173   Node* const promise =
    174       CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
    175 
    176   Node* const base = AllocateInNewSpace(kTotalSize);
    177   Node* const closure_context = base;
    178   {
    179     // Initialize closure context
    180     InitializeFunctionContext(native_context, closure_context, context_length);
    181     init_closure_context(closure_context);
    182   }
    183 
    184   Node* const promise_map =
    185       LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
    186   // Assert that the JSPromise map has an instance size is
    187   // JSPromise::kSizeWithEmbedderFields.
    188   CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
    189                              IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
    190                                             kPointerSize)));
    191   Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
    192   {
    193     // Initialize throwawayPromise
    194     StoreMapNoWriteBarrier(throwaway, promise_map);
    195     InitializeJSObjectFromMap(
    196         throwaway, promise_map,
    197         IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
    198     PromiseInit(throwaway);
    199   }
    200 
    201   Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
    202   {
    203     // Initialize resolve handler
    204     InitializeNativeClosure(closure_context, native_context, on_resolve,
    205                             on_resolve_context_index);
    206   }
    207 
    208   Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
    209   {
    210     // Initialize reject handler
    211     InitializeNativeClosure(closure_context, native_context, on_reject,
    212                             on_reject_context_index);
    213   }
    214 
    215   {
    216     // Add PromiseHooks if needed
    217     Label next(this);
    218     GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
    219     CallRuntime(Runtime::kAwaitPromisesInit, context, promise, outer_promise,
    220                 throwaway);
    221     Goto(&next);
    222     BIND(&next);
    223   }
    224 
    225   // The Promise will be thrown away and not handled, but it shouldn't trigger
    226   // unhandled reject events as its work is done
    227   PromiseSetHasHandler(throwaway);
    228 
    229   Label do_perform_promise_then(this);
    230   GotoIfNot(IsDebugActive(), &do_perform_promise_then);
    231   {
    232     Label common(this);
    233     GotoIf(TaggedIsSmi(value), &common);
    234     GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
    235     {
    236       // Mark the reject handler callback to be a forwarding edge, rather
    237       // than a meaningful catch handler
    238       Node* const key =
    239           HeapConstant(factory()->promise_forwarding_handler_symbol());
    240       SetPropertyStrict(CAST(context), CAST(on_reject), CAST(key),
    241                         TrueConstant());
    242 
    243       GotoIf(IsFalse(is_predicted_as_caught), &common);
    244       PromiseSetHandledHint(value);
    245     }
    246 
    247     Goto(&common);
    248     BIND(&common);
    249     // Mark the dependency to outer Promise in case the throwaway Promise is
    250     // found on the Promise stack
    251     CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
    252 
    253     Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
    254     SetPropertyStrict(CAST(context), CAST(throwaway), CAST(key),
    255                       CAST(outer_promise));
    256   }
    257 
    258   Goto(&do_perform_promise_then);
    259   BIND(&do_perform_promise_then);
    260   return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
    261                      on_resolve, on_reject, throwaway);
    262 }
    263 
    264 void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
    265                                                      Node* native_context,
    266                                                      Node* function,
    267                                                      Node* context_index) {
    268   Node* const function_map = LoadContextElement(
    269       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
    270   // Ensure that we don't have to initialize prototype_or_initial_map field of
    271   // JSFunction.
    272   CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map),
    273                              IntPtrConstant(JSFunction::kSizeWithoutPrototype /
    274                                             kPointerSize)));
    275   STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
    276   StoreMapNoWriteBarrier(function, function_map);
    277   StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
    278                        Heap::kEmptyFixedArrayRootIndex);
    279   StoreObjectFieldRoot(function, JSObject::kElementsOffset,
    280                        Heap::kEmptyFixedArrayRootIndex);
    281   StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
    282                        Heap::kManyClosuresCellRootIndex);
    283 
    284   Node* shared_info = LoadContextElement(native_context, context_index);
    285   CSA_ASSERT(this, IsSharedFunctionInfo(shared_info));
    286   StoreObjectFieldNoWriteBarrier(
    287       function, JSFunction::kSharedFunctionInfoOffset, shared_info);
    288   StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
    289 
    290   Node* const code = GetSharedFunctionInfoCode(shared_info);
    291   StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
    292 }
    293 
    294 Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
    295                                                   Node* done) {
    296   Node* const map = LoadContextElement(
    297       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
    298   Node* const on_fulfilled_shared = LoadContextElement(
    299       native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
    300   CSA_ASSERT(this,
    301              HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
    302   Node* const closure_context =
    303       AllocateAsyncIteratorValueUnwrapContext(native_context, done);
    304   return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
    305                                            closure_context);
    306 }
    307 
    308 Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
    309     Node* native_context, Node* done) {
    310   CSA_ASSERT(this, IsNativeContext(native_context));
    311   CSA_ASSERT(this, IsBoolean(done));
    312 
    313   Node* const context =
    314       CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
    315   StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
    316                                     done);
    317   return context;
    318 }
    319 
    320 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
    321   Node* const value = Parameter(Descriptor::kValue);
    322   Node* const context = Parameter(Descriptor::kContext);
    323 
    324   Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
    325   CSA_ASSERT(this, IsBoolean(done));
    326 
    327   Node* const unwrapped_value =
    328       CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
    329 
    330   Return(unwrapped_value);
    331 }
    332 
    333 }  // namespace internal
    334 }  // namespace v8
    335