LCOV - code coverage report
Current view: top level - src/builtins - builtins-async-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 123 123 100.0 %
Date: 2019-04-17 Functions: 9 9 100.0 %

          Line data    Source code
       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         336 : Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator,
      27             :                                        Node* value, Node* outer_promise,
      28             :                                        Node* on_resolve_context_index,
      29             :                                        Node* on_reject_context_index,
      30             :                                        Node* is_predicted_as_caught) {
      31         672 :   Node* const native_context = LoadNativeContext(context);
      32             : 
      33             :   static const int kWrappedPromiseOffset =
      34             :       FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
      35             :   static const int kResolveClosureOffset =
      36             :       kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
      37             :   static const int kRejectClosureOffset =
      38             :       kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
      39             :   static const int kTotalSize =
      40             :       kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
      41             : 
      42         336 :   TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
      43             :   TNode<Context> closure_context = UncheckedCast<Context>(base);
      44             :   {
      45             :     // Initialize the await context, storing the {generator} as extension.
      46         336 :     StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
      47         336 :     StoreObjectFieldNoWriteBarrier(closure_context, Context::kLengthOffset,
      48             :                                    SmiConstant(Context::MIN_CONTEXT_SLOTS));
      49             :     Node* const empty_scope_info =
      50         672 :         LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
      51         336 :     StoreContextElementNoWriteBarrier(
      52         336 :         closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
      53         336 :     StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
      54         336 :                                       native_context);
      55         336 :     StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
      56         336 :                                       generator);
      57         336 :     StoreContextElementNoWriteBarrier(
      58         336 :         closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
      59             :   }
      60             : 
      61             :   // Let promiseCapability be ! NewPromiseCapability(%Promise%).
      62             :   Node* const promise_fun =
      63         672 :       LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
      64             :   CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
      65             :   Node* const promise_map =
      66             :       LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
      67             :   // Assert that the JSPromise map has an instance size is
      68             :   // JSPromise::kSizeWithEmbedderFields.
      69             :   CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
      70             :                              IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
      71             :                                             kTaggedSize)));
      72         336 :   TNode<HeapObject> wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
      73             :   {
      74             :     // Initialize Promise
      75         336 :     StoreMapNoWriteBarrier(wrapped_value, promise_map);
      76             :     StoreObjectFieldRoot(wrapped_value, JSPromise::kPropertiesOrHashOffset,
      77         336 :                          RootIndex::kEmptyFixedArray);
      78             :     StoreObjectFieldRoot(wrapped_value, JSPromise::kElementsOffset,
      79         336 :                          RootIndex::kEmptyFixedArray);
      80         336 :     PromiseInit(wrapped_value);
      81             :   }
      82             : 
      83             :   // Initialize resolve handler
      84         336 :   TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
      85             :   InitializeNativeClosure(closure_context, native_context, on_resolve,
      86         336 :                           on_resolve_context_index);
      87             : 
      88             :   // Initialize reject handler
      89         336 :   TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
      90             :   InitializeNativeClosure(closure_context, native_context, on_reject,
      91         336 :                           on_reject_context_index);
      92             : 
      93        1008 :   VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
      94             :            UndefinedConstant());
      95             : 
      96             :   // Deal with PromiseHooks and debug support in the runtime. This
      97             :   // also allocates the throwaway promise, which is only needed in
      98             :   // case of PromiseHooks or debugging.
      99         336 :   Label if_debugging(this, Label::kDeferred), do_resolve_promise(this);
     100         672 :   Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
     101         336 :          &if_debugging, &do_resolve_promise);
     102         336 :   BIND(&if_debugging);
     103             :   var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInitOld, context, value,
     104             :                                  wrapped_value, outer_promise, on_reject,
     105         336 :                                  is_predicted_as_caught));
     106         336 :   Goto(&do_resolve_promise);
     107         336 :   BIND(&do_resolve_promise);
     108             : 
     109             :   // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
     110         336 :   CallBuiltin(Builtins::kResolvePromise, context, wrapped_value, value);
     111             : 
     112         672 :   return CallBuiltin(Builtins::kPerformPromiseThen, context, wrapped_value,
     113        1008 :                      on_resolve, on_reject, var_throwaway.value());
     114             : }
     115             : 
     116         336 : Node* AsyncBuiltinsAssembler::AwaitOptimized(Node* context, Node* generator,
     117             :                                              Node* promise, Node* outer_promise,
     118             :                                              Node* on_resolve_context_index,
     119             :                                              Node* on_reject_context_index,
     120             :                                              Node* is_predicted_as_caught) {
     121         672 :   Node* const native_context = LoadNativeContext(context);
     122             :   CSA_ASSERT(this, IsJSPromise(promise));
     123             : 
     124             :   static const int kResolveClosureOffset =
     125             :       FixedArray::SizeFor(Context::MIN_CONTEXT_SLOTS);
     126             :   static const int kRejectClosureOffset =
     127             :       kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
     128             :   static const int kTotalSize =
     129             :       kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
     130             : 
     131             :   // 2. Let promise be ? PromiseResolve(« promise »).
     132             :   // Node* const promise =
     133             :   // CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
     134             : 
     135         336 :   TNode<HeapObject> base = AllocateInNewSpace(kTotalSize);
     136             :   TNode<Context> closure_context = UncheckedCast<Context>(base);
     137             :   {
     138             :     // Initialize the await context, storing the {generator} as extension.
     139         336 :     StoreMapNoWriteBarrier(closure_context, RootIndex::kAwaitContextMap);
     140         336 :     StoreObjectFieldNoWriteBarrier(closure_context, Context::kLengthOffset,
     141             :                                    SmiConstant(Context::MIN_CONTEXT_SLOTS));
     142             :     Node* const empty_scope_info =
     143         672 :         LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
     144         336 :     StoreContextElementNoWriteBarrier(
     145         336 :         closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
     146         336 :     StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
     147         336 :                                       native_context);
     148         336 :     StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
     149         336 :                                       generator);
     150         336 :     StoreContextElementNoWriteBarrier(
     151         336 :         closure_context, Context::NATIVE_CONTEXT_INDEX, native_context);
     152             :   }
     153             : 
     154             :   // Initialize resolve handler
     155         336 :   TNode<HeapObject> on_resolve = InnerAllocate(base, kResolveClosureOffset);
     156             :   InitializeNativeClosure(closure_context, native_context, on_resolve,
     157         336 :                           on_resolve_context_index);
     158             : 
     159             :   // Initialize reject handler
     160         336 :   TNode<HeapObject> on_reject = InnerAllocate(base, kRejectClosureOffset);
     161             :   InitializeNativeClosure(closure_context, native_context, on_reject,
     162         336 :                           on_reject_context_index);
     163             : 
     164        1008 :   VARIABLE(var_throwaway, MachineRepresentation::kTaggedPointer,
     165             :            UndefinedConstant());
     166             : 
     167             :   // Deal with PromiseHooks and debug support in the runtime. This
     168             :   // also allocates the throwaway promise, which is only needed in
     169             :   // case of PromiseHooks or debugging.
     170         336 :   Label if_debugging(this, Label::kDeferred), do_perform_promise_then(this);
     171         672 :   Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
     172         336 :          &if_debugging, &do_perform_promise_then);
     173         336 :   BIND(&if_debugging);
     174             :   var_throwaway.Bind(CallRuntime(Runtime::kAwaitPromisesInit, context, promise,
     175             :                                  promise, outer_promise, on_reject,
     176         336 :                                  is_predicted_as_caught));
     177         336 :   Goto(&do_perform_promise_then);
     178         336 :   BIND(&do_perform_promise_then);
     179             : 
     180         672 :   return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
     181        1008 :                      on_resolve, on_reject, var_throwaway.value());
     182             : }
     183             : 
     184         336 : Node* AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
     185             :                                     Node* outer_promise,
     186             :                                     Node* on_resolve_context_index,
     187             :                                     Node* on_reject_context_index,
     188             :                                     Node* is_predicted_as_caught) {
     189         672 :   VARIABLE(result, MachineRepresentation::kTagged);
     190         336 :   Label if_old(this), if_new(this), done(this),
     191         336 :       if_slow_constructor(this, Label::kDeferred);
     192             : 
     193             :   STATIC_ASSERT(sizeof(FLAG_harmony_await_optimization) == 1);
     194             :   TNode<Word32T> flag_value = UncheckedCast<Word32T>(Load(
     195             :       MachineType::Uint8(),
     196         672 :       ExternalConstant(
     197         672 :           ExternalReference::address_of_harmony_await_optimization_flag())));
     198        1008 :   GotoIf(Word32Equal(flag_value, Int32Constant(0)), &if_old);
     199             : 
     200             :   // We're running with --harmony-await-optimization enabled, which means
     201             :   // we do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily
     202             :   // create wrapper promises. Now if {value} is already a promise with the
     203             :   // intrinsics %Promise% constructor as its "constructor", we don't need
     204             :   // to allocate the wrapper promise and can just use the `AwaitOptimized`
     205             :   // logic.
     206         672 :   GotoIf(TaggedIsSmi(value), &if_old);
     207         672 :   Node* const value_map = LoadMap(value);
     208         672 :   GotoIfNot(IsJSPromiseMap(value_map), &if_old);
     209             :   // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
     210             :   // is the (initial) Promise.prototype and the @@species protector is
     211             :   // intact, as that guards the lookup path for "constructor" on
     212             :   // JSPromise instances which have the (initial) Promise.prototype.
     213         672 :   Node* const native_context = LoadNativeContext(context);
     214             :   Node* const promise_prototype =
     215         672 :       LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
     216         672 :   GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
     217         336 :             &if_slow_constructor);
     218         672 :   Branch(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor, &if_new);
     219             : 
     220             :   // At this point, {value} doesn't have the initial promise prototype or
     221             :   // the promise @@species protector was invalidated, but {value} could still
     222             :   // have the %Promise% as its "constructor", so we need to check that as well.
     223         336 :   BIND(&if_slow_constructor);
     224             :   {
     225             :     Node* const value_constructor =
     226        1008 :         GetProperty(context, value, isolate()->factory()->constructor_string());
     227             :     Node* const promise_function =
     228         672 :         LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
     229         672 :     Branch(WordEqual(value_constructor, promise_function), &if_new, &if_old);
     230             :   }
     231             : 
     232         336 :   BIND(&if_old);
     233         336 :   result.Bind(AwaitOld(context, generator, value, outer_promise,
     234             :                        on_resolve_context_index, on_reject_context_index,
     235         336 :                        is_predicted_as_caught));
     236         336 :   Goto(&done);
     237             : 
     238         336 :   BIND(&if_new);
     239         336 :   result.Bind(AwaitOptimized(context, generator, value, outer_promise,
     240             :                              on_resolve_context_index, on_reject_context_index,
     241         336 :                              is_predicted_as_caught));
     242         336 :   Goto(&done);
     243             : 
     244         336 :   BIND(&done);
     245         672 :   return result.value();
     246             : }
     247             : 
     248        1344 : void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
     249             :                                                      Node* native_context,
     250             :                                                      Node* function,
     251             :                                                      Node* context_index) {
     252        1344 :   TNode<Map> function_map = CAST(LoadContextElement(
     253             :       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
     254             :   // Ensure that we don't have to initialize prototype_or_initial_map field of
     255             :   // JSFunction.
     256             :   CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(function_map),
     257             :                              IntPtrConstant(JSFunction::kSizeWithoutPrototype /
     258             :                                             kTaggedSize)));
     259             :   STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
     260        1344 :   StoreMapNoWriteBarrier(function, function_map);
     261             :   StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
     262        1344 :                        RootIndex::kEmptyFixedArray);
     263             :   StoreObjectFieldRoot(function, JSObject::kElementsOffset,
     264        1344 :                        RootIndex::kEmptyFixedArray);
     265             :   StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
     266        1344 :                        RootIndex::kManyClosuresCell);
     267             : 
     268             :   TNode<SharedFunctionInfo> shared_info =
     269        1344 :       CAST(LoadContextElement(native_context, context_index));
     270             :   StoreObjectFieldNoWriteBarrier(
     271        1344 :       function, JSFunction::kSharedFunctionInfoOffset, shared_info);
     272        1344 :   StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
     273             : 
     274             :   // For the native closures that are initialized here (for `await`)
     275             :   // we know that their SharedFunctionInfo::function_data() slot
     276             :   // contains a builtin index (as Smi), so there's no need to use
     277             :   // CodeStubAssembler::GetSharedFunctionInfoCode() helper here,
     278             :   // which almost doubles the size of `await` builtins (unnecessarily).
     279             :   TNode<Smi> builtin_id = LoadObjectField<Smi>(
     280        1344 :       shared_info, SharedFunctionInfo::kFunctionDataOffset);
     281        1344 :   TNode<Code> code = LoadBuiltin(builtin_id);
     282        1344 :   StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
     283        1344 : }
     284             : 
     285         168 : Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
     286             :                                                   Node* done) {
     287         336 :   Node* const map = LoadContextElement(
     288         336 :       native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
     289         336 :   Node* const on_fulfilled_shared = LoadContextElement(
     290         168 :       native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
     291             :   CSA_ASSERT(this,
     292             :              HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
     293             :   Node* const closure_context =
     294         168 :       AllocateAsyncIteratorValueUnwrapContext(native_context, done);
     295             :   return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
     296         168 :                                            closure_context);
     297             : }
     298             : 
     299         168 : Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
     300             :     Node* native_context, Node* done) {
     301             :   CSA_ASSERT(this, IsNativeContext(native_context));
     302             :   CSA_ASSERT(this, IsBoolean(done));
     303             : 
     304             :   Node* const context =
     305         168 :       CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
     306         336 :   StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
     307         168 :                                     done);
     308         168 :   return context;
     309             : }
     310             : 
     311         224 : TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
     312             :   Node* const value = Parameter(Descriptor::kValue);
     313             :   Node* const context = Parameter(Descriptor::kContext);
     314             : 
     315         112 :   Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
     316             :   CSA_ASSERT(this, IsBoolean(done));
     317             : 
     318             :   Node* const unwrapped_value =
     319         112 :       CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
     320             : 
     321          56 :   Return(unwrapped_value);
     322          56 : }
     323             : 
     324             : }  // namespace internal
     325       59456 : }  // namespace v8

Generated by: LCOV version 1.10