ParseableSerializer.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #if !NO_RUNTIME
  2. using System;
  3. using System.Net;
  4. using ProtoBuf.Meta;
  5. #if FEAT_IKVM
  6. using Type = IKVM.Reflection.Type;
  7. using IKVM.Reflection;
  8. #else
  9. using System.Reflection;
  10. #endif
  11. namespace ProtoBuf.Serializers
  12. {
  13. sealed class ParseableSerializer : IProtoSerializer
  14. {
  15. private readonly MethodInfo parse;
  16. public static ParseableSerializer TryCreate(Type type, TypeModel model)
  17. {
  18. if (type == null) throw new ArgumentNullException("type");
  19. #if WINRT || PORTABLE || COREFX
  20. MethodInfo method = null;
  21. #if WINRT || COREFX
  22. foreach (MethodInfo tmp in type.GetTypeInfo().GetDeclaredMethods("Parse"))
  23. #else
  24. foreach (MethodInfo tmp in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly))
  25. #endif
  26. {
  27. ParameterInfo[] p;
  28. if (tmp.Name == "Parse" && tmp.IsPublic && tmp.IsStatic && tmp.DeclaringType == type && (p = tmp.GetParameters()) != null && p.Length == 1 && p[0].ParameterType == typeof(string))
  29. {
  30. method = tmp;
  31. break;
  32. }
  33. }
  34. #else
  35. MethodInfo method = type.GetMethod("Parse",
  36. BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly,
  37. null, new Type[] { model.MapType(typeof(string)) }, null);
  38. #endif
  39. if (method != null && method.ReturnType == type)
  40. {
  41. if (Helpers.IsValueType(type))
  42. {
  43. MethodInfo toString = GetCustomToString(type);
  44. if (toString == null || toString.ReturnType != model.MapType(typeof(string))) return null; // need custom ToString, fools
  45. }
  46. return new ParseableSerializer(method);
  47. }
  48. return null;
  49. }
  50. private static MethodInfo GetCustomToString(Type type)
  51. {
  52. #if WINRT
  53. foreach (MethodInfo method in type.GetTypeInfo().GetDeclaredMethods("ToString"))
  54. {
  55. if (method.IsPublic && !method.IsStatic && method.GetParameters().Length == 0) return method;
  56. }
  57. return null;
  58. #elif PORTABLE || COREFX
  59. MethodInfo method = Helpers.GetInstanceMethod(type, "ToString", Helpers.EmptyTypes);
  60. if (method == null || !method.IsPublic || method.IsStatic || method.DeclaringType != type) return null;
  61. return method;
  62. #else
  63. return type.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly,
  64. null, Helpers.EmptyTypes, null);
  65. #endif
  66. }
  67. private ParseableSerializer(MethodInfo parse)
  68. {
  69. this.parse = parse;
  70. }
  71. public Type ExpectedType { get { return parse.DeclaringType; } }
  72. bool IProtoSerializer.RequiresOldValue { get { return false; } }
  73. bool IProtoSerializer.ReturnsValue { get { return true; } }
  74. #if !FEAT_IKVM
  75. public object Read(object value, ProtoReader source)
  76. {
  77. Helpers.DebugAssert(value == null); // since replaces
  78. return parse.Invoke(null, new object[] { source.ReadString() });
  79. }
  80. public void Write(object value, ProtoWriter dest)
  81. {
  82. ProtoWriter.WriteString(value.ToString(), dest);
  83. }
  84. #endif
  85. #if FEAT_COMPILER
  86. void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  87. {
  88. Type type = ExpectedType;
  89. if (Helpers.IsValueType(type))
  90. { // note that for structs, we've already asserted that a custom ToString
  91. // exists; no need to handle the box/callvirt scenario
  92. // force it to a variable if needed, so we can take the address
  93. using (Compiler.Local loc = ctx.GetLocalWithValue(type, valueFrom))
  94. {
  95. ctx.LoadAddress(loc, type);
  96. ctx.EmitCall(GetCustomToString(type));
  97. }
  98. }
  99. else {
  100. ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("ToString"));
  101. }
  102. ctx.EmitBasicWrite("WriteString", valueFrom);
  103. }
  104. void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  105. {
  106. ctx.EmitBasicRead("ReadString", ctx.MapType(typeof(string)));
  107. ctx.EmitCall(parse);
  108. }
  109. #endif
  110. }
  111. }
  112. #endif