Changeset View
Changeset View
Standalone View
Standalone View
source/gui/Scripting/JSInterface_GUIProxy_impl.h
/* Copyright (C) 2020 Wildfire Games. | /* Copyright (C) 2021 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
// This file is included directly into actual implementation files. | // This file is included directly into actual implementation files. | ||||
#include "JSInterface_GUIProxy.h" | #include "JSInterface_GUIProxy.h" | ||||
#include "gui/CGUI.h" | #include "gui/CGUI.h" | ||||
#include "gui/CGUISetting.h" | #include "gui/CGUISetting.h" | ||||
#include "gui/ObjectBases/IGUIObject.h" | #include "gui/ObjectBases/IGUIObject.h" | ||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
#include "scriptinterface/FunctionWrapper.h" | |||||
#include "scriptinterface/ScriptExtraHeaders.h" | #include "scriptinterface/ScriptExtraHeaders.h" | ||||
#include "scriptinterface/ScriptInterface.h" | #include "scriptinterface/ScriptInterface.h" | ||||
#include <string> | #include <string> | ||||
template <typename T> | template <typename T> | ||||
JSClass& JSI_GUIProxy<T>::ClassDefinition() | JSClass& JSI_GUIProxy<T>::ClassDefinition() | ||||
{ | { | ||||
static JSClass c = PROXY_CLASS_DEF("GUIObjectProxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) | JSCLASS_HAS_RESERVED_SLOTS(1)); | static JSClass c = PROXY_CLASS_DEF("GUIObjectProxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) | JSCLASS_HAS_RESERVED_SLOTS(1)); | ||||
return c; | return c; | ||||
} | } | ||||
template <typename T> | template <typename T> | ||||
JSI_GUIProxy<T>& JSI_GUIProxy<T>::Singleton() | JSI_GUIProxy<T>& JSI_GUIProxy<T>::Singleton() | ||||
{ | { | ||||
static JSI_GUIProxy<T> s; | static JSI_GUIProxy<T> s; | ||||
return s; | return s; | ||||
} | } | ||||
// Call this for every specialised type. You will need to override IGUIObject::CreateJSObject() in your class interface. | |||||
#define DECLARE_GUIPROXY(Type) \ | |||||
void Type::CreateJSObject() \ | |||||
{ \ | |||||
ScriptRequest rq(m_pGUI.GetScriptInterface()); \ | |||||
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>; \ | |||||
m_JSObject = ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton())); \ | |||||
} \ | |||||
template class JSI_GUIProxy<Type>; | |||||
// Use a common namespace to avoid duplicating the symbols un-necessarily. | // Use a common namespace to avoid duplicating the symbols un-necessarily. | ||||
namespace JSInterface_GUIProxy | namespace JSInterface_GUIProxy | ||||
{ | { | ||||
/** | template<typename T> | ||||
* Conveniently wrap a simple C++ function to a JSNative. | inline T* GuiObjectGetter(const ScriptRequest&, JS::CallArgs& args) | ||||
*/ | |||||
template<class OG, class R, void (R::*funcptr)(ScriptInterface&, JS::MutableHandleValue)> | |||||
inline bool apply_to(JSContext* cx, uint argc, JS::Value* vp) | |||||
{ | { | ||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); | return IGUIProxyObject::FromPrivateSlot<T>(args.thisv().toObjectOrNull()); | ||||
OG* e = static_cast<OG*>(js::GetProxyPrivate(args.thisv().toObjectOrNull()).toPrivate()); | |||||
if (!e) | |||||
return false; | |||||
(static_cast<R*>(e)->*(funcptr))(*(ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface), args.rval()); | |||||
return true; | |||||
} | } | ||||
// Default implementation of the cache via unordered_map | // Default implementation of the cache via unordered_map | ||||
class MapCache : public GUIProxyProps | class MapCache : public GUIProxyProps | ||||
{ | { | ||||
public: | public: | ||||
virtual ~MapCache() {}; | virtual ~MapCache() {}; | ||||
virtual bool has(const std::string& name) const override | virtual bool has(const std::string& name) const override | ||||
{ | { | ||||
return m_Functions.find(name) != m_Functions.end(); | return m_Functions.find(name) != m_Functions.end(); | ||||
} | } | ||||
virtual JSObject* get(const std::string& name) const override | virtual JSObject* get(const std::string& name) const override | ||||
{ | { | ||||
return m_Functions.at(name).get(); | return m_Functions.at(name).get(); | ||||
} | } | ||||
virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSNative function, int nargs) override | virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSFunction* function) override | ||||
{ | { | ||||
m_Functions[name].init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, function, nargs, 0, name.c_str()))); | m_Functions[name].init(rq.cx, JS_GetFunctionObject(function)); | ||||
return true; | return true; | ||||
} | } | ||||
protected: | protected: | ||||
std::unordered_map<std::string, JS::PersistentRootedObject> m_Functions; | std::unordered_map<std::string, JS::PersistentRootedObject> m_Functions; | ||||
}; | }; | ||||
} | } | ||||
Show All 19 Lines | |||||
} | } | ||||
template <typename T> | template <typename T> | ||||
std::pair<const js::BaseProxyHandler*, GUIProxyProps*> JSI_GUIProxy<T>::CreateData(ScriptInterface& scriptInterface) | std::pair<const js::BaseProxyHandler*, GUIProxyProps*> JSI_GUIProxy<T>::CreateData(ScriptInterface& scriptInterface) | ||||
{ | { | ||||
using PropertyCache = typename PropCache::type; | using PropertyCache = typename PropCache::type; | ||||
PropertyCache* data = new PropertyCache(); | PropertyCache* data = new PropertyCache(); | ||||
ScriptRequest rq(scriptInterface); | ScriptRequest rq(scriptInterface); | ||||
// Functions common to all children of IGUIObject. | |||||
JSI_GUIProxy<IGUIObject>::CreateFunctions(rq, data); | |||||
// Let derived classes register their own interface. | |||||
if constexpr (!std::is_same_v<T, IGUIObject>) | |||||
CreateFunctions(rq, data); | CreateFunctions(rq, data); | ||||
return { &Singleton(), data }; | return { &Singleton(), data }; | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
void JSI_GUIProxy<T>::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr, JS::PersistentRootedObject& val) | template<auto callable> | ||||
void JSI_GUIProxy<T>::CreateFunction(const ScriptRequest& rq, GUIProxyProps* cache, const std::string& name) | |||||
{ | |||||
cache->setFunction(rq, name, ScriptFunction::Create<callable, JSInterface_GUIProxy::GuiObjectGetter<T>>(rq, name.c_str())); | |||||
} | |||||
template<typename T> | |||||
std::unique_ptr<IGUIProxyObject> JSI_GUIProxy<T>::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr) | |||||
{ | { | ||||
js::ProxyOptions options; | js::ProxyOptions options; | ||||
options.setClass(&ClassDefinition()); | options.setClass(&ClassDefinition()); | ||||
auto ret = std::make_unique<IGUIProxyObject>(); | |||||
ret->m_Ptr = static_cast<IGUIObject*>(ptr); | |||||
JS::RootedValue cppObj(rq.cx), data(rq.cx); | JS::RootedValue cppObj(rq.cx), data(rq.cx); | ||||
cppObj.get().setPrivate(ptr); | cppObj.get().setPrivate(ret->m_Ptr); | ||||
data.get().setPrivate(static_cast<void*>(dataPtr)); | data.get().setPrivate(static_cast<void*>(dataPtr)); | ||||
val.init(rq.cx, js::NewProxyObject(rq.cx, &Singleton(), cppObj, nullptr, options)); | ret->m_Object.init(rq.cx, js::NewProxyObject(rq.cx, &Singleton(), cppObj, nullptr, options)); | ||||
js::SetProxyReservedSlot(val, 0, data); | js::SetProxyReservedSlot(ret->m_Object, 0, data); | ||||
return ret; | |||||
} | } | ||||
template <typename T> | template <typename T> | ||||
bool JSI_GUIProxy<T>::get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue UNUSED(receiver), JS::HandleId id, JS::MutableHandleValue vp) const | bool JSI_GUIProxy<T>::get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue UNUSED(receiver), JS::HandleId id, JS::MutableHandleValue vp) const | ||||
{ | { | ||||
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; | ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; | ||||
ScriptRequest rq(*pScriptInterface); | ScriptRequest rq(*pScriptInterface); | ||||
T* e = static_cast<T*>(js::GetProxyPrivate(proxy.get()).toPrivate()); | T* e = IGUIProxyObject::FromPrivateSlot<T>(proxy.get()); | ||||
if (!e) | if (!e) | ||||
return false; | return false; | ||||
JS::RootedValue idval(rq.cx); | JS::RootedValue idval(rq.cx); | ||||
if (!JS_IdToValue(rq.cx, id, &idval)) | if (!JS_IdToValue(rq.cx, id, &idval)) | ||||
return false; | return false; | ||||
std::string propName; | std::string propName; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | bool JSI_GUIProxy<T>::get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue UNUSED(receiver), JS::HandleId id, JS::MutableHandleValue vp) const | ||||
return false; | return false; | ||||
} | } | ||||
template <typename T> | template <typename T> | ||||
bool JSI_GUIProxy<T>::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue vp, | bool JSI_GUIProxy<T>::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue vp, | ||||
JS::HandleValue UNUSED(receiver), JS::ObjectOpResult& result) const | JS::HandleValue UNUSED(receiver), JS::ObjectOpResult& result) const | ||||
{ | { | ||||
T* e = static_cast<T*>(js::GetProxyPrivate(proxy.get()).toPrivate()); | T* e = IGUIProxyObject::FromPrivateSlot<T>(proxy.get()); | ||||
if (!e) | if (!e) | ||||
{ | { | ||||
LOGERROR("C++ GUI Object could not be found"); | LOGERROR("C++ GUI Object could not be found"); | ||||
return result.fail(JSMSG_OBJECT_REQUIRED); | return result.fail(JSMSG_OBJECT_REQUIRED); | ||||
} | } | ||||
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); | ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); | ||||
Show All 38 Lines | bool JSI_GUIProxy<T>::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue vp, | ||||
LOGERROR("Property '%s' does not exist!", propName.c_str()); | LOGERROR("Property '%s' does not exist!", propName.c_str()); | ||||
return result.fail(JSMSG_BAD_PROP_ID); | return result.fail(JSMSG_BAD_PROP_ID); | ||||
} | } | ||||
template<typename T> | template<typename T> | ||||
bool JSI_GUIProxy<T>::delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult& result) const | bool JSI_GUIProxy<T>::delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult& result) const | ||||
{ | { | ||||
T* e = static_cast<T*>(js::GetProxyPrivate(proxy.get()).toPrivate()); | T* e = IGUIProxyObject::FromPrivateSlot<T>(proxy.get()); | ||||
if (!e) | if (!e) | ||||
{ | { | ||||
LOGERROR("C++ GUI Object could not be found"); | LOGERROR("C++ GUI Object could not be found"); | ||||
return result.fail(JSMSG_OBJECT_REQUIRED); | return result.fail(JSMSG_OBJECT_REQUIRED); | ||||
} | } | ||||
ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); | ScriptRequest rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface); | ||||
Show All 19 Lines |
Wildfire Games · Phabricator