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

137 lines
4.5 KiB
C#

#if !NO_RUNTIME
using System;
using System.Net;
using ProtoBuf.Meta;
#if FEAT_IKVM
using Type = IKVM.Reflection.Type;
using IKVM.Reflection;
#else
using System.Reflection;
#endif
namespace ProtoBuf.Serializers
{
internal sealed class ParseableSerializer : IProtoSerializer
{
private readonly MethodInfo parse;
public static ParseableSerializer TryCreate(Type type, TypeModel model)
{
if (type == null) throw new ArgumentNullException("type");
#if WINRT || PORTABLE
MethodInfo method = null;
#if WINRT
foreach (MethodInfo tmp in type.GetTypeInfo().GetDeclaredMethods("Parse"))
#else
foreach (MethodInfo tmp in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly))
#endif
{
ParameterInfo[] p;
if (tmp.Name == "Parse" && tmp.IsPublic && tmp.IsStatic && tmp.DeclaringType == type && (p = tmp.GetParameters()) != null && p.Length == 1 && p[0].ParameterType == typeof(string))
{
method = tmp;
break;
}
}
#else
MethodInfo method = type.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly,
null, new Type[] {model.MapType(typeof (string))}, null);
#endif
if (method != null && method.ReturnType == type)
{
if (Helpers.IsValueType(type))
{
MethodInfo toString = GetCustomToString(type);
if (toString == null || toString.ReturnType != model.MapType(typeof (string)))
return null; // need custom ToString, fools
}
return new ParseableSerializer(method);
}
return null;
}
private static MethodInfo GetCustomToString(Type type)
{
#if WINRT
foreach (MethodInfo method in type.GetTypeInfo().GetDeclaredMethods("ToString"))
{
if (method.IsPublic && !method.IsStatic && method.GetParameters().Length == 0) return method;
}
return null;
#elif PORTABLE
MethodInfo method = Helpers.GetInstanceMethod(type, "ToString", Helpers.EmptyTypes);
if (method == null || !method.IsPublic || method.IsStatic || method.DeclaringType != type) return null;
return method;
#else
return type.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly,
null, Helpers.EmptyTypes, null);
#endif
}
private ParseableSerializer(MethodInfo parse)
{
this.parse = parse;
}
public Type ExpectedType
{
get { return parse.DeclaringType; }
}
bool IProtoSerializer.RequiresOldValue
{
get { return false; }
}
bool IProtoSerializer.ReturnsValue
{
get { return true; }
}
#if !FEAT_IKVM
public object Read(object value, ProtoReader source)
{
Helpers.DebugAssert(value == null); // since replaces
return parse.Invoke(null, new object[] {source.ReadString()});
}
public void Write(object value, ProtoWriter dest)
{
ProtoWriter.WriteString(value.ToString(), dest);
}
#endif
#if FEAT_COMPILER
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
Type type = ExpectedType;
if (type.IsValueType)
{ // note that for structs, we've already asserted that a custom ToString
// exists; no need to handle the box/callvirt scenario
// force it to a variable if needed, so we can take the address
using (Compiler.Local loc = ctx.GetLocalWithValue(type, valueFrom))
{
ctx.LoadAddress(loc, type);
ctx.EmitCall(GetCustomToString(type));
}
}
else {
ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("ToString"));
}
ctx.EmitBasicWrite("WriteString", valueFrom);
}
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
{
ctx.EmitBasicRead("ReadString", ctx.MapType(typeof(string)));
ctx.EmitCall(parse);
}
#endif
}
}
#endif