Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/serialization/BinarySerializer.cpp
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | static u8 GetArrayType(js::Scalar::Type arrayType) | ||||
} | } | ||||
} | } | ||||
CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer) : | CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer) : | ||||
m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_ScriptBackrefsNext(0) | m_ScriptInterface(scriptInterface), m_Serializer(serializer), m_ScriptBackrefsNext(0) | ||||
{ | { | ||||
ScriptRequest rq(m_ScriptInterface); | ScriptRequest rq(m_ScriptInterface); | ||||
m_ScriptBackrefSymbol.init(rq.cx, JS::NewSymbol(rq.cx, nullptr)); | JS_AddExtraGCRootsTracer(m_ScriptInterface.GetGeneralJSContext(), Trace, this); | ||||
} | |||||
CBinarySerializerScriptImpl::~CBinarySerializerScriptImpl() | |||||
{ | |||||
JS_RemoveExtraGCRootsTracer(m_ScriptInterface.GetGeneralJSContext(), Trace, this); | |||||
} | } | ||||
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) | void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val) | ||||
{ | { | ||||
ScriptRequest rq(m_ScriptInterface); | ScriptRequest rq(m_ScriptInterface); | ||||
switch (JS_TypeOfValue(rq.cx, val)) | switch (JS_TypeOfValue(rq.cx, val)) | ||||
{ | { | ||||
Show All 13 Lines | case JSTYPE_OBJECT: | ||||
{ | { | ||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_NULL); | m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_NULL); | ||||
break; | break; | ||||
} | } | ||||
JS::RootedObject obj(rq.cx, &val.toObject()); | JS::RootedObject obj(rq.cx, &val.toObject()); | ||||
// If we've already serialized this object, just output a reference to it | // If we've already serialized this object, just output a reference to it | ||||
i32 tag = GetScriptBackrefTag(obj); | u32 tag = GetScriptBackrefTag(obj); | ||||
if (tag != -1) | if (tag != 0) | ||||
{ | { | ||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BACKREF); | m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BACKREF); | ||||
m_Serializer.NumberI32("tag", tag, 0, JSVAL_INT_MAX); | m_Serializer.NumberU32("tag", tag, 0, JSVAL_INT_MAX); | ||||
break; | break; | ||||
} | } | ||||
// Arrays, Maps and Sets are special cases of Objects | // Arrays, Maps and Sets are special cases of Objects | ||||
bool isArray; | bool isArray; | ||||
bool isMap; | bool isMap; | ||||
bool isSet; | bool isSet; | ||||
▲ Show 20 Lines • Show All 327 Lines • ▼ Show 20 Lines | else | ||||
if (!chars) | if (!chars) | ||||
throw PSERROR_Serialize_ScriptError("JS_GetTwoByteStringCharsAndLength failed"); | throw PSERROR_Serialize_ScriptError("JS_GetTwoByteStringCharsAndLength failed"); | ||||
m_Serializer.NumberU32_Unbounded("string length", (u32)length); | m_Serializer.NumberU32_Unbounded("string length", (u32)length); | ||||
m_Serializer.RawBytes(name, (const u8*)chars, length*2); | m_Serializer.RawBytes(name, (const u8*)chars, length*2); | ||||
} | } | ||||
} | } | ||||
i32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj) | void CBinarySerializerScriptImpl::Trace(JSTracer *trc, void *data) | ||||
{ | |||||
CBinarySerializerScriptImpl* serializer = static_cast<CBinarySerializerScriptImpl*>(data); | |||||
serializer->m_ScriptBackrefTags.trace(trc); | |||||
} | |||||
u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj) | |||||
{ | { | ||||
// To support non-tree structures (e.g. "var x = []; var y = [x, x];"), we need a way | // To support non-tree structures (e.g. "var x = []; var y = [x, x];"), we need a way | ||||
// to indicate multiple references to one object(/array). So every time we serialize a | // to indicate multiple references to one object(/array). So every time we serialize a | ||||
// new object, we give it a new tag; when we serialize it a second time we just refer | // new object, we give it a new tag; when we serialize it a second time we just refer | ||||
// to that tag. | // to that tag. | ||||
// | // | ||||
// Tags are stored on the object. To avoid overwriting any existing property, | // Tags are stored on the object. To avoid overwriting any existing property, | ||||
// they are saved as a uniquely-named, non-enumerable property (the serializer's unique symbol). | // they are saved as a uniquely-named, non-enumerable property (the serializer's unique symbol). | ||||
ScriptRequest rq(m_ScriptInterface); | ScriptRequest rq(m_ScriptInterface); | ||||
JS::RootedValue symbolValue(rq.cx, JS::SymbolValue(m_ScriptBackrefSymbol)); | ObjectTagMap::Ptr ptr = m_ScriptBackrefTags.lookup(JS::Heap<JSObject*>(obj.get())); | ||||
JS::RootedId symbolId(rq.cx); | if (!ptr.found()) | ||||
ENSURE(JS_ValueToId(rq.cx, symbolValue, &symbolId)); | |||||
JS::RootedValue tagValue(rq.cx); | |||||
// If it was already there, return the tag | |||||
bool tagFound; | |||||
ENSURE(JS_HasPropertyById(rq.cx, obj, symbolId, &tagFound)); | |||||
if (tagFound) | |||||
{ | |||||
ENSURE(JS_GetPropertyById(rq.cx, obj, symbolId, &tagValue)); | |||||
ENSURE(tagValue.isInt32()); | |||||
return tagValue.toInt32(); | |||||
} | |||||
tagValue = JS::Int32Value(m_ScriptBackrefsNext); | |||||
// TODO: this fails if the object cannot be written to. | |||||
// This means we could end up in an infinite loop... | |||||
if (!JS_DefinePropertyById(rq.cx, obj, symbolId, tagValue, JSPROP_READONLY)) | |||||
{ | { | ||||
// For now just warn, this should be user-fixable and may not actually error out. | ENSURE(m_ScriptBackrefTags.put(JS::Heap<JSObject*>(obj.get()), ++m_ScriptBackrefsNext)); | ||||
JS::RootedValue objVal(rq.cx, JS::ObjectValue(*obj.get())); | // Return 0 to mean "you have to serialize this object"; | ||||
LOGWARNING("Serialization symbol cannot be written on object %s", m_ScriptInterface.ToString(&objVal)); | return 0; | ||||
} | } | ||||
else | |||||
++m_ScriptBackrefsNext; | return ptr->value(); | ||||
// Return a non-tag number so callers know they need to serialize the object | |||||
return -1; | |||||
} | } |
Wildfire Games · Phabricator