diff options
Diffstat (limited to 'deps/v8/src/runtime/runtime-promise.cc')
-rw-r--r-- | deps/v8/src/runtime/runtime-promise.cc | 270 |
1 files changed, 162 insertions, 108 deletions
diff --git a/deps/v8/src/runtime/runtime-promise.cc b/deps/v8/src/runtime/runtime-promise.cc index 226993a50e..ec340e5b0a 100644 --- a/deps/v8/src/runtime/runtime-promise.cc +++ b/deps/v8/src/runtime/runtime-promise.cc @@ -1,27 +1,28 @@ // 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. - #include "src/runtime/runtime-utils.h" #include "src/debug/debug.h" #include "src/elements.h" -#include "src/promise-utils.h" namespace v8 { namespace internal { namespace { -void PromiseRejectEvent(Isolate* isolate, Handle<JSReceiver> promise, +void PromiseRejectEvent(Isolate* isolate, Handle<JSPromise> promise, Handle<Object> rejected_promise, Handle<Object> value, bool debug_event) { + isolate->RunPromiseHook(PromiseHookType::kResolve, promise, + isolate->factory()->undefined_value()); + if (isolate->debug()->is_active() && debug_event) { isolate->debug()->OnPromiseReject(rejected_promise, value); } - Handle<Symbol> key = isolate->factory()->promise_has_handler_symbol(); - // Do not report if we actually have a handler. - if (JSReceiver::GetDataProperty(promise, key)->IsUndefined(isolate)) { + + // Report only if we don't actually have a handler. + if (!promise->has_handler()) { isolate->ReportPromiseReject(Handle<JSObject>::cast(promise), value, v8::kPromiseRejectWithNoHandler); } @@ -30,9 +31,9 @@ void PromiseRejectEvent(Isolate* isolate, Handle<JSReceiver> promise, } // namespace RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) { - DCHECK(args.length() == 2); + DCHECK_EQ(2, args.length()); HandleScope scope(isolate); - CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); CONVERT_ARG_HANDLE_CHECKED(Object, value, 1); Handle<Object> rejected_promise = promise; @@ -41,142 +42,126 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) { // undefined, which will be interpreted by PromiseRejectEvent // as being a caught exception event. rejected_promise = isolate->GetPromiseOnStackOnThrow(); + isolate->debug()->OnAsyncTaskEvent( + debug::kDebugEnqueuePromiseReject, + isolate->debug()->NextAsyncTaskId(promise)); } PromiseRejectEvent(isolate, promise, rejected_promise, value, true); return isolate->heap()->undefined_value(); } +RUNTIME_FUNCTION(Runtime_ReportPromiseReject) { + DCHECK_EQ(2, args.length()); + HandleScope scope(isolate); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, value, 1); + isolate->ReportPromiseReject(Handle<JSObject>::cast(promise), value, + v8::kPromiseRejectWithNoHandler); + return isolate->heap()->undefined_value(); +} + RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) { - DCHECK(args.length() == 1); + DCHECK_EQ(1, args.length()); HandleScope scope(isolate); - CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); - Handle<Symbol> key = isolate->factory()->promise_has_handler_symbol(); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); // At this point, no revocation has been issued before - CHECK(JSReceiver::GetDataProperty(promise, key)->IsUndefined(isolate)); + CHECK(!promise->has_handler()); isolate->ReportPromiseReject(promise, Handle<Object>(), v8::kPromiseHandlerAddedAfterReject); return isolate->heap()->undefined_value(); } namespace { -void EnqueuePromiseReactionJob(Isolate* isolate, Handle<Object> value, - Handle<Object> tasks, Handle<Object> deferred, - Handle<Object> status) { - Handle<Object> debug_id = isolate->factory()->undefined_value(); - Handle<Object> debug_name = isolate->factory()->undefined_value(); - if (isolate->debug()->is_active()) { - MaybeHandle<Object> maybe_result; - Handle<Object> argv[] = {deferred, status}; - maybe_result = Execution::TryCall( - isolate, isolate->promise_debug_get_info(), - isolate->factory()->undefined_value(), arraysize(argv), argv); - Handle<Object> result; - if ((maybe_result).ToHandle(&result)) { - CHECK(result->IsJSArray()); - Handle<JSArray> array = Handle<JSArray>::cast(result); - ElementsAccessor* accessor = array->GetElementsAccessor(); - DCHECK(accessor->HasElement(array, 0)); - DCHECK(accessor->HasElement(array, 1)); - debug_id = accessor->Get(array, 0); - debug_name = accessor->Get(array, 1); - } + +// In an async function, reuse the existing stack related to the outer +// Promise. Otherwise, e.g. in a direct call to then, save a new stack. +// Promises with multiple reactions with one or more of them being async +// functions will not get a good stack trace, as async functions require +// different stacks from direct Promise use, but we save and restore a +// stack once for all reactions. +// +// If this isn't a case of async function, we return false, otherwise +// we set the correct id and return true. +// +// TODO(littledan): Improve this case. +bool GetDebugIdForAsyncFunction(Isolate* isolate, + Handle<PromiseReactionJobInfo> info, + int* debug_id) { + // deferred_promise can be Undefined, FixedArray or userland promise object. + if (!info->deferred_promise()->IsJSPromise()) { + return false; } - Handle<PromiseReactionJobInfo> info = - isolate->factory()->NewPromiseReactionJobInfo(value, tasks, deferred, - debug_id, debug_name, - isolate->native_context()); - isolate->EnqueueMicrotask(info); -} -void PromiseFulfill(Isolate* isolate, Handle<JSReceiver> promise, - Handle<Smi> status, Handle<Object> value, - Handle<Symbol> reaction) { - Handle<Object> tasks = JSReceiver::GetDataProperty(promise, reaction); - if (!tasks->IsUndefined(isolate)) { - Handle<Object> deferred = JSReceiver::GetDataProperty( - promise, isolate->factory()->promise_deferred_reaction_symbol()); - EnqueuePromiseReactionJob(isolate, value, tasks, deferred, status); + Handle<JSPromise> deferred_promise(JSPromise::cast(info->deferred_promise()), + isolate); + Handle<Symbol> handled_by_symbol = + isolate->factory()->promise_handled_by_symbol(); + Handle<Object> handled_by_promise = + JSObject::GetDataProperty(deferred_promise, handled_by_symbol); + + if (!handled_by_promise->IsJSPromise()) { + return false; } -} -} // namespace -RUNTIME_FUNCTION(Runtime_PromiseReject) { - DCHECK(args.length() == 3); - HandleScope scope(isolate); - CONVERT_ARG_HANDLE_CHECKED(JSReceiver, promise, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, reason, 1); - CONVERT_BOOLEAN_ARG_CHECKED(debug_event, 2); + Handle<JSPromise> handled_by_promise_js = + Handle<JSPromise>::cast(handled_by_promise); + Handle<Symbol> async_stack_id_symbol = + isolate->factory()->promise_async_stack_id_symbol(); + Handle<Object> id = + JSObject::GetDataProperty(handled_by_promise_js, async_stack_id_symbol); - PromiseRejectEvent(isolate, promise, promise, reason, debug_event); + // id can be Undefined or Smi. + if (!id->IsSmi()) { + return false; + } - Handle<Smi> status = handle(Smi::FromInt(kPromiseRejected), isolate); - Handle<Symbol> reaction = - isolate->factory()->promise_reject_reactions_symbol(); - PromiseFulfill(isolate, promise, status, reason, reaction); - return isolate->heap()->undefined_value(); + *debug_id = Handle<Smi>::cast(id)->value(); + return true; } -RUNTIME_FUNCTION(Runtime_PromiseFulfill) { - DCHECK(args.length() == 4); - HandleScope scope(isolate); - CONVERT_ARG_HANDLE_CHECKED(JSReceiver, promise, 0); - CONVERT_ARG_HANDLE_CHECKED(Smi, status, 1); - CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); - CONVERT_ARG_HANDLE_CHECKED(Symbol, reaction, 3); - PromiseFulfill(isolate, promise, status, value, reaction); - return isolate->heap()->undefined_value(); +void SetDebugInfo(Isolate* isolate, Handle<JSPromise> promise, + Handle<PromiseReactionJobInfo> info, int status) { + int id = kDebugPromiseNoID; + if (!GetDebugIdForAsyncFunction(isolate, info, &id)) { + id = isolate->debug()->NextAsyncTaskId(promise); + DCHECK(status != v8::Promise::kPending); + } + info->set_debug_id(id); } +void EnqueuePromiseReactionJob(Isolate* isolate, Handle<JSPromise> promise, + Handle<PromiseReactionJobInfo> info, + int status) { + if (isolate->debug()->is_active()) { + SetDebugInfo(isolate, promise, info, status); + } + + isolate->EnqueueMicrotask(info); +} + +} // namespace + RUNTIME_FUNCTION(Runtime_EnqueuePromiseReactionJob) { HandleScope scope(isolate); - DCHECK(args.length() == 4); - CONVERT_ARG_HANDLE_CHECKED(Object, value, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, tasks, 1); - CONVERT_ARG_HANDLE_CHECKED(Object, deferred, 2); - CONVERT_ARG_HANDLE_CHECKED(Object, status, 3); - EnqueuePromiseReactionJob(isolate, value, tasks, deferred, status); + DCHECK_EQ(3, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); + CONVERT_ARG_HANDLE_CHECKED(PromiseReactionJobInfo, info, 1); + CONVERT_SMI_ARG_CHECKED(status, 2); + EnqueuePromiseReactionJob(isolate, promise, info, status); return isolate->heap()->undefined_value(); } RUNTIME_FUNCTION(Runtime_EnqueuePromiseResolveThenableJob) { HandleScope scope(isolate); - DCHECK(args.length() == 3); - CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); - CONVERT_ARG_HANDLE_CHECKED(JSReceiver, resolution, 1); - CONVERT_ARG_HANDLE_CHECKED(JSReceiver, then, 2); - - // TODO(gsathya): Add fast path for native promises with unmodified - // PromiseThen (which don't need these resolving functions, but - // instead can just call resolve/reject directly). - Handle<JSFunction> resolve, reject; - PromiseUtils::CreateResolvingFunctions( - isolate, promise, isolate->factory()->false_value(), &resolve, &reject); - - Handle<Object> debug_id, debug_name; - if (isolate->debug()->is_active()) { - debug_id = - handle(Smi::FromInt(isolate->GetNextDebugMicrotaskId()), isolate); - debug_name = isolate->factory()->PromiseResolveThenableJob_string(); - isolate->debug()->OnAsyncTaskEvent(isolate->factory()->enqueue_string(), - debug_id, - Handle<String>::cast(debug_name)); - } else { - debug_id = isolate->factory()->undefined_value(); - debug_name = isolate->factory()->undefined_value(); - } - - Handle<PromiseResolveThenableJobInfo> info = - isolate->factory()->NewPromiseResolveThenableJobInfo( - resolution, then, resolve, reject, debug_id, debug_name, - isolate->native_context()); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(PromiseResolveThenableJobInfo, info, 0); isolate->EnqueueMicrotask(info); - return isolate->heap()->undefined_value(); } RUNTIME_FUNCTION(Runtime_EnqueueMicrotask) { HandleScope scope(isolate); - DCHECK(args.length() == 1); + DCHECK_EQ(1, args.length()); CONVERT_ARG_HANDLE_CHECKED(JSFunction, microtask, 0); isolate->EnqueueMicrotask(microtask); return isolate->heap()->undefined_value(); @@ -184,10 +169,79 @@ RUNTIME_FUNCTION(Runtime_EnqueueMicrotask) { RUNTIME_FUNCTION(Runtime_RunMicrotasks) { HandleScope scope(isolate); - DCHECK(args.length() == 0); + DCHECK_EQ(0, args.length()); isolate->RunMicrotasks(); return isolate->heap()->undefined_value(); } +RUNTIME_FUNCTION(Runtime_PromiseStatus) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); + + return Smi::FromInt(promise->status()); +} + +RUNTIME_FUNCTION(Runtime_PromiseResult) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); + return promise->result(); +} + +RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) { + SealHandleScope shs(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_CHECKED(JSPromise, promise, 0); + + promise->set_has_handler(true); + return isolate->heap()->undefined_value(); +} + +RUNTIME_FUNCTION(Runtime_PromiseMarkHandledHint) { + SealHandleScope shs(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_CHECKED(JSPromise, promise, 0); + + promise->set_handled_hint(true); + return isolate->heap()->undefined_value(); +} + +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); + return isolate->heap()->undefined_value(); +} + +RUNTIME_FUNCTION(Runtime_PromiseHookResolve) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); + isolate->RunPromiseHook(PromiseHookType::kResolve, promise, + isolate->factory()->undefined_value()); + return isolate->heap()->undefined_value(); +} + +RUNTIME_FUNCTION(Runtime_PromiseHookBefore) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); + isolate->RunPromiseHook(PromiseHookType::kBefore, promise, + isolate->factory()->undefined_value()); + return isolate->heap()->undefined_value(); +} + +RUNTIME_FUNCTION(Runtime_PromiseHookAfter) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); + isolate->RunPromiseHook(PromiseHookType::kAfter, promise, + isolate->factory()->undefined_value()); + return isolate->heap()->undefined_value(); +} + } // namespace internal } // namespace v8 |