11cb0ef41Sopenharmony_ci// Copyright 2016 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/builtins/builtins-async-gen.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/builtins/builtins-utils-gen.h"
81cb0ef41Sopenharmony_ci#include "src/heap/factory-inl.h"
91cb0ef41Sopenharmony_ci#include "src/objects/js-generator.h"
101cb0ef41Sopenharmony_ci#include "src/objects/js-promise.h"
111cb0ef41Sopenharmony_ci#include "src/objects/shared-function-info.h"
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_cinamespace v8 {
141cb0ef41Sopenharmony_cinamespace internal {
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cinamespace {
171cb0ef41Sopenharmony_ci// Describe fields of Context associated with the AsyncIterator unwrap closure.
181cb0ef41Sopenharmony_ciclass ValueUnwrapContext {
191cb0ef41Sopenharmony_ci public:
201cb0ef41Sopenharmony_ci  enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
211cb0ef41Sopenharmony_ci};
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci}  // namespace
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ciTNode<Object> AsyncBuiltinsAssembler::Await(
261cb0ef41Sopenharmony_ci    TNode<Context> context, TNode<JSGeneratorObject> generator,
271cb0ef41Sopenharmony_ci    TNode<Object> value, TNode<JSPromise> outer_promise,
281cb0ef41Sopenharmony_ci    TNode<SharedFunctionInfo> on_resolve_sfi,
291cb0ef41Sopenharmony_ci    TNode<SharedFunctionInfo> on_reject_sfi,
301cb0ef41Sopenharmony_ci    TNode<Oddball> is_predicted_as_caught) {
311cb0ef41Sopenharmony_ci  const TNode<NativeContext> native_context = LoadNativeContext(context);
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci  // We do the `PromiseResolve(%Promise%,value)` avoiding to unnecessarily
341cb0ef41Sopenharmony_ci  // create wrapper promises. Now if {value} is already a promise with the
351cb0ef41Sopenharmony_ci  // intrinsics %Promise% constructor as its "constructor", we don't need
361cb0ef41Sopenharmony_ci  // to allocate the wrapper promise.
371cb0ef41Sopenharmony_ci  {
381cb0ef41Sopenharmony_ci    TVARIABLE(Object, var_value, value);
391cb0ef41Sopenharmony_ci    Label if_slow_path(this, Label::kDeferred), if_done(this),
401cb0ef41Sopenharmony_ci        if_slow_constructor(this, Label::kDeferred);
411cb0ef41Sopenharmony_ci    GotoIf(TaggedIsSmi(value), &if_slow_path);
421cb0ef41Sopenharmony_ci    TNode<HeapObject> value_object = CAST(value);
431cb0ef41Sopenharmony_ci    const TNode<Map> value_map = LoadMap(value_object);
441cb0ef41Sopenharmony_ci    GotoIfNot(IsJSPromiseMap(value_map), &if_slow_path);
451cb0ef41Sopenharmony_ci    // We can skip the "constructor" lookup on {value} if it's [[Prototype]]
461cb0ef41Sopenharmony_ci    // is the (initial) Promise.prototype and the @@species protector is
471cb0ef41Sopenharmony_ci    // intact, as that guards the lookup path for "constructor" on
481cb0ef41Sopenharmony_ci    // JSPromise instances which have the (initial) Promise.prototype.
491cb0ef41Sopenharmony_ci    const TNode<Object> promise_prototype =
501cb0ef41Sopenharmony_ci        LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
511cb0ef41Sopenharmony_ci    GotoIfNot(TaggedEqual(LoadMapPrototype(value_map), promise_prototype),
521cb0ef41Sopenharmony_ci              &if_slow_constructor);
531cb0ef41Sopenharmony_ci    Branch(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor,
541cb0ef41Sopenharmony_ci           &if_done);
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci    // At this point, {value} doesn't have the initial promise prototype or
571cb0ef41Sopenharmony_ci    // the promise @@species protector was invalidated, but {value} could still
581cb0ef41Sopenharmony_ci    // have the %Promise% as its "constructor", so we need to check that as
591cb0ef41Sopenharmony_ci    // well.
601cb0ef41Sopenharmony_ci    BIND(&if_slow_constructor);
611cb0ef41Sopenharmony_ci    {
621cb0ef41Sopenharmony_ci      const TNode<Object> value_constructor = GetProperty(
631cb0ef41Sopenharmony_ci          context, value, isolate()->factory()->constructor_string());
641cb0ef41Sopenharmony_ci      const TNode<Object> promise_function =
651cb0ef41Sopenharmony_ci          LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
661cb0ef41Sopenharmony_ci      Branch(TaggedEqual(value_constructor, promise_function), &if_done,
671cb0ef41Sopenharmony_ci             &if_slow_path);
681cb0ef41Sopenharmony_ci    }
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci    BIND(&if_slow_path);
711cb0ef41Sopenharmony_ci    {
721cb0ef41Sopenharmony_ci      // We need to mark the {value} wrapper as having {outer_promise}
731cb0ef41Sopenharmony_ci      // as its parent, which is why we need to inline a good chunk of
741cb0ef41Sopenharmony_ci      // logic from the `PromiseResolve` builtin here.
751cb0ef41Sopenharmony_ci      var_value = NewJSPromise(native_context, outer_promise);
761cb0ef41Sopenharmony_ci      CallBuiltin(Builtin::kResolvePromise, native_context, var_value.value(),
771cb0ef41Sopenharmony_ci                  value);
781cb0ef41Sopenharmony_ci      Goto(&if_done);
791cb0ef41Sopenharmony_ci    }
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci    BIND(&if_done);
821cb0ef41Sopenharmony_ci    value = var_value.value();
831cb0ef41Sopenharmony_ci  }
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci  static const int kClosureContextSize =
861cb0ef41Sopenharmony_ci      FixedArray::SizeFor(Context::MIN_CONTEXT_EXTENDED_SLOTS);
871cb0ef41Sopenharmony_ci  TNode<Context> closure_context =
881cb0ef41Sopenharmony_ci      UncheckedCast<Context>(AllocateInNewSpace(kClosureContextSize));
891cb0ef41Sopenharmony_ci  {
901cb0ef41Sopenharmony_ci    // Initialize the await context, storing the {generator} as extension.
911cb0ef41Sopenharmony_ci    TNode<Map> map = CAST(
921cb0ef41Sopenharmony_ci        LoadContextElement(native_context, Context::AWAIT_CONTEXT_MAP_INDEX));
931cb0ef41Sopenharmony_ci    StoreMapNoWriteBarrier(closure_context, map);
941cb0ef41Sopenharmony_ci    StoreObjectFieldNoWriteBarrier(
951cb0ef41Sopenharmony_ci        closure_context, Context::kLengthOffset,
961cb0ef41Sopenharmony_ci        SmiConstant(Context::MIN_CONTEXT_EXTENDED_SLOTS));
971cb0ef41Sopenharmony_ci    const TNode<Object> empty_scope_info =
981cb0ef41Sopenharmony_ci        LoadContextElement(native_context, Context::SCOPE_INFO_INDEX);
991cb0ef41Sopenharmony_ci    StoreContextElementNoWriteBarrier(
1001cb0ef41Sopenharmony_ci        closure_context, Context::SCOPE_INFO_INDEX, empty_scope_info);
1011cb0ef41Sopenharmony_ci    StoreContextElementNoWriteBarrier(closure_context, Context::PREVIOUS_INDEX,
1021cb0ef41Sopenharmony_ci                                      native_context);
1031cb0ef41Sopenharmony_ci    StoreContextElementNoWriteBarrier(closure_context, Context::EXTENSION_INDEX,
1041cb0ef41Sopenharmony_ci                                      generator);
1051cb0ef41Sopenharmony_ci  }
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci  // Allocate and initialize resolve handler
1081cb0ef41Sopenharmony_ci  TNode<HeapObject> on_resolve =
1091cb0ef41Sopenharmony_ci      AllocateInNewSpace(JSFunction::kSizeWithoutPrototype);
1101cb0ef41Sopenharmony_ci  InitializeNativeClosure(closure_context, native_context, on_resolve,
1111cb0ef41Sopenharmony_ci                          on_resolve_sfi);
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci  // Allocate and initialize reject handler
1141cb0ef41Sopenharmony_ci  TNode<HeapObject> on_reject =
1151cb0ef41Sopenharmony_ci      AllocateInNewSpace(JSFunction::kSizeWithoutPrototype);
1161cb0ef41Sopenharmony_ci  InitializeNativeClosure(closure_context, native_context, on_reject,
1171cb0ef41Sopenharmony_ci                          on_reject_sfi);
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  // Deal with PromiseHooks and debug support in the runtime. This
1201cb0ef41Sopenharmony_ci  // also allocates the throwaway promise, which is only needed in
1211cb0ef41Sopenharmony_ci  // case of PromiseHooks or debugging.
1221cb0ef41Sopenharmony_ci  TVARIABLE(Object, var_throwaway, UndefinedConstant());
1231cb0ef41Sopenharmony_ci  Label if_instrumentation(this, Label::kDeferred),
1241cb0ef41Sopenharmony_ci      if_instrumentation_done(this);
1251cb0ef41Sopenharmony_ci  TNode<Uint32T> promiseHookFlags = PromiseHookFlags();
1261cb0ef41Sopenharmony_ci  GotoIf(IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(
1271cb0ef41Sopenharmony_ci             promiseHookFlags),
1281cb0ef41Sopenharmony_ci         &if_instrumentation);
1291cb0ef41Sopenharmony_ci#ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
1301cb0ef41Sopenharmony_ci  // This call to NewJSPromise is to keep behaviour parity with what happens
1311cb0ef41Sopenharmony_ci  // in Runtime::kDebugAsyncFunctionSuspended below if native hooks are set.
1321cb0ef41Sopenharmony_ci  // It creates a throwaway promise that will trigger an init event and get
1331cb0ef41Sopenharmony_ci  // passed into Builtin::kPerformPromiseThen below.
1341cb0ef41Sopenharmony_ci  GotoIfNot(IsContextPromiseHookEnabled(promiseHookFlags),
1351cb0ef41Sopenharmony_ci            &if_instrumentation_done);
1361cb0ef41Sopenharmony_ci  var_throwaway = NewJSPromise(context, value);
1371cb0ef41Sopenharmony_ci#endif  // V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
1381cb0ef41Sopenharmony_ci  Goto(&if_instrumentation_done);
1391cb0ef41Sopenharmony_ci  BIND(&if_instrumentation);
1401cb0ef41Sopenharmony_ci  {
1411cb0ef41Sopenharmony_ci    var_throwaway = CallRuntime(Runtime::kDebugAsyncFunctionSuspended,
1421cb0ef41Sopenharmony_ci                                native_context, value, outer_promise, on_reject,
1431cb0ef41Sopenharmony_ci                                generator, is_predicted_as_caught);
1441cb0ef41Sopenharmony_ci    Goto(&if_instrumentation_done);
1451cb0ef41Sopenharmony_ci  }
1461cb0ef41Sopenharmony_ci  BIND(&if_instrumentation_done);
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci  return CallBuiltin(Builtin::kPerformPromiseThen, native_context, value,
1491cb0ef41Sopenharmony_ci                     on_resolve, on_reject, var_throwaway.value());
1501cb0ef41Sopenharmony_ci}
1511cb0ef41Sopenharmony_ci
1521cb0ef41Sopenharmony_civoid AsyncBuiltinsAssembler::InitializeNativeClosure(
1531cb0ef41Sopenharmony_ci    TNode<Context> context, TNode<NativeContext> native_context,
1541cb0ef41Sopenharmony_ci    TNode<HeapObject> function, TNode<SharedFunctionInfo> shared_info) {
1551cb0ef41Sopenharmony_ci  TNode<Map> function_map = CAST(LoadContextElement(
1561cb0ef41Sopenharmony_ci      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
1571cb0ef41Sopenharmony_ci  // Ensure that we don't have to initialize prototype_or_initial_map field of
1581cb0ef41Sopenharmony_ci  // JSFunction.
1591cb0ef41Sopenharmony_ci  CSA_DCHECK(this,
1601cb0ef41Sopenharmony_ci             IntPtrEqual(LoadMapInstanceSizeInWords(function_map),
1611cb0ef41Sopenharmony_ci                         IntPtrConstant(JSFunction::kSizeWithoutPrototype /
1621cb0ef41Sopenharmony_ci                                        kTaggedSize)));
1631cb0ef41Sopenharmony_ci  STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize);
1641cb0ef41Sopenharmony_ci  StoreMapNoWriteBarrier(function, function_map);
1651cb0ef41Sopenharmony_ci  StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
1661cb0ef41Sopenharmony_ci                       RootIndex::kEmptyFixedArray);
1671cb0ef41Sopenharmony_ci  StoreObjectFieldRoot(function, JSObject::kElementsOffset,
1681cb0ef41Sopenharmony_ci                       RootIndex::kEmptyFixedArray);
1691cb0ef41Sopenharmony_ci  StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset,
1701cb0ef41Sopenharmony_ci                       RootIndex::kManyClosuresCell);
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci  StoreObjectFieldNoWriteBarrier(
1731cb0ef41Sopenharmony_ci      function, JSFunction::kSharedFunctionInfoOffset, shared_info);
1741cb0ef41Sopenharmony_ci  StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_ci  // For the native closures that are initialized here (for `await`)
1771cb0ef41Sopenharmony_ci  // we know that their SharedFunctionInfo::function_data(kAcquireLoad) slot
1781cb0ef41Sopenharmony_ci  // contains a builtin index (as Smi), so there's no need to use
1791cb0ef41Sopenharmony_ci  // CodeStubAssembler::GetSharedFunctionInfoCode() helper here,
1801cb0ef41Sopenharmony_ci  // which almost doubles the size of `await` builtins (unnecessarily).
1811cb0ef41Sopenharmony_ci  TNode<Smi> builtin_id = LoadObjectField<Smi>(
1821cb0ef41Sopenharmony_ci      shared_info, SharedFunctionInfo::kFunctionDataOffset);
1831cb0ef41Sopenharmony_ci  TNode<CodeT> code = LoadBuiltin(builtin_id);
1841cb0ef41Sopenharmony_ci  StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
1851cb0ef41Sopenharmony_ci}
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ciTNode<JSFunction> AsyncBuiltinsAssembler::CreateUnwrapClosure(
1881cb0ef41Sopenharmony_ci    TNode<NativeContext> native_context, TNode<Oddball> done) {
1891cb0ef41Sopenharmony_ci  const TNode<Map> map = CAST(LoadContextElement(
1901cb0ef41Sopenharmony_ci      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
1911cb0ef41Sopenharmony_ci  const TNode<SharedFunctionInfo> on_fulfilled_shared =
1921cb0ef41Sopenharmony_ci      AsyncIteratorValueUnwrapSharedFunConstant();
1931cb0ef41Sopenharmony_ci  const TNode<Context> closure_context =
1941cb0ef41Sopenharmony_ci      AllocateAsyncIteratorValueUnwrapContext(native_context, done);
1951cb0ef41Sopenharmony_ci  return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
1961cb0ef41Sopenharmony_ci                                           closure_context);
1971cb0ef41Sopenharmony_ci}
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ciTNode<Context> AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
2001cb0ef41Sopenharmony_ci    TNode<NativeContext> native_context, TNode<Oddball> done) {
2011cb0ef41Sopenharmony_ci  CSA_DCHECK(this, IsBoolean(done));
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci  TNode<Context> context = AllocateSyntheticFunctionContext(
2041cb0ef41Sopenharmony_ci      native_context, ValueUnwrapContext::kLength);
2051cb0ef41Sopenharmony_ci  StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
2061cb0ef41Sopenharmony_ci                                    done);
2071cb0ef41Sopenharmony_ci  return context;
2081cb0ef41Sopenharmony_ci}
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ciTF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
2111cb0ef41Sopenharmony_ci  auto value = Parameter<Object>(Descriptor::kValue);
2121cb0ef41Sopenharmony_ci  auto context = Parameter<Context>(Descriptor::kContext);
2131cb0ef41Sopenharmony_ci
2141cb0ef41Sopenharmony_ci  const TNode<Object> done =
2151cb0ef41Sopenharmony_ci      LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
2161cb0ef41Sopenharmony_ci  CSA_DCHECK(this, IsBoolean(CAST(done)));
2171cb0ef41Sopenharmony_ci
2181cb0ef41Sopenharmony_ci  const TNode<Object> unwrapped_value =
2191cb0ef41Sopenharmony_ci      CallBuiltin(Builtin::kCreateIterResultObject, context, value, done);
2201cb0ef41Sopenharmony_ci
2211cb0ef41Sopenharmony_ci  Return(unwrapped_value);
2221cb0ef41Sopenharmony_ci}
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ci}  // namespace internal
2251cb0ef41Sopenharmony_ci}  // namespace v8
226