builtins-async-gen.cc 8.59 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/builtins/builtins-async-gen.h"
6

7
#include "src/builtins/builtins-utils-gen.h"
8 9
#include "src/factory-inl.h"
#include "src/objects/shared-function-info.h"
10 11 12 13

namespace v8 {
namespace internal {

14 15
using compiler::Node;

16 17 18 19 20 21 22 23 24
namespace {
// Describe fields of Context associated with the AsyncIterator unwrap closure.
class ValueUnwrapContext {
 public:
  enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
};

}  // namespace

25 26
Node* AsyncBuiltinsAssembler::Await(
    Node* context, Node* generator, Node* value, Node* outer_promise,
27
    int context_length, const ContextInitializer& init_closure_context,
28
    Node* on_resolve_context_index, Node* on_reject_context_index,
29
    Node* is_predicted_as_caught) {
30 31 32 33 34 35 36 37 38 39
  DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);

  Node* const native_context = LoadNativeContext(context);

  static const int kWrappedPromiseOffset = FixedArray::SizeFor(context_length);
  static const int kThrowawayPromiseOffset =
      kWrappedPromiseOffset + JSPromise::kSizeWithEmbedderFields;
  static const int kResolveClosureOffset =
      kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
  static const int kRejectClosureOffset =
40 41 42
      kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
  static const int kTotalSize =
      kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
43 44 45 46 47 48 49 50 51

  Node* const base = AllocateInNewSpace(kTotalSize);
  Node* const closure_context = base;
  {
    // Initialize closure context
    InitializeFunctionContext(native_context, closure_context, context_length);
    init_closure_context(closure_context);
  }

52
  // Let promiseCapability be ! NewPromiseCapability(%Promise%).
53 54
  Node* const promise_fun =
      LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
55
  CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
56 57
  Node* const promise_map =
      LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
58 59 60 61 62
  // Assert that the JSPromise map has an instance size is
  // JSPromise::kSizeWithEmbedderFields.
  CSA_ASSERT(this, WordEqual(LoadMapInstanceSize(promise_map),
                             IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
                                            kPointerSize)));
63 64 65 66 67 68
  Node* const wrapped_value = InnerAllocate(base, kWrappedPromiseOffset);
  {
    // Initialize Promise
    StoreMapNoWriteBarrier(wrapped_value, promise_map);
    InitializeJSObjectFromMap(
        wrapped_value, promise_map,
69
        IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
70 71
    PromiseInit(wrapped_value);
  }
72

73 74 75 76 77 78
  Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
  {
    // Initialize throwawayPromise
    StoreMapNoWriteBarrier(throwaway, promise_map);
    InitializeJSObjectFromMap(
        throwaway, promise_map,
79
        IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
80 81
    PromiseInit(throwaway);
  }
82

83 84 85 86 87 88
  Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
  {
    // Initialize resolve handler
    InitializeNativeClosure(closure_context, native_context, on_resolve,
                            on_resolve_context_index);
  }
89

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
  Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
  {
    // Initialize reject handler
    InitializeNativeClosure(closure_context, native_context, on_reject,
                            on_reject_context_index);
  }

  {
    // Add PromiseHooks if needed
    Label next(this);
    GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next);
    CallRuntime(Runtime::kPromiseHookInit, context, wrapped_value,
                outer_promise);
    CallRuntime(Runtime::kPromiseHookInit, context, throwaway, wrapped_value);
    Goto(&next);
    BIND(&next);
  }
107

108 109
  // Perform ! Call(promiseCapability.[[Resolve]], undefined, « promise »).
  CallBuiltin(Builtins::kResolveNativePromise, context, wrapped_value, value);
110 111 112

  // The Promise will be thrown away and not handled, but it shouldn't trigger
  // 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 123 124 125
    {
      // Mark the reject handler callback to be a forwarding edge, rather
      // than a meaningful catch handler
      Node* const key =
          HeapConstant(factory()->promise_forwarding_handler_symbol());
126
      CallRuntime(Runtime::kSetProperty, context, on_reject, key,
127
                  TrueConstant(), SmiConstant(LanguageMode::kStrict));
128

129 130
      GotoIf(IsFalse(is_predicted_as_caught), &common);
      PromiseSetHandledHint(value);
131 132 133
    }

    Goto(&common);
134
    BIND(&common);
135 136 137 138 139
    // Mark the dependency to outer Promise in case the throwaway Promise is
    // found on the Promise stack
    CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));

    Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
140
    CallRuntime(Runtime::kSetProperty, context, throwaway, key, outer_promise,
141
                SmiConstant(LanguageMode::kStrict));
142 143 144
  }

  Goto(&do_perform_promise_then);
145
  BIND(&do_perform_promise_then);
146

147
  CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapped_value,
148
              on_resolve, on_reject, throwaway);
149 150 151 152

  return wrapped_value;
}

153 154 155
void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
                                                     Node* native_context,
                                                     Node* function,
156
                                                     Node* context_index) {
157 158
  Node* const function_map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
159 160 161 162 163
  // Ensure that we don't have to initialize prototype_or_initial_map field of
  // JSFunction.
  CSA_ASSERT(this, WordEqual(LoadMapInstanceSize(function_map),
                             IntPtrConstant(JSFunction::kSizeWithoutPrototype /
                                            kPointerSize)));
164
  StoreMapNoWriteBarrier(function, function_map);
165
  StoreObjectFieldRoot(function, JSObject::kPropertiesOrHashOffset,
166 167 168 169 170 171 172 173 174 175 176 177
                       Heap::kEmptyFixedArrayRootIndex);
  StoreObjectFieldRoot(function, JSObject::kElementsOffset,
                       Heap::kEmptyFixedArrayRootIndex);
  StoreObjectFieldRoot(function, JSFunction::kFeedbackVectorOffset,
                       Heap::kUndefinedCellRootIndex);

  Node* shared_info = LoadContextElement(native_context, context_index);
  CSA_ASSERT(this, IsSharedFunctionInfo(shared_info));
  StoreObjectFieldNoWriteBarrier(
      function, JSFunction::kSharedFunctionInfoOffset, shared_info);
  StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context);

178 179 180
  Node* const code =
      LoadObjectField(shared_info, SharedFunctionInfo::kCodeOffset);
  StoreObjectFieldNoWriteBarrier(function, JSFunction::kCodeOffset, code);
181 182
}

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
Node* AsyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
                                                  Node* done) {
  Node* const map = LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
  Node* const on_fulfilled_shared = LoadContextElement(
      native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
  CSA_ASSERT(this,
             HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE));
  Node* const closure_context =
      AllocateAsyncIteratorValueUnwrapContext(native_context, done);
  return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared,
                                           closure_context);
}

Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
    Node* native_context, Node* done) {
  CSA_ASSERT(this, IsNativeContext(native_context));
  CSA_ASSERT(this, IsBoolean(done));

  Node* const context =
      CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
  StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
                                    done);
  return context;
}

TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
  Node* const value = Parameter(Descriptor::kValue);
  Node* const context = Parameter(Descriptor::kContext);

  Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
  CSA_ASSERT(this, IsBoolean(done));

216 217
  Node* const unwrapped_value =
      CallBuiltin(Builtins::kCreateIterResultObject, context, value, done);
218 219 220 221

  Return(unwrapped_value);
}

222 223
}  // namespace internal
}  // namespace v8