SurrogateSerializer.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #if !NO_RUNTIME
  2. using System;
  3. using ProtoBuf.Meta;
  4. using System.Reflection;
  5. namespace ProtoBuf.Serializers
  6. {
  7. sealed class SurrogateSerializer : IProtoTypeSerializer
  8. {
  9. bool IProtoTypeSerializer.HasCallbacks(ProtoBuf.Meta.TypeModel.CallbackType callbackType) { return false; }
  10. #if FEAT_COMPILER
  11. void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, ProtoBuf.Meta.TypeModel.CallbackType callbackType) { }
  12. void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx) { throw new NotSupportedException(); }
  13. #endif
  14. bool IProtoTypeSerializer.CanCreateInstance() => false;
  15. object IProtoTypeSerializer.CreateInstance(ProtoReader source) => throw new NotSupportedException();
  16. void IProtoTypeSerializer.Callback(object value, ProtoBuf.Meta.TypeModel.CallbackType callbackType, SerializationContext context) { }
  17. public bool ReturnsValue => false;
  18. public bool RequiresOldValue => true;
  19. public Type ExpectedType => forType;
  20. private readonly Type forType, declaredType;
  21. private readonly MethodInfo toTail, fromTail;
  22. IProtoTypeSerializer rootTail;
  23. public SurrogateSerializer(TypeModel model, Type forType, Type declaredType, IProtoTypeSerializer rootTail)
  24. {
  25. Helpers.DebugAssert(forType != null, "forType");
  26. Helpers.DebugAssert(declaredType != null, "declaredType");
  27. Helpers.DebugAssert(rootTail != null, "rootTail");
  28. Helpers.DebugAssert(rootTail.RequiresOldValue, "RequiresOldValue");
  29. Helpers.DebugAssert(!rootTail.ReturnsValue, "ReturnsValue");
  30. Helpers.DebugAssert(declaredType == rootTail.ExpectedType || Helpers.IsSubclassOf(declaredType, rootTail.ExpectedType));
  31. this.forType = forType;
  32. this.declaredType = declaredType;
  33. this.rootTail = rootTail;
  34. toTail = GetConversion(model, true);
  35. fromTail = GetConversion(model, false);
  36. }
  37. private static bool HasCast(TypeModel model, Type type, Type from, Type to, out MethodInfo op)
  38. {
  39. #if PROFILE259
  40. System.Collections.Generic.List<MethodInfo> list = new System.Collections.Generic.List<MethodInfo>();
  41. foreach (var item in type.GetRuntimeMethods())
  42. {
  43. if (item.IsStatic) list.Add(item);
  44. }
  45. MethodInfo[] found = list.ToArray();
  46. #else
  47. const BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
  48. MethodInfo[] found = type.GetMethods(flags);
  49. #endif
  50. ParameterInfo[] paramTypes;
  51. Type convertAttributeType = null;
  52. for (int i = 0; i < found.Length; i++)
  53. {
  54. MethodInfo m = found[i];
  55. if (m.ReturnType != to) continue;
  56. paramTypes = m.GetParameters();
  57. if (paramTypes.Length == 1 && paramTypes[0].ParameterType == from)
  58. {
  59. if (convertAttributeType == null)
  60. {
  61. convertAttributeType = model.MapType(typeof(ProtoConverterAttribute), false);
  62. if (convertAttributeType == null)
  63. { // attribute isn't defined in the source assembly: stop looking
  64. break;
  65. }
  66. }
  67. if (m.IsDefined(convertAttributeType, true))
  68. {
  69. op = m;
  70. return true;
  71. }
  72. }
  73. }
  74. for (int i = 0; i < found.Length; i++)
  75. {
  76. MethodInfo m = found[i];
  77. if ((m.Name != "op_Implicit" && m.Name != "op_Explicit") || m.ReturnType != to)
  78. {
  79. continue;
  80. }
  81. paramTypes = m.GetParameters();
  82. if (paramTypes.Length == 1 && paramTypes[0].ParameterType == from)
  83. {
  84. op = m;
  85. return true;
  86. }
  87. }
  88. op = null;
  89. return false;
  90. }
  91. public MethodInfo GetConversion(TypeModel model, bool toTail)
  92. {
  93. Type to = toTail ? declaredType : forType;
  94. Type from = toTail ? forType : declaredType;
  95. MethodInfo op;
  96. if (HasCast(model, declaredType, from, to, out op) || HasCast(model, forType, from, to, out op))
  97. {
  98. return op;
  99. }
  100. throw new InvalidOperationException("No suitable conversion operator found for surrogate: " +
  101. forType.FullName + " / " + declaredType.FullName);
  102. }
  103. public void Write(object value, ProtoWriter writer)
  104. {
  105. rootTail.Write(toTail.Invoke(null, new object[] { value }), writer);
  106. }
  107. public object Read(object value, ProtoReader source)
  108. {
  109. // convert the incoming value
  110. object[] args = { value };
  111. value = toTail.Invoke(null, args);
  112. // invoke the tail and convert the outgoing value
  113. args[0] = rootTail.Read(value, source);
  114. return fromTail.Invoke(null, args);
  115. }
  116. #if FEAT_COMPILER
  117. void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  118. {
  119. Helpers.DebugAssert(valueFrom != null); // don't support stack-head for this
  120. using (Compiler.Local converted = new Compiler.Local(ctx, declaredType)) // declare/re-use local
  121. {
  122. ctx.LoadValue(valueFrom); // load primary onto stack
  123. ctx.EmitCall(toTail); // static convert op, primary-to-surrogate
  124. ctx.StoreValue(converted); // store into surrogate local
  125. rootTail.EmitRead(ctx, converted); // downstream processing against surrogate local
  126. ctx.LoadValue(converted); // load from surrogate local
  127. ctx.EmitCall(fromTail); // static convert op, surrogate-to-primary
  128. ctx.StoreValue(valueFrom); // store back into primary
  129. }
  130. }
  131. void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  132. {
  133. ctx.LoadValue(valueFrom);
  134. ctx.EmitCall(toTail);
  135. rootTail.EmitWrite(ctx, null);
  136. }
  137. #endif
  138. }
  139. }
  140. #endif