Line data Source code
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 81 : void AsyncHooksWrap::Enable() { enabled_ = true; }
12 :
13 9 : void AsyncHooksWrap::Disable() { enabled_ = false; }
14 :
15 : v8::Local<v8::Function> AsyncHooksWrap::init_function() const {
16 799 : return init_function_.Get(isolate_);
17 : }
18 54 : void AsyncHooksWrap::set_init_function(v8::Local<v8::Function> value) {
19 54 : init_function_.Reset(isolate_, value);
20 54 : }
21 : v8::Local<v8::Function> AsyncHooksWrap::before_function() const {
22 247 : return before_function_.Get(isolate_);
23 : }
24 9 : void AsyncHooksWrap::set_before_function(v8::Local<v8::Function> value) {
25 9 : before_function_.Reset(isolate_, value);
26 9 : }
27 : v8::Local<v8::Function> AsyncHooksWrap::after_function() const {
28 304 : return after_function_.Get(isolate_);
29 : }
30 27 : void AsyncHooksWrap::set_after_function(v8::Local<v8::Function> value) {
31 27 : after_function_.Reset(isolate_, value);
32 27 : }
33 : v8::Local<v8::Function> AsyncHooksWrap::promiseResolve_function() const {
34 523 : return promiseResolve_function_.Get(isolate_);
35 : }
36 9 : void AsyncHooksWrap::set_promiseResolve_function(
37 : v8::Local<v8::Function> value) {
38 9 : promiseResolve_function_.Reset(isolate_, value);
39 9 : }
40 :
41 108 : static AsyncHooksWrap* UnwrapHook(
42 : const v8::FunctionCallbackInfo<v8::Value>& args) {
43 : Isolate* isolate = args.GetIsolate();
44 216 : HandleScope scope(isolate);
45 : Local<Object> hook = args.This();
46 :
47 : AsyncHooks* hooks = PerIsolateData::Get(isolate)->GetAsyncHooks();
48 :
49 108 : if (!hooks->async_hook_ctor.Get(isolate)->HasInstance(hook)) {
50 : isolate->ThrowException(
51 18 : String::NewFromUtf8(
52 : isolate, "Invalid 'this' passed instead of AsyncHooks instance",
53 : NewStringType::kNormal)
54 18 : .ToLocalChecked());
55 : return nullptr;
56 : }
57 :
58 : Local<External> wrap = Local<External>::Cast(hook->GetInternalField(0));
59 90 : void* ptr = wrap->Value();
60 : return static_cast<AsyncHooksWrap*>(ptr);
61 : }
62 :
63 90 : static void EnableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
64 90 : AsyncHooksWrap* wrap = UnwrapHook(args);
65 90 : if (wrap) {
66 : wrap->Enable();
67 : }
68 90 : }
69 :
70 18 : static void DisableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
71 18 : AsyncHooksWrap* wrap = UnwrapHook(args);
72 18 : if (wrap) {
73 : wrap->Disable();
74 : }
75 18 : }
76 :
77 45 : async_id_t AsyncHooks::GetExecutionAsyncId() const {
78 45 : return asyncContexts.top().execution_async_id;
79 : }
80 :
81 45 : async_id_t AsyncHooks::GetTriggerAsyncId() const {
82 45 : return asyncContexts.top().trigger_async_id;
83 : }
84 :
85 117 : Local<Object> AsyncHooks::CreateHook(
86 : const v8::FunctionCallbackInfo<v8::Value>& args) {
87 : Isolate* isolate = args.GetIsolate();
88 117 : EscapableHandleScope handle_scope(isolate);
89 :
90 117 : Local<Context> currentContext = isolate->GetCurrentContext();
91 :
92 225 : if (args.Length() != 1 || !args[0]->IsObject()) {
93 : isolate->ThrowException(
94 27 : String::NewFromUtf8(isolate, "Invalid arguments passed to createHook",
95 : NewStringType::kNormal)
96 27 : .ToLocalChecked());
97 27 : return Local<Object>();
98 : }
99 :
100 180 : 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 270 : SET_HOOK_FN(init);
116 270 : SET_HOOK_FN(before);
117 270 : SET_HOOK_FN(after);
118 270 : SET_HOOK_FN(promiseResolve);
119 : #undef SET_HOOK_FN
120 :
121 90 : async_wraps_.push_back(wrap);
122 :
123 : Local<Object> obj = async_hooks_templ.Get(isolate)
124 90 : ->NewInstance(currentContext)
125 : .ToLocalChecked();
126 180 : obj->SetInternalField(0, External::New(isolate, wrap));
127 :
128 : return handle_scope.Escape(obj);
129 : }
130 :
131 792 : void AsyncHooks::ShellPromiseHook(PromiseHookType type, Local<Promise> promise,
132 : Local<Value> parent) {
133 : AsyncHooks* hooks =
134 792 : PerIsolateData::Get(promise->GetIsolate())->GetAsyncHooks();
135 :
136 1584 : HandleScope handle_scope(hooks->isolate_);
137 :
138 792 : Local<Context> currentContext = hooks->isolate_->GetCurrentContext();
139 :
140 792 : if (type == PromiseHookType::kInit) {
141 270 : ++hooks->current_async_id;
142 : Local<Integer> async_id =
143 270 : Integer::New(hooks->isolate_, hooks->current_async_id);
144 :
145 810 : CHECK(!promise
146 : ->HasPrivate(currentContext,
147 : hooks->async_id_smb.Get(hooks->isolate_))
148 : .ToChecked());
149 : promise->SetPrivate(currentContext,
150 540 : hooks->async_id_smb.Get(hooks->isolate_), async_id);
151 :
152 270 : if (parent->IsPromise()) {
153 : Local<Promise> parent_promise = parent.As<Promise>();
154 : Local<Value> parent_async_id =
155 : parent_promise
156 288 : ->GetPrivate(hooks->isolate_->GetCurrentContext(),
157 288 : hooks->async_id_smb.Get(hooks->isolate_))
158 144 : .ToLocalChecked();
159 : promise->SetPrivate(currentContext,
160 : hooks->trigger_id_smb.Get(hooks->isolate_),
161 288 : parent_async_id);
162 : } else {
163 126 : CHECK(parent->IsUndefined());
164 126 : Local<Integer> trigger_id = Integer::New(hooks->isolate_, 0);
165 : promise->SetPrivate(currentContext,
166 : hooks->trigger_id_smb.Get(hooks->isolate_),
167 252 : trigger_id);
168 : }
169 522 : } else if (type == PromiseHookType::kBefore) {
170 : AsyncContext ctx;
171 : ctx.execution_async_id =
172 : promise
173 252 : ->GetPrivate(hooks->isolate_->GetCurrentContext(),
174 252 : hooks->async_id_smb.Get(hooks->isolate_))
175 : .ToLocalChecked()
176 : .As<Integer>()
177 126 : ->Value();
178 : ctx.trigger_async_id =
179 : promise
180 252 : ->GetPrivate(hooks->isolate_->GetCurrentContext(),
181 252 : hooks->trigger_id_smb.Get(hooks->isolate_))
182 : .ToLocalChecked()
183 : .As<Integer>()
184 126 : ->Value();
185 : hooks->asyncContexts.push(ctx);
186 396 : } else if (type == PromiseHookType::kAfter) {
187 : hooks->asyncContexts.pop();
188 : }
189 :
190 2274 : for (AsyncHooksWrap* wrap : hooks->async_wraps_) {
191 1482 : PromiseHookDispatch(type, promise, parent, wrap, hooks);
192 : }
193 792 : }
194 :
195 50 : void AsyncHooks::Initialize() {
196 100 : HandleScope handle_scope(isolate_);
197 :
198 100 : async_hook_ctor.Reset(isolate_, FunctionTemplate::New(isolate_));
199 50 : async_hook_ctor.Get(isolate_)->SetClassName(
200 50 : String::NewFromUtf8(isolate_, "AsyncHook", NewStringType::kNormal)
201 50 : .ToLocalChecked());
202 :
203 50 : async_hooks_templ.Reset(isolate_,
204 150 : async_hook_ctor.Get(isolate_)->InstanceTemplate());
205 100 : async_hooks_templ.Get(isolate_)->SetInternalFieldCount(1);
206 150 : async_hooks_templ.Get(isolate_)->Set(
207 50 : String::NewFromUtf8(isolate_, "enable", v8::NewStringType::kNormal)
208 : .ToLocalChecked(),
209 50 : FunctionTemplate::New(isolate_, EnableHook));
210 150 : async_hooks_templ.Get(isolate_)->Set(
211 50 : String::NewFromUtf8(isolate_, "disable", v8::NewStringType::kNormal)
212 : .ToLocalChecked(),
213 50 : FunctionTemplate::New(isolate_, DisableHook));
214 :
215 100 : async_id_smb.Reset(isolate_, Private::New(isolate_));
216 100 : trigger_id_smb.Reset(isolate_, Private::New(isolate_));
217 :
218 50 : isolate_->SetPromiseHook(ShellPromiseHook);
219 50 : }
220 :
221 50 : void AsyncHooks::Deinitialize() {
222 50 : isolate_->SetPromiseHook(nullptr);
223 140 : for (AsyncHooksWrap* wrap : async_wraps_) {
224 180 : delete wrap;
225 : }
226 50 : }
227 :
228 1482 : void AsyncHooks::PromiseHookDispatch(PromiseHookType type,
229 : Local<Promise> promise,
230 : Local<Value> parent, AsyncHooksWrap* wrap,
231 : AsyncHooks* hooks) {
232 1482 : if (!wrap->IsEnabled()) {
233 132 : return;
234 : }
235 :
236 2700 : HandleScope handle_scope(hooks->isolate_);
237 :
238 2700 : TryCatch try_catch(hooks->isolate_);
239 1350 : try_catch.SetVerbose(true);
240 :
241 1350 : i::Isolate* isolate = reinterpret_cast<i::Isolate*>(hooks->isolate_);
242 1350 : if (isolate->has_scheduled_exception()) {
243 0 : isolate->ScheduleThrow(isolate->scheduled_exception());
244 :
245 : DCHECK(try_catch.HasCaught());
246 0 : Shell::ReportException(hooks->isolate_, &try_catch);
247 0 : return;
248 : }
249 :
250 : Local<Value> rcv = Undefined(hooks->isolate_);
251 1350 : Local<Context> context = hooks->isolate_->GetCurrentContext();
252 : Local<Value> async_id =
253 2700 : promise->GetPrivate(context, hooks->async_id_smb.Get(hooks->isolate_))
254 : .ToLocalChecked();
255 1350 : 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 1350 : if (type == PromiseHookType::kInit) {
263 466 : if (!wrap->init_function().IsEmpty()) {
264 : Local<Value> initArgs[4] = {
265 : async_id,
266 333 : String::NewFromUtf8(hooks->isolate_, "PROMISE",
267 333 : NewStringType::kNormal)
268 : .ToLocalChecked(),
269 : promise
270 666 : ->GetPrivate(context, hooks->trigger_id_smb.Get(hooks->isolate_))
271 : .ToLocalChecked(),
272 666 : promise};
273 333 : result = wrap->init_function()->Call(context, rcv, 4, initArgs);
274 : }
275 884 : } else if (type == PromiseHookType::kBefore) {
276 209 : if (!wrap->before_function().IsEmpty()) {
277 38 : result = wrap->before_function()->Call(context, rcv, 1, args);
278 : }
279 675 : } else if (type == PromiseHookType::kAfter) {
280 209 : if (!wrap->after_function().IsEmpty()) {
281 95 : result = wrap->after_function()->Call(context, rcv, 1, args);
282 : }
283 466 : } else if (type == PromiseHookType::kResolve) {
284 466 : if (!wrap->promiseResolve_function().IsEmpty()) {
285 57 : result = wrap->promiseResolve_function()->Call(context, rcv, 1, args);
286 : }
287 : }
288 :
289 1350 : if (try_catch.HasCaught()) {
290 57 : Shell::ReportException(hooks->isolate_, &try_catch);
291 : }
292 : }
293 :
294 60196 : } // namespace v8
|