You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
5.9 KiB
147 lines
5.9 KiB
#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 (Helpers.IsValueType(type) || !recursionCheck) { ctx.LoadNullRef(); } |
|
else { ctx.CopyValue(); } |
|
} |
|
ctx.LoadReaderWriter(); |
|
ctx.EmitCall(Helpers.GetStaticMethod(rwType, "StartSubItem", |
|
read ? new Type[] { rwType } : new Type[] { ctx.MapType(typeof(object)), rwType })); |
|
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(Helpers.GetStaticMethod(rwType, "EndSubItem", new Type[] { ctx.MapType(typeof(SubItemToken)), rwType })); |
|
} |
|
return true; |
|
#endif |
|
} |
|
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) |
|
{ |
|
if (!EmitDedicatedMethod(ctx, valueFrom, false)) |
|
{ |
|
ctx.LoadValue(valueFrom); |
|
if (Helpers.IsValueType(type)) ctx.CastToObject(type); |
|
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method |
|
ctx.LoadReaderWriter(); |
|
ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoWriter)), recursionCheck ? "WriteObject" : "WriteRecursionSafeObject", new Type[] { ctx.MapType(typeof(object)), ctx.MapType(typeof(int)), ctx.MapType(typeof(ProtoWriter)) })); |
|
} |
|
} |
|
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) |
|
{ |
|
if (!EmitDedicatedMethod(ctx, valueFrom, true)) |
|
{ |
|
ctx.LoadValue(valueFrom); |
|
if (Helpers.IsValueType(type)) ctx.CastToObject(type); |
|
ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method |
|
ctx.LoadReaderWriter(); |
|
ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoReader)), "ReadObject")); |
|
ctx.CastFromObject(type); |
|
} |
|
} |
|
#endif |
|
} |
|
} |
|
#endif |