151 lines
3.8 KiB
C#
151 lines
3.8 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using ProtoBuf.Compiler;
|
|
using ProtoBuf.Meta;
|
|
|
|
namespace ProtoBuf.Serializers;
|
|
|
|
internal sealed class SurrogateSerializer : IProtoTypeSerializer, IProtoSerializer
|
|
{
|
|
private readonly Type forType;
|
|
|
|
private readonly Type declaredType;
|
|
|
|
private readonly MethodInfo toTail;
|
|
|
|
private readonly MethodInfo fromTail;
|
|
|
|
private IProtoTypeSerializer rootTail;
|
|
|
|
public bool ReturnsValue => false;
|
|
|
|
public bool RequiresOldValue => true;
|
|
|
|
public Type ExpectedType => forType;
|
|
|
|
bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void IProtoTypeSerializer.EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
|
|
{
|
|
}
|
|
|
|
void IProtoTypeSerializer.EmitCreateInstance(CompilerContext ctx)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
bool IProtoTypeSerializer.CanCreateInstance()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
object IProtoTypeSerializer.CreateInstance(ProtoReader source)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
void IProtoTypeSerializer.Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
|
|
{
|
|
}
|
|
|
|
public SurrogateSerializer(TypeModel model, Type forType, Type declaredType, IProtoTypeSerializer rootTail)
|
|
{
|
|
this.forType = forType;
|
|
this.declaredType = declaredType;
|
|
this.rootTail = rootTail;
|
|
toTail = GetConversion(model, toTail: true);
|
|
fromTail = GetConversion(model, toTail: false);
|
|
}
|
|
|
|
private static bool HasCast(TypeModel model, Type type, Type from, Type to, out MethodInfo op)
|
|
{
|
|
MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
|
Type type2 = null;
|
|
foreach (MethodInfo methodInfo in methods)
|
|
{
|
|
if ((object)methodInfo.ReturnType != to)
|
|
{
|
|
continue;
|
|
}
|
|
ParameterInfo[] parameters = methodInfo.GetParameters();
|
|
if (parameters.Length != 1 || (object)parameters[0].ParameterType != from)
|
|
{
|
|
continue;
|
|
}
|
|
if ((object)type2 == null)
|
|
{
|
|
type2 = model.MapType(typeof(ProtoConverterAttribute), demand: false);
|
|
if ((object)type2 == null)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (methodInfo.IsDefined(type2, inherit: true))
|
|
{
|
|
op = methodInfo;
|
|
return true;
|
|
}
|
|
}
|
|
foreach (MethodInfo methodInfo2 in methods)
|
|
{
|
|
if ((!(methodInfo2.Name != "op_Implicit") || !(methodInfo2.Name != "op_Explicit")) && (object)methodInfo2.ReturnType == to)
|
|
{
|
|
ParameterInfo[] parameters = methodInfo2.GetParameters();
|
|
if (parameters.Length == 1 && (object)parameters[0].ParameterType == from)
|
|
{
|
|
op = methodInfo2;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
op = null;
|
|
return false;
|
|
}
|
|
|
|
public MethodInfo GetConversion(TypeModel model, bool toTail)
|
|
{
|
|
Type to = (toTail ? declaredType : forType);
|
|
Type type = (toTail ? forType : declaredType);
|
|
if (HasCast(model, declaredType, type, to, out var op) || HasCast(model, forType, type, to, out op))
|
|
{
|
|
return op;
|
|
}
|
|
throw new InvalidOperationException("No suitable conversion operator found for surrogate: " + forType.FullName + " / " + declaredType.FullName);
|
|
}
|
|
|
|
public void Write(object value, ProtoWriter writer)
|
|
{
|
|
rootTail.Write(toTail.Invoke(null, new object[1] { value }), writer);
|
|
}
|
|
|
|
public object Read(object value, ProtoReader source)
|
|
{
|
|
object[] array = new object[1] { value };
|
|
value = toTail.Invoke(null, array);
|
|
array[0] = rootTail.Read(value, source);
|
|
return fromTail.Invoke(null, array);
|
|
}
|
|
|
|
void IProtoSerializer.EmitRead(CompilerContext ctx, Local valueFrom)
|
|
{
|
|
using Local local = new Local(ctx, declaredType);
|
|
ctx.LoadValue(valueFrom);
|
|
ctx.EmitCall(toTail);
|
|
ctx.StoreValue(local);
|
|
rootTail.EmitRead(ctx, local);
|
|
ctx.LoadValue(local);
|
|
ctx.EmitCall(fromTail);
|
|
ctx.StoreValue(valueFrom);
|
|
}
|
|
|
|
void IProtoSerializer.EmitWrite(CompilerContext ctx, Local valueFrom)
|
|
{
|
|
ctx.LoadValue(valueFrom);
|
|
ctx.EmitCall(toTail);
|
|
rootTail.EmitWrite(ctx, null);
|
|
}
|
|
}
|