NullDecorator.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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 NullDecorator : ProtoDecoratorBase
  13. {
  14. private readonly Type expectedType;
  15. public const int Tag = 1;
  16. public NullDecorator(TypeModel model, IProtoSerializer tail) : base(tail)
  17. {
  18. if (!tail.ReturnsValue)
  19. throw new NotSupportedException("NullDecorator only supports implementations that return values");
  20. Type tailType = tail.ExpectedType;
  21. if (Helpers.IsValueType(tailType))
  22. {
  23. #if NO_GENERICS
  24. throw new NotSupportedException("NullDecorator cannot be used with a struct without generics support");
  25. #else
  26. expectedType = model.MapType(typeof(Nullable<>)).MakeGenericType(tailType);
  27. #endif
  28. }
  29. else
  30. {
  31. expectedType = tailType;
  32. }
  33. }
  34. public override Type ExpectedType
  35. {
  36. get { return expectedType; }
  37. }
  38. public override bool ReturnsValue
  39. {
  40. get { return true; }
  41. }
  42. public override bool RequiresOldValue
  43. {
  44. get { return true; }
  45. }
  46. #if FEAT_COMPILER
  47. protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  48. {
  49. using (Compiler.Local oldValue = ctx.GetLocalWithValue(expectedType, valueFrom))
  50. using (Compiler.Local token = new Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))))
  51. using (Compiler.Local field = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
  52. {
  53. ctx.LoadReaderWriter();
  54. ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
  55. ctx.StoreValue(token);
  56. Compiler.CodeLabel next = ctx.DefineLabel(), processField = ctx.DefineLabel(), end = ctx.DefineLabel();
  57. ctx.MarkLabel(next);
  58. ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
  59. ctx.CopyValue();
  60. ctx.StoreValue(field);
  61. ctx.LoadValue(Tag); // = 1 - process
  62. ctx.BranchIfEqual(processField, true);
  63. ctx.LoadValue(field);
  64. ctx.LoadValue(1); // < 1 - exit
  65. ctx.BranchIfLess(end, false);
  66. // default: skip
  67. ctx.LoadReaderWriter();
  68. ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
  69. ctx.Branch(next, true);
  70. // process
  71. ctx.MarkLabel(processField);
  72. if (Tail.RequiresOldValue)
  73. {
  74. if (Helpers.IsValueType(expectedType))
  75. {
  76. ctx.LoadAddress(oldValue, expectedType);
  77. ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
  78. }
  79. else
  80. {
  81. ctx.LoadValue(oldValue);
  82. }
  83. }
  84. Tail.EmitRead(ctx, null);
  85. // note we demanded always returns a value
  86. if (Helpers.IsValueType(expectedType))
  87. {
  88. ctx.EmitCtor(expectedType, Tail.ExpectedType); // re-nullable<T> it
  89. }
  90. ctx.StoreValue(oldValue);
  91. ctx.Branch(next, false);
  92. // outro
  93. ctx.MarkLabel(end);
  94. ctx.LoadValue(token);
  95. ctx.LoadReaderWriter();
  96. ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
  97. ctx.LoadValue(oldValue); // load the old value
  98. }
  99. }
  100. protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  101. {
  102. using(Compiler.Local valOrNull = ctx.GetLocalWithValue(expectedType, valueFrom))
  103. using(Compiler.Local token = new Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken))))
  104. {
  105. ctx.LoadNullRef();
  106. ctx.LoadReaderWriter();
  107. ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
  108. ctx.StoreValue(token);
  109. if (Helpers.IsValueType(expectedType))
  110. {
  111. ctx.LoadAddress(valOrNull, expectedType);
  112. ctx.LoadValue(expectedType.GetProperty("HasValue"));
  113. }
  114. else
  115. {
  116. ctx.LoadValue(valOrNull);
  117. }
  118. Compiler.CodeLabel @end = ctx.DefineLabel();
  119. ctx.BranchIfFalse(@end, false);
  120. if (Helpers.IsValueType(expectedType))
  121. {
  122. ctx.LoadAddress(valOrNull, expectedType);
  123. ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
  124. }
  125. else
  126. {
  127. ctx.LoadValue(valOrNull);
  128. }
  129. Tail.EmitWrite(ctx, null);
  130. ctx.MarkLabel(@end);
  131. ctx.LoadValue(token);
  132. ctx.LoadReaderWriter();
  133. ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
  134. }
  135. }
  136. #endif
  137. #if !FEAT_IKVM
  138. public override object Read(object value, ProtoReader source)
  139. {
  140. SubItemToken tok = ProtoReader.StartSubItem(source);
  141. int field;
  142. while((field = source.ReadFieldHeader()) > 0)
  143. {
  144. if(field == Tag) {
  145. value = Tail.Read(value, source);
  146. } else {
  147. source.SkipField();
  148. }
  149. }
  150. ProtoReader.EndSubItem(tok, source);
  151. return value;
  152. }
  153. public override void Write(object value, ProtoWriter dest)
  154. {
  155. SubItemToken token = ProtoWriter.StartSubItem(null, dest);
  156. if(value != null)
  157. {
  158. Tail.Write(value, dest);
  159. }
  160. ProtoWriter.EndSubItem(token, dest);
  161. }
  162. #endif
  163. }
  164. }
  165. #endif