Index: source/gui/CGUI.cpp =================================================================== --- source/gui/CGUI.cpp +++ source/gui/CGUI.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -129,7 +129,7 @@ ret = IN_HANDLED; ScriptRequest rq(m_ScriptInterface); - JS::RootedObject globalObj(rq.cx, rq.glob); + JS::RootedObject globalObj(rq.cx, &rq.globalObject()); JS::RootedValue result(rq.cx); if (!JS_CallFunctionValue(rq.cx, globalObj, m_GlobalHotkeys[hotkey][eventName], JS::HandleValueArray::empty(), &result)) ScriptException::CatchPending(rq); Index: source/gui/GUIManager.cpp =================================================================== --- source/gui/GUIManager.cpp +++ source/gui/GUIManager.cpp @@ -270,7 +270,7 @@ std::shared_ptr scriptInterface = gui->GetScriptInterface(); ScriptRequest rq(scriptInterface); - JS::RootedObject globalObj(rq.cx, rq.glob); + JS::RootedObject globalObj(rq.cx, &rq.globalObject()); JS::RootedValue funcVal(rq.cx, *callbackFunction); Index: source/scriptinterface/FunctionWrapper.h =================================================================== --- source/scriptinterface/FunctionWrapper.h +++ source/scriptinterface/FunctionWrapper.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -388,7 +388,7 @@ template thisGetter = nullptr, u16 flags = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT> static void Register(const ScriptRequest& rq, const char* name) { - JS_DefineFunction(rq.cx, rq.nativeScope, name, &ToJSNative, args_info::nb_args, flags); + JS_DefineFunction(rq.cx, rq.GetNativeScope(), name, &ToJSNative, args_info::nb_args, flags); } /** Index: source/scriptinterface/ScriptInterface.h =================================================================== --- source/scriptinterface/ScriptInterface.h +++ source/scriptinterface/ScriptInterface.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -135,11 +135,16 @@ * entering the ScriptInterface compartment. It should only be used in specific situations, * for instance when initializing a persistent rooted. * If you need the compartmented context of the ScriptInterface, you should create a - * ScriptInterface::Request and use the context from that. + * ScriptRequest and use the context from that. */ JSContext* GetGeneralJSContext() const; std::shared_ptr GetContext() const; + /** + * Get the JS Object on which functions are placed into. + */ + JS::HandleObject GetNativeScope() const; + /** * Load global scripts that most script interfaces need, * located in the /globalscripts directory. VFS must be initialized. Index: source/scriptinterface/ScriptInterface.cpp =================================================================== --- source/scriptinterface/ScriptInterface.cpp +++ source/scriptinterface/ScriptInterface.cpp @@ -72,31 +72,29 @@ * Constructor for ScriptRequest - here because it needs access into ScriptInterface_impl. */ ScriptRequest::ScriptRequest(const ScriptInterface& scriptInterface) : - cx(scriptInterface.m->m_cx), - glob(scriptInterface.m->m_glob), - nativeScope(scriptInterface.m->m_nativeScope), - m_ScriptInterface(scriptInterface) + cx(scriptInterface.m->m_cx) { m_FormerRealm = JS::EnterRealm(cx, scriptInterface.m->m_glob); } -ScriptRequest::~ScriptRequest() -{ - JS::LeaveRealm(cx, m_FormerRealm); -} - -ScriptRequest::ScriptRequest(JSContext* cx) : ScriptRequest(ScriptInterface::CmptPrivate::GetScriptInterface(cx)) -{ -} - JS::Value ScriptRequest::globalValue() const { - return JS::ObjectValue(*glob); + return JS::ObjectValue(*JS::GetRealmGlobalOrNull(JS::GetCurrentRealmOrNull(cx))); +} + +JSObject& ScriptRequest::globalObject() const +{ + return *JS::GetRealmGlobalOrNull(JS::GetCurrentRealmOrNull(cx)); +} + +JS::HandleObject ScriptRequest::GetNativeScope() const +{ + return GetScriptInterface().GetNativeScope(); } const ScriptInterface& ScriptRequest::GetScriptInterface() const { - return m_ScriptInterface; + return ScriptInterface::CmptPrivate::GetScriptInterface(cx); } namespace @@ -358,7 +356,7 @@ ScriptRequest rq(this); m_CmptPrivate.pScriptInterface = this; - JS::SetRealmPrivate(JS::GetObjectRealmOrNull(rq.glob), (void*)&m_CmptPrivate); + JS::SetRealmPrivate(JS::GetObjectRealmOrNull(&rq.globalObject()), (void*)&m_CmptPrivate); } ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, const ScriptInterface& neighbor) @@ -376,7 +374,7 @@ ScriptRequest rq(this); m_CmptPrivate.pScriptInterface = this; - JS::SetRealmPrivate(JS::GetObjectRealmOrNull(rq.glob), (void*)&m_CmptPrivate); + JS::SetRealmPrivate(JS::GetObjectRealmOrNull(&rq.globalObject()), (void*)&m_CmptPrivate); } ScriptInterface::~ScriptInterface() @@ -435,7 +433,7 @@ { ScriptRequest rq(this); JS::RootedValue math(rq.cx); - JS::RootedObject global(rq.cx, rq.glob); + JS::RootedObject global(rq.cx, &rq.globalObject()); if (JS_GetProperty(rq.cx, global, "Math", &math) && math.isObject()) { JS::RootedObject mathObj(rq.cx, &math.toObject()); @@ -463,6 +461,11 @@ return m->m_context; } +JS::HandleObject ScriptInterface::GetNativeScope() const +{ + return m->m_nativeScope; +} + void ScriptInterface::CallConstructor(JS::HandleValue ctor, JS::HandleValueArray argv, JS::MutableHandleValue out) const { ScriptRequest rq(this); @@ -491,7 +494,7 @@ throw PSERROR_Scripting_DefineType_AlreadyExists(); } - JS::RootedObject global(rq.cx, rq.glob); + JS::RootedObject global(rq.cx, &rq.globalObject()); JS::RootedObject obj(rq.cx, JS_InitClass(rq.cx, global, nullptr, clasp, constructor, minArgs, // Constructor, min args @@ -526,7 +529,7 @@ bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate) { ScriptRequest rq(this); - JS::RootedObject global(rq.cx, rq.glob); + JS::RootedObject global(rq.cx, &rq.globalObject()); bool found; if (!JS_HasProperty(rq.cx, global, name, &found)) @@ -570,7 +573,7 @@ bool ScriptInterface::GetGlobalProperty(const ScriptRequest& rq, const std::string& name, JS::MutableHandleValue out) { // Try to get the object as a property of the global object. - JS::RootedObject global(rq.cx, rq.glob); + JS::RootedObject global(rq.cx, &rq.globalObject()); if (!JS_GetProperty(rq.cx, global, name.c_str(), out)) { out.set(JS::NullHandleValue); @@ -583,7 +586,7 @@ // Some objects, such as const definitions, or Class definitions, are hidden inside closures. // We must fetch those from the correct lexical scope. //JS::RootedValue glob(cx); - JS::RootedObject lexical_environment(rq.cx, JS_GlobalLexicalEnvironment(rq.glob)); + JS::RootedObject lexical_environment(rq.cx, JS_GlobalLexicalEnvironment(&rq.globalObject())); if (!JS_GetProperty(rq.cx, lexical_environment, name.c_str(), out)) { out.set(JS::NullHandleValue); @@ -610,7 +613,7 @@ bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code) const { ScriptRequest rq(this); - JS::RootedObject global(rq.cx, rq.glob); + JS::RootedObject global(rq.cx, &rq.globalObject()); // CompileOptions does not copy the contents of the filename string pointer. // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. Index: source/scriptinterface/ScriptRequest.h =================================================================== --- source/scriptinterface/ScriptRequest.h +++ source/scriptinterface/ScriptRequest.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -69,14 +69,19 @@ ScriptRequest(const ScriptInterface& scriptInterface); ScriptRequest(const ScriptInterface* scriptInterface) : ScriptRequest(*scriptInterface) {} ScriptRequest(std::shared_ptr scriptInterface) : ScriptRequest(*scriptInterface) {} - ~ScriptRequest(); /** * Create a script request from a JSContext. * This can be used to get the script interface in a JSNative function. - * In general, you shouldn't have to rely on this otherwise. + * NB: this does not enter the realm, as we assume we are already in the realm of cx. + * Should that somehow not be true, do not use this constructor. */ - ScriptRequest(JSContext* cx); + ScriptRequest(JSContext* cx) : cx(cx), m_FormerRealm(nullptr) {} + + ~ScriptRequest() { + if (m_FormerRealm) + JS::LeaveRealm(cx, m_FormerRealm); + } /** * Return the scriptInterface active when creating this ScriptRequest. @@ -85,17 +90,31 @@ */ const ScriptInterface& GetScriptInterface() const; + /** + * Return the global value of the current context (aka Realm aka ScriptInterface). + * Ideally not for use in inner loops. + */ JS::Value globalValue() const; + /** + * Return the global object of the current context (aka Realm aka ScriptInterface). + * Ideally not for use in inner loops. + */ + JSObject& globalObject() const; + + /** + * Return the object on which functions are exposed. + * Goes through ScriptInterface - don't use in hot code. + * Primarily exists to avoid including ScriptInterface in FunctionWrapper.h + */ + JS::HandleObject GetNativeScope() const; + +public: // Note that JSContext actually changes behind the scenes when creating another ScriptRequest for another realm, // so be _very_ careful when juggling between different realms. JSContext* cx; - JS::HandleObject glob; - JS::HandleObject nativeScope; private: - const ScriptInterface& m_ScriptInterface; JS::Realm* m_FormerRealm; }; - #endif // INCLUDED_SCRIPTREQUEST Index: source/simulation2/components/CCmpAIManager.cpp =================================================================== --- source/simulation2/components/CCmpAIManager.cpp +++ source/simulation2/components/CCmpAIManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -252,7 +252,7 @@ // Register the sim globals for easy & explicit access. Mark it replaceable for hotloading. JS::RootedValue global(rq.cx, simrq.globalValue()); m_ScriptInterface->SetGlobal("Sim", global, true); - JS::RootedValue scope(rq.cx, JS::ObjectValue(*simrq.nativeScope.get())); + JS::RootedValue scope(rq.cx, JS::ObjectValue(*simrq.GetNativeScope().get())); m_ScriptInterface->SetGlobal("SimEngine", scope, true); } Index: source/simulation2/scripting/EngineScriptConversions.cpp =================================================================== --- source/simulation2/scripting/EngineScriptConversions.cpp +++ source/simulation2/scripting/EngineScriptConversions.cpp @@ -156,7 +156,7 @@ template<> void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, const CFixedVector3D& val) { - JS::RootedObject global(rq.cx, rq.glob); + JS::RootedObject global(rq.cx, &rq.globalObject()); JS::RootedValue valueVector3D(rq.cx); if (!ScriptInterface::GetGlobalProperty(rq, "Vector3D", &valueVector3D)) FAIL_VOID("Failed to get Vector3D constructor"); @@ -192,7 +192,7 @@ template<> void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, const CFixedVector2D& val) { - JS::RootedObject global(rq.cx, rq.glob); + JS::RootedObject global(rq.cx, &rq.globalObject()); JS::RootedValue valueVector2D(rq.cx); if (!ScriptInterface::GetGlobalProperty(rq, "Vector2D", &valueVector2D)) FAIL_VOID("Failed to get Vector2D constructor");