Rewrite the clone-object-between-JS-contexts code (for GUI<->simulation interface) to be more efficient.

Delete unnecessary unused incomplete profiler scripting support.
Clean up some JSAPI code to use newer features.
Display simulation script functions in the profiler.

This was SVN commit r7503.
This commit is contained in:
Ykkrosh 2010-05-05 22:36:35 +00:00
parent 3238cd8bc0
commit fe53bce3b1
13 changed files with 298 additions and 169 deletions

View File

@ -57,6 +57,7 @@ CGUI
#include "ps/Profile.h"
#include "scripting/ScriptingHost.h"
#include "scripting/JSConversions.h"
#include "ps/Hotkey.h"
#include "ps/Globals.h"
#include "ps/Filesystem.h"

View File

@ -25,6 +25,7 @@
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "ps/XML/Xeromyces.h"
#include "scripting/ScriptingHost.h"
#include "scriptinterface/ScriptInterface.h"
CGUIManager* g_GUI = NULL;

View File

@ -68,49 +68,6 @@ bool IsNewSimulation(void* UNUSED(cbdata))
return g_UseSimulation2;
}
// TODO: this should probably be moved into ScriptInterface in case anyone else wants to use it
static jsval CloneValueBetweenContexts(JSContext* cxFrom, JSContext* cxTo, jsval val)
{
if (JSVAL_IS_INT(val) || JSVAL_IS_BOOLEAN(val) || JSVAL_IS_NULL(val) || JSVAL_IS_VOID(val))
return val;
if (JSVAL_IS_DOUBLE(val))
{
jsval rval;
if (JS_NewNumberValue(cxTo, *JSVAL_TO_DOUBLE(val), &rval))
return rval;
else
return JSVAL_VOID;
}
if (JSVAL_IS_STRING(val))
{
JSString* str = JS_NewUCStringCopyN(cxTo, JS_GetStringChars(JSVAL_TO_STRING(val)), JS_GetStringLength(JSVAL_TO_STRING(val)));
if (str == NULL)
return JSVAL_VOID;
return STRING_TO_JSVAL(str);
}
if (JSVAL_IS_OBJECT(val))
{
jsval source;
if (!JS_CallFunctionName(cxFrom, JSVAL_TO_OBJECT(val), "toSource", 0, NULL, &source))
return JSVAL_VOID;
if (!JSVAL_IS_STRING(source))
return JSVAL_VOID;
JS_AddRoot(cxFrom, &source);
jsval rval;
JSBool ok = JS_EvaluateUCScript(cxTo, JS_GetGlobalObject(cxTo),
JS_GetStringChars(JSVAL_TO_STRING(source)), JS_GetStringLength(JSVAL_TO_STRING(source)),
"(CloneValueBetweenContexts)", 0, &rval);
JS_RemoveRoot(cxFrom, &source);
return ok ? rval : JSVAL_VOID;
}
LOGERROR(L"CloneValueBetweenContexts: value is of unexpected type");
return JSVAL_VOID;
}
CScriptVal GuiInterfaceCall(void* cbdata, std::wstring name, CScriptVal data)
{
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
@ -128,10 +85,8 @@ CScriptVal GuiInterfaceCall(void* cbdata, std::wstring name, CScriptVal data)
if (g_Game && g_Game->GetLocalPlayer())
player = g_Game->GetLocalPlayer()->GetPlayerID();
JSContext* cxGui = guiManager->GetScriptInterface().GetContext();
JSContext* cxSim = sim->GetScriptInterface().GetContext();
CScriptVal ret = gui->ScriptCall(player, name, CloneValueBetweenContexts(cxGui, cxSim, data.get()));
return CloneValueBetweenContexts(cxSim, cxGui, ret.get());
CScriptVal ret = gui->ScriptCall(player, name, sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), data.get()));
return guiManager->GetScriptInterface().CloneValueFromOtherContext(sim->GetScriptInterface(), ret.get());
}
void PostNetworkCommand(void* cbdata, CScriptVal cmd)
@ -151,7 +106,7 @@ void PostNetworkCommand(void* cbdata, CScriptVal cmd)
if (g_Game && g_Game->GetLocalPlayer())
player = g_Game->GetLocalPlayer()->GetPlayerID();
jsval cmd2 = CloneValueBetweenContexts(guiManager->GetScriptInterface().GetContext(), sim->GetScriptInterface().GetContext(), cmd.get());
jsval cmd2 = sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), cmd.get());
queue->PushClientCommand(player, cmd2);
// TODO: This shouldn't call Push, it should send the message to the network layer

View File

@ -444,7 +444,6 @@ static void RegisterJavascriptInterfaces()
// ps
JSI_Console::init();
CProfileNode::ScriptingInit();
CGameAttributes::ScriptingInit();
CPlayerSlot::ScriptingInit();
CPlayer::ScriptingInit();

View File

@ -581,18 +581,6 @@ bool CProfileNode::Return()
return( recursion == 0 );
}
void CProfileNode::ScriptingInit()
{
AddProperty( L"name", (IJSObject::GetFn)&CProfileNode::JS_GetName );
/*
AddReadOnlyClassProperty( L"callsTotal", &CProfileNode::calls_total );
AddReadOnlyClassProperty( L"callsPerFrame", &CProfileNode::calls_frame_last );
AddReadOnlyClassProperty( L"timeTotal", &CProfileNode::time_total );
AddReadOnlyClassProperty( L"timePerFrame", &CProfileNode::time_frame_last );
*/
CJSObject<CProfileNode, true>::ScriptingInit( "ProfilerNode" );
}
CProfileManager::CProfileManager()
{
root = new CProfileNode( "root", NULL );

View File

@ -24,8 +24,6 @@
#include <vector>
#include "Singleton.h"
#include "scripting/ScriptableObject.h"
#define PROFILE_AMORTIZE
#define PROFILE_AMORTIZE_FRAMES 50
@ -33,7 +31,10 @@
class CProfileManager;
class CProfileNodeTable;
class CProfileNode : public CJSObject<CProfileNode, true>
class CStr8;
class CStrW;
class CProfileNode
{
friend class CProfileManager;
friend class CProfileNodeTable;
@ -112,11 +113,6 @@ public:
void Call();
// Leaves the node. Returns true if the node has actually been left
bool Return();
// Javascript stuff...
static void ScriptingInit();
jsval JS_GetName(JSContext*) { return( ToJSVal( CStrW( name ) ) ); }
};
class CProfileManager : public Singleton<CProfileManager>

View File

@ -0,0 +1,43 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 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
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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/>.
*/
#include "js/jsapi.h"
// Exception-safety and GC-safety wrapper for JSIdArray
// (TODO: it'd be nicer to use the existing js::AutoIdArray but currently that
// requires a load of semi-private SpiderMonkey headers that cause a load of compile warnings)
class IdArrayWrapper
{
JSContext* m_cx;
JSIdArray* m_ida;
public:
IdArrayWrapper(JSContext* cx, JSIdArray* ida) :
m_cx(cx), m_ida(ida)
{
for (jsint i = 0; i < m_ida->length; ++i)
if (!JS_AddRoot(m_cx, &m_ida->vector[i]))
debug_warn(L"JS_AddRoot failed");
}
~IdArrayWrapper()
{
for (jsint i = 0; i < m_ida->length; ++i)
if (!JS_RemoveRoot(m_cx, &m_ida->vector[i]))
debug_warn(L"JS_RemoveRoot failed");
JS_DestroyIdArray(m_cx, m_ida);
}
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,15 +18,15 @@
#include "precompiled.h"
#include "ScriptInterface.h"
#include "AutoRooters.h"
#include "lib/debug.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "ps/utf16string.h"
#include <cassert>
#include "js/jsapi.h"
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
@ -35,6 +35,16 @@
const int RUNTIME_SIZE = 4 * 1024 * 1024; // TODO: how much memory is needed?
const int STACK_CHUNK_SIZE = 8192;
#ifdef NDEBUG
#define ENABLE_SCRIPT_PROFILING 0
#else
#define ENABLE_SCRIPT_PROFILING 1
#endif
#ifdef ENABLE_SCRIPT_PROFILING
#include "js/jsdbgapi.h"
#endif
////////////////////////////////////////////////////////////////
struct ScriptInterface_impl
@ -102,6 +112,34 @@ JSBool print(JSContext* cx, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsva
} // anonymous namespace
#if ENABLE_SCRIPT_PROFILING
static void* jshook_script(JSContext* UNUSED(cx), JSStackFrame* UNUSED(fp), JSBool before, JSBool* UNUSED(ok), void* closure)
{
if (before)
g_Profiler.StartScript("script invocation");
else
g_Profiler.Stop();
return closure;
}
static void* jshook_function(JSContext* cx, JSStackFrame* fp, JSBool before, JSBool* UNUSED(ok), void* closure)
{
JSFunction* fn = JS_GetFrameFunction(cx, fp);
if (before)
{
if (fn)
g_Profiler.StartScript(JS_GetFunctionName(fn));
else
g_Profiler.StartScript("function invocation");
}
else
g_Profiler.Stop();
return closure;
}
#endif
ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, JSContext* cx)
{
JSBool ok;
@ -117,10 +155,17 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, JSContex
m_rt = JS_NewRuntime(RUNTIME_SIZE);
debug_assert(m_rt); // TODO: error handling
#if ENABLE_SCRIPT_PROFILING
// Register our script and function handlers - note: docs say they don't like
// nulls passed as closures, nor should they return nulls.
JS_SetExecuteHook(m_rt, jshook_script, this);
JS_SetCallHook(m_rt, jshook_function, this);
#endif
m_cx = JS_NewContext(m_rt, STACK_CHUNK_SIZE);
debug_assert(m_cx);
// For GC debugging with SpiderMonkey 1.8+:
// For GC debugging:
// JS_SetGCZeal(m_cx, 2);
JS_SetContextPrivate(m_cx, NULL);
@ -192,7 +237,7 @@ void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs)
m->Register(name, fptr, (uintN)nargs);
}
JSContext* ScriptInterface::GetContext()
JSContext* ScriptInterface::GetContext() const
{
return m->m_cx;
}
@ -498,3 +543,123 @@ void* ScriptInterface::GetPrivate(JSContext* cx, JSObject* obj)
// TODO: use JS_GetInstancePrivate
return JS_GetPrivate(cx, obj);
}
class ValueCloner
{
public:
ValueCloner(JSContext* cxFrom, JSContext* cxTo) : cxFrom(cxFrom), cxTo(cxTo)
{
}
// Return the cloned object (or an already-computed object if we've cloned val before)
jsval GetOrClone(jsval val)
{
if (!JSVAL_IS_GCTHING(val) || JSVAL_IS_NULL(val))
return val;
std::map<jsval, jsval>::iterator it = m_Mapping.find(val);
if (it != m_Mapping.end())
return it->second;
m_ValuesOld.push_back(CScriptValRooted(cxFrom, val)); // root it so our mapping doesn't get invalidated
return Clone(val);
}
private:
#define CLONE_REQUIRE(expr, msg) if (!(expr)) { debug_warn(L"Internal error in CloneValueFromOtherContext: " msg); return JSVAL_VOID; }
// Clone a new value (and root it and add it to the mapping)
jsval Clone(jsval val)
{
if (JSVAL_IS_DOUBLE(val))
{
jsval rval;
CLONE_REQUIRE(JS_NewNumberValue(cxTo, *JSVAL_TO_DOUBLE(val), &rval), L"JS_NewNumberValue");
m_Mapping[val] = rval;
m_ValuesNew.push_back(CScriptValRooted(cxTo, rval));
return rval;
}
if (JSVAL_IS_STRING(val))
{
JSString* str = JS_NewUCStringCopyN(cxTo, JS_GetStringChars(JSVAL_TO_STRING(val)), JS_GetStringLength(JSVAL_TO_STRING(val)));
CLONE_REQUIRE(str, L"JS_NewUCStringCopyN");
jsval rval = STRING_TO_JSVAL(str);
m_Mapping[val] = rval;
m_ValuesNew.push_back(CScriptValRooted(cxTo, rval));
return rval;
}
debug_assert(JSVAL_IS_OBJECT(val));
JSObject* newObj;
if (JS_IsArrayObject(cxFrom, JSVAL_TO_OBJECT(val)))
{
jsuint length;
CLONE_REQUIRE(JS_GetArrayLength(cxFrom, JSVAL_TO_OBJECT(val), &length), L"JS_GetArrayLength");
newObj = JS_NewArrayObject(cxTo, length, NULL);
CLONE_REQUIRE(newObj, L"JS_NewArrayObject");
}
else
{
newObj = JS_NewObject(cxTo, NULL, NULL, NULL);
CLONE_REQUIRE(newObj, L"JS_NewObject");
}
m_Mapping[val] = OBJECT_TO_JSVAL(newObj);
m_ValuesNew.push_back(CScriptValRooted(cxTo, OBJECT_TO_JSVAL(newObj)));
JSIdArray* ida = JS_Enumerate(cxFrom, JSVAL_TO_OBJECT(val));
CLONE_REQUIRE(ida, L"JS_Enumerate");
IdArrayWrapper idaWrapper(cxFrom, ida);
for (jsint i = 0; i < ida->length; ++i)
{
jsid id = ida->vector[i];
jsval idval, propval;
CLONE_REQUIRE(JS_IdToValue(cxFrom, id, &idval), L"JS_IdToValue");
CLONE_REQUIRE(JS_GetPropertyById(cxFrom, JSVAL_TO_OBJECT(val), id, &propval), L"JS_GetPropertyById");
jsval newPropval = GetOrClone(propval);
if (JSVAL_IS_INT(idval))
{
// int jsids are portable across runtimes
CLONE_REQUIRE(JS_SetPropertyById(cxTo, newObj, id, &newPropval), L"JS_SetPropertyById");
}
else if (JSVAL_IS_STRING(idval))
{
// string jsids are runtime-specific, so we need to copy the string content
JSString* idstr = JS_ValueToString(cxFrom, idval);
CLONE_REQUIRE(idstr, L"JS_ValueToString (id)");
CLONE_REQUIRE(JS_SetUCProperty(cxTo, newObj, JS_GetStringChars(idstr), JS_GetStringLength(idstr), &newPropval), L"JS_SetUCProperty");
}
else
{
// this apparently could be an XML object; ignore it
}
}
return OBJECT_TO_JSVAL(newObj);
}
JSContext* cxFrom;
JSContext* cxTo;
std::map<jsval, jsval> m_Mapping;
std::deque<CScriptValRooted> m_ValuesOld;
std::deque<CScriptValRooted> m_ValuesNew;
};
jsval ScriptInterface::CloneValueFromOtherContext(const ScriptInterface& otherContext, jsval val)
{
PROFILE("CloneValueFromOtherContext");
JSContext* cxTo = GetContext();
JSContext* cxFrom = otherContext.GetContext();
ValueCloner cloner(cxFrom, cxTo);
return cloner.GetOrClone(val);
}

View File

@ -60,7 +60,7 @@ public:
void SetCallbackData(void* cbdata);
static void* GetCallbackData(JSContext* cx);
JSContext* GetContext();
JSContext* GetContext() const;
/**
* Call a constructor function, roughly equivalent to JS "new ctor".
@ -155,6 +155,14 @@ public:
*/
bool LoadScript(const std::wstring& filename, const std::wstring& code);
/**
* Construct a new value (usable in this ScriptInterface's context) by cloning
* a value from a different context.
* Complex values (functions, XML, etc) won't be cloned correctly, but basic
* types and cyclic references should be fine.
*/
jsval CloneValueFromOtherContext(const ScriptInterface& otherContext, jsval val);
/**
* Convert a jsval to a C++ type. (This might trigger GC.)
*/

View File

@ -56,4 +56,51 @@ public:
TS_ASSERT(!script.LoadScript(L"test.js", L"with(1){}"));
TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements");
}
void test_clone_basic()
{
ScriptInterface script1("Test");
ScriptInterface script2("Test");
CScriptVal obj1;
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", obj1));
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
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]})");
}
void test_clone_getters()
{
// The tests should be run with JS_SetGCZeal so this can try to find GC bugs
ScriptInterface script1("Test");
ScriptInterface script2("Test");
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));
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
std::string source;
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}, n:(void 0)})");
}
void test_clone_cyclic()
{
ScriptInterface script1("Test");
ScriptInterface script2("Test");
CScriptVal obj1;
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", obj1));
CScriptVal obj2 = script2.CloneValueFromOtherContext(script1, obj1.get());
std::string source;
TS_ASSERT(script2.CallFunction(obj2.get(), "toSource", source));
TS_ASSERT_STR_EQUALS(source, "({a:#1=[#1#], b:#1#})");
}
};

View File

@ -27,7 +27,7 @@
#include "ps/utf16string.h"
#include "scriptinterface/ScriptInterface.h"
#include "js/jsapi.h"
#include "scriptinterface/AutoRooters.h"
CBinarySerializer::CBinarySerializer(ScriptInterface& scriptInterface) :
m_ScriptInterface(scriptInterface)
@ -104,47 +104,6 @@ void CBinarySerializer::PutScriptVal(const char* UNUSED(name), jsval value)
////////////////////////////////////////////////////////////////
// Exception-safety and GC-safety wrapper for JSIdArray
class IdArrayWrapper
{
JSContext* m_cx;
JSIdArray* m_ida;
public:
IdArrayWrapper(JSContext* cx, JSIdArray* ida) :
m_cx(cx), m_ida(ida)
{
for (jsint i = 0; i < m_ida->length; ++i)
if (!JS_AddRoot(m_cx, &m_ida->vector[i]))
throw PSERROR_Serialize_ScriptError("JS_AddRoot failed");
}
~IdArrayWrapper()
{
for (jsint i = 0; i < m_ida->length; ++i)
if (!JS_RemoveRoot(m_cx, &m_ida->vector[i]))
throw PSERROR_Serialize_ScriptError("JS_RemoveRoot failed");
JS_DestroyIdArray(m_cx, m_ida);
}
};
class RootWrapper
{
JSContext* m_cx;
void* m_obj;
public:
// obj must be a JSObject** or JSString** or jsval* etc
RootWrapper(JSContext* cx, void* obj) :
m_cx(cx), m_obj(obj)
{
if (!JS_AddRoot(m_cx, m_obj))
throw PSERROR_Serialize_ScriptError("JS_AddRoot failed");
}
~RootWrapper()
{
if (!JS_RemoveRoot(m_cx, m_obj))
throw PSERROR_Serialize_ScriptError("JS_RemoveRoot failed");
}
};
void CBinarySerializer::HandleScriptVal(jsval val)
{
JSContext* cx = m_ScriptInterface.GetContext();
@ -216,40 +175,26 @@ void CBinarySerializer::HandleScriptVal(jsval val)
for (jsint i = 0; i < ida->length; ++i)
{
jsval idval, propval;
uintN attrs;
JSBool found;
// Find the attribute name
// (TODO: just use JS_GetPropertyById if we ever upgrade to Spidermonkey 1.8.1)
if (!JS_IdToValue(cx, ida->vector[i], &idval))
{
LOGERROR(L"JS_IdToValue failed");
throw PSERROR_Serialize_ScriptError();
}
JSString* idstr = JS_ValueToString(cx, idval);
if (!idstr)
{
LOGERROR(L"JS_ValueToString failed");
throw PSERROR_Serialize_ScriptError();
}
RootWrapper idstrWrapper(cx, &idstr);
if (!JS_GetUCPropertyAttributes(cx, obj, JS_GetStringChars(idstr), JS_GetStringLength(idstr), &attrs, &found))
throw PSERROR_Serialize_ScriptError("JS_GetUCPropertyAttributes failed");
if (!found)
throw PSERROR_Serialize_ScriptError("JS_GetUCPropertyAttributes didn't find enumerated property");
if (attrs & JSPROP_GETTER)
// Forbid getters, because they will be weird and not serialise properly
JSPropertyDescriptor descr;
if (!JS_GetPropertyDescriptorById(cx, obj, ida->vector[i], JSRESOLVE_QUALIFIED, &descr))
throw PSERROR_Serialize_ScriptError("JS_GetPropertyDescriptorById failed");
if (descr.attrs & JSPROP_GETTER)
throw PSERROR_Serialize_ScriptError("Cannot serialize property getters");
// Get the property name as a string
if (!JS_IdToValue(cx, ida->vector[i], &idval))
throw PSERROR_Serialize_ScriptError("JS_IdToValue failed");
JSString* idstr = JS_ValueToString(cx, idval);
if (!idstr)
throw PSERROR_Serialize_ScriptError("JS_ValueToString failed");
CScriptValRooted idstrRoot(cx, STRING_TO_JSVAL(idstr));
ScriptString("prop name", idstr);
if (!JS_GetUCProperty(cx, obj, JS_GetStringChars(idstr), JS_GetStringLength(idstr), &propval))
{
LOGERROR(L"JS_GetUCProperty failed");
throw PSERROR_Serialize_ScriptError();
}
if (!JS_GetPropertyById(cx, obj, ida->vector[i], &propval))
throw PSERROR_Serialize_ScriptError("JS_GetPropertyById failed");
HandleScriptVal(propval);
}

View File

@ -73,26 +73,6 @@ void CStdDeserializer::FreeScriptBackrefs()
////////////////////////////////////////////////////////////////
class RootWrapper
{
JSContext* m_cx;
void* m_obj;
public:
// obj must be a JSObject** or JSString** or jsval* etc
RootWrapper(JSContext* cx, void* obj) :
m_cx(cx), m_obj(obj)
{
if (!JS_AddRoot(m_cx, m_obj))
throw PSERROR_Deserialize_ScriptError("JS_AddRoot failed");
}
~RootWrapper()
{
if (!JS_RemoveRoot(m_cx, m_obj))
throw PSERROR_Deserialize_ScriptError("JS_RemoveRoot failed");
}
};
jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent)
{
JSContext* cx = m_ScriptInterface.GetContext();
@ -120,7 +100,7 @@ jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent)
if (!obj)
throw PSERROR_Deserialize_ScriptError();
RootWrapper objWrapper(cx, &obj);
CScriptValRooted objRoot(cx, OBJECT_TO_JSVAL(obj));
AddScriptBackref(obj);
@ -133,7 +113,7 @@ jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent)
StringUTF16(propname);
jsval propval = ReadScriptVal(NULL);
RootWrapper propvalWrapper(cx, &propval);
CScriptValRooted propvalRoot(cx, propval);
if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), &propval))
throw PSERROR_Deserialize_ScriptError();

View File

@ -36,6 +36,7 @@
#include "ps/Filesystem.h"
#include "ps/Profile.h"
#include "ps/GameSetup/Paths.h"
#include "scripting/ScriptingHost.h"
using namespace AtlasMessage;