mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
src: implement native changes for async_hooks
Changes in the native code for the upcoming async_hooks module. These have been separated to help with review and testing. Changes include: * Introduce an async id stack that tracks recursive calls into async execution contexts. For performance reasons the id stack is held as a double* and assigned to a Float64Array. If the stack grows too large it is then placed in it's own stack and replaced with a new double*. This should accommodate arbitrarily large stacks. I'm not especially happy with the complexity involved with this async id stack, but it's also the fastest and most full proof way of handling it that I have found. * Add helper functions in Environment and AsyncWrap to work with the async id stack. * Add AsyncWrap::Reset() to allow AsyncWrap instances that have been placed in a resource pool, instead of being released, to be reinitialized. AsyncWrap::AsyncWrap() also now uses Reset() for initialization. * AsyncWrap* parent no longer needs to be passed via the constructor. * Introduce Environment::AsyncHooks class to contain the needed native functionality. This includes the pointer to the async id stack, and array of v8::Eternal<v8::String>'s that hold the names of all providers, mechanisms for storing/retrieving the trigger id, etc. * Introduce Environment::AsyncHooks::ExecScope as a way to track the current id and trigger id of function execution via RAII. * If the user passes --abort-on-uncaught-exception then instead of throwing the application will print a stack trace and abort. PR-URL: https://github.com/nodejs/node/pull/12892 Ref: https://github.com/nodejs/node/pull/11883 Ref: https://github.com/nodejs/node/pull/8531 Reviewed-By: Andreas Madsen <amwebdk@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
This commit is contained in:
1 parent
fe2df3b842
commit
c0bde73f1b
23 files changed
+528
-295
No files matched your search
@@ -36,18 +36,18 @@
|
||||
|
||||
namespace node {
|
||||
|
||||
inline bool AsyncWrap::ran_init_callback() const {
|
||||
return static_cast<bool>(bits_ & 1);
|
||||
}
|
||||
|
||||
|
||||
inline AsyncWrap::ProviderType AsyncWrap::provider_type() const {
|
||||
return static_cast<ProviderType>(bits_ >> 1);
|
||||
return provider_type_;
|
||||
}
|
||||
|
||||
|
||||
inline double AsyncWrap::get_id() const {
|
||||
return id_;
|
||||
return async_id_;
|
||||
}
|
||||
|
||||
|
||||
inline double AsyncWrap::get_trigger_id() const {
|
||||
return trigger_id_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+174
-118
@@ -30,13 +30,14 @@
|
||||
#include "v8.h"
|
||||
#include "v8-profiler.h"
|
||||
|
||||
using v8::Boolean;
|
||||
using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::Context;
|
||||
using v8::Float64Array;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::HandleScope;
|
||||
using v8::HeapProfiler;
|
||||
using v8::Int32;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
@@ -45,8 +46,11 @@ using v8::Number;
|
||||
using v8::Object;
|
||||
using v8::RetainedObjectInfo;
|
||||
using v8::TryCatch;
|
||||
using v8::Uint32Array;
|
||||
using v8::Value;
|
||||
|
||||
using AsyncHooks = node::Environment::AsyncHooks;
|
||||
|
||||
namespace node {
|
||||
|
||||
static const char* const provider_names[] = {
|
||||
@@ -57,6 +61,8 @@ static const char* const provider_names[] = {
|
||||
};
|
||||
|
||||
|
||||
// Report correct information in a heapdump.
|
||||
|
||||
class RetainedAsyncInfo: public RetainedObjectInfo {
|
||||
public:
|
||||
explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap);
|
||||
@@ -128,55 +134,31 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local<Value> wrapper) {
|
||||
// end RetainedAsyncInfo
|
||||
|
||||
|
||||
static void EnableHooksJS(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Local<Function> init_fn = env->async_hooks_init_function();
|
||||
if (init_fn.IsEmpty() || !init_fn->IsFunction())
|
||||
return env->ThrowTypeError("init callback is not assigned to a function");
|
||||
env->async_hooks()->set_enable_callbacks(1);
|
||||
}
|
||||
|
||||
|
||||
static void DisableHooksJS(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
env->async_hooks()->set_enable_callbacks(0);
|
||||
}
|
||||
|
||||
|
||||
static void SetupHooks(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
if (env->async_hooks()->callbacks_enabled())
|
||||
return env->ThrowError("hooks should not be set while also enabled");
|
||||
if (!args[0]->IsObject())
|
||||
return env->ThrowTypeError("first argument must be an object");
|
||||
|
||||
// All of init, before, after, destroy are supplied by async_hooks
|
||||
// internally, so this should every only be called once. At which time all
|
||||
// the functions should be set. Detect this by checking if init !IsEmpty().
|
||||
CHECK(env->async_hooks_init_function().IsEmpty());
|
||||
|
||||
Local<Object> fn_obj = args[0].As<Object>();
|
||||
|
||||
Local<Value> init_v = fn_obj->Get(
|
||||
env->context(),
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "init")).ToLocalChecked();
|
||||
Local<Value> pre_v = fn_obj->Get(
|
||||
env->context(),
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "pre")).ToLocalChecked();
|
||||
Local<Value> post_v = fn_obj->Get(
|
||||
env->context(),
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "post")).ToLocalChecked();
|
||||
Local<Value> destroy_v = fn_obj->Get(
|
||||
env->context(),
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "destroy")).ToLocalChecked();
|
||||
#define SET_HOOK_FN(name) \
|
||||
Local<Value> name##_v = fn_obj->Get( \
|
||||
env->context(), \
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), #name)).ToLocalChecked(); \
|
||||
CHECK(name##_v->IsFunction()); \
|
||||
env->set_async_hooks_##name##_function(name##_v.As<Function>());
|
||||
|
||||
if (!init_v->IsFunction())
|
||||
return env->ThrowTypeError("init callback must be a function");
|
||||
|
||||
env->set_async_hooks_init_function(init_v.As<Function>());
|
||||
|
||||
if (pre_v->IsFunction())
|
||||
env->set_async_hooks_pre_function(pre_v.As<Function>());
|
||||
if (post_v->IsFunction())
|
||||
env->set_async_hooks_post_function(post_v.As<Function>());
|
||||
if (destroy_v->IsFunction())
|
||||
env->set_async_hooks_destroy_function(destroy_v.As<Function>());
|
||||
SET_HOOK_FN(init);
|
||||
SET_HOOK_FN(before);
|
||||
SET_HOOK_FN(after);
|
||||
SET_HOOK_FN(destroy);
|
||||
#undef SET_HOOK_FN
|
||||
}
|
||||
|
||||
|
||||
@@ -196,20 +178,76 @@ void AsyncWrap::Initialize(Local<Object> target,
|
||||
HandleScope scope(isolate);
|
||||
|
||||
env->SetMethod(target, "setupHooks", SetupHooks);
|
||||
env->SetMethod(target, "disable", DisableHooksJS);
|
||||
env->SetMethod(target, "enable", EnableHooksJS);
|
||||
|
||||
v8::PropertyAttribute ReadOnlyDontDelete =
|
||||
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
|
||||
|
||||
#define FORCE_SET_TARGET_FIELD(obj, str, field) \
|
||||
(obj)->ForceSet(context, \
|
||||
FIXED_ONE_BYTE_STRING(isolate, str), \
|
||||
field, \
|
||||
ReadOnlyDontDelete).FromJust()
|
||||
|
||||
// Attach the uint32_t[] where each slot contains the count of the number of
|
||||
// callbacks waiting to be called on a particular event. It can then be
|
||||
// incremented/decremented from JS quickly to communicate to C++ if there are
|
||||
// any callbacks waiting to be called.
|
||||
uint32_t* fields_ptr = env->async_hooks()->fields();
|
||||
int fields_count = env->async_hooks()->fields_count();
|
||||
Local<ArrayBuffer> fields_ab =
|
||||
ArrayBuffer::New(isolate, fields_ptr, fields_count * sizeof(*fields_ptr));
|
||||
FORCE_SET_TARGET_FIELD(target,
|
||||
"async_hook_fields",
|
||||
Uint32Array::New(fields_ab, 0, fields_count));
|
||||
|
||||
// The following v8::Float64Array has 5 fields. These fields are shared in
|
||||
// this way to allow JS and C++ to read/write each value as quickly as
|
||||
// possible. The fields are represented as follows:
|
||||
//
|
||||
// kAsyncUid: Maintains the state of the next unique id to be assigned.
|
||||
//
|
||||
// kInitTriggerId: Write the id of the resource responsible for a handle's
|
||||
// creation just before calling the new handle's constructor. After the new
|
||||
// handle is constructed kInitTriggerId is set back to 0.
|
||||
double* uid_fields_ptr = env->async_hooks()->uid_fields();
|
||||
int uid_fields_count = env->async_hooks()->uid_fields_count();
|
||||
Local<ArrayBuffer> uid_fields_ab = ArrayBuffer::New(
|
||||
isolate,
|
||||
uid_fields_ptr,
|
||||
uid_fields_count * sizeof(*uid_fields_ptr));
|
||||
FORCE_SET_TARGET_FIELD(target,
|
||||
"async_uid_fields",
|
||||
Float64Array::New(uid_fields_ab, 0, uid_fields_count));
|
||||
|
||||
Local<Object> constants = Object::New(isolate);
|
||||
#define SET_HOOKS_CONSTANT(name) \
|
||||
FORCE_SET_TARGET_FIELD( \
|
||||
constants, #name, Integer::New(isolate, AsyncHooks::name));
|
||||
|
||||
SET_HOOKS_CONSTANT(kInit);
|
||||
SET_HOOKS_CONSTANT(kBefore);
|
||||
SET_HOOKS_CONSTANT(kAfter);
|
||||
SET_HOOKS_CONSTANT(kDestroy);
|
||||
SET_HOOKS_CONSTANT(kCurrentAsyncId);
|
||||
SET_HOOKS_CONSTANT(kCurrentTriggerId);
|
||||
SET_HOOKS_CONSTANT(kAsyncUidCntr);
|
||||
SET_HOOKS_CONSTANT(kInitTriggerId);
|
||||
#undef SET_HOOKS_CONSTANT
|
||||
FORCE_SET_TARGET_FIELD(target, "constants", constants);
|
||||
|
||||
Local<Object> async_providers = Object::New(isolate);
|
||||
#define V(PROVIDER) \
|
||||
async_providers->Set(FIXED_ONE_BYTE_STRING(isolate, #PROVIDER), \
|
||||
Integer::New(isolate, AsyncWrap::PROVIDER_ ## PROVIDER));
|
||||
#define V(p) \
|
||||
FORCE_SET_TARGET_FIELD( \
|
||||
async_providers, #p, Integer::New(isolate, AsyncWrap::PROVIDER_ ## p));
|
||||
NODE_ASYNC_PROVIDER_TYPES(V)
|
||||
#undef V
|
||||
target->Set(FIXED_ONE_BYTE_STRING(isolate, "Providers"), async_providers);
|
||||
FORCE_SET_TARGET_FIELD(target, "Providers", async_providers);
|
||||
|
||||
#undef FORCE_SET_TARGET_FIELD
|
||||
|
||||
env->set_async_hooks_init_function(Local<Function>());
|
||||
env->set_async_hooks_pre_function(Local<Function>());
|
||||
env->set_async_hooks_post_function(Local<Function>());
|
||||
env->set_async_hooks_before_function(Local<Function>());
|
||||
env->set_async_hooks_after_function(Local<Function>());
|
||||
env->set_async_hooks_destroy_function(Local<Function>());
|
||||
}
|
||||
|
||||
@@ -218,16 +256,11 @@ void AsyncWrap::DestroyIdsCb(uv_idle_t* handle) {
|
||||
uv_idle_stop(handle);
|
||||
|
||||
Environment* env = Environment::from_destroy_ids_idle_handle(handle);
|
||||
// None of the V8 calls done outside the HandleScope leak a handle. If this
|
||||
// changes in the future then the SealHandleScope wrapping the uv_run()
|
||||
// will catch this can cause the process to abort.
|
||||
|
||||
HandleScope handle_scope(env->isolate());
|
||||
Context::Scope context_scope(env->context());
|
||||
Local<Function> fn = env->async_hooks_destroy_function();
|
||||
|
||||
if (fn.IsEmpty())
|
||||
return env->destroy_ids_list()->clear();
|
||||
|
||||
TryCatch try_catch(env->isolate());
|
||||
|
||||
std::vector<double> destroy_ids_list;
|
||||
@@ -262,59 +295,24 @@ void LoadAsyncWrapperInfo(Environment* env) {
|
||||
|
||||
AsyncWrap::AsyncWrap(Environment* env,
|
||||
Local<Object> object,
|
||||
ProviderType provider,
|
||||
AsyncWrap* parent)
|
||||
: BaseObject(env, object), bits_(static_cast<uint32_t>(provider) << 1),
|
||||
id_(env->get_async_wrap_uid()) {
|
||||
ProviderType provider)
|
||||
: BaseObject(env, object),
|
||||
provider_type_(provider) {
|
||||
CHECK_NE(provider, PROVIDER_NONE);
|
||||
CHECK_GE(object->InternalFieldCount(), 1);
|
||||
|
||||
// Shift provider value over to prevent id collision.
|
||||
persistent().SetWrapperClassId(NODE_ASYNC_ID_OFFSET + provider);
|
||||
|
||||
Local<Function> init_fn = env->async_hooks_init_function();
|
||||
|
||||
// No init callback exists, no reason to go on.
|
||||
if (init_fn.IsEmpty())
|
||||
return;
|
||||
|
||||
// If async wrap callbacks are disabled and no parent was passed that has
|
||||
// run the init callback then return.
|
||||
if (!env->async_wrap_callbacks_enabled() &&
|
||||
(parent == nullptr || !parent->ran_init_callback()))
|
||||
return;
|
||||
|
||||
HandleScope scope(env->isolate());
|
||||
|
||||
Local<Value> argv[] = {
|
||||
Number::New(env->isolate(), get_id()),
|
||||
Int32::New(env->isolate(), provider),
|
||||
Null(env->isolate()),
|
||||
Null(env->isolate())
|
||||
};
|
||||
|
||||
if (parent != nullptr) {
|
||||
argv[2] = Number::New(env->isolate(), parent->get_id());
|
||||
argv[3] = parent->object();
|
||||
}
|
||||
|
||||
TryCatch try_catch(env->isolate());
|
||||
|
||||
MaybeLocal<Value> ret =
|
||||
init_fn->Call(env->context(), object, arraysize(argv), argv);
|
||||
|
||||
if (ret.IsEmpty()) {
|
||||
ClearFatalExceptionHandlers(env);
|
||||
FatalException(env->isolate(), try_catch);
|
||||
}
|
||||
|
||||
bits_ |= 1; // ran_init_callback() is true now.
|
||||
// Use AsyncReset() call to execute the init() callbacks.
|
||||
AsyncReset();
|
||||
}
|
||||
|
||||
|
||||
AsyncWrap::~AsyncWrap() {
|
||||
if (!ran_init_callback())
|
||||
if (env()->async_hooks()->fields()[AsyncHooks::kDestroy] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (env()->destroy_ids_list()->empty())
|
||||
uv_idle_start(env()->destroy_ids_idle_handle(), DestroyIdsCb);
|
||||
@@ -323,16 +321,49 @@ AsyncWrap::~AsyncWrap() {
|
||||
}
|
||||
|
||||
|
||||
// Generalized call for both the constructor and for handles that are pooled
|
||||
// and reused over their lifetime. This way a new uid can be assigned when
|
||||
// the resource is pulled out of the pool and put back into use.
|
||||
void AsyncWrap::Reset() {
|
||||
AsyncHooks* async_hooks = env()->async_hooks();
|
||||
async_id_ = env()->new_async_id();
|
||||
trigger_id_ = env()->get_init_trigger_id();
|
||||
|
||||
// Nothing to execute, so can continue normally.
|
||||
if (async_hooks->fields()[AsyncHooks::kInit] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
HandleScope scope(env()->isolate());
|
||||
Local<Function> init_fn = env()->async_hooks_init_function();
|
||||
|
||||
Local<Value> argv[] = {
|
||||
Number::New(env()->isolate(), get_id()),
|
||||
env()->async_hooks()->provider_string(provider_type()),
|
||||
object(),
|
||||
Number::New(env()->isolate(), get_trigger_id()),
|
||||
};
|
||||
|
||||
TryCatch try_catch(env()->isolate());
|
||||
MaybeLocal<Value> ret = init_fn->Call(
|
||||
env()->context(), object(), arraysize(argv), argv);
|
||||
|
||||
if (ret.IsEmpty()) {
|
||||
ClearFatalExceptionHandlers(env());
|
||||
FatalException(env()->isolate(), try_catch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
|
||||
int argc,
|
||||
Local<Value>* argv) {
|
||||
CHECK(env()->context() == env()->isolate()->GetCurrentContext());
|
||||
|
||||
Local<Function> pre_fn = env()->async_hooks_pre_function();
|
||||
Local<Function> post_fn = env()->async_hooks_post_function();
|
||||
Local<Value> uid = Number::New(env()->isolate(), get_id());
|
||||
AsyncHooks* async_hooks = env()->async_hooks();
|
||||
Local<Object> context = object();
|
||||
Local<Object> domain;
|
||||
Local<Value> uid;
|
||||
bool has_domain = false;
|
||||
|
||||
Environment::AsyncCallbackScope callback_scope(env());
|
||||
@@ -357,9 +388,15 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
|
||||
}
|
||||
}
|
||||
|
||||
if (ran_init_callback() && !pre_fn.IsEmpty()) {
|
||||
// Want currentId() to return the correct value from the callbacks.
|
||||
AsyncHooks::ExecScope exec_scope(env(), get_id(), get_trigger_id());
|
||||
|
||||
if (async_hooks->fields()[AsyncHooks::kBefore] > 0) {
|
||||
uid = Number::New(env()->isolate(), get_id());
|
||||
Local<Function> fn = env()->async_hooks_before_function();
|
||||
TryCatch try_catch(env()->isolate());
|
||||
MaybeLocal<Value> ar = pre_fn->Call(env()->context(), context, 1, &uid);
|
||||
MaybeLocal<Value> ar = fn->Call(
|
||||
env()->context(), Undefined(env()->isolate()), 1, &uid);
|
||||
if (ar.IsEmpty()) {
|
||||
ClearFatalExceptionHandlers(env());
|
||||
FatalException(env()->isolate(), try_catch);
|
||||
@@ -367,14 +404,23 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
|
||||
}
|
||||
}
|
||||
|
||||
Local<Value> ret = cb->Call(context, argc, argv);
|
||||
// Finally... Get to running the user's callback.
|
||||
MaybeLocal<Value> ret = cb->Call(env()->context(), context, argc, argv);
|
||||
|
||||
if (ran_init_callback() && !post_fn.IsEmpty()) {
|
||||
Local<Value> did_throw = Boolean::New(env()->isolate(), ret.IsEmpty());
|
||||
Local<Value> vals[] = { uid, did_throw };
|
||||
Local<Value> ret_v;
|
||||
if (!ret.ToLocal(&ret_v)) {
|
||||
return Local<Value>();
|
||||
}
|
||||
|
||||
// If the callback failed then the after() hooks will be called at the end
|
||||
// of _fatalException().
|
||||
if (async_hooks->fields()[AsyncHooks::kAfter] > 0) {
|
||||
if (uid.IsEmpty())
|
||||
uid = Number::New(env()->isolate(), get_id());
|
||||
Local<Function> fn = env()->async_hooks_after_function();
|
||||
TryCatch try_catch(env()->isolate());
|
||||
MaybeLocal<Value> ar =
|
||||
post_fn->Call(env()->context(), context, arraysize(vals), vals);
|
||||
MaybeLocal<Value> ar = fn->Call(
|
||||
env()->context(), Undefined(env()->isolate()), 1, &uid);
|
||||
if (ar.IsEmpty()) {
|
||||
ClearFatalExceptionHandlers(env());
|
||||
FatalException(env()->isolate(), try_catch);
|
||||
@@ -382,9 +428,8 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.IsEmpty()) {
|
||||
return ret;
|
||||
}
|
||||
// The execution scope of the id and trigger_id only go this far.
|
||||
exec_scope.Dispose();
|
||||
|
||||
if (has_domain) {
|
||||
Local<Value> exit_v = domain->Get(env()->exit_string());
|
||||
@@ -397,7 +442,7 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
|
||||
}
|
||||
|
||||
if (callback_scope.in_makecallback()) {
|
||||
return ret;
|
||||
return ret_v;
|
||||
}
|
||||
|
||||
Environment::TickInfo* tick_info = env()->tick_info();
|
||||
@@ -406,18 +451,29 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
|
||||
env()->isolate()->RunMicrotasks();
|
||||
}
|
||||
|
||||
// Make sure the stack unwound properly. If there are nested MakeCallback's
|
||||
// then it should return early and not reach this code.
|
||||
CHECK_EQ(env()->current_async_id(), 0);
|
||||
CHECK_EQ(env()->trigger_id(), 0);
|
||||
|
||||
Local<Object> process = env()->process_object();
|
||||
|
||||
if (tick_info->length() == 0) {
|
||||
tick_info->set_index(0);
|
||||
return ret;
|
||||
return ret_v;
|
||||
}
|
||||
|
||||
if (env()->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
|
||||
return Local<Value>();
|
||||
}
|
||||
MaybeLocal<Value> rcheck =
|
||||
env()->tick_callback_function()->Call(env()->context(),
|
||||
process,
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
return ret;
|
||||
// Make sure the stack unwound properly.
|
||||
CHECK_EQ(env()->current_async_id(), 0);
|
||||
CHECK_EQ(env()->trigger_id(), 0);
|
||||
|
||||
return rcheck.IsEmpty() ? Local<Value>() : ret_v;
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
+16
-15
@@ -76,8 +76,7 @@ class AsyncWrap : public BaseObject {
|
||||
|
||||
AsyncWrap(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
ProviderType provider,
|
||||
AsyncWrap* parent = nullptr);
|
||||
ProviderType provider);
|
||||
|
||||
virtual ~AsyncWrap();
|
||||
|
||||
@@ -93,28 +92,30 @@ class AsyncWrap : public BaseObject {
|
||||
|
||||
inline double get_id() const;
|
||||
|
||||
inline double get_trigger_id() const;
|
||||
|
||||
void Reset();
|
||||
|
||||
// Only call these within a valid HandleScope.
|
||||
// TODO(trevnorris): These should return a MaybeLocal.
|
||||
v8::Local<v8::Value> MakeCallback(const v8::Local<v8::Function> cb,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
inline v8::Local<v8::Value> MakeCallback(const v8::Local<v8::String> symbol,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
inline v8::Local<v8::Value> MakeCallback(uint32_t index,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
|
||||
virtual size_t self_size() const = 0;
|
||||
|
||||
private:
|
||||
inline AsyncWrap();
|
||||
inline bool ran_init_callback() const;
|
||||
|
||||
// When the async hooks init JS function is called from the constructor it is
|
||||
// expected the context object will receive a _asyncQueue object property
|
||||
// that will be used to call pre/post in MakeCallback.
|
||||
uint32_t bits_;
|
||||
const double id_;
|
||||
const ProviderType provider_type_;
|
||||
// Because the values may be Reset(), cannot be made const.
|
||||
double async_id_;
|
||||
double trigger_id_;
|
||||
};
|
||||
|
||||
void LoadAsyncWrapperInfo(Environment* env);
|
||||
|
||||
@@ -23,13 +23,11 @@ using v8::Value;
|
||||
template <typename WrapType, typename UVType>
|
||||
ConnectionWrap<WrapType, UVType>::ConnectionWrap(Environment* env,
|
||||
Local<Object> object,
|
||||
ProviderType provider,
|
||||
AsyncWrap* parent)
|
||||
ProviderType provider)
|
||||
: StreamWrap(env,
|
||||
object,
|
||||
reinterpret_cast<uv_stream_t*>(&handle_),
|
||||
provider,
|
||||
parent) {}
|
||||
provider) {}
|
||||
|
||||
|
||||
template <typename WrapType, typename UVType>
|
||||
@@ -53,6 +51,7 @@ void ConnectionWrap<WrapType, UVType>::OnConnection(uv_stream_t* handle,
|
||||
};
|
||||
|
||||
if (status == 0) {
|
||||
env->set_init_trigger_id(wrap_data->get_id());
|
||||
// Instantiate the client javascript object and handle.
|
||||
Local<Object> client_obj = WrapType::Instantiate(env, wrap_data);
|
||||
|
||||
@@ -115,14 +114,12 @@ void ConnectionWrap<WrapType, UVType>::AfterConnect(uv_connect_t* req,
|
||||
template ConnectionWrap<PipeWrap, uv_pipe_t>::ConnectionWrap(
|
||||
Environment* env,
|
||||
Local<Object> object,
|
||||
ProviderType provider,
|
||||
AsyncWrap* parent);
|
||||
ProviderType provider);
|
||||
|
||||
template ConnectionWrap<TCPWrap, uv_tcp_t>::ConnectionWrap(
|
||||
Environment* env,
|
||||
Local<Object> object,
|
||||
ProviderType provider,
|
||||
AsyncWrap* parent);
|
||||
ProviderType provider);
|
||||
|
||||
template void ConnectionWrap<PipeWrap, uv_pipe_t>::OnConnection(
|
||||
uv_stream_t* handle, int status);
|
||||
|
||||
@@ -22,8 +22,7 @@ class ConnectionWrap : public StreamWrap {
|
||||
protected:
|
||||
ConnectionWrap(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
ProviderType provider,
|
||||
AsyncWrap* parent);
|
||||
ProviderType provider);
|
||||
~ConnectionWrap() {
|
||||
}
|
||||
|
||||
|
||||
+141
-14
@@ -80,8 +80,29 @@ inline uint32_t* IsolateData::zero_fill_field() const {
|
||||
return zero_fill_field_;
|
||||
}
|
||||
|
||||
inline Environment::AsyncHooks::AsyncHooks() {
|
||||
for (int i = 0; i < kFieldsCount; i++) fields_[i] = 0;
|
||||
inline Environment::AsyncHooks::AsyncHooks(v8::Isolate* isolate)
|
||||
: isolate_(isolate),
|
||||
fields_(),
|
||||
uid_fields_() {
|
||||
v8::HandleScope handle_scope(isolate_);
|
||||
|
||||
// kAsyncUidCntr should start at 1 because that'll be the id the execution
|
||||
// context during bootstrap (code that runs before entering uv_run()).
|
||||
uid_fields_[AsyncHooks::kAsyncUidCntr] = 1;
|
||||
|
||||
// Create all the provider strings that will be passed to JS. Place them in
|
||||
// an array so the array index matches the PROVIDER id offset. This way the
|
||||
// strings can be retrieved quickly.
|
||||
#define V(Provider) \
|
||||
providers_[AsyncWrap::PROVIDER_ ## Provider].Set( \
|
||||
isolate_, \
|
||||
v8::String::NewFromOneByte( \
|
||||
isolate_, \
|
||||
reinterpret_cast<const uint8_t*>(#Provider), \
|
||||
v8::NewStringType::kInternalized, \
|
||||
sizeof(#Provider) - 1).ToLocalChecked());
|
||||
NODE_ASYNC_PROVIDER_TYPES(V)
|
||||
#undef V
|
||||
}
|
||||
|
||||
inline uint32_t* Environment::AsyncHooks::fields() {
|
||||
@@ -92,12 +113,94 @@ inline int Environment::AsyncHooks::fields_count() const {
|
||||
return kFieldsCount;
|
||||
}
|
||||
|
||||
inline bool Environment::AsyncHooks::callbacks_enabled() {
|
||||
return fields_[kEnableCallbacks] != 0;
|
||||
inline double* Environment::AsyncHooks::uid_fields() {
|
||||
return uid_fields_;
|
||||
}
|
||||
|
||||
inline void Environment::AsyncHooks::set_enable_callbacks(uint32_t flag) {
|
||||
fields_[kEnableCallbacks] = flag;
|
||||
inline int Environment::AsyncHooks::uid_fields_count() const {
|
||||
return kUidFieldsCount;
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> Environment::AsyncHooks::provider_string(int idx) {
|
||||
return providers_[idx].Get(isolate_);
|
||||
}
|
||||
|
||||
inline void Environment::AsyncHooks::push_ids(double async_id,
|
||||
double trigger_id) {
|
||||
CHECK_GE(async_id, 0);
|
||||
CHECK_GE(trigger_id, 0);
|
||||
|
||||
ids_stack_.push({ uid_fields_[kCurrentAsyncId],
|
||||
uid_fields_[kCurrentTriggerId] });
|
||||
uid_fields_[kCurrentAsyncId] = async_id;
|
||||
uid_fields_[kCurrentTriggerId] = trigger_id;
|
||||
}
|
||||
|
||||
inline bool Environment::AsyncHooks::pop_ids(double async_id) {
|
||||
// In case of an exception then this may have already been reset, if the
|
||||
// stack was multiple MakeCallback()'s deep.
|
||||
if (ids_stack_.empty()) return false;
|
||||
|
||||
// Ask for the async_id to be restored as a sanity check that the stack
|
||||
// hasn't been corrupted.
|
||||
if (uid_fields_[kCurrentAsyncId] != async_id) {
|
||||
fprintf(stderr,
|
||||
"Error: async hook stack has become corrupted ("
|
||||
"actual: %'.f, expected: %'.f)\n",
|
||||
uid_fields_[kCurrentAsyncId],
|
||||
async_id);
|
||||
Environment* env = Environment::GetCurrent(isolate_);
|
||||
DumpBacktrace(stderr);
|
||||
fflush(stderr);
|
||||
if (!env->abort_on_uncaught_exception())
|
||||
exit(1);
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
ABORT_NO_BACKTRACE();
|
||||
}
|
||||
|
||||
auto ids = ids_stack_.top();
|
||||
ids_stack_.pop();
|
||||
uid_fields_[kCurrentAsyncId] = ids.async_id;
|
||||
uid_fields_[kCurrentTriggerId] = ids.trigger_id;
|
||||
return !ids_stack_.empty();
|
||||
}
|
||||
|
||||
inline void Environment::AsyncHooks::clear_id_stack() {
|
||||
while (!ids_stack_.empty())
|
||||
ids_stack_.pop();
|
||||
uid_fields_[kCurrentAsyncId] = 0;
|
||||
uid_fields_[kCurrentTriggerId] = 0;
|
||||
}
|
||||
|
||||
inline Environment::AsyncHooks::InitScope::InitScope(
|
||||
Environment* env, double init_trigger_id)
|
||||
: env_(env),
|
||||
uid_fields_(env->async_hooks()->uid_fields()) {
|
||||
env->async_hooks()->push_ids(uid_fields_[AsyncHooks::kCurrentAsyncId],
|
||||
init_trigger_id);
|
||||
}
|
||||
|
||||
inline Environment::AsyncHooks::InitScope::~InitScope() {
|
||||
env_->async_hooks()->pop_ids(uid_fields_[AsyncHooks::kCurrentAsyncId]);
|
||||
}
|
||||
|
||||
inline Environment::AsyncHooks::ExecScope::ExecScope(
|
||||
Environment* env, double async_id, double trigger_id)
|
||||
: env_(env),
|
||||
async_id_(async_id),
|
||||
disposed_(false) {
|
||||
env->async_hooks()->push_ids(async_id, trigger_id);
|
||||
}
|
||||
|
||||
inline Environment::AsyncHooks::ExecScope::~ExecScope() {
|
||||
if (disposed_) return;
|
||||
Dispose();
|
||||
}
|
||||
|
||||
inline void Environment::AsyncHooks::ExecScope::Dispose() {
|
||||
disposed_ = true;
|
||||
env_->async_hooks()->pop_ids(async_id_);
|
||||
}
|
||||
|
||||
inline Environment::AsyncCallbackScope::AsyncCallbackScope(Environment* env)
|
||||
@@ -187,12 +290,13 @@ inline Environment::Environment(IsolateData* isolate_data,
|
||||
v8::Local<v8::Context> context)
|
||||
: isolate_(context->GetIsolate()),
|
||||
isolate_data_(isolate_data),
|
||||
async_hooks_(context->GetIsolate()),
|
||||
timer_base_(uv_now(isolate_data->event_loop())),
|
||||
using_domains_(false),
|
||||
printed_error_(false),
|
||||
trace_sync_io_(false),
|
||||
abort_on_uncaught_exception_(false),
|
||||
makecallback_cntr_(0),
|
||||
async_wrap_id_(0),
|
||||
#if HAVE_INSPECTOR
|
||||
inspector_agent_(this),
|
||||
#endif
|
||||
@@ -231,11 +335,6 @@ inline v8::Isolate* Environment::isolate() const {
|
||||
return isolate_;
|
||||
}
|
||||
|
||||
inline bool Environment::async_wrap_callbacks_enabled() const {
|
||||
// The const_cast is okay, it doesn't violate conceptual const-ness.
|
||||
return const_cast<Environment*>(this)->async_hooks()->callbacks_enabled();
|
||||
}
|
||||
|
||||
inline bool Environment::in_domain() const {
|
||||
// The const_cast is okay, it doesn't violate conceptual const-ness.
|
||||
return using_domains() &&
|
||||
@@ -314,14 +413,42 @@ inline void Environment::set_trace_sync_io(bool value) {
|
||||
trace_sync_io_ = value;
|
||||
}
|
||||
|
||||
inline double Environment::get_async_wrap_uid() {
|
||||
return ++async_wrap_id_;
|
||||
inline bool Environment::abort_on_uncaught_exception() const {
|
||||
return abort_on_uncaught_exception_;
|
||||
}
|
||||
|
||||
inline void Environment::set_abort_on_uncaught_exception(bool value) {
|
||||
abort_on_uncaught_exception_ = value;
|
||||
}
|
||||
|
||||
inline std::vector<double>* Environment::destroy_ids_list() {
|
||||
return &destroy_ids_list_;
|
||||
}
|
||||
|
||||
inline double Environment::new_async_id() {
|
||||
return ++async_hooks()->uid_fields()[AsyncHooks::kAsyncUidCntr];
|
||||
}
|
||||
|
||||
inline double Environment::current_async_id() {
|
||||
return async_hooks()->uid_fields()[AsyncHooks::kCurrentAsyncId];
|
||||
}
|
||||
|
||||
inline double Environment::trigger_id() {
|
||||
return async_hooks()->uid_fields()[AsyncHooks::kCurrentTriggerId];
|
||||
}
|
||||
|
||||
inline double Environment::get_init_trigger_id() {
|
||||
double* uid_fields = async_hooks()->uid_fields();
|
||||
double tid = uid_fields[AsyncHooks::kInitTriggerId];
|
||||
uid_fields[AsyncHooks::kInitTriggerId] = 0;
|
||||
if (tid <= 0) tid = current_async_id();
|
||||
return tid;
|
||||
}
|
||||
|
||||
inline void Environment::set_init_trigger_id(const double id) {
|
||||
async_hooks()->uid_fields()[AsyncHooks::kInitTriggerId] = id;
|
||||
}
|
||||
|
||||
inline double* Environment::heap_statistics_buffer() const {
|
||||
CHECK_NE(heap_statistics_buffer_, nullptr);
|
||||
return heap_statistics_buffer_;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <list>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
|
||||
namespace node {
|
||||
|
||||
@@ -87,7 +88,6 @@ namespace node {
|
||||
V(address_string, "address") \
|
||||
V(args_string, "args") \
|
||||
V(async, "async") \
|
||||
V(async_queue_string, "_asyncQueue") \
|
||||
V(buffer_string, "buffer") \
|
||||
V(bytes_string, "bytes") \
|
||||
V(bytes_parsed_string, "bytesParsed") \
|
||||
@@ -254,8 +254,8 @@ namespace node {
|
||||
V(as_external, v8::External) \
|
||||
V(async_hooks_destroy_function, v8::Function) \
|
||||
V(async_hooks_init_function, v8::Function) \
|
||||
V(async_hooks_post_function, v8::Function) \
|
||||
V(async_hooks_pre_function, v8::Function) \
|
||||
V(async_hooks_before_function, v8::Function) \
|
||||
V(async_hooks_after_function, v8::Function) \
|
||||
V(binding_cache_object, v8::Object) \
|
||||
V(buffer_constructor_function, v8::Function) \
|
||||
V(buffer_prototype_object, v8::Object) \
|
||||
@@ -291,6 +291,11 @@ struct node_ares_task {
|
||||
RB_ENTRY(node_ares_task) node;
|
||||
};
|
||||
|
||||
struct node_async_ids {
|
||||
double async_id;
|
||||
double trigger_id;
|
||||
};
|
||||
|
||||
RB_HEAD(node_ares_task_list, node_ares_task);
|
||||
|
||||
class IsolateData {
|
||||
@@ -331,31 +336,96 @@ class Environment {
|
||||
public:
|
||||
class AsyncHooks {
|
||||
public:
|
||||
// Reason for both UidFields and Fields are that one is stored as a double*
|
||||
// and the other as a uint32_t*.
|
||||
enum Fields {
|
||||
kInit,
|
||||
kBefore,
|
||||
kAfter,
|
||||
kDestroy,
|
||||
kFieldsCount,
|
||||
};
|
||||
|
||||
enum UidFields {
|
||||
kCurrentAsyncId,
|
||||
kCurrentTriggerId,
|
||||
kAsyncUidCntr,
|
||||
kInitTriggerId,
|
||||
kUidFieldsCount,
|
||||
};
|
||||
|
||||
AsyncHooks() = delete;
|
||||
|
||||
inline uint32_t* fields();
|
||||
inline int fields_count() const;
|
||||
inline bool callbacks_enabled();
|
||||
inline void set_enable_callbacks(uint32_t flag);
|
||||
inline double* uid_fields();
|
||||
inline int uid_fields_count() const;
|
||||
inline v8::Local<v8::String> provider_string(int idx);
|
||||
|
||||
inline void push_ids(double async_id, double trigger_id);
|
||||
inline bool pop_ids(double async_id);
|
||||
inline void clear_id_stack(); // Used in fatal exceptions.
|
||||
|
||||
// Used to propagate the trigger_id to the constructor of any newly created
|
||||
// resources using RAII. Instead of needing to pass the trigger_id along
|
||||
// with other constructor arguments.
|
||||
class InitScope {
|
||||
public:
|
||||
InitScope() = delete;
|
||||
explicit InitScope(Environment* env, double init_trigger_id);
|
||||
~InitScope();
|
||||
|
||||
private:
|
||||
Environment* env_;
|
||||
double* uid_fields_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(InitScope);
|
||||
};
|
||||
|
||||
// Used to manage the stack of async and trigger ids as calls are made into
|
||||
// JS. Mainly used in MakeCallback().
|
||||
class ExecScope {
|
||||
public:
|
||||
ExecScope() = delete;
|
||||
explicit ExecScope(Environment* env, double async_id, double trigger_id);
|
||||
~ExecScope();
|
||||
void Dispose();
|
||||
|
||||
private:
|
||||
Environment* env_;
|
||||
double async_id_;
|
||||
// Manually track if the destructor has run so it isn't accidentally run
|
||||
// twice on RAII cleanup.
|
||||
bool disposed_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExecScope);
|
||||
};
|
||||
|
||||
private:
|
||||
friend class Environment; // So we can call the constructor.
|
||||
inline AsyncHooks();
|
||||
|
||||
enum Fields {
|
||||
// Set this to not zero if the init hook should be called.
|
||||
kEnableCallbacks,
|
||||
kFieldsCount
|
||||
};
|
||||
|
||||
inline explicit AsyncHooks(v8::Isolate* isolate);
|
||||
// Keep a list of all Persistent strings used for Provider types.
|
||||
v8::Eternal<v8::String> providers_[AsyncWrap::PROVIDERS_LENGTH];
|
||||
// Used by provider_string().
|
||||
v8::Isolate* isolate_;
|
||||
// Stores the ids of the current execution context stack.
|
||||
std::stack<struct node_async_ids> ids_stack_;
|
||||
// Used to communicate state between C++ and JS cheaply. Is placed in an
|
||||
// Uint32Array() and attached to the async_wrap object.
|
||||
uint32_t fields_[kFieldsCount];
|
||||
// Used to communicate ids between C++ and JS cheaply. Placed in a
|
||||
// Float64Array and attached to the async_wrap object. Using a double only
|
||||
// gives us 2^53-1 unique ids, but that should be sufficient.
|
||||
double uid_fields_[kUidFieldsCount];
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AsyncHooks);
|
||||
};
|
||||
|
||||
class AsyncCallbackScope {
|
||||
public:
|
||||
AsyncCallbackScope() = delete;
|
||||
explicit AsyncCallbackScope(Environment* env);
|
||||
~AsyncCallbackScope();
|
||||
|
||||
inline bool in_makecallback();
|
||||
|
||||
private:
|
||||
@@ -452,7 +522,6 @@ class Environment {
|
||||
|
||||
inline v8::Isolate* isolate() const;
|
||||
inline uv_loop_t* event_loop() const;
|
||||
inline bool async_wrap_callbacks_enabled() const;
|
||||
inline bool in_domain() const;
|
||||
inline uint32_t watched_providers() const;
|
||||
|
||||
@@ -489,7 +558,15 @@ class Environment {
|
||||
void PrintSyncTrace() const;
|
||||
inline void set_trace_sync_io(bool value);
|
||||
|
||||
inline double get_async_wrap_uid();
|
||||
inline bool abort_on_uncaught_exception() const;
|
||||
inline void set_abort_on_uncaught_exception(bool value);
|
||||
|
||||
// The necessary API for async_hooks.
|
||||
inline double new_async_id();
|
||||
inline double current_async_id();
|
||||
inline double trigger_id();
|
||||
inline double get_init_trigger_id();
|
||||
inline void set_init_trigger_id(const double id);
|
||||
|
||||
// List of id's that have been destroyed and need the destroy() cb called.
|
||||
inline std::vector<double>* destroy_ids_list();
|
||||
@@ -594,8 +671,8 @@ class Environment {
|
||||
bool using_domains_;
|
||||
bool printed_error_;
|
||||
bool trace_sync_io_;
|
||||
bool abort_on_uncaught_exception_;
|
||||
size_t makecallback_cntr_;
|
||||
double async_wrap_id_;
|
||||
std::vector<double> destroy_ids_list_;
|
||||
#if HAVE_INSPECTOR
|
||||
inspector::Agent inspector_agent_;
|
||||
|
||||
+2
-3
@@ -90,9 +90,8 @@ void HandleWrap::Close(const FunctionCallbackInfo<Value>& args) {
|
||||
HandleWrap::HandleWrap(Environment* env,
|
||||
Local<Object> object,
|
||||
uv_handle_t* handle,
|
||||
AsyncWrap::ProviderType provider,
|
||||
AsyncWrap* parent)
|
||||
: AsyncWrap(env, object, provider, parent),
|
||||
AsyncWrap::ProviderType provider)
|
||||
: AsyncWrap(env, object, provider),
|
||||
state_(kInitialized),
|
||||
handle_(handle) {
|
||||
handle_->data = this;
|
||||
|
||||
+1
-2
@@ -74,8 +74,7 @@ class HandleWrap : public AsyncWrap {
|
||||
HandleWrap(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
uv_handle_t* handle,
|
||||
AsyncWrap::ProviderType provider,
|
||||
AsyncWrap* parent = nullptr);
|
||||
AsyncWrap::ProviderType provider);
|
||||
~HandleWrap() override;
|
||||
|
||||
private:
|
||||
|
||||
+3
-14
@@ -12,7 +12,6 @@ namespace node {
|
||||
|
||||
using v8::Array;
|
||||
using v8::Context;
|
||||
using v8::External;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::HandleScope;
|
||||
@@ -21,8 +20,8 @@ using v8::Object;
|
||||
using v8::Value;
|
||||
|
||||
|
||||
JSStream::JSStream(Environment* env, Local<Object> obj, AsyncWrap* parent)
|
||||
: AsyncWrap(env, obj, AsyncWrap::PROVIDER_JSSTREAM, parent),
|
||||
JSStream::JSStream(Environment* env, Local<Object> obj)
|
||||
: AsyncWrap(env, obj, AsyncWrap::PROVIDER_JSSTREAM),
|
||||
StreamBase(env) {
|
||||
node::Wrap(obj, this);
|
||||
MakeWeak<JSStream>(this);
|
||||
@@ -115,17 +114,7 @@ void JSStream::New(const FunctionCallbackInfo<Value>& args) {
|
||||
// normal function.
|
||||
CHECK(args.IsConstructCall());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
JSStream* wrap;
|
||||
|
||||
if (args.Length() == 0) {
|
||||
wrap = new JSStream(env, args.This(), nullptr);
|
||||
} else if (args[0]->IsExternal()) {
|
||||
void* ptr = args[0].As<External>()->Value();
|
||||
wrap = new JSStream(env, args.This(), static_cast<AsyncWrap*>(ptr));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
CHECK(wrap);
|
||||
new JSStream(env, args.This());
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ class JSStream : public AsyncWrap, public StreamBase {
|
||||
size_t self_size() const override { return sizeof(*this); }
|
||||
|
||||
protected:
|
||||
JSStream(Environment* env, v8::Local<v8::Object> obj, AsyncWrap* parent);
|
||||
JSStream(Environment* env, v8::Local<v8::Object> obj);
|
||||
|
||||
AsyncWrap* GetAsyncWrap() override;
|
||||
|
||||
|
||||
+31
-41
@@ -154,6 +154,8 @@ using v8::Uint32Array;
|
||||
using v8::V8;
|
||||
using v8::Value;
|
||||
|
||||
using AsyncHooks = node::Environment::AsyncHooks;
|
||||
|
||||
static bool print_eval = false;
|
||||
static bool force_repl = false;
|
||||
static bool syntax_check_only = false;
|
||||
@@ -174,6 +176,7 @@ static node_module* modlist_linked;
|
||||
static node_module* modlist_addon;
|
||||
static bool trace_enabled = false;
|
||||
static std::string trace_enabled_categories; // NOLINT(runtime/string)
|
||||
static bool abort_on_uncaught_exception = false;
|
||||
|
||||
#if defined(NODE_HAVE_I18N_SUPPORT)
|
||||
// Path to ICU data (for i18n / Intl)
|
||||
@@ -1302,21 +1305,13 @@ Local<Value> MakeCallback(Environment* env,
|
||||
// If you hit this assertion, you forgot to enter the v8::Context first.
|
||||
CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());
|
||||
|
||||
Local<Function> pre_fn = env->async_hooks_pre_function();
|
||||
Local<Function> post_fn = env->async_hooks_post_function();
|
||||
Local<Object> object, domain;
|
||||
bool ran_init_callback = false;
|
||||
bool has_domain = false;
|
||||
|
||||
Environment::AsyncCallbackScope callback_scope(env);
|
||||
|
||||
// TODO(trevnorris): Adding "_asyncQueue" to the "this" in the init callback
|
||||
// is a horrible way to detect usage. Rethink how detection should happen.
|
||||
if (recv->IsObject()) {
|
||||
object = recv.As<Object>();
|
||||
Local<Value> async_queue_v = object->Get(env->async_queue_string());
|
||||
if (async_queue_v->IsObject())
|
||||
ran_init_callback = true;
|
||||
}
|
||||
|
||||
if (env->using_domains()) {
|
||||
@@ -1340,34 +1335,13 @@ Local<Value> MakeCallback(Environment* env,
|
||||
}
|
||||
}
|
||||
|
||||
if (ran_init_callback && !pre_fn.IsEmpty()) {
|
||||
TryCatch try_catch(env->isolate());
|
||||
MaybeLocal<Value> ar = pre_fn->Call(env->context(), object, 0, nullptr);
|
||||
if (ar.IsEmpty()) {
|
||||
ClearFatalExceptionHandlers(env);
|
||||
FatalException(env->isolate(), try_catch);
|
||||
return Local<Value>();
|
||||
}
|
||||
}
|
||||
// TODO(trevnorris): Correct this once node::MakeCallback() support id and
|
||||
// triggerId. Consider completely removing it until then so the async id can
|
||||
// propagate through to the fatalException after hook calls.
|
||||
AsyncHooks::ExecScope exec_scope(env, 0, 0);
|
||||
|
||||
Local<Value> ret = callback->Call(recv, argc, argv);
|
||||
|
||||
if (ran_init_callback && !post_fn.IsEmpty()) {
|
||||
Local<Value> did_throw = Boolean::New(env->isolate(), ret.IsEmpty());
|
||||
// Currently there's no way to retrieve an uid from node::MakeCallback().
|
||||
// This needs to be fixed.
|
||||
Local<Value> vals[] =
|
||||
{ Undefined(env->isolate()).As<Value>(), did_throw };
|
||||
TryCatch try_catch(env->isolate());
|
||||
MaybeLocal<Value> ar =
|
||||
post_fn->Call(env->context(), object, arraysize(vals), vals);
|
||||
if (ar.IsEmpty()) {
|
||||
ClearFatalExceptionHandlers(env);
|
||||
FatalException(env->isolate(), try_catch);
|
||||
return Local<Value>();
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.IsEmpty()) {
|
||||
// NOTE: For backwards compatibility with public API we return Undefined()
|
||||
// if the top level call threw.
|
||||
@@ -1375,6 +1349,8 @@ Local<Value> MakeCallback(Environment* env,
|
||||
ret : Undefined(env->isolate()).As<Value>();
|
||||
}
|
||||
|
||||
exec_scope.Dispose();
|
||||
|
||||
if (has_domain) {
|
||||
Local<Value> exit_v = domain->Get(env->exit_string());
|
||||
if (exit_v->IsFunction()) {
|
||||
@@ -1395,6 +1371,11 @@ Local<Value> MakeCallback(Environment* env,
|
||||
env->isolate()->RunMicrotasks();
|
||||
}
|
||||
|
||||
// Make sure the stack unwound properly. If there are nested MakeCallback's
|
||||
// then it should return early and not reach this code.
|
||||
CHECK_EQ(env->current_async_id(), 0);
|
||||
CHECK_EQ(env->trigger_id(), 0);
|
||||
|
||||
Local<Object> process = env->process_object();
|
||||
|
||||
if (tick_info->length() == 0) {
|
||||
@@ -1411,10 +1392,10 @@ Local<Value> MakeCallback(Environment* env,
|
||||
|
||||
|
||||
Local<Value> MakeCallback(Environment* env,
|
||||
Local<Object> recv,
|
||||
Local<String> symbol,
|
||||
int argc,
|
||||
Local<Value> argv[]) {
|
||||
Local<Object> recv,
|
||||
Local<String> symbol,
|
||||
int argc,
|
||||
Local<Value> argv[]) {
|
||||
Local<Value> cb_v = recv->Get(symbol);
|
||||
CHECK(cb_v->IsFunction());
|
||||
return MakeCallback(env, recv.As<Value>(), cb_v.As<Function>(), argc, argv);
|
||||
@@ -1422,10 +1403,10 @@ Local<Value> MakeCallback(Environment* env,
|
||||
|
||||
|
||||
Local<Value> MakeCallback(Environment* env,
|
||||
Local<Object> recv,
|
||||
const char* method,
|
||||
int argc,
|
||||
Local<Value> argv[]) {
|
||||
Local<Object> recv,
|
||||
const char* method,
|
||||
int argc,
|
||||
Local<Value> argv[]) {
|
||||
Local<String> method_string = OneByteString(env->isolate(), method);
|
||||
return MakeCallback(env, recv, method_string, argc, argv);
|
||||
}
|
||||
@@ -3925,6 +3906,12 @@ static void ParseArgs(int* argc,
|
||||
} else if (strcmp(arg, "--") == 0) {
|
||||
index += 1;
|
||||
break;
|
||||
} else if (strcmp(arg, "--abort-on-uncaught-exception") ||
|
||||
strcmp(arg, "--abort_on_uncaught_exception")) {
|
||||
abort_on_uncaught_exception = true;
|
||||
// Also a V8 option. Pass through as-is.
|
||||
new_v8_argv[new_v8_argc] = arg;
|
||||
new_v8_argc += 1;
|
||||
} else {
|
||||
// V8 option. Pass through as-is.
|
||||
new_v8_argv[new_v8_argc] = arg;
|
||||
@@ -4471,8 +4458,11 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
|
||||
if (debug_options.inspector_enabled() && !debugger_running)
|
||||
return 12; // Signal internal error.
|
||||
|
||||
env.set_abort_on_uncaught_exception(abort_on_uncaught_exception);
|
||||
|
||||
{
|
||||
Environment::AsyncCallbackScope callback_scope(&env);
|
||||
Environment::AsyncHooks::ExecScope exec_scope(&env, 1, 0);
|
||||
LoadEnvironment(&env);
|
||||
}
|
||||
|
||||
|
||||
@@ -476,6 +476,8 @@ class Parser : public AsyncWrap {
|
||||
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
|
||||
// Should always be called from the same context.
|
||||
CHECK_EQ(env, parser->env());
|
||||
// The parser is being reused. Reset the uid and call init() callbacks.
|
||||
parser->Reset();
|
||||
parser->Init(type);
|
||||
}
|
||||
|
||||
|
||||
+7
-13
@@ -38,7 +38,6 @@ namespace node {
|
||||
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::External;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
@@ -47,15 +46,17 @@ using v8::Local;
|
||||
using v8::Object;
|
||||
using v8::Value;
|
||||
|
||||
using AsyncHooks = Environment::AsyncHooks;
|
||||
|
||||
|
||||
Local<Object> PipeWrap::Instantiate(Environment* env, AsyncWrap* parent) {
|
||||
EscapableHandleScope handle_scope(env->isolate());
|
||||
AsyncHooks::InitScope init_scope(env, parent->get_id());
|
||||
CHECK_EQ(false, env->pipe_constructor_template().IsEmpty());
|
||||
Local<Function> constructor = env->pipe_constructor_template()->GetFunction();
|
||||
CHECK_EQ(false, constructor.IsEmpty());
|
||||
Local<Value> ptr = External::New(env->isolate(), parent);
|
||||
Local<Object> instance =
|
||||
constructor->NewInstance(env->context(), 1, &ptr).ToLocalChecked();
|
||||
constructor->NewInstance(env->context()).ToLocalChecked();
|
||||
return handle_scope.Escape(instance);
|
||||
}
|
||||
|
||||
@@ -114,23 +115,16 @@ void PipeWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
// normal function.
|
||||
CHECK(args.IsConstructCall());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
if (args[0]->IsExternal()) {
|
||||
void* ptr = args[0].As<External>()->Value();
|
||||
new PipeWrap(env, args.This(), false, static_cast<AsyncWrap*>(ptr));
|
||||
} else {
|
||||
new PipeWrap(env, args.This(), args[0]->IsTrue(), nullptr);
|
||||
}
|
||||
new PipeWrap(env, args.This(), args[0]->IsTrue());
|
||||
}
|
||||
|
||||
|
||||
PipeWrap::PipeWrap(Environment* env,
|
||||
Local<Object> object,
|
||||
bool ipc,
|
||||
AsyncWrap* parent)
|
||||
bool ipc)
|
||||
: ConnectionWrap(env,
|
||||
object,
|
||||
AsyncWrap::PROVIDER_PIPEWRAP,
|
||||
parent) {
|
||||
AsyncWrap::PROVIDER_PIPEWRAP) {
|
||||
int r = uv_pipe_init(env->event_loop(), &handle_, ipc);
|
||||
CHECK_EQ(r, 0); // How do we proxy this error up to javascript?
|
||||
// Suggestion: uv_pipe_init() returns void.
|
||||
|
||||
+1
-2
@@ -42,8 +42,7 @@ class PipeWrap : public ConnectionWrap<PipeWrap, uv_pipe_t> {
|
||||
private:
|
||||
PipeWrap(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
bool ipc,
|
||||
AsyncWrap* parent);
|
||||
bool ipc);
|
||||
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void Bind(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
@@ -23,6 +23,8 @@ using v8::PropertyCallbackInfo;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
using AsyncHooks = Environment::AsyncHooks;
|
||||
|
||||
template <class Base>
|
||||
void StreamBase::AddMethods(Environment* env,
|
||||
Local<FunctionTemplate> t,
|
||||
@@ -134,6 +136,7 @@ void StreamBase::JSMethod(const FunctionCallbackInfo<Value>& args) {
|
||||
if (!wrap->IsAlive())
|
||||
return args.GetReturnValue().Set(UV_EINVAL);
|
||||
|
||||
AsyncHooks::InitScope init_scope(handle->env(), handle->get_id());
|
||||
args.GetReturnValue().Set((wrap->*Method)(args));
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ int StreamBase::Shutdown(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args[0]->IsObject());
|
||||
Local<Object> req_wrap_obj = args[0].As<Object>();
|
||||
|
||||
AsyncWrap* wrap = GetAsyncWrap();
|
||||
if (wrap != nullptr)
|
||||
env->set_init_trigger_id(wrap->get_id());
|
||||
ShutdownWrap* req_wrap = new ShutdownWrap(env,
|
||||
req_wrap_obj,
|
||||
this,
|
||||
@@ -129,6 +132,11 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {
|
||||
if (storage_size > INT_MAX)
|
||||
return UV_ENOBUFS;
|
||||
|
||||
AsyncWrap* wrap = GetAsyncWrap();
|
||||
// NOTE: All tests show that GetAsyncWrap() never returns nullptr here. If it
|
||||
// can then replace the CHECK_NE() with if (wrap != nullptr).
|
||||
CHECK_NE(wrap, nullptr);
|
||||
env->set_init_trigger_id(wrap->get_id());
|
||||
WriteWrap* req_wrap = WriteWrap::New(env,
|
||||
req_wrap_obj,
|
||||
this,
|
||||
@@ -201,6 +209,7 @@ int StreamBase::WriteBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
const char* data = Buffer::Data(args[1]);
|
||||
size_t length = Buffer::Length(args[1]);
|
||||
|
||||
AsyncWrap* wrap;
|
||||
WriteWrap* req_wrap;
|
||||
uv_buf_t buf;
|
||||
buf.base = const_cast<char*>(data);
|
||||
@@ -216,6 +225,9 @@ int StreamBase::WriteBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
goto done;
|
||||
CHECK_EQ(count, 1);
|
||||
|
||||
wrap = GetAsyncWrap();
|
||||
if (wrap != nullptr)
|
||||
env->set_init_trigger_id(wrap->get_id());
|
||||
// Allocate, or write rest
|
||||
req_wrap = WriteWrap::New(env, req_wrap_obj, this, AfterWrite);
|
||||
|
||||
@@ -247,6 +259,7 @@ int StreamBase::WriteString(const FunctionCallbackInfo<Value>& args) {
|
||||
Local<Object> req_wrap_obj = args[0].As<Object>();
|
||||
Local<String> string = args[1].As<String>();
|
||||
Local<Object> send_handle_obj;
|
||||
AsyncWrap* wrap;
|
||||
if (args[2]->IsObject())
|
||||
send_handle_obj = args[2].As<Object>();
|
||||
|
||||
@@ -297,6 +310,9 @@ int StreamBase::WriteString(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(count, 1);
|
||||
}
|
||||
|
||||
wrap = GetAsyncWrap();
|
||||
if (wrap != nullptr)
|
||||
env->set_init_trigger_id(wrap->get_id());
|
||||
req_wrap = WriteWrap::New(env, req_wrap_obj, this, AfterWrite, storage_size);
|
||||
|
||||
data = req_wrap->Extra();
|
||||
|
||||
+2
-4
@@ -86,13 +86,11 @@ void StreamWrap::Initialize(Local<Object> target,
|
||||
StreamWrap::StreamWrap(Environment* env,
|
||||
Local<Object> object,
|
||||
uv_stream_t* stream,
|
||||
AsyncWrap::ProviderType provider,
|
||||
AsyncWrap* parent)
|
||||
AsyncWrap::ProviderType provider)
|
||||
: HandleWrap(env,
|
||||
object,
|
||||
reinterpret_cast<uv_handle_t*>(stream),
|
||||
provider,
|
||||
parent),
|
||||
provider),
|
||||
StreamBase(env),
|
||||
stream_(stream) {
|
||||
set_after_write_cb({ OnAfterWriteImpl, this });
|
||||
|
||||
+1
-2
@@ -81,8 +81,7 @@ class StreamWrap : public HandleWrap, public StreamBase {
|
||||
StreamWrap(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
uv_stream_t* stream,
|
||||
AsyncWrap::ProviderType provider,
|
||||
AsyncWrap* parent = nullptr);
|
||||
AsyncWrap::ProviderType provider);
|
||||
|
||||
~StreamWrap() {
|
||||
}
|
||||
|
||||
+9
-16
@@ -40,7 +40,6 @@ namespace node {
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::External;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
@@ -51,15 +50,17 @@ using v8::Object;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
using AsyncHooks = Environment::AsyncHooks;
|
||||
|
||||
|
||||
Local<Object> TCPWrap::Instantiate(Environment* env, AsyncWrap* parent) {
|
||||
EscapableHandleScope handle_scope(env->isolate());
|
||||
AsyncHooks::InitScope init_scope(env, parent->get_id());
|
||||
CHECK_EQ(env->tcp_constructor_template().IsEmpty(), false);
|
||||
Local<Function> constructor = env->tcp_constructor_template()->GetFunction();
|
||||
CHECK_EQ(constructor.IsEmpty(), false);
|
||||
Local<Value> ptr = External::New(env->isolate(), parent);
|
||||
Local<Object> instance =
|
||||
constructor->NewInstance(env->context(), 1, &ptr).ToLocalChecked();
|
||||
constructor->NewInstance(env->context()).ToLocalChecked();
|
||||
return handle_scope.Escape(instance);
|
||||
}
|
||||
|
||||
@@ -134,24 +135,14 @@ void TCPWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
// normal function.
|
||||
CHECK(args.IsConstructCall());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
TCPWrap* wrap;
|
||||
if (args.Length() == 0) {
|
||||
wrap = new TCPWrap(env, args.This(), nullptr);
|
||||
} else if (args[0]->IsExternal()) {
|
||||
void* ptr = args[0].As<External>()->Value();
|
||||
wrap = new TCPWrap(env, args.This(), static_cast<AsyncWrap*>(ptr));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
CHECK(wrap);
|
||||
new TCPWrap(env, args.This());
|
||||
}
|
||||
|
||||
|
||||
TCPWrap::TCPWrap(Environment* env, Local<Object> object, AsyncWrap* parent)
|
||||
TCPWrap::TCPWrap(Environment* env, Local<Object> object)
|
||||
: ConnectionWrap(env,
|
||||
object,
|
||||
AsyncWrap::PROVIDER_TCPWRAP,
|
||||
parent) {
|
||||
AsyncWrap::PROVIDER_TCPWRAP) {
|
||||
int r = uv_tcp_init(env->event_loop(), &handle_);
|
||||
CHECK_EQ(r, 0); // How do we proxy this error up to javascript?
|
||||
// Suggestion: uv_tcp_init() returns void.
|
||||
@@ -279,6 +270,7 @@ void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) {
|
||||
int err = uv_ip4_addr(*ip_address, port, &addr);
|
||||
|
||||
if (err == 0) {
|
||||
env->set_init_trigger_id(wrap->get_id());
|
||||
ConnectWrap* req_wrap =
|
||||
new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_TCPCONNECTWRAP);
|
||||
err = uv_tcp_connect(req_wrap->req(),
|
||||
@@ -314,6 +306,7 @@ void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) {
|
||||
int err = uv_ip6_addr(*ip_address, port, &addr);
|
||||
|
||||
if (err == 0) {
|
||||
env->set_init_trigger_id(wrap->get_id());
|
||||
ConnectWrap* req_wrap =
|
||||
new ConnectWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_TCPCONNECTWRAP);
|
||||
err = uv_tcp_connect(req_wrap->req(),
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ class TCPWrap : public ConnectionWrap<TCPWrap, uv_tcp_t> {
|
||||
int (*F)(const typename T::HandleType*, sockaddr*, int*)>
|
||||
friend void GetSockOrPeerName(const v8::FunctionCallbackInfo<v8::Value>&);
|
||||
|
||||
TCPWrap(Environment* env, v8::Local<v8::Object> object, AsyncWrap* parent);
|
||||
TCPWrap(Environment* env, v8::Local<v8::Object> object);
|
||||
~TCPWrap();
|
||||
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
+9
-14
@@ -37,7 +37,6 @@ namespace node {
|
||||
using v8::Array;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::External;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
using v8::HandleScope;
|
||||
@@ -50,6 +49,8 @@ using v8::String;
|
||||
using v8::Undefined;
|
||||
using v8::Value;
|
||||
|
||||
using AsyncHooks = Environment::AsyncHooks;
|
||||
|
||||
|
||||
class SendWrap : public ReqWrap<uv_udp_send_t> {
|
||||
public:
|
||||
@@ -88,7 +89,7 @@ static void NewSendWrap(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
UDPWrap::UDPWrap(Environment* env, Local<Object> object, AsyncWrap* parent)
|
||||
UDPWrap::UDPWrap(Environment* env, Local<Object> object)
|
||||
: HandleWrap(env,
|
||||
object,
|
||||
reinterpret_cast<uv_handle_t*>(&handle_),
|
||||
@@ -155,15 +156,7 @@ void UDPWrap::Initialize(Local<Object> target,
|
||||
void UDPWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args.IsConstructCall());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
if (args.Length() == 0) {
|
||||
new UDPWrap(env, args.This(), nullptr);
|
||||
} else if (args[0]->IsExternal()) {
|
||||
new UDPWrap(env,
|
||||
args.This(),
|
||||
static_cast<AsyncWrap*>(args[0].As<External>()->Value()));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
new UDPWrap(env, args.This());
|
||||
}
|
||||
|
||||
|
||||
@@ -303,6 +296,7 @@ void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) {
|
||||
node::Utf8Value address(env->isolate(), args[4]);
|
||||
const bool have_callback = args[5]->IsTrue();
|
||||
|
||||
env->set_init_trigger_id(wrap->get_id());
|
||||
SendWrap* req_wrap = new SendWrap(env, req_wrap_obj, have_callback);
|
||||
size_t msg_size = 0;
|
||||
|
||||
@@ -450,11 +444,12 @@ void UDPWrap::OnRecv(uv_udp_t* handle,
|
||||
|
||||
Local<Object> UDPWrap::Instantiate(Environment* env, AsyncWrap* parent) {
|
||||
EscapableHandleScope scope(env->isolate());
|
||||
AsyncHooks::InitScope init_scope(env, parent->get_id());
|
||||
// If this assert fires then Initialize hasn't been called yet.
|
||||
CHECK_EQ(env->udp_constructor_function().IsEmpty(), false);
|
||||
Local<Value> ptr = External::New(env->isolate(), parent);
|
||||
return scope.Escape(env->udp_constructor_function()
|
||||
->NewInstance(env->context(), 1, &ptr).ToLocalChecked());
|
||||
Local<Object> instance = env->udp_constructor_function()
|
||||
->NewInstance(env->context()).ToLocalChecked();
|
||||
return scope.Escape(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ class UDPWrap: public HandleWrap {
|
||||
int (*F)(const typename T::HandleType*, sockaddr*, int*)>
|
||||
friend void GetSockOrPeerName(const v8::FunctionCallbackInfo<v8::Value>&);
|
||||
|
||||
UDPWrap(Environment* env, v8::Local<v8::Object> object, AsyncWrap* parent);
|
||||
UDPWrap(Environment* env, v8::Local<v8::Object> object);
|
||||
|
||||
static void DoBind(const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||
int family);
|
||||
|
||||
Reference in new issue
Block a user