Commit b1c148b9 authored by gsathya's avatar gsathya Committed by Commit bot

[promisehook] Implement PromiseHook

This adds kInit, kResolve, kBefore and kAfter lifecycle hooks to promises.

This also exposes an API to set the PromiseHook.

BUG=v8:4643

Review-Url: https://codereview.chromium.org/2575313002
Cr-Commit-Position: refs/heads/master@{#41775}
parent ca8d3ba7
...@@ -5756,6 +5756,27 @@ typedef void (*BeforeCallEnteredCallback)(Isolate*); ...@@ -5756,6 +5756,27 @@ typedef void (*BeforeCallEnteredCallback)(Isolate*);
typedef void (*CallCompletedCallback)(Isolate*); typedef void (*CallCompletedCallback)(Isolate*);
typedef void (*DeprecatedCallCompletedCallback)(); typedef void (*DeprecatedCallCompletedCallback)();
/**
* PromiseHook with type kInit is called when a new promise is
* created. When a new promise is created as part of the chain in the
* case of Promise.then or in the intermediate promises created by
* Promise.{race, all}/AsyncFunctionAwait, we pass the parent promise
* otherwise we pass undefined.
*
* PromiseHook with type kResolve is called at the beginning of
* resolve or reject function defined by CreateResolvingFunctions.
*
* PromiseHook with type kBefore is called at the beginning of the
* PromiseReactionJob.
*
* PromiseHook with type kAfter is called right at the end of the
* PromiseReactionJob.
*/
enum class PromiseHookType { kInit, kResolve, kBefore, kAfter };
typedef void (*PromiseHook)(PromiseHookType type, Local<Promise> promise,
Local<Value> parent);
// --- Promise Reject Callback --- // --- Promise Reject Callback ---
enum PromiseRejectEvent { enum PromiseRejectEvent {
kPromiseRejectWithNoHandler = 0, kPromiseRejectWithNoHandler = 0,
...@@ -6866,6 +6887,12 @@ class V8_EXPORT Isolate { ...@@ -6866,6 +6887,12 @@ class V8_EXPORT Isolate {
void RemoveCallCompletedCallback( void RemoveCallCompletedCallback(
DeprecatedCallCompletedCallback callback)); DeprecatedCallCompletedCallback callback));
/**
* Experimental: Set the PromiseHook callback for various promise
* lifecycle events.
*/
void SetPromiseHook(PromiseHook hook);
/** /**
* Set callback to notify about promise reject with no handler, or * Set callback to notify about promise reject with no handler, or
* revocation of such a previous notification once the handler is added. * revocation of such a previous notification once the handler is added.
......
...@@ -8260,6 +8260,10 @@ void Isolate::RemoveCallCompletedCallback( ...@@ -8260,6 +8260,10 @@ void Isolate::RemoveCallCompletedCallback(
reinterpret_cast<CallCompletedCallback>(callback)); reinterpret_cast<CallCompletedCallback>(callback));
} }
void Isolate::SetPromiseHook(PromiseHook hook) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->SetPromiseHook(hook);
}
void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) { void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) {
if (callback == NULL) return; if (callback == NULL) return;
......
...@@ -1582,9 +1582,8 @@ ExternalReference ExternalReference::is_tail_call_elimination_enabled_address( ...@@ -1582,9 +1582,8 @@ ExternalReference ExternalReference::is_tail_call_elimination_enabled_address(
return ExternalReference(isolate->is_tail_call_elimination_enabled_address()); return ExternalReference(isolate->is_tail_call_elimination_enabled_address());
} }
ExternalReference ExternalReference::is_promisehook_enabled_address( ExternalReference ExternalReference::promise_hook_address(Isolate* isolate) {
Isolate* isolate) { return ExternalReference(isolate->promise_hook_address());
return ExternalReference(isolate->is_promisehook_enabled_address());
} }
ExternalReference ExternalReference::debug_is_active_address( ExternalReference ExternalReference::debug_is_active_address(
......
...@@ -1047,7 +1047,7 @@ class ExternalReference BASE_EMBEDDED { ...@@ -1047,7 +1047,7 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference invoke_function_callback(Isolate* isolate); static ExternalReference invoke_function_callback(Isolate* isolate);
static ExternalReference invoke_accessor_getter_callback(Isolate* isolate); static ExternalReference invoke_accessor_getter_callback(Isolate* isolate);
static ExternalReference is_promisehook_enabled_address(Isolate* isolate); static ExternalReference promise_hook_address(Isolate* isolate);
V8_EXPORT_PRIVATE static ExternalReference runtime_function_table_address( V8_EXPORT_PRIVATE static ExternalReference runtime_function_table_address(
Isolate* isolate); Isolate* isolate);
......
...@@ -1890,7 +1890,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1890,7 +1890,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
{ // Internal: PromiseInternalConstructor { // Internal: PromiseInternalConstructor
Handle<JSFunction> function = Handle<JSFunction> function =
SimpleCreateFunction(isolate, factory->empty_string(), SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kPromiseInternalConstructor, 0, false); Builtins::kPromiseInternalConstructor, 1, false);
InstallWithIntrinsicDefaultProto( InstallWithIntrinsicDefaultProto(
isolate, function, Context::PROMISE_INTERNAL_CONSTRUCTOR_INDEX); isolate, function, Context::PROMISE_INTERNAL_CONSTRUCTOR_INDEX);
} }
......
...@@ -371,6 +371,12 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context, ...@@ -371,6 +371,12 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred), Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred),
if_rejectpromise(this, Label::kDeferred); if_rejectpromise(this, Label::kDeferred);
Label cycle_check(this);
GotoUnless(IsPromiseHookEnabled(), &cycle_check);
CallRuntime(Runtime::kPromiseHookResolve, context, promise);
Goto(&cycle_check);
Bind(&cycle_check);
// 6. If SameValue(resolution, promise) is true, then // 6. If SameValue(resolution, promise) is true, then
GotoIf(SameValue(promise, result, context), &if_cycle); GotoIf(SameValue(promise, result, context), &if_cycle);
...@@ -591,6 +597,9 @@ TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { ...@@ -591,6 +597,9 @@ TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
{ {
Node* const instance = AllocateJSPromise(context); Node* const instance = AllocateJSPromise(context);
var_result.Bind(instance); var_result.Bind(instance);
GotoUnless(IsPromiseHookEnabled(), &init);
CallRuntime(Runtime::kPromiseHookInit, context, instance,
UndefinedConstant());
Goto(&init); Goto(&init);
} }
...@@ -671,9 +680,17 @@ TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { ...@@ -671,9 +680,17 @@ TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
} }
TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) { TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) {
Node* const context = Parameter(3); Node* const parent = Parameter(1);
Node* const context = Parameter(4);
Node* const instance = AllocateJSPromise(context); Node* const instance = AllocateJSPromise(context);
PromiseInit(instance); PromiseInit(instance);
Label out(this);
GotoUnless(IsPromiseHookEnabled(), &out);
CallRuntime(Runtime::kPromiseHookInit, context, instance, parent);
Goto(&out);
Bind(&out);
Return(instance); Return(instance);
} }
...@@ -684,6 +701,13 @@ TF_BUILTIN(PromiseCreateAndSet, PromiseBuiltinsAssembler) { ...@@ -684,6 +701,13 @@ TF_BUILTIN(PromiseCreateAndSet, PromiseBuiltinsAssembler) {
Node* const instance = AllocateJSPromise(context); Node* const instance = AllocateJSPromise(context);
PromiseSet(instance, status, result); PromiseSet(instance, status, result);
Label out(this);
GotoUnless(IsPromiseHookEnabled(), &out);
CallRuntime(Runtime::kPromiseHookInit, context, instance,
UndefinedConstant());
Goto(&out);
Bind(&out);
Return(instance); Return(instance);
} }
...@@ -752,7 +776,7 @@ TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) { ...@@ -752,7 +776,7 @@ TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) {
native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX); native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX);
Node* const capability = Node* const capability =
CallJS(call_callable, context, promise_internal_capability, CallJS(call_callable, context, promise_internal_capability,
UndefinedConstant()); UndefinedConstant(), promise);
var_deferred.Bind(capability); var_deferred.Bind(capability);
Goto(&perform_promise_then); Goto(&perform_promise_then);
} }
...@@ -849,6 +873,7 @@ TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) { ...@@ -849,6 +873,7 @@ TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) {
} }
TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
Node* const promise = Parameter(1);
Node* const value = Parameter(2); Node* const value = Parameter(2);
Node* const handler = Parameter(3); Node* const handler = Parameter(3);
Node* const deferred = Parameter(4); Node* const deferred = Parameter(4);
...@@ -865,13 +890,17 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { ...@@ -865,13 +890,17 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
Variable var_reason(this, MachineRepresentation::kTagged); Variable var_reason(this, MachineRepresentation::kTagged);
Node* const is_debug_active = IsDebugActive(); Node* const is_debug_active = IsDebugActive();
Label run_handler(this), if_rejectpromise(this), Label run_handler(this), if_rejectpromise(this), promisehook_before(this),
debug_push(this, Label::kDeferred), debug_pop(this, Label::kDeferred); promisehook_after(this), debug_pop(this);
Branch(is_debug_active, &debug_push, &run_handler);
Bind(&debug_push); GotoUnless(is_debug_active, &promisehook_before);
CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise);
Goto(&promisehook_before);
Bind(&promisehook_before);
{ {
CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); GotoUnless(IsPromiseHookEnabled(), &run_handler);
CallRuntime(Runtime::kPromiseHookBefore, context, promise);
Goto(&run_handler); Goto(&run_handler);
} }
...@@ -893,14 +922,15 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { ...@@ -893,14 +922,15 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
Branch(IsUndefined(on_resolve), &if_internalhandler, &if_customhandler); Branch(IsUndefined(on_resolve), &if_internalhandler, &if_customhandler);
Bind(&if_internalhandler); Bind(&if_internalhandler);
InternalResolvePromise(context, deferred_promise, result, &debug_pop); InternalResolvePromise(context, deferred_promise, result,
&promisehook_after);
Bind(&if_customhandler); Bind(&if_customhandler);
{ {
Node* const maybe_exception = CallJS(call_callable, context, on_resolve, Node* const maybe_exception = CallJS(call_callable, context, on_resolve,
UndefinedConstant(), result); UndefinedConstant(), result);
GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
Goto(&debug_pop); Goto(&promisehook_after);
} }
} }
...@@ -914,6 +944,13 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) { ...@@ -914,6 +944,13 @@ TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate);
CallStub(promise_handle_reject, context, deferred_promise, on_reject, CallStub(promise_handle_reject, context, deferred_promise, on_reject,
var_reason.value()); var_reason.value());
Goto(&promisehook_after);
}
Bind(&promisehook_after);
{
GotoUnless(IsPromiseHookEnabled(), &debug_pop);
CallRuntime(Runtime::kPromiseHookAfter, context, promise);
Goto(&debug_pop); Goto(&debug_pop);
} }
......
...@@ -566,7 +566,7 @@ namespace internal { ...@@ -566,7 +566,7 @@ namespace internal {
\ \
/* Promise */ \ /* Promise */ \
TFJ(PromiseConstructor, 1) \ TFJ(PromiseConstructor, 1) \
TFJ(PromiseInternalConstructor, 0) \ TFJ(PromiseInternalConstructor, 1) \
TFJ(IsPromise, 1) \ TFJ(IsPromise, 1) \
CPP(CreateResolvingFunctions) \ CPP(CreateResolvingFunctions) \
TFJ(PromiseResolveClosure, 1) \ TFJ(PromiseResolveClosure, 1) \
......
...@@ -8247,11 +8247,10 @@ Node* CodeStubAssembler::IsDebugActive() { ...@@ -8247,11 +8247,10 @@ Node* CodeStubAssembler::IsDebugActive() {
} }
Node* CodeStubAssembler::IsPromiseHookEnabled() { Node* CodeStubAssembler::IsPromiseHookEnabled() {
Node* const is_promisehook_enabled = Node* const promise_hook = Load(
Load(MachineType::Uint8(), MachineType::Pointer(),
ExternalConstant( ExternalConstant(ExternalReference::promise_hook_address(isolate())));
ExternalReference::is_promisehook_enabled_address(isolate()))); return WordNotEqual(promise_hook, IntPtrConstant(0));
return Word32NotEqual(is_promisehook_enabled, Int32Constant(0));
} }
Node* CodeStubAssembler::AllocateJSPromise(Node* context) { Node* CodeStubAssembler::AllocateJSPromise(Node* context) {
......
...@@ -253,8 +253,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) { ...@@ -253,8 +253,8 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) {
"double_absolute_constant"); "double_absolute_constant");
Add(ExternalReference::address_of_double_neg_constant().address(), Add(ExternalReference::address_of_double_neg_constant().address(),
"double_negate_constant"); "double_negate_constant");
Add(ExternalReference::is_promisehook_enabled_address(isolate).address(), Add(ExternalReference::promise_hook_address(isolate).address(),
"Isolate::is_promisehook_enabled_address()"); "Isolate::promise_hook_address()");
// Debug addresses // Debug addresses
Add(ExternalReference::debug_after_break_target_address(isolate).address(), Add(ExternalReference::debug_after_break_target_address(isolate).address(),
......
...@@ -2148,11 +2148,11 @@ Isolate::Isolate(bool enable_serializer) ...@@ -2148,11 +2148,11 @@ Isolate::Isolate(bool enable_serializer)
// be fixed once the default isolate cleanup is done. // be fixed once the default isolate cleanup is done.
random_number_generator_(NULL), random_number_generator_(NULL),
rail_mode_(PERFORMANCE_ANIMATION), rail_mode_(PERFORMANCE_ANIMATION),
promise_hook_(NULL),
load_start_time_ms_(0), load_start_time_ms_(0),
serializer_enabled_(enable_serializer), serializer_enabled_(enable_serializer),
has_fatal_error_(false), has_fatal_error_(false),
initialized_from_snapshot_(false), initialized_from_snapshot_(false),
is_promisehook_enabled_(false),
is_tail_call_elimination_enabled_(true), is_tail_call_elimination_enabled_(true),
is_isolate_in_background_(false), is_isolate_in_background_(false),
cpu_profiler_(NULL), cpu_profiler_(NULL),
...@@ -3170,9 +3170,14 @@ void Isolate::FireCallCompletedCallback() { ...@@ -3170,9 +3170,14 @@ void Isolate::FireCallCompletedCallback() {
} }
} }
void Isolate::EnablePromiseHook() { is_promisehook_enabled_ = true; } void Isolate::SetPromiseHook(PromiseHook hook) { promise_hook_ = hook; }
void Isolate::DisablePromiseHook() { is_promisehook_enabled_ = false; } void Isolate::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
Handle<Object> parent) {
if (promise_hook_ == nullptr) return;
promise_hook_(type, v8::Utils::PromiseToLocal(promise),
v8::Utils::ToLocal(parent));
}
void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) { void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) {
promise_reject_callback_ = callback; promise_reject_callback_ = callback;
......
...@@ -1125,12 +1125,12 @@ class Isolate { ...@@ -1125,12 +1125,12 @@ class Isolate {
int GetNextUniqueSharedFunctionInfoId() { return next_unique_sfi_id_++; } int GetNextUniqueSharedFunctionInfoId() { return next_unique_sfi_id_++; }
#endif #endif
Address is_promisehook_enabled_address() { Address promise_hook_address() {
return reinterpret_cast<Address>(&is_promisehook_enabled_); return reinterpret_cast<Address>(&promise_hook_);
} }
bool IsPromiseHookEnabled() { return is_promisehook_enabled_; } void SetPromiseHook(PromiseHook hook);
void EnablePromiseHook(); void RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
void DisablePromiseHook(); Handle<Object> parent);
// Support for dynamically disabling tail call elimination. // Support for dynamically disabling tail call elimination.
Address is_tail_call_elimination_enabled_address() { Address is_tail_call_elimination_enabled_address() {
...@@ -1366,6 +1366,7 @@ class Isolate { ...@@ -1366,6 +1366,7 @@ class Isolate {
AccessCompilerData* access_compiler_data_; AccessCompilerData* access_compiler_data_;
base::RandomNumberGenerator* random_number_generator_; base::RandomNumberGenerator* random_number_generator_;
base::AtomicValue<RAILMode> rail_mode_; base::AtomicValue<RAILMode> rail_mode_;
PromiseHook promise_hook_;
base::Mutex rail_mutex_; base::Mutex rail_mutex_;
double load_start_time_ms_; double load_start_time_ms_;
...@@ -1378,9 +1379,6 @@ class Isolate { ...@@ -1378,9 +1379,6 @@ class Isolate {
// True if this isolate was initialized from a snapshot. // True if this isolate was initialized from a snapshot.
bool initialized_from_snapshot_; bool initialized_from_snapshot_;
// True if PromiseHook feature is enabled.
bool is_promisehook_enabled_;
// True if ES2015 tail call elimination feature is enabled. // True if ES2015 tail call elimination feature is enabled.
bool is_tail_call_elimination_enabled_; bool is_tail_call_elimination_enabled_;
......
...@@ -82,7 +82,7 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) { ...@@ -82,7 +82,7 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) {
} }
// Just forwarding the exception, so no debugEvent for throwawayCapability. // Just forwarding the exception, so no debugEvent for throwawayCapability.
var throwawayCapability = CreateInternalPromiseCapability(); var throwawayCapability = CreateInternalPromiseCapability(promise);
// The Promise will be thrown away and not handled, but it shouldn't trigger // The Promise will be thrown away and not handled, but it shouldn't trigger
// unhandled reject events as its work is done // unhandled reject events as its work is done
......
...@@ -76,7 +76,7 @@ SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true); ...@@ -76,7 +76,7 @@ SET_PRIVATE(PromiseIdRejectHandler, promiseForwardingHandlerSymbol, true);
// This is used by utils and v8-extras. // This is used by utils and v8-extras.
function PromiseCreate() { function PromiseCreate() {
return %promise_internal_constructor(); return %promise_internal_constructor(UNDEFINED);
} }
// Only used by async-await.js // Only used by async-await.js
...@@ -92,9 +92,9 @@ function DoRejectPromise(promise, reason) { ...@@ -92,9 +92,9 @@ function DoRejectPromise(promise, reason) {
// The resultCapability.promise is only ever fulfilled internally, // The resultCapability.promise is only ever fulfilled internally,
// so we don't need the closures to protect against accidentally // so we don't need the closures to protect against accidentally
// calling them multiple times. // calling them multiple times.
function CreateInternalPromiseCapability() { function CreateInternalPromiseCapability(parent) {
return { return {
promise: %promise_internal_constructor(), promise: %promise_internal_constructor(parent),
resolve: UNDEFINED, resolve: UNDEFINED,
reject: UNDEFINED reject: UNDEFINED
}; };
...@@ -105,7 +105,7 @@ function CreateInternalPromiseCapability() { ...@@ -105,7 +105,7 @@ function CreateInternalPromiseCapability() {
function NewPromiseCapability(C, debugEvent) { function NewPromiseCapability(C, debugEvent) {
if (C === GlobalPromise) { if (C === GlobalPromise) {
// Optimized case, avoid extra closure. // Optimized case, avoid extra closure.
var promise = %promise_internal_constructor(); var promise = %promise_internal_constructor(UNDEFINED);
// TODO(gsathya): Remove container for callbacks when this is // TODO(gsathya): Remove container for callbacks when this is
// moved to CPP/TF. // moved to CPP/TF.
var callbacks = %create_resolving_functions(promise, debugEvent); var callbacks = %create_resolving_functions(promise, debugEvent);
...@@ -168,7 +168,7 @@ function PromiseResolve(x) { ...@@ -168,7 +168,7 @@ function PromiseResolve(x) {
// Avoid creating resolving functions. // Avoid creating resolving functions.
if (this === GlobalPromise) { if (this === GlobalPromise) {
var promise = %promise_internal_constructor(); var promise = %promise_internal_constructor(UNDEFINED);
%promise_resolve(promise, x); %promise_resolve(promise, x);
return promise; return promise;
} }
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
JSObject* PromiseUtils::GetPromise(Handle<Context> context) { JSPromise* PromiseUtils::GetPromise(Handle<Context> context) {
return JSObject::cast(context->get(kPromiseSlot)); return JSPromise::cast(context->get(kPromiseSlot));
} }
Object* PromiseUtils::GetDebugEvent(Handle<Context> context) { Object* PromiseUtils::GetDebugEvent(Handle<Context> context) {
......
...@@ -29,7 +29,7 @@ class PromiseUtils : public AllStatic { ...@@ -29,7 +29,7 @@ class PromiseUtils : public AllStatic {
// These get and set the slots on the PromiseResolvingContext, which // These get and set the slots on the PromiseResolvingContext, which
// is used by the resolve/reject promise callbacks. // is used by the resolve/reject promise callbacks.
static JSObject* GetPromise(Handle<Context> context); static JSPromise* GetPromise(Handle<Context> context);
static Object* GetDebugEvent(Handle<Context> context); static Object* GetDebugEvent(Handle<Context> context);
static bool HasAlreadyVisited(Handle<Context> context); static bool HasAlreadyVisited(Handle<Context> context);
static void SetAlreadyVisited(Handle<Context> context); static void SetAlreadyVisited(Handle<Context> context);
......
...@@ -15,6 +15,9 @@ namespace { ...@@ -15,6 +15,9 @@ namespace {
void PromiseRejectEvent(Isolate* isolate, Handle<JSPromise> promise, void PromiseRejectEvent(Isolate* isolate, Handle<JSPromise> promise,
Handle<Object> rejected_promise, Handle<Object> value, Handle<Object> rejected_promise, Handle<Object> value,
bool debug_event) { bool debug_event) {
isolate->RunPromiseHook(PromiseHookType::kResolve, promise,
isolate->factory()->undefined_value());
if (isolate->debug()->is_active() && debug_event) { if (isolate->debug()->is_active() && debug_event) {
isolate->debug()->OnPromiseReject(rejected_promise, value); isolate->debug()->OnPromiseReject(rejected_promise, value);
} }
...@@ -284,5 +287,41 @@ RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) { ...@@ -284,5 +287,41 @@ RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) {
return isolate->heap()->undefined_value(); 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 internal
} // namespace v8 } // namespace v8
...@@ -293,7 +293,7 @@ namespace internal { ...@@ -293,7 +293,7 @@ namespace internal {
F(CreateListFromArrayLike, 1, 1) \ F(CreateListFromArrayLike, 1, 1) \
F(CreateResolvingFunctions, 1, 1) \ F(CreateResolvingFunctions, 1, 1) \
F(EnqueueMicrotask, 1, 1) \ F(EnqueueMicrotask, 1, 1) \
F(EnqueuePromiseReactionJob, 4, 1) \ F(EnqueuePromiseReactionJob, 5, 1) \
F(EnqueuePromiseResolveThenableJob, 3, 1) \ F(EnqueuePromiseResolveThenableJob, 3, 1) \
F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \ F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \
F(ExportExperimentalFromRuntime, 1, 1) \ F(ExportExperimentalFromRuntime, 1, 1) \
...@@ -309,6 +309,10 @@ namespace internal { ...@@ -309,6 +309,10 @@ namespace internal {
F(PromiseDeferred, 1, 1) \ F(PromiseDeferred, 1, 1) \
F(PromiseReject, 3, 1) \ F(PromiseReject, 3, 1) \
F(PromiseFulfill, 3, 1) \ F(PromiseFulfill, 3, 1) \
F(PromiseHookInit, 2, 1) \
F(PromiseHookResolve, 1, 1) \
F(PromiseHookBefore, 1, 1) \
F(PromiseHookAfter, 1, 1) \
F(PromiseMarkAsHandled, 1, 1) \ F(PromiseMarkAsHandled, 1, 1) \
F(PromiseRejectEventFromStack, 2, 1) \ F(PromiseRejectEventFromStack, 2, 1) \
F(PromiseRejectReactions, 1, 1) \ F(PromiseRejectReactions, 1, 1) \
......
...@@ -1392,8 +1392,7 @@ class WasmInstanceBuilder { ...@@ -1392,8 +1392,7 @@ class WasmInstanceBuilder {
// Helper routines to print out errors with imports. // Helper routines to print out errors with imports.
void ReportLinkError(const char* error, uint32_t index, void ReportLinkError(const char* error, uint32_t index,
Handle<String> module_name, Handle<String> module_name, Handle<String> import_name) {
Handle<String> import_name) {
thrower_->LinkError( thrower_->LinkError(
"Import #%d module=\"%.*s\" function=\"%.*s\" error: %s", index, "Import #%d module=\"%.*s\" function=\"%.*s\" error: %s", index,
module_name->length(), module_name->ToCString().get(), module_name->length(), module_name->ToCString().get(),
...@@ -1431,7 +1430,7 @@ class WasmInstanceBuilder { ...@@ -1431,7 +1430,7 @@ class WasmInstanceBuilder {
// Look up the value in the module. // Look up the value in the module.
if (!module->IsJSReceiver()) { if (!module->IsJSReceiver()) {
return ReportTypeError("module is not an object or function", index, return ReportTypeError("module is not an object or function", index,
module_name); module_name);
} }
result = Object::GetPropertyOrElement(module, import_name); result = Object::GetPropertyOrElement(module, import_name);
...@@ -1522,17 +1521,15 @@ class WasmInstanceBuilder { ...@@ -1522,17 +1521,15 @@ class WasmInstanceBuilder {
WasmImport& import = module_->import_table[index]; WasmImport& import = module_->import_table[index];
Handle<String> module_name; Handle<String> module_name;
MaybeHandle<String> maybe_module_name = MaybeHandle<String> maybe_module_name = ExtractStringFromModuleBytes(
ExtractStringFromModuleBytes(isolate_, compiled_module_, isolate_, compiled_module_, import.module_name_offset,
import.module_name_offset, import.module_name_length);
import.module_name_length);
if (!maybe_module_name.ToHandle(&module_name)) return -1; if (!maybe_module_name.ToHandle(&module_name)) return -1;
Handle<String> import_name; Handle<String> import_name;
MaybeHandle<String> maybe_import_name = MaybeHandle<String> maybe_import_name = ExtractStringFromModuleBytes(
ExtractStringFromModuleBytes(isolate_, compiled_module_, isolate_, compiled_module_, import.field_name_offset,
import.field_name_offset, import.field_name_length);
import.field_name_length);
if (!maybe_import_name.ToHandle(&import_name)) return -1; if (!maybe_import_name.ToHandle(&import_name)) return -1;
MaybeHandle<Object> result = MaybeHandle<Object> result =
...@@ -1555,8 +1552,8 @@ class WasmInstanceBuilder { ...@@ -1555,8 +1552,8 @@ class WasmInstanceBuilder {
module_->origin); module_->origin);
if (import_wrapper.is_null()) { if (import_wrapper.is_null()) {
ReportLinkError( ReportLinkError(
"imported function does not match the expected type", "imported function does not match the expected type", index,
index, module_name, import_name); module_name, import_name);
return -1; return -1;
} }
code_table->set(num_imported_functions, *import_wrapper); code_table->set(num_imported_functions, *import_wrapper);
...@@ -1630,8 +1627,8 @@ class WasmInstanceBuilder { ...@@ -1630,8 +1627,8 @@ class WasmInstanceBuilder {
// Global imports are converted to numbers and written into the // Global imports are converted to numbers and written into the
// {globals_} array buffer. // {globals_} array buffer.
if (!value->IsNumber()) { if (!value->IsNumber()) {
ReportLinkError("global import must be a number", ReportLinkError("global import must be a number", index,
index, module_name, import_name); module_name, import_name);
return -1; return -1;
} }
WriteGlobalValue(module_->globals[import.index], value); WriteGlobalValue(module_->globals[import.index], value);
......
This diff is collapsed.
...@@ -1792,6 +1792,9 @@ TEST(CodeStubAssemblerGraphsCorrectness) { ...@@ -1792,6 +1792,9 @@ TEST(CodeStubAssemblerGraphsCorrectness) {
v8_isolate->Dispose(); v8_isolate->Dispose();
} }
void CustomPromiseHook(v8::PromiseHookType type, v8::Local<v8::Promise> promise,
v8::Local<v8::Value> parentPromise) {}
TEST(IsPromiseHookEnabled) { TEST(IsPromiseHookEnabled) {
Isolate* isolate(CcTest::InitIsolateOnce()); Isolate* isolate(CcTest::InitIsolateOnce());
...@@ -1805,16 +1808,15 @@ TEST(IsPromiseHookEnabled) { ...@@ -1805,16 +1808,15 @@ TEST(IsPromiseHookEnabled) {
CHECK(!code.is_null()); CHECK(!code.is_null());
FunctionTester ft(code, kNumParams); FunctionTester ft(code, kNumParams);
CHECK_EQ(false, isolate->IsPromiseHookEnabled());
Handle<Object> result = Handle<Object> result =
ft.Call(isolate->factory()->undefined_value()).ToHandleChecked(); ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK_EQ(isolate->heap()->false_value(), *result); CHECK_EQ(isolate->heap()->false_value(), *result);
isolate->EnablePromiseHook(); isolate->SetPromiseHook(CustomPromiseHook);
result = ft.Call(isolate->factory()->undefined_value()).ToHandleChecked(); result = ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK_EQ(isolate->heap()->true_value(), *result); CHECK_EQ(isolate->heap()->true_value(), *result);
isolate->DisablePromiseHook(); isolate->SetPromiseHook(nullptr);
result = ft.Call(isolate->factory()->undefined_value()).ToHandleChecked(); result = ft.Call(isolate->factory()->undefined_value()).ToHandleChecked();
CHECK_EQ(isolate->heap()->false_value(), *result); CHECK_EQ(isolate->heap()->false_value(), *result);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment