SurrogateSerializer.cs 6.5 KB

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