Skip to content

Commit 02eb24d

Browse files
committed
esm: use index-based resolution callbacks
This makes use of a new module resolution V8 API that passes in an index to the module request array to identify the module request, which simplifies the module linking process.
1 parent 1241e04 commit 02eb24d

File tree

3 files changed

+80
-70
lines changed

3 files changed

+80
-70
lines changed

src/module_wrap.cc

Lines changed: 74 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,6 @@ ModuleWrap::ModuleWrap(Realm* realm,
165165
}
166166
MakeWeak();
167167
module_.SetWeak();
168-
169-
HandleScope scope(realm->isolate());
170-
Local<Context> context = realm->context();
171-
Local<FixedArray> requests = module->GetModuleRequests();
172-
for (int i = 0; i < requests->Length(); i++) {
173-
ModuleCacheKey module_cache_key = ModuleCacheKey::From(
174-
context, requests->Get(context, i).As<ModuleRequest>());
175-
resolve_cache_[module_cache_key] = i;
176-
}
177168
}
178169

179170
ModuleWrap::~ModuleWrap() {
@@ -664,29 +655,55 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
664655
Local<FixedArray> requests =
665656
dependent->module_.Get(isolate)->GetModuleRequests();
666657
Local<Array> modules = args[0].As<Array>();
667-
CHECK_EQ(modules->Length(), static_cast<uint32_t>(requests->Length()));
668-
669-
for (int i = 0; i < requests->Length(); i++) {
658+
int request_count = requests->Length();
659+
CHECK_EQ(modules->Length(), static_cast<uint32_t>(request_count));
660+
661+
// Track the duplicated module requests. For example if a modulelooks like
662+
// this:
663+
//
664+
// import { foo } from 'mod' with { type: 'json' };
665+
// import { bar } from 'mod' with { type: 'json' };
666+
// import { baz } from 'mod2';
667+
//
668+
// The first two module requests are identical. The map would look like
669+
// { mod_key: 0, mod2_key: 2 } in this case, so that module request 0 and
670+
// module request 1 would be mapped to mod_key and both should resolve to the
671+
// module identified by module request 0 (the first one with this identity),
672+
// and module request 2 should resolve the module identified by index 2.
673+
std::unordered_map<ModuleCacheKey, int, ModuleCacheKey::Hash>
674+
module_request_map;
675+
Local<Value> current_module;
676+
Local<Value> first_seen_module;
677+
for (int i = 0; i < request_count; i++) {
678+
// TODO(joyeecheung): merge this with the serializeKey() in module_map.js.
679+
// This currently doesn't sort the import attributes.
670680
ModuleCacheKey module_cache_key = ModuleCacheKey::From(
671681
context, requests->Get(context, i).As<ModuleRequest>());
672-
DCHECK(dependent->resolve_cache_.contains(module_cache_key));
673-
674-
Local<Value> module_i;
675-
Local<Value> module_cache_i;
676-
uint32_t coalesced_index = dependent->resolve_cache_[module_cache_key];
677-
if (!modules->Get(context, i).ToLocal(&module_i) ||
678-
!modules->Get(context, coalesced_index).ToLocal(&module_cache_i) ||
679-
!module_i->StrictEquals(module_cache_i)) {
680-
// If the module is different from the one of the same request, throw an
681-
// error.
682-
THROW_ERR_MODULE_LINK_MISMATCH(
683-
realm->env(),
684-
"Module request '%s' at index %d must be linked "
685-
"to the same module requested at index %d",
686-
module_cache_key.ToString(),
687-
i,
688-
coalesced_index);
689-
return;
682+
auto it = module_request_map.find(module_cache_key);
683+
if (it == module_request_map.end()) {
684+
// This is the first request with this identity, record it - any mismatch
685+
// for this would only be found in subsequent requests, so no need to
686+
// check here.
687+
module_request_map[module_cache_key] = i;
688+
} else { // This identity has been seen before, check for mismatch.
689+
int first_seen_index = it->second;
690+
// Check that the module is the same as the one resolved by the first
691+
// request with this identity.
692+
if (!modules->Get(context, i).ToLocal(&current_module) ||
693+
!modules->Get(context, first_seen_index)
694+
.ToLocal(&first_seen_module) ||
695+
!current_module->StrictEquals(first_seen_module)) {
696+
// If the module is different from the one of the same request, throw an
697+
// error.
698+
THROW_ERR_MODULE_LINK_MISMATCH(
699+
realm->env(),
700+
"Module request '%s' at index %d must be linked "
701+
"to the same module requested at index %d",
702+
module_cache_key.ToString(),
703+
i,
704+
first_seen_index);
705+
return;
706+
}
690707
}
691708
}
692709

@@ -1013,11 +1030,10 @@ void ModuleWrap::HasAsyncGraph(Local<Name> property,
10131030
// static
10141031
MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
10151032
Local<Context> context,
1016-
Local<String> specifier,
1017-
Local<FixedArray> import_attributes,
1033+
size_t module_request_index,
10181034
Local<Module> referrer) {
10191035
ModuleWrap* resolved_module;
1020-
if (!ResolveModule(context, specifier, import_attributes, referrer)
1036+
if (!ResolveModule(context, module_request_index, referrer)
10211037
.To(&resolved_module)) {
10221038
return {};
10231039
}
@@ -1028,11 +1044,10 @@ MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
10281044
// static
10291045
MaybeLocal<Object> ModuleWrap::ResolveSourceCallback(
10301046
Local<Context> context,
1031-
Local<String> specifier,
1032-
Local<FixedArray> import_attributes,
1047+
size_t module_request_index,
10331048
Local<Module> referrer) {
10341049
ModuleWrap* resolved_module;
1035-
if (!ResolveModule(context, specifier, import_attributes, referrer)
1050+
if (!ResolveModule(context, module_request_index, referrer)
10361051
.To(&resolved_module)) {
10371052
return {};
10381053
}
@@ -1053,12 +1068,22 @@ MaybeLocal<Object> ModuleWrap::ResolveSourceCallback(
10531068
return module_source_object.As<Object>();
10541069
}
10551070

1071+
static std::string GetSpecifierFromModuleRequest(Local<Context> context,
1072+
Local<Module> referrer,
1073+
size_t module_request_index) {
1074+
Local<ModuleRequest> raw_request =
1075+
referrer->GetModuleRequests()
1076+
->Get(context, static_cast<int>(module_request_index))
1077+
.As<ModuleRequest>();
1078+
Local<String> specifier = raw_request->GetSpecifier();
1079+
Utf8Value specifier_utf8(Isolate::GetCurrent(), specifier);
1080+
return specifier_utf8.ToString();
1081+
}
1082+
10561083
// static
1057-
Maybe<ModuleWrap*> ModuleWrap::ResolveModule(
1058-
Local<Context> context,
1059-
Local<String> specifier,
1060-
Local<FixedArray> import_attributes,
1061-
Local<Module> referrer) {
1084+
Maybe<ModuleWrap*> ModuleWrap::ResolveModule(Local<Context> context,
1085+
size_t module_request_index,
1086+
Local<Module> referrer) {
10621087
Isolate* isolate = Isolate::GetCurrent();
10631088
Environment* env = Environment::GetCurrent(context);
10641089
if (env == nullptr) {
@@ -1068,31 +1093,24 @@ Maybe<ModuleWrap*> ModuleWrap::ResolveModule(
10681093
// Check that the referrer is not yet been instantiated.
10691094
DCHECK(referrer->GetStatus() <= Module::kInstantiated);
10701095

1071-
ModuleCacheKey cache_key =
1072-
ModuleCacheKey::From(context, specifier, import_attributes);
1073-
10741096
ModuleWrap* dependent = ModuleWrap::GetFromModule(env, referrer);
10751097
if (dependent == nullptr) {
1098+
std::string specifier =
1099+
GetSpecifierFromModuleRequest(context, referrer, module_request_index);
10761100
THROW_ERR_VM_MODULE_LINK_FAILURE(
1077-
env, "request for '%s' is from invalid module", cache_key.specifier);
1101+
env, "request for '%s' is from invalid module", specifier);
10781102
return Nothing<ModuleWrap*>();
10791103
}
10801104
if (!dependent->IsLinked()) {
1105+
std::string specifier =
1106+
GetSpecifierFromModuleRequest(context, referrer, module_request_index);
10811107
THROW_ERR_VM_MODULE_LINK_FAILURE(
1082-
env,
1083-
"request for '%s' is from a module not been linked",
1084-
cache_key.specifier);
1085-
return Nothing<ModuleWrap*>();
1086-
}
1087-
1088-
auto it = dependent->resolve_cache_.find(cache_key);
1089-
if (it == dependent->resolve_cache_.end()) {
1090-
THROW_ERR_VM_MODULE_LINK_FAILURE(
1091-
env, "request for '%s' is not in cache", cache_key.specifier);
1108+
env, "request for '%s' is from a module not been linked", specifier);
10921109
return Nothing<ModuleWrap*>();
10931110
}
10941111

1095-
ModuleWrap* module_wrap = dependent->GetLinkedRequest(it->second);
1112+
ModuleWrap* module_wrap =
1113+
dependent->GetLinkedRequest(static_cast<uint32_t>(module_request_index));
10961114
CHECK_NOT_NULL(module_wrap);
10971115
return Just(module_wrap);
10981116
}

src/module_wrap.h

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,6 @@ struct ModuleCacheKey : public MemoryRetainer {
9393
};
9494

9595
class ModuleWrap : public BaseObject {
96-
using ResolveCache =
97-
std::unordered_map<ModuleCacheKey, uint32_t, ModuleCacheKey::Hash>;
98-
9996
public:
10097
enum InternalFields {
10198
kModuleSlot = BaseObject::kInternalFieldCount,
@@ -197,26 +194,21 @@ class ModuleWrap : public BaseObject {
197194

198195
static v8::MaybeLocal<v8::Module> ResolveModuleCallback(
199196
v8::Local<v8::Context> context,
200-
v8::Local<v8::String> specifier,
201-
v8::Local<v8::FixedArray> import_attributes,
197+
size_t module_request_index,
202198
v8::Local<v8::Module> referrer);
203199
static v8::MaybeLocal<v8::Object> ResolveSourceCallback(
204200
v8::Local<v8::Context> context,
205-
v8::Local<v8::String> specifier,
206-
v8::Local<v8::FixedArray> import_attributes,
201+
size_t module_request_index,
207202
v8::Local<v8::Module> referrer);
208203
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);
209204

210205
// This method may throw a JavaScript exception, so the return type is
211206
// wrapped in a Maybe.
212-
static v8::Maybe<ModuleWrap*> ResolveModule(
213-
v8::Local<v8::Context> context,
214-
v8::Local<v8::String> specifier,
215-
v8::Local<v8::FixedArray> import_attributes,
216-
v8::Local<v8::Module> referrer);
207+
static v8::Maybe<ModuleWrap*> ResolveModule(v8::Local<v8::Context> context,
208+
size_t module_request_index,
209+
v8::Local<v8::Module> referrer);
217210

218211
v8::Global<v8::Module> module_;
219-
ResolveCache resolve_cache_;
220212
contextify::ContextifyContext* contextify_context_ = nullptr;
221213
bool synthetic_ = false;
222214
bool linked_ = false;

test/parallel/test-internal-module-wrap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ function testLinkMismatch() {
9191
}, {
9292
code: 'ERR_MODULE_LINK_MISMATCH',
9393
// Test that ModuleCacheKey::ToString() is used in the error message.
94-
message: `Module request 'ModuleCacheKey("bar")' at index 0 must be linked to the same module requested at index 1`
94+
message: `Module request 'ModuleCacheKey("bar")' at index 1 must be linked to the same module requested at index 0`
9595
});
9696
}
9797

0 commit comments

Comments
 (0)