Skip to content

Commit ff7a116

Browse files
joyeecheungtargos
authored andcommitted
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: #19374 PR-URL: #19112 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Gus Caplan <[email protected]>
1 parent 5e90fc6 commit ff7a116

23 files changed

+370
-217
lines changed

.eslintrc.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,3 @@ globals:
214214
LTTNG_HTTP_SERVER_RESPONSE: false
215215
LTTNG_NET_SERVER_CONNECTION: false
216216
LTTNG_NET_STREAM_END: false
217-
internalBinding: false

lib/domain.js

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const util = require('util');
3030
const EventEmitter = require('events');
3131
const errors = require('internal/errors');
3232
const { createHook } = require('async_hooks');
33+
const { internalBinding } = require('internal/bootstrap_loaders');
3334

3435
// overwrite process.domain with a getter/setter that will allow for more
3536
// effective optimizations

lib/internal/bootstrap_loaders.js

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// This file creates the internal module & binding loaders used by built-in
2+
// modules. In contrast, user land modules are loaded using
3+
// lib/module.js (CommonJS Modules) or lib/internal/loader/* (ES Modules).
4+
//
5+
// This file is compiled and run by node.cc before bootstrap_node.js
6+
// was called, therefore the loaders are bootstraped before we start to
7+
// actually bootstrap Node.js. It creates the following objects:
8+
//
9+
// C++ binding loaders:
10+
// - process.binding(): the legacy C++ binding loader, accessible from user land
11+
// because it is an object attached to the global process object.
12+
// These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE()
13+
// and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees
14+
// about the stability of these bindings, but still have to take care of
15+
// compatibility issues caused by them from time to time.
16+
// - process._linkedBinding(): intended to be used by embedders to add
17+
// additional C++ bindings in their applications. These C++ bindings
18+
// can be created using NODE_MODULE_CONTEXT_AWARE_CPP() with the flag
19+
// NM_F_LINKED.
20+
// - internalBinding(): the private internal C++ binding loader, inaccessible
21+
// from user land because they are only available from NativeModule.require()
22+
// These C++ bindings are created using NODE_MODULE_CONTEXT_AWARE_INTERNAL()
23+
// and have their nm_flags set to NM_F_INTERNAL.
24+
//
25+
// Internal JavaScript module loader:
26+
// - NativeModule: a minimal module system used to load the JavaScript core
27+
// modules found in lib/**/*.js and deps/**/*.js. All core modules are
28+
// compiled into the node binary via node_javascript.cc generated by js2c.py,
29+
// so they can be loaded faster without the cost of I/O. This class makes the
30+
// lib/internal/*, deps/internal/* modules and internalBinding() available by
31+
// default to core modules, and lets the core modules require itself via
32+
// require('internal/bootstrap_loaders') even when this file is not written in
33+
// CommonJS style.
34+
//
35+
// Other objects:
36+
// - process.moduleLoadList: an array recording the bindings and the modules
37+
// loaded in the process and the order in which they are loaded.
38+
39+
'use strict';
40+
41+
(function bootstrapInternalLoaders(process, getBinding, getLinkedBinding,
42+
getInternalBinding) {
43+
44+
// Set up process.moduleLoadList
45+
const moduleLoadList = [];
46+
Object.defineProperty(process, 'moduleLoadList', {
47+
value: moduleLoadList,
48+
configurable: true,
49+
enumerable: true,
50+
writable: false
51+
});
52+
53+
// Set up process.binding() and process._linkedBinding()
54+
{
55+
const bindingObj = Object.create(null);
56+
57+
process.binding = function binding(module) {
58+
module = String(module);
59+
let mod = bindingObj[module];
60+
if (typeof mod !== 'object') {
61+
mod = bindingObj[module] = getBinding(module);
62+
moduleLoadList.push(`Binding ${module}`);
63+
}
64+
return mod;
65+
};
66+
67+
process._linkedBinding = function _linkedBinding(module) {
68+
module = String(module);
69+
let mod = bindingObj[module];
70+
if (typeof mod !== 'object')
71+
mod = bindingObj[module] = getLinkedBinding(module);
72+
return mod;
73+
};
74+
}
75+
76+
// Set up internalBinding() in the closure
77+
let internalBinding;
78+
{
79+
const bindingObj = Object.create(null);
80+
internalBinding = function internalBinding(module) {
81+
let mod = bindingObj[module];
82+
if (typeof mod !== 'object') {
83+
mod = bindingObj[module] = getInternalBinding(module);
84+
moduleLoadList.push(`Internal Binding ${module}`);
85+
}
86+
return mod;
87+
};
88+
}
89+
90+
// Minimal sandbox helper
91+
const ContextifyScript = process.binding('contextify').ContextifyScript;
92+
function runInThisContext(code, options) {
93+
const script = new ContextifyScript(code, options);
94+
return script.runInThisContext();
95+
}
96+
97+
// Set up NativeModule
98+
function NativeModule(id) {
99+
this.filename = `${id}.js`;
100+
this.id = id;
101+
this.exports = {};
102+
this.loaded = false;
103+
this.loading = false;
104+
}
105+
106+
NativeModule._source = getBinding('natives');
107+
NativeModule._cache = {};
108+
109+
const config = getBinding('config');
110+
111+
// Think of this as module.exports in this file even though it is not
112+
// written in CommonJS style.
113+
const loaderExports = { internalBinding, NativeModule };
114+
const loaderId = 'internal/bootstrap_loaders';
115+
NativeModule.require = function(id) {
116+
if (id === loaderId) {
117+
return loaderExports;
118+
}
119+
120+
const cached = NativeModule.getCached(id);
121+
if (cached && (cached.loaded || cached.loading)) {
122+
return cached.exports;
123+
}
124+
125+
if (!NativeModule.exists(id)) {
126+
// Model the error off the internal/errors.js model, but
127+
// do not use that module given that it could actually be
128+
// the one causing the error if there's a bug in Node.js
129+
const err = new Error(`No such built-in module: ${id}`);
130+
err.code = 'ERR_UNKNOWN_BUILTIN_MODULE';
131+
err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]';
132+
throw err;
133+
}
134+
135+
moduleLoadList.push(`NativeModule ${id}`);
136+
137+
const nativeModule = new NativeModule(id);
138+
139+
nativeModule.cache();
140+
nativeModule.compile();
141+
142+
return nativeModule.exports;
143+
};
144+
145+
NativeModule.getCached = function(id) {
146+
return NativeModule._cache[id];
147+
};
148+
149+
NativeModule.exists = function(id) {
150+
return NativeModule._source.hasOwnProperty(id);
151+
};
152+
153+
if (config.exposeInternals) {
154+
NativeModule.nonInternalExists = function(id) {
155+
// Do not expose this to user land even with --expose-internals
156+
if (id === loaderId) {
157+
return false;
158+
}
159+
return NativeModule.exists(id);
160+
};
161+
162+
NativeModule.isInternal = function(id) {
163+
// Do not expose this to user land even with --expose-internals
164+
return id === loaderId;
165+
};
166+
} else {
167+
NativeModule.nonInternalExists = function(id) {
168+
return NativeModule.exists(id) && !NativeModule.isInternal(id);
169+
};
170+
171+
NativeModule.isInternal = function(id) {
172+
return id.startsWith('internal/');
173+
};
174+
}
175+
176+
NativeModule.getSource = function(id) {
177+
return NativeModule._source[id];
178+
};
179+
180+
NativeModule.wrap = function(script) {
181+
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
182+
};
183+
184+
NativeModule.wrapper = [
185+
'(function (exports, require, module, process) {',
186+
'\n});'
187+
];
188+
189+
NativeModule.prototype.compile = function() {
190+
let source = NativeModule.getSource(this.id);
191+
source = NativeModule.wrap(source);
192+
193+
this.loading = true;
194+
195+
try {
196+
const fn = runInThisContext(source, {
197+
filename: this.filename,
198+
lineOffset: 0,
199+
displayErrors: true
200+
});
201+
fn(this.exports, NativeModule.require, this, process);
202+
203+
this.loaded = true;
204+
} finally {
205+
this.loading = false;
206+
}
207+
};
208+
209+
NativeModule.prototype.cache = function() {
210+
NativeModule._cache[this.id] = this;
211+
};
212+
213+
// This will be passed to the bootstrapNodeJSCore function in
214+
// bootstrap_node.js.
215+
return loaderExports;
216+
});

0 commit comments

Comments
 (0)