diff options
Diffstat (limited to 'deps/v8/src/d8/async-hooks-wrapper.cc')
-rw-r--r-- | deps/v8/src/d8/async-hooks-wrapper.cc | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/deps/v8/src/d8/async-hooks-wrapper.cc b/deps/v8/src/d8/async-hooks-wrapper.cc new file mode 100644 index 0000000000..a3fc9dba1f --- /dev/null +++ b/deps/v8/src/d8/async-hooks-wrapper.cc @@ -0,0 +1,294 @@ +// Copyright 2018 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/d8/async-hooks-wrapper.h" +#include "src/d8/d8.h" +#include "src/execution/isolate-inl.h" + +namespace v8 { + +void AsyncHooksWrap::Enable() { enabled_ = true; } + +void AsyncHooksWrap::Disable() { enabled_ = false; } + +v8::Local<v8::Function> AsyncHooksWrap::init_function() const { + return init_function_.Get(isolate_); +} +void AsyncHooksWrap::set_init_function(v8::Local<v8::Function> value) { + init_function_.Reset(isolate_, value); +} +v8::Local<v8::Function> AsyncHooksWrap::before_function() const { + return before_function_.Get(isolate_); +} +void AsyncHooksWrap::set_before_function(v8::Local<v8::Function> value) { + before_function_.Reset(isolate_, value); +} +v8::Local<v8::Function> AsyncHooksWrap::after_function() const { + return after_function_.Get(isolate_); +} +void AsyncHooksWrap::set_after_function(v8::Local<v8::Function> value) { + after_function_.Reset(isolate_, value); +} +v8::Local<v8::Function> AsyncHooksWrap::promiseResolve_function() const { + return promiseResolve_function_.Get(isolate_); +} +void AsyncHooksWrap::set_promiseResolve_function( + v8::Local<v8::Function> value) { + promiseResolve_function_.Reset(isolate_, value); +} + +static AsyncHooksWrap* UnwrapHook( + const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + Local<Object> hook = args.This(); + + AsyncHooks* hooks = PerIsolateData::Get(isolate)->GetAsyncHooks(); + + if (!hooks->async_hook_ctor.Get(isolate)->HasInstance(hook)) { + isolate->ThrowException( + String::NewFromUtf8( + isolate, "Invalid 'this' passed instead of AsyncHooks instance", + NewStringType::kNormal) + .ToLocalChecked()); + return nullptr; + } + + Local<External> wrap = Local<External>::Cast(hook->GetInternalField(0)); + void* ptr = wrap->Value(); + return static_cast<AsyncHooksWrap*>(ptr); +} + +static void EnableHook(const v8::FunctionCallbackInfo<v8::Value>& args) { + AsyncHooksWrap* wrap = UnwrapHook(args); + if (wrap) { + wrap->Enable(); + } +} + +static void DisableHook(const v8::FunctionCallbackInfo<v8::Value>& args) { + AsyncHooksWrap* wrap = UnwrapHook(args); + if (wrap) { + wrap->Disable(); + } +} + +async_id_t AsyncHooks::GetExecutionAsyncId() const { + return asyncContexts.top().execution_async_id; +} + +async_id_t AsyncHooks::GetTriggerAsyncId() const { + return asyncContexts.top().trigger_async_id; +} + +Local<Object> AsyncHooks::CreateHook( + const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); + EscapableHandleScope handle_scope(isolate); + + Local<Context> currentContext = isolate->GetCurrentContext(); + + if (args.Length() != 1 || !args[0]->IsObject()) { + isolate->ThrowException( + String::NewFromUtf8(isolate, "Invalid arguments passed to createHook", + NewStringType::kNormal) + .ToLocalChecked()); + return Local<Object>(); + } + + AsyncHooksWrap* wrap = new AsyncHooksWrap(isolate); + + Local<Object> fn_obj = args[0].As<Object>(); + +#define SET_HOOK_FN(name) \ + Local<Value> name##_v = \ + fn_obj \ + ->Get(currentContext, \ + String::NewFromUtf8(isolate, #name, NewStringType::kNormal) \ + .ToLocalChecked()) \ + .ToLocalChecked(); \ + if (name##_v->IsFunction()) { \ + wrap->set_##name##_function(name##_v.As<Function>()); \ + } + + SET_HOOK_FN(init); + SET_HOOK_FN(before); + SET_HOOK_FN(after); + SET_HOOK_FN(promiseResolve); +#undef SET_HOOK_FN + + async_wraps_.push_back(wrap); + + Local<Object> obj = async_hooks_templ.Get(isolate) + ->NewInstance(currentContext) + .ToLocalChecked(); + obj->SetInternalField(0, External::New(isolate, wrap)); + + return handle_scope.Escape(obj); +} + +void AsyncHooks::ShellPromiseHook(PromiseHookType type, Local<Promise> promise, + Local<Value> parent) { + AsyncHooks* hooks = + PerIsolateData::Get(promise->GetIsolate())->GetAsyncHooks(); + + HandleScope handle_scope(hooks->isolate_); + + Local<Context> currentContext = hooks->isolate_->GetCurrentContext(); + + if (type == PromiseHookType::kInit) { + ++hooks->current_async_id; + Local<Integer> async_id = + Integer::New(hooks->isolate_, hooks->current_async_id); + + CHECK(!promise + ->HasPrivate(currentContext, + hooks->async_id_smb.Get(hooks->isolate_)) + .ToChecked()); + promise->SetPrivate(currentContext, + hooks->async_id_smb.Get(hooks->isolate_), async_id); + + if (parent->IsPromise()) { + Local<Promise> parent_promise = parent.As<Promise>(); + Local<Value> parent_async_id = + parent_promise + ->GetPrivate(hooks->isolate_->GetCurrentContext(), + hooks->async_id_smb.Get(hooks->isolate_)) + .ToLocalChecked(); + promise->SetPrivate(currentContext, + hooks->trigger_id_smb.Get(hooks->isolate_), + parent_async_id); + } else { + CHECK(parent->IsUndefined()); + Local<Integer> trigger_id = Integer::New(hooks->isolate_, 0); + promise->SetPrivate(currentContext, + hooks->trigger_id_smb.Get(hooks->isolate_), + trigger_id); + } + } else if (type == PromiseHookType::kBefore) { + AsyncContext ctx; + ctx.execution_async_id = + promise + ->GetPrivate(hooks->isolate_->GetCurrentContext(), + hooks->async_id_smb.Get(hooks->isolate_)) + .ToLocalChecked() + .As<Integer>() + ->Value(); + ctx.trigger_async_id = + promise + ->GetPrivate(hooks->isolate_->GetCurrentContext(), + hooks->trigger_id_smb.Get(hooks->isolate_)) + .ToLocalChecked() + .As<Integer>() + ->Value(); + hooks->asyncContexts.push(ctx); + } else if (type == PromiseHookType::kAfter) { + hooks->asyncContexts.pop(); + } + + for (AsyncHooksWrap* wrap : hooks->async_wraps_) { + PromiseHookDispatch(type, promise, parent, wrap, hooks); + } +} + +void AsyncHooks::Initialize() { + HandleScope handle_scope(isolate_); + + async_hook_ctor.Reset(isolate_, FunctionTemplate::New(isolate_)); + async_hook_ctor.Get(isolate_)->SetClassName( + String::NewFromUtf8(isolate_, "AsyncHook", NewStringType::kNormal) + .ToLocalChecked()); + + async_hooks_templ.Reset(isolate_, + async_hook_ctor.Get(isolate_)->InstanceTemplate()); + async_hooks_templ.Get(isolate_)->SetInternalFieldCount(1); + async_hooks_templ.Get(isolate_)->Set( + String::NewFromUtf8(isolate_, "enable", v8::NewStringType::kNormal) + .ToLocalChecked(), + FunctionTemplate::New(isolate_, EnableHook)); + async_hooks_templ.Get(isolate_)->Set( + String::NewFromUtf8(isolate_, "disable", v8::NewStringType::kNormal) + .ToLocalChecked(), + FunctionTemplate::New(isolate_, DisableHook)); + + async_id_smb.Reset(isolate_, Private::New(isolate_)); + trigger_id_smb.Reset(isolate_, Private::New(isolate_)); + + isolate_->SetPromiseHook(ShellPromiseHook); +} + +void AsyncHooks::Deinitialize() { + isolate_->SetPromiseHook(nullptr); + for (AsyncHooksWrap* wrap : async_wraps_) { + delete wrap; + } +} + +void AsyncHooks::PromiseHookDispatch(PromiseHookType type, + Local<Promise> promise, + Local<Value> parent, AsyncHooksWrap* wrap, + AsyncHooks* hooks) { + if (!wrap->IsEnabled()) { + return; + } + + HandleScope handle_scope(hooks->isolate_); + + TryCatch try_catch(hooks->isolate_); + try_catch.SetVerbose(true); + + i::Isolate* isolate = reinterpret_cast<i::Isolate*>(hooks->isolate_); + if (isolate->has_scheduled_exception()) { + isolate->ScheduleThrow(isolate->scheduled_exception()); + + DCHECK(try_catch.HasCaught()); + Shell::ReportException(hooks->isolate_, &try_catch); + return; + } + + Local<Value> rcv = Undefined(hooks->isolate_); + Local<Context> context = hooks->isolate_->GetCurrentContext(); + Local<Value> async_id = + promise->GetPrivate(context, hooks->async_id_smb.Get(hooks->isolate_)) + .ToLocalChecked(); + Local<Value> args[1] = {async_id}; + + // This is unused. It's here to silence the warning about + // not using the MaybeLocal return value from Call. + MaybeLocal<Value> result; + + // Sacrifice the brevity for readability and debugfulness + if (type == PromiseHookType::kInit) { + if (!wrap->init_function().IsEmpty()) { + Local<Value> initArgs[4] = { + async_id, + String::NewFromUtf8(hooks->isolate_, "PROMISE", + NewStringType::kNormal) + .ToLocalChecked(), + promise + ->GetPrivate(context, hooks->trigger_id_smb.Get(hooks->isolate_)) + .ToLocalChecked(), + promise}; + result = wrap->init_function()->Call(context, rcv, 4, initArgs); + } + } else if (type == PromiseHookType::kBefore) { + if (!wrap->before_function().IsEmpty()) { + result = wrap->before_function()->Call(context, rcv, 1, args); + } + } else if (type == PromiseHookType::kAfter) { + if (!wrap->after_function().IsEmpty()) { + result = wrap->after_function()->Call(context, rcv, 1, args); + } + } else if (type == PromiseHookType::kResolve) { + if (!wrap->promiseResolve_function().IsEmpty()) { + result = wrap->promiseResolve_function()->Call(context, rcv, 1, args); + } + } + + if (try_catch.HasCaught()) { + Shell::ReportException(hooks->isolate_, &try_catch); + } +} + +} // namespace v8 |