1
0
forked from 0ad/0ad

More exact stack rooting (CallFunction object).

Changes CallFunction and CallFunctionVoid to use a HandleValue as object
parameter. Also changes some JS serialization/deserialization functions
to only support the JSAPI rooted types (drop support for CScriptVal and
CScriptValRooted there). Some other functions got changed too because
they were closely related.

Refs #2415
Refs #2462

This was SVN commit r15592.
This commit is contained in:
Yves 2014-07-31 19:18:40 +00:00
parent d677a30c39
commit 5c07a25ddc
26 changed files with 320 additions and 237 deletions

View File

@ -172,11 +172,15 @@ void CGUIManager::LoadPage(SGUIPage& page)
// If we're hotloading then try to grab some data from the previous page
shared_ptr<ScriptInterface::StructuredClone> hotloadData;
if (page.gui)
{
CScriptVal hotloadDataVal;
shared_ptr<ScriptInterface> scriptInterface = page.gui->GetScriptInterface();
scriptInterface->CallFunction(scriptInterface->GetGlobalObject(), "getHotloadData", hotloadDataVal);
hotloadData = scriptInterface->WriteStructuredClone(hotloadDataVal.get());
{
shared_ptr<ScriptInterface> scriptInterface = page.gui->GetScriptInterface();
JSContext* cx = scriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, scriptInterface->GetGlobalObject());
JS::RootedValue hotloadDataVal(cx);
scriptInterface->CallFunction(global, "getHotloadData", &hotloadDataVal);
hotloadData = scriptInterface->WriteStructuredClone(hotloadDataVal);
}
page.inputs.clear();
@ -228,16 +232,20 @@ void CGUIManager::LoadPage(SGUIPage& page)
page.gui->SendEventToAll("load");
shared_ptr<ScriptInterface> scriptInterface = page.gui->GetScriptInterface();
CScriptVal initDataVal;
CScriptVal hotloadDataVal;
JSContext* cx = scriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue initDataVal(cx);
JS::RootedValue hotloadDataVal(cx);
JS::RootedValue global(cx, scriptInterface->GetGlobalObject());
if (page.initData)
initDataVal = scriptInterface->ReadStructuredClone(page.initData);
initDataVal.set(scriptInterface->ReadStructuredClone(page.initData));
if (hotloadData)
hotloadDataVal = scriptInterface->ReadStructuredClone(hotloadData);
hotloadDataVal.set(scriptInterface->ReadStructuredClone(hotloadData));
// Call the init() function
if (!scriptInterface->CallFunctionVoid(
scriptInterface->GetGlobalObject(),
global,
"init",
initDataVal,
hotloadDataVal)
@ -269,8 +277,9 @@ CScriptVal CGUIManager::GetSavedGameData(ScriptInterface*& pPageScriptInterface)
JSContext* cx = top()->GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, top()->GetGlobalObject());
JS::RootedValue data(cx);
if (!top()->GetScriptInterface()->CallFunction(top()->GetGlobalObject(), "getSavedGameData", &data))
if (!top()->GetScriptInterface()->CallFunction(global, "getSavedGameData", &data))
LOGERROR(L"Failed to call getSavedGameData() on the current GUI page");
pPageScriptInterface = GetScriptInterface().get();
return CScriptVal(data);
@ -278,15 +287,24 @@ CScriptVal CGUIManager::GetSavedGameData(ScriptInterface*& pPageScriptInterface)
std::string CGUIManager::GetSavedGameData()
{
CScriptVal data;
top()->GetScriptInterface()->CallFunction(top()->GetGlobalObject(), "getSavedGameData", data);
return top()->GetScriptInterface()->StringifyJSON(data.get(), false);
shared_ptr<ScriptInterface> scriptInterface = top()->GetScriptInterface();
JSContext* cx = scriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue data(cx);
JS::RootedValue global(cx, top()->GetGlobalObject());
scriptInterface->CallFunction(global, "getSavedGameData", &data);
return scriptInterface->StringifyJSON(data, false);
}
void CGUIManager::RestoreSavedGameData(std::string jsonData)
{
top()->GetScriptInterface()->CallFunctionVoid(top()->GetGlobalObject(), "restoreSavedGameData",
top()->GetScriptInterface()->ParseJSON(jsonData));
shared_ptr<ScriptInterface> scriptInterface = top()->GetScriptInterface();
JSContext* cx = scriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, top()->GetGlobalObject());
scriptInterface->CallFunctionVoid(global, "restoreSavedGameData", scriptInterface->ParseJSON(jsonData));
}
InReaction CGUIManager::HandleEvent(const SDL_Event_* ev)
@ -297,12 +315,17 @@ InReaction CGUIManager::HandleEvent(const SDL_Event_* ev)
// visible game area), sometimes they'll want to intercepts events before the GUI (e.g.
// to capture all mouse events until a mouseup after dragging).
// So we call two separate handler functions:
shared_ptr<ScriptInterface> scriptInterface = top()->GetScriptInterface();
JSContext* cx = scriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, top()->GetGlobalObject());
bool handled;
{
PROFILE("handleInputBeforeGui");
if (top()->GetScriptInterface()->CallFunction(top()->GetGlobalObject(), "handleInputBeforeGui", *ev, top()->FindObjectUnderMouse(), handled))
if (scriptInterface->CallFunction(global, "handleInputBeforeGui", *ev, top()->FindObjectUnderMouse(), handled))
if (handled)
return IN_HANDLED;
}
@ -316,7 +339,8 @@ InReaction CGUIManager::HandleEvent(const SDL_Event_* ev)
{
PROFILE("handleInputAfterGui");
if (top()->GetScriptInterface()->CallFunction(top()->GetGlobalObject(), "handleInputAfterGui", *ev, handled)) if (handled)
if (scriptInterface->CallFunction(global, "handleInputAfterGui", *ev, handled))
if (handled)
return IN_HANDLED;
}

View File

@ -124,13 +124,16 @@ u8* CSimulationMessage::Serialize(u8* pBuffer) const
{
// TODO: ought to handle serialization exceptions
// TODO: ought to represent common commands more efficiently
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
u8* pos = CNetMessage::Serialize(pBuffer);
CBufferBinarySerializer serializer(*m_ScriptInterface, pos);
serializer.NumberU32_Unbounded("client", m_Client);
serializer.NumberI32_Unbounded("player", m_Player);
serializer.NumberU32_Unbounded("turn", m_Turn);
serializer.ScriptVal("command", m_Data);
serializer.ScriptVal("command", tmpData);
return serializer.GetBuffer();
}
@ -138,14 +141,18 @@ const u8* CSimulationMessage::Deserialize(const u8* pStart, const u8* pEnd)
{
// TODO: ought to handle serialization exceptions
// TODO: ought to represent common commands more efficiently
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
std::istringstream stream(std::string(pos, pEnd));
CStdDeserializer deserializer(*m_ScriptInterface, stream);
deserializer.NumberU32_Unbounded("client", m_Client);
deserializer.NumberI32_Unbounded("player", m_Player);
deserializer.NumberU32_Unbounded("turn", m_Turn);
deserializer.ScriptVal("command", m_Data);
deserializer.ScriptVal("command", &tmpData);
m_Data = CScriptValRooted(cx, tmpData);
return pEnd;
}
@ -153,12 +160,15 @@ size_t CSimulationMessage::GetSerializedLength() const
{
// TODO: serializing twice is stupidly inefficient - we should just
// do it once, store the result, and use it here and in Serialize
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
CLengthBinarySerializer serializer(*m_ScriptInterface);
serializer.NumberU32_Unbounded("client", m_Client);
serializer.NumberI32_Unbounded("player", m_Player);
serializer.NumberU32_Unbounded("turn", m_Turn);
serializer.ScriptVal("command", m_Data);
serializer.ScriptVal("command", tmpData);
return CNetMessage::GetSerializedLength() + serializer.GetLength();
}
@ -186,28 +196,39 @@ CGameSetupMessage::CGameSetupMessage(ScriptInterface& scriptInterface, jsval dat
u8* CGameSetupMessage::Serialize(u8* pBuffer) const
{
// TODO: ought to handle serialization exceptions
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
u8* pos = CNetMessage::Serialize(pBuffer);
CBufferBinarySerializer serializer(m_ScriptInterface, pos);
serializer.ScriptVal("command", m_Data);
serializer.ScriptVal("command", tmpData);
return serializer.GetBuffer();
}
const u8* CGameSetupMessage::Deserialize(const u8* pStart, const u8* pEnd)
{
// TODO: ought to handle serialization exceptions
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
std::istringstream stream(std::string(pos, pEnd));
CStdDeserializer deserializer(m_ScriptInterface, stream);
deserializer.ScriptVal("command", m_Data);
deserializer.ScriptVal("command", &tmpData);
m_Data = CScriptValRooted(cx, tmpData);
return pEnd;
}
size_t CGameSetupMessage::GetSerializedLength() const
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpData(cx, m_Data.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
CLengthBinarySerializer serializer(m_ScriptInterface);
serializer.ScriptVal("command", m_Data);
serializer.ScriptVal("command", tmpData);
return CNetMessage::GetSerializedLength() + serializer.GetLength();
}

View File

@ -27,9 +27,12 @@ public:
void test_sim()
{
ScriptInterface script("Test", "Test", g_ScriptRuntime);
CScriptValRooted val;
script.Eval("[4]", val);
CSimulationMessage msg(script, 1, 2, 3, val.get());
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue val(cx);
script.Eval("[4]", &val);
CSimulationMessage msg(script, 1, 2, 3, val);
TS_ASSERT_STR_EQUALS(msg.ToString(), "CSimulationMessage { m_Client: 1, m_Player: 2, m_Turn: 3, m_Data: [4] }");
size_t len = msg.GetSerializedLength();

View File

@ -323,8 +323,8 @@ void RunHardwareDetection()
g_UserReporter.SubmitReport("hwdetect", 11, scriptInterface.StringifyJSON(settings, false));
// Run the detection script:
scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "RunHardwareDetection", settings);
JS::RootedValue global(cx, scriptInterface.GetGlobalObject());
scriptInterface.CallFunctionVoid(global, "RunHardwareDetection", settings);
}
static void ReportGLLimits(ScriptInterface& scriptInterface, JS::HandleValue settings)

View File

@ -74,7 +74,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
// Call the named property on the given object
#define OVERLOADS(z, i, data) \
template <typename R TYPENAME_T0_TAIL(z, i)> \
bool CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) R& ret);
bool CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) R& ret);
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
@ -83,7 +83,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
// (as people would expect it to work based on the SpiderMonkey rooting guide).
#define OVERLOADS(z, i, data) \
template <typename R TYPENAME_T0_TAIL(z, i)> \
bool CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret);
bool CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret);
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
@ -91,7 +91,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
// without requiring implicit conversion.
#define OVERLOADS(z, i, data) \
template <typename R TYPENAME_T0_TAIL(z, i)> \
bool CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret);
bool CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret);
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS

View File

@ -129,16 +129,15 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#define OVERLOADS(z, i, data) \
template<typename R TYPENAME_T0_TAIL(z, i)> \
bool ScriptInterface::CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) R& ret) \
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) R& ret) \
{ \
JSContext* cx = GetContext(); \
JSAutoRequest rq(cx); \
JS::RootedValue jsRet(cx); \
JS::RootedValue val1(cx, val); \
JS::AutoValueVector argv(cx); \
argv.resize(i); \
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
bool ok = CallFunction_(val1, name, argv.length(), argv.begin(), &jsRet); \
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), &jsRet); \
if (!ok) \
return false; \
return FromJSVal(cx, jsRet, ret); \
@ -148,16 +147,15 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#define OVERLOADS(z, i, data) \
template<typename R TYPENAME_T0_TAIL(z, i)> \
bool ScriptInterface::CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret) \
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::Rooted<R>* ret) \
{ \
JSContext* cx = GetContext(); \
JSAutoRequest rq(cx); \
JS::MutableHandle<R> jsRet(ret); \
JS::RootedValue val1(cx, val); \
JS::AutoValueVector argv(cx); \
argv.resize(i); \
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
bool ok = CallFunction_(val1, name, argv.length(), argv.begin(), jsRet); \
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), jsRet); \
if (!ok) \
return false; \
return true; \
@ -167,15 +165,14 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#define OVERLOADS(z, i, data) \
template<typename R TYPENAME_T0_TAIL(z, i)> \
bool ScriptInterface::CallFunction(jsval val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret) \
bool ScriptInterface::CallFunction(JS::HandleValue val, const char* name, T0_A0_CONST_REF(z,i) JS::MutableHandle<R> ret) \
{ \
JSContext* cx = GetContext(); \
JSAutoRequest rq(cx); \
JS::RootedValue val1(cx, val); \
JS::AutoValueVector argv(cx); \
argv.resize(i); \
BOOST_PP_REPEAT_##z (i, ASSIGN_OR_TO_JS_VAL, ~) \
bool ok = CallFunction_(val1, name, argv.length(), argv.begin(), ret); \
bool ok = CallFunction_(val, name, argv.length(), argv.begin(), ret); \
if (!ok) \
return false; \
return true; \

View File

@ -964,12 +964,11 @@ JSObject* ScriptInterface::CreateCustomObject(const std::string & typeName)
return JS_NewObject(m->m_cx, (*it).second.m_Class, prototype, NULL);
}
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name)
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name)
{
JSAutoRequest rq(m->m_cx);
JS::RootedValue jsRet(m->m_cx);
JS::RootedValue val1(m->m_cx, val);
return CallFunction_(val1, name, 0, NULL, &jsRet);
return CallFunction_(val, name, 0, NULL, &jsRet);
}
bool ScriptInterface::CallFunction_(JS::HandleValue val, const char* name, uint argc, jsval* argv, JS::MutableHandleValue ret)
@ -1361,7 +1360,8 @@ std::string ScriptInterface::StringifyJSON(jsval obj, bool indent)
std::wstring ScriptInterface::ToString(jsval obj, bool pretty)
{
JSAutoRequest rq(m->m_cx);
if (JSVAL_IS_VOID(obj))
if (obj.isUndefined())
return L"(void 0)";
// Try to stringify as JSON if possible
@ -1373,7 +1373,7 @@ std::wstring ScriptInterface::ToString(jsval obj, bool pretty)
// Temporary disable the error reporter, so we don't print complaints about cyclic values
JSErrorReporter er = JS_SetErrorReporter(m->m_cx, NULL);
JSBool ok = JS_Stringify(m->m_cx, &obj, NULL, INT_TO_JSVAL(2), &StringifierW::callback, &str);
JSBool ok = JS_Stringify(m->m_cx, &obj, NULL, JS::NumberValue(2), &StringifierW::callback, &str);
// Restore error reporter
JS_SetErrorReporter(m->m_cx, er);
@ -1389,7 +1389,8 @@ std::wstring ScriptInterface::ToString(jsval obj, bool pretty)
// so fall back to obj.toSource()
std::wstring source = L"(error)";
CallFunction(obj, "toSource", source);
JS::RootedValue tmpObj(m->m_cx, obj); // TODO: pass Handle as argument already
CallFunction(tmpObj, "toSource", source);
return source;
}

View File

@ -150,25 +150,25 @@ public:
/**
* Call the named property on the given object, with void return type and 0 arguments
*/
bool CallFunctionVoid(jsval val, const char* name);
bool CallFunctionVoid(JS::HandleValue val, const char* name);
/**
* Call the named property on the given object, with void return type and 1 argument
*/
template<typename T0>
bool CallFunctionVoid(jsval val, const char* name, const T0& a0);
bool CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0);
/**
* Call the named property on the given object, with void return type and 2 arguments
*/
template<typename T0, typename T1>
bool CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1);
bool CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1);
/**
* Call the named property on the given object, with void return type and 3 arguments
*/
template<typename T0, typename T1, typename T2>
bool CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2);
bool CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1, const T2& a2);
JSObject* CreateCustomObject(const std::string & typeName);
void DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
@ -480,45 +480,42 @@ inline void ScriptInterface::AssignOrToJSVal<JS::Value>(JS::MutableHandleValue h
}
template<typename T0>
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0)
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0)
{
JSContext* cx = GetContext();
JSAutoRequest rq(cx);
JS::RootedValue jsRet(cx);
JS::RootedValue val1(cx, val);
JS::AutoValueVector argv(cx);
argv.resize(1);
AssignOrToJSVal(argv.handleAt(0), a0);
return CallFunction_(val1, name, 1, argv.begin(), &jsRet);
return CallFunction_(val, name, 1, argv.begin(), &jsRet);
}
template<typename T0, typename T1>
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1)
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1)
{
JSContext* cx = GetContext();
JSAutoRequest rq(cx);
JS::RootedValue jsRet(cx);
JS::RootedValue val1(cx, val);
JS::AutoValueVector argv(cx);
argv.resize(2);
AssignOrToJSVal(argv.handleAt(0), a0);
AssignOrToJSVal(argv.handleAt(1), a1);
return CallFunction_(val1, name, 2, argv.begin(), &jsRet);
return CallFunction_(val, name, 2, argv.begin(), &jsRet);
}
template<typename T0, typename T1, typename T2>
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2)
bool ScriptInterface::CallFunctionVoid(JS::HandleValue val, const char* name, const T0& a0, const T1& a1, const T2& a2)
{
JSContext* cx = GetContext();
JSAutoRequest rq(cx);
JS::RootedValue jsRet(cx);
JS::RootedValue val1(cx, val);
JS::AutoValueVector argv(cx);
argv.resize(3);
AssignOrToJSVal(argv.handleAt(0), a0);
AssignOrToJSVal(argv.handleAt(1), a1);
AssignOrToJSVal(argv.handleAt(2), a2);
return CallFunction_(val1, name, 3, argv.begin(), &jsRet);
return CallFunction_(val, name, 3, argv.begin(), &jsRet);
}
template<typename T>

View File

@ -40,7 +40,8 @@ class TestScriptConversions : public CxxTest::TestSuite
// We want to convert values to strings, but can't just call toSource() on them
// since they might not be objects. So just use uneval.
std::string source;
TS_ASSERT(script.CallFunction(JS::ObjectValue(*JS_GetGlobalForScopeChain(cx)), "uneval", CScriptVal(v1), source));
JS::RootedValue global(cx, script.GetGlobalObject());
TS_ASSERT(script.CallFunction(global, "uneval", v1, source));
TS_ASSERT_STR_EQUALS(source, expected);
}
@ -56,7 +57,8 @@ class TestScriptConversions : public CxxTest::TestSuite
ScriptInterface::ToJSVal(cx, &v1, value);
std::string source;
TS_ASSERT(script.CallFunction(JS::ObjectValue(*JS_GetGlobalForScopeChain(cx)), "uneval", CScriptVal(v1), source));
JS::RootedValue global(cx, script.GetGlobalObject());
TS_ASSERT(script.CallFunction(global, "uneval", v1, source));
if (expected)
TS_ASSERT_STR_EQUALS(source, expected);

View File

@ -65,14 +65,21 @@ public:
ScriptInterface script1("Test", "Test", g_ScriptRuntime);
ScriptInterface script2("Test", "Test", g_ScriptRuntime);
CScriptVal obj1;
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", obj1));
JSContext* cx1 = script1.GetContext();
JSAutoRequest rq1(cx1);
JS::RootedValue obj1(cx1);
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", &obj1));
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
{
JSContext* cx2 = script2.GetContext();
JSAutoRequest rq2(cx2);
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1));
std::string source;
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({x:123, y:[1, 1.5, \"2\", \"test\", (void 0), null, true, false]})");
std::string source;
TS_ASSERT(script2.CallFunction(obj2, "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({x:123, y:[1, 1.5, \"2\", \"test\", (void 0), null, true, false]})");
}
}
void test_clone_getters()
@ -81,14 +88,22 @@ public:
ScriptInterface script1("Test", "Test", g_ScriptRuntime);
ScriptInterface script2("Test", "Test", g_ScriptRuntime);
CScriptVal obj1;
TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", obj1));
JSContext* cx1 = script1.GetContext();
JSAutoRequest rq1(cx1);
JS::RootedValue obj1(cx1);
TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", &obj1));
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
{
JSContext* cx2 = script2.GetContext();
JSAutoRequest rq2(cx2);
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1));
std::string source;
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}})");
std::string source;
TS_ASSERT(script2.CallFunction(obj2, "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}})");
}
}
void test_clone_cyclic()
@ -96,24 +111,29 @@ public:
ScriptInterface script1("Test", "Test", g_ScriptRuntime);
ScriptInterface script2("Test", "Test", g_ScriptRuntime);
CScriptVal obj1;
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", obj1));
JSContext* cx1 = script1.GetContext();
JSAutoRequest rq1(cx1);
JS::RootedValue obj1(cx1);
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", &obj1));
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
{
JSContext* cx2 = script2.GetContext();
JSAutoRequest rq(cx2);
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1));
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0]
JSContext* cx2 = script2.GetContext();
JSAutoRequest rq(cx2);
JS::RootedValue prop_a(cx2);
JS::RootedValue prop_b(cx2);
JS::RootedValue prop_x1(cx2);
TS_ASSERT(JS_GetProperty(cx2, &(obj2.get().toObject()), "a", prop_a.address()));
TS_ASSERT(JS_GetProperty(cx2, &(obj2.get().toObject()), "b", prop_b.address()));
TS_ASSERT(prop_a.get().isObject());
TS_ASSERT(prop_b.get().isObject());
TS_ASSERT(JS_GetProperty(cx2, &(prop_a.get().toObject()), "0", prop_x1.address()));
TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get());
TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get());
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0]
JS::RootedValue prop_a(cx2);
JS::RootedValue prop_b(cx2);
JS::RootedValue prop_x1(cx2);
TS_ASSERT(script2.GetProperty(obj2, "a", &prop_a));
TS_ASSERT(script2.GetProperty(obj2, "b", &prop_b));
TS_ASSERT(prop_a.isObject());
TS_ASSERT(prop_b.isObject());
TS_ASSERT(script2.GetProperty(prop_a, "0", &prop_x1));
TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get());
TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get());
}
}
/**

View File

@ -639,12 +639,18 @@ ScriptInterface& CSimulation2::GetScriptInterface() const
void CSimulation2::ReplaceSkirmishGlobals()
{
GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "ReplaceSkirmishGlobals");
JSContext* cx = GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject());
GetScriptInterface().CallFunctionVoid(global, "ReplaceSkirmishGlobals");
}
void CSimulation2::InitGame(const CScriptVal& data)
{
GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "InitGame", data);
JSContext* cx = GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject());
GetScriptInterface().CallFunctionVoid(global, "InitGame", data);
}
void CSimulation2::Update(int turnLength)
@ -728,7 +734,10 @@ CScriptVal CSimulation2::GetMapSettings()
void CSimulation2::LoadPlayerSettings(bool newPlayers)
{
GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadPlayerSettings", m->m_MapSettings, newPlayers);
JSContext* cx = GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject());
GetScriptInterface().CallFunctionVoid(global, "LoadPlayerSettings", m->m_MapSettings, newPlayers);
}
void CSimulation2::LoadMapSettings()
@ -736,10 +745,11 @@ void CSimulation2::LoadMapSettings()
JSContext* cx = GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, GetScriptInterface().GetGlobalObject());
JS::RootedValue tmpMapSettings(cx, m->m_MapSettings.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
// Initialize here instead of in Update()
GetScriptInterface().CallFunctionVoid(GetScriptInterface().GetGlobalObject(), "LoadMapSettings", tmpMapSettings);
GetScriptInterface().CallFunctionVoid(global, "LoadMapSettings", tmpMapSettings);
if (!m->m_StartupScript.empty())
GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript);

View File

@ -180,20 +180,32 @@ private:
void Run(JS::HandleValue state, int playerID)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpObj(cx, m_Obj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_Commands.clear();
m_ScriptInterface->CallFunctionVoid(m_Obj.get(), "HandleMessage", state, playerID);
m_ScriptInterface->CallFunctionVoid(tmpObj, "HandleMessage", state, playerID);
}
// overloaded with a sharedAI part.
// javascript can handle both natively on the same function.
void Run(JS::HandleValue state, int playerID, CScriptValRooted SharedAI)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpObj(cx, m_Obj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_Commands.clear();
m_ScriptInterface->CallFunctionVoid(m_Obj.get(), "HandleMessage", state, playerID, SharedAI);
m_ScriptInterface->CallFunctionVoid(tmpObj, "HandleMessage", state, playerID, SharedAI);
}
void InitAI(JS::HandleValue state, CScriptValRooted SharedAI)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpObj(cx, m_Obj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_Commands.clear();
m_ScriptInterface->CallFunctionVoid(m_Obj.get(), "Init", state, m_Player, SharedAI);
m_ScriptInterface->CallFunctionVoid(tmpObj, "Init", state, m_Player, SharedAI);
}
CAIWorker& m_Worker;
@ -470,7 +482,9 @@ public:
m_ScriptInterface->SetProperty(state, "passabilityMap", m_PassabilityMapVal, true);
m_ScriptInterface->SetProperty(state, "territoryMap", m_TerritoryMapVal, true);
m_ScriptInterface->CallFunctionVoid(m_SharedAIObj.get(), "init", state);
JS::RootedValue tmpSharedAIObj(cx, m_SharedAIObj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
m_ScriptInterface->CallFunctionVoid(tmpSharedAIObj, "init", state);
for (size_t i = 0; i < m_Players.size(); ++i)
{
@ -593,8 +607,9 @@ public:
serializer.Bool("useSharedScript", m_HasSharedComponent);
if (m_HasSharedComponent)
{
CScriptVal sharedData;
if (!m_ScriptInterface->CallFunction(m_SharedAIObj.get(), "Serialize", sharedData))
JS::RootedValue sharedData(cx);
JS::RootedValue tmpSharedAIObj(cx, m_SharedAIObj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
if (!m_ScriptInterface->CallFunction(tmpSharedAIObj, "Serialize", &sharedData))
LOGERROR(L"AI shared script Serialize call failed");
serializer.ScriptVal("sharedData", sharedData);
}
@ -607,7 +622,7 @@ public:
serializer.NumberU32_Unbounded("num commands", (u32)m_Players[i]->m_Commands.size());
for (size_t j = 0; j < m_Players[i]->m_Commands.size(); ++j)
{
CScriptVal val = m_ScriptInterface->ReadStructuredClone(m_Players[i]->m_Commands[j]);
JS::RootedValue val(cx, m_ScriptInterface->ReadStructuredClone(m_Players[i]->m_Commands[j]));
serializer.ScriptVal("command", val);
}
@ -654,9 +669,10 @@ public:
TryLoadSharedComponent(false);
if (m_HasSharedComponent)
{
CScriptVal sharedData;
deserializer.ScriptVal("sharedData", sharedData);
if (!m_ScriptInterface->CallFunctionVoid(m_SharedAIObj.get(), "Deserialize", sharedData))
JS::RootedValue sharedData(cx);
JS::RootedValue tmpSharedAIObj(cx, m_SharedAIObj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31
deserializer.ScriptVal("sharedData", &sharedData);
if (!m_ScriptInterface->CallFunctionVoid(tmpSharedAIObj, "Deserialize", sharedData))
LOGERROR(L"AI shared script Deserialize call failed");
}
@ -676,9 +692,9 @@ public:
m_Players.back()->m_Commands.reserve(numCommands);
for (size_t j = 0; j < numCommands; ++j)
{
CScriptVal val;
deserializer.ScriptVal("command", val);
m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val.get()));
JS::RootedValue val(cx);
deserializer.ScriptVal("command", &val);
m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val));
}
// TODO: this is yucky but necessary while the AIs are sharing data between contexts;
@ -691,8 +707,8 @@ public:
bool hasCustomDeserialize = m_ScriptInterface->HasProperty(tmpPlayerObj, "Deserialize");
if (hasCustomDeserialize)
{
CScriptVal scriptData;
deserializer.ScriptVal("data", scriptData);
JS::RootedValue scriptData(cx);
deserializer.ScriptVal("data", &scriptData);
if (m_Players[i]->m_UseSharedComponent)
{
if (!m_ScriptInterface->CallFunctionVoid(tmpPlayerObj, "Deserialize", scriptData, m_SharedAIObj))
@ -705,7 +721,8 @@ public:
}
else
{
deserializer.ScriptVal("data", tmpPlayerObj.get());
deserializer.ScriptVal("data", &tmpPlayerObj);
m_Players.back()->m_Obj = CScriptValRooted(cx, tmpPlayerObj);
}
}
}
@ -761,7 +778,8 @@ private:
if (m_HasSharedComponent)
{
PROFILE3("AI run shared component");
m_ScriptInterface->CallFunctionVoid(m_SharedAIObj.get(), "onUpdate", state);
JS::RootedValue tmpSharedAIObj(cx, m_SharedAIObj.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31
m_ScriptInterface->CallFunctionVoid(tmpSharedAIObj, "onUpdate", state);
}
for (size_t i = 0; i < m_Players.size(); ++i)

View File

@ -50,25 +50,33 @@ public:
virtual void Serialize(ISerializer& serialize)
{
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpRoot(cx); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
serialize.NumberU32_Unbounded("num commands", (u32)m_LocalQueue.size());
for (size_t i = 0; i < m_LocalQueue.size(); ++i)
{
tmpRoot.set(m_LocalQueue[i].data.get());
serialize.NumberI32_Unbounded("player", m_LocalQueue[i].player);
serialize.ScriptVal("data", m_LocalQueue[i].data.get());
serialize.ScriptVal("data", tmpRoot);
}
}
virtual void Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserialize)
{
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
JSAutoRequest rq(cx);
u32 numCmds;
deserialize.NumberU32_Unbounded("num commands", numCmds);
for (size_t i = 0; i < numCmds; ++i)
{
i32 player;
CScriptValRooted data;
JS::RootedValue data(cx);
deserialize.NumberI32_Unbounded("player", player);
deserialize.ScriptVal("data", data);
SimulationCommand c = { player, data };
deserialize.ScriptVal("data", &data);
SimulationCommand c = { player, CScriptValRooted(cx, data) };
m_LocalQueue.push_back(c);
}
}
@ -96,20 +104,23 @@ public:
virtual void FlushTurn(const std::vector<SimulationCommand>& commands)
{
ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue global(cx, scriptInterface.GetGlobalObject());
std::vector<SimulationCommand> localCommands;
m_LocalQueue.swap(localCommands);
for (size_t i = 0; i < localCommands.size(); ++i)
{
bool ok = scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "ProcessCommand", localCommands[i].player, localCommands[i].data);
bool ok = scriptInterface.CallFunctionVoid(global, "ProcessCommand", localCommands[i].player, localCommands[i].data);
if (!ok)
LOGERROR(L"Failed to call ProcessCommand() global script function");
}
for (size_t i = 0; i < commands.size(); ++i)
{
bool ok = scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "ProcessCommand", commands[i].player, commands[i].data);
bool ok = scriptInterface.CallFunctionVoid(global, "ProcessCommand", commands[i].player, commands[i].data);
if (!ok)
LOGERROR(L"Failed to call ProcessCommand() global script function");
}

View File

@ -64,11 +64,15 @@ void CComponentTypeScript::Deinit()
void CComponentTypeScript::HandleMessage(const CMessage& msg, bool global)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
const char* name = global ? msg.GetScriptGlobalHandlerName() : msg.GetScriptHandlerName();
CScriptVal msgVal = msg.ToJSValCached(m_ScriptInterface);
JS::RootedValue msgVal(cx, msg.ToJSValCached(m_ScriptInterface));
if (!m_ScriptInterface.CallFunctionVoid(m_Instance.get(), name, msgVal))
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
if (!m_ScriptInterface.CallFunctionVoid(tmpInstance, name, msgVal))
LOGERROR(L"Script message handler %hs failed", name);
}
@ -77,19 +81,23 @@ void CComponentTypeScript::Serialize(ISerializer& serialize)
// If the component set Serialize = null, then do no work here
if (m_HasNullSerialize)
return;
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue tmpInstance(cx, m_Instance.get()); // TODO: Check if this temporary root can be removed after SpiderMonkey 31 upgrade
// Support a custom "Serialize" function, which returns a new object that will be
// serialized instead of the component itself
if (m_HasCustomSerialize)
{
CScriptVal val;
if (!m_ScriptInterface.CallFunction(m_Instance.get(), "Serialize", val))
JS::RootedValue val(cx);
if (!m_ScriptInterface.CallFunction(tmpInstance, "Serialize", &val))
LOGERROR(L"Script Serialize call failed");
serialize.ScriptVal("object", val);
}
else
{
serialize.ScriptVal("object", m_Instance.get());
serialize.ScriptVal("object", tmpInstance);
}
}
@ -107,7 +115,7 @@ void CComponentTypeScript::Deserialize(const CParamNode& paramNode, IDeserialize
// If Serialize = null, we'll still call Deserialize but with undefined argument
if (!m_HasNullSerialize)
deserialize.ScriptVal("object", val.get());
deserialize.ScriptVal("object", &val);
if (!m_ScriptInterface.CallFunctionVoid(tmpInstance, "Deserialize", val))
LOGERROR(L"Script Deserialize call failed");
@ -118,7 +126,7 @@ void CComponentTypeScript::Deserialize(const CParamNode& paramNode, IDeserialize
{
// Use ScriptObjectAppend so we don't lose the carefully-constructed
// prototype/parent of this object
deserialize.ScriptObjectAppend("object", m_Instance.getRef());
deserialize.ScriptObjectAppend("object", tmpInstance);
}
}

View File

@ -52,12 +52,17 @@ public:
// void CallVoid(const char* funcname);
// template<typename T0> void CallVoid(const char* funcname, const T0& a0);
// ...
// TODO: Check if these temporary roots can be removed after SpiderMonkey 31 upgrade
#define OVERLOADS(z, i, data) \
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(i, typename T)> \
R Call(const char* funcname BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(i, const T, &a)) \
{ \
JSContext* cx = m_ScriptInterface.GetContext(); \
JSAutoRequest rq(cx); \
JS::RootedValue tmpInstance(cx, m_Instance.get()); \
R ret; \
if (m_ScriptInterface.CallFunction(m_Instance.get(), funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a), ret)) \
if (m_ScriptInterface.CallFunction(tmpInstance, funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a), ret)) \
return ret; \
LOGERROR(L"Error calling component script function %hs", funcname); \
return R(); \
@ -65,7 +70,10 @@ public:
BOOST_PP_IF(i, template<, ) BOOST_PP_ENUM_PARAMS(i, typename T) BOOST_PP_IF(i, >, ) \
void CallVoid(const char* funcname BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(i, const T, &a)) \
{ \
if (m_ScriptInterface.CallFunctionVoid(m_Instance.get(), funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a))) \
JSContext* cx = m_ScriptInterface.GetContext(); \
JSAutoRequest rq(cx); \
JS::RootedValue tmpInstance(cx, m_Instance.get()); \
if (m_ScriptInterface.CallFunctionVoid(tmpInstance, funcname BOOST_PP_ENUM_TRAILING_PARAMS(i, a))) \
return; \
LOGERROR(L"Error calling component script function %hs", funcname); \
}

View File

@ -60,7 +60,7 @@ CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(ScriptInterface& script
{
}
void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
@ -119,7 +119,8 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
// Now handle its array buffer
// this may be a backref, since ArrayBuffers can be shared by multiple views
HandleScriptVal(JS::ObjectValue(*JS_GetArrayBufferViewBuffer(obj)));
JS::RootedValue bufferVal(cx, JS::ObjectValue(*JS_GetArrayBufferViewBuffer(obj)));
HandleScriptVal(bufferVal);
break;
}
else if (JS_IsArrayBufferObject(obj))
@ -182,10 +183,10 @@ void CBinarySerializerScriptImpl::HandleScriptVal(jsval val)
// If serialize is null, so don't serialize anything more
if (!serialize.isNull())
{
CScriptValRooted data;
if (!m_ScriptInterface.CallFunction(val, "Serialize", data))
JS::RootedValue data(cx);
if (!m_ScriptInterface.CallFunction(val, "Serialize", &data))
throw PSERROR_Serialize_ScriptError("Prototype Serialize function failed");
HandleScriptVal(data.get());
HandleScriptVal(data);
}
break;
}

View File

@ -58,7 +58,7 @@ public:
CBinarySerializerScriptImpl(ScriptInterface& scriptInterface, ISerializer& serializer);
void ScriptString(const char* name, JSString* string);
void HandleScriptVal(jsval val);
void HandleScriptVal(JS::HandleValue val);
void SetSerializablePrototypes(std::map<JSObject*, std::wstring>& prototypes);
private:
ScriptInterface& m_ScriptInterface;
@ -185,7 +185,7 @@ protected:
m_Impl.Put(name, (u8*)value.data(), value.length());
}
virtual void PutScriptVal(const char* UNUSED(name), jsval value)
virtual void PutScriptVal(const char* UNUSED(name), JS::HandleValue value)
{
m_ScriptImpl->HandleScriptVal(value);
}

View File

@ -147,7 +147,7 @@ void CDebugSerializer::PutString(const char* name, const std::string& value)
m_Stream << INDENT << name << ": " << "\"" << escaped << "\"\n";
}
void CDebugSerializer::PutScriptVal(const char* name, jsval value)
void CDebugSerializer::PutScriptVal(const char* name, JS::HandleValue value)
{
std::wstring source = m_ScriptInterface.ToString(value, true);

View File

@ -54,7 +54,7 @@ protected:
virtual void PutNumber(const char* name, fixed value);
virtual void PutBool(const char* name, bool value);
virtual void PutString(const char* name, const std::string& value);
virtual void PutScriptVal(const char* name, jsval value);
virtual void PutScriptVal(const char* name, JS::HandleValue value);
virtual void PutRaw(const char* name, const u8* data, size_t len);
private:

View File

@ -56,12 +56,10 @@ public:
virtual void String(const char* name, std::wstring& out, uint32_t minlength, uint32_t maxlength);
/// Deserialize a jsval, replacing 'out'
virtual void ScriptVal(const char* name, jsval& out) = 0;
virtual void ScriptVal(const char* name, CScriptVal& out) = 0;
virtual void ScriptVal(const char* name, CScriptValRooted& out) = 0;
virtual void ScriptVal(const char* name, JS::MutableHandleValue out) = 0;
/// Deserialize an object jsval, appending properties to object 'obj'
virtual void ScriptObjectAppend(const char* name, jsval& obj) = 0;
/// Deserialize an object value, appending properties to object 'objVal'
virtual void ScriptObjectAppend(const char* name, JS::HandleValue objVal) = 0;
/// Deserialize a JSString
virtual void ScriptString(const char* name, JSString*& out) = 0;

View File

@ -92,21 +92,11 @@ void ISerializer::String(const char* name, const std::wstring& value, uint32_t m
PutString(name, str);
}
void ISerializer::ScriptVal(const char* name, jsval value)
void ISerializer::ScriptVal(const char* name, JS::HandleValue value)
{
PutScriptVal(name, value);
}
void ISerializer::ScriptVal(const char* name, CScriptVal value)
{
PutScriptVal(name, value.get());
}
void ISerializer::ScriptVal(const char* name, CScriptValRooted value)
{
PutScriptVal(name, value.get());
}
void ISerializer::RawBytes(const char* name, const u8* data, size_t len)
{
PutRaw(name, data, len);

View File

@ -218,22 +218,8 @@ public:
/**
* Serialize a jsval.
* The value must not contain any unserializable values (like functions).
* Likely to trigger GC, so value must be rooted.
*/
void ScriptVal(const char* name, jsval value);
/**
* Serialize a CScriptVal.
* The value must not contain any unserializable values (like functions).
* Likely to trigger GC, so value must be rooted.
*/
void ScriptVal(const char* name, CScriptVal value);
/**
* Serialize a CScriptValRooted.
* The value must not contain any unserializable values (like functions).
*/
void ScriptVal(const char* name, CScriptValRooted value);
void ScriptVal(const char* name, JS::HandleValue value);
/**
* Serialize a stream of bytes.
@ -269,7 +255,7 @@ protected:
virtual void PutNumber(const char* name, fixed value) = 0;
virtual void PutBool(const char* name, bool value) = 0;
virtual void PutString(const char* name, const std::string& value) = 0;
virtual void PutScriptVal(const char* name, jsval value) = 0;
virtual void PutScriptVal(const char* name, JS::HandleValue value) = 0;
virtual void PutRaw(const char* name, const u8* data, size_t len) = 0;
};

View File

@ -190,19 +190,6 @@ struct SerializeBool
}
};
struct SerializeScriptVal
{
void operator()(ISerializer& serialize, const char* name, CScriptValRooted value)
{
serialize.ScriptVal(name, value);
}
void operator()(IDeserializer& deserialize, const char* name, CScriptValRooted& value)
{
deserialize.ScriptVal(name, value);
}
};
struct SerializeWaypoint
{
void operator()(ISerializer& serialize, const char* UNUSED(name), const ICmpPathfinder::Waypoint& value)

View File

@ -122,10 +122,9 @@ void CStdDeserializer::FreeScriptBackrefs()
////////////////////////////////////////////////////////////////
jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* appendParent)
jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject appendParent)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
uint8_t type;
@ -142,20 +141,20 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
case SCRIPT_TYPE_OBJECT:
case SCRIPT_TYPE_OBJECT_PROTOTYPE:
{
JSObject* obj;
JS::RootedObject obj(cx);
if (appendParent)
{
obj = appendParent;
obj.set(appendParent);
}
else if (type == SCRIPT_TYPE_ARRAY)
{
u32 length;
NumberU32_Unbounded("array length", length);
obj = JS_NewArrayObject(cx, length, NULL);
obj.set(JS_NewArrayObject(cx, length, NULL));
}
else if (type == SCRIPT_TYPE_OBJECT)
{
obj = JS_NewObject(cx, NULL, NULL, NULL);
obj.set(JS_NewObject(cx, NULL, NULL, NULL));
}
else // SCRIPT_TYPE_OBJECT_PROTOTYPE
{
@ -163,18 +162,17 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
String("proto name", prototypeName, 0, 256);
// Get constructor object
JSObject* proto = GetSerializablePrototype(prototypeName);
JS::RootedObject proto(cx, GetSerializablePrototype(prototypeName));
if (!proto)
throw PSERROR_Deserialize_ScriptError("Failed to find serializable prototype for object");
JSObject* parent = JS_GetParent(proto);
JS::RootedObject parent(cx, JS_GetParent(proto));
if (!proto || !parent)
throw PSERROR_Deserialize_ScriptError();
obj = JS_NewObject(cx, NULL, proto, parent);
obj.set(JS_NewObject(cx, NULL, proto, parent));
if (!obj)
throw PSERROR_Deserialize_ScriptError("JS_NewObject failed");
CScriptValRooted objRoot(cx, JS::ObjectValue(*obj));
// Does it have custom Deserialize function?
// if so, we let it handle the deserialized data, rather than adding properties directly
@ -190,11 +188,12 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
bool hasNullSerialize = hasCustomSerialize && JSVAL_IS_NULL(serialize);
// If Serialize is null, we'll still call Deserialize but with undefined argument
CScriptValRooted data;
JS::RootedValue data(cx);
if (!hasNullSerialize)
ScriptVal("data", data);
ScriptVal("data", &data);
m_ScriptInterface.CallFunctionVoid(JS::ObjectValue(*obj), "Deserialize", data);
JS::RootedValue objVal(cx, JS::ObjectValue(*obj));
m_ScriptInterface.CallFunctionVoid(objVal, "Deserialize", data);
AddScriptBackref(obj);
@ -215,9 +214,7 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
{
utf16string propname;
ReadStringUTF16("prop name", propname);
JS::RootedValue propval(cx, ReadScriptVal("prop value", NULL));
CScriptValRooted propvalRoot(cx, propval);
JS::RootedValue propval(cx, ReadScriptVal("prop value", JS::NullPtr()));
if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), propval.address()))
throw PSERROR_Deserialize_ScriptError();
@ -325,7 +322,7 @@ jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JSObject* append
u32 arrayTag = ReserveScriptBackref();
// Get buffer object
jsval bufferVal = ReadScriptVal("buffer", NULL);
jsval bufferVal = ReadScriptVal("buffer", JS::NullPtr());
if (!bufferVal.isObject())
throw PSERROR_Deserialize_ScriptError();
@ -420,27 +417,21 @@ void CStdDeserializer::ScriptString(const char* name, JSString*& out)
throw PSERROR_Deserialize_ScriptError("JS_NewUCStringCopyN failed");
}
void CStdDeserializer::ScriptVal(const char* name, jsval& out)
void CStdDeserializer::ScriptVal(const char* name, JS::MutableHandleValue out)
{
out = ReadScriptVal(name, NULL);
out.set(ReadScriptVal(name, JS::NullPtr()));
}
void CStdDeserializer::ScriptVal(const char* name, CScriptVal& out)
void CStdDeserializer::ScriptObjectAppend(const char* name, JS::HandleValue objVal)
{
out = ReadScriptVal(name, NULL);
}
void CStdDeserializer::ScriptVal(const char* name, CScriptValRooted& out)
{
out = CScriptValRooted(m_ScriptInterface.GetContext(), ReadScriptVal(name, NULL));
}
void CStdDeserializer::ScriptObjectAppend(const char* name, jsval& obj)
{
if (!obj.isObject())
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
if (!objVal.isObject())
throw PSERROR_Deserialize_ScriptError();
ReadScriptVal(name, JSVAL_TO_OBJECT(obj));
JS::RootedObject obj(cx, &objVal.toObject());
ReadScriptVal(name, obj);
}
void CStdDeserializer::SetSerializablePrototypes(std::map<std::wstring, JSObject*>& prototypes)

View File

@ -31,10 +31,8 @@ public:
CStdDeserializer(ScriptInterface& scriptInterface, std::istream& stream);
virtual ~CStdDeserializer();
virtual void ScriptVal(const char* name, jsval& out);
virtual void ScriptVal(const char* name, CScriptVal& out);
virtual void ScriptVal(const char* name, CScriptValRooted& out);
virtual void ScriptObjectAppend(const char* name, jsval& obj);
virtual void ScriptVal(const char* name, JS::MutableHandleValue out);
virtual void ScriptObjectAppend(const char* name, JS::HandleValue objVal);
virtual void ScriptString(const char* name, JSString*& out);
virtual std::istream& GetStream();
@ -46,7 +44,7 @@ protected:
virtual void Get(const char* name, u8* data, size_t len);
private:
jsval ReadScriptVal(const char* name, JSObject* appendParent);
jsval ReadScriptVal(const char* name, JS::HandleObject appendParent);
void ReadStringUTF16(const char* name, utf16string& str);
virtual void AddScriptBackref(JSObject* obj);

View File

@ -267,8 +267,11 @@ public:
void test_script_basic()
{
ScriptInterface script("Test", "Test", g_ScriptRuntime);
CScriptVal obj;
TS_ASSERT(script.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", obj));
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue obj(cx);
TS_ASSERT(script.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", &obj));
{
std::stringstream stream;
@ -326,8 +329,8 @@ public:
CStdDeserializer deserialize(script, stream);
jsval newobj;
deserialize.ScriptVal("script", newobj);
JS::RootedValue newobj(cx);
deserialize.ScriptVal("script", &newobj);
TS_ASSERT(stream.good());
TS_ASSERT_EQUALS(stream.peek(), EOF);
@ -340,8 +343,11 @@ public:
void helper_script_roundtrip(const char* msg, const char* input, const char* expected, size_t expstreamlen = 0, const char* expstream = NULL)
{
ScriptInterface script("Test", "Test", g_ScriptRuntime);
CScriptVal obj;
TSM_ASSERT(msg, script.Eval(input, obj));
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue obj(cx);
TSM_ASSERT(msg, script.Eval(input, &obj));
std::stringstream stream;
CStdSerializer serialize(script, stream);
@ -355,8 +361,8 @@ public:
CStdDeserializer deserialize(script, stream);
jsval newobj;
deserialize.ScriptVal("script", newobj);
JS::RootedValue newobj(cx);
deserialize.ScriptVal("script", &newobj);
TSM_ASSERT(msg, stream.good());
TSM_ASSERT_EQUALS(msg, stream.peek(), EOF);
@ -567,14 +573,17 @@ public:
void test_script_exceptions()
{
ScriptInterface script("Test", "Test", g_ScriptRuntime);
CScriptVal obj;
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue obj(cx);
std::stringstream stream;
CStdSerializer serialize(script, stream);
TestLogger logger;
TS_ASSERT(script.Eval("([1, 2, function () { }])", obj));
TS_ASSERT(script.Eval("([1, 2, function () { }])", &obj));
TS_ASSERT_THROWS(serialize.ScriptVal("script", obj), PSERROR_Serialize_InvalidScriptValue);
}
@ -599,8 +608,11 @@ public:
const char* input = "var x = {}; for (var i=0;i<256;++i) x[i]=Math.pow(i, 2); x";
ScriptInterface script("Test", "Test", g_ScriptRuntime);
CScriptVal obj;
TS_ASSERT(script.Eval(input, obj));
JSContext* cx = script.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue obj(cx);
TS_ASSERT(script.Eval(input, &obj));
for (size_t i = 0; i < 256; ++i)
{
@ -611,8 +623,8 @@ public:
CStdDeserializer deserialize(script, stream);
jsval newobj;
deserialize.ScriptVal("script", newobj);
JS::RootedValue newobj(cx);
deserialize.ScriptVal("script", &newobj);
TS_ASSERT(stream.good());
TS_ASSERT_EQUALS(stream.peek(), EOF);