forked from mirrors/0ad
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:
@@ -0,0 +1,4 @@
|
|||||||
|
export function getMeta()
|
||||||
|
{
|
||||||
|
return import.meta;
|
||||||
|
}
|
||||||
@@ -160,6 +160,10 @@ ModuleLoader::CompiledModule::CompiledModule(const ScriptRequest& rq, const VfsP
|
|||||||
throw std::invalid_argument{fmt::format("Unable to compile module: \"{}\".",
|
throw std::invalid_argument{fmt::format("Unable to compile module: \"{}\".",
|
||||||
filePathStr)};
|
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):
|
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};
|
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,
|
[[nodiscard]] JSObject* ModuleLoader::ResolveHook(JSContext* cx, JS::HandleValue,
|
||||||
JS::HandleObject moduleRequest) noexcept
|
JS::HandleObject moduleRequest) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Functions used by the `ScriptContext`.
|
// 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,
|
[[nodiscard]] static JSObject* ResolveHook(JSContext* cx, JS::HandleValue,
|
||||||
JS::HandleObject moduleRequest) noexcept;
|
JS::HandleObject moduleRequest) noexcept;
|
||||||
[[nodiscard]] static bool DynamicImportHook(JSContext* cx, JS::HandleValue referencingPrivate,
|
[[nodiscard]] static bool DynamicImportHook(JSContext* cx, JS::HandleValue referencingPrivate,
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ ScriptContext::ScriptContext(int contextSize, uint32_t heapGrowthBytesGCTrigger)
|
|||||||
JS::SetPromiseRejectionTrackerCallback(m_cx, &Script::UnhandledRejectedPromise);
|
JS::SetPromiseRejectionTrackerCallback(m_cx, &Script::UnhandledRejectedPromise);
|
||||||
|
|
||||||
JSRuntime* runtime{JS_GetRuntime(m_cx)};
|
JSRuntime* runtime{JS_GetRuntime(m_cx)};
|
||||||
|
JS::SetModuleMetadataHook(runtime, &Script::ModuleLoader::MetadataHook);
|
||||||
JS::SetModuleResolveHook(runtime, &Script::ModuleLoader::ResolveHook);
|
JS::SetModuleResolveHook(runtime, &Script::ModuleLoader::ResolveHook);
|
||||||
JS::SetModuleDynamicImportHook(runtime, &Script::ModuleLoader::DynamicImportHook);
|
JS::SetModuleDynamicImportHook(runtime, &Script::ModuleLoader::DynamicImportHook);
|
||||||
}
|
}
|
||||||
@@ -166,6 +167,7 @@ ScriptContext::~ScriptContext()
|
|||||||
JSRuntime* runtime{JS_GetRuntime(m_cx)};
|
JSRuntime* runtime{JS_GetRuntime(m_cx)};
|
||||||
JS::SetModuleDynamicImportHook(runtime, nullptr);
|
JS::SetModuleDynamicImportHook(runtime, nullptr);
|
||||||
JS::SetModuleResolveHook(runtime, nullptr);
|
JS::SetModuleResolveHook(runtime, nullptr);
|
||||||
|
JS::SetModuleMetadataHook(runtime, nullptr);
|
||||||
|
|
||||||
// Switch back to normal performance mode to avoid assertion in debug mode.
|
// Switch back to normal performance mode to avoid assertion in debug mode.
|
||||||
js::gc::SetPerformanceHint(m_cx, js::gc::PerformanceHint::Normal);
|
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(pi, 3.1416);
|
||||||
TS_ASSERT_LESS_THAN(3.1415, pi);
|
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");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user