runtime-promise.cc 11.7 KB
Newer Older
1 2 3
// 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.
4

5
#include "src/api/api-inl.h"
6
#include "src/debug/debug.h"
7 8
#include "src/execution/arguments-inl.h"
#include "src/execution/microtask-queue.h"
9
#include "src/logging/counters.h"
10
#include "src/objects/elements.h"
11
#include "src/objects/heap-object-inl.h"
12
#include "src/objects/js-promise-inl.h"
13
#include "src/objects/objects-inl.h"
14
#include "src/objects/oddball-inl.h"
15
#include "src/runtime/runtime-utils.h"
16 17 18 19 20

namespace v8 {
namespace internal {

RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
21
  DCHECK_EQ(2, args.length());
22
  HandleScope scope(isolate);
23
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
24 25 26 27
  CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);

  Handle<Object> rejected_promise = promise;
  if (isolate->debug()->is_active()) {
28 29
    // If the Promise.reject() call is caught, then this will return
    // undefined, which we interpret as being a caught exception event.
30 31
    rejected_promise = isolate->GetPromiseOnStackOnThrow();
  }
32 33 34
  isolate->RunPromiseHook(PromiseHookType::kResolve, promise,
                          isolate->factory()->undefined_value());
  isolate->debug()->OnPromiseReject(rejected_promise, value);
35

36 37 38 39 40
  // Report only if we don't actually have a handler.
  if (!promise->has_handler()) {
    isolate->ReportPromiseReject(promise, value,
                                 v8::kPromiseRejectWithNoHandler);
  }
41
  return ReadOnlyRoots(isolate).undefined_value();
42 43
}

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
RUNTIME_FUNCTION(Runtime_PromiseRejectAfterResolved) {
  DCHECK_EQ(2, args.length());
  HandleScope scope(isolate);
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
  CONVERT_ARG_HANDLE_CHECKED(Object, reason, 1);
  isolate->ReportPromiseReject(promise, reason,
                               v8::kPromiseRejectAfterResolved);
  return ReadOnlyRoots(isolate).undefined_value();
}

RUNTIME_FUNCTION(Runtime_PromiseResolveAfterResolved) {
  DCHECK_EQ(2, args.length());
  HandleScope scope(isolate);
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
  CONVERT_ARG_HANDLE_CHECKED(Object, resolution, 1);
  isolate->ReportPromiseReject(promise, resolution,
                               v8::kPromiseResolveAfterResolved);
  return ReadOnlyRoots(isolate).undefined_value();
}

64
RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) {
65
  DCHECK_EQ(1, args.length());
66
  HandleScope scope(isolate);
67
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
68
  // At this point, no revocation has been issued before
69
  CHECK(!promise->has_handler());
70 71
  isolate->ReportPromiseReject(promise, Handle<Object>(),
                               v8::kPromiseHandlerAddedAfterReject);
72
  return ReadOnlyRoots(isolate).undefined_value();
73 74 75 76
}

RUNTIME_FUNCTION(Runtime_EnqueueMicrotask) {
  HandleScope scope(isolate);
77
  DCHECK_EQ(1, args.length());
78
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
79 80 81

  Handle<CallableTask> microtask = isolate->factory()->NewCallableTask(
      function, handle(function->native_context(), isolate));
82
  MicrotaskQueue* microtask_queue =
83
      function->native_context().microtask_queue();
84
  if (microtask_queue) microtask_queue->EnqueueMicrotask(*microtask);
85
  return ReadOnlyRoots(isolate).undefined_value();
86 87
}

88
RUNTIME_FUNCTION(Runtime_PerformMicrotaskCheckpoint) {
89
  HandleScope scope(isolate);
90
  DCHECK_EQ(0, args.length());
91
  MicrotasksScope::PerformCheckpoint(reinterpret_cast<v8::Isolate*>(isolate));
92
  return ReadOnlyRoots(isolate).undefined_value();
93 94
}

95 96 97 98 99 100 101 102
RUNTIME_FUNCTION(Runtime_RunMicrotaskCallback) {
  HandleScope scope(isolate);
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_CHECKED(Object, microtask_callback, 0);
  CONVERT_ARG_CHECKED(Object, microtask_data, 1);
  MicrotaskCallback callback = ToCData<MicrotaskCallback>(microtask_callback);
  void* data = ToCData<void*>(microtask_data);
  callback(data);
103
  RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
104
  return ReadOnlyRoots(isolate).undefined_value();
105 106
}

107 108
RUNTIME_FUNCTION(Runtime_PromiseStatus) {
  HandleScope scope(isolate);
109
  DCHECK_EQ(1, args.length());
110 111 112 113 114
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);

  return Smi::FromInt(promise->status());
}

115
RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) {
116
  SealHandleScope shs(isolate);
117
  DCHECK_EQ(1, args.length());
118
  CONVERT_ARG_CHECKED(JSPromise, promise, 0);
119

120
  promise.set_has_handler(true);
121
  return ReadOnlyRoots(isolate).undefined_value();
122 123
}

124 125 126 127 128 129
RUNTIME_FUNCTION(Runtime_PromiseHookInit) {
  HandleScope scope(isolate);
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
  CONVERT_ARG_HANDLE_CHECKED(Object, parent, 1);
  isolate->RunPromiseHook(PromiseHookType::kInit, promise, parent);
130
  return ReadOnlyRoots(isolate).undefined_value();
131 132
}

133 134 135 136 137 138 139 140 141 142 143
namespace {

Handle<JSPromise> AwaitPromisesInitCommon(Isolate* isolate,
                                          Handle<Object> value,
                                          Handle<JSPromise> promise,
                                          Handle<JSPromise> outer_promise,
                                          Handle<JSFunction> reject_handler,
                                          bool is_predicted_as_caught) {
  // Allocate the throwaway promise and fire the appropriate init
  // hook for the throwaway promise (passing the {promise} as its
  // parent).
144 145 146
  Handle<JSPromise> throwaway = isolate->factory()->NewJSPromiseWithoutHook();
  isolate->RunPromiseHook(PromiseHookType::kInit, throwaway, promise);

147 148 149 150 151 152
  // On inspector side we capture async stack trace and store it by
  // outer_promise->async_task_id when async function is suspended first time.
  // To use captured stack trace later throwaway promise should have the same
  // async_task_id as outer_promise since we generate WillHandle and DidHandle
  // events using throwaway promise.
  throwaway->set_async_task_id(outer_promise->async_task_id());
153 154 155 156 157 158 159 160 161 162 163

  // The Promise will be thrown away and not handled, but it
  // shouldn't trigger unhandled reject events as its work is done
  throwaway->set_has_handler(true);

  // Enable proper debug support for promises.
  if (isolate->debug()->is_active()) {
    if (value->IsJSPromise()) {
      Object::SetProperty(
          isolate, reject_handler,
          isolate->factory()->promise_forwarding_handler_symbol(),
164
          isolate->factory()->true_value(), StoreOrigin::kMaybeKeyed,
165
          Just(ShouldThrow::kThrowOnError))
166 167 168 169 170 171
          .Check();
      Handle<JSPromise>::cast(value)->set_handled_hint(is_predicted_as_caught);
    }

    // Mark the dependency to {outer_promise} in case the {throwaway}
    // Promise is found on the Promise stack
172 173 174 175
    Object::SetProperty(isolate, throwaway,
                        isolate->factory()->promise_handled_by_symbol(),
                        outer_promise, StoreOrigin::kMaybeKeyed,
                        Just(ShouldThrow::kThrowOnError))
176 177 178
        .Check();
  }

179 180 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
  return throwaway;
}

}  // namespace

RUNTIME_FUNCTION(Runtime_AwaitPromisesInit) {
  DCHECK_EQ(5, args.length());
  HandleScope scope(isolate);
  CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 1);
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, outer_promise, 2);
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, reject_handler, 3);
  CONVERT_BOOLEAN_ARG_CHECKED(is_predicted_as_caught, 4);
  return *AwaitPromisesInitCommon(isolate, value, promise, outer_promise,
                                  reject_handler, is_predicted_as_caught);
}

RUNTIME_FUNCTION(Runtime_AwaitPromisesInitOld) {
  DCHECK_EQ(5, args.length());
  HandleScope scope(isolate);
  CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 1);
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, outer_promise, 2);
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, reject_handler, 3);
  CONVERT_BOOLEAN_ARG_CHECKED(is_predicted_as_caught, 4);

  // Fire the init hook for the wrapper promise (that we created for the
  // {value} previously).
  isolate->RunPromiseHook(PromiseHookType::kInit, promise, outer_promise);
  return *AwaitPromisesInitCommon(isolate, value, promise, outer_promise,
                                  reject_handler, is_predicted_as_caught);
210 211
}

212 213 214
RUNTIME_FUNCTION(Runtime_PromiseHookBefore) {
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
215
  CONVERT_ARG_HANDLE_CHECKED(JSReceiver, maybe_promise, 0);
216 217
  if (!maybe_promise->IsJSPromise())
    return ReadOnlyRoots(isolate).undefined_value();
218 219
  Handle<JSPromise> promise = Handle<JSPromise>::cast(maybe_promise);
  if (isolate->debug()->is_active()) isolate->PushPromise(promise);
220 221
  isolate->RunPromiseHook(PromiseHookType::kBefore, promise,
                          isolate->factory()->undefined_value());
222
  return ReadOnlyRoots(isolate).undefined_value();
223 224 225 226 227
}

RUNTIME_FUNCTION(Runtime_PromiseHookAfter) {
  HandleScope scope(isolate);
  DCHECK_EQ(1, args.length());
228
  CONVERT_ARG_HANDLE_CHECKED(JSReceiver, maybe_promise, 0);
229 230
  if (!maybe_promise->IsJSPromise())
    return ReadOnlyRoots(isolate).undefined_value();
231 232
  Handle<JSPromise> promise = Handle<JSPromise>::cast(maybe_promise);
  if (isolate->debug()->is_active()) isolate->PopPromise();
233 234
  isolate->RunPromiseHook(PromiseHookType::kAfter, promise,
                          isolate->factory()->undefined_value());
235
  return ReadOnlyRoots(isolate).undefined_value();
236 237
}

238 239 240 241 242 243
RUNTIME_FUNCTION(Runtime_RejectPromise) {
  HandleScope scope(isolate);
  DCHECK_EQ(3, args.length());
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
  CONVERT_ARG_HANDLE_CHECKED(Object, reason, 1);
  CONVERT_ARG_HANDLE_CHECKED(Oddball, debug_event, 2);
244 245
  return *JSPromise::Reject(promise, reason,
                            debug_event->BooleanValue(isolate));
246 247 248 249 250 251 252 253 254 255 256 257 258
}

RUNTIME_FUNCTION(Runtime_ResolvePromise) {
  HandleScope scope(isolate);
  DCHECK_EQ(2, args.length());
  CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
  CONVERT_ARG_HANDLE_CHECKED(Object, resolution, 1);
  Handle<Object> result;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
                                     JSPromise::Resolve(promise, resolution));
  return *result;
}

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
// A helper function to be called when constructing AggregateError objects. This
// takes care of the Error-related construction, e.g., stack traces.
RUNTIME_FUNCTION(Runtime_ConstructAggregateErrorHelper) {
  HandleScope scope(isolate);
  DCHECK_EQ(3, args.length());
  CONVERT_ARG_HANDLE_CHECKED(JSFunction, target, 0);
  CONVERT_ARG_HANDLE_CHECKED(Object, new_target, 1);
  CONVERT_ARG_HANDLE_CHECKED(Object, message, 2);

  DCHECK_EQ(*target, *isolate->aggregate_error_function());

  Handle<Object> result;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
      isolate, result,
      ErrorUtils::Construct(isolate, target, new_target, message));
  return *result;
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
// A helper function to be called when constructing AggregateError objects. This
// takes care of the Error-related construction, e.g., stack traces.
RUNTIME_FUNCTION(Runtime_ConstructInternalAggregateErrorHelper) {
  HandleScope scope(isolate);
  DCHECK_GE(args.length(), 1);
  CONVERT_ARG_HANDLE_CHECKED(Smi, message, 0);

  Handle<Object> arg0;
  if (args.length() >= 2) {
    DCHECK(args[1].IsObject());
    arg0 = args.at<Object>(1);
  }

  Handle<Object> arg1;
  if (args.length() >= 3) {
    DCHECK(args[2].IsObject());
    arg1 = args.at<Object>(2);
  }

  Handle<Object> arg2;
  if (args.length() >= 4) {
    CHECK(args[3].IsObject());
    arg2 = args.at<Object>(3);
  }

  Handle<Object> message_string = MessageFormatter::Format(
      isolate, MessageTemplate(message->value()), arg0, arg1, arg2);

  Handle<Object> result;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
      isolate, result,
      ErrorUtils::Construct(isolate, isolate->aggregate_error_function(),
                            isolate->aggregate_error_function(),
                            message_string));
  return *result;
}

314 315
}  // namespace internal
}  // namespace v8