Index: ps/trunk/source/gui/CGUI.h =================================================================== --- ps/trunk/source/gui/CGUI.h +++ ps/trunk/source/gui/CGUI.h @@ -47,6 +47,7 @@ struct SGUIScrollBarStyle; namespace js { class BaseProxyHandler; } +class GUIProxyProps; using map_pObjects = std::map; @@ -247,7 +248,7 @@ */ const CGUIColor& GetPreDefinedColor(const CStr& name) const { return m_PreDefinedColors.at(name); } - void* GetProxyData(const js::BaseProxyHandler* ptr) { return m_ProxyData.at(ptr); } + GUIProxyProps* GetProxyData(const js::BaseProxyHandler* ptr) { return m_ProxyData.at(ptr).get(); } shared_ptr GetScriptInterface() { return m_ScriptInterface; }; @@ -611,15 +612,16 @@ std::map m_ObjectTypes; /** - * This is intended to store the JS Functions returned when accessing some object properties. - * It's not a great solution, but I can't find a better one at the moment. + * This is intended to store the JSFunction when accessing certain properties. * The problem is that these functions are per-scriptInterface, and proxy handlers aren't. * So we know what we want to store, but we don't really have anywhere to store it. * It would be simpler to recreate the functions on every JS call, but that is slower * (this may or may not matter now and in the future). - * Another alternative would be to store them on each proxied object, but that wastes memory. + * It's not a great solution, but I can't find a better one at the moment. + * An alternative would be to store these on the proxy's prototype, + * but that embarks a lot of un-necessary code. */ - std::unordered_map m_ProxyData; + std::unordered_map> m_ProxyData; /** * Map from hotkey names to objects that listen to the hotkey. Index: ps/trunk/source/gui/CGUI.cpp =================================================================== --- ps/trunk/source/gui/CGUI.cpp +++ ps/trunk/source/gui/CGUI.cpp @@ -23,6 +23,7 @@ #include "gui/ObjectTypes/CGUIDummyObject.h" #include "gui/ObjectTypes/CTooltip.h" #include "gui/Scripting/ScriptFunctions.h" +#include "gui/Scripting/JSInterface_GUIProxy.h" #include "i18n/L10n.h" #include "lib/bits.h" #include "lib/input.h" Index: ps/trunk/source/gui/ObjectBases/IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/ObjectBases/IGUIObject.cpp +++ ps/trunk/source/gui/ObjectBases/IGUIObject.cpp @@ -462,15 +462,8 @@ void IGUIObject::CreateJSObject() { ScriptRequest rq(m_pGUI.GetScriptInterface()); - - js::ProxyOptions options; - options.setClass(&JSI_GUIProxy::ClassDefinition()); - - JS::RootedValue cppObj(rq.cx), data(rq.cx); - cppObj.get().setPrivate(this); - data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy::Singleton())); - m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy::Singleton(), cppObj, nullptr, options)); - js::SetProxyReservedSlot(m_JSObject, 0, data); + using ProxyHandler = JSI_GUIProxy>; + ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject); } JSObject* IGUIObject::GetJSObject() Index: ps/trunk/source/gui/ObjectTypes/CButton.h =================================================================== --- ps/trunk/source/gui/ObjectTypes/CButton.h +++ ps/trunk/source/gui/ObjectTypes/CButton.h @@ -27,9 +27,6 @@ class CButton : public IGUIObject, public IGUITextOwner, public IGUIButtonBehavior { GUI_OBJECT(CButton) - - friend JSI_GUIProxy; - public: CButton(CGUI& pGUI); virtual ~CButton(); @@ -54,6 +51,11 @@ */ virtual void Draw(); + /** + * Populate @param ret with the object's text size. + */ + void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret); + protected: /** * Sets up text, should be called every time changes has been @@ -73,8 +75,6 @@ virtual void CreateJSObject(); - void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret); - // Settings float m_BufferZone; i32 m_CellID; Index: ps/trunk/source/gui/ObjectTypes/CButton.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CButton.cpp +++ ps/trunk/source/gui/ObjectTypes/CButton.cpp @@ -119,23 +119,16 @@ return m_TextColorOver || m_TextColor; } -void CButton::CreateJSObject() -{ - ScriptRequest rq(m_pGUI.GetScriptInterface()); - - js::ProxyOptions options; - options.setClass(&JSI_GUIProxy::ClassDefinition()); - - JS::RootedValue cppObj(rq.cx), data(rq.cx); - cppObj.get().setPrivate(this); - data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy::Singleton())); - m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy::Singleton(), cppObj, nullptr, options)); - js::SetProxyReservedSlot(m_JSObject, 0, data); -} - void CButton::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret) { ScriptRequest rq(scriptInterface); UpdateText(); ScriptInterface::ToJSVal(rq, ret, m_GeneratedTexts[0].GetSize()); } + +void CButton::CreateJSObject() +{ + ScriptRequest rq(m_pGUI.GetScriptInterface()); + using ProxyHandler = JSI_GUIProxy>; + ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject); +} Index: ps/trunk/source/gui/ObjectTypes/CList.h =================================================================== --- ps/trunk/source/gui/ObjectTypes/CList.h +++ ps/trunk/source/gui/ObjectTypes/CList.h @@ -38,9 +38,6 @@ class CList : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner { GUI_OBJECT(CList) - - friend JSI_GUIProxy; - public: CList(CGUI& pGUI); virtual ~CList(); Index: ps/trunk/source/gui/ObjectTypes/CList.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CList.cpp +++ ps/trunk/source/gui/ObjectTypes/CList.cpp @@ -504,14 +504,7 @@ void CList::CreateJSObject() { ScriptRequest rq(m_pGUI.GetScriptInterface()); - - js::ProxyOptions options; - options.setClass(&JSI_GUIProxy::ClassDefinition()); - - JS::RootedValue cppObj(rq.cx), data(rq.cx); - cppObj.get().setPrivate(this); - data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy::Singleton())); - m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy::Singleton(), cppObj, nullptr, options)); - js::SetProxyReservedSlot(m_JSObject, 0, data); + using ProxyHandler = JSI_GUIProxy>; + ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject); } Index: ps/trunk/source/gui/ObjectTypes/CText.h =================================================================== --- ps/trunk/source/gui/ObjectTypes/CText.h +++ ps/trunk/source/gui/ObjectTypes/CText.h @@ -30,9 +30,6 @@ class CText : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner { GUI_OBJECT(CText) - - friend JSI_GUIProxy; - public: CText(CGUI& pGUI); virtual ~CText(); @@ -52,6 +49,10 @@ */ virtual bool MouseOverIcon(); + /** + * Populate @param ret with the object's text size. + */ + void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret); protected: /** * Sets up text, should be called every time changes has been @@ -71,8 +72,6 @@ virtual void CreateJSObject(); - void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret); - /** * Placement of text. Ignored when scrollbars are active. */ Index: ps/trunk/source/gui/ObjectTypes/CText.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CText.cpp +++ ps/trunk/source/gui/ObjectTypes/CText.cpp @@ -257,15 +257,8 @@ void CText::CreateJSObject() { ScriptRequest rq(m_pGUI.GetScriptInterface()); - - js::ProxyOptions options; - options.setClass(&JSI_GUIProxy::ClassDefinition()); - - JS::RootedValue cppObj(rq.cx), data(rq.cx); - cppObj.get().setPrivate(this); - data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy::Singleton())); - m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy::Singleton(), cppObj, nullptr, options)); - js::SetProxyReservedSlot(m_JSObject, 0, data); + using ProxyHandler = JSI_GUIProxy>; + ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject); } void CText::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret) Index: ps/trunk/source/gui/Scripting/JSInterface_CButton.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_CButton.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_CButton.cpp @@ -17,65 +17,22 @@ #include "precompiled.h" -#include "JSInterface_GUIProxy.h" - -#include "gui/CGUI.h" -#include "gui/CGUISetting.h" -#include "gui/ObjectBases/IGUIObject.h" -#include "gui/ObjectTypes/CButton.h" -#include "ps/CLogger.h" -#include "scriptinterface/ScriptExtraHeaders.h" -#include "scriptinterface/ScriptInterface.h" - -#include - -// Include the definition of the generic templates. #include "JSInterface_GUIProxy_impl.h" -namespace -{ -struct SData -{ - JS::PersistentRootedObject m_ToString; - JS::PersistentRootedObject m_Focus; - JS::PersistentRootedObject m_Blur; - JS::PersistentRootedObject m_GetComputedSize; - JS::PersistentRootedObject m_GetTextSize; -}; -} +#include "gui/ObjectTypes/CButton.h" -template <> -bool JSI_GUIProxy::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const -{ - const SData& data = *static_cast(js::GetProxyReservedSlot(proxy, 0).toPrivate()); - if (propName == "toString") - return vp.setObjectOrNull(data.m_ToString), true; - if (propName == "focus") - return vp.setObjectOrNull(data.m_Focus), true; - if (propName == "blur") - return vp.setObjectOrNull(data.m_Blur), true; - if (propName == "getComputedSize") - return vp.setObjectOrNull(data.m_GetComputedSize), true; - if (propName == "getTextSize") - return vp.setObjectOrNull(data.m_GetTextSize), true; - return false; -} +using GUIObjectType = CButton; -template <> -std::pair JSI_GUIProxy::CreateData(ScriptInterface& scriptInterface) +template<> +void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) { - SData* data = new SData(); - ScriptRequest rq(scriptInterface); - -#define func(class, func) &apply_to - data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString"))); - data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus"))); - data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur"))); - data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize"))); - data->m_GetTextSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(CButton, getTextSize), 0, 0, "getTextSize"))); +#define func(class, func) &JSInterface_GUIProxy::apply_to + cache->setFunction(rq, "toString", func(IGUIObject, toString), 0); + cache->setFunction(rq, "focus", func(IGUIObject, focus), 0); + cache->setFunction(rq, "blur", func(IGUIObject, blur), 0); + cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0); + cache->setFunction(rq, "getTextSize", func(GUIObjectType, getTextSize), 0); #undef func - - return { &Singleton(), data }; } -template class JSI_GUIProxy; +template class JSI_GUIProxy; Index: ps/trunk/source/gui/Scripting/JSInterface_CList.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_CList.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_CList.cpp @@ -17,31 +17,9 @@ #include "precompiled.h" -#include "JSInterface_GUIProxy.h" - -#include "gui/CGUI.h" -#include "gui/CGUISetting.h" -#include "gui/ObjectBases/IGUIObject.h" -#include "gui/ObjectTypes/CList.h" -#include "ps/CLogger.h" -#include "scriptinterface/ScriptExtraHeaders.h" -#include "scriptinterface/ScriptInterface.h" - -#include - -// Include the definition of the generic templates. #include "JSInterface_GUIProxy_impl.h" -namespace -{ -struct SData -{ - JS::PersistentRootedObject m_ToString; - JS::PersistentRootedObject m_Focus; - JS::PersistentRootedObject m_Blur; - JS::PersistentRootedObject m_GetComputedSize; - JS::PersistentRootedObject m_AddItem; -}; +#include "gui/ObjectTypes/CList.h" bool CList_AddItem(JSContext* cx, uint argc, JS::Value* vp) { @@ -58,40 +36,19 @@ e->AddItem(text, text); return true; } -} -template <> -bool JSI_GUIProxy::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const -{ - const SData& data = *static_cast(js::GetProxyReservedSlot(proxy, 0).toPrivate()); - if (propName == "toString") - return vp.setObjectOrNull(data.m_ToString), true; - if (propName == "focus") - return vp.setObjectOrNull(data.m_Focus), true; - if (propName == "blur") - return vp.setObjectOrNull(data.m_Blur), true; - if (propName == "getComputedSize") - return vp.setObjectOrNull(data.m_GetComputedSize), true; - if (propName == "addItem") - return vp.setObjectOrNull(data.m_AddItem), true; - return false; -} +using GUIObjectType = CList; -template <> -std::pair JSI_GUIProxy::CreateData(ScriptInterface& scriptInterface) +template<> +void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) { - SData* data = new SData(); - ScriptRequest rq(scriptInterface); - -#define func(class, func) &apply_to - data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString"))); - data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus"))); - data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur"))); - data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize"))); +#define func(class, func) &JSInterface_GUIProxy::apply_to + cache->setFunction(rq, "toString", func(IGUIObject, toString), 0); + cache->setFunction(rq, "focus", func(IGUIObject, focus), 0); + cache->setFunction(rq, "blur", func(IGUIObject, blur), 0); + cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0); #undef func - data->m_AddItem.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, CList_AddItem, 1, 0, "addItem"))); - - return { &Singleton(), data }; + cache->setFunction(rq, "addItem", CList_AddItem, 1); } -template class JSI_GUIProxy; +template class JSI_GUIProxy; Index: ps/trunk/source/gui/Scripting/JSInterface_CText.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_CText.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_CText.cpp @@ -17,65 +17,22 @@ #include "precompiled.h" -#include "JSInterface_GUIProxy.h" - -#include "gui/CGUI.h" -#include "gui/CGUISetting.h" -#include "gui/ObjectBases/IGUIObject.h" -#include "gui/ObjectTypes/CText.h" -#include "ps/CLogger.h" -#include "scriptinterface/ScriptExtraHeaders.h" -#include "scriptinterface/ScriptInterface.h" - -#include - -// Include the definition of the generic templates. #include "JSInterface_GUIProxy_impl.h" -namespace -{ -struct SData -{ - JS::PersistentRootedObject m_ToString; - JS::PersistentRootedObject m_Focus; - JS::PersistentRootedObject m_Blur; - JS::PersistentRootedObject m_GetComputedSize; - JS::PersistentRootedObject m_GetTextSize; -}; -} +#include "gui/ObjectTypes/CText.h" -template <> -bool JSI_GUIProxy::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const -{ - const SData& data = *static_cast(js::GetProxyReservedSlot(proxy, 0).toPrivate()); - if (propName == "toString") - return vp.setObjectOrNull(data.m_ToString), true; - if (propName == "focus") - return vp.setObjectOrNull(data.m_Focus), true; - if (propName == "blur") - return vp.setObjectOrNull(data.m_Blur), true; - if (propName == "getComputedSize") - return vp.setObjectOrNull(data.m_GetComputedSize), true; - if (propName == "getTextSize") - return vp.setObjectOrNull(data.m_GetTextSize), true; - return false; -} +using GUIObjectType = CText; -template <> -std::pair JSI_GUIProxy::CreateData(ScriptInterface& scriptInterface) +template<> +void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) { - SData* data = new SData(); - ScriptRequest rq(scriptInterface); - -#define func(class, func) &apply_to - data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString"))); - data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus"))); - data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur"))); - data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize"))); - data->m_GetTextSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(CText, getTextSize), 0, 0, "getTextSize"))); +#define func(class, func) &JSInterface_GUIProxy::apply_to + cache->setFunction(rq, "toString", func(IGUIObject, toString), 0); + cache->setFunction(rq, "focus", func(IGUIObject, focus), 0); + cache->setFunction(rq, "blur", func(IGUIObject, blur), 0); + cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0); + cache->setFunction(rq, "getTextSize", func(GUIObjectType, getTextSize), 0); #undef func - - return { &Singleton(), data }; } -template class JSI_GUIProxy; +template class JSI_GUIProxy; Index: ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.h =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.h +++ ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.h @@ -23,6 +23,7 @@ #include class ScriptInterface; +class ScriptRequest; // See JSI_GuiProxy below #if GCC_VERSION @@ -34,6 +35,22 @@ #endif /** + * Proxies need to store some data whose lifetime is tied to an interface. + * This is the virtual interface of that data. + */ +class GUIProxyProps +{ +public: + virtual ~GUIProxyProps() {}; + + // @return true if @param name exists in this cache. + virtual bool has(const std::string& name) const = 0; + // @return the JSFunction matching @param name. Must call has() first as it can assume existence. + virtual JSObject* get(const std::string& name) const = 0; + virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSNative function, int nargs) = 0; +}; + +/** * Handles the js interface with C++ GUI objects. * Proxy handlers must live for at least as long as the JS runtime * where a proxy object with that handler was created. The reason is that @@ -43,6 +60,17 @@ * GUI Objects only exist in C++ and have no JS-only properties. * As such, there is no "target" JS object that this proxy could point to, * and thus we should inherit from BaseProxyHandler and not js::Wrapper. + * + * Proxies can be used with prototypes and almost treated like regular JS objects. + * However, the default implementation embarks a lot of code that we don't really need here, + * since the only fanciness is that we cache JSFunction* properties. + * As such, these GUI proxies don't have one and instead override get/set directly. + * + * To add a new JSI_GUIProxy, you'll need to: + * - overload the GUI Object's CreateJSObject with the correct types and pointers + * - change the CGUI::AddObjectTypes method + * - explicitly instantiate the template. + * */ template class JSI_GUIProxy : public js::BaseProxyHandler @@ -54,17 +82,27 @@ // For convenience, this is the single instantiated JSI_GUIProxy. static JSI_GUIProxy& Singleton(); - static std::pair CreateData(ScriptInterface& scriptInterface); + // Call this in CGUI::AddObjectTypes. + static std::pair CreateData(ScriptInterface& scriptInterface); + + static void CreateJSObject(const ScriptRequest& rq, GUIObjectType* ptr, GUIProxyProps* data, JS::PersistentRootedObject& val); protected: // @param family can't be nullptr because that's used for some DOM object and it crashes. JSI_GUIProxy() : BaseProxyHandler(this, false, false) {}; + // Note: SM provides no virtual destructor for baseProxyHandler. // This also enforces making proxy handlers dataless static variables. ~JSI_GUIProxy() {}; - // This handles returning function properties. - // Specialize this. - bool FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const; + // The default implementations need to know the type of the GUIProxyProps for this proxy type. + // This is done by specializing this struct's alias type. + struct PropCache; + + // Specialize this to define the custom properties of this type. + static void CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache); + + // This handles returning custom properties. Specialize this if needed. + bool PropGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const; protected: // BaseProxyHandler interface below Index: ps/trunk/source/gui/Scripting/JSInterface_GUIProxy_impl.h =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUIProxy_impl.h +++ ps/trunk/source/gui/Scripting/JSInterface_GUIProxy_impl.h @@ -17,6 +17,17 @@ // This file is included directly into actual implementation files. +#include "JSInterface_GUIProxy.h" + +#include "gui/CGUI.h" +#include "gui/CGUISetting.h" +#include "gui/ObjectBases/IGUIObject.h" +#include "ps/CLogger.h" +#include "scriptinterface/ScriptExtraHeaders.h" +#include "scriptinterface/ScriptInterface.h" + +#include + template JSClass& JSI_GUIProxy::ClassDefinition() { @@ -31,8 +42,12 @@ return s; } -namespace +// Use a common namespace to avoid duplicating the symbols un-necessarily. +namespace JSInterface_GUIProxy { +/** + * Conveniently wrap a simple C++ function to a JSNative. + */ template inline bool apply_to(JSContext* cx, uint argc, JS::Value* vp) { @@ -45,6 +60,76 @@ return true; } + +// Default implementation of the cache via unordered_map +class MapCache : public GUIProxyProps +{ +public: + virtual ~MapCache() {}; + + virtual bool has(const std::string& name) const override + { + return m_Functions.find(name) != m_Functions.end(); + } + + virtual JSObject* get(const std::string& name) const override + { + return m_Functions.at(name).get(); + } + + virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSNative function, int nargs) override + { + m_Functions[name].init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, function, nargs, 0, name.c_str()))); + return true; + } + +protected: + std::unordered_map m_Functions; +}; +} + +// The default propcache is a MapCache. +template +struct JSI_GUIProxy::PropCache +{ + using type = JSInterface_GUIProxy::MapCache; +}; + +template +bool JSI_GUIProxy::PropGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const +{ + using PropertyCache = typename PropCache::type; + // Since we know at compile time what the type actually is, avoid the virtual call. + const PropertyCache* data = static_cast(static_cast(js::GetProxyReservedSlot(proxy, 0).toPrivate())); + if (data->has(propName)) + { + vp.setObjectOrNull(data->get(propName)); + return true; + } + return false; +} + +template +std::pair JSI_GUIProxy::CreateData(ScriptInterface& scriptInterface) +{ + using PropertyCache = typename PropCache::type; + PropertyCache* data = new PropertyCache(); + ScriptRequest rq(scriptInterface); + CreateFunctions(rq, data); + return { &Singleton(), data }; +} + +template +void JSI_GUIProxy::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr, JS::PersistentRootedObject& val) +{ + js::ProxyOptions options; + options.setClass(&ClassDefinition()); + + JS::RootedValue cppObj(rq.cx), data(rq.cx); + cppObj.get().setPrivate(ptr); + data.get().setPrivate(static_cast(dataPtr)); + val.init(rq.cx, js::NewProxyObject(rq.cx, &Singleton(), cppObj, nullptr, options)); + js::SetProxyReservedSlot(val, 0, data); } template @@ -66,7 +151,7 @@ return false; // Return function properties. Specializable. - if (FuncGetter(proxy, propName, vp)) + if (PropGetter(proxy, propName, vp)) return true; // Use onWhatever to access event handlers Index: ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp @@ -17,59 +17,19 @@ #include "precompiled.h" -#include "JSInterface_GUIProxy.h" - -#include "gui/CGUI.h" -#include "gui/CGUISetting.h" -#include "gui/ObjectBases/IGUIObject.h" -#include "gui/ObjectTypes/CText.h" -#include "ps/CLogger.h" -#include "scriptinterface/ScriptExtraHeaders.h" -#include "scriptinterface/ScriptInterface.h" - -#include - -// Include the definition of the generic templates. #include "JSInterface_GUIProxy_impl.h" -namespace -{ -struct SData -{ - JS::PersistentRootedObject m_ToString; - JS::PersistentRootedObject m_Focus; - JS::PersistentRootedObject m_Blur; - JS::PersistentRootedObject m_GetComputedSize; -}; -} - -template <> -bool JSI_GUIProxy::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const -{ - const SData& data = *static_cast(js::GetProxyReservedSlot(proxy, 0).toPrivate()); - if (propName == "toString") - return vp.setObjectOrNull(data.m_ToString), true; - if (propName == "focus") - return vp.setObjectOrNull(data.m_Focus), true; - if (propName == "blur") - return vp.setObjectOrNull(data.m_Blur), true; - if (propName == "getComputedSize") - return vp.setObjectOrNull(data.m_GetComputedSize), true; - return false; -} +using GUIObjectType = IGUIObject; -template <> -std::pair JSI_GUIProxy::CreateData(ScriptInterface& scriptInterface) +template<> +void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) { - SData* data = new SData(); - ScriptRequest rq(scriptInterface); -#define func(class, func) &apply_to - data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString"))); - data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus"))); - data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur"))); - data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize"))); +#define func(class, func) &JSInterface_GUIProxy::apply_to + cache->setFunction(rq, "toString", func(IGUIObject, toString), 0); + cache->setFunction(rq, "focus", func(IGUIObject, focus), 0); + cache->setFunction(rq, "blur", func(IGUIObject, blur), 0); + cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0); #undef func - return { &Singleton(), data }; } -template class JSI_GUIProxy; +template class JSI_GUIProxy;