Skip to content

Commit 4cc4195

Browse files
joyeecheungaduh95
authored andcommitted
module: handle instantiated async module jobs in require(esm)
When require(esm) encounters a cached module job that is instantiated but not yet evaluated, run the evaluation. This catches an edge case previously missed in #57187. PR-URL: #58067 Fixes: #58061 Reviewed-By: Jacob Smith <[email protected]>
1 parent 6582b19 commit 4cc4195

File tree

9 files changed

+59
-11
lines changed

9 files changed

+59
-11
lines changed

lib/internal/modules/esm/loader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ class ModuleLoader {
378378
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
379379
}
380380
const status = job.module.getStatus();
381-
debug('Module status', filename, status);
381+
debug('Module status', job, status);
382382
if (status === kEvaluated) {
383383
return { wrap: job.module, namespace: job.module.getNamespaceSync(filename, parentFilename) };
384384
} else if (status === kInstantiated) {

lib/internal/modules/esm/module_job.js

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
2222
debug = fn;
2323
});
2424

25-
const { ModuleWrap, kEvaluationPhase, kInstantiated } = internalBinding('module_wrap');
25+
const {
26+
ModuleWrap,
27+
kErrored,
28+
kEvaluated,
29+
kEvaluationPhase,
30+
kInstantiated,
31+
kUninstantiated,
32+
} = internalBinding('module_wrap');
2633
const {
2734
privateSymbols: {
2835
entry_point_module_private_symbol,
@@ -280,17 +287,34 @@ class ModuleJob extends ModuleJobBase {
280287
runSync(parent) {
281288
assert(this.phase === kEvaluationPhase);
282289
assert(this.module instanceof ModuleWrap);
283-
if (this.instantiated !== undefined) {
284-
return { __proto__: null, module: this.module };
290+
let status = this.module.getStatus();
291+
292+
debug('ModuleJob.runSync', this.module);
293+
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
294+
// fully synchronous instead.
295+
if (status === kUninstantiated) {
296+
this.module.async = this.module.instantiateSync();
297+
status = this.module.getStatus();
285298
}
299+
if (status === kInstantiated || status === kErrored) {
300+
const filename = urlToFilename(this.url);
301+
const parentFilename = urlToFilename(parent?.filename);
302+
this.module.async ??= this.module.isGraphAsync();
286303

287-
this.module.instantiate();
288-
this.instantiated = PromiseResolve();
289-
setHasStartedUserESMExecution();
290-
const filename = urlToFilename(this.url);
291-
const parentFilename = urlToFilename(parent?.filename);
292-
const namespace = this.module.evaluateSync(filename, parentFilename);
293-
return { __proto__: null, module: this.module, namespace };
304+
if (this.module.async && !getOptionValue('--experimental-print-required-tla')) {
305+
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
306+
}
307+
if (status === kInstantiated) {
308+
setHasStartedUserESMExecution();
309+
const namespace = this.module.evaluateSync(filename, parentFilename);
310+
return { __proto__: null, module: this.module, namespace };
311+
}
312+
throw this.module.getError();
313+
314+
} else if (status === kEvaluated) {
315+
return { __proto__: null, module: this.module, namespace: this.module.getNamespaceSync() };
316+
}
317+
assert.fail(`Unexpected module status ${status}.`);
294318
}
295319

296320
async run(isEntryPoint = false) {

src/debug_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ void NODE_EXTERN_PRIVATE FWrite(FILE* file, const std::string& str);
5252
V(NGTCP2_DEBUG) \
5353
V(SEA) \
5454
V(WASI) \
55+
V(MODULE) \
5556
V(MKSNAPSHOT) \
5657
V(SNAPSHOT_SERDES) \
5758
V(PERMISSION_MODEL) \

src/module_wrap.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,16 @@ void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
815815
args.GetReturnValue().Set(module->GetStatus());
816816
}
817817

818+
void ModuleWrap::IsGraphAsync(const FunctionCallbackInfo<Value>& args) {
819+
Isolate* isolate = args.GetIsolate();
820+
ModuleWrap* obj;
821+
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
822+
823+
Local<Module> module = obj->module_.Get(isolate);
824+
825+
args.GetReturnValue().Set(module->IsGraphAsync());
826+
}
827+
818828
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
819829
Isolate* isolate = args.GetIsolate();
820830
ModuleWrap* obj;
@@ -1171,6 +1181,7 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
11711181
isolate, tpl, "createCachedData", CreateCachedData);
11721182
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
11731183
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
1184+
SetProtoMethodNoSideEffect(isolate, tpl, "isGraphAsync", IsGraphAsync);
11741185
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
11751186
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
11761187
isolate_data->set_module_wrap_constructor_template(tpl);
@@ -1227,6 +1238,7 @@ void ModuleWrap::RegisterExternalReferences(
12271238
registry->Register(GetNamespace);
12281239
registry->Register(GetStatus);
12291240
registry->Register(GetError);
1241+
registry->Register(IsGraphAsync);
12301242

12311243
registry->Register(CreateRequiredModuleFacade);
12321244

src/module_wrap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class ModuleWrap : public BaseObject {
111111
static void Instantiate(const v8::FunctionCallbackInfo<v8::Value>& args);
112112
static void Evaluate(const v8::FunctionCallbackInfo<v8::Value>& args);
113113
static void GetNamespace(const v8::FunctionCallbackInfo<v8::Value>& args);
114+
static void IsGraphAsync(const v8::FunctionCallbackInfo<v8::Value>& args);
114115
static void GetStatus(const v8::FunctionCallbackInfo<v8::Value>& args);
115116
static void GetError(const v8::FunctionCallbackInfo<v8::Value>& args);
116117

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import '../common/index.mjs';
2+
import assert from 'node:assert';
3+
import { b, c } from '../fixtures/es-modules/require-module-instantiated/a.mjs';
4+
assert.strictEqual(b, c);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as b } from './b.cjs';
2+
export { default as c } from './c.mjs';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./c.mjs');
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const foo = 1;
2+
export default foo;
3+
export { foo as 'module.exports' };

0 commit comments

Comments
 (0)