Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/serialization/BinarySerializer.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 | 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_ScriptBackrefsNext(1) | ||||
{ | { | ||||
m_ScriptBackrefs.init(); | m_ScriptBackrefs.init(); | ||||
m_SerializablePrototypes->init(); | |||||
} | } | ||||
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) | void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) | ||||
{ | { | ||||
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)) | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | else | ||||
const JSClass* jsclass = JS_GetClass(obj); | const JSClass* jsclass = JS_GetClass(obj); | ||||
if (!jsclass) | if (!jsclass) | ||||
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 | |||||
JS::RootedObject proto(cx); | |||||
JS_GetPrototype(cx, obj, &proto); | |||||
if (!proto) | |||||
throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed"); | |||||
if (m_SerializablePrototypes->empty() || !IsSerializablePrototype(proto)) | |||||
{ | |||||
// Standard Object prototype | // 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 | |||||
{ | |||||
// User-defined custom prototype | |||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_PROTOTYPE); | |||||
const std::wstring prototypeName = GetPrototypeName(proto); | |||||
m_Serializer.String("proto name", prototypeName, 0, 256); | |||||
// Does it have custom Serialize function? | |||||
// if so, we serialize the data it returns, rather than the object's properties directly | |||||
bool 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.isNull()) | |||||
{ | |||||
JS::RootedValue data(cx); | |||||
if (!m_ScriptInterface.CallFunction(val, "Serialize", &data)) | |||||
throw PSERROR_Serialize_ScriptError("Prototype Serialize function failed"); | |||||
HandleScriptVal(data); | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
} | } | ||||
else if (protokey == JSProto_Number) | else if (protokey == JSProto_Number) | ||||
{ | { | ||||
// Standard Number object | // Standard Number object | ||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_NUMBER); | m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_NUMBER); | ||||
// Get primitive value | // Get primitive value | ||||
double d; | double d; | ||||
if (!JS::ToNumber(cx, val, &d)) | if (!JS::ToNumber(cx, val, &d)) | ||||
▲ Show 20 Lines • Show All 260 Lines • ▼ Show 20 Lines | u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj) | ||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
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) | |||||
{ | |||||
return m_SerializablePrototypes->has(prototype); | |||||
} | |||||
std::wstring CBinarySerializerScriptImpl::GetPrototypeName(JS::HandleObject prototype) | |||||
{ | |||||
std::wstring ret; | |||||
bool found = m_SerializablePrototypes->find(prototype, ret); | |||||
ENSURE(found); | |||||
return ret; | |||||
} | |||||
void CBinarySerializerScriptImpl::SetSerializablePrototypes(shared_ptr<ObjectIdCache<std::wstring> > prototypes) | |||||
{ | |||||
m_SerializablePrototypes = prototypes; | |||||
} |
Wildfire Games · Phabricator