Skip to content

Commit 30d3249

Browse files
joyeecheungMylesBorins
authored andcommitted
lib: refactor NativeModule
Refactor the internal NativeModule class to a JS class and add more documentation about its properties. PR-URL: #30856 Reviewed-By: Denys Otrishko <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 60225c1 commit 30d3249

File tree

1 file changed

+146
-132
lines changed

1 file changed

+146
-132
lines changed

‎lib/internal/bootstrap/loaders.js‎

Lines changed: 146 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -137,166 +137,180 @@ let internalBinding;
137137
};
138138
}
139139

140-
// Think of this as module.exports in this file even though it is not
141-
// written in CommonJS style.
142-
constloaderExports={
143-
internalBinding,
144-
NativeModule,
145-
require: nativeModuleRequire
146-
};
147-
148140
constloaderId='internal/bootstrap/loaders';
149-
150-
// Set up NativeModule.
151-
functionNativeModule(id){
152-
this.filename=`${id}.js`;
153-
this.id=id;
154-
this.exports={};
155-
this.module=undefined;
156-
this.exportKeys=undefined;
157-
this.loaded=false;
158-
this.loading=false;
159-
this.canBeRequiredByUsers=!id.startsWith('internal/');
160-
}
161-
162-
// To be called during pre-execution when --expose-internals is on.
163-
// Enables the user-land module loader to access internal modules.
164-
NativeModule.exposeInternals=function(){
165-
for(const[id,mod]ofNativeModule.map){
166-
// Do not expose this to user land even with --expose-internals.
167-
if(id!==loaderId){
168-
mod.canBeRequiredByUsers=true;
169-
}
170-
}
171-
};
172-
173141
const{
174142
moduleIds,
175143
compileFunction
176144
}=internalBinding('native_module');
177145

178-
NativeModule.map=newMap();
179-
for(leti=0;i<moduleIds.length;++i){
180-
constid=moduleIds[i];
181-
constmod=newNativeModule(id);
182-
NativeModule.map.set(id,mod);
183-
}
146+
constgetOwn=(target,property,receiver)=>{
147+
returnObjectPrototypeHasOwnProperty(target,property) ?
148+
ReflectGet(target,property,receiver) :
149+
undefined;
150+
};
184151

185-
functionnativeModuleRequire(id){
186-
if(id===loaderId){
187-
returnloaderExports;
152+
/**
153+
* An internal abstraction for the built-in JavaScript modules of Node.js.
154+
* Be careful not to expose this to user land unless --expose-internals is
155+
* used, in which case there is no compatibility guarantee about this class.
156+
*/
157+
classNativeModule{
158+
/**
159+
* A map from the module IDs to the module instances.
160+
* @type{Map<string, NativeModule>}
161+
*/
162+
staticmap=newMap(moduleIds.map((id)=>[id,newNativeModule(id)]));
163+
164+
constructor(id){
165+
this.filename=`${id}.js`;
166+
this.id=id;
167+
this.canBeRequiredByUsers=!id.startsWith('internal/');
168+
169+
// The CJS exports object of the module.
170+
this.exports={};
171+
// States used to work around circular dependencies.
172+
this.loaded=false;
173+
this.loading=false;
174+
175+
// The following properties are used by the ESM implementation and only
176+
// initialized when the native module is loaded by users.
177+
/**
178+
* The C++ ModuleWrap binding used to interface with the ESM implementation.
179+
* @type{ModuleWrap|undefined}
180+
*/
181+
this.module=undefined;
182+
/**
183+
* Exported names for the ESM imports.
184+
* @type{string[]|undefined}
185+
*/
186+
this.exportKeys=undefined;
188187
}
189188

190-
constmod=NativeModule.map.get(id);
191-
// Can't load the internal errors module from here, have to use a raw error.
192-
// eslint-disable-next-line no-restricted-syntax
193-
if(!mod)thrownewTypeError(`Missing internal module '${id}'`);
194-
returnmod.compile();
195-
}
189+
// To be called during pre-execution when --expose-internals is on.
190+
// Enables the user-land module loader to access internal modules.
191+
staticexposeInternals(){
192+
for(const[id,mod]ofNativeModule.map){
193+
// Do not expose this to user land even with --expose-internals.
194+
if(id!==loaderId){
195+
mod.canBeRequiredByUsers=true;
196+
}
197+
}
198+
}
196199

197-
NativeModule.exists=function(id){
198-
returnNativeModule.map.has(id);
199-
};
200+
staticexists(id){
201+
returnNativeModule.map.has(id);
202+
}
200203

201-
NativeModule.canBeRequiredByUsers=function(id){
202-
constmod=NativeModule.map.get(id);
203-
returnmod&&mod.canBeRequiredByUsers;
204-
};
204+
staticcanBeRequiredByUsers(id){
205+
constmod=NativeModule.map.get(id);
206+
returnmod&&mod.canBeRequiredByUsers;
207+
}
205208

206-
// Allow internal modules from dependencies to require
207-
// other modules from dependencies by providing fallbacks.
208-
functionrequireWithFallbackInDeps(request){
209-
if(!NativeModule.map.has(request)){
210-
request=`internal/deps/${request}`;
209+
// Used by user-land module loaders to compile and load builtins.
210+
compileForPublicLoader(){
211+
if(!this.canBeRequiredByUsers){
212+
// No code because this is an assertion against bugs
213+
// eslint-disable-next-line no-restricted-syntax
214+
thrownewError(`Should not compile ${this.id} for public use`);
215+
}
216+
this.compileForInternalLoader();
217+
if(!this.exportKeys){
218+
// When using --expose-internals, we do not want to reflect the named
219+
// exports from core modules as this can trigger unnecessary getters.
220+
constinternal=this.id.startsWith('internal/');
221+
this.exportKeys=internal ? [] : ObjectKeys(this.exports);
222+
}
223+
this.getESMFacade();
224+
this.syncExports();
225+
returnthis.exports;
211226
}
212-
returnnativeModuleRequire(request);
213-
}
214227

215-
// This is exposed for public loaders
216-
NativeModule.prototype.compileForPublicLoader=function(){
217-
if(!this.canBeRequiredByUsers){
218-
// No code because this is an assertion against bugs
219-
// eslint-disable-next-line no-restricted-syntax
220-
thrownewError(`Should not compile ${this.id} for public use`);
228+
getESMFacade(){
229+
if(this.module)returnthis.module;
230+
const{ ModuleWrap }=internalBinding('module_wrap');
231+
consturl=`node:${this.id}`;
232+
constnativeModule=this;
233+
this.module=newModuleWrap(
234+
url,undefined,[...this.exportKeys,'default'],
235+
function(){
236+
nativeModule.syncExports();
237+
this.setExport('default',nativeModule.exports);
238+
});
239+
// Ensure immediate sync execution to capture exports now
240+
this.module.instantiate();
241+
this.module.evaluate(-1,false);
242+
returnthis.module;
221243
}
222-
this.compile();
223-
if(!this.exportKeys){
224-
// When using --expose-internals, we do not want to reflect the named
225-
// exports from core modules as this can trigger unnecessary getters.
226-
constinternal=this.id.startsWith('internal/');
227-
this.exportKeys=internal ? [] : ObjectKeys(this.exports);
244+
245+
// Provide named exports for all builtin libraries so that the libraries
246+
// may be imported in a nicer way for ESM users. The default export is left
247+
// as the entire namespace (module.exports) and updates when this function is
248+
// called so that APMs and other behavior are supported.
249+
syncExports(){
250+
constnames=this.exportKeys;
251+
if(this.module){
252+
for(leti=0;i<names.length;i++){
253+
constexportName=names[i];
254+
if(exportName==='default')continue;
255+
this.module.setExport(exportName,
256+
getOwn(this.exports,exportName,this.exports));
257+
}
258+
}
228259
}
229-
this.getESMFacade();
230-
this.syncExports();
231-
returnthis.exports;
232-
};
233260

234-
constgetOwn=(target,property,receiver)=>{
235-
returnObjectPrototypeHasOwnProperty(target,property) ?
236-
ReflectGet(target,property,receiver) :
237-
undefined;
238-
};
261+
compileForInternalLoader(){
262+
if(this.loaded||this.loading){
263+
returnthis.exports;
264+
}
239265

240-
NativeModule.prototype.getURL=function(){
241-
return`node:${this.id}`;
242-
};
266+
constid=this.id;
267+
this.loading=true;
243268

244-
NativeModule.prototype.getESMFacade=function(){
245-
if(this.module)returnthis.module;
246-
const{ ModuleWrap }=internalBinding('module_wrap');
247-
consturl=this.getURL();
248-
constnativeModule=this;
249-
this.module=newModuleWrap(
250-
url,undefined,[...this.exportKeys,'default'],
251-
function(){
252-
nativeModule.syncExports();
253-
this.setExport('default',nativeModule.exports);
254-
});
255-
// Ensure immediate sync execution to capture exports now
256-
this.module.instantiate();
257-
this.module.evaluate(-1,false);
258-
returnthis.module;
259-
};
269+
try{
270+
constrequireFn=this.id.startsWith('internal/deps/') ?
271+
requireWithFallbackInDeps : nativeModuleRequire;
260272

261-
// Provide named exports for all builtin libraries so that the libraries
262-
// may be imported in a nicer way for ESM users. The default export is left
263-
// as the entire namespace (module.exports) and updates when this function is
264-
// called so that APMs and other behavior are supported.
265-
NativeModule.prototype.syncExports=function(){
266-
constnames=this.exportKeys;
267-
if(this.module){
268-
for(leti=0;i<names.length;i++){
269-
constexportName=names[i];
270-
if(exportName==='default')continue;
271-
this.module.setExport(exportName,
272-
getOwn(this.exports,exportName,this.exports));
273+
constfn=compileFunction(id);
274+
fn(this.exports,requireFn,this,process,internalBinding,primordials);
275+
276+
this.loaded=true;
277+
}finally{
278+
this.loading=false;
273279
}
274-
}
275-
};
276280

277-
NativeModule.prototype.compile=function(){
278-
if(this.loaded||this.loading){
281+
moduleLoadList.push(`NativeModule ${id}`);
279282
returnthis.exports;
280283
}
284+
}
281285

282-
constid=this.id;
283-
this.loading=true;
286+
// Think of this as module.exports in this file even though it is not
287+
// written in CommonJS style.
288+
constloaderExports={
289+
internalBinding,
290+
NativeModule,
291+
require: nativeModuleRequire
292+
};
284293

285-
try{
286-
constrequireFn=this.id.startsWith('internal/deps/') ?
287-
requireWithFallbackInDeps : nativeModuleRequire;
294+
functionnativeModuleRequire(id){
295+
if(id===loaderId){
296+
returnloaderExports;
297+
}
288298

289-
constfn=compileFunction(id);
290-
fn(this.exports,requireFn,this,process,internalBinding,primordials);
299+
constmod=NativeModule.map.get(id);
300+
// Can't load the internal errors module from here, have to use a raw error.
301+
// eslint-disable-next-line no-restricted-syntax
302+
if(!mod)thrownewTypeError(`Missing internal module '${id}'`);
303+
returnmod.compileForInternalLoader();
304+
}
291305

292-
this.loaded=true;
293-
}finally{
294-
this.loading=false;
306+
// Allow internal modules from dependencies to require
307+
// other modules from dependencies by providing fallbacks.
308+
functionrequireWithFallbackInDeps(request){
309+
if(!NativeModule.map.has(request)){
310+
request=`internal/deps/${request}`;
295311
}
312+
returnnativeModuleRequire(request);
313+
}
296314

297-
moduleLoadList.push(`NativeModule ${id}`);
298-
returnthis.exports;
299-
};
300-
301-
// This will be passed to internal/bootstrap/node.js.
315+
// Pass the exports back to C++ land for C++ internals to use.
302316
returnloaderExports;

0 commit comments

Comments
(0)