Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/serialization/BinarySerializer.cpp
Show First 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | static u8 GetArrayType(js::Scalar::Type arrayType) | ||||
default: | default: | ||||
LOGERROR("Cannot serialize unrecognized typed array view: %d", arrayType); | LOGERROR("Cannot serialize unrecognized typed array view: %d", arrayType); | ||||
throw PSERROR_Serialize_InvalidScriptValue(); | throw PSERROR_Serialize_InvalidScriptValue(); | ||||
} | } | ||||
} | } | ||||
CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer) : | CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer) : | ||||
m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_ScriptBackrefs(scriptInterface.GetRuntime()), | m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_ScriptBackrefs(scriptInterface.GetRuntime()), | ||||
m_SerializablePrototypes(new ObjectIdCache<std::wstring>(scriptInterface.GetRuntime())), m_ScriptBackrefsNext(1) | m_SerializablePrototypes(new ObjectIdCache<SSerializablePrototype>(scriptInterface.GetRuntime())), m_ScriptBackrefsNext(1) | ||||
{ | { | ||||
m_ScriptBackrefs.init(); | m_ScriptBackrefs.init(); | ||||
m_SerializablePrototypes->init(); | m_SerializablePrototypes->init(); | ||||
} | } | ||||
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) | void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val, bool only_props) | ||||
{ | { | ||||
JSContext* cx = m_ScriptInterface.GetContext(); | JSContext* cx = m_ScriptInterface.GetContext(); | ||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
switch (JS_TypeOfValue(cx, val)) | switch (JS_TypeOfValue(cx, val)) | ||||
{ | { | ||||
case JSTYPE_VOID: | case JSTYPE_VOID: | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | else | ||||
throw PSERROR_Serialize_ScriptError("JS_GetClass failed"); | throw PSERROR_Serialize_ScriptError("JS_GetClass failed"); | ||||
JSProtoKey protokey = JSCLASS_CACHED_PROTO_KEY(jsclass); | JSProtoKey protokey = JSCLASS_CACHED_PROTO_KEY(jsclass); | ||||
if (protokey == JSProto_Object) | if (protokey == JSProto_Object) | ||||
{ | { | ||||
// Object class - check for user-defined prototype | // Object class - check for user-defined prototype | ||||
JS::RootedObject proto(cx); | JS::RootedObject proto(cx); | ||||
JS_GetPrototype(cx, obj, &proto); | if (!JS_GetPrototype(cx, obj, &proto)) | ||||
if (!proto) | |||||
throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed"); | throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed"); | ||||
if (m_SerializablePrototypes->empty() || !IsSerializablePrototype(proto)) | SSerializablePrototype prototype = GetPrototypeData(proto); | ||||
{ | if (prototype.name == L"Object") | ||||
// Standard Object prototype | |||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT); | m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT); | ||||
// TODO: maybe we should throw an error for unrecognized non-Object prototypes? | |||||
// (requires fixing AI serialization first and excluding component scripts) | |||||
} | |||||
else | else | ||||
{ | { | ||||
// User-defined custom prototype | // Component serialization can skip this part as we deserialize with an already-created object. | ||||
if (!only_props) | |||||
{ | |||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_PROTOTYPE); | m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_PROTOTYPE); | ||||
m_Serializer.String("proto", prototype.name, 0, 256); | |||||
const std::wstring prototypeName = GetPrototypeName(proto); | } | ||||
m_Serializer.String("proto name", prototypeName, 0, 256); | |||||
// Does it have custom Serialize function? | // Does it have custom Serialize function? | ||||
// if so, we serialize the data it returns, rather than the object's properties directly | // if so, we serialize the data it returns, rather than the object's properties directly | ||||
bool hasCustomSerialize; | if (prototype.hasCustomSerialize) | ||||
if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize)) | |||||
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed"); | |||||
if (hasCustomSerialize) | |||||
{ | { | ||||
JS::RootedValue serialize(cx); | |||||
if (!JS_GetProperty(cx, obj, "Serialize", &serialize)) | |||||
throw PSERROR_Serialize_ScriptError("JS_GetProperty failed"); | |||||
// If serialize is null, so don't serialize anything more | // If serialize is null, so don't serialize anything more | ||||
if (!serialize.isNull()) | if (!prototype.hasNullSerialize) | ||||
{ | { | ||||
JS::RootedValue data(cx); | JS::RootedValue data(cx); | ||||
if (!m_ScriptInterface.CallFunction(val, "Serialize", &data)) | if (!m_ScriptInterface.CallFunction(val, "Serialize", &data)) | ||||
throw PSERROR_Serialize_ScriptError("Prototype Serialize function failed"); | throw PSERROR_Serialize_ScriptError("Prototype Serialize function failed"); | ||||
HandleScriptVal(data); | m_Serializer.ScriptVal("ser_obj", &data); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
else if (protokey == JSProto_Number) | else if (protokey == JSProto_Number) | ||||
{ | { | ||||
// Standard Number object | // Standard Number object | ||||
▲ Show 20 Lines • Show All 265 Lines • ▼ Show 20 Lines | u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj) | ||||
m_ScriptBackrefs.add(cx, obj, m_ScriptBackrefsNext); | m_ScriptBackrefs.add(cx, obj, m_ScriptBackrefsNext); | ||||
m_ScriptBackrefsNext++; | m_ScriptBackrefsNext++; | ||||
// Return a non-tag number so callers know they need to serialize the object | // Return a non-tag number so callers know they need to serialize the object | ||||
return 0; | return 0; | ||||
} | } | ||||
bool CBinarySerializerScriptImpl::IsSerializablePrototype(JS::HandleObject prototype) | CBinarySerializerScriptImpl::SSerializablePrototype CBinarySerializerScriptImpl::GetPrototypeData(JS::HandleObject prototype) | ||||
{ | { | ||||
return m_SerializablePrototypes->has(prototype); | SSerializablePrototype ret; | ||||
} | bool found = m_SerializablePrototypes->find(prototype, ret); | ||||
if (!found) | |||||
{ | |||||
JSContext* cx = m_ScriptInterface.GetContext(); | |||||
JSAutoRequest rq(cx); | |||||
std::wstring CBinarySerializerScriptImpl::GetPrototypeName(JS::HandleObject prototype) | JS::RootedValue constructor(cx, JS::ObjectOrNullValue(JS_GetConstructor(cx, prototype))); | ||||
if (!m_ScriptInterface.GetProperty(constructor, "name", ret.name)) | |||||
throw PSERROR_Serialize_ScriptError("Could not get constructor name."); | |||||
// Nothing to do for basic Object objects. | |||||
if (ret.name == L"Object") | |||||
{ | { | ||||
std::wstring ret; | m_SerializablePrototypes->add(cx, prototype, ret); | ||||
bool found = m_SerializablePrototypes->find(prototype, ret); | |||||
ENSURE(found); | |||||
return ret; | return ret; | ||||
} | } | ||||
void CBinarySerializerScriptImpl::SetSerializablePrototypes(shared_ptr<ObjectIdCache<std::wstring> > prototypes) | /* | ||||
u32 nbArgs; | |||||
m_ScriptInterface.GetProperty(constructor, "length", nbArgs); | |||||
// We cannot recreate objects if their constructor takes mandatory arguments. | |||||
if (nbArgs != 0) | |||||
throw PSERROR_Serialize_ScriptError("Cannot serialize objects with no default constructors."); | |||||
*/ | |||||
bool hasCustomDeserialize; | |||||
if (!JS_HasProperty(cx, prototype, "Serialize", &ret.hasCustomSerialize) || | |||||
!JS_HasProperty(cx, prototype, "Deserialize", &hasCustomDeserialize)) | |||||
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed"); | |||||
if (ret.hasCustomSerialize) | |||||
{ | { | ||||
m_SerializablePrototypes = prototypes; | JS::RootedValue serialize(cx); | ||||
if (!JS_GetProperty(cx, prototype, "Serialize", &serialize)) | |||||
throw PSERROR_Serialize_ScriptError("JS_GetProperty failed"); | |||||
if (serialize.isNull()) | |||||
ret.hasNullSerialize = true; | |||||
else if (!hasCustomDeserialize) | |||||
throw PSERROR_Serialize_ScriptError("Cannot serialize script with non-null Serialize but no Deserialize."); | |||||
} | |||||
m_SerializablePrototypes->add(cx, prototype, ret); | |||||
} | |||||
return ret; | |||||
} | } |
Wildfire Games · Phabricator