Skip to content

Commit

Permalink
src: lazily load internalBinding('uv') and build the errmap lazily
Browse files Browse the repository at this point in the history
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: #25143
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
  • Loading branch information
joyeecheung authored and targos committed Jan 1, 2019
1 parent c7d2dbd commit dd6667d
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 33 deletions.
29 changes: 21 additions & 8 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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++.
Expand All @@ -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;
Expand Down Expand Up @@ -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 = '';

Expand Down Expand Up @@ -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);
Expand Down
15 changes: 12 additions & 3 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ const {
isNativeError
} = internalBinding('types');

const { errmap } = internalBinding('uv');

const noCrypto = !process.versions.openssl;

const experimentalWarnings = new Set();
Expand Down Expand Up @@ -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}`;
}

Expand Down
79 changes: 57 additions & 22 deletions src/uv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (env->options()->pending_deprecation && env->EmitErrNameWarning()) {
Expand All @@ -57,6 +76,29 @@ void ErrName(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(OneByteString(env->isolate(), name));
}

void GetErrMap(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Local<Context> context = env->context();

Local<Map> 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<Value> 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<Object> target,
Local<Value> unused,
Expand All @@ -70,28 +112,21 @@ void Initialize(Local<Object> target,
->GetFunction(env->context())
.ToLocalChecked()).FromJust();

#define V(name, _) NODE_DEFINE_CONSTANT(target, UV_##name);
UV_ERRNO_MAP(V)
#undef V

Local<Map> err_map = Map::New(isolate);

#define V(name, msg) do { \
Local<Value> 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<PropertyAttribute>(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<String> name = OneByteString(isolate, prefixed_name.c_str());
Local<Integer> 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
Expand Down

0 comments on commit dd6667d

Please sign in to comment.