Issue #1691 - Part 5b: Stop modules from entraining the top-level JSScript. https://bugzilla.mozilla.org/show_bug.cgi?id=1489477

(cherry picked from commit 6ce0af856186e65fb4987daf4e06dc68fb3f7483)
This commit is contained in:
Brian Smith 2023-04-09 10:25:44 -05:00 committed by roytam1
commit 5c09a3b3cc
4 changed files with 71 additions and 21 deletions

View file

@ -835,18 +835,22 @@ ModuleObject::fixEnvironmentsAfterCompartmentMerge()
AssertModuleScopesMatch(this);
}
bool
ModuleObject::hasScript() const
JSScript*
ModuleObject::maybeScript() const
{
// When modules are parsed via the Reflect.parse() API, the module object
// doesn't have a script.
return !getReservedSlot(ScriptSlot).isUndefined();
Value value = getReservedSlot(ScriptSlot);
if (value.isUndefined())
return nullptr;
return value.toGCThing()->as<JSScript>();
}
JSScript*
ModuleObject::script() const
{
return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
JSScript* ptr = maybeScript();
MOZ_RELEASE_ASSERT(ptr);
return ptr;
}
static inline void
@ -906,8 +910,8 @@ ModuleObject::enclosingScope() const
ModuleObject::trace(JSTracer* trc, JSObject* obj)
{
ModuleObject& module = obj->as<ModuleObject>();
if (module.hasScript()) {
JSScript* script = module.script();
JSScript* script = module.maybeScript();
if (script) {
TraceManuallyBarrieredEdge(trc, &script, "Module script");
module.setReservedSlot(ScriptSlot, PrivateValue(script));
}
@ -976,6 +980,11 @@ ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue
#endif
RootedScript script(cx, self->script());
// The top-level script if a module is only ever executed once. Clear the
// reference to prevent us keeping this alive unnecessarily.
self->setReservedSlot(ScriptSlot, UndefinedValue());
RootedModuleEnvironmentObject scope(cx, self->environment());
if (!scope) {
JS_ReportErrorASCII(cx, "Module declarations have not yet been instantiated");

View file

@ -266,6 +266,7 @@ class ModuleObject : public NativeObject
#endif
void fixEnvironmentsAfterCompartmentMerge();
JSScript* maybeScript() const;
JSScript* script() const;
Scope* enclosingScope() const;
ModuleEnvironmentObject& initialEnvironment() const;
@ -312,7 +313,6 @@ class ModuleObject : public NativeObject
static void trace(JSTracer* trc, JSObject* obj);
static void finalize(js::FreeOp* fop, JSObject* obj);
bool hasScript() const;
bool hasImportBindings() const;
FunctionDeclarationVector* functionDeclarations();
};

View file

@ -38,6 +38,18 @@ Reflect.Loader = new class {
module.declarationInstantiation();
return module.evaluation();
}
populateImportMeta(module, metaObject) {
// For the shell, use the script's filename as the base URL.
let path;
if (ReflectApply(MapPrototypeHas, this.modulePaths, [module])) {
path = ReflectApply(MapPrototypeGet, this.modulePaths, [module]);
} else {
path = "(unknown)";
}
metaObject.url = path;
}
};
setModuleResolveHook((referencingInfo, requestName) => {

View file

@ -2830,7 +2830,7 @@ DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp, Sprinter* sprinte
RootedScript script(cx);
RootedValue value(cx, p.argv[i]);
if (value.isObject() && value.toObject().is<ModuleObject>())
script = value.toObject().as<ModuleObject>().script();
script = value.toObject().as<ModuleObject>().maybeScript();
else
script = ValueToScript(cx, value, fun.address());
if (!script)
@ -4132,23 +4132,48 @@ ShellGetModulePrivate(JSContext* cx, unsigned argc, Value* vp)
return true;
}
ShellModuleMetadataHook(JSContext* cx, HandleObject module, HandleObject metaObject)
static bool
SetModuleMetadataHook(JSContext* cx, unsigned argc, Value* vp)
{
// For the shell, just use the script's filename as the base URL.
RootedScript script(cx, module->as<ModuleObject>().script());
const char* filename = script->scriptSource()->filename();
MOZ_ASSERT(filename);
RootedString url(cx, NewStringCopyZ<CanGC>(cx, filename));
if (!url)
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"setModuleMetadataHook", "0", "s");
return false;
}
if (!JS_DefineProperty(cx, metaObject, "url", url, JSPROP_ENUMERATE))
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
const char* typeName = InformalValueTypeName(args[0]);
JS_ReportErrorASCII(cx, "expected hook function, got %s", typeName);
return false;
}
Handle<GlobalObject*> global = cx->global();
global->setReservedSlot(GlobalAppSlotModuleMetadataHook, args[0]);
args.rval().setUndefined();
return true;
}
static bool
CallModuleMetadataHook(JSContext* cx, HandleObject module, HandleObject metaObject)
{
Handle<GlobalObject*> global = cx->global();
RootedValue hookValue(cx, global->getReservedSlot(GlobalAppSlotModuleMetadataHook));
if (hookValue.isUndefined()) {
JS_ReportErrorASCII(cx, "Module metadata hook not set");
return false;
}
MOZ_ASSERT(hookValue.toObject().is<JSFunction>());
JS::AutoValueArray<2> args(cx);
args[0].setObject(*module);
args[1].setObject(*metaObject);
RootedValue dummy(cx);
return JS_CallFunctionValue(cx, nullptr, hookValue, args, &dummy);
}
static bool
SetModuleDynamicImportHook(JSContext* cx, unsigned argc, Value* vp)
{
@ -5616,7 +5641,11 @@ DumpScopeChain(JSContext* cx, unsigned argc, Value* vp)
}
script = JSFunction::getOrCreateScript(cx, fun);
} else {
script = obj->as<ModuleObject>().script();
script = obj->as<ModuleObject>().maybeScript();
if (!script) {
JS_ReportErrorASCII(cx, "module does not have an associated script");
return false;
}
}
script->bodyScope()->dump();
@ -7451,7 +7480,7 @@ ProcessArgs(JSContext* cx, OptionParser* op)
if (const char* path = op->getStringOption("module-load-path"))
moduleLoadPath = path;
if (!modulePaths.empty() && !InitModuleLoader(cx))
if (!InitModuleLoader(cx))
return false;
while (!filePaths.empty() || !codeChunks.empty() || !modulePaths.empty()) {