From dd6667d05eee97c1d9e61196f1a4cdad0646c81b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 20 Dec 2018 05:33:10 +0800 Subject: [PATCH] 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. PR-URL: https://github.com/nodejs/node/pull/25143 Reviewed-By: Anna Henningsen Reviewed-By: Daniel Bevenius Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- lib/internal/errors.js | 29 +++++++++++----- lib/internal/util.js | 15 ++++++-- src/uv.cc | 79 ++++++++++++++++++++++++++++++------------ 3 files changed, 90 insertions(+), 33 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index f1965e825ec3d2..adbbb7d0046006 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 c393e44d5d6258..4ead8705777ee8 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..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,6 +76,29 @@ 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(); + Local context = env->context(); + + Local err_map = Map::New(isolate); + + 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); +} void Initialize(Local target, Local unused, @@ -70,28 +112,21 @@ void Initialize(Local target, ->GetFunction(env->context()) .ToLocalChecked()).FromJust(); -#define V(name, _) NODE_DEFINE_CONSTANT(target, UV_##name); - UV_ERRNO_MAP(V) -#undef V - - 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 + // 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(); + } - target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "errmap"), - err_map).FromJust(); + env->SetMethod(target, "getErrorMap", GetErrMap); } } // anonymous namespace