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.
This commit is contained in:
Yves 2014-07-13 15:31:48 +00:00
parent f4949c82ff
commit 1b5ab8142e
10 changed files with 47 additions and 38 deletions

View File

@ -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

View File

@ -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)
{

View File

@ -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<CColor>::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<CClientArea>::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]);

View File

@ -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);

View File

@ -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);

View File

@ -51,8 +51,8 @@ template<> void ScriptInterface::ToJSVal<IComponent*>(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<IComponent*>(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<void*>(val));
ret = JS::ObjectValue(*obj);
}

View File

@ -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

View File

@ -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;

View File

@ -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; }

View File

@ -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); \
}