Quasar/Server/Core/ProtoBuf/Serializers/SubItemSerializer.cs

147 lines
5.6 KiB
C#

#if !NO_RUNTIME
using System;
using ProtoBuf.Meta;
#if FEAT_COMPILER
#if FEAT_IKVM
using IKVM.Reflection.Emit;
using Type = IKVM.Reflection.Type;
#else
using System.Reflection.Emit;
#endif
#endif
namespace ProtoBuf.Serializers
{
sealed class SubItemSerializer : IProtoTypeSerializer
{
bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
{
return ((IProtoTypeSerializer)proxy.Serializer).HasCallbacks(callbackType);
}
bool IProtoTypeSerializer.CanCreateInstance()
{
return ((IProtoTypeSerializer)proxy.Serializer).CanCreateInstance();
}
#if FEAT_COMPILER
void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
{
((IProtoTypeSerializer)proxy.Serializer).EmitCallback(ctx, valueFrom, callbackType);
}
void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx)
{
((IProtoTypeSerializer)proxy.Serializer).EmitCreateInstance(ctx);
}
#endif
#if !FEAT_IKVM
void IProtoTypeSerializer.Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
{
((IProtoTypeSerializer)proxy.Serializer).Callback(value, callbackType, context);
}
object IProtoTypeSerializer.CreateInstance(ProtoReader source)
{
return ((IProtoTypeSerializer)proxy.Serializer).CreateInstance(source);
}
#endif
private readonly int key;
private readonly Type type;
private readonly ISerializerProxy proxy;
private readonly bool recursionCheck;
public SubItemSerializer(Type type, int key, ISerializerProxy proxy, bool recursionCheck)
{
if (type == null) throw new ArgumentNullException("type");
if (proxy == null) throw new ArgumentNullException("proxy");
this.type = type;
this.proxy= proxy;
this.key = key;
this.recursionCheck = recursionCheck;
}
Type IProtoSerializer.ExpectedType
{
get { return type; }
}
bool IProtoSerializer.RequiresOldValue { get { return true; } }
bool IProtoSerializer.ReturnsValue { get { return true; } }
#if !FEAT_IKVM
void IProtoSerializer.Write(object value, ProtoWriter dest)
{
if (recursionCheck)
{
ProtoWriter.WriteObject(value, key, dest);
}
else
{
ProtoWriter.WriteRecursionSafeObject(value, key, dest);
}
}
object IProtoSerializer.Read(object value, ProtoReader source)
{
return ProtoReader.ReadObject(value, key, source);
}
#endif
#if FEAT_COMPILER
bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read)
{
#if SILVERLIGHT
return false;
#else
MethodBuilder method = ctx.GetDedicatedMethod(key, read);
if (method == null) return false;
using (Compiler.Local token = new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))))
{
Type rwType = ctx.MapType(read ? typeof(ProtoReader) : typeof(ProtoWriter));
ctx.LoadValue(valueFrom);
if (!read) // write requires the object for StartSubItem; read doesn't
{ // (if recursion-check is disabled [subtypes] then null is fine too)
if (type.IsValueType || !recursionCheck) { ctx.LoadNullRef(); }
else { ctx.CopyValue(); }
}
ctx.LoadReaderWriter();
ctx.EmitCall(rwType.GetMethod("StartSubItem"));
ctx.StoreValue(token);
// note: value already on the stack
ctx.LoadReaderWriter();
ctx.EmitCall(method);
// handle inheritance (we will be calling the *base* version of things,
// but we expect Read to return the "type" type)
if (read && type != method.ReturnType) ctx.Cast(this.type);
ctx.LoadValue(token);
ctx.LoadReaderWriter();
ctx.EmitCall(rwType.GetMethod("EndSubItem"));
}
return true;
#endif
}
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (!EmitDedicatedMethod(ctx, valueFrom, false))
{
ctx.LoadValue(valueFrom);
if (type.IsValueType) ctx.CastToObject(type);
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod(recursionCheck ? "WriteObject" : "WriteRecursionSafeObject"));
}
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
if (!EmitDedicatedMethod(ctx, valueFrom, true))
{
ctx.LoadValue(valueFrom);
if (type.IsValueType) ctx.CastToObject(type);
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("ReadObject"));
ctx.CastFromObject(type);
}
}
#endif
}
}
#endif