From ff7a116ba3b4c3992aaec6212c79a7006784d91b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sun, 4 Mar 2018 04:55:45 +0800 Subject: [PATCH] src: move internal loaders out of bootstrap_node.js - Moves the creation of `process.binding()`, `process._linkedBinding()` `internalBinding()` and `NativeModule` into a separate file `lib/internal/bootstrap_loaders.js`, and documents them there. This file will be compiled and run before `bootstrap_node.js`, which means we now bootstrap the internal module & binding system before actually bootstrapping Node.js. - Rename the special ID that can be used to require `NativeModule` as `internal/bootstrap_loaders` since it is setup there. Also put `internalBinding` in the object exported by `NativeModule.require` instead of putting it inside the `NativeModule.wrapper` - Use the original `getBinding()` to get the source code of native modules instead of getting it from `process.binding('native')` so that users cannot fake native modules by modifying the binding object. - Names the bootstrapping functions so their names show up in the stack trace. Backport-PR-URL: https://github.com/nodejs/node/pull/19374 PR-URL: https://github.com/nodejs/node/pull/19112 Reviewed-By: Anna Henningsen Reviewed-By: Anatoli Papirovski Reviewed-By: James M Snell Reviewed-By: Gus Caplan --- .eslintrc.yaml | 1 - lib/domain.js | 1 + lib/internal/bootstrap_loaders.js | 216 ++++++++++++++++++ lib/internal/bootstrap_node.js | 172 +------------- lib/internal/loader/CreateDynamicModule.js | 1 + lib/internal/loader/DefaultResolve.js | 2 +- lib/internal/loader/ModuleJob.js | 1 + lib/internal/loader/ModuleWrap.js | 1 + lib/internal/loader/Translators.js | 2 +- lib/internal/process/modules.js | 1 + lib/internal/vm/Module.js | 1 + lib/module.js | 2 +- node.gyp | 1 + src/node.cc | 129 ++++++++--- src/node_javascript.h | 3 +- test/message/error_exit.out | 2 +- test/message/eval_messages.out | 8 +- .../events_unhandled_error_common_trace.out | 2 +- .../events_unhandled_error_nexttick.out | 4 +- .../events_unhandled_error_sameline.out | 4 +- test/message/nexttick_throw.out | 2 +- .../test-loaders-hidden-from-users.js | 25 ++ tools/js2c.py | 6 +- 23 files changed, 370 insertions(+), 217 deletions(-) create mode 100644 lib/internal/bootstrap_loaders.js create mode 100644 test/parallel/test-loaders-hidden-from-users.js diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 99489d2e7d8589..201af11f017733 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -214,4 +214,3 @@ globals: LTTNG_HTTP_SERVER_RESPONSE: false LTTNG_NET_SERVER_CONNECTION: false LTTNG_NET_STREAM_END: false - internalBinding: false diff --git a/lib/domain.js b/lib/domain.js index 08fbd207f171d3..95946852db7ff4 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -30,6 +30,7 @@ const util = require('util'); const EventEmitter = require('events'); const errors = require('internal/errors'); const { createHook } = require('async_hooks'); +const { internalBinding } = require('internal/bootstrap_loaders'); // overwrite process.domain with a getter/setter that will allow for more // effective optimizations diff --git a/lib/internal/bootstrap_loaders.js b/lib/internal/bootstrap_loaders.js new file mode 100644 index 00000000000000..71dd574c787701 --- /dev/null +++ b/lib/internal/bootstrap_loaders.js @@ -0,0 +1,216 @@ +// This file creates the internal module & binding loaders used by built-in +// modules. In contrast, user land modules are loaded using +// lib/module.js (CommonJS Modules) or lib/internal/loader/* (ES Modules). +// +// This file is compiled and run by node.cc before bootstrap_node.js +// was called, therefore the loaders are bootstraped before we start to +// actually bootstrap Node.js. It creates the following objects: +// +// C++ binding loaders: +// - process.binding(): the legacy C++ binding loader, accessible from user land +// because it is an object attached to the global process object. +// These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE() +// and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees +// about the stability of these bindings, but still have to take care of +// compatibility issues caused by them from time to time. +// - process._linkedBinding(): intended to be used by embedders to add +// additional C++ bindings in their applications. These C++ bindings +// can be created using NODE_MODULE_CONTEXT_AWARE_CPP() with the flag +// NM_F_LINKED. +// - internalBinding(): the private internal C++ binding loader, inaccessible +// from user land because they are only available from NativeModule.require() +// These C++ bindings are created using NODE_MODULE_CONTEXT_AWARE_INTERNAL() +// and have their nm_flags set to NM_F_INTERNAL. +// +// Internal JavaScript module loader: +// - NativeModule: a minimal module system used to load the JavaScript core +// modules found in lib/**/*.js and deps/**/*.js. All core modules are +// compiled into the node binary via node_javascript.cc generated by js2c.py, +// so they can be loaded faster without the cost of I/O. This class makes the +// lib/internal/*, deps/internal/* modules and internalBinding() available by +// default to core modules, and lets the core modules require itself via +// require('internal/bootstrap_loaders') even when this file is not written in +// CommonJS style. +// +// Other objects: +// - process.moduleLoadList: an array recording the bindings and the modules +// loaded in the process and the order in which they are loaded. + +'use strict'; + +(function bootstrapInternalLoaders(process, getBinding, getLinkedBinding, + getInternalBinding) { + + // Set up process.moduleLoadList + const moduleLoadList = []; + Object.defineProperty(process, 'moduleLoadList', { + value: moduleLoadList, + configurable: true, + enumerable: true, + writable: false + }); + + // Set up process.binding() and process._linkedBinding() + { + const bindingObj = Object.create(null); + + process.binding = function binding(module) { + module = String(module); + let mod = bindingObj[module]; + if (typeof mod !== 'object') { + mod = bindingObj[module] = getBinding(module); + moduleLoadList.push(`Binding ${module}`); + } + return mod; + }; + + process._linkedBinding = function _linkedBinding(module) { + module = String(module); + let mod = bindingObj[module]; + if (typeof mod !== 'object') + mod = bindingObj[module] = getLinkedBinding(module); + return mod; + }; + } + + // Set up internalBinding() in the closure + let internalBinding; + { + const bindingObj = Object.create(null); + internalBinding = function internalBinding(module) { + let mod = bindingObj[module]; + if (typeof mod !== 'object') { + mod = bindingObj[module] = getInternalBinding(module); + moduleLoadList.push(`Internal Binding ${module}`); + } + return mod; + }; + } + + // Minimal sandbox helper + const ContextifyScript = process.binding('contextify').ContextifyScript; + function runInThisContext(code, options) { + const script = new ContextifyScript(code, options); + return script.runInThisContext(); + } + + // Set up NativeModule + function NativeModule(id) { + this.filename = `${id}.js`; + this.id = id; + this.exports = {}; + this.loaded = false; + this.loading = false; + } + + NativeModule._source = getBinding('natives'); + NativeModule._cache = {}; + + const config = getBinding('config'); + + // Think of this as module.exports in this file even though it is not + // written in CommonJS style. + const loaderExports = { internalBinding, NativeModule }; + const loaderId = 'internal/bootstrap_loaders'; + NativeModule.require = function(id) { + if (id === loaderId) { + return loaderExports; + } + + const cached = NativeModule.getCached(id); + if (cached && (cached.loaded || cached.loading)) { + return cached.exports; + } + + if (!NativeModule.exists(id)) { + // Model the error off the internal/errors.js model, but + // do not use that module given that it could actually be + // the one causing the error if there's a bug in Node.js + const err = new Error(`No such built-in module: ${id}`); + err.code = 'ERR_UNKNOWN_BUILTIN_MODULE'; + err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]'; + throw err; + } + + moduleLoadList.push(`NativeModule ${id}`); + + const nativeModule = new NativeModule(id); + + nativeModule.cache(); + nativeModule.compile(); + + return nativeModule.exports; + }; + + NativeModule.getCached = function(id) { + return NativeModule._cache[id]; + }; + + NativeModule.exists = function(id) { + return NativeModule._source.hasOwnProperty(id); + }; + + if (config.exposeInternals) { + NativeModule.nonInternalExists = function(id) { + // Do not expose this to user land even with --expose-internals + if (id === loaderId) { + return false; + } + return NativeModule.exists(id); + }; + + NativeModule.isInternal = function(id) { + // Do not expose this to user land even with --expose-internals + return id === loaderId; + }; + } else { + NativeModule.nonInternalExists = function(id) { + return NativeModule.exists(id) && !NativeModule.isInternal(id); + }; + + NativeModule.isInternal = function(id) { + return id.startsWith('internal/'); + }; + } + + NativeModule.getSource = function(id) { + return NativeModule._source[id]; + }; + + NativeModule.wrap = function(script) { + return NativeModule.wrapper[0] + script + NativeModule.wrapper[1]; + }; + + NativeModule.wrapper = [ + '(function (exports, require, module, process) {', + '\n});' + ]; + + NativeModule.prototype.compile = function() { + let source = NativeModule.getSource(this.id); + source = NativeModule.wrap(source); + + this.loading = true; + + try { + const fn = runInThisContext(source, { + filename: this.filename, + lineOffset: 0, + displayErrors: true + }); + fn(this.exports, NativeModule.require, this, process); + + this.loaded = true; + } finally { + this.loading = false; + } + }; + + NativeModule.prototype.cache = function() { + NativeModule._cache[this.id] = this; + }; + + // This will be passed to the bootstrapNodeJSCore function in + // bootstrap_node.js. + return loaderExports; +}); diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index d8096f90e172ea..690eeb5a996efb 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -4,11 +4,16 @@ // responsible for bootstrapping the node.js core. As special caution is given // to the performance of the startup process, many dependencies are invoked // lazily. +// +// Before this file is run, lib/internal/bootstrap_loaders.js gets run first +// to bootstrap the internal binding and module loaders, including +// process.binding(), process._linkedBinding(), internalBinding() and +// NativeModule. And then { internalBinding, NativeModule } will be passed +// into this bootstrapper to bootstrap Node.js core. 'use strict'; -(function(process) { - let internalBinding; +(function bootstrapNodeJSCore(process, { internalBinding, NativeModule }) { const exceptionHandlerState = { captureFn: null }; function startup() { @@ -246,54 +251,6 @@ perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); } - const moduleLoadList = []; - Object.defineProperty(process, 'moduleLoadList', { - value: moduleLoadList, - configurable: true, - enumerable: true, - writable: false - }); - - { - const bindingObj = Object.create(null); - - const getBinding = process.binding; - process.binding = function binding(module) { - module = String(module); - let mod = bindingObj[module]; - if (typeof mod !== 'object') { - mod = bindingObj[module] = getBinding(module); - moduleLoadList.push(`Binding ${module}`); - } - return mod; - }; - - const getLinkedBinding = process._linkedBinding; - process._linkedBinding = function _linkedBinding(module) { - module = String(module); - let mod = bindingObj[module]; - if (typeof mod !== 'object') - mod = bindingObj[module] = getLinkedBinding(module); - return mod; - }; - } - - { - const bindingObj = Object.create(null); - - const getInternalBinding = process._internalBinding; - delete process._internalBinding; - - internalBinding = function internalBinding(module) { - let mod = bindingObj[module]; - if (typeof mod !== 'object') { - mod = bindingObj[module] = getInternalBinding(module); - moduleLoadList.push(`Internal Binding ${module}`); - } - return mod; - }; - } - function setupProcessObject() { process._setupProcessObject(pushValueToArray); @@ -548,120 +505,5 @@ new vm.Script(source, { displayErrors: true, filename }); } - // Below you find a minimal module system, which is used to load the node - // core modules found in lib/*.js. All core modules are compiled into the - // node binary, so they can be loaded faster. - - const ContextifyScript = process.binding('contextify').ContextifyScript; - function runInThisContext(code, options) { - const script = new ContextifyScript(code, options); - return script.runInThisContext(); - } - - function NativeModule(id) { - this.filename = `${id}.js`; - this.id = id; - this.exports = {}; - this.loaded = false; - this.loading = false; - } - - NativeModule._source = process.binding('natives'); - NativeModule._cache = {}; - - const config = process.binding('config'); - - NativeModule.require = function(id) { - if (id === 'native_module') { - return NativeModule; - } - - const cached = NativeModule.getCached(id); - if (cached && (cached.loaded || cached.loading)) { - return cached.exports; - } - - if (!NativeModule.exists(id)) { - // Model the error off the internal/errors.js model, but - // do not use that module given that it could actually be - // the one causing the error if there's a bug in Node.js - const err = new Error(`No such built-in module: ${id}`); - err.code = 'ERR_UNKNOWN_BUILTIN_MODULE'; - err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]'; - throw err; - } - - moduleLoadList.push(`NativeModule ${id}`); - - const nativeModule = new NativeModule(id); - - nativeModule.cache(); - nativeModule.compile(); - - return nativeModule.exports; - }; - - NativeModule.getCached = function(id) { - return NativeModule._cache[id]; - }; - - NativeModule.exists = function(id) { - return NativeModule._source.hasOwnProperty(id); - }; - - if (config.exposeInternals) { - NativeModule.nonInternalExists = NativeModule.exists; - - NativeModule.isInternal = function(id) { - return false; - }; - } else { - NativeModule.nonInternalExists = function(id) { - return NativeModule.exists(id) && !NativeModule.isInternal(id); - }; - - NativeModule.isInternal = function(id) { - return id.startsWith('internal/'); - }; - } - - - NativeModule.getSource = function(id) { - return NativeModule._source[id]; - }; - - NativeModule.wrap = function(script) { - return NativeModule.wrapper[0] + script + NativeModule.wrapper[1]; - }; - - NativeModule.wrapper = [ - '(function (exports, require, module, internalBinding, process) {', - '\n});' - ]; - - NativeModule.prototype.compile = function() { - let source = NativeModule.getSource(this.id); - source = NativeModule.wrap(source); - - this.loading = true; - - try { - const fn = runInThisContext(source, { - filename: this.filename, - lineOffset: 0, - displayErrors: true - }); - fn(this.exports, NativeModule.require, this, internalBinding, process); - - this.loaded = true; - } finally { - this.loading = false; - } - }; - - NativeModule.prototype.cache = function() { - NativeModule._cache[this.id] = this; - }; - startup(); }); diff --git a/lib/internal/loader/CreateDynamicModule.js b/lib/internal/loader/CreateDynamicModule.js index f2596de04bfcb3..8c289171386a0d 100644 --- a/lib/internal/loader/CreateDynamicModule.js +++ b/lib/internal/loader/CreateDynamicModule.js @@ -1,5 +1,6 @@ 'use strict'; +const { internalBinding } = require('internal/bootstrap_loaders'); const { ModuleWrap } = internalBinding('module_wrap'); const debug = require('util').debuglog('esm'); const ArrayJoin = Function.call.bind(Array.prototype.join); diff --git a/lib/internal/loader/DefaultResolve.js b/lib/internal/loader/DefaultResolve.js index 1c5323436c3391..ce1e8737b2e5fe 100644 --- a/lib/internal/loader/DefaultResolve.js +++ b/lib/internal/loader/DefaultResolve.js @@ -3,7 +3,7 @@ const { URL } = require('url'); const CJSmodule = require('module'); const internalFS = require('internal/fs'); -const NativeModule = require('native_module'); +const { NativeModule, internalBinding } = require('internal/bootstrap_loaders'); const { extname } = require('path'); const { realpathSync } = require('fs'); const preserveSymlinks = !!process.binding('config').preserveSymlinks; diff --git a/lib/internal/loader/ModuleJob.js b/lib/internal/loader/ModuleJob.js index b3553fc7235d95..162b0504d3d313 100644 --- a/lib/internal/loader/ModuleJob.js +++ b/lib/internal/loader/ModuleJob.js @@ -1,5 +1,6 @@ 'use strict'; +const { internalBinding } = require('internal/bootstrap_loaders'); const { ModuleWrap } = internalBinding('module_wrap'); const { SafeSet, SafePromise } = require('internal/safe_globals'); const { decorateErrorStack } = require('internal/util'); diff --git a/lib/internal/loader/ModuleWrap.js b/lib/internal/loader/ModuleWrap.js index b2b11daead7dde..6015e37d9aea63 100644 --- a/lib/internal/loader/ModuleWrap.js +++ b/lib/internal/loader/ModuleWrap.js @@ -2,4 +2,5 @@ // exposes ModuleWrap for testing +const { internalBinding } = require('internal/bootstrap_loaders'); module.exports = internalBinding('module_wrap').ModuleWrap; diff --git a/lib/internal/loader/Translators.js b/lib/internal/loader/Translators.js index 18b1b12fd15854..74dd4358274d3b 100644 --- a/lib/internal/loader/Translators.js +++ b/lib/internal/loader/Translators.js @@ -1,7 +1,7 @@ 'use strict'; +const { NativeModule, internalBinding } = require('internal/bootstrap_loaders'); const { ModuleWrap } = internalBinding('module_wrap'); -const NativeModule = require('native_module'); const internalCJSModule = require('internal/module'); const CJSModule = require('module'); const internalURLModule = require('internal/url'); diff --git a/lib/internal/process/modules.js b/lib/internal/process/modules.js index bc977c718725f2..c53c55a53ca48e 100644 --- a/lib/internal/process/modules.js +++ b/lib/internal/process/modules.js @@ -1,5 +1,6 @@ 'use strict'; +const { internalBinding } = require('internal/bootstrap_loaders'); const { setImportModuleDynamicallyCallback } = internalBinding('module_wrap'); diff --git a/lib/internal/vm/Module.js b/lib/internal/vm/Module.js index a8fb7303aec131..e954babf1df5af 100644 --- a/lib/internal/vm/Module.js +++ b/lib/internal/vm/Module.js @@ -1,5 +1,6 @@ 'use strict'; +const { internalBinding } = require('internal/bootstrap_loaders'); const { emitExperimentalWarning } = require('internal/util'); const { URL } = require('internal/url'); const { kParsingContext, isContext } = process.binding('contextify'); diff --git a/lib/module.js b/lib/module.js index 877cca590f077b..ae0d44988be398 100644 --- a/lib/module.js +++ b/lib/module.js @@ -21,7 +21,7 @@ 'use strict'; -const NativeModule = require('native_module'); +const { NativeModule } = require('internal/bootstrap_loaders'); const util = require('util'); const { decorateErrorStack } = require('internal/util'); const { getURLFromFilePath } = require('internal/url'); diff --git a/node.gyp b/node.gyp index 349f9a0b6fb755..f6586621bbe6fa 100644 --- a/node.gyp +++ b/node.gyp @@ -25,6 +25,7 @@ 'node_lib_target_name%': 'node_lib', 'node_intermediate_lib_type%': 'static_library', 'library_files': [ + 'lib/internal/bootstrap_loaders.js', 'lib/internal/bootstrap_node.js', 'lib/async_hooks.js', 'lib/assert.js', diff --git a/src/node.cc b/src/node.cc index e87dfe614a3cd3..7d126a97a62b96 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2854,7 +2854,7 @@ static void ThrowIfNoSuchModule(Environment* env, const char* module_v) { env->ThrowError(errmsg); } -static void Binding(const FunctionCallbackInfo& args) { +static void GetBinding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsString()); @@ -2881,7 +2881,7 @@ static void Binding(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(exports); } -static void InternalBinding(const FunctionCallbackInfo& args) { +static void GetInternalBinding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsString()); @@ -2896,7 +2896,7 @@ static void InternalBinding(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(exports); } -static void LinkedBinding(const FunctionCallbackInfo& args) { +static void GetLinkedBinding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args.GetIsolate()); CHECK(args[0]->IsString()); @@ -3596,10 +3596,6 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "uptime", Uptime); env->SetMethod(process, "memoryUsage", MemoryUsage); - 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); env->SetMethod(process, "_setupPromises", SetupPromises); @@ -3637,8 +3633,10 @@ static void RawDebug(const FunctionCallbackInfo& args) { fflush(stderr); } -void LoadEnvironment(Environment* env) { - HandleScope handle_scope(env->isolate()); + +static Local GetBootstrapper(Environment* env, Local source, + Local script_name) { + EscapableHandleScope scope(env->isolate()); TryCatch try_catch(env->isolate()); @@ -3647,20 +3645,59 @@ void LoadEnvironment(Environment* env) { // are not safe to ignore. try_catch.SetVerbose(false); - // Execute the lib/internal/bootstrap_node.js file which was included as a - // static C string in node_natives.h by node_js2c. - // 'internal_bootstrap_node_native' is the string containing that source code. - Local script_name = FIXED_ONE_BYTE_STRING(env->isolate(), - "bootstrap_node.js"); - Local f_value = ExecuteString(env, MainSource(env), script_name); + // Execute the factory javascript file + Local factory_v = ExecuteString(env, source, script_name); if (try_catch.HasCaught()) { ReportException(env, try_catch); exit(10); } - // The bootstrap_node.js file returns a function 'f' - CHECK(f_value->IsFunction()); - Local f = Local::Cast(f_value); + CHECK(factory_v->IsFunction()); + Local factory = Local::Cast(factory_v); + + return scope.Escape(factory); +} + +static bool ExecuteBootstrapper(Environment* env, Local factory, + int argc, Local argv[], + Local* out) { + bool ret = factory->Call( + env->context(), Null(env->isolate()), argc, argv).ToLocal(out); + + // If there was an error during bootstrap then it was either handled by the + // FatalException handler or it's unrecoverable (e.g. max call stack + // exceeded). Either way, clear the stack so that the AsyncCallbackScope + // destructor doesn't fail on the id check. + // There are only two ways to have a stack size > 1: 1) the user manually + // called MakeCallback or 2) user awaited during bootstrap, which triggered + // _tickCallback(). + if (!ret) { + env->async_hooks()->clear_async_id_stack(); + } + + return ret; +} + + +void LoadEnvironment(Environment* env) { + HandleScope handle_scope(env->isolate()); + + TryCatch try_catch(env->isolate()); + // Disable verbose mode to stop FatalException() handler from trying + // to handle the exception. Errors this early in the start-up phase + // are not safe to ignore. + try_catch.SetVerbose(false); + + // The factory scripts are lib/internal/bootstrap_loaders.js and + // lib/internal/bootstrap_node.js, each included as a static C string + // defined in node_javascript.h, generated in node_javascript.cc by + // node_js2c. + Local loaders_bootstrapper = + GetBootstrapper(env, LoadersBootstrapperSource(env), + FIXED_ONE_BYTE_STRING(env->isolate(), "bootstrap_loaders.js")); + Local node_bootstrapper = + GetBootstrapper(env, NodeBootstrapperSource(env), + FIXED_ONE_BYTE_STRING(env->isolate(), "bootstrap_node.js")); // Add a reference to the global object Local global = env->context()->Global(); @@ -3691,25 +3728,47 @@ void LoadEnvironment(Environment* env) { // (Allows you to set stuff on `global` from anywhere in JavaScript.) global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global); - // Now we call 'f' with the 'process' variable that we've built up with - // all our bindings. Inside bootstrap_node.js and internal/process we'll - // take care of assigning things to their places. + // Create binding loaders + v8::Local get_binding_fn = + env->NewFunctionTemplate(GetBinding)->GetFunction(env->context()) + .ToLocalChecked(); - // We start the process this way in order to be more modular. Developers - // who do not like how bootstrap_node.js sets up the module system but do - // like Node's I/O bindings may want to replace 'f' with their own function. - Local arg = env->process_object(); + v8::Local get_linked_binding_fn = + env->NewFunctionTemplate(GetLinkedBinding)->GetFunction(env->context()) + .ToLocalChecked(); - auto ret = f->Call(env->context(), Null(env->isolate()), 1, &arg); - // If there was an error during bootstrap then it was either handled by the - // FatalException handler or it's unrecoverable (e.g. max call stack - // exceeded). Either way, clear the stack so that the AsyncCallbackScope - // destructor doesn't fail on the id check. - // There are only two ways to have a stack size > 1: 1) the user manually - // called MakeCallback or 2) user awaited during bootstrap, which triggered - // _tickCallback(). - if (ret.IsEmpty()) - env->async_hooks()->clear_async_id_stack(); + v8::Local get_internal_binding_fn = + env->NewFunctionTemplate(GetInternalBinding)->GetFunction(env->context()) + .ToLocalChecked(); + + Local loaders_bootstrapper_args[] = { + env->process_object(), + get_binding_fn, + get_linked_binding_fn, + get_internal_binding_fn + }; + + // Bootstrap internal loaders + Local bootstrapped_loaders; + if (!ExecuteBootstrapper(env, loaders_bootstrapper, + arraysize(loaders_bootstrapper_args), + loaders_bootstrapper_args, + &bootstrapped_loaders)) { + return; + } + + // Bootstrap Node.js + Local bootstrapped_node; + Local node_bootstrapper_args[] = { + env->process_object(), + bootstrapped_loaders + }; + if (!ExecuteBootstrapper(env, node_bootstrapper, + arraysize(node_bootstrapper_args), + node_bootstrapper_args, + &bootstrapped_node)) { + return; + } } static void PrintHelp() { diff --git a/src/node_javascript.h b/src/node_javascript.h index 664778091ff669..d1ad9a25745e9b 100644 --- a/src/node_javascript.h +++ b/src/node_javascript.h @@ -29,7 +29,8 @@ namespace node { void DefineJavaScript(Environment* env, v8::Local target); -v8::Local MainSource(Environment* env); +v8::Local LoadersBootstrapperSource(Environment* env); +v8::Local NodeBootstrapperSource(Environment* env); } // namespace node diff --git a/test/message/error_exit.out b/test/message/error_exit.out index 8f03f08a7e0392..5f4f792727bab0 100644 --- a/test/message/error_exit.out +++ b/test/message/error_exit.out @@ -12,4 +12,4 @@ AssertionError [ERR_ASSERTION]: 1 === 2 at Function.Module._load (module.js:*:*) at Function.Module.runMain (module.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) diff --git a/test/message/eval_messages.out b/test/message/eval_messages.out index 6c725cb1cc8a14..7dbf9e950c2c71 100644 --- a/test/message/eval_messages.out +++ b/test/message/eval_messages.out @@ -10,7 +10,7 @@ SyntaxError: Strict mode code may not include a with statement at Module._compile (module.js:*:*) at evalScript (bootstrap_node.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) 42 42 [eval]:1 @@ -25,7 +25,7 @@ Error: hello at Module._compile (module.js:*:*) at evalScript (bootstrap_node.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) [eval]:1 throw new Error("hello") @@ -39,7 +39,7 @@ Error: hello at Module._compile (module.js:*:*) at evalScript (bootstrap_node.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) 100 [eval]:1 var x = 100; y = x; @@ -53,7 +53,7 @@ ReferenceError: y is not defined at Module._compile (module.js:*:*) at evalScript (bootstrap_node.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) [eval]:1 var ______________________________________________; throw 10 diff --git a/test/message/events_unhandled_error_common_trace.out b/test/message/events_unhandled_error_common_trace.out index d39a95cb77f068..15355734705331 100644 --- a/test/message/events_unhandled_error_common_trace.out +++ b/test/message/events_unhandled_error_common_trace.out @@ -19,4 +19,4 @@ Emitted 'error' event at: at Module._compile (module.js:*:*) [... lines matching original stack trace ...] at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) diff --git a/test/message/events_unhandled_error_nexttick.out b/test/message/events_unhandled_error_nexttick.out index f0591610ffdb31..c578f55f90c45a 100644 --- a/test/message/events_unhandled_error_nexttick.out +++ b/test/message/events_unhandled_error_nexttick.out @@ -11,10 +11,10 @@ Error at Function.Module._load (module.js:*:*) at Function.Module.runMain (module.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) Emitted 'error' event at: at process.nextTick (*events_unhandled_error_nexttick.js:*:*) at process._tickCallback (internal/process/next_tick.js:*:*) at Function.Module.runMain (module.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) diff --git a/test/message/events_unhandled_error_sameline.out b/test/message/events_unhandled_error_sameline.out index 100c294276d04b..d8441c44d41fd5 100644 --- a/test/message/events_unhandled_error_sameline.out +++ b/test/message/events_unhandled_error_sameline.out @@ -11,9 +11,9 @@ Error at Function.Module._load (module.js:*:*) at Function.Module.runMain (module.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) Emitted 'error' event at: at Object. (*events_unhandled_error_sameline.js:*:*) at Module._compile (module.js:*:*) [... lines matching original stack trace ...] - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) diff --git a/test/message/nexttick_throw.out b/test/message/nexttick_throw.out index 1c9eca8405d928..41412b95f6ae5a 100644 --- a/test/message/nexttick_throw.out +++ b/test/message/nexttick_throw.out @@ -7,4 +7,4 @@ ReferenceError: undefined_reference_error_maker is not defined at process._tickCallback (internal/process/next_tick.js:*:*) at Function.Module.runMain (module.js:*:*) at startup (bootstrap_node.js:*:*) - at bootstrap_node.js:*:* + at bootstrapNodeJSCore (bootstrap_node.js:*:*) diff --git a/test/parallel/test-loaders-hidden-from-users.js b/test/parallel/test-loaders-hidden-from-users.js new file mode 100644 index 00000000000000..b3e6622c8c0c88 --- /dev/null +++ b/test/parallel/test-loaders-hidden-from-users.js @@ -0,0 +1,25 @@ +// Flags: --expose-internals + +'use strict'; + +const common = require('../common'); + +common.expectsError( + () => { + require('internal/bootstrap_loaders'); + }, { + code: 'MODULE_NOT_FOUND', + message: 'Cannot find module \'internal/bootstrap_loaders\'' + } +); + +common.expectsError( + () => { + const source = 'module.exports = require("internal/bootstrap_loaders")'; + process.binding('natives').owo = source; + require('owo'); + }, { + code: 'MODULE_NOT_FOUND', + message: 'Cannot find module \'owo\'' + } +); diff --git a/tools/js2c.py b/tools/js2c.py index fa22b0b9722ce4..868e87759e43a3 100755 --- a/tools/js2c.py +++ b/tools/js2c.py @@ -185,7 +185,11 @@ def ReadMacros(lines): {definitions} -v8::Local MainSource(Environment* env) {{ +v8::Local LoadersBootstrapperSource(Environment* env) {{ + return internal_bootstrap_loaders_value.ToStringChecked(env->isolate()); +}} + +v8::Local NodeBootstrapperSource(Environment* env) {{ return internal_bootstrap_node_value.ToStringChecked(env->isolate()); }}