From c5d4f00c8925300b356c120fbc25f573e01289f2 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 20 Dec 2018 05:33:10 +0800 Subject: [PATCH 1/2] src: lazily load internalBinding('uv') and build the errmap lazily This removes the `internalBinding('uv')` call from the normal bootstrap for now, and avoids building `errmap` by default which expands to a lot of calls into V8. --- lib/internal/errors.js | 29 +++++++++++++++++++++-------- lib/internal/util.js | 15 ++++++++++++--- src/uv.cc | 39 +++++++++++++++++++++++---------------- 3 files changed, 56 insertions(+), 27 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 8be692ef5777db..dae7f8600d1786 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -15,11 +15,6 @@ const kInfo = Symbol('info'); const messages = new Map(); const codes = {}; -const { - errmap, - UV_EAI_NODATA, - UV_EAI_NONAME -} = internalBinding('uv'); const { kMaxLength } = internalBinding('buffer'); const { defineProperty } = Object; @@ -237,6 +232,24 @@ function getMessage(key, args) { return util.format.apply(null, args); } +let uvBinding; + +function lazyUv() { + if (!uvBinding) { + uvBinding = internalBinding('uv'); + } + return uvBinding; +} + +function lazyErrmapGet(name) { + uvBinding = lazyUv(); + if (!uvBinding.errmap) { + uvBinding.errmap = uvBinding.getErrorMap(); + } + return uvBinding.errmap.get(name); +} + + /** * This creates an error compatible with errors produced in the C++ * function UVException using a context object with data assembled in C++. @@ -247,7 +260,7 @@ function getMessage(key, args) { * @returns {Error} */ function uvException(ctx) { - const [ code, uvmsg ] = errmap.get(ctx.errno); + const [ code, uvmsg ] = lazyErrmapGet(ctx.errno); let message = `${code}: ${ctx.message || uvmsg}, ${ctx.syscall}`; let path; @@ -303,7 +316,7 @@ function uvException(ctx) { * @returns {Error} */ function uvExceptionWithHostPort(err, syscall, address, port) { - const [ code, uvmsg ] = errmap.get(err); + const [ code, uvmsg ] = lazyErrmapGet(err); const message = `${syscall} ${code}: ${uvmsg}`; let details = ''; @@ -421,7 +434,7 @@ function dnsException(code, syscall, hostname) { if (typeof code === 'number') { // FIXME(bnoordhuis) Remove this backwards compatibility nonsense and pass // the true error to the user. ENOTFOUND is not even a proper POSIX error! - if (code === UV_EAI_NODATA || code === UV_EAI_NONAME) { + if (code === lazyUv().UV_EAI_NODATA || code === lazyUv().UV_EAI_NONAME) { code = 'ENOTFOUND'; // Fabricated error name. } else { code = lazyInternalUtil().getSystemErrorName(code); diff --git a/lib/internal/util.js b/lib/internal/util.js index 7af06351c91406..ee6e0f749c364a 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -16,8 +16,6 @@ const { isNativeError } = internalBinding('types'); -const { errmap } = internalBinding('uv'); - const noCrypto = !process.versions.openssl; const experimentalWarnings = new Set(); @@ -250,8 +248,19 @@ function getConstructorOf(obj) { return null; } +let uvBinding; +function lazyErrmapGet(name) { + if (!uvBinding) { + uvBinding = internalBinding('uv'); + } + if (!uvBinding.errmap) { + uvBinding.errmap = uvBinding.getErrorMap(); + } + return uvBinding.errmap.get(name); +} + function getSystemErrorName(err) { - const entry = errmap.get(err); + const entry = lazyErrmapGet(err); return entry ? entry[0] : `Unknown system error ${err}`; } diff --git a/src/uv.cc b/src/uv.cc index 27daed556ecc7f..048c000cd376e3 100644 --- a/src/uv.cc +++ b/src/uv.cc @@ -58,21 +58,10 @@ void ErrName(const FunctionCallbackInfo& args) { } -void Initialize(Local target, - Local unused, - Local context, - void* priv) { - Environment* env = Environment::GetCurrent(context); +void GetErrMap(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); - target->Set(env->context(), - FIXED_ONE_BYTE_STRING(isolate, "errname"), - env->NewFunctionTemplate(ErrName) - ->GetFunction(env->context()) - .ToLocalChecked()).FromJust(); - -#define V(name, _) NODE_DEFINE_CONSTANT(target, UV_##name); - UV_ERRNO_MAP(V) -#undef V + Local context = env->context(); Local err_map = Map::New(isolate); @@ -90,8 +79,26 @@ void Initialize(Local target, UV_ERRNO_MAP(V) #undef V - target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "errmap"), - err_map).FromJust(); + args.GetReturnValue().Set(err_map); +} + +void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + target->Set(env->context(), + FIXED_ONE_BYTE_STRING(isolate, "errname"), + env->NewFunctionTemplate(ErrName) + ->GetFunction(env->context()) + .ToLocalChecked()).FromJust(); + +#define V(name, _) NODE_DEFINE_CONSTANT(target, UV_##name); + UV_ERRNO_MAP(V) +#undef V + + env->SetMethod(target, "getErrorMap", GetErrMap); } } // anonymous namespace From 0226fe1bd36e85c14ddabc758c16559c1f1f9fbf Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 28 Dec 2018 23:37:17 +0800 Subject: [PATCH 2/2] fixup! src: lazily load internalBinding('uv') and build the errmap lazily --- src/uv.cc | 64 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/uv.cc b/src/uv.cc index 048c000cd376e3..a7d0b1012ce2f4 100644 --- a/src/uv.cc +++ b/src/uv.cc @@ -25,20 +25,39 @@ #include "env-inl.h" namespace node { + +namespace per_process { +struct UVError { + int value; + const char* name; + const char* message; +}; + +// We only expand the macro once here to reduce the amount of code +// generated. +static const struct UVError uv_errors_map[] = { +#define V(name, message) {UV_##name, #name, message}, + UV_ERRNO_MAP(V) +#undef V +}; +} // namespace per_process + namespace { using v8::Array; using v8::Context; +using v8::DontDelete; using v8::FunctionCallbackInfo; using v8::Integer; using v8::Isolate; using v8::Local; using v8::Map; using v8::Object; +using v8::PropertyAttribute; +using v8::ReadOnly; using v8::String; using v8::Value; - void ErrName(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (env->options()->pending_deprecation && env->EmitErrNameWarning()) { @@ -57,7 +76,6 @@ void ErrName(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(OneByteString(env->isolate(), name)); } - void GetErrMap(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); @@ -65,19 +83,19 @@ void GetErrMap(const FunctionCallbackInfo& args) { Local err_map = Map::New(isolate); -#define V(name, msg) do { \ - Local arr[] = { \ - OneByteString(isolate, #name), \ - OneByteString(isolate, msg) \ - }; \ - if (err_map->Set(context, \ - Integer::New(isolate, UV_##name), \ - Array::New(isolate, arr, arraysize(arr))).IsEmpty()) { \ - return; \ - } \ -} while (0); - UV_ERRNO_MAP(V) -#undef V + size_t errors_len = arraysize(per_process::uv_errors_map); + for (size_t i = 0; i < errors_len; ++i) { + const auto& error = per_process::uv_errors_map[i]; + Local arr[] = {OneByteString(isolate, error.name), + OneByteString(isolate, error.message)}; + if (err_map + ->Set(context, + Integer::New(isolate, error.value), + Array::New(isolate, arr, arraysize(arr))) + .IsEmpty()) { + return; + } + } args.GetReturnValue().Set(err_map); } @@ -94,9 +112,19 @@ void Initialize(Local target, ->GetFunction(env->context()) .ToLocalChecked()).FromJust(); -#define V(name, _) NODE_DEFINE_CONSTANT(target, UV_##name); - UV_ERRNO_MAP(V) -#undef V + // TODO(joyeecheung): This should be deprecated in user land in favor of + // `util.getSystemErrorName(err)`. + PropertyAttribute attributes = + static_cast(ReadOnly | DontDelete); + size_t errors_len = arraysize(per_process::uv_errors_map); + const std::string prefix = "UV_"; + for (size_t i = 0; i < errors_len; ++i) { + const auto& error = per_process::uv_errors_map[i]; + const std::string prefixed_name = prefix + error.name; + Local name = OneByteString(isolate, prefixed_name.c_str()); + Local value = Integer::New(isolate, error.value); + target->DefineOwnProperty(context, name, value, attributes).FromJust(); + } env->SetMethod(target, "getErrorMap", GetErrMap); }