Skip to content

Commit 91e4a7c

Browse files
joyeecheungtargos
authored andcommitted
loader: use default loader as cascaded loader in the in loader worker
Use the default loader as the cascaded loader in the loader worker. Otherwise we spawn loader workers in the loader workers indefinitely. PR-URL: #47620Fixes: #47566 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Michaël Zasso <targos@protonmail.com>
1 parent 9101630 commit 91e4a7c

8 files changed

Lines changed: 106 additions & 10 deletions

File tree

‎lib/internal/main/worker_thread.js‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ port.on('message', (message) => {
133133
if(manifestSrc){
134134
require('internal/process/policy').setup(manifestSrc,manifestURL);
135135
}
136-
setupUserModules();
136+
constisLoaderWorker=
137+
doEval==='internal'&&
138+
filename===require('internal/modules/esm/utils').loaderWorkerId;
139+
setupUserModules(isLoaderWorker);
137140

138141
if(!hasStdin)
139142
process.stdin.push(null);

‎lib/internal/modules/esm/hooks.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const {
5151
}=require('internal/modules/esm/resolve');
5252
const{
5353
getDefaultConditions,
54+
loaderWorkerId,
5455
}=require('internal/modules/esm/utils');
5556
const{ deserializeError }=require('internal/error_serdes');
5657
const{
@@ -493,7 +494,7 @@ class HooksProxy {
493494
constlock=newSharedArrayBuffer(SHARED_MEMORY_BYTE_LENGTH);
494495
this.#lock =newInt32Array(lock);
495496

496-
this.#worker =newInternalWorker('internal/modules/esm/worker',{
497+
this.#worker =newInternalWorker(loaderWorkerId,{
497498
stderr: false,
498499
stdin: false,
499500
stdout: false,

‎lib/internal/modules/esm/loader.js‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,10 @@ let emittedExperimentalWarning = false;
412412
* @returns {DefaultModuleLoader | CustomizedModuleLoader}
413413
*/
414414
functioncreateModuleLoader(useCustomLoadersIfPresent=true){
415-
if(useCustomLoadersIfPresent){
415+
if(useCustomLoadersIfPresent&&
416+
// Don't spawn a new worker if we're already in a worker thread created by instantiating CustomizedModuleLoader;
417+
// doing so would cause an infinite loop.
418+
!require('internal/modules/esm/utils').isLoaderWorker()){
416419
constuserLoaderPaths=getOptionValue('--experimental-loader');
417420
if(userLoaderPaths.length>0){
418421
if(!emittedExperimentalWarning){

‎lib/internal/modules/esm/utils.js‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,22 @@ async function importModuleDynamicallyCallback(wrap, specifier, assertions) {
9191
thrownewERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();
9292
}
9393

94-
functioninitializeESM(){
94+
// This is configured during pre-execution. Specifically it's set to true for
95+
// the loader worker in internal/main/worker_thread.js.
96+
let_isLoaderWorker=false;
97+
functioninitializeESM(isLoaderWorker=false){
98+
_isLoaderWorker=isLoaderWorker;
9599
initializeDefaultConditions();
96100
// Setup per-isolate callbacks that locate data or callbacks that we keep
97101
// track of for different ESM modules.
98102
setInitializeImportMetaObjectCallback(initializeImportMetaObject);
99103
setImportModuleDynamicallyCallback(importModuleDynamicallyCallback);
100104
}
101105

106+
functionisLoaderWorker(){
107+
return_isLoaderWorker;
108+
}
109+
102110
asyncfunctioninitializeHooks(){
103111
constcustomLoaderPaths=getOptionValue('--experimental-loader');
104112

@@ -165,4 +173,6 @@ module.exports = {
165173
initializeHooks,
166174
getDefaultConditions,
167175
getConditionsSet,
176+
loaderWorkerId: 'internal/modules/esm/worker',
177+
isLoaderWorker,
168178
};

‎lib/internal/modules/esm/worker.js‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const { receiveMessageOnPort } = require('internal/worker/io');
2929
const{
3030
WORKER_TO_MAIN_THREAD_NOTIFICATION,
3131
}=require('internal/modules/esm/shared_constants');
32-
const{initializeESM,initializeHooks }=require('internal/modules/esm/utils');
32+
const{ initializeHooks }=require('internal/modules/esm/utils');
3333

3434

3535
functiontransferArrayBuffer(hasError,source){
@@ -80,7 +80,6 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler) {
8080

8181

8282
try{
83-
initializeESM();
8483
constinitResult=awaitinitializeHooks();
8584
hooks=initResult.hooks;
8685
preloadScripts=initResult.preloadScripts;

‎lib/internal/process/pre_execution.js‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ function prepareExecution(options) {
119119
}
120120
}
121121

122-
functionsetupUserModules(){
122+
functionsetupUserModules(isLoaderWorker=false){
123123
initializeCJSLoader();
124-
initializeESMLoader();
124+
initializeESMLoader(isLoaderWorker);
125125
constCJSLoader=require('internal/modules/cjs/loader');
126126
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
127127
loadPreloadModules();
@@ -600,9 +600,9 @@ function initializeCJSLoader() {
600600
initializeCJS();
601601
}
602602

603-
functioninitializeESMLoader(){
603+
functioninitializeESMLoader(isLoaderWorker){
604604
const{ initializeESM }=require('internal/modules/esm/utils');
605-
initializeESM();
605+
initializeESM(isLoaderWorker);
606606

607607
// Patch the vm module when --experimental-vm-modules is on.
608608
// Please update the comments in vm.js when this block changes.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import{spawnPromisified}from'../common/index.mjs';
2+
import*asfixturesfrom'../common/fixtures.mjs';
3+
importassertfrom'node:assert';
4+
import{execPath}from'node:process';
5+
import{describe,it}from'node:test';
6+
7+
describe('Worker threads do not spawn infinitely',{concurrency: true},()=>{
8+
it('should not trigger an infinite loop when using a loader exports no recognized hooks',async()=>{
9+
const{ code, signal, stdout, stderr }=awaitspawnPromisified(execPath,[
10+
'--no-warnings',
11+
'--experimental-loader',
12+
fixtures.fileURL('empty.js'),
13+
'--eval',
14+
'setTimeout(() => console.log("hello"),99)',
15+
]);
16+
17+
assert.strictEqual(stderr,'');
18+
assert.match(stdout,/^hello\r?\n$/);
19+
assert.strictEqual(code,0);
20+
assert.strictEqual(signal,null);
21+
});
22+
23+
it('should support a CommonJS entry point and a loader that imports a CommonJS module',async()=>{
24+
const{ code, signal, stdout, stderr }=awaitspawnPromisified(execPath,[
25+
'--no-warnings',
26+
'--experimental-loader',
27+
fixtures.fileURL('es-module-loaders/loader-with-dep.mjs'),
28+
fixtures.path('print-delayed.js'),
29+
]);
30+
31+
assert.strictEqual(stderr,'');
32+
assert.match(stdout,/^delayed\r?\n$/);
33+
assert.strictEqual(code,0);
34+
assert.strictEqual(signal,null);
35+
});
36+
37+
it('should support --require and --import along with using a loader written in CJS and CJS entry point',async()=>{
38+
const{ code, signal, stdout, stderr }=awaitspawnPromisified(execPath,[
39+
'--no-warnings',
40+
'--eval',
41+
'setTimeout(() => console.log("D"),99)',
42+
'--import',
43+
fixtures.fileURL('printC.js'),
44+
'--experimental-loader',
45+
fixtures.fileURL('printB.js'),
46+
'--require',
47+
fixtures.path('printA.js'),
48+
]);
49+
50+
assert.strictEqual(stderr,'');
51+
// The worker code should always run before the --import, but the console.log might arrive late.
52+
assert.match(stdout,/^A\r?\nA\r?\n(B\r?\nC|C\r?\nB)\r?\nD\r?\n$/);
53+
assert.strictEqual(code,0);
54+
assert.strictEqual(signal,null);
55+
});
56+
57+
it('should support --require and --import along with using a loader written in ESM and ESM entry point',async()=>{
58+
const{ code, signal, stdout, stderr }=awaitspawnPromisified(execPath,[
59+
'--no-warnings',
60+
'--require',
61+
fixtures.path('printA.js'),
62+
'--experimental-loader',
63+
'data:text/javascript,console.log("B")',
64+
'--import',
65+
fixtures.fileURL('printC.js'),
66+
'--input-type=module',
67+
'--eval',
68+
'setTimeout(() => console.log("D"),99)',
69+
]);
70+
71+
assert.strictEqual(stderr,'');
72+
// The worker code should always run before the --import, but the console.log might arrive late.
73+
assert.match(stdout,/^A\r?\nA\r?\n(B\r?\nC|C\r?\nB)\r?\nD\r?\n$/);
74+
assert.strictEqual(code,0);
75+
assert.strictEqual(signal,null);
76+
});
77+
});

‎test/fixtures/print-delayed.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
setTimeout(()=>{
2+
console.log('delayed');
3+
},100);

0 commit comments

Comments
 (0)