ParseableSerializer.cs 4.4 KB

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