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