[promises] Move PromiseHasUserDefinedRejectHandler to c++
BUG=v8:5343 Review-Url: https://codereview.chromium.org/2604483002 Cr-Commit-Position: refs/heads/master@{#41947}
This commit is contained in:
parent
1344e3a9ca
commit
0f5c69c5ed
@ -105,8 +105,6 @@ enum ContextLookupFlags {
|
||||
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
|
||||
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
|
||||
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
|
||||
V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \
|
||||
promise_has_user_defined_reject_handler) \
|
||||
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
|
||||
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
|
||||
V(PROMISE_INTERNAL_REJECT_INDEX, JSFunction, promise_internal_reject) \
|
||||
|
@ -218,7 +218,6 @@
|
||||
V(promise_debug_marker_symbol) \
|
||||
V(promise_forwarding_handler_symbol) \
|
||||
V(promise_handled_by_symbol) \
|
||||
V(promise_handled_hint_symbol) \
|
||||
V(sealed_symbol) \
|
||||
V(stack_trace_symbol) \
|
||||
V(strict_function_transition_symbol) \
|
||||
|
101
src/isolate.cc
101
src/isolate.cc
@ -1808,23 +1808,91 @@ void Isolate::PopPromise() {
|
||||
global_handles()->Destroy(global_promise.location());
|
||||
}
|
||||
|
||||
bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<Object> promise) {
|
||||
Handle<JSFunction> fun = promise_has_user_defined_reject_handler();
|
||||
Handle<Object> has_reject_handler;
|
||||
// If we are, e.g., overflowing the stack, don't try to call out to JS
|
||||
if (!AllowJavascriptExecution::IsAllowed(this)) return false;
|
||||
// Call the registered function to check for a handler
|
||||
if (Execution::TryCall(this, fun, promise, 0, NULL)
|
||||
.ToHandle(&has_reject_handler)) {
|
||||
return has_reject_handler->IsTrue(this);
|
||||
namespace {
|
||||
bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate,
|
||||
Handle<JSPromise> promise);
|
||||
|
||||
bool PromiseHandlerCheck(Isolate* isolate, Handle<JSReceiver> handler,
|
||||
Handle<JSObject> deferred) {
|
||||
// Recurse to the forwarding Promise, if any. This may be due to
|
||||
// - await reaction forwarding to the throwaway Promise, which has
|
||||
// a dependency edge to the outer Promise.
|
||||
// - PromiseIdResolveHandler forwarding to the output of .then
|
||||
// - Promise.all/Promise.race forwarding to a throwaway Promise, which
|
||||
// has a dependency edge to the generated outer Promise.
|
||||
Handle<Symbol> key = isolate->factory()->promise_forwarding_handler_symbol();
|
||||
Handle<Object> forwarding_handler = JSReceiver::GetDataProperty(handler, key);
|
||||
if (forwarding_handler->IsUndefined(isolate)) {
|
||||
return true;
|
||||
}
|
||||
// If an exception is thrown in the course of execution of this built-in
|
||||
// function, it indicates either a bug, or a synthetic uncatchable
|
||||
// exception in the shutdown path. In either case, it's OK to predict either
|
||||
// way in DevTools.
|
||||
|
||||
// TODO(gsathya): Remove this once we get rid of deferred objects.
|
||||
Handle<String> promise_str = isolate->factory()->promise_string();
|
||||
Handle<Object> deferred_promise_obj =
|
||||
JSObject::GetDataProperty(deferred, promise_str);
|
||||
if (!deferred_promise_obj->IsJSPromise()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return InternalPromiseHasUserDefinedRejectHandler(
|
||||
isolate, Handle<JSPromise>::cast(deferred_promise_obj));
|
||||
|
||||
// Otherwise, this is a real reject handler for the Promise
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalPromiseHasUserDefinedRejectHandler(Isolate* isolate,
|
||||
Handle<JSPromise> promise) {
|
||||
// If this promise was marked as being handled by a catch block
|
||||
// in an async function, then it has a user-defined reject handler.
|
||||
if (promise->handled_hint()) return true;
|
||||
|
||||
// If this Promise is subsumed by another Promise (a Promise resolved
|
||||
// with another Promise, or an intermediate, hidden, throwaway Promise
|
||||
// within async/await), then recurse on the outer Promise.
|
||||
// In this case, the dependency is one possible way that the Promise
|
||||
// could be resolved, so it does not subsume the other following cases.
|
||||
Handle<Symbol> key = isolate->factory()->promise_handled_by_symbol();
|
||||
Handle<Object> outer_promise_obj = JSObject::GetDataProperty(promise, key);
|
||||
if (outer_promise_obj->IsJSPromise() &&
|
||||
InternalPromiseHasUserDefinedRejectHandler(
|
||||
isolate, Handle<JSPromise>::cast(outer_promise_obj))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Handle<Object> queue(promise->reject_reactions(), isolate);
|
||||
Handle<Object> deferred(promise->deferred(), isolate);
|
||||
|
||||
if (queue->IsUndefined(isolate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queue->IsCallable()) {
|
||||
return PromiseHandlerCheck(isolate, Handle<JSReceiver>::cast(queue),
|
||||
Handle<JSObject>::cast(deferred));
|
||||
}
|
||||
|
||||
Handle<FixedArray> queue_arr = Handle<FixedArray>::cast(queue);
|
||||
Handle<FixedArray> deferred_arr = Handle<FixedArray>::cast(deferred);
|
||||
for (int i = 0; i < deferred_arr->length(); i++) {
|
||||
Handle<JSReceiver> queue_item(JSReceiver::cast(queue_arr->get(i)));
|
||||
Handle<JSObject> deferred_item(JSObject::cast(deferred_arr->get(i)));
|
||||
if (PromiseHandlerCheck(isolate, queue_item, deferred_item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Isolate::PromiseHasUserDefinedRejectHandler(Handle<Object> promise) {
|
||||
if (!promise->IsJSPromise()) return false;
|
||||
return InternalPromiseHasUserDefinedRejectHandler(
|
||||
this, Handle<JSPromise>::cast(promise));
|
||||
}
|
||||
|
||||
Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
|
||||
Handle<Object> undefined = factory()->undefined_value();
|
||||
ThreadLocalTop* tltop = thread_local_top();
|
||||
@ -1842,7 +1910,7 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
|
||||
continue;
|
||||
case HandlerTable::CAUGHT:
|
||||
case HandlerTable::DESUGARING:
|
||||
if (retval->IsJSObject()) {
|
||||
if (retval->IsJSPromise()) {
|
||||
// Caught the result of an inner async/await invocation.
|
||||
// Mark the inner promise as caught in the "synchronous case" so
|
||||
// that Debug::OnException will see. In the synchronous case,
|
||||
@ -1850,10 +1918,7 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
|
||||
// await, the function which has this exception event has not yet
|
||||
// returned, so the generated Promise has not yet been marked
|
||||
// by AsyncFunctionAwaitCaught with promiseHandledHintSymbol.
|
||||
Handle<Symbol> key = factory()->promise_handled_hint_symbol();
|
||||
JSObject::SetProperty(Handle<JSObject>::cast(retval), key,
|
||||
factory()->true_value(), STRICT)
|
||||
.Assert();
|
||||
Handle<JSPromise>::cast(retval)->set_handled_hint(true);
|
||||
}
|
||||
return retval;
|
||||
case HandlerTable::PROMISE:
|
||||
|
@ -32,8 +32,6 @@ var promiseHandledBySymbol =
|
||||
utils.ImportNow("promise_handled_by_symbol");
|
||||
var promiseForwardingHandlerSymbol =
|
||||
utils.ImportNow("promise_forwarding_handler_symbol");
|
||||
var promiseHandledHintSymbol =
|
||||
utils.ImportNow("promise_handled_hint_symbol");
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@ -114,7 +112,7 @@ function AsyncFunctionAwaitUncaught(generator, awaited, outerPromise) {
|
||||
// prediction indicates that there is a locally surrounding catch block
|
||||
function AsyncFunctionAwaitCaught(generator, awaited, outerPromise) {
|
||||
if (DEBUG_IS_ACTIVE && %is_promise(awaited)) {
|
||||
SET_PRIVATE(awaited, promiseHandledHintSymbol, true);
|
||||
%PromiseMarkHandledHint(awaited);
|
||||
}
|
||||
AsyncFunctionAwait(generator, awaited, outerPromise);
|
||||
}
|
||||
|
@ -18,8 +18,6 @@ var promiseHandledBySymbol =
|
||||
utils.ImportNow("promise_handled_by_symbol");
|
||||
var promiseForwardingHandlerSymbol =
|
||||
utils.ImportNow("promise_forwarding_handler_symbol");
|
||||
var promiseHandledHintSymbol =
|
||||
utils.ImportNow("promise_handled_hint_symbol");
|
||||
var ObjectHasOwnProperty; // Used by HAS_PRIVATE.
|
||||
var GlobalPromise = global.Promise;
|
||||
|
||||
@ -263,68 +261,6 @@ function PromiseRace(iterable) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
||||
// Utility for debugger
|
||||
|
||||
function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) {
|
||||
// Recurse to the forwarding Promise, if any. This may be due to
|
||||
// - await reaction forwarding to the throwaway Promise, which has
|
||||
// a dependency edge to the outer Promise.
|
||||
// - PromiseIdResolveHandler forwarding to the output of .then
|
||||
// - Promise.all/Promise.race forwarding to a throwaway Promise, which
|
||||
// has a dependency edge to the generated outer Promise.
|
||||
if (GET_PRIVATE(handler, promiseForwardingHandlerSymbol)) {
|
||||
return PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise);
|
||||
}
|
||||
|
||||
// Otherwise, this is a real reject handler for the Promise
|
||||
return true;
|
||||
}
|
||||
|
||||
function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
|
||||
// If this promise was marked as being handled by a catch block
|
||||
// in an async function, then it has a user-defined reject handler.
|
||||
if (GET_PRIVATE(promise, promiseHandledHintSymbol)) return true;
|
||||
|
||||
// If this Promise is subsumed by another Promise (a Promise resolved
|
||||
// with another Promise, or an intermediate, hidden, throwaway Promise
|
||||
// within async/await), then recurse on the outer Promise.
|
||||
// In this case, the dependency is one possible way that the Promise
|
||||
// could be resolved, so it does not subsume the other following cases.
|
||||
var outerPromise = GET_PRIVATE(promise, promiseHandledBySymbol);
|
||||
if (outerPromise &&
|
||||
PromiseHasUserDefinedRejectHandlerRecursive(outerPromise)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!%is_promise(promise)) return false;
|
||||
|
||||
var queue = %PromiseRejectReactions(promise);
|
||||
var deferred = %PromiseDeferred(promise);
|
||||
|
||||
if (IS_UNDEFINED(queue)) return false;
|
||||
|
||||
if (!IS_ARRAY(queue)) {
|
||||
return PromiseHasUserDefinedRejectHandlerCheck(queue, deferred);
|
||||
}
|
||||
|
||||
for (var i = 0; i < queue.length; i++) {
|
||||
if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], deferred[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return whether the promise will be handled by a user-defined reject
|
||||
// handler somewhere down the promise chain. For this, we do a depth-first
|
||||
// search for a reject handler that's not the default PromiseIdRejectHandler.
|
||||
// This function also traverses dependencies of one Promise on another,
|
||||
// set up through async/await and Promises resolved with Promises.
|
||||
function PromiseHasUserDefinedRejectHandler() {
|
||||
return PromiseHasUserDefinedRejectHandlerRecursive(this);
|
||||
};
|
||||
|
||||
function MarkPromiseAsHandled(promise) {
|
||||
%PromiseMarkAsHandled(promise);
|
||||
}
|
||||
@ -341,7 +277,6 @@ utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
|
||||
|
||||
%InstallToContext([
|
||||
"promise_create", PromiseCreate,
|
||||
"promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
|
||||
"promise_reject", DoRejectPromise,
|
||||
// TODO(gsathya): Remove this once we update the promise builtin.
|
||||
"promise_internal_reject", RejectPromise,
|
||||
|
@ -7096,6 +7096,7 @@ ACCESSORS(JSPromise, fulfill_reactions, Object, kFulfillReactionsOffset)
|
||||
ACCESSORS(JSPromise, reject_reactions, Object, kRejectReactionsOffset)
|
||||
SMI_ACCESSORS(JSPromise, flags, kFlagsOffset)
|
||||
BOOL_ACCESSORS(JSPromise, flags, has_handler, kHasHandlerBit)
|
||||
BOOL_ACCESSORS(JSPromise, flags, handled_hint, kHandledHintBit)
|
||||
|
||||
ACCESSORS(JSRegExp, data, Object, kDataOffset)
|
||||
ACCESSORS(JSRegExp, flags, Object, kFlagsOffset)
|
||||
|
@ -8655,6 +8655,10 @@ class JSPromise : public JSObject {
|
||||
// [has_handler]: Whether this promise has a reject handler or not.
|
||||
DECL_BOOLEAN_ACCESSORS(has_handler)
|
||||
|
||||
// [handled_hint]: Whether this promise will be handled by a catch
|
||||
// block in an async function.
|
||||
DECL_BOOLEAN_ACCESSORS(handled_hint)
|
||||
|
||||
static const char* Status(int status);
|
||||
|
||||
DECLARE_CAST(JSPromise)
|
||||
@ -8675,6 +8679,7 @@ class JSPromise : public JSObject {
|
||||
|
||||
// Flags layout.
|
||||
static const int kHasHandlerBit = 0;
|
||||
static const int kHandledHintBit = 1;
|
||||
};
|
||||
|
||||
// Regular expressions
|
||||
|
@ -279,7 +279,7 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectReactions) {
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) {
|
||||
HandleScope scope(isolate);
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
|
||||
|
||||
@ -287,6 +287,15 @@ RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PromiseMarkHandledHint) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_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());
|
||||
|
@ -313,6 +313,7 @@ namespace internal {
|
||||
F(PromiseHookBefore, 1, 1) \
|
||||
F(PromiseHookAfter, 1, 1) \
|
||||
F(PromiseMarkAsHandled, 1, 1) \
|
||||
F(PromiseMarkHandledHint, 1, 1) \
|
||||
F(PromiseRejectEventFromStack, 2, 1) \
|
||||
F(PromiseRejectReactions, 1, 1) \
|
||||
F(PromiseRevokeReject, 1, 1) \
|
||||
|
Loading…
Reference in New Issue
Block a user