• 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::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