mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Bug 1489477 - Stop modules from entraining the top-level JSScript r=sfink
This commit is contained in:
parent
f7ab18d265
commit
056511891c
@ -989,18 +989,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 getReservedSlot(ScriptSlot).toGCThing()->as<JSScript>();
|
||||
JSScript* ptr = maybeScript();
|
||||
MOZ_RELEASE_ASSERT(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -1157,6 +1161,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");
|
||||
|
@ -302,6 +302,7 @@ class ModuleObject : public NativeObject
|
||||
#endif
|
||||
void fixEnvironmentsAfterCompartmentMerge();
|
||||
|
||||
JSScript* maybeScript() const;
|
||||
JSScript* script() const;
|
||||
Scope* enclosingScope() const;
|
||||
ModuleEnvironmentObject& initialEnvironment() const;
|
||||
@ -348,7 +349,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();
|
||||
};
|
||||
|
@ -3120,6 +3120,9 @@ extern JS_PUBLIC_API(void)
|
||||
GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue requestedModuleObject,
|
||||
uint32_t* lineNumber, uint32_t* columnNumber);
|
||||
|
||||
/*
|
||||
* Get the top-level script for a module which has not yet been executed.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSScript*)
|
||||
GetModuleScript(JS::HandleObject moduleRecord);
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global getModuleLoadPath setModuleLoadHook setModuleResolveHook parseModule os */
|
||||
/* global getModuleLoadPath setModuleLoadHook setModuleResolveHook setModuleMetadataHook */
|
||||
/* global parseModule os */
|
||||
|
||||
// A basic synchronous module loader for testing the shell.
|
||||
{
|
||||
@ -173,6 +174,18 @@ const ReflectLoader = new class {
|
||||
let path = this.resolve(name, null);
|
||||
return this.loadAndExecute(path);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
setModuleLoadHook((path) => ReflectLoader.importRoot(path));
|
||||
@ -182,4 +195,9 @@ setModuleResolveHook((module, requestName) => {
|
||||
return ReflectLoader.loadAndParse(path);
|
||||
});
|
||||
|
||||
setModuleMetadataHook((module, metaObject) => {
|
||||
ReflectLoader.populateImportMeta(module, metaObject);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
@ -192,6 +192,7 @@ enum GlobalAppSlot
|
||||
{
|
||||
GlobalAppSlotModuleLoadHook, // Shell-specific; load a module graph
|
||||
GlobalAppSlotModuleResolveHook, // HostResolveImportedModule
|
||||
GlobalAppSlotModuleMetadataHook, // HostPopulateImportMeta
|
||||
GlobalAppSlotCount
|
||||
};
|
||||
static_assert(GlobalAppSlotCount <= JSCLASS_GLOBAL_APPLICATION_SLOTS,
|
||||
@ -3275,7 +3276,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 = TestingFunctionArgumentToScript(cx, value, fun.address());
|
||||
}
|
||||
@ -4750,25 +4751,47 @@ CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier
|
||||
}
|
||||
|
||||
static bool
|
||||
ShellModuleMetadataHook(JSContext* cx, HandleObject module, HandleObject metaObject)
|
||||
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
|
||||
GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@ -7258,7 +7281,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();
|
||||
@ -7978,6 +8005,12 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
" This hook is used to look up a previously loaded module object. It should\n"
|
||||
" be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("setModuleMetadataHook", SetModuleMetadataHook, 1, 0,
|
||||
"setModuleMetadataHook(function(module) {})",
|
||||
" Set the HostPopulateImportMeta hook to |function|.\n"
|
||||
" This hook is used to create the metadata object returned by import.meta for\n"
|
||||
" a module. It should be implemented by the module loader."),
|
||||
|
||||
JS_FN_HELP("getModuleLoadPath", GetModuleLoadPath, 0, 0,
|
||||
"getModuleLoadPath()",
|
||||
" Return any --module-load-path argument passed to the shell. Used by the\n"
|
||||
@ -9687,7 +9720,7 @@ ProcessArgs(JSContext* cx, OptionParser* op)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!modulePaths.empty() && !InitModuleLoader(cx)) {
|
||||
if (!InitModuleLoader(cx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -10689,7 +10722,7 @@ main(int argc, char** argv, char** envp)
|
||||
js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
|
||||
|
||||
JS::SetModuleResolveHook(cx->runtime(), CallModuleResolveHook);
|
||||
JS::SetModuleMetadataHook(cx->runtime(), ShellModuleMetadataHook);
|
||||
JS::SetModuleMetadataHook(cx->runtime(), CallModuleMetadataHook);
|
||||
|
||||
result = Shell(cx, &op, envp);
|
||||
|
||||
|
@ -154,11 +154,13 @@ AssertScopeMatchesEnvironment(Scope* scope, JSObject* originalEnv)
|
||||
MOZ_CRASH("NonSyntactic should not have a syntactic environment");
|
||||
break;
|
||||
|
||||
case ScopeKind::Module:
|
||||
MOZ_ASSERT(env->as<ModuleEnvironmentObject>().module().script() ==
|
||||
si.scope()->as<ModuleScope>().script());
|
||||
case ScopeKind::Module: {
|
||||
ModuleObject* module = &env->as<ModuleEnvironmentObject>().module();
|
||||
MOZ_ASSERT_IF(module->maybeScript(),
|
||||
module->script() == si.scope()->as<ModuleScope>().script());
|
||||
env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
|
||||
break;
|
||||
}
|
||||
|
||||
case ScopeKind::WasmInstance:
|
||||
env = &env->as<WasmInstanceEnvironmentObject>().enclosingEnvironment();
|
||||
|
Loading…
Reference in New Issue
Block a user