diff --git a/lib/internal/loader/ModuleWrap.js b/lib/internal/loader/ModuleWrap.js index 4d35356ec2..8766041d94 100644 --- a/lib/internal/loader/ModuleWrap.js +++ b/lib/internal/loader/ModuleWrap.js @@ -1,6 +1,7 @@ 'use strict'; -const { ModuleWrap } = process.binding('module_wrap'); +const { ModuleWrap } = + require('internal/process').internalBinding('module_wrap'); const debug = require('util').debuglog('esm'); const ArrayJoin = Function.call.bind(Array.prototype.join); const ArrayMap = Function.call.bind(Array.prototype.map); diff --git a/lib/internal/loader/search.js b/lib/internal/loader/search.js index 8b1a3e0f83..3d78443c9f 100644 --- a/lib/internal/loader/search.js +++ b/lib/internal/loader/search.js @@ -3,7 +3,7 @@ const { URL } = require('url'); const CJSmodule = require('module'); const errors = require('internal/errors'); -const { resolve } = process.binding('module_wrap'); +const { resolve } = require('internal/process').internalBinding('module_wrap'); module.exports = (target, base) => { if (base === undefined) { diff --git a/lib/internal/process.js b/lib/internal/process.js index 21a74abba7..be1c44c4a0 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -4,6 +4,9 @@ const errors = require('internal/errors'); const util = require('util'); const constants = process.binding('constants').os.signals; +const internalBinding = process._internalBinding; +delete process._internalBinding; + const assert = process.assert = function(x, msg) { if (!x) throw new errors.Error('ERR_ASSERTION', msg || 'assertion error'); }; @@ -256,5 +259,6 @@ module.exports = { setupKillAndExit, setupSignalHandlers, setupChannel, - setupRawDebug + setupRawDebug, + internalBinding }; diff --git a/src/env-inl.h b/src/env-inl.h index 6be914ffb3..d3d5247c5b 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -275,7 +275,17 @@ inline Environment::Environment(IsolateData* isolate_data, v8::HandleScope handle_scope(isolate()); v8::Context::Scope context_scope(context); set_as_external(v8::External::New(isolate(), this)); - set_binding_cache_object(v8::Object::New(isolate())); + + v8::Local null = v8::Null(isolate()); + v8::Local binding_cache_object = v8::Object::New(isolate()); + CHECK(binding_cache_object->SetPrototype(context, null).FromJust()); + set_binding_cache_object(binding_cache_object); + + v8::Local internal_binding_cache_object = + v8::Object::New(isolate()); + CHECK(internal_binding_cache_object->SetPrototype(context, null).FromJust()); + set_internal_binding_cache_object(internal_binding_cache_object); + set_module_load_list_array(v8::Array::New(isolate())); AssignToContext(context); diff --git a/src/env.h b/src/env.h index 912301a75f..3288f56d80 100644 --- a/src/env.h +++ b/src/env.h @@ -307,6 +307,7 @@ class ModuleWrap; V(async_hooks_after_function, v8::Function) \ V(async_hooks_promise_resolve_function, v8::Function) \ V(binding_cache_object, v8::Object) \ + V(internal_binding_cache_object, v8::Object) \ V(buffer_prototype_object, v8::Object) \ V(context, v8::Context) \ V(domain_array, v8::Array) \ diff --git a/src/module_wrap.cc b/src/module_wrap.cc index cbee6faff3..829248b681 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -7,6 +7,7 @@ #include "node_url.h" #include "util.h" #include "util-inl.h" +#include "node_internals.h" namespace node { namespace loader { @@ -523,5 +524,5 @@ void ModuleWrap::Initialize(Local target, } // namespace loader } // namespace node -NODE_MODULE_CONTEXT_AWARE_BUILTIN(module_wrap, - node::loader::ModuleWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE_INTERNAL(module_wrap, + node::loader::ModuleWrap::Initialize) diff --git a/src/node.cc b/src/node.cc index 7a9ea4d058..8ec46062d0 100644 --- a/src/node.cc +++ b/src/node.cc @@ -186,6 +186,7 @@ static bool v8_is_profiling = false; static bool node_is_initialized = false; static node_module* modpending; static node_module* modlist_builtin; +static node_module* modlist_internal; static node_module* modlist_linked; static node_module* modlist_addon; static bool trace_enabled = false; @@ -2610,6 +2611,9 @@ extern "C" void node_module_register(void* m) { if (mp->nm_flags & NM_F_BUILTIN) { mp->nm_link = modlist_builtin; modlist_builtin = mp; + } else if (mp->nm_flags & NM_F_INTERNAL) { + mp->nm_link = modlist_internal; + modlist_internal = mp; } else if (!node_is_initialized) { // "Linked" modules are included as part of the node project. // Like builtins they are registered *before* node::Init runs. @@ -2621,28 +2625,28 @@ extern "C" void node_module_register(void* m) { } } -struct node_module* get_builtin_module(const char* name) { +inline struct node_module* FindModule(struct node_module* list, + const char* name, + int flag) { struct node_module* mp; - for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) { + for (mp = list; mp != nullptr; mp = mp->nm_link) { if (strcmp(mp->nm_modname, name) == 0) break; } - CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0); - return (mp); + CHECK(mp == nullptr || (mp->nm_flags & flag) != 0); + return mp; } -struct node_module* get_linked_module(const char* name) { - struct node_module* mp; - - for (mp = modlist_linked; mp != nullptr; mp = mp->nm_link) { - if (strcmp(mp->nm_modname, name) == 0) - break; - } - - CHECK(mp == nullptr || (mp->nm_flags & NM_F_LINKED) != 0); - return mp; +node_module* get_builtin_module(const char* name) { + return FindModule(modlist_builtin, name, NM_F_BUILTIN); +} +node_module* get_internal_module(const char* name) { + return FindModule(modlist_internal, name, NM_F_INTERNAL); +} +node_module* get_linked_module(const char* name) { + return FindModule(modlist_linked, name, NM_F_LINKED); } struct DLib { @@ -2916,24 +2920,60 @@ void ProcessEmitWarning(Environment* env, const char* fmt, ...) { f.As()->Call(process, 1, &arg); } +static bool PullFromCache(Environment* env, + const FunctionCallbackInfo& args, + Local module, + Local cache) { + Local context = env->context(); + Local exports_v; + Local exports; + if (cache->Get(context, module).ToLocal(&exports_v) && + exports_v->IsObject() && + exports_v->ToObject(context).ToLocal(&exports)) { + args.GetReturnValue().Set(exports); + return true; + } + return false; +} + +static Local InitModule(Environment* env, + node_module* mod, + Local module) { + Local exports = Object::New(env->isolate()); + // Internal bindings don't have a "module" object, only exports. + CHECK_EQ(mod->nm_register_func, nullptr); + CHECK_NE(mod->nm_context_register_func, nullptr); + Local unused = Undefined(env->isolate()); + mod->nm_context_register_func(exports, + unused, + env->context(), + mod->nm_priv); + return exports; +} + +static void ThrowIfNoSuchModule(Environment* env, const char* module_v) { + char errmsg[1024]; + snprintf(errmsg, + sizeof(errmsg), + "No such module: %s", + module_v); + env->ThrowError(errmsg); +} static void Binding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local module = args[0]->ToString(env->isolate()); - node::Utf8Value module_v(env->isolate(), module); + Local module; + if (!args[0]->ToString(env->context()).ToLocal(&module)) return; Local cache = env->binding_cache_object(); - Local exports; - if (cache->Has(env->context(), module).FromJust()) { - exports = cache->Get(module)->ToObject(env->isolate()); - args.GetReturnValue().Set(exports); + if (PullFromCache(env, args, module, cache)) return; - } // Append a string to process.moduleLoadList char buf[1024]; + node::Utf8Value module_v(env->isolate(), module); snprintf(buf, sizeof(buf), "Binding %s", *module_v); Local modules = env->module_load_list_array(); @@ -2941,33 +2981,49 @@ static void Binding(const FunctionCallbackInfo& args) { modules->Set(l, OneByteString(env->isolate(), buf)); node_module* mod = get_builtin_module(*module_v); + Local exports; if (mod != nullptr) { - exports = Object::New(env->isolate()); - // Internal bindings don't have a "module" object, only exports. - CHECK_EQ(mod->nm_register_func, nullptr); - CHECK_NE(mod->nm_context_register_func, nullptr); - Local unused = Undefined(env->isolate()); - mod->nm_context_register_func(exports, unused, - env->context(), mod->nm_priv); - cache->Set(module, exports); + exports = InitModule(env, mod, module); } else if (!strcmp(*module_v, "constants")) { exports = Object::New(env->isolate()); CHECK(exports->SetPrototype(env->context(), Null(env->isolate())).FromJust()); DefineConstants(env->isolate(), exports); - cache->Set(module, exports); } else if (!strcmp(*module_v, "natives")) { exports = Object::New(env->isolate()); DefineJavaScript(env, exports); - cache->Set(module, exports); } else { - char errmsg[1024]; - snprintf(errmsg, - sizeof(errmsg), - "No such module: %s", - *module_v); - return env->ThrowError(errmsg); + return ThrowIfNoSuchModule(env, *module_v); } + cache->Set(module, exports); + + args.GetReturnValue().Set(exports); +} + +static void InternalBinding(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + Local module; + if (!args[0]->ToString(env->context()).ToLocal(&module)) return; + + Local cache = env->internal_binding_cache_object(); + + if (PullFromCache(env, args, module, cache)) + return; + + // Append a string to process.moduleLoadList + char buf[1024]; + node::Utf8Value module_v(env->isolate(), module); + snprintf(buf, sizeof(buf), "Internal Binding %s", *module_v); + + Local modules = env->module_load_list_array(); + uint32_t l = modules->Length(); + modules->Set(l, OneByteString(env->isolate(), buf)); + + node_module* mod = get_internal_module(*module_v); + if (mod == nullptr) return ThrowIfNoSuchModule(env, *module_v); + Local exports = InitModule(env, mod, module); + cache->Set(module, exports); args.GetReturnValue().Set(exports); } @@ -2975,7 +3031,8 @@ static void Binding(const FunctionCallbackInfo& args) { static void LinkedBinding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args.GetIsolate()); - Local module_name = args[0]->ToString(env->isolate()); + Local module_name; + if (!args[0]->ToString(env->context()).ToLocal(&module_name)) return; Local cache = env->binding_cache_object(); Local exports_v = cache->Get(module_name); @@ -3719,6 +3776,7 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "binding", Binding); env->SetMethod(process, "_linkedBinding", LinkedBinding); + env->SetMethod(process, "_internalBinding", InternalBinding); env->SetMethod(process, "_setupProcessObject", SetupProcessObject); env->SetMethod(process, "_setupNextTick", SetupNextTick); diff --git a/src/node.h b/src/node.h index 2542d69f76..7531c97a33 100644 --- a/src/node.h +++ b/src/node.h @@ -441,8 +441,9 @@ typedef void (*addon_context_register_func)( v8::Local context, void* priv); -#define NM_F_BUILTIN 0x01 -#define NM_F_LINKED 0x02 +#define NM_F_BUILTIN 0x01 +#define NM_F_LINKED 0x02 +#define NM_F_INTERNAL 0x04 struct node_module { int nm_version; @@ -456,9 +457,6 @@ struct node_module { struct node_module* nm_link; }; -node_module* get_builtin_module(const char *name); -node_module* get_linked_module(const char *name); - extern "C" NODE_EXTERN void node_module_register(void* mod); #ifdef _WIN32 diff --git a/src/node_internals.h b/src/node_internals.h index 7c4f7a6a7f..54f20c39d3 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -324,6 +324,9 @@ class InternalCallbackScope { bool closed_ = false; }; +#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ + NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_INTERNAL) \ + } // namespace node diff --git a/test/parallel/test-internal-process-binding.js b/test/parallel/test-internal-process-binding.js new file mode 100644 index 0000000000..09e3f31096 --- /dev/null +++ b/test/parallel/test-internal-process-binding.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(undefined, process._internalBinding); +assert.strictEqual(undefined, process.internalBinding); +assert.throws(() => { + process.binding('module_wrap'); +}, /No such module/);