1 #include "node.h"
2 #include "async_wrap-inl.h"
3 #include "env-inl.h"
4 #include "v8.h"
5
6 namespace node {
7
8 using v8::Context;
9 using v8::EscapableHandleScope;
10 using v8::Function;
11 using v8::HandleScope;
12 using v8::Isolate;
13 using v8::Local;
14 using v8::MaybeLocal;
15 using v8::MicrotasksScope;
16 using v8::Object;
17 using v8::String;
18 using v8::Value;
19
CallbackScope(Isolate * isolate,Local<Object> object,async_context async_context)20 CallbackScope::CallbackScope(Isolate* isolate,
21 Local<Object> object,
22 async_context async_context)
23 : CallbackScope(Environment::GetCurrent(isolate), object, async_context) {}
24
CallbackScope(Environment * env,Local<Object> object,async_context asyncContext)25 CallbackScope::CallbackScope(Environment* env,
26 Local<Object> object,
27 async_context asyncContext)
28 : private_(new InternalCallbackScope(env,
29 object,
30 asyncContext)),
31 try_catch_(env->isolate()) {
32 try_catch_.SetVerbose(true);
33 }
34
~CallbackScope()35 CallbackScope::~CallbackScope() {
36 if (try_catch_.HasCaught())
37 private_->MarkAsFailed();
38 delete private_;
39 }
40
InternalCallbackScope(AsyncWrap * async_wrap,int flags)41 InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap, int flags)
42 : InternalCallbackScope(async_wrap->env(),
43 async_wrap->object(),
44 { async_wrap->get_async_id(),
45 async_wrap->get_trigger_async_id() },
46 flags) {}
47
InternalCallbackScope(Environment * env,Local<Object> object,const async_context & asyncContext,int flags)48 InternalCallbackScope::InternalCallbackScope(Environment* env,
49 Local<Object> object,
50 const async_context& asyncContext,
51 int flags)
52 : env_(env),
53 async_context_(asyncContext),
54 object_(object),
55 skip_hooks_(flags & kSkipAsyncHooks),
56 skip_task_queues_(flags & kSkipTaskQueues) {
57 CHECK_NOT_NULL(env);
58 env->PushAsyncCallbackScope();
59
60 if (!env->can_call_into_js()) {
61 failed_ = true;
62 return;
63 }
64
65 HandleScope handle_scope(env->isolate());
66 // If you hit this assertion, you forgot to enter the v8::Context first.
67 CHECK_EQ(Environment::GetCurrent(env->isolate()), env);
68
69 env->isolate()->SetIdle(false);
70
71 env->async_hooks()->push_async_context(
72 async_context_.async_id, async_context_.trigger_async_id, object);
73
74 pushed_ids_ = true;
75
76 if (asyncContext.async_id != 0 && !skip_hooks_) {
77 // No need to check a return value because the application will exit if
78 // an exception occurs.
79 AsyncWrap::EmitBefore(env, asyncContext.async_id);
80 }
81 }
82
~InternalCallbackScope()83 InternalCallbackScope::~InternalCallbackScope() {
84 Close();
85 env_->PopAsyncCallbackScope();
86 }
87
Close()88 void InternalCallbackScope::Close() {
89 if (closed_) return;
90 closed_ = true;
91
92 auto idle = OnScopeLeave([&]() { env_->isolate()->SetIdle(true); });
93
94 if (!env_->can_call_into_js()) return;
95 auto perform_stopping_check = [&]() {
96 if (env_->is_stopping()) {
97 MarkAsFailed();
98 env_->async_hooks()->clear_async_id_stack();
99 }
100 };
101 perform_stopping_check();
102
103 if (!failed_ && async_context_.async_id != 0 && !skip_hooks_) {
104 AsyncWrap::EmitAfter(env_, async_context_.async_id);
105 }
106
107 if (pushed_ids_)
108 env_->async_hooks()->pop_async_context(async_context_.async_id);
109
110 if (failed_) return;
111
112 if (env_->async_callback_scope_depth() > 1 || skip_task_queues_) {
113 return;
114 }
115
116 TickInfo* tick_info = env_->tick_info();
117
118 if (!env_->can_call_into_js()) return;
119
120 auto weakref_cleanup = OnScopeLeave([&]() { env_->RunWeakRefCleanup(); });
121
122 if (!tick_info->has_tick_scheduled()) {
123 MicrotasksScope::PerformCheckpoint(env_->isolate());
124
125 perform_stopping_check();
126 }
127
128 // Make sure the stack unwound properly. If there are nested MakeCallback's
129 // then it should return early and not reach this code.
130 if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
131 CHECK_EQ(env_->execution_async_id(), 0);
132 CHECK_EQ(env_->trigger_async_id(), 0);
133 }
134
135 if (!tick_info->has_tick_scheduled() && !tick_info->has_rejection_to_warn()) {
136 return;
137 }
138
139 HandleScope handle_scope(env_->isolate());
140 Local<Object> process = env_->process_object();
141
142 if (!env_->can_call_into_js()) return;
143
144 Local<Function> tick_callback = env_->tick_callback_function();
145
146 // The tick is triggered before JS land calls SetTickCallback
147 // to initializes the tick callback during bootstrap.
148 CHECK(!tick_callback.IsEmpty());
149
150 if (tick_callback->Call(env_->context(), process, 0, nullptr).IsEmpty()) {
151 failed_ = true;
152 }
153 perform_stopping_check();
154 }
155
InternalMakeCallback(Environment * env,Local<Object> resource,Local<Object> recv,const Local<Function> callback,int argc,Local<Value> argv[],async_context asyncContext)156 MaybeLocal<Value> InternalMakeCallback(Environment* env,
157 Local<Object> resource,
158 Local<Object> recv,
159 const Local<Function> callback,
160 int argc,
161 Local<Value> argv[],
162 async_context asyncContext) {
163 CHECK(!recv.IsEmpty());
164 #ifdef DEBUG
165 for (int i = 0; i < argc; i++)
166 CHECK(!argv[i].IsEmpty());
167 #endif
168
169 Local<Function> hook_cb = env->async_hooks_callback_trampoline();
170 int flags = InternalCallbackScope::kNoFlags;
171 bool use_async_hooks_trampoline = false;
172 AsyncHooks* async_hooks = env->async_hooks();
173 if (!hook_cb.IsEmpty()) {
174 // Use the callback trampoline if there are any before or after hooks, or
175 // we can expect some kind of usage of async_hooks.executionAsyncResource().
176 flags = InternalCallbackScope::kSkipAsyncHooks;
177 use_async_hooks_trampoline =
178 async_hooks->fields()[AsyncHooks::kBefore] +
179 async_hooks->fields()[AsyncHooks::kAfter] +
180 async_hooks->fields()[AsyncHooks::kUsesExecutionAsyncResource] > 0;
181 }
182
183 InternalCallbackScope scope(env, resource, asyncContext, flags);
184 if (scope.Failed()) {
185 return MaybeLocal<Value>();
186 }
187
188 MaybeLocal<Value> ret;
189
190 if (use_async_hooks_trampoline) {
191 MaybeStackBuffer<Local<Value>, 16> args(3 + argc);
192 args[0] = v8::Number::New(env->isolate(), asyncContext.async_id);
193 args[1] = resource;
194 args[2] = callback;
195 for (int i = 0; i < argc; i++) {
196 args[i + 3] = argv[i];
197 }
198 ret = hook_cb->Call(env->context(), recv, args.length(), &args[0]);
199 } else {
200 ret = callback->Call(env->context(), recv, argc, argv);
201 }
202
203 if (ret.IsEmpty()) {
204 scope.MarkAsFailed();
205 return MaybeLocal<Value>();
206 }
207
208 scope.Close();
209 if (scope.Failed()) {
210 return MaybeLocal<Value>();
211 }
212
213 return ret;
214 }
215
216 // Public MakeCallback()s
217
MakeCallback(Isolate * isolate,Local<Object> recv,const char * method,int argc,Local<Value> argv[],async_context asyncContext)218 MaybeLocal<Value> MakeCallback(Isolate* isolate,
219 Local<Object> recv,
220 const char* method,
221 int argc,
222 Local<Value> argv[],
223 async_context asyncContext) {
224 Local<String> method_string =
225 String::NewFromUtf8(isolate, method).ToLocalChecked();
226 return MakeCallback(isolate, recv, method_string, argc, argv, asyncContext);
227 }
228
MakeCallback(Isolate * isolate,Local<Object> recv,Local<String> symbol,int argc,Local<Value> argv[],async_context asyncContext)229 MaybeLocal<Value> MakeCallback(Isolate* isolate,
230 Local<Object> recv,
231 Local<String> symbol,
232 int argc,
233 Local<Value> argv[],
234 async_context asyncContext) {
235 // Check can_call_into_js() first because calling Get() might do so.
236 Environment* env = Environment::GetCurrent(recv->CreationContext());
237 CHECK_NOT_NULL(env);
238 if (!env->can_call_into_js()) return Local<Value>();
239
240 Local<Value> callback_v;
241 if (!recv->Get(isolate->GetCurrentContext(), symbol).ToLocal(&callback_v))
242 return Local<Value>();
243 if (!callback_v->IsFunction()) {
244 // This used to return an empty value, but Undefined() makes more sense
245 // since no exception is pending here.
246 return Undefined(isolate);
247 }
248 Local<Function> callback = callback_v.As<Function>();
249 return MakeCallback(isolate, recv, callback, argc, argv, asyncContext);
250 }
251
MakeCallback(Isolate * isolate,Local<Object> recv,Local<Function> callback,int argc,Local<Value> argv[],async_context asyncContext)252 MaybeLocal<Value> MakeCallback(Isolate* isolate,
253 Local<Object> recv,
254 Local<Function> callback,
255 int argc,
256 Local<Value> argv[],
257 async_context asyncContext) {
258 // Observe the following two subtleties:
259 //
260 // 1. The environment is retrieved from the callback function's context.
261 // 2. The context to enter is retrieved from the environment.
262 //
263 // Because of the AssignToContext() call in src/node_contextify.cc,
264 // the two contexts need not be the same.
265 Environment* env = Environment::GetCurrent(callback->CreationContext());
266 CHECK_NOT_NULL(env);
267 Context::Scope context_scope(env->context());
268 MaybeLocal<Value> ret =
269 InternalMakeCallback(env, recv, recv, callback, argc, argv, asyncContext);
270 if (ret.IsEmpty() && env->async_callback_scope_depth() == 0) {
271 // This is only for legacy compatibility and we may want to look into
272 // removing/adjusting it.
273 return Undefined(env->isolate());
274 }
275 return ret;
276 }
277
278 // Legacy MakeCallback()s
279
MakeCallback(Isolate * isolate,Local<Object> recv,const char * method,int argc,Local<Value> * argv)280 Local<Value> MakeCallback(Isolate* isolate,
281 Local<Object> recv,
282 const char* method,
283 int argc,
284 Local<Value>* argv) {
285 EscapableHandleScope handle_scope(isolate);
286 return handle_scope.Escape(
287 MakeCallback(isolate, recv, method, argc, argv, {0, 0})
288 .FromMaybe(Local<Value>()));
289 }
290
MakeCallback(Isolate * isolate,Local<Object> recv,Local<String> symbol,int argc,Local<Value> * argv)291 Local<Value> MakeCallback(Isolate* isolate,
292 Local<Object> recv,
293 Local<String> symbol,
294 int argc,
295 Local<Value>* argv) {
296 EscapableHandleScope handle_scope(isolate);
297 return handle_scope.Escape(
298 MakeCallback(isolate, recv, symbol, argc, argv, {0, 0})
299 .FromMaybe(Local<Value>()));
300 }
301
MakeCallback(Isolate * isolate,Local<Object> recv,Local<Function> callback,int argc,Local<Value> * argv)302 Local<Value> MakeCallback(Isolate* isolate,
303 Local<Object> recv,
304 Local<Function> callback,
305 int argc,
306 Local<Value>* argv) {
307 EscapableHandleScope handle_scope(isolate);
308 return handle_scope.Escape(
309 MakeCallback(isolate, recv, callback, argc, argv, {0, 0})
310 .FromMaybe(Local<Value>()));
311 }
312
313 } // namespace node
314