Skip to content

Commit c4f2cf9

Browse files
bfarias-godaddyhybrist
authored andcommitted
module: move esm loader hooks to worker thread
1 parent d12d5ef commit c4f2cf9

21 files changed

+574
-156
lines changed

lib/internal/bootstrap/pre_execution.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ function prepareMainThreadExecution(expandArgv1 = false) {
2828

2929

3030
setupDebugEnv();
31+
// Load policy from disk and parse it.
32+
initializePolicy();
3133

3234
// Print stack trace on `SIGINT` if option `--trace-sigint` presents.
3335
setupStacktracePrinterOnSigint();
@@ -45,9 +47,6 @@ function prepareMainThreadExecution(expandArgv1 = false) {
4547
// process.disconnect().
4648
setupChildProcessIpcChannel();
4749

48-
// Load policy from disk and parse it.
49-
initializePolicy();
50-
5150
// If this is a worker in cluster mode, start up the communication
5251
// channel. This needs to be done before any user code gets executed
5352
// (including preload modules).
@@ -56,7 +55,32 @@ function prepareMainThreadExecution(expandArgv1 = false) {
5655
initializeDeprecations();
5756
initializeWASI();
5857
initializeCJSLoader();
59-
initializeESMLoader();
58+
59+
function startLoaders() {
60+
const loaderHREF = getOptionValue('--experimental-loader');
61+
if (!loaderHREF) return null;
62+
const { InternalWorker } = require('internal/worker');
63+
const { MessageChannel } = require('internal/worker/io');
64+
// DO NOT ADD CODE ABOVE THIS LINE
65+
// THIS SHOULD POST THE PORTS ASAP TO THE PARENT
66+
const {
67+
port1: outsideBelowPort,
68+
port2: insideBelowPort
69+
} = new MessageChannel();
70+
outsideBelowPort.name = 'outsideBelowPort';
71+
InternalWorker('internal/modules/esm/worker', {
72+
// stdout: true,
73+
// strerr: true,
74+
transferList: [ insideBelowPort ],
75+
workerData: {
76+
loaderHREF,
77+
insideBelowPort,
78+
insideAbovePort: null
79+
}
80+
}).unref();
81+
return outsideBelowPort;
82+
}
83+
initializeESMLoader(startLoaders());
6084

6185
const CJSLoader = require('internal/modules/cjs/loader');
6286
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
@@ -332,12 +356,12 @@ function initializeClusterIPC() {
332356
}
333357
}
334358

359+
const { pathToFileURL, URL } = require('url');
335360
function initializePolicy() {
336361
const experimentalPolicy = getOptionValue('--experimental-policy');
337362
if (experimentalPolicy) {
338363
process.emitWarning('Policies are experimental.',
339364
'ExperimentalWarning');
340-
const { pathToFileURL, URL } = require('url');
341365
// URL here as it is slightly different parsing
342366
// no bare specifiers for now
343367
let manifestURL;
@@ -396,14 +420,16 @@ function initializeCJSLoader() {
396420
require('internal/modules/run_main').executeUserEntryPoint;
397421
}
398422

399-
function initializeESMLoader() {
423+
function initializeESMLoader(bottomLoader) {
400424
// Create this WeakMap in js-land because V8 has no C++ API for WeakMap.
401425
internalBinding('module_wrap').callbackMap = new SafeWeakMap();
402426

403427
const {
404428
setImportModuleDynamicallyCallback,
405429
setInitializeImportMetaObjectCallback
406430
} = internalBinding('module_wrap');
431+
const esmLoader = require('internal/modules/esm/loader');
432+
esmLoader.initUserLoaders(bottomLoader);
407433
const esm = require('internal/process/esm_loader');
408434
// Setup per-isolate callbacks that locate data or callbacks that we keep
409435
// track of for different ESM modules.

lib/internal/main/worker_thread.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ port.on('message', (message) => {
9898
cwdCounter,
9999
filename,
100100
doEval,
101+
internal,
101102
workerData,
103+
loaderPort,
102104
publicPort,
103105
manifestSrc,
104106
manifestURL,
@@ -113,14 +115,16 @@ port.on('message', (message) => {
113115
initializeDeprecations();
114116
initializeWASI();
115117
initializeCJSLoader();
116-
initializeESMLoader();
117-
118-
const CJSLoader = require('internal/modules/cjs/loader');
119-
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
120-
loadPreloadModules();
121-
initializeFrozenIntrinsics();
122-
if (argv !== undefined) {
123-
process.argv = process.argv.concat(argv);
118+
initializeESMLoader(loaderPort);
119+
120+
if (!internal) {
121+
const CJSLoader = require('internal/modules/cjs/loader');
122+
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
123+
loadPreloadModules();
124+
initializeFrozenIntrinsics();
125+
if (argv !== undefined) {
126+
process.argv = process.argv.concat(argv);
127+
}
124128
}
125129
publicWorker.parentPort = publicPort;
126130
publicWorker.workerData = workerData;
@@ -147,7 +151,9 @@ port.on('message', (message) => {
147151
debug(`[${threadId}] starts worker script ${filename} ` +
148152
`(eval = ${eval}) at cwd = ${process.cwd()}`);
149153
port.postMessage({ type: UP_AND_RUNNING });
150-
if (doEval) {
154+
if (internal) {
155+
require(filename);
156+
} else if (doEval) {
151157
const { evalScript } = require('internal/process/execution');
152158
const name = '[worker eval]';
153159
// This is necessary for CJS module compilation.
@@ -164,6 +170,7 @@ port.on('message', (message) => {
164170
// runMain here might be monkey-patched by users in --require.
165171
// XXX: the monkey-patchability here should probably be deprecated.
166172
process.argv.splice(1, 0, filename);
173+
const CJSLoader = require('internal/modules/cjs/loader');
167174
CJSLoader.Module.runMain(filename);
168175
}
169176
} else if (message.type === STDIO_PAYLOAD) {
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/* eslint-disable */
2+
let nextId = 1;
3+
function getNewId() {
4+
const id = nextId;
5+
nextId++;
6+
return id;
7+
}
8+
9+
class LoaderWorker {
10+
resolveImportURL() {
11+
throw new TypeError('Not implemented');
12+
}
13+
14+
getFormat() {
15+
throw new TypeError('Not implemented');
16+
}
17+
18+
getSource() {
19+
throw new TypeError('Not implemented');
20+
}
21+
22+
transformSource() {
23+
throw new TypeError('Not implemented');
24+
}
25+
26+
addBelowPort() {
27+
throw new TypeError('Not implemented');
28+
}
29+
}
30+
31+
class RemoteLoaderWorker {
32+
constructor(port) {
33+
this._port = port;
34+
this._pending = new Map();
35+
36+
this._port.on('message', this._handleMessage.bind(this));
37+
this._port.unref();
38+
}
39+
40+
_handleMessage({ id, error, result }) {
41+
if (!this._pending.has(id)) return;
42+
const { resolve, reject } = this._pending.get(id);
43+
this._pending.delete(id);
44+
this._port.unref();
45+
46+
if (error) {
47+
reject(error);
48+
} else {
49+
resolve(result);
50+
}
51+
}
52+
53+
_send(method, params, transferList) {
54+
const id = getNewId();
55+
const message = { id, method, params };
56+
return new Promise((resolve, reject) => {
57+
this._pending.set(id, { resolve, reject });
58+
this._port.postMessage(message, transferList);
59+
this._port.ref();
60+
});
61+
}
62+
}
63+
64+
for (const method of Object.getOwnPropertyNames(LoaderWorker.prototype)) {
65+
Object.defineProperty(RemoteLoaderWorker.prototype, method, {
66+
configurable: true,
67+
enumerable: false,
68+
value(data, transferList) {
69+
return this._send(method, data, transferList);
70+
},
71+
});
72+
}
73+
74+
function connectIncoming(port, instance) {
75+
port.on('message', async ({ id, method, params }) => {
76+
if (!id) return;
77+
78+
let result = null;
79+
let error = null;
80+
try {
81+
if (typeof instance[method] !== 'function') {
82+
throw new TypeError(`No such RPC method: ${method}`);
83+
}
84+
result = await instance[method](params);
85+
} catch (e) {
86+
error = e;
87+
}
88+
port.postMessage({ id, error, result });
89+
});
90+
91+
return instance;
92+
}
93+
94+
module.exports = {
95+
connectIncoming,
96+
getNewId,
97+
LoaderWorker,
98+
RemoteLoaderWorker,
99+
};

0 commit comments

Comments
 (0)