• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/d8/async-hooks-wrapper.h"
6 #include "src/d8/d8.h"
7 #include "src/execution/isolate-inl.h"
8 
9 namespace v8 {
10 
Enable()11 void AsyncHooksWrap::Enable() { enabled_ = true; }
12 
Disable()13 void AsyncHooksWrap::Disable() { enabled_ = false; }
14 
init_function() const15 v8::Local<v8::Function> AsyncHooksWrap::init_function() const {
16   return init_function_.Get(isolate_);
17 }
set_init_function(v8::Local<v8::Function> value)18 void AsyncHooksWrap::set_init_function(v8::Local<v8::Function> value) {
19   init_function_.Reset(isolate_, value);
20 }
before_function() const21 v8::Local<v8::Function> AsyncHooksWrap::before_function() const {
22   return before_function_.Get(isolate_);
23 }
set_before_function(v8::Local<v8::Function> value)24 void AsyncHooksWrap::set_before_function(v8::Local<v8::Function> value) {
25   before_function_.Reset(isolate_, value);
26 }
after_function() const27 v8::Local<v8::Function> AsyncHooksWrap::after_function() const {
28   return after_function_.Get(isolate_);
29 }
set_after_function(v8::Local<v8::Function> value)30 void AsyncHooksWrap::set_after_function(v8::Local<v8::Function> value) {
31   after_function_.Reset(isolate_, value);
32 }
promiseResolve_function() const33 v8::Local<v8::Function> AsyncHooksWrap::promiseResolve_function() const {
34   return promiseResolve_function_.Get(isolate_);
35 }
set_promiseResolve_function(v8::Local<v8::Function> value)36 void AsyncHooksWrap::set_promiseResolve_function(
37     v8::Local<v8::Function> value) {
38   promiseResolve_function_.Reset(isolate_, value);
39 }
40 
UnwrapHook(const v8::FunctionCallbackInfo<v8::Value> & args)41 static AsyncHooksWrap* UnwrapHook(
42     const v8::FunctionCallbackInfo<v8::Value>& args) {
43   Isolate* isolate = args.GetIsolate();
44   HandleScope scope(isolate);
45   Local<Object> hook = args.This();
46 
47   AsyncHooks* hooks = PerIsolateData::Get(isolate)->GetAsyncHooks();
48 
49   if (!hooks->async_hook_ctor.Get(isolate)->HasInstance(hook)) {
50     isolate->ThrowException(String::NewFromUtf8Literal(
51         isolate, "Invalid 'this' passed instead of AsyncHooks instance"));
52     return nullptr;
53   }
54 
55   Local<External> wrap = Local<External>::Cast(hook->GetInternalField(0));
56   void* ptr = wrap->Value();
57   return static_cast<AsyncHooksWrap*>(ptr);
58 }
59 
EnableHook(const v8::FunctionCallbackInfo<v8::Value> & args)60 static void EnableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
61   AsyncHooksWrap* wrap = UnwrapHook(args);
62   if (wrap) {
63     wrap->Enable();
64   }
65 }
66 
DisableHook(const v8::FunctionCallbackInfo<v8::Value> & args)67 static void DisableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
68   AsyncHooksWrap* wrap = UnwrapHook(args);
69   if (wrap) {
70     wrap->Disable();
71   }
72 }
73 
GetExecutionAsyncId() const74 async_id_t AsyncHooks::GetExecutionAsyncId() const {
75   return asyncContexts.top().execution_async_id;
76 }
77 
GetTriggerAsyncId() const78 async_id_t AsyncHooks::GetTriggerAsyncId() const {
79   return asyncContexts.top().trigger_async_id;
80 }
81 
CreateHook(const v8::FunctionCallbackInfo<v8::Value> & args)82 Local<Object> AsyncHooks::CreateHook(
83     const v8::FunctionCallbackInfo<v8::Value>& args) {
84   Isolate* isolate = args.GetIsolate();
85   EscapableHandleScope handle_scope(isolate);
86 
87   Local<Context> currentContext = isolate->GetCurrentContext();
88 
89   if (args.Length() != 1 || !args[0]->IsObject()) {
90     isolate->ThrowException(String::NewFromUtf8Literal(
91         isolate, "Invalid arguments passed to createHook"));
92     return Local<Object>();
93   }
94 
95   AsyncHooksWrap* wrap = new AsyncHooksWrap(isolate);
96 
97   Local<Object> fn_obj = args[0].As<Object>();
98 
99 #define SET_HOOK_FN(name)                                                     \
100   Local<Value> name##_v =                                                     \
101       fn_obj->Get(currentContext, String::NewFromUtf8Literal(isolate, #name)) \
102           .ToLocalChecked();                                                  \
103   if (name##_v->IsFunction()) {                                               \
104     wrap->set_##name##_function(name##_v.As<Function>());                     \
105   }
106 
107   SET_HOOK_FN(init);
108   SET_HOOK_FN(before);
109   SET_HOOK_FN(after);
110   SET_HOOK_FN(promiseResolve);
111 #undef SET_HOOK_FN
112 
113   async_wraps_.push_back(wrap);
114 
115   Local<Object> obj = async_hooks_templ.Get(isolate)
116                           ->NewInstance(currentContext)
117                           .ToLocalChecked();
118   obj->SetInternalField(0, External::New(isolate, wrap));
119 
120   return handle_scope.Escape(obj);
121 }
122 
ShellPromiseHook(PromiseHookType type,Local<Promise> promise,Local<Value> parent)123 void AsyncHooks::ShellPromiseHook(PromiseHookType type, Local<Promise> promise,
124                                   Local<Value> parent) {
125   AsyncHooks* hooks =
126       PerIsolateData::Get(promise->GetIsolate())->GetAsyncHooks();
127 
128   HandleScope handle_scope(hooks->isolate_);
129 
130   Local<Context> currentContext = hooks->isolate_->GetCurrentContext();
131   DCHECK(!currentContext.IsEmpty());
132 
133   if (type == PromiseHookType::kInit) {
134     ++hooks->current_async_id;
135     Local<Integer> async_id =
136         Integer::New(hooks->isolate_, hooks->current_async_id);
137 
138     CHECK(!promise
139                ->HasPrivate(currentContext,
140                             hooks->async_id_smb.Get(hooks->isolate_))
141                .ToChecked());
142     promise->SetPrivate(currentContext,
143                         hooks->async_id_smb.Get(hooks->isolate_), async_id);
144 
145     if (parent->IsPromise()) {
146       Local<Promise> parent_promise = parent.As<Promise>();
147       Local<Value> parent_async_id =
148           parent_promise
149               ->GetPrivate(hooks->isolate_->GetCurrentContext(),
150                            hooks->async_id_smb.Get(hooks->isolate_))
151               .ToLocalChecked();
152       promise->SetPrivate(currentContext,
153                           hooks->trigger_id_smb.Get(hooks->isolate_),
154                           parent_async_id);
155     } else {
156       CHECK(parent->IsUndefined());
157       Local<Integer> trigger_id = Integer::New(hooks->isolate_, 0);
158       promise->SetPrivate(currentContext,
159                           hooks->trigger_id_smb.Get(hooks->isolate_),
160                           trigger_id);
161     }
162   } else if (type == PromiseHookType::kBefore) {
163     AsyncContext ctx;
164     ctx.execution_async_id =
165         promise
166             ->GetPrivate(hooks->isolate_->GetCurrentContext(),
167                          hooks->async_id_smb.Get(hooks->isolate_))
168             .ToLocalChecked()
169             .As<Integer>()
170             ->Value();
171     ctx.trigger_async_id =
172         promise
173             ->GetPrivate(hooks->isolate_->GetCurrentContext(),
174                          hooks->trigger_id_smb.Get(hooks->isolate_))
175             .ToLocalChecked()
176             .As<Integer>()
177             ->Value();
178     hooks->asyncContexts.push(ctx);
179   } else if (type == PromiseHookType::kAfter) {
180     hooks->asyncContexts.pop();
181   }
182 
183   for (AsyncHooksWrap* wrap : hooks->async_wraps_) {
184     PromiseHookDispatch(type, promise, parent, wrap, hooks);
185   }
186 }
187 
Initialize()188 void AsyncHooks::Initialize() {
189   HandleScope handle_scope(isolate_);
190 
191   async_hook_ctor.Reset(isolate_, FunctionTemplate::New(isolate_));
192   async_hook_ctor.Get(isolate_)->SetClassName(
193       String::NewFromUtf8Literal(isolate_, "AsyncHook"));
194 
195   async_hooks_templ.Reset(isolate_,
196                           async_hook_ctor.Get(isolate_)->InstanceTemplate());
197   async_hooks_templ.Get(isolate_)->SetInternalFieldCount(1);
198   async_hooks_templ.Get(isolate_)->Set(
199       isolate_, "enable", FunctionTemplate::New(isolate_, EnableHook));
200   async_hooks_templ.Get(isolate_)->Set(
201       isolate_, "disable", FunctionTemplate::New(isolate_, DisableHook));
202 
203   async_id_smb.Reset(isolate_, Private::New(isolate_));
204   trigger_id_smb.Reset(isolate_, Private::New(isolate_));
205 
206   isolate_->SetPromiseHook(ShellPromiseHook);
207 }
208 
Deinitialize()209 void AsyncHooks::Deinitialize() {
210   isolate_->SetPromiseHook(nullptr);
211   for (AsyncHooksWrap* wrap : async_wraps_) {
212     delete wrap;
213   }
214 }
215 
PromiseHookDispatch(PromiseHookType type,Local<Promise> promise,Local<Value> parent,AsyncHooksWrap * wrap,AsyncHooks * hooks)216 void AsyncHooks::PromiseHookDispatch(PromiseHookType type,
217                                      Local<Promise> promise,
218                                      Local<Value> parent, AsyncHooksWrap* wrap,
219                                      AsyncHooks* hooks) {
220   if (!wrap->IsEnabled()) {
221     return;
222   }
223 
224   HandleScope handle_scope(hooks->isolate_);
225 
226   TryCatch try_catch(hooks->isolate_);
227   try_catch.SetVerbose(true);
228 
229   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(hooks->isolate_);
230   if (isolate->has_scheduled_exception()) {
231     isolate->ScheduleThrow(isolate->scheduled_exception());
232 
233     DCHECK(try_catch.HasCaught());
234     Shell::ReportException(hooks->isolate_, &try_catch);
235     return;
236   }
237 
238   Local<Value> rcv = Undefined(hooks->isolate_);
239   Local<Context> context = hooks->isolate_->GetCurrentContext();
240   Local<Value> async_id =
241       promise->GetPrivate(context, hooks->async_id_smb.Get(hooks->isolate_))
242           .ToLocalChecked();
243   Local<Value> args[1] = {async_id};
244 
245   // This is unused. It's here to silence the warning about
246   // not using the MaybeLocal return value from Call.
247   MaybeLocal<Value> result;
248 
249   // Sacrifice the brevity for readability and debugfulness
250   if (type == PromiseHookType::kInit) {
251     if (!wrap->init_function().IsEmpty()) {
252       Local<Value> initArgs[4] = {
253           async_id, String::NewFromUtf8Literal(hooks->isolate_, "PROMISE"),
254           promise
255               ->GetPrivate(context, hooks->trigger_id_smb.Get(hooks->isolate_))
256               .ToLocalChecked(),
257           promise};
258       result = wrap->init_function()->Call(context, rcv, 4, initArgs);
259     }
260   } else if (type == PromiseHookType::kBefore) {
261     if (!wrap->before_function().IsEmpty()) {
262       result = wrap->before_function()->Call(context, rcv, 1, args);
263     }
264   } else if (type == PromiseHookType::kAfter) {
265     if (!wrap->after_function().IsEmpty()) {
266       result = wrap->after_function()->Call(context, rcv, 1, args);
267     }
268   } else if (type == PromiseHookType::kResolve) {
269     if (!wrap->promiseResolve_function().IsEmpty()) {
270       result = wrap->promiseResolve_function()->Call(context, rcv, 1, args);
271     }
272   }
273 }
274 
275 }  // namespace v8
276