Skip to content

Commit 0330add

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 0330add

File tree

3 files changed

+73
-67
lines changed

3 files changed

+73
-67
lines changed

src/module_wrap.cc

Lines changed: 67 additions & 53 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,50 @@ 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 this:
662+
//
663+
// import { foo } from 'mod' with { type: 'json' };
664+
// import { bar } from 'mod' with { type: 'json' };
665+
// import { baz } from 'mod2';
666+
//
667+
// The first two module requests are identical. The map would look like
668+
// { mod_key: 0, mod2_key: 2 } in this case, so that module request 0 and module
669+
// request 1 would be mapped to mod_key and both should resolve to the module
670+
// identified by module request 0 (the first one with this identity),
671+
// and module request 2 should resolve the module identified by index 2.
672+
std::unordered_map<ModuleCacheKey, int, ModuleCacheKey::Hash> module_request_map;
673+
Local<Value> current_module;
674+
Local<Value> first_seen_module;
675+
for (int i = 0; i < request_count; i++) {
676+
// TODO(joyeecheung): merge this with the serializeKey() in module_map.js. This
677+
// currently doesn't sort the import attributes.
670678
ModuleCacheKey module_cache_key = ModuleCacheKey::From(
671679
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;
680+
auto it = module_request_map.find(module_cache_key);
681+
if (it == module_request_map.end()) {
682+
// This is the first request with this identity, record it - any mismatch
683+
// for this would only be found in subsequent requests, so no need to check here.
684+
module_request_map[module_cache_key] = i;
685+
} else { // This identity has been seen before, check for mismatch.
686+
int first_seen_index = it->second;
687+
// Check that the module is the same as the one resolved by the first request with
688+
// this identity.
689+
if (!modules->Get(context, i).ToLocal(&current_module) ||
690+
!modules->Get(context, first_seen_index).ToLocal(&first_seen_module) ||
691+
!current_module->StrictEquals(first_seen_module)) {
692+
// If the module is different from the one of the same request, throw an error.
693+
THROW_ERR_MODULE_LINK_MISMATCH(
694+
realm->env(),
695+
"Module request '%s' at index %d must be linked "
696+
"to the same module requested at index %d",
697+
module_cache_key.ToString(),
698+
i,
699+
first_seen_index);
700+
return;
701+
}
690702
}
691703
}
692704

@@ -1013,11 +1025,10 @@ void ModuleWrap::HasAsyncGraph(Local<Name> property,
10131025
// static
10141026
MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
10151027
Local<Context> context,
1016-
Local<String> specifier,
1017-
Local<FixedArray> import_attributes,
1028+
size_t module_request_index,
10181029
Local<Module> referrer) {
10191030
ModuleWrap* resolved_module;
1020-
if (!ResolveModule(context, specifier, import_attributes, referrer)
1031+
if (!ResolveModule(context, module_request_index, referrer)
10211032
.To(&resolved_module)) {
10221033
return {};
10231034
}
@@ -1028,11 +1039,10 @@ MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
10281039
// static
10291040
MaybeLocal<Object> ModuleWrap::ResolveSourceCallback(
10301041
Local<Context> context,
1031-
Local<String> specifier,
1032-
Local<FixedArray> import_attributes,
1042+
size_t module_request_index,
10331043
Local<Module> referrer) {
10341044
ModuleWrap* resolved_module;
1035-
if (!ResolveModule(context, specifier, import_attributes, referrer)
1045+
if (!ResolveModule(context, module_request_index, referrer)
10361046
.To(&resolved_module)) {
10371047
return {};
10381048
}
@@ -1053,11 +1063,22 @@ MaybeLocal<Object> ModuleWrap::ResolveSourceCallback(
10531063
return module_source_object.As<Object>();
10541064
}
10551065

1066+
static std::string GetSpecifierFromModuleRequest(Local<Context> context,
1067+
Local<Module> referrer,
1068+
size_t module_request_index) {
1069+
Local<ModuleRequest> raw_request =
1070+
referrer->GetModuleRequests()
1071+
->Get(context, static_cast<int>(module_request_index))
1072+
.As<ModuleRequest>();
1073+
Local<String> specifier = raw_request->GetSpecifier();
1074+
Utf8Value specifier_utf8(Isolate::GetCurrent(), specifier);
1075+
return specifier_utf8.ToString();
1076+
}
1077+
10561078
// static
10571079
Maybe<ModuleWrap*> ModuleWrap::ResolveModule(
10581080
Local<Context> context,
1059-
Local<String> specifier,
1060-
Local<FixedArray> import_attributes,
1081+
size_t module_request_index,
10611082
Local<Module> referrer) {
10621083
Isolate* isolate = Isolate::GetCurrent();
10631084
Environment* env = Environment::GetCurrent(context);
@@ -1068,31 +1089,24 @@ Maybe<ModuleWrap*> ModuleWrap::ResolveModule(
10681089
// Check that the referrer is not yet been instantiated.
10691090
DCHECK(referrer->GetStatus() <= Module::kInstantiated);
10701091

1071-
ModuleCacheKey cache_key =
1072-
ModuleCacheKey::From(context, specifier, import_attributes);
1073-
10741092
ModuleWrap* dependent = ModuleWrap::GetFromModule(env, referrer);
10751093
if (dependent == nullptr) {
1094+
std::string specifier =
1095+
GetSpecifierFromModuleRequest(context, referrer, module_request_index);
10761096
THROW_ERR_VM_MODULE_LINK_FAILURE(
1077-
env, "request for '%s' is from invalid module", cache_key.specifier);
1097+
env, "request for '%s' is from invalid module", specifier);
10781098
return Nothing<ModuleWrap*>();
10791099
}
10801100
if (!dependent->IsLinked()) {
1101+
std::string specifier =
1102+
GetSpecifierFromModuleRequest(context, referrer, module_request_index);
10811103
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);
1104+
env, "request for '%s' is from a module not been linked", specifier);
10921105
return Nothing<ModuleWrap*>();
10931106
}
10941107

1095-
ModuleWrap* module_wrap = dependent->GetLinkedRequest(it->second);
1108+
ModuleWrap* module_wrap =
1109+
dependent->GetLinkedRequest(static_cast<uint32_t>(module_request_index));
10961110
CHECK_NOT_NULL(module_wrap);
10971111
return Just(module_wrap);
10981112
}

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)