#include "env-inl.h" #include "node.h" #include "node_internals.h" #include "v8.h" #include namespace node { using v8::Array; using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::Isolate; using v8::kPromiseHandlerAddedAfterReject; using v8::kPromiseRejectAfterResolved; using v8::kPromiseRejectWithNoHandler; using v8::kPromiseResolveAfterResolved; using v8::Local; using v8::MaybeLocal; using v8::Number; using v8::Object; using v8::Promise; using v8::PromiseRejectEvent; using v8::PromiseRejectMessage; using v8::Value; namespace task_queue { static void RunMicrotasks(const FunctionCallbackInfo& args) { args.GetIsolate()->RunMicrotasks(); } static void SetTickCallback(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsFunction()); env->set_tick_callback_function(args[0].As()); } static void PromiseRejectCallback(PromiseRejectMessage message) { static std::atomic unhandledRejections{0}; static std::atomic rejectionsHandledAfter{0}; Local promise = message.GetPromise(); Isolate* isolate = promise->GetIsolate(); PromiseRejectEvent event = message.GetEvent(); Environment* env = Environment::GetCurrent(isolate); if (env == nullptr) return; Local callback = env->promise_reject_callback(); Local value; Local type = Number::New(env->isolate(), event); if (event == kPromiseRejectWithNoHandler) { value = message.GetValue(); unhandledRejections++; TRACE_COUNTER2(TRACING_CATEGORY_NODE2(promises, rejections), "rejections", "unhandled", unhandledRejections, "handledAfter", rejectionsHandledAfter); } else if (event == kPromiseHandlerAddedAfterReject) { value = Undefined(isolate); rejectionsHandledAfter++; TRACE_COUNTER2(TRACING_CATEGORY_NODE2(promises, rejections), "rejections", "unhandled", unhandledRejections, "handledAfter", rejectionsHandledAfter); } else if (event == kPromiseResolveAfterResolved) { value = message.GetValue(); } else if (event == kPromiseRejectAfterResolved) { value = message.GetValue(); } else { return; } if (value.IsEmpty()) { value = Undefined(isolate); } Local args[] = { type, promise, value }; MaybeLocal ret = callback->Call(env->context(), Undefined(isolate), arraysize(args), args); if (!ret.IsEmpty() && ret.ToLocalChecked()->IsTrue()) env->tick_info()->promise_rejections_toggle_on(); } static void InitializePromiseRejectCallback( const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); CHECK(args[0]->IsFunction()); // TODO(joyeecheung): this may be moved to somewhere earlier in the bootstrap // to make sure it's only called once isolate->SetPromiseRejectCallback(PromiseRejectCallback); env->set_promise_reject_callback(args[0].As()); } static void Initialize(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); Isolate* isolate = env->isolate(); env->SetMethod(target, "setTickCallback", SetTickCallback); env->SetMethod(target, "runMicrotasks", RunMicrotasks); target->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "tickInfo"), env->tick_info()->fields().GetJSArray()).FromJust(); Local events = Object::New(isolate); NODE_DEFINE_CONSTANT(events, kPromiseRejectWithNoHandler); NODE_DEFINE_CONSTANT(events, kPromiseHandlerAddedAfterReject); NODE_DEFINE_CONSTANT(events, kPromiseResolveAfterResolved); NODE_DEFINE_CONSTANT(events, kPromiseRejectAfterResolved); target->Set(env->context(), FIXED_ONE_BYTE_STRING(isolate, "promiseRejectEvents"), events).FromJust(); env->SetMethod(target, "initializePromiseRejectCallback", InitializePromiseRejectCallback); } } // namespace task_queue } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(task_queue, node::task_queue::Initialize)