Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/serialization/StdDeserializer.cpp
/* Copyright (C) 2019 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
reinterpret_cast<CStdDeserializer*>(data)->TraceMember(trc); | reinterpret_cast<CStdDeserializer*>(data)->TraceMember(trc); | ||||
} | } | ||||
void CStdDeserializer::TraceMember(JSTracer *trc) | void CStdDeserializer::TraceMember(JSTracer *trc) | ||||
{ | { | ||||
for (size_t i=0; i<m_ScriptBackrefs.size(); ++i) | for (size_t i=0; i<m_ScriptBackrefs.size(); ++i) | ||||
JS_CallObjectTracer(trc, &m_ScriptBackrefs[i], "StdDeserializer::m_ScriptBackrefs"); | JS_CallObjectTracer(trc, &m_ScriptBackrefs[i], "StdDeserializer::m_ScriptBackrefs"); | ||||
for (std::pair<const std::wstring, JS::Heap<JSObject*>>& proto : m_SerializablePrototypes) | |||||
JS_CallObjectTracer(trc, &proto.second, "StdDeserializer::m_SerializablePrototypes"); | |||||
} | } | ||||
void CStdDeserializer::Get(const char* name, u8* data, size_t len) | void CStdDeserializer::Get(const char* name, u8* data, size_t len) | ||||
{ | { | ||||
#if DEBUG_SERIALIZER_ANNOTATE | #if DEBUG_SERIALIZER_ANNOTATE | ||||
std::string strName; | std::string strName; | ||||
char c = m_Stream.get(); | char c = m_Stream.get(); | ||||
ENSURE(c == '<'); | ENSURE(c == '<'); | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | JS::Value CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject appendParent) | ||||
case SCRIPT_TYPE_VOID: | case SCRIPT_TYPE_VOID: | ||||
return JS::UndefinedValue(); | return JS::UndefinedValue(); | ||||
case SCRIPT_TYPE_NULL: | case SCRIPT_TYPE_NULL: | ||||
return JS::NullValue(); | return JS::NullValue(); | ||||
case SCRIPT_TYPE_ARRAY: | case SCRIPT_TYPE_ARRAY: | ||||
case SCRIPT_TYPE_OBJECT: | case SCRIPT_TYPE_OBJECT: | ||||
case SCRIPT_TYPE_OBJECT_PROTOTYPE: | |||||
{ | { | ||||
JS::RootedObject obj(cx); | JS::RootedObject obj(cx); | ||||
if (appendParent) | if (appendParent) | ||||
{ | { | ||||
obj.set(appendParent); | obj.set(appendParent); | ||||
} | } | ||||
else if (type == SCRIPT_TYPE_ARRAY) | else if (type == SCRIPT_TYPE_ARRAY) | ||||
{ | { | ||||
u32 length; | u32 length; | ||||
NumberU32_Unbounded("array length", length); | NumberU32_Unbounded("array length", length); | ||||
obj.set(JS_NewArrayObject(cx, length)); | obj.set(JS_NewArrayObject(cx, length)); | ||||
} | } | ||||
else if (type == SCRIPT_TYPE_OBJECT) | else // SCRIPT_TYPE_OBJECT | ||||
{ | { | ||||
obj.set(JS_NewPlainObject(cx)); | obj.set(JS_NewPlainObject(cx)); | ||||
} | } | ||||
else // SCRIPT_TYPE_OBJECT_PROTOTYPE | |||||
{ | |||||
std::wstring prototypeName; | |||||
String("proto name", prototypeName, 0, 256); | |||||
// Get constructor object | |||||
JS::RootedObject proto(cx); | |||||
GetSerializablePrototype(prototypeName, &proto); | |||||
if (!proto) | |||||
throw PSERROR_Deserialize_ScriptError("Failed to find serializable prototype for object"); | |||||
obj.set(JS_NewObjectWithGivenProto(cx, nullptr, proto)); | |||||
if (!obj) | |||||
throw PSERROR_Deserialize_ScriptError("JS_NewObject failed"); | |||||
// Does it have custom Deserialize function? | |||||
// if so, we let it handle the deserialized data, rather than adding properties directly | |||||
bool hasCustomDeserialize, hasCustomSerialize; | |||||
if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize) || !JS_HasProperty(cx, obj, "Deserialize", &hasCustomDeserialize)) | |||||
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed"); | |||||
if (hasCustomDeserialize) | |||||
{ | |||||
AddScriptBackref(obj); | |||||
JS::RootedValue serialize(cx); | |||||
if (!JS_GetProperty(cx, obj, "Serialize", &serialize)) | |||||
throw PSERROR_Serialize_ScriptError("JS_GetProperty failed"); | |||||
bool hasNullSerialize = hasCustomSerialize && serialize.isNull(); | |||||
// If Serialize is null, we'll still call Deserialize but with undefined argument | |||||
JS::RootedValue data(cx); | |||||
if (!hasNullSerialize) | |||||
ScriptVal("data", &data); | |||||
JS::RootedValue objVal(cx, JS::ObjectValue(*obj)); | |||||
m_ScriptInterface.CallFunctionVoid(objVal, "Deserialize", data); | |||||
return JS::ObjectValue(*obj); | |||||
} | |||||
} | |||||
if (!obj) | if (!obj) | ||||
throw PSERROR_Deserialize_ScriptError("Deserializer failed to create new object"); | throw PSERROR_Deserialize_ScriptError("Deserializer failed to create new object"); | ||||
AddScriptBackref(obj); | AddScriptBackref(obj); | ||||
uint32_t numProps; | uint32_t numProps; | ||||
NumberU32_Unbounded("num props", numProps); | NumberU32_Unbounded("num props", numProps); | ||||
▲ Show 20 Lines • Show All 288 Lines • ▼ Show 20 Lines | void CStdDeserializer::ScriptObjectAppend(const char* name, JS::HandleValue objVal) | ||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
if (!objVal.isObject()) | if (!objVal.isObject()) | ||||
throw PSERROR_Deserialize_ScriptError(); | throw PSERROR_Deserialize_ScriptError(); | ||||
JS::RootedObject obj(cx, &objVal.toObject()); | JS::RootedObject obj(cx, &objVal.toObject()); | ||||
ReadScriptVal(name, obj); | ReadScriptVal(name, obj); | ||||
} | } | ||||
void CStdDeserializer::SetSerializablePrototypes(std::map<std::wstring, JS::Heap<JSObject*> >& prototypes) | |||||
{ | |||||
m_SerializablePrototypes = prototypes; | |||||
} | |||||
bool CStdDeserializer::IsSerializablePrototype(const std::wstring& name) | |||||
{ | |||||
return m_SerializablePrototypes.find(name) != m_SerializablePrototypes.end(); | |||||
} | |||||
void CStdDeserializer::GetSerializablePrototype(const std::wstring& name, JS::MutableHandleObject ret) | |||||
{ | |||||
std::map<std::wstring, JS::Heap<JSObject*> >::iterator it = m_SerializablePrototypes.find(name); | |||||
if (it != m_SerializablePrototypes.end()) | |||||
ret.set(it->second); | |||||
else | |||||
ret.set(NULL); | |||||
} |
Wildfire Games · Phabricator