Skip to content

Commit d2d73b4

Browse files
joyeecheungmarco-ippolito
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 Backport-PR-URL: #59504 Fixes: #58061 Reviewed-By: Jacob Smith <jacob@frende.me> Refs: #52697
1 parent 1186650 commit d2d73b4

File tree

9 files changed

+58
-12
lines changed

9 files changed

+58
-12
lines changed

β€Žlib/internal/modules/esm/loader.jsβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ class ModuleLoader {
336336
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
337337
}
338338
const status = job.module.getStatus();
339-
debug('Module status', filename, status);
339+
debug('Module status', job, status);
340340
if (status === kEvaluated) {
341341
return { wrap: job.module, namespace: job.module.getNamespaceSync(filename, parentFilename) };
342342
} else if (status === kInstantiated) {

β€Žlib/internal/modules/esm/module_job.jsβ€Ž

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
2121
debug = fn;
2222
});
2323

24-
const { ModuleWrap, kInstantiated } = internalBinding('module_wrap');
25-
24+
const {
25+
ModuleWrap,
26+
kErrored,
27+
kEvaluated,
28+
kInstantiated,
29+
kUninstantiated,
30+
} = internalBinding('module_wrap');
2631
const { decorateErrorStack, kEmptyObject } = require('internal/util');
2732
const {
2833
getSourceMapsEnabled,
@@ -242,17 +247,34 @@ class ModuleJob extends ModuleJobBase {
242247

243248
runSync(parent) {
244249
assert(this.module instanceof ModuleWrap);
245-
if (this.instantiated !== undefined) {
246-
return { __proto__: null, module: this.module };
250+
let status = this.module.getStatus();
251+
252+
debug('ModuleJob.runSync', this.module);
253+
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
254+
// fully synchronous instead.
255+
if (status === kUninstantiated) {
256+
this.module.async = this.module.instantiateSync();
257+
status = this.module.getStatus();
247258
}
259+
if (status === kInstantiated || status === kErrored) {
260+
const filename = urlToFilename(this.url);
261+
const parentFilename = urlToFilename(parent?.filename);
262+
this.module.async ??= this.module.isGraphAsync();
248263

249-
this.module.instantiate();
250-
this.instantiated = PromiseResolve();
251-
setHasStartedUserESMExecution();
252-
const filename = urlToFilename(this.url);
253-
const parentFilename = urlToFilename(parent?.filename);
254-
const namespace = this.module.evaluateSync(filename, parentFilename);
255-
return { __proto__: null, module: this.module, namespace };
264+
if (this.module.async && !getOptionValue('--experimental-print-required-tla')) {
265+
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
266+
}
267+
if (status === kInstantiated) {
268+
setHasStartedUserESMExecution();
269+
const namespace = this.module.evaluateSync(filename, parentFilename);
270+
return { __proto__: null, module: this.module, namespace };
271+
}
272+
throw this.module.getError();
273+
274+
} else if (status === kEvaluated) {
275+
return { __proto__: null, module: this.module, namespace: this.module.getNamespaceSync() };
276+
}
277+
assert.fail(`Unexpected module status ${status}.`);
256278
}
257279

258280
async run() {

β€Žsrc/debug_utils.hβ€Ž

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

β€Žsrc/module_wrap.ccβ€Ž

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

746+
void ModuleWrap::IsGraphAsync(const FunctionCallbackInfo<Value>& args) {
747+
Isolate* isolate = args.GetIsolate();
748+
ModuleWrap* obj;
749+
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
750+
751+
Local<Module> module = obj->module_.Get(isolate);
752+
753+
args.GetReturnValue().Set(module->IsGraphAsync());
754+
}
755+
746756
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
747757
Isolate* isolate = args.GetIsolate();
748758
ModuleWrap* obj;
@@ -1090,6 +1100,7 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
10901100
isolate, tpl, "createCachedData", CreateCachedData);
10911101
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
10921102
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
1103+
SetProtoMethodNoSideEffect(isolate, tpl, "isGraphAsync", IsGraphAsync);
10931104
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
10941105
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
10951106
isolate_data->set_module_wrap_constructor_template(tpl);
@@ -1146,6 +1157,7 @@ void ModuleWrap::RegisterExternalReferences(
11461157
registry->Register(GetNamespace);
11471158
registry->Register(GetStatus);
11481159
registry->Register(GetError);
1160+
registry->Register(IsGraphAsync);
11491161

11501162
registry->Register(CreateRequiredModuleFacade);
11511163

β€Žsrc/module_wrap.hβ€Ž

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

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)