vm: hint module identifier in instantiate errors

PR-URL: https://github.com/nodejs/node/pull/60199
Fixes: https://github.com/nodejs/node/issues/60157
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
Chengzhong Wu 2025-10-12 22:28:56 +08:00 committed by GitHub
parent d54e6aec9e
commit 1986ee4b65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 28 additions and 17 deletions

View File

@ -144,12 +144,12 @@ ModuleWrap::ModuleWrap(Realm* realm,
Local<Object> context_object, Local<Object> context_object,
Local<Value> synthetic_evaluation_step) Local<Value> synthetic_evaluation_step)
: BaseObject(realm, object), : BaseObject(realm, object),
url_(Utf8Value(realm->isolate(), url).ToString()),
module_(realm->isolate(), module), module_(realm->isolate(), module),
module_hash_(module->GetIdentityHash()) { module_hash_(module->GetIdentityHash()) {
realm->env()->hash_to_module_map.emplace(module_hash_, this); realm->env()->hash_to_module_map.emplace(module_hash_, this);
object->SetInternalField(kModuleSlot, module); object->SetInternalField(kModuleSlot, module);
object->SetInternalField(kURLSlot, url);
object->SetInternalField(kModuleSourceObjectSlot, object->SetInternalField(kModuleSourceObjectSlot,
v8::Undefined(realm->isolate())); v8::Undefined(realm->isolate()));
object->SetInternalField(kSyntheticEvaluationStepsSlot, object->SetInternalField(kSyntheticEvaluationStepsSlot,
@ -968,8 +968,7 @@ void ModuleWrap::GetModuleSourceObject(
obj->object()->GetInternalField(kModuleSourceObjectSlot).As<Value>(); obj->object()->GetInternalField(kModuleSourceObjectSlot).As<Value>();
if (module_source_object->IsUndefined()) { if (module_source_object->IsUndefined()) {
Local<String> url = obj->object()->GetInternalField(kURLSlot).As<String>(); THROW_ERR_SOURCE_PHASE_NOT_DEFINED(isolate, obj->url_);
THROW_ERR_SOURCE_PHASE_NOT_DEFINED(isolate, url);
return; return;
} }
@ -1043,10 +1042,8 @@ MaybeLocal<Object> ModuleWrap::ResolveSourceCallback(
->GetInternalField(ModuleWrap::kModuleSourceObjectSlot) ->GetInternalField(ModuleWrap::kModuleSourceObjectSlot)
.As<Value>(); .As<Value>();
if (module_source_object->IsUndefined()) { if (module_source_object->IsUndefined()) {
Local<String> url = resolved_module->object() THROW_ERR_SOURCE_PHASE_NOT_DEFINED(Isolate::GetCurrent(),
->GetInternalField(ModuleWrap::kURLSlot) resolved_module->url_);
.As<String>();
THROW_ERR_SOURCE_PHASE_NOT_DEFINED(Isolate::GetCurrent(), url);
return {}; return {};
} }
CHECK(module_source_object->IsObject()); CHECK(module_source_object->IsObject());
@ -1078,17 +1075,21 @@ Maybe<ModuleWrap*> ModuleWrap::ResolveModule(
return Nothing<ModuleWrap*>(); return Nothing<ModuleWrap*>();
} }
if (!dependent->IsLinked()) { if (!dependent->IsLinked()) {
THROW_ERR_VM_MODULE_LINK_FAILURE( THROW_ERR_VM_MODULE_LINK_FAILURE(env,
env, "request for '%s' can not be resolved on "
"request for '%s' is from a module not been linked", "module '%s' that is not linked",
cache_key.specifier); cache_key.specifier,
dependent->url_);
return Nothing<ModuleWrap*>(); return Nothing<ModuleWrap*>();
} }
auto it = dependent->resolve_cache_.find(cache_key); auto it = dependent->resolve_cache_.find(cache_key);
if (it == dependent->resolve_cache_.end()) { if (it == dependent->resolve_cache_.end()) {
THROW_ERR_VM_MODULE_LINK_FAILURE( THROW_ERR_VM_MODULE_LINK_FAILURE(
env, "request for '%s' is not in cache", cache_key.specifier); env,
"request for '%s' is not cached on module '%s'",
cache_key.specifier,
dependent->url_);
return Nothing<ModuleWrap*>(); return Nothing<ModuleWrap*>();
} }

View File

@ -99,7 +99,6 @@ class ModuleWrap : public BaseObject {
public: public:
enum InternalFields { enum InternalFields {
kModuleSlot = BaseObject::kInternalFieldCount, kModuleSlot = BaseObject::kInternalFieldCount,
kURLSlot,
kModuleSourceObjectSlot, kModuleSourceObjectSlot,
kSyntheticEvaluationStepsSlot, kSyntheticEvaluationStepsSlot,
kContextObjectSlot, // Object whose creation context is the target Context kContextObjectSlot, // Object whose creation context is the target Context
@ -215,6 +214,7 @@ class ModuleWrap : public BaseObject {
v8::Local<v8::FixedArray> import_attributes, v8::Local<v8::FixedArray> import_attributes,
v8::Local<v8::Module> referrer); v8::Local<v8::Module> referrer);
std::string url_;
v8::Global<v8::Module> module_; v8::Global<v8::Module> module_;
ResolveCache resolve_cache_; ResolveCache resolve_cache_;
contextify::ContextifyContext* contextify_context_ = nullptr; contextify::ContextifyContext* contextify_context_ = nullptr;

View File

@ -295,12 +295,11 @@ inline v8::Local<v8::Object> ERR_BUFFER_TOO_LARGE(v8::Isolate* isolate) {
} }
inline void THROW_ERR_SOURCE_PHASE_NOT_DEFINED(v8::Isolate* isolate, inline void THROW_ERR_SOURCE_PHASE_NOT_DEFINED(v8::Isolate* isolate,
v8::Local<v8::String> url) { const std::string& url) {
std::string message = std::string(*v8::String::Utf8Value(isolate, url));
return THROW_ERR_SOURCE_PHASE_NOT_DEFINED( return THROW_ERR_SOURCE_PHASE_NOT_DEFINED(
isolate, isolate,
"Source phase import object is not defined for module %s", "Source phase import object is not defined for module '%s'",
message.c_str()); url);
} }
inline v8::Local<v8::Object> ERR_STRING_TOO_LONG(v8::Isolate* isolate) { inline v8::Local<v8::Object> ERR_STRING_TOO_LONG(v8::Isolate* isolate) {

View File

@ -65,3 +65,14 @@ test('mismatch linkage', () => {
code: 'ERR_MODULE_LINK_MISMATCH', code: 'ERR_MODULE_LINK_MISMATCH',
}); });
}); });
test('instantiate error should hint about module identifier', () => {
const foo = new SourceTextModule('import bar from "bar"', { identifier: 'file://foo' });
const bar = new SourceTextModule('import "unknown"', { identifier: 'file://bar' });
foo.linkRequests([bar]);
assert.throws(() => foo.instantiate(), {
message: `request for 'unknown' can not be resolved on module 'file://bar' that is not linked`,
code: 'ERR_VM_MODULE_LINK_FAILURE',
});
});