1#include "node.h" 2#include "async_wrap-inl.h" 3#include "env-inl.h" 4#include "v8.h" 5 6namespace node { 7 8using v8::Context; 9using v8::EscapableHandleScope; 10using v8::Function; 11using v8::HandleScope; 12using v8::Isolate; 13using v8::Local; 14using v8::MaybeLocal; 15using v8::Object; 16using v8::String; 17using v8::Value; 18 19CallbackScope::CallbackScope(Isolate* isolate, 20 Local<Object> object, 21 async_context async_context) 22 : CallbackScope(Environment::GetCurrent(isolate), object, async_context) {} 23 24CallbackScope::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 34CallbackScope::~CallbackScope() { 35 if (try_catch_.HasCaught()) 36 private_->MarkAsFailed(); 37 delete private_; 38} 39 40InternalCallbackScope::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 47InternalCallbackScope::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 91InternalCallbackScope::~InternalCallbackScope() { 92 Close(); 93 env_->PopAsyncCallbackScope(); 94} 95 96void 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 170MaybeLocal<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 233MaybeLocal<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 244MaybeLocal<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 268MaybeLocal<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. 299MaybeLocal<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 327Local<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 338Local<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 349Local<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