From 1b5ab8142e1ef055eb86b29f5a060d32d3e2afcc Mon Sep 17 00:00:00 2001 From: Yves Date: Sun, 13 Jul 2014 15:31:48 +0000 Subject: [PATCH] Makes custom JS objects compatible with SpiderMonkey ESR31. In v24 you called JS_InitClass and passed in a definition of JSNative functions. Later you could call JS_NewObject with this class and the object would get a prototype with the specified JSNative functions. In ESR31 you now have to explicitly store the prototype object returned by JS_InitClass and pass it as prototype argument to JS_NewObject to achieve the same. This change modifies our existing ScriptInterface implementation for custom object types a bit and uses it at places where the JSAPI was used directly before. Refs #2462 This was SVN commit r15524. --- source/gui/IGUIObject.cpp | 8 ++++---- source/gui/scripting/JSInterface_GUITypes.cpp | 16 ++++++++++++---- source/gui/scripting/JSInterface_IGUIObject.cpp | 14 ++++++++++---- source/scriptinterface/ScriptInterface.cpp | 8 +++----- source/scriptinterface/ScriptInterface.h | 4 ++-- .../scripting/EngineScriptConversions.cpp | 12 ++---------- source/simulation2/system/IComponent.cpp | 4 ++-- source/simulation2/system/IComponent.h | 6 +++++- source/simulation2/system/Interface.h | 2 +- source/simulation2/system/InterfaceScripted.h | 11 ++++++----- 10 files changed, 47 insertions(+), 38 deletions(-) diff --git a/source/gui/IGUIObject.cpp b/source/gui/IGUIObject.cpp index e7de78a35e..d37e9533d9 100644 --- a/source/gui/IGUIObject.cpp +++ b/source/gui/IGUIObject.cpp @@ -518,11 +518,11 @@ JSObject* IGUIObject::GetJSObject() // not have these objects hang around forever using up memory, though. if (m_JSObject.uninitialised()) { - JSObject* obj = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL); - m_JSObject = CScriptValRooted(cx, OBJECT_TO_JSVAL(obj)); - JS_SetPrivate(JSVAL_TO_OBJECT(m_JSObject.get()), this); + JS::RootedObject obj(cx, m_pGUI->GetScriptInterface()->CreateCustomObject("GUIObject")); + m_JSObject = CScriptValRooted(cx, JS::ObjectValue(*obj)); + JS_SetPrivate(obj, this); } - return JSVAL_TO_OBJECT(m_JSObject.get());; + return &m_JSObject.get().toObject(); } CStr IGUIObject::GetPresentableName() const diff --git a/source/gui/scripting/JSInterface_GUITypes.cpp b/source/gui/scripting/JSInterface_GUITypes.cpp index 65fbac5d68..735c1571a8 100644 --- a/source/gui/scripting/JSInterface_GUITypes.cpp +++ b/source/gui/scripting/JSInterface_GUITypes.cpp @@ -51,8 +51,11 @@ JSFunctionSpec JSI_GUISize::JSI_methods[] = JSBool JSI_GUISize::construct(JSContext* cx, uint argc, jsval* vp) { + JSAutoRequest rq(cx); JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL); + + ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUISize")); if (args.length() == 8) { @@ -166,9 +169,12 @@ JSFunctionSpec JSI_GUIColor::JSI_methods[] = JSBool JSI_GUIColor::construct(JSContext* cx, uint argc, jsval* vp) { + JSAutoRequest rq(cx); JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - JSObject* obj = JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL); - + + ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUIColor")); + if (args.length() == 4) { JS_SetProperty(cx, obj, "r", &args[0]); @@ -243,7 +249,9 @@ JSBool JSI_GUIMouse::construct(JSContext* cx, uint argc, jsval* vp) { JSAutoRequest rq(cx); JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - JSObject* obj = JS_NewObject(cx, &JSI_GUIMouse::JSI_class, NULL, NULL); + + ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUIMouse")); if (args.length() == 3) { diff --git a/source/gui/scripting/JSInterface_IGUIObject.cpp b/source/gui/scripting/JSInterface_IGUIObject.cpp index f5b0213db8..0d0cb9ccf6 100644 --- a/source/gui/scripting/JSInterface_IGUIObject.cpp +++ b/source/gui/scripting/JSInterface_IGUIObject.cpp @@ -56,6 +56,8 @@ JSFunctionSpec JSI_IGUIObject::JSI_methods[] = JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp) { JSAutoRequest rq(cx); + ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL); if (!e) return JS_FALSE; @@ -161,8 +163,8 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand { CColor colour; GUI::GetSetting(e, propName, colour); - JS::RootedObject obj(cx, JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL)); - vp.set(JS::ObjectValue(*obj)); + JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUIColor")); + vp.setObject(*obj); JS::RootedValue c(cx); // Attempt to minimise ugliness through macrosity #define P(x) c = JS::NumberValue(colour.x); \ @@ -183,7 +185,8 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Hand CClientArea area; GUI::GetSetting(e, propName, area); - vp.set(JS::ObjectValue(*JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL))); + JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUISize")); + vp.setObject(*obj); try { ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; @@ -595,14 +598,17 @@ JSBool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Hand JSBool JSI_IGUIObject::construct(JSContext* cx, uint argc, jsval* vp) { + JSAutoRequest rq(cx); JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; + if (args.length() == 0) { JS_ReportError(cx, "GUIObject has no default constructor"); return JS_FALSE; } - JS::RootedObject obj(cx, JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL)); + JS::RootedObject obj(cx, pScriptInterface->CreateCustomObject("GUIObject")); // Store the IGUIObject in the JS object's 'private' area IGUIObject* guiObject = (IGUIObject*)JSVAL_TO_PRIVATE(args[0]); diff --git a/source/scriptinterface/ScriptInterface.cpp b/source/scriptinterface/ScriptInterface.cpp index 3b728e830d..86d7727ff1 100644 --- a/source/scriptinterface/ScriptInterface.cpp +++ b/source/scriptinterface/ScriptInterface.cpp @@ -946,7 +946,7 @@ void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructo CustomType type; - type.m_Object = obj; + type.m_Prototype = obj; type.m_Class = clasp; type.m_Constructor = constructor; @@ -960,12 +960,10 @@ JSObject* ScriptInterface::CreateCustomObject(const std::string & typeName) if (it == m_CustomObjectTypes.end()) throw PSERROR_Scripting_TypeDoesNotExist(); - JSFunction* ctor = JS_NewFunction(m->m_cx, (*it).second.m_Constructor, 0, 0, - NULL, "ctor_fun"); - return JS_New(m->m_cx, JS_GetFunctionObject(ctor), 0, NULL); + JS::RootedObject prototype(m->m_cx, (*it).second.m_Prototype); + return JS_NewObject(m->m_cx, (*it).second.m_Class, prototype, NULL); } - bool ScriptInterface::CallFunctionVoid(jsval val, const char* name) { JSAutoRequest rq(m->m_cx); diff --git a/source/scriptinterface/ScriptInterface.h b/source/scriptinterface/ScriptInterface.h index d17ce6295f..699945543a 100644 --- a/source/scriptinterface/ScriptInterface.h +++ b/source/scriptinterface/ScriptInterface.h @@ -413,8 +413,8 @@ private: class CustomType { public: - JSObject * m_Object; - JSClass * m_Class; + JSObject* m_Prototype; + JSClass* m_Class; JSNative m_Constructor; }; void Register(const char* name, JSNative fptr, size_t nargs); diff --git a/source/simulation2/scripting/EngineScriptConversions.cpp b/source/simulation2/scripting/EngineScriptConversions.cpp index 71d4166e8d..f38a6c465a 100644 --- a/source/simulation2/scripting/EngineScriptConversions.cpp +++ b/source/simulation2/scripting/EngineScriptConversions.cpp @@ -51,8 +51,8 @@ template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::Value& // Otherwise we need to construct a wrapper object // (TODO: cache wrapper objects?) - JSClass* cls = val->GetJSClass(); - if (!cls) + JS::RootedObject obj(cx); + if (!val->NewJSObject(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface, &obj)) { // Report as an error, since scripts really shouldn't try to use unscriptable interfaces LOGERROR(L"IComponent does not have a scriptable interface"); @@ -60,15 +60,7 @@ template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::Value& return; } - JS::RootedObject obj(cx, JS_NewObject(cx, cls, NULL, NULL)); - if (!obj) - { - LOGERROR(L"Failed to construct IComponent script object"); - ret = JS::UndefinedValue(); - return; - } JS_SetPrivate(obj, static_cast(val)); - ret = JS::ObjectValue(*obj); } diff --git a/source/simulation2/system/IComponent.cpp b/source/simulation2/system/IComponent.cpp index 2aca346322..cfa852b5a8 100644 --- a/source/simulation2/system/IComponent.cpp +++ b/source/simulation2/system/IComponent.cpp @@ -33,9 +33,9 @@ void IComponent::HandleMessage(const CMessage& UNUSED(msg), bool UNUSED(global)) { } -JSClass* IComponent::GetJSClass() const +bool IComponent::NewJSObject(ScriptInterface& UNUSED(scriptInterface), JS::MutableHandleObject UNUSED(out)) const { - return NULL; + return false; } jsval IComponent::GetJSInstance() const diff --git a/source/simulation2/system/IComponent.h b/source/simulation2/system/IComponent.h index 7e1b29a935..06873d0b28 100644 --- a/source/simulation2/system/IComponent.h +++ b/source/simulation2/system/IComponent.h @@ -56,7 +56,11 @@ public: virtual void Serialize(ISerializer& serialize) = 0; virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) = 0; - virtual JSClass* GetJSClass() const; + /** + * Returns false by default, indicating that a scripted wrapper of this IComponent is not supported. + * Derrived classes should return true if they implement such a wrapper. + */ + virtual bool NewJSObject(ScriptInterface& scriptInterface, JS::MutableHandleObject out) const; virtual jsval GetJSInstance() const; virtual int GetComponentTypeId() const = 0; diff --git a/source/simulation2/system/Interface.h b/source/simulation2/system/Interface.h index 9d1283877b..4308b4825e 100644 --- a/source/simulation2/system/Interface.h +++ b/source/simulation2/system/Interface.h @@ -21,7 +21,7 @@ #include "simulation2/system/IComponent.h" #define DECLARE_INTERFACE_TYPE(iname) \ - virtual JSClass* GetJSClass() const; \ + virtual bool NewJSObject(ScriptInterface& scriptInterface, JS::MutableHandleObject out) const; \ static void InterfaceInit(ScriptInterface& scriptInterface); \ static int GetInterfaceId() { return IID_##iname; } diff --git a/source/simulation2/system/InterfaceScripted.h b/source/simulation2/system/InterfaceScripted.h index f589849284..6c814f6ec8 100644 --- a/source/simulation2/system/InterfaceScripted.h +++ b/source/simulation2/system/InterfaceScripted.h @@ -32,12 +32,13 @@ { NULL } \ }; \ void ICmp##iname::InterfaceInit(ScriptInterface& scriptInterface) { \ - JSContext* cx = scriptInterface.GetContext(); \ - JSAutoRequest rq(cx); \ - JSObject* global = JS_GetGlobalForScopeChain(cx); \ - JS_InitClass(cx, global, NULL, &class_ICmp##iname, NULL, 0, NULL, methods_ICmp##iname, NULL, NULL); \ + scriptInterface.DefineCustomObjectType(&class_ICmp##iname, NULL, 0, NULL, methods_ICmp##iname, NULL, NULL); \ + } \ + bool ICmp##iname::NewJSObject(ScriptInterface& scriptInterface, JS::MutableHandleObject out) const\ + { \ + out.set(scriptInterface.CreateCustomObject("ICmp" #iname)); \ + return true; \ } \ - JSClass* ICmp##iname::GetJSClass() const { return &class_ICmp##iname; } \ void RegisterComponentInterface_##iname(ScriptInterface& scriptInterface) { \ ICmp##iname::InterfaceInit(scriptInterface); \ }