Skip to content

Commit a2a6f44

Browse files
bfarias-godaddyjkrems
authored andcommitted
module: move esm loader hooks to worker thread
1 parent 2551a21 commit a2a6f44

File tree

11 files changed

+537
-90
lines changed

11 files changed

+537
-90
lines changed

lib/internal/bootstrap/pre_execution.js

+32-6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ function prepareMainThreadExecution(expandArgv1 = false) {
3636
}
3737

3838
setupDebugEnv();
39+
// Load policy from disk and parse it.
40+
initializePolicy();
3941

4042
// Process initial diagnostic reporting configuration, if present.
4143
initializeReport();
@@ -50,9 +52,6 @@ function prepareMainThreadExecution(expandArgv1 = false) {
5052
// process.disconnect().
5153
setupChildProcessIpcChannel();
5254

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

6690
const CJSLoader = require('internal/modules/cjs/loader');
6791
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
@@ -336,12 +360,12 @@ function initializeClusterIPC() {
336360
}
337361
}
338362

363+
const { pathToFileURL, URL } = require('url');
339364
function initializePolicy() {
340365
const experimentalPolicy = getOptionValue('--experimental-policy');
341366
if (experimentalPolicy) {
342367
process.emitWarning('Policies are experimental.',
343368
'ExperimentalWarning');
344-
const { pathToFileURL, URL } = require('url');
345369
// URL here as it is slightly different parsing
346370
// no bare specifiers for now
347371
let manifestURL;
@@ -400,14 +424,16 @@ function initializeCJSLoader() {
400424
require('internal/modules/run_main').executeUserEntryPoint;
401425
}
402426

403-
function initializeESMLoader() {
427+
function initializeESMLoader(bottomLoader) {
404428
// Create this WeakMap in js-land because V8 has no C++ API for WeakMap.
405429
internalBinding('module_wrap').callbackMap = new SafeWeakMap();
406430

407431
const {
408432
setImportModuleDynamicallyCallback,
409433
setInitializeImportMetaObjectCallback
410434
} = internalBinding('module_wrap');
435+
const esmLoader = require('internal/modules/esm/loader');
436+
esmLoader.initUserLoaders(bottomLoader);
411437
const esm = require('internal/process/esm_loader');
412438
// Setup per-isolate callbacks that locate data or callbacks that we keep
413439
// track of for different ESM modules.

lib/internal/main/worker_thread.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ port.on('message', (message) => {
9797
cwdCounter,
9898
filename,
9999
doEval,
100+
internal,
100101
workerData,
102+
loaderPort,
101103
publicPort,
102104
manifestSrc,
103105
manifestURL,
@@ -112,14 +114,16 @@ port.on('message', (message) => {
112114
initializeDeprecations();
113115
initializeWASI();
114116
initializeCJSLoader();
115-
initializeESMLoader();
116-
117-
const CJSLoader = require('internal/modules/cjs/loader');
118-
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
119-
loadPreloadModules();
120-
initializeFrozenIntrinsics();
121-
if (argv !== undefined) {
122-
process.argv = process.argv.concat(argv);
117+
initializeESMLoader(loaderPort);
118+
119+
if (!internal) {
120+
const CJSLoader = require('internal/modules/cjs/loader');
121+
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
122+
loadPreloadModules();
123+
initializeFrozenIntrinsics();
124+
if (argv !== undefined) {
125+
process.argv = process.argv.concat(argv);
126+
}
123127
}
124128
publicWorker.parentPort = publicPort;
125129
publicWorker.workerData = workerData;
@@ -147,7 +151,10 @@ port.on('message', (message) => {
147151
`(eval = ${eval}) at cwd = ${process.cwd()}`);
148152
port.unref();
149153
port.postMessage({ type: UP_AND_RUNNING });
150-
if (doEval) {
154+
if (internal) {
155+
debug(`WHTY ${publicPort}`);
156+
require(filename);
157+
} else if (doEval) {
151158
const { evalScript } = require('internal/process/execution');
152159
const name = '[worker eval]';
153160
// This is necessary for CJS module compilation.
@@ -164,6 +171,7 @@ port.on('message', (message) => {
164171
// runMain here might be monkey-patched by users in --require.
165172
// XXX: the monkey-patchability here should probably be deprecated.
166173
process.argv.splice(1, 0, filename);
174+
const CJSLoader = require('internal/modules/cjs/loader');
167175
CJSLoader.Module.runMain(filename);
168176
}
169177
} else if (message.type === STDIO_PAYLOAD) {

lib/internal/modules/esm/ipc_types.js

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* eslint-disable */
2+
let nextId = 1;
3+
function getNewId() {
4+
const id = nextId;
5+
nextId++;
6+
return id;
7+
}
8+
class RPCIncomingBridge {
9+
port;
10+
constructor(port, handler) {
11+
this.port = port;
12+
port.on('message', async (msg) => {
13+
const { id, body } = msg;
14+
try {
15+
const result = await handler(body);
16+
port.postMessage({
17+
id,
18+
result,
19+
hadThrow: false
20+
});
21+
} catch (e) {
22+
port.postMessage({
23+
id,
24+
result: e,
25+
hadThrow: true
26+
});
27+
}
28+
});
29+
}
30+
}
31+
class RPCOutgoingBridge {
32+
pending = new Map();
33+
port;
34+
constructor(port) {
35+
this.port = port;
36+
port.on('message', async (msg) => {
37+
// console.dir({RPCOutgoingBridge_onmessage: msg})
38+
const { id, result, hadThrow } = msg;
39+
if (this.pending.has(id)) {
40+
const handler = this.pending.get(id);
41+
this.pending.delete(id);
42+
if (this.pending.size === 0) {
43+
this.port.unref();
44+
}
45+
46+
if (hadThrow) {
47+
handler.throw(result);
48+
} else {
49+
handler.return(result);
50+
}
51+
}
52+
});
53+
this.port.unref();
54+
}
55+
send(body, transferList) {
56+
const id = getNewId();
57+
// console.dir({RPCOutgoingBridge_send: body})
58+
return new Promise((f, r) => {
59+
this.pending.set(id, {
60+
return: f,
61+
throw: r
62+
});
63+
if (this.pending.size === 1) {
64+
this.port.ref();
65+
}
66+
this.port.postMessage({
67+
id,
68+
body
69+
}, transferList);
70+
});
71+
}
72+
}
73+
class ResolveRequest {
74+
static type = 'resolve.request';
75+
type = ResolveRequest.type;
76+
specifier;
77+
base;
78+
clientId;
79+
conditions;
80+
constructor({ specifier, base, clientId, conditions }) {
81+
this.specifier = specifier;
82+
this.base = base;
83+
this.clientId = clientId;
84+
this.conditions = conditions;
85+
}
86+
static fromOrNull(o) {
87+
if (o.type !== ResolveRequest.type) return null;
88+
return new ResolveRequest(o);
89+
}
90+
}
91+
class ResolveResponse {
92+
static type = 'resolve.response';
93+
type = ResolveResponse.type;
94+
url;
95+
format;
96+
constructor({ url, format }) {
97+
this.url = url;
98+
this.format = format;
99+
}
100+
static fromOrNull(o) {
101+
if (o.type !== ResolveResponse.type) return null;
102+
return new ResolveResponse(o);
103+
}
104+
}
105+
class GetFormatRequest {
106+
static type = 'getFormat.request';
107+
type = GetFormatRequest.type;
108+
url;
109+
clientId;
110+
constructor({ url, clientId }) {
111+
this.url = url;
112+
this.clientId = clientId;
113+
}
114+
static fromOrNull(o) {
115+
if (o.type !== GetFormatRequest.type) return null;
116+
return new GetFormatRequest(o);
117+
}
118+
}
119+
class GetFormatResponse {
120+
static type = 'getFormat.response';
121+
type = GetFormatResponse.type;
122+
format;
123+
constructor({ format }) {
124+
this.format = format;
125+
}
126+
static fromOrNull(o) {
127+
if (o.type !== GetFormatResponse.type) return null;
128+
return new GetFormatResponse(o);
129+
}
130+
}
131+
module.exports = {
132+
GetFormatRequest,
133+
GetFormatResponse,
134+
ResolveRequest,
135+
ResolveResponse,
136+
RPCIncomingBridge,
137+
RPCOutgoingBridge,
138+
getNewId
139+
};

0 commit comments

Comments
 (0)