V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
async-hooks-wrapper.cc
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/async-hooks-wrapper.h"
6 #include "src/d8.h"
7 #include "src/isolate-inl.h"
8 
9 namespace v8 {
10 
11 void AsyncHooksWrap::Enable() { enabled_ = true; }
12 
13 void AsyncHooksWrap::Disable() { enabled_ = false; }
14 
15 v8::Local<v8::Function> AsyncHooksWrap::init_function() const {
16  return init_function_.Get(isolate_);
17 }
18 void AsyncHooksWrap::set_init_function(v8::Local<v8::Function> value) {
19  init_function_.Reset(isolate_, value);
20 }
21 v8::Local<v8::Function> AsyncHooksWrap::before_function() const {
22  return before_function_.Get(isolate_);
23 }
24 void AsyncHooksWrap::set_before_function(v8::Local<v8::Function> value) {
25  before_function_.Reset(isolate_, value);
26 }
27 v8::Local<v8::Function> AsyncHooksWrap::after_function() const {
28  return after_function_.Get(isolate_);
29 }
30 void AsyncHooksWrap::set_after_function(v8::Local<v8::Function> value) {
31  after_function_.Reset(isolate_, value);
32 }
33 v8::Local<v8::Function> AsyncHooksWrap::promiseResolve_function() const {
34  return promiseResolve_function_.Get(isolate_);
35 }
36 void AsyncHooksWrap::set_promiseResolve_function(
38  promiseResolve_function_.Reset(isolate_, value);
39 }
40 
41 static AsyncHooksWrap* UnwrapHook(
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(
52  isolate, "Invalid 'this' passed instead of AsyncHooks instance",
54  .ToLocalChecked());
55  return nullptr;
56  }
57 
58  Local<External> wrap = Local<External>::Cast(hook->GetInternalField(0));
59  void* ptr = wrap->Value();
60  return static_cast<AsyncHooksWrap*>(ptr);
61 }
62 
63 static void EnableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
64  AsyncHooksWrap* wrap = UnwrapHook(args);
65  if (wrap) {
66  wrap->Enable();
67  }
68 }
69 
70 static void DisableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
71  AsyncHooksWrap* wrap = UnwrapHook(args);
72  if (wrap) {
73  wrap->Disable();
74  }
75 }
76 
77 async_id_t AsyncHooks::GetExecutionAsyncId() const {
78  return asyncContexts.top().execution_async_id;
79 }
80 
81 async_id_t AsyncHooks::GetTriggerAsyncId() const {
82  return asyncContexts.top().trigger_async_id;
83 }
84 
85 Local<Object> AsyncHooks::CreateHook(
87  Isolate* isolate = args.GetIsolate();
88  EscapableHandleScope handle_scope(isolate);
89 
90  Local<Context> currentContext = isolate->GetCurrentContext();
91 
92  if (args.Length() != 1 || !args[0]->IsObject()) {
93  isolate->ThrowException(
94  String::NewFromUtf8(isolate, "Invalid arguments passed to createHook",
96  .ToLocalChecked());
97  return Local<Object>();
98  }
99 
100  AsyncHooksWrap* wrap = new AsyncHooksWrap(isolate);
101 
102  Local<Object> fn_obj = args[0].As<Object>();
103 
104 #define SET_HOOK_FN(name) \
105  Local<Value> name##_v = \
106  fn_obj \
107  ->Get(currentContext, \
108  String::NewFromUtf8(isolate, #name, NewStringType::kNormal) \
109  .ToLocalChecked()) \
110  .ToLocalChecked(); \
111  if (name##_v->IsFunction()) { \
112  wrap->set_##name##_function(name##_v.As<Function>()); \
113  }
114 
115  SET_HOOK_FN(init);
116  SET_HOOK_FN(before);
117  SET_HOOK_FN(after);
118  SET_HOOK_FN(promiseResolve);
119 #undef SET_HOOK_FN
120 
121  async_wraps_.push_back(wrap);
122 
123  Local<Object> obj = async_hooks_templ.Get(isolate)
124  ->NewInstance(currentContext)
125  .ToLocalChecked();
126  obj->SetInternalField(0, External::New(isolate, wrap));
127 
128  return handle_scope.Escape(obj);
129 }
130 
131 void AsyncHooks::ShellPromiseHook(PromiseHookType type, Local<Promise> promise,
132  Local<Value> parent) {
133  AsyncHooks* hooks =
134  PerIsolateData::Get(promise->GetIsolate())->GetAsyncHooks();
135 
136  HandleScope handle_scope(hooks->isolate_);
137 
138  Local<Context> currentContext = hooks->isolate_->GetCurrentContext();
139 
140  if (type == PromiseHookType::kInit) {
141  ++hooks->current_async_id;
142  Local<Integer> async_id =
143  Integer::New(hooks->isolate_, hooks->current_async_id);
144 
145  CHECK(!promise
146  ->HasPrivate(currentContext,
147  hooks->async_id_smb.Get(hooks->isolate_))
148  .ToChecked());
149  promise->SetPrivate(currentContext,
150  hooks->async_id_smb.Get(hooks->isolate_), async_id);
151 
152  if (parent->IsPromise()) {
153  Local<Promise> parent_promise = parent.As<Promise>();
154  Local<Value> parent_async_id =
155  parent_promise
156  ->GetPrivate(hooks->isolate_->GetCurrentContext(),
157  hooks->async_id_smb.Get(hooks->isolate_))
158  .ToLocalChecked();
159  promise->SetPrivate(currentContext,
160  hooks->trigger_id_smb.Get(hooks->isolate_),
161  parent_async_id);
162  } else {
163  CHECK(parent->IsUndefined());
164  Local<Integer> trigger_id = Integer::New(hooks->isolate_, 0);
165  promise->SetPrivate(currentContext,
166  hooks->trigger_id_smb.Get(hooks->isolate_),
167  trigger_id);
168  }
169  } else if (type == PromiseHookType::kBefore) {
170  AsyncContext ctx;
171  ctx.execution_async_id =
172  promise
173  ->GetPrivate(hooks->isolate_->GetCurrentContext(),
174  hooks->async_id_smb.Get(hooks->isolate_))
175  .ToLocalChecked()
176  .As<Integer>()
177  ->Value();
178  ctx.trigger_async_id =
179  promise
180  ->GetPrivate(hooks->isolate_->GetCurrentContext(),
181  hooks->trigger_id_smb.Get(hooks->isolate_))
182  .ToLocalChecked()
183  .As<Integer>()
184  ->Value();
185  hooks->asyncContexts.push(ctx);
186  } else if (type == PromiseHookType::kAfter) {
187  hooks->asyncContexts.pop();
188  }
189 
190  for (AsyncHooksWrap* wrap : hooks->async_wraps_) {
191  PromiseHookDispatch(type, promise, parent, wrap, hooks);
192  }
193 }
194 
195 void AsyncHooks::Initialize() {
196  HandleScope handle_scope(isolate_);
197 
198  async_hook_ctor.Reset(isolate_, FunctionTemplate::New(isolate_));
199  async_hook_ctor.Get(isolate_)->SetClassName(
200  String::NewFromUtf8(isolate_, "AsyncHook", NewStringType::kNormal)
201  .ToLocalChecked());
202 
203  async_hooks_templ.Reset(isolate_,
204  async_hook_ctor.Get(isolate_)->InstanceTemplate());
205  async_hooks_templ.Get(isolate_)->SetInternalFieldCount(1);
206  async_hooks_templ.Get(isolate_)->Set(
208  .ToLocalChecked(),
209  FunctionTemplate::New(isolate_, EnableHook));
210  async_hooks_templ.Get(isolate_)->Set(
211  String::NewFromUtf8(isolate_, "disable", v8::NewStringType::kNormal)
212  .ToLocalChecked(),
213  FunctionTemplate::New(isolate_, DisableHook));
214 
215  async_id_smb.Reset(isolate_, Private::New(isolate_));
216  trigger_id_smb.Reset(isolate_, Private::New(isolate_));
217 
218  isolate_->SetPromiseHook(ShellPromiseHook);
219 }
220 
221 void AsyncHooks::Deinitialize() {
222  isolate_->SetPromiseHook(nullptr);
223  for (AsyncHooksWrap* wrap : async_wraps_) {
224  delete wrap;
225  }
226 }
227 
228 void AsyncHooks::PromiseHookDispatch(PromiseHookType type,
229  Local<Promise> promise,
230  Local<Value> parent, AsyncHooksWrap* wrap,
231  AsyncHooks* hooks) {
232  if (!wrap->IsEnabled()) {
233  return;
234  }
235 
236  HandleScope handle_scope(hooks->isolate_);
237 
238  TryCatch try_catch(hooks->isolate_);
239  try_catch.SetVerbose(true);
240 
241  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(hooks->isolate_);
242  if (isolate->has_scheduled_exception()) {
243  isolate->ScheduleThrow(isolate->scheduled_exception());
244 
245  DCHECK(try_catch.HasCaught());
246  Shell::ReportException(hooks->isolate_, &try_catch);
247  return;
248  }
249 
250  Local<Value> rcv = Undefined(hooks->isolate_);
251  Local<Context> context = hooks->isolate_->GetCurrentContext();
252  Local<Value> async_id =
253  promise->GetPrivate(context, hooks->async_id_smb.Get(hooks->isolate_))
254  .ToLocalChecked();
255  Local<Value> args[1] = {async_id};
256 
257  // This is unused. It's here to silence the warning about
258  // not using the MaybeLocal return value from Call.
259  MaybeLocal<Value> result;
260 
261  // Sacrifice the brevity for readability and debugfulness
262  if (type == PromiseHookType::kInit) {
263  if (!wrap->init_function().IsEmpty()) {
264  Local<Value> initArgs[4] = {
265  async_id,
266  String::NewFromUtf8(hooks->isolate_, "PROMISE",
268  .ToLocalChecked(),
269  promise
270  ->GetPrivate(context, hooks->trigger_id_smb.Get(hooks->isolate_))
271  .ToLocalChecked(),
272  promise};
273  result = wrap->init_function()->Call(context, rcv, 4, initArgs);
274  }
275  } else if (type == PromiseHookType::kBefore) {
276  if (!wrap->before_function().IsEmpty()) {
277  result = wrap->before_function()->Call(context, rcv, 1, args);
278  }
279  } else if (type == PromiseHookType::kAfter) {
280  if (!wrap->after_function().IsEmpty()) {
281  result = wrap->after_function()->Call(context, rcv, 1, args);
282  }
283  } else if (type == PromiseHookType::kResolve) {
284  if (!wrap->promiseResolve_function().IsEmpty()) {
285  result = wrap->promiseResolve_function()->Call(context, rcv, 1, args);
286  }
287  }
288 
289  if (try_catch.HasCaught()) {
290  Shell::ReportException(hooks->isolate_, &try_catch);
291  }
292 }
293 
294 } // namespace v8
static V8_INLINE Local< T > Cast(Local< S > that)
Definition: v8.h:251
Definition: v8.h:85
Definition: libplatform.h:13
static Local< Private > New(Isolate *isolate, Local< String > name=Local< String >())
Definition: api.cc:7945
static V8_WARN_UNUSED_RESULT MaybeLocal< String > NewFromUtf8(Isolate *isolate, const char *data, v8::NewStringType type, int length=-1)
Definition: api.cc:6511
static Local< FunctionTemplate > New(Isolate *isolate, FunctionCallback callback=nullptr, Local< Value > data=Local< Value >(), Local< Signature > signature=Local< Signature >(), int length=0, ConstructorBehavior behavior=ConstructorBehavior::kAllow, SideEffectType side_effect_type=SideEffectType::kHasSideEffect)
Definition: api.cc:1417
V8_INLINE void Reset()
Definition: v8.h:9469