Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/serialization/BinarySerializer.cpp
Show First 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | case js::Scalar::Uint8Clamped: | ||||
return SCRIPT_TYPED_ARRAY_UINT8_CLAMPED; | return SCRIPT_TYPED_ARRAY_UINT8_CLAMPED; | ||||
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_ScriptBackrefsNext(0) | ||||
m_ScriptBackrefsNext(1) | |||||
{ | { | ||||
m_ScriptBackrefs.init(); | JSContext* cx = m_ScriptInterface.GetContext(); | ||||
JSAutoRequest rq(cx); | |||||
m_ScriptBackrefSymbol.init(cx, JS::NewSymbol(cx, nullptr)); | |||||
} | } | ||||
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 All 14 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(cx, &val.toObject()); | JS::RootedObject obj(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 | ||||
u32 tag = GetScriptBackrefTag(obj); | i32 tag = GetScriptBackrefTag(obj); | ||||
if (tag) | if (tag != -1) | ||||
{ | { | ||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BACKREF); | m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BACKREF); | ||||
m_Serializer.NumberU32_Unbounded("tag", tag); | m_Serializer.NumberI32("tag", tag, 0, JSVAL_INT_MAX); | ||||
break; | break; | ||||
} | } | ||||
// Arrays are special cases of Object | // Arrays are special cases of Object | ||||
bool isArray; | bool isArray; | ||||
if (JS_IsArrayObject(cx, obj, &isArray) && isArray) | if (JS_IsArrayObject(cx, obj, &isArray) && isArray) | ||||
{ | { | ||||
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_ARRAY); | m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_ARRAY); | ||||
▲ Show 20 Lines • Show All 296 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); | ||||
} | } | ||||
} | } | ||||
u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj) | i32 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 non-zero tag; when we serialize it a second time we just | // new object, we give it a new tag; when we serialize it a second time we just refer | ||||
// refer to that tag. | // to that tag. | ||||
// | // | ||||
// The tags are stored in a map. Maybe it'd be more efficient to store it inline in the object | // Tags are stored on the object. To avoid overwriting any existing property, | ||||
// somehow? but this works okay for now | // they are saved as a uniquely-named, non-enumerable property (the serializer's unique symbol). | ||||
// If it was already there, return the tag | |||||
u32 tag; | |||||
if (m_ScriptBackrefs.find(obj, tag)) | |||||
return tag; | |||||
JSContext* cx = m_ScriptInterface.GetContext(); | JSContext* cx = m_ScriptInterface.GetContext(); | ||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
m_ScriptBackrefs.add(cx, obj, m_ScriptBackrefsNext); | JS::RootedValue symbolValue(cx, JS::SymbolValue(m_ScriptBackrefSymbol)); | ||||
JS::RootedId symbolId(cx); | |||||
ENSURE(JS_ValueToId(cx, symbolValue, &symbolId)); | |||||
JS::RootedValue tagValue(cx); | |||||
// If it was already there, return the tag | |||||
bool tagFound; | |||||
ENSURE(JS_HasPropertyById(cx, obj, symbolId, &tagFound)); | |||||
if (tagFound) | |||||
{ | |||||
ENSURE(JS_GetPropertyById(cx, obj, symbolId, &tagValue)); | |||||
ENSURE(tagValue.isInt32()); | |||||
return tagValue.toInt32(); | |||||
} | |||||
tagValue = JS::Int32Value(m_ScriptBackrefsNext); | |||||
JS_SetPropertyById(cx, obj, symbolId, tagValue); | |||||
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 -1; | ||||
} | } |
Wildfire Games · Phabricator