0ad/source/simulation2/serialization/StdDeserializer.cpp
Ykkrosh fe53bce3b1 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.
2010-05-05 22:36:35 +00:00

195 lines
4.7 KiB
C++

/* 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 "precompiled.h"
#include "StdDeserializer.h"
#include "SerializedScriptTypes.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "scriptinterface/ScriptInterface.h"
#include "js/jsapi.h"
CStdDeserializer::CStdDeserializer(ScriptInterface& scriptInterface, std::istream& stream) :
m_ScriptInterface(scriptInterface), m_Stream(stream)
{
}
CStdDeserializer::~CStdDeserializer()
{
FreeScriptBackrefs();
}
void CStdDeserializer::Get(u8* data, size_t len)
{
m_Stream.read((char*)data, len);
if (!m_Stream.good()) // hit eof before len, or other errors
throw PSERROR_Deserialize_ReadFailed();
}
void CStdDeserializer::AddScriptBackref(JSObject* obj)
{
std::pair<std::map<u32, JSObject*>::iterator, bool> it = m_ScriptBackrefs.insert(std::make_pair((u32)m_ScriptBackrefs.size()+1, obj));
debug_assert(it.second);
if (!JS_AddRoot(m_ScriptInterface.GetContext(), (void*)&it.first->second))
throw PSERROR_Deserialize_ScriptError("JS_AddRoot failed");
}
JSObject* CStdDeserializer::GetScriptBackref(u32 tag)
{
std::map<u32, JSObject*>::const_iterator it = m_ScriptBackrefs.find(tag);
if (it == m_ScriptBackrefs.end())
return NULL;
return it->second;
}
void CStdDeserializer::FreeScriptBackrefs()
{
std::map<u32, JSObject*>::iterator it = m_ScriptBackrefs.begin();
for (; it != m_ScriptBackrefs.end(); ++it)
{
if (!JS_RemoveRoot(m_ScriptInterface.GetContext(), (void*)&it->second))
throw PSERROR_Deserialize_ScriptError("JS_RemoveRoot failed");
}
m_ScriptBackrefs.clear();
}
////////////////////////////////////////////////////////////////
jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent)
{
JSContext* cx = m_ScriptInterface.GetContext();
uint8_t type;
NumberU8_Unbounded(type);
switch (type)
{
case SCRIPT_TYPE_VOID:
return JSVAL_VOID;
case SCRIPT_TYPE_NULL:
return JSVAL_NULL;
case SCRIPT_TYPE_ARRAY:
case SCRIPT_TYPE_OBJECT:
{
JSObject* obj;
if (appendParent)
obj = appendParent;
else if (type == SCRIPT_TYPE_ARRAY)
obj = JS_NewArrayObject(cx, 0, NULL);
else
obj = JS_NewObject(cx, NULL, NULL, NULL);
if (!obj)
throw PSERROR_Deserialize_ScriptError();
CScriptValRooted objRoot(cx, OBJECT_TO_JSVAL(obj));
AddScriptBackref(obj);
uint32_t numProps;
NumberU32_Unbounded(numProps);
for (uint32_t i = 0; i < numProps; ++i)
{
utf16string propname;
StringUTF16(propname);
jsval propval = ReadScriptVal(NULL);
CScriptValRooted propvalRoot(cx, propval);
if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), &propval))
throw PSERROR_Deserialize_ScriptError();
}
return OBJECT_TO_JSVAL(obj);
}
case SCRIPT_TYPE_STRING:
{
JSString* str;
ScriptString(str);
return STRING_TO_JSVAL(str);
}
case SCRIPT_TYPE_INT:
{
int32_t value;
NumberI32(value, JSVAL_INT_MIN, JSVAL_INT_MAX);
return INT_TO_JSVAL(value);
}
case SCRIPT_TYPE_DOUBLE:
{
double value;
NumberDouble_Unbounded(value);
jsval rval;
if (!JS_NewNumberValue(cx, value, &rval))
throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed");
return rval;
}
case SCRIPT_TYPE_BOOLEAN:
{
uint8_t value;
NumberU8(value, 0, 1);
return BOOLEAN_TO_JSVAL(value ? JS_TRUE : JS_FALSE);
}
case SCRIPT_TYPE_BACKREF:
{
u32 tag;
NumberU32_Unbounded(tag);
JSObject* obj = GetScriptBackref(tag);
if (!obj)
throw PSERROR_Deserialize_ScriptError("Invalid backref tag");
return OBJECT_TO_JSVAL(obj);
}
default:
throw PSERROR_Deserialize_OutOfBounds();
}
}
void CStdDeserializer::ScriptString(JSString*& out)
{
utf16string str;
StringUTF16(str);
out = JS_NewUCStringCopyN(m_ScriptInterface.GetContext(), (const jschar*)str.data(), str.length());
if (!out)
{
LOGERROR(L"JS_NewUCStringCopyN failed");
throw PSERROR_Deserialize_ScriptError();
}
}
void CStdDeserializer::ScriptVal(jsval& out)
{
out = ReadScriptVal(NULL);
}
void CStdDeserializer::ScriptVal(CScriptVal& out)
{
out = ReadScriptVal(NULL);
}
void CStdDeserializer::ScriptObjectAppend(jsval& obj)
{
if (!JSVAL_IS_OBJECT(obj))
throw PSERROR_Deserialize_ScriptError();
ReadScriptVal(JSVAL_TO_OBJECT(obj));
}