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 https://github.com/nodejs/node/pull/57187. PR-URL: https://github.com/nodejs/node/pull/58067 Fixes: https://github.com/nodejs/node/issues/58061 Reviewed-By: Jacob Smith <jacob@frende.me>
This commit is contained in:
parent
6582b19488
commit
4cc4195493
@ -378,7 +378,7 @@ class ModuleLoader {
|
|||||||
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
|
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
|
||||||
}
|
}
|
||||||
const status = job.module.getStatus();
|
const status = job.module.getStatus();
|
||||||
debug('Module status', filename, status);
|
debug('Module status', job, status);
|
||||||
if (status === kEvaluated) {
|
if (status === kEvaluated) {
|
||||||
return { wrap: job.module, namespace: job.module.getNamespaceSync(filename, parentFilename) };
|
return { wrap: job.module, namespace: job.module.getNamespaceSync(filename, parentFilename) };
|
||||||
} else if (status === kInstantiated) {
|
} else if (status === kInstantiated) {
|
||||||
|
@ -22,7 +22,14 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
|
|||||||
debug = fn;
|
debug = fn;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { ModuleWrap, kEvaluationPhase, kInstantiated } = internalBinding('module_wrap');
|
const {
|
||||||
|
ModuleWrap,
|
||||||
|
kErrored,
|
||||||
|
kEvaluated,
|
||||||
|
kEvaluationPhase,
|
||||||
|
kInstantiated,
|
||||||
|
kUninstantiated,
|
||||||
|
} = internalBinding('module_wrap');
|
||||||
const {
|
const {
|
||||||
privateSymbols: {
|
privateSymbols: {
|
||||||
entry_point_module_private_symbol,
|
entry_point_module_private_symbol,
|
||||||
@ -280,18 +287,35 @@ class ModuleJob extends ModuleJobBase {
|
|||||||
runSync(parent) {
|
runSync(parent) {
|
||||||
assert(this.phase === kEvaluationPhase);
|
assert(this.phase === kEvaluationPhase);
|
||||||
assert(this.module instanceof ModuleWrap);
|
assert(this.module instanceof ModuleWrap);
|
||||||
if (this.instantiated !== undefined) {
|
let status = this.module.getStatus();
|
||||||
return { __proto__: null, module: this.module };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.module.instantiate();
|
debug('ModuleJob.runSync', this.module);
|
||||||
this.instantiated = PromiseResolve();
|
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
|
||||||
setHasStartedUserESMExecution();
|
// fully synchronous instead.
|
||||||
|
if (status === kUninstantiated) {
|
||||||
|
this.module.async = this.module.instantiateSync();
|
||||||
|
status = this.module.getStatus();
|
||||||
|
}
|
||||||
|
if (status === kInstantiated || status === kErrored) {
|
||||||
const filename = urlToFilename(this.url);
|
const filename = urlToFilename(this.url);
|
||||||
const parentFilename = urlToFilename(parent?.filename);
|
const parentFilename = urlToFilename(parent?.filename);
|
||||||
|
this.module.async ??= this.module.isGraphAsync();
|
||||||
|
|
||||||
|
if (this.module.async && !getOptionValue('--experimental-print-required-tla')) {
|
||||||
|
throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename);
|
||||||
|
}
|
||||||
|
if (status === kInstantiated) {
|
||||||
|
setHasStartedUserESMExecution();
|
||||||
const namespace = this.module.evaluateSync(filename, parentFilename);
|
const namespace = this.module.evaluateSync(filename, parentFilename);
|
||||||
return { __proto__: null, module: this.module, namespace };
|
return { __proto__: null, module: this.module, namespace };
|
||||||
}
|
}
|
||||||
|
throw this.module.getError();
|
||||||
|
|
||||||
|
} else if (status === kEvaluated) {
|
||||||
|
return { __proto__: null, module: this.module, namespace: this.module.getNamespaceSync() };
|
||||||
|
}
|
||||||
|
assert.fail(`Unexpected module status ${status}.`);
|
||||||
|
}
|
||||||
|
|
||||||
async run(isEntryPoint = false) {
|
async run(isEntryPoint = false) {
|
||||||
await this.instantiate();
|
await this.instantiate();
|
||||||
|
@ -52,6 +52,7 @@ void NODE_EXTERN_PRIVATE FWrite(FILE* file, const std::string& str);
|
|||||||
V(NGTCP2_DEBUG) \
|
V(NGTCP2_DEBUG) \
|
||||||
V(SEA) \
|
V(SEA) \
|
||||||
V(WASI) \
|
V(WASI) \
|
||||||
|
V(MODULE) \
|
||||||
V(MKSNAPSHOT) \
|
V(MKSNAPSHOT) \
|
||||||
V(SNAPSHOT_SERDES) \
|
V(SNAPSHOT_SERDES) \
|
||||||
V(PERMISSION_MODEL) \
|
V(PERMISSION_MODEL) \
|
||||||
|
@ -815,6 +815,16 @@ void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
|
|||||||
args.GetReturnValue().Set(module->GetStatus());
|
args.GetReturnValue().Set(module->GetStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModuleWrap::IsGraphAsync(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
ModuleWrap* obj;
|
||||||
|
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
||||||
|
|
||||||
|
Local<Module> module = obj->module_.Get(isolate);
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(module->IsGraphAsync());
|
||||||
|
}
|
||||||
|
|
||||||
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
|
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
|
||||||
Isolate* isolate = args.GetIsolate();
|
Isolate* isolate = args.GetIsolate();
|
||||||
ModuleWrap* obj;
|
ModuleWrap* obj;
|
||||||
@ -1171,6 +1181,7 @@ void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
|
|||||||
isolate, tpl, "createCachedData", CreateCachedData);
|
isolate, tpl, "createCachedData", CreateCachedData);
|
||||||
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
|
SetProtoMethodNoSideEffect(isolate, tpl, "getNamespace", GetNamespace);
|
||||||
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
|
SetProtoMethodNoSideEffect(isolate, tpl, "getStatus", GetStatus);
|
||||||
|
SetProtoMethodNoSideEffect(isolate, tpl, "isGraphAsync", IsGraphAsync);
|
||||||
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
|
SetProtoMethodNoSideEffect(isolate, tpl, "getError", GetError);
|
||||||
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
|
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
|
||||||
isolate_data->set_module_wrap_constructor_template(tpl);
|
isolate_data->set_module_wrap_constructor_template(tpl);
|
||||||
@ -1227,6 +1238,7 @@ void ModuleWrap::RegisterExternalReferences(
|
|||||||
registry->Register(GetNamespace);
|
registry->Register(GetNamespace);
|
||||||
registry->Register(GetStatus);
|
registry->Register(GetStatus);
|
||||||
registry->Register(GetError);
|
registry->Register(GetError);
|
||||||
|
registry->Register(IsGraphAsync);
|
||||||
|
|
||||||
registry->Register(CreateRequiredModuleFacade);
|
registry->Register(CreateRequiredModuleFacade);
|
||||||
|
|
||||||
|
@ -111,6 +111,7 @@ class ModuleWrap : public BaseObject {
|
|||||||
static void Instantiate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void Instantiate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void Evaluate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void Evaluate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetNamespace(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetNamespace(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void IsGraphAsync(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetStatus(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetStatus(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetError(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void GetError(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
|
||||||
|
4
test/es-module/test-require-module-instantiated.mjs
Normal file
4
test/es-module/test-require-module-instantiated.mjs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import '../common/index.mjs';
|
||||||
|
import assert from 'node:assert';
|
||||||
|
import { b, c } from '../fixtures/es-modules/require-module-instantiated/a.mjs';
|
||||||
|
assert.strictEqual(b, c);
|
2
test/fixtures/es-modules/require-module-instantiated/a.mjs
vendored
Normal file
2
test/fixtures/es-modules/require-module-instantiated/a.mjs
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default as b } from './b.cjs';
|
||||||
|
export { default as c } from './c.mjs';
|
1
test/fixtures/es-modules/require-module-instantiated/b.cjs
vendored
Normal file
1
test/fixtures/es-modules/require-module-instantiated/b.cjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./c.mjs');
|
3
test/fixtures/es-modules/require-module-instantiated/c.mjs
vendored
Normal file
3
test/fixtures/es-modules/require-module-instantiated/c.mjs
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const foo = 1;
|
||||||
|
export default foo;
|
||||||
|
export { foo as 'module.exports' };
|
Loading…
x
Reference in New Issue
Block a user