src: clear all linked module caches once instantiated · nodejs/node@84701ff · GitHub
Skip to content

Commit 84701ff

Browse files
legendecasaduh95
authored andcommitted
src: clear all linked module caches once instantiated
There are two phases in module linking: link, and instantiate. These two operations are required to be separated to allow cyclic dependencies. `v8::Module::InstantiateModule` is only required to be invoked on the root module. The global references created by `ModuleWrap::Link` are only cleared at `ModuleWrap::Instantiate`. So the global references created for depended modules are usually not cleared because `ModuleWrap::Instantiate` is not invoked for each of depended modules, and caused memory leak. The change references the linked modules in an object internal slot. This is not an issue for Node.js ESM support as these modules can not be off-loaded. However, this could be outstanding for `vm.Module`. PR-URL: #59117 Backport-PR-URL: #60152 Fixes: #50113 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent ef005d0 commit 84701ff

3 files changed

Lines changed: 115 additions & 46 deletions

File tree

src/module_wrap.cc

Lines changed: 79 additions & 38 deletions

src/module_wrap.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,16 @@ struct ModuleCacheKey : public MemoryRetainer {
8484
};
8585

8686
class ModuleWrap : public BaseObject {
87+
using ResolveCache =
88+
std::unordered_map<ModuleCacheKey, uint32_t, ModuleCacheKey::Hash>;
89+
8790
public:
8891
enum InternalFields {
8992
kModuleSlot = BaseObject::kInternalFieldCount,
9093
kURLSlot,
9194
kSyntheticEvaluationStepsSlot,
92-
kContextObjectSlot, // Object whose creation context is the target Context
95+
kContextObjectSlot, // Object whose creation context is the target Context
96+
kLinkedRequestsSlot, // Array of linked requests
9397
kInternalFieldCount
9498
};
9599

@@ -105,23 +109,25 @@ class ModuleWrap : public BaseObject {
105109
v8::Local<v8::Module> module,
106110
v8::Local<v8::Object> meta);
107111

108-
void MemoryInfo(MemoryTracker* tracker) const override {
109-
tracker->TrackField("resolve_cache", resolve_cache_);
110-
}
111112
static void HasTopLevelAwait(const v8::FunctionCallbackInfo<v8::Value>& args);
112113

113114
v8::Local<v8::Context> context() const;
114115
v8::Maybe<bool> CheckUnsettledTopLevelAwait();
115116

116117
SET_MEMORY_INFO_NAME(ModuleWrap)
117118
SET_SELF_SIZE(ModuleWrap)
119+
SET_NO_MEMORY_INFO()
118120

119121
bool IsNotIndicativeOfMemoryLeakAtExit() const override {
120122
// XXX: The garbage collection rules for ModuleWrap are *super* unclear.
121123
// Do these objects ever get GC'd? Are we just okay with leaking them?
122124
return true;
123125
}
124126

127+
bool IsLinked() const { return linked_; }
128+
129+
ModuleWrap* GetLinkedRequest(uint32_t index);
130+
125131
static v8::Local<v8::PrimitiveArray> GetHostDefinedOptions(
126132
v8::Isolate* isolate, v8::Local<v8::Symbol> symbol);
127133

@@ -183,13 +189,19 @@ class ModuleWrap : public BaseObject {
183189
v8::Local<v8::Module> referrer);
184190
static ModuleWrap* GetFromModule(node::Environment*, v8::Local<v8::Module>);
185191

192+
// This method may throw a JavaScript exception, so the return type is
193+
// wrapped in a Maybe.
194+
static v8::Maybe<ModuleWrap*> ResolveModule(
195+
v8::Local<v8::Context> context,
196+
v8::Local<v8::String> specifier,
197+
v8::Local<v8::FixedArray> import_attributes,
198+
v8::Local<v8::Module> referrer);
199+
186200
v8::Global<v8::Module> module_;
187-
std::unordered_map<ModuleCacheKey,
188-
v8::Global<v8::Object>,
189-
ModuleCacheKey::Hash>
190-
resolve_cache_;
201+
ResolveCache resolve_cache_;
191202
contextify::ContextifyContext* contextify_context_ = nullptr;
192203
bool synthetic_ = false;
204+
bool linked_ = false;
193205
int module_hash_;
194206
};
195207

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

Lines changed: 16 additions & 0 deletions

0 commit comments

Comments
 (0)