aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/d8/async-hooks-wrapper.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/d8/async-hooks-wrapper.cc')
-rw-r--r--deps/v8/src/d8/async-hooks-wrapper.cc294
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