#include "async-wrap.h" #include "async-wrap-inl.h" #include "env.h" #include "env-inl.h" #include "util.h" #include "util-inl.h" #include "v8.h" #include "v8-profiler.h" using v8::Array; using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::Handle; using v8::HandleScope; using v8::HeapProfiler; using v8::Integer; using v8::Isolate; using v8::Local; using v8::Object; using v8::RetainedObjectInfo; using v8::TryCatch; using v8::Value; using v8::kExternalUint32Array; namespace node { static const char* const provider_names[] = { #define V(PROVIDER) \ #PROVIDER, NODE_ASYNC_PROVIDER_TYPES(V) #undef V }; class RetainedAsyncInfo: public RetainedObjectInfo { public: explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap); virtual void Dispose() override; virtual bool IsEquivalent(RetainedObjectInfo* other) override; virtual intptr_t GetHash() override; virtual const char* GetLabel() override; virtual intptr_t GetSizeInBytes() override; private: const char* label_; const AsyncWrap* wrap_; const int length_; }; RetainedAsyncInfo::RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap) : label_(provider_names[class_id - NODE_ASYNC_ID_OFFSET]), wrap_(wrap), length_(wrap->self_size()) { } void RetainedAsyncInfo::Dispose() { delete this; } bool RetainedAsyncInfo::IsEquivalent(RetainedObjectInfo* other) { return label_ == other->GetLabel() && wrap_ == static_cast(other)->wrap_; } intptr_t RetainedAsyncInfo::GetHash() { return reinterpret_cast(wrap_); } const char* RetainedAsyncInfo::GetLabel() { return label_; } intptr_t RetainedAsyncInfo::GetSizeInBytes() { return length_; } RetainedObjectInfo* WrapperInfo(uint16_t class_id, Handle wrapper) { // No class_id should be the provider type of NONE. CHECK_NE(NODE_ASYNC_ID_OFFSET, class_id); CHECK(wrapper->IsObject()); CHECK(!wrapper.IsEmpty()); Local object = wrapper.As(); CHECK_GT(object->InternalFieldCount(), 0); AsyncWrap* wrap = Unwrap(object); CHECK_NE(nullptr, wrap); return new RetainedAsyncInfo(class_id, wrap); } // end RetainedAsyncInfo static void EnableHooksJS(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->async_hooks()->set_enable_callbacks(1); } static void DisableHooksJS(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->async_hooks()->set_enable_callbacks(0); } static void SetupHooks(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsFunction()); CHECK(args[1]->IsFunction()); CHECK(args[2]->IsFunction()); env->set_async_hooks_init_function(args[0].As()); env->set_async_hooks_pre_function(args[1].As()); env->set_async_hooks_post_function(args[2].As()); env->set_using_asyncwrap(true); } static void Initialize(Handle target, Handle unused, Handle context) { Environment* env = Environment::GetCurrent(context); Isolate* isolate = env->isolate(); HandleScope scope(isolate); env->SetMethod(target, "setupHooks", SetupHooks); env->SetMethod(target, "disable", DisableHooksJS); env->SetMethod(target, "enable", EnableHooksJS); Local async_providers = Object::New(isolate); #define V(PROVIDER) \ async_providers->Set(FIXED_ONE_BYTE_STRING(isolate, #PROVIDER), \ Integer::New(isolate, AsyncWrap::PROVIDER_ ## PROVIDER)); NODE_ASYNC_PROVIDER_TYPES(V) #undef V target->Set(FIXED_ONE_BYTE_STRING(isolate, "Providers"), async_providers); } void LoadAsyncWrapperInfo(Environment* env) { HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler(); #define V(PROVIDER) \ heap_profiler->SetWrapperClassInfoProvider( \ (NODE_ASYNC_ID_OFFSET + AsyncWrap::PROVIDER_ ## PROVIDER), WrapperInfo); NODE_ASYNC_PROVIDER_TYPES(V) #undef V } Handle AsyncWrap::MakeCallback(const Handle cb, int argc, Handle* argv) { CHECK(env()->context() == env()->isolate()->GetCurrentContext()); Local context = object(); Local process = env()->process_object(); Local domain; bool has_domain = false; bool has_abort_on_uncaught_and_domains = false; if (env()->using_domains()) { Local domain_v = context->Get(env()->domain_string()); has_domain = domain_v->IsObject(); if (has_domain) { domain = domain_v.As(); if (domain->Get(env()->disposed_string())->IsTrue()) return Undefined(env()->isolate()); has_abort_on_uncaught_and_domains = env()->using_abort_on_uncaught_exc(); } } TryCatch try_catch; try_catch.SetVerbose(true); if (has_domain) { Local enter_v = domain->Get(env()->enter_string()); if (enter_v->IsFunction()) { enter_v.As()->Call(domain, 0, nullptr); if (try_catch.HasCaught()) return Undefined(env()->isolate()); } } if (has_async_queue()) { try_catch.SetVerbose(false); env()->async_hooks_pre_function()->Call(context, 0, nullptr); if (try_catch.HasCaught()) FatalError("node::AsyncWrap::MakeCallback", "pre hook threw"); try_catch.SetVerbose(true); } Local ret; if (has_abort_on_uncaught_and_domains) { Local fn = process->Get(env()->domain_abort_uncaught_exc_string()); if (fn->IsFunction()) { Local special_context = Array::New(env()->isolate(), 2); special_context->Set(0, context); special_context->Set(1, cb); ret = fn.As()->Call(special_context, argc, argv); } else { ret = cb->Call(context, argc, argv); } } else { ret = cb->Call(context, argc, argv); } if (try_catch.HasCaught()) { return Undefined(env()->isolate()); } if (has_async_queue()) { try_catch.SetVerbose(false); env()->async_hooks_post_function()->Call(context, 0, nullptr); if (try_catch.HasCaught()) FatalError("node::AsyncWrap::MakeCallback", "post hook threw"); try_catch.SetVerbose(true); } if (has_domain) { Local exit_v = domain->Get(env()->exit_string()); if (exit_v->IsFunction()) { exit_v.As()->Call(domain, 0, nullptr); if (try_catch.HasCaught()) return Undefined(env()->isolate()); } } Environment::TickInfo* tick_info = env()->tick_info(); if (tick_info->in_tick()) { return ret; } if (tick_info->length() == 0) { env()->isolate()->RunMicrotasks(); } if (tick_info->length() == 0) { tick_info->set_index(0); return ret; } tick_info->set_in_tick(true); env()->tick_callback_function()->Call(process, 0, nullptr); tick_info->set_in_tick(false); if (try_catch.HasCaught()) { tick_info->set_last_threw(true); return Undefined(env()->isolate()); } return ret; } } // namespace node NODE_MODULE_CONTEXT_AWARE_BUILTIN(async_wrap, node::Initialize)