Support the import.meta property

Through the `import.meta` property the engine can pass some values to
the module. Currently only the `path` of the script file is exposed.
This commit is contained in:
phosit
2025-01-15 22:15:08 +01:00
committed by phosit
parent a40caaffc4
commit d76e107dca
5 changed files with 52 additions and 0 deletions
+24
View File
@@ -160,6 +160,10 @@ ModuleLoader::CompiledModule::CompiledModule(const ScriptRequest& rq, const VfsP
throw std::invalid_argument{fmt::format("Unable to compile module: \"{}\".",
filePathStr)};
}
JS::RootedValue modInfo{rq.cx};
Script::CreateObject(rq, &modInfo, "path", filePathStr);
JS::SetModulePrivate(m_ModuleObject, modInfo);
}
ModuleLoader::Future::Future(const ScriptRequest& rq, ModuleLoader& loader, const VfsPath& modulePath):
@@ -236,6 +240,26 @@ void ModuleLoader::Future::SetReservedSlot(JS::Value privateValue) noexcept
return Future{rq, *this, modulePath};
}
/**
* This is only executed once per module. Following accesses of `import.meta`
* evaluate to the same object.
*/
[[nodiscard]] bool ModuleLoader::MetadataHook(JSContext* cx, JS::HandleValue privateValue,
JS::HandleObject metaObject) noexcept
{
const ScriptRequest rq{cx};
JS::RootedValue path{cx};
if (!Script::GetProperty(rq, privateValue, "path", &path))
return false;
JS::RootedValue metaValue{cx, JS::ObjectValue(*metaObject)};
if (!Script::SetProperty(rq, metaValue, "path", path))
return false;
return true;
}
[[nodiscard]] JSObject* ModuleLoader::ResolveHook(JSContext* cx, JS::HandleValue,
JS::HandleObject moduleRequest) noexcept
{
+2
View File
@@ -100,6 +100,8 @@ public:
private:
// Functions used by the `ScriptContext`.
[[nodiscard]] static bool MetadataHook(JSContext* cx, JS::HandleValue privateValue,
JS::HandleObject metaObject) noexcept;
[[nodiscard]] static JSObject* ResolveHook(JSContext* cx, JS::HandleValue,
JS::HandleObject moduleRequest) noexcept;
[[nodiscard]] static bool DynamicImportHook(JSContext* cx, JS::HandleValue referencingPrivate,
+2
View File
@@ -155,6 +155,7 @@ ScriptContext::ScriptContext(int contextSize, uint32_t heapGrowthBytesGCTrigger)
JS::SetPromiseRejectionTrackerCallback(m_cx, &Script::UnhandledRejectedPromise);
JSRuntime* runtime{JS_GetRuntime(m_cx)};
JS::SetModuleMetadataHook(runtime, &Script::ModuleLoader::MetadataHook);
JS::SetModuleResolveHook(runtime, &Script::ModuleLoader::ResolveHook);
JS::SetModuleDynamicImportHook(runtime, &Script::ModuleLoader::DynamicImportHook);
}
@@ -166,6 +167,7 @@ ScriptContext::~ScriptContext()
JSRuntime* runtime{JS_GetRuntime(m_cx)};
JS::SetModuleDynamicImportHook(runtime, nullptr);
JS::SetModuleResolveHook(runtime, nullptr);
JS::SetModuleMetadataHook(runtime, nullptr);
// Switch back to normal performance mode to avoid assertion in debug mode.
js::gc::SetPerformanceHint(m_cx, js::gc::PerformanceHint::Normal);
@@ -401,4 +401,24 @@ public:
TS_ASSERT_LESS_THAN(pi, 3.1416);
TS_ASSERT_LESS_THAN(3.1415, pi);
}
void test_Meta()
{
ScriptInterface script{"Test", "Test", g_ScriptContext};
const ScriptRequest rq{script};
auto future = script.GetModuleLoader().LoadModule(rq, "meta.js");
g_ScriptContext->RunJobs();
JS::RootedObject ns{rq.cx, future.Get()};
const JS::RootedValue modNamespace{rq.cx, JS::ObjectValue(*ns)};
JS::RootedValue meta{rq.cx};
TS_ASSERT(ScriptFunction::Call(rq, modNamespace, "getMeta", &meta));
std::string path;
TS_ASSERT(Script::GetProperty(rq, meta, "path", path));
TS_ASSERT_STR_EQUALS(path, "meta.js");
}
};