SubItemSerializer.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #if !NO_RUNTIME
  2. using System;
  3. using ProtoBuf.Meta;
  4. #if FEAT_COMPILER
  5. #if FEAT_IKVM
  6. using IKVM.Reflection.Emit;
  7. using Type = IKVM.Reflection.Type;
  8. #else
  9. using System.Reflection.Emit;
  10. #endif
  11. #endif
  12. namespace ProtoBuf.Serializers
  13. {
  14. sealed class SubItemSerializer : IProtoTypeSerializer
  15. {
  16. bool IProtoTypeSerializer.HasCallbacks(TypeModel.CallbackType callbackType)
  17. {
  18. return ((IProtoTypeSerializer)proxy.Serializer).HasCallbacks(callbackType);
  19. }
  20. bool IProtoTypeSerializer.CanCreateInstance()
  21. {
  22. return ((IProtoTypeSerializer)proxy.Serializer).CanCreateInstance();
  23. }
  24. #if FEAT_COMPILER
  25. void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
  26. {
  27. ((IProtoTypeSerializer)proxy.Serializer).EmitCallback(ctx, valueFrom, callbackType);
  28. }
  29. void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx)
  30. {
  31. ((IProtoTypeSerializer)proxy.Serializer).EmitCreateInstance(ctx);
  32. }
  33. #endif
  34. #if !FEAT_IKVM
  35. void IProtoTypeSerializer.Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
  36. {
  37. ((IProtoTypeSerializer)proxy.Serializer).Callback(value, callbackType, context);
  38. }
  39. object IProtoTypeSerializer.CreateInstance(ProtoReader source)
  40. {
  41. return ((IProtoTypeSerializer)proxy.Serializer).CreateInstance(source);
  42. }
  43. #endif
  44. private readonly int key;
  45. private readonly Type type;
  46. private readonly ISerializerProxy proxy;
  47. private readonly bool recursionCheck;
  48. public SubItemSerializer(Type type, int key, ISerializerProxy proxy, bool recursionCheck)
  49. {
  50. if (type == null) throw new ArgumentNullException("type");
  51. if (proxy == null) throw new ArgumentNullException("proxy");
  52. this.type = type;
  53. this.proxy= proxy;
  54. this.key = key;
  55. this.recursionCheck = recursionCheck;
  56. }
  57. Type IProtoSerializer.ExpectedType
  58. {
  59. get { return type; }
  60. }
  61. bool IProtoSerializer.RequiresOldValue { get { return true; } }
  62. bool IProtoSerializer.ReturnsValue { get { return true; } }
  63. #if !FEAT_IKVM
  64. void IProtoSerializer.Write(object value, ProtoWriter dest)
  65. {
  66. if (recursionCheck)
  67. {
  68. ProtoWriter.WriteObject(value, key, dest);
  69. }
  70. else
  71. {
  72. ProtoWriter.WriteRecursionSafeObject(value, key, dest);
  73. }
  74. }
  75. object IProtoSerializer.Read(object value, ProtoReader source)
  76. {
  77. return ProtoReader.ReadObject(value, key, source);
  78. }
  79. #endif
  80. #if FEAT_COMPILER
  81. bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read)
  82. {
  83. #if SILVERLIGHT
  84. return false;
  85. #else
  86. MethodBuilder method = ctx.GetDedicatedMethod(key, read);
  87. if (method == null) return false;
  88. using (Compiler.Local token = new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))))
  89. {
  90. Type rwType = ctx.MapType(read ? typeof(ProtoReader) : typeof(ProtoWriter));
  91. ctx.LoadValue(valueFrom);
  92. if (!read) // write requires the object for StartSubItem; read doesn't
  93. { // (if recursion-check is disabled [subtypes] then null is fine too)
  94. if (Helpers.IsValueType(type) || !recursionCheck) { ctx.LoadNullRef(); }
  95. else { ctx.CopyValue(); }
  96. }
  97. ctx.LoadReaderWriter();
  98. ctx.EmitCall(Helpers.GetStaticMethod(rwType, "StartSubItem",
  99. read ? new Type[] { rwType } : new Type[] { ctx.MapType(typeof(object)), rwType }));
  100. ctx.StoreValue(token);
  101. // note: value already on the stack
  102. ctx.LoadReaderWriter();
  103. ctx.EmitCall(method);
  104. // handle inheritance (we will be calling the *base* version of things,
  105. // but we expect Read to return the "type" type)
  106. if (read && type != method.ReturnType) ctx.Cast(this.type);
  107. ctx.LoadValue(token);
  108. ctx.LoadReaderWriter();
  109. ctx.EmitCall(Helpers.GetStaticMethod(rwType, "EndSubItem", new Type[] { ctx.MapType(typeof(SubItemToken)), rwType }));
  110. }
  111. return true;
  112. #endif
  113. }
  114. void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  115. {
  116. if (!EmitDedicatedMethod(ctx, valueFrom, false))
  117. {
  118. ctx.LoadValue(valueFrom);
  119. if (Helpers.IsValueType(type)) ctx.CastToObject(type);
  120. ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method
  121. ctx.LoadReaderWriter();
  122. ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoWriter)), recursionCheck ? "WriteObject" : "WriteRecursionSafeObject", new Type[] { ctx.MapType(typeof(object)), ctx.MapType(typeof(int)), ctx.MapType(typeof(ProtoWriter)) }));
  123. }
  124. }
  125. void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  126. {
  127. if (!EmitDedicatedMethod(ctx, valueFrom, true))
  128. {
  129. ctx.LoadValue(valueFrom);
  130. if (Helpers.IsValueType(type)) ctx.CastToObject(type);
  131. ctx.LoadValue(ctx.MapMetaKeyToCompiledKey(key)); // re-map for formality, but would expect identical, else dedicated method
  132. ctx.LoadReaderWriter();
  133. ctx.EmitCall(Helpers.GetStaticMethod(ctx.MapType(typeof(ProtoReader)), "ReadObject"));
  134. ctx.CastFromObject(type);
  135. }
  136. }
  137. #endif
  138. }
  139. }
  140. #endif