builtins-async-function-gen.cc 13.1 KB
Newer Older
1 2 3 4
// Copyright 2017 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
#include "src/builtins/builtins-utils-gen.h"
7
#include "src/builtins/builtins.h"
8
#include "src/codegen/code-stub-assembler.h"
9
#include "src/objects/js-generator.h"
10
#include "src/objects/js-promise.h"
11
#include "src/objects/objects-inl.h"
12 13 14 15

namespace v8 {
namespace internal {

16
class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler {
17
 public:
18
  explicit AsyncFunctionBuiltinsAssembler(compiler::CodeAssemblerState* state)
19 20 21
      : AsyncBuiltinsAssembler(state) {}

 protected:
22 23
  template <typename Descriptor>
  void AsyncFunctionAwait(const bool is_predicted_as_caught);
24

25
  void AsyncFunctionAwaitResumeClosure(
26
      const TNode<Context> context, const TNode<Object> sent_value,
27
      JSGeneratorObject::ResumeMode resume_mode);
28 29
};

30
void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure(
31
    TNode<Context> context, TNode<Object> sent_value,
32 33 34 35
    JSGeneratorObject::ResumeMode resume_mode) {
  DCHECK(resume_mode == JSGeneratorObject::kNext ||
         resume_mode == JSGeneratorObject::kThrow);

36 37
  TNode<JSAsyncFunctionObject> async_function_object =
      CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
38

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
  // Push the promise for the {async_function_object} back onto the catch
  // prediction stack to handle exceptions thrown after resuming from the
  // await properly.
  Label if_instrumentation(this, Label::kDeferred),
      if_instrumentation_done(this);
  Branch(IsDebugActive(), &if_instrumentation, &if_instrumentation_done);
  BIND(&if_instrumentation);
  {
    TNode<JSPromise> promise = LoadObjectField<JSPromise>(
        async_function_object, JSAsyncFunctionObject::kPromiseOffset);
    CallRuntime(Runtime::kDebugAsyncFunctionResumed, context, promise);
    Goto(&if_instrumentation_done);
  }
  BIND(&if_instrumentation_done);

54 55 56
  // Inline version of GeneratorPrototypeNext / GeneratorPrototypeReturn with
  // unnecessary runtime checks removed.

57
  // Ensure that the {async_function_object} is neither closed nor running.
58
  CSA_SLOW_ASSERT(
59 60 61 62
      this, SmiGreaterThan(
                LoadObjectField<Smi>(async_function_object,
                                     JSGeneratorObject::kContinuationOffset),
                SmiConstant(JSGeneratorObject::kGeneratorClosed)));
63

64 65
  // Remember the {resume_mode} for the {async_function_object}.
  StoreObjectFieldNoWriteBarrier(async_function_object,
66 67 68
                                 JSGeneratorObject::kResumeModeOffset,
                                 SmiConstant(resume_mode));

69 70
  // Resume the {receiver} using our trampoline.
  Callable callable = CodeFactory::ResumeGenerator(isolate());
71
  CallStub(callable, context, sent_value, async_function_object);
72 73 74 75 76

  // The resulting Promise is a throwaway, so it doesn't matter what it
  // resolves to. What is important is that we don't end up keeping the
  // whole chain of intermediate Promises alive by returning the return value
  // of ResumeGenerator, as that would create a memory leak.
77 78
}

79 80 81 82 83 84 85 86
TF_BUILTIN(AsyncFunctionEnter, AsyncFunctionBuiltinsAssembler) {
  TNode<JSFunction> closure = CAST(Parameter(Descriptor::kClosure));
  TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));

  // Compute the number of registers and parameters.
  TNode<SharedFunctionInfo> shared = LoadObjectField<SharedFunctionInfo>(
      closure, JSFunction::kSharedFunctionInfoOffset);
87 88 89
  TNode<IntPtrT> formal_parameter_count =
      ChangeInt32ToIntPtr(LoadObjectField<Uint16T>(
          shared, SharedFunctionInfo::kFormalParameterCountOffset));
90 91
  TNode<BytecodeArray> bytecode_array =
      LoadSharedFunctionInfoBytecodeArray(shared);
92 93
  TNode<IntPtrT> frame_size = ChangeInt32ToIntPtr(LoadObjectField<Uint32T>(
      bytecode_array, BytecodeArray::kFrameSizeOffset));
94
  TNode<IntPtrT> parameters_and_register_length =
95
      Signed(IntPtrAdd(WordSar(frame_size, IntPtrConstant(kTaggedSizeLog2)),
96 97
                       formal_parameter_count));

98 99 100
  // Allocate and initialize the register file.
  TNode<FixedArrayBase> parameters_and_registers =
      AllocateFixedArray(HOLEY_ELEMENTS, parameters_and_register_length,
101
                         kAllowLargeObjectAllocation);
102 103 104 105
  FillFixedArrayWithValue(HOLEY_ELEMENTS, parameters_and_registers,
                          IntPtrConstant(0), parameters_and_register_length,
                          RootIndex::kUndefinedValue);

106 107
  // Allocate space for the promise, the async function object.
  TNode<IntPtrT> size = IntPtrConstant(JSPromise::kSizeWithEmbedderFields +
108
                                       JSAsyncFunctionObject::kHeaderSize);
109 110
  TNode<HeapObject> base = AllocateInNewSpace(size);

111
  // Initialize the promise.
112
  TNode<NativeContext> native_context = LoadNativeContext(context);
113 114 115 116 117
  TNode<JSFunction> promise_function =
      CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
  TNode<Map> promise_map = LoadObjectField<Map>(
      promise_function, JSFunction::kPrototypeOrInitialMapOffset);
  TNode<JSPromise> promise = UncheckedCast<JSPromise>(
118
      InnerAllocate(base, JSAsyncFunctionObject::kHeaderSize));
119 120 121 122 123 124 125 126
  StoreMapNoWriteBarrier(promise, promise_map);
  StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset,
                       RootIndex::kEmptyFixedArray);
  StoreObjectFieldRoot(promise, JSPromise::kElementsOffset,
                       RootIndex::kEmptyFixedArray);
  PromiseInit(promise);

  // Initialize the async function object.
127 128
  TNode<Map> async_function_object_map = CAST(LoadContextElement(
      native_context, Context::ASYNC_FUNCTION_OBJECT_MAP_INDEX));
129 130
  TNode<JSAsyncFunctionObject> async_function_object =
      UncheckedCast<JSAsyncFunctionObject>(base);
131 132
  StoreMapNoWriteBarrier(async_function_object, async_function_object_map);
  StoreObjectFieldRoot(async_function_object,
133
                       JSAsyncFunctionObject::kPropertiesOrHashOffset,
134 135
                       RootIndex::kEmptyFixedArray);
  StoreObjectFieldRoot(async_function_object,
136
                       JSAsyncFunctionObject::kElementsOffset,
137
                       RootIndex::kEmptyFixedArray);
138 139 140 141 142 143
  StoreObjectFieldNoWriteBarrier(
      async_function_object, JSAsyncFunctionObject::kFunctionOffset, closure);
  StoreObjectFieldNoWriteBarrier(
      async_function_object, JSAsyncFunctionObject::kContextOffset, context);
  StoreObjectFieldNoWriteBarrier(
      async_function_object, JSAsyncFunctionObject::kReceiverOffset, receiver);
144
  StoreObjectFieldNoWriteBarrier(async_function_object,
145
                                 JSAsyncFunctionObject::kInputOrDebugPosOffset,
146 147
                                 SmiConstant(0));
  StoreObjectFieldNoWriteBarrier(async_function_object,
148 149
                                 JSAsyncFunctionObject::kResumeModeOffset,
                                 SmiConstant(JSAsyncFunctionObject::kNext));
150
  StoreObjectFieldNoWriteBarrier(
151 152
      async_function_object, JSAsyncFunctionObject::kContinuationOffset,
      SmiConstant(JSAsyncFunctionObject::kGeneratorExecuting));
153
  StoreObjectFieldNoWriteBarrier(
154 155
      async_function_object,
      JSAsyncFunctionObject::kParametersAndRegistersOffset,
156
      parameters_and_registers);
157 158 159
  StoreObjectFieldNoWriteBarrier(
      async_function_object, JSAsyncFunctionObject::kPromiseOffset, promise);

160 161 162 163 164 165 166 167
  // Fire promise hooks if enabled and push the Promise under construction
  // in an async function on the catch prediction stack to handle exceptions
  // thrown before the first await.
  Label if_instrumentation(this, Label::kDeferred),
      if_instrumentation_done(this);
  Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
         &if_instrumentation, &if_instrumentation_done);
  BIND(&if_instrumentation);
168
  {
169 170
    CallRuntime(Runtime::kDebugAsyncFunctionEntered, context, promise);
    Goto(&if_instrumentation_done);
171
  }
172
  BIND(&if_instrumentation_done);
173 174 175 176 177

  Return(async_function_object);
}

TF_BUILTIN(AsyncFunctionReject, AsyncFunctionBuiltinsAssembler) {
178 179
  TNode<JSAsyncFunctionObject> async_function_object =
      CAST(Parameter(Descriptor::kAsyncFunctionObject));
180 181 182
  TNode<Object> reason = CAST(Parameter(Descriptor::kReason));
  TNode<Oddball> can_suspend = CAST(Parameter(Descriptor::kCanSuspend));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
183 184
  TNode<JSPromise> promise = LoadObjectField<JSPromise>(
      async_function_object, JSAsyncFunctionObject::kPromiseOffset);
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202

  // Reject the {promise} for the given {reason}, disabling the
  // additional debug event for the rejection since a debug event
  // already happend for the exception that got us here.
  CallBuiltin(Builtins::kRejectPromise, context, promise, reason,
              FalseConstant());

  Label if_debugging(this, Label::kDeferred);
  GotoIf(HasAsyncEventDelegate(), &if_debugging);
  GotoIf(IsDebugActive(), &if_debugging);
  Return(promise);

  BIND(&if_debugging);
  TailCallRuntime(Runtime::kDebugAsyncFunctionFinished, context, can_suspend,
                  promise);
}

TF_BUILTIN(AsyncFunctionResolve, AsyncFunctionBuiltinsAssembler) {
203 204
  TNode<JSAsyncFunctionObject> async_function_object =
      CAST(Parameter(Descriptor::kAsyncFunctionObject));
205 206 207
  TNode<Object> value = CAST(Parameter(Descriptor::kValue));
  TNode<Oddball> can_suspend = CAST(Parameter(Descriptor::kCanSuspend));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
208 209
  TNode<JSPromise> promise = LoadObjectField<JSPromise>(
      async_function_object, JSAsyncFunctionObject::kPromiseOffset);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

  CallBuiltin(Builtins::kResolvePromise, context, promise, value);

  Label if_debugging(this, Label::kDeferred);
  GotoIf(HasAsyncEventDelegate(), &if_debugging);
  GotoIf(IsDebugActive(), &if_debugging);
  Return(promise);

  BIND(&if_debugging);
  TailCallRuntime(Runtime::kDebugAsyncFunctionFinished, context, can_suspend,
                  promise);
}

// AsyncFunctionReject and AsyncFunctionResolve are both required to return
// the promise instead of the result of RejectPromise or ResolvePromise
// respectively from a lazy deoptimization.
TF_BUILTIN(AsyncFunctionLazyDeoptContinuation, AsyncFunctionBuiltinsAssembler) {
  TNode<JSPromise> promise = CAST(Parameter(Descriptor::kPromise));
  Return(promise);
}

231 232
TF_BUILTIN(AsyncFunctionAwaitRejectClosure, AsyncFunctionBuiltinsAssembler) {
  CSA_ASSERT_JS_ARGC_EQ(this, 1);
233 234
  const TNode<Object> sentError = CAST(Parameter(Descriptor::kSentError));
  const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
235 236 237 238

  AsyncFunctionAwaitResumeClosure(context, sentError,
                                  JSGeneratorObject::kThrow);
  Return(UndefinedConstant());
239 240
}

241 242
TF_BUILTIN(AsyncFunctionAwaitResolveClosure, AsyncFunctionBuiltinsAssembler) {
  CSA_ASSERT_JS_ARGC_EQ(this, 1);
243 244
  const TNode<Object> sentValue = CAST(Parameter(Descriptor::kSentValue));
  const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
245 246 247

  AsyncFunctionAwaitResumeClosure(context, sentValue, JSGeneratorObject::kNext);
  Return(UndefinedConstant());
248 249 250 251 252
}

// ES#abstract-ops-async-function-await
// AsyncFunctionAwait ( value )
// Shared logic for the core of await. The parser desugars
253
//   await value
254
// into
255 256 257 258
//   yield AsyncFunctionAwait{Caught,Uncaught}(.generator_object, value)
// The 'value' parameter is the value; the .generator_object stands in
// for the asyncContext.
template <typename Descriptor>
259
void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
260 261 262 263 264 265
    const bool is_predicted_as_caught) {
  TNode<JSAsyncFunctionObject> async_function_object =
      CAST(Parameter(Descriptor::kAsyncFunctionObject));
  TNode<Object> value = CAST(Parameter(Descriptor::kValue));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));

266
  TNode<JSPromise> outer_promise = LoadObjectField<JSPromise>(
267
      async_function_object, JSAsyncFunctionObject::kPromiseOffset);
268

269
  Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred);
270
  GotoIf(HasAsyncEventDelegate(), &call_debug_hook);
271 272 273
  Goto(&after_debug_hook);
  BIND(&after_debug_hook);

274
  Await(context, async_function_object, value, outer_promise,
275
        Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
276 277
        Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN,
        is_predicted_as_caught);
278 279 280 281

  // Return outer promise to avoid adding an load of the outer promise before
  // suspending in BytecodeGenerator.
  Return(outer_promise);
282 283 284 285

  BIND(&call_debug_hook);
  CallRuntime(Runtime::kDebugAsyncFunctionSuspended, context, outer_promise);
  Goto(&after_debug_hook);
286 287 288 289 290 291
}

// Called by the parser from the desugaring of 'await' when catch
// prediction indicates that there is a locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
  static const bool kIsPredictedAsCaught = true;
292
  AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught);
293 294 295 296 297 298
}

// Called by the parser from the desugaring of 'await' when catch
// prediction indicates no locally surrounding catch block.
TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
  static const bool kIsPredictedAsCaught = false;
299
  AsyncFunctionAwait<Descriptor>(kIsPredictedAsCaught);
300 301
}

302 303
}  // namespace internal
}  // namespace v8