DefaultValueDecorator.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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 DefaultValueDecorator : ProtoDecoratorBase
  13. {
  14. public override Type ExpectedType { get { return Tail.ExpectedType; } }
  15. public override bool RequiresOldValue { get { return Tail.RequiresOldValue; } }
  16. public override bool ReturnsValue { get { return Tail.ReturnsValue; } }
  17. private readonly object defaultValue;
  18. public DefaultValueDecorator(TypeModel model, object defaultValue, IProtoSerializer tail) : base(tail)
  19. {
  20. if (defaultValue == null) throw new ArgumentNullException("defaultValue");
  21. Type type = model.MapType(defaultValue.GetType());
  22. if (type != tail.ExpectedType
  23. #if FEAT_IKVM // in IKVM, we'll have the default value as an underlying type
  24. && !(tail.ExpectedType.IsEnum && type == tail.ExpectedType.GetEnumUnderlyingType())
  25. #endif
  26. )
  27. {
  28. throw new ArgumentException("Default value is of incorrect type", "defaultValue");
  29. }
  30. this.defaultValue = defaultValue;
  31. }
  32. #if !FEAT_IKVM
  33. public override void Write(object value, ProtoWriter dest)
  34. {
  35. if (!object.Equals(value, defaultValue))
  36. {
  37. Tail.Write(value, dest);
  38. }
  39. }
  40. public override object Read(object value, ProtoReader source)
  41. {
  42. return Tail.Read(value, source);
  43. }
  44. #endif
  45. #if FEAT_COMPILER
  46. protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  47. {
  48. Compiler.CodeLabel done = ctx.DefineLabel();
  49. if (valueFrom == null)
  50. {
  51. ctx.CopyValue(); // on the stack
  52. Compiler.CodeLabel needToPop = ctx.DefineLabel();
  53. EmitBranchIfDefaultValue(ctx, needToPop);
  54. Tail.EmitWrite(ctx, null);
  55. ctx.Branch(done, true);
  56. ctx.MarkLabel(needToPop);
  57. ctx.DiscardValue();
  58. }
  59. else
  60. {
  61. ctx.LoadValue(valueFrom); // variable/parameter
  62. EmitBranchIfDefaultValue(ctx, done);
  63. Tail.EmitWrite(ctx, valueFrom);
  64. }
  65. ctx.MarkLabel(done);
  66. }
  67. private void EmitBeq(Compiler.CompilerContext ctx, Compiler.CodeLabel label, Type type)
  68. {
  69. switch (Helpers.GetTypeCode(type))
  70. {
  71. case ProtoTypeCode.Boolean:
  72. case ProtoTypeCode.Byte:
  73. case ProtoTypeCode.Char:
  74. case ProtoTypeCode.Double:
  75. case ProtoTypeCode.Int16:
  76. case ProtoTypeCode.Int32:
  77. case ProtoTypeCode.Int64:
  78. case ProtoTypeCode.SByte:
  79. case ProtoTypeCode.Single:
  80. case ProtoTypeCode.UInt16:
  81. case ProtoTypeCode.UInt32:
  82. case ProtoTypeCode.UInt64:
  83. ctx.BranchIfEqual(label, false);
  84. break;
  85. default:
  86. #if COREFX
  87. MethodInfo method = type.GetMethod("op_Equality", new Type[] { type, type });
  88. if (method == null || !method.IsPublic || !method.IsStatic) method = null;
  89. #else
  90. MethodInfo method = type.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static,
  91. null, new Type[] { type, type}, null);
  92. #endif
  93. if (method == null || method.ReturnType != ctx.MapType(typeof(bool)))
  94. {
  95. throw new InvalidOperationException("No suitable equality operator found for default-values of type: " + type.FullName);
  96. }
  97. ctx.EmitCall(method);
  98. ctx.BranchIfTrue(label, false);
  99. break;
  100. }
  101. }
  102. private void EmitBranchIfDefaultValue(Compiler.CompilerContext ctx, Compiler.CodeLabel label)
  103. {
  104. Type expected = ExpectedType;
  105. switch (Helpers.GetTypeCode(expected))
  106. {
  107. case ProtoTypeCode.Boolean:
  108. if ((bool)defaultValue)
  109. {
  110. ctx.BranchIfTrue(label, false);
  111. }
  112. else
  113. {
  114. ctx.BranchIfFalse(label, false);
  115. }
  116. break;
  117. case ProtoTypeCode.Byte:
  118. if ((byte)defaultValue == (byte)0)
  119. {
  120. ctx.BranchIfFalse(label, false);
  121. }
  122. else
  123. {
  124. ctx.LoadValue((int)(byte)defaultValue);
  125. EmitBeq(ctx, label, expected);
  126. }
  127. break;
  128. case ProtoTypeCode.SByte:
  129. if ((sbyte)defaultValue == (sbyte)0)
  130. {
  131. ctx.BranchIfFalse(label, false);
  132. }
  133. else
  134. {
  135. ctx.LoadValue((int)(sbyte)defaultValue);
  136. EmitBeq(ctx, label, expected);
  137. }
  138. break;
  139. case ProtoTypeCode.Int16:
  140. if ((short)defaultValue == (short)0)
  141. {
  142. ctx.BranchIfFalse(label, false);
  143. }
  144. else
  145. {
  146. ctx.LoadValue((int)(short)defaultValue);
  147. EmitBeq(ctx, label, expected);
  148. }
  149. break;
  150. case ProtoTypeCode.UInt16:
  151. if ((ushort)defaultValue == (ushort)0)
  152. {
  153. ctx.BranchIfFalse(label, false);
  154. }
  155. else
  156. {
  157. ctx.LoadValue((int)(ushort)defaultValue);
  158. EmitBeq(ctx, label, expected);
  159. }
  160. break;
  161. case ProtoTypeCode.Int32:
  162. if ((int)defaultValue == (int)0)
  163. {
  164. ctx.BranchIfFalse(label, false);
  165. }
  166. else
  167. {
  168. ctx.LoadValue((int)defaultValue);
  169. EmitBeq(ctx, label, expected);
  170. }
  171. break;
  172. case ProtoTypeCode.UInt32:
  173. if ((uint)defaultValue == (uint)0)
  174. {
  175. ctx.BranchIfFalse(label, false);
  176. }
  177. else
  178. {
  179. ctx.LoadValue((int)(uint)defaultValue);
  180. EmitBeq(ctx, label, expected);
  181. }
  182. break;
  183. case ProtoTypeCode.Char:
  184. if ((char)defaultValue == (char)0)
  185. {
  186. ctx.BranchIfFalse(label, false);
  187. }
  188. else
  189. {
  190. ctx.LoadValue((int)(char)defaultValue);
  191. EmitBeq(ctx, label, expected);
  192. }
  193. break;
  194. case ProtoTypeCode.Int64:
  195. ctx.LoadValue((long)defaultValue);
  196. EmitBeq(ctx, label, expected);
  197. break;
  198. case ProtoTypeCode.UInt64:
  199. ctx.LoadValue((long)(ulong)defaultValue);
  200. EmitBeq(ctx, label, expected);
  201. break;
  202. case ProtoTypeCode.Double:
  203. ctx.LoadValue((double)defaultValue);
  204. EmitBeq(ctx, label, expected);
  205. break;
  206. case ProtoTypeCode.Single:
  207. ctx.LoadValue((float)defaultValue);
  208. EmitBeq(ctx, label, expected);
  209. break;
  210. case ProtoTypeCode.String:
  211. ctx.LoadValue((string)defaultValue);
  212. EmitBeq(ctx, label, expected);
  213. break;
  214. case ProtoTypeCode.Decimal:
  215. {
  216. decimal d = (decimal)defaultValue;
  217. ctx.LoadValue(d);
  218. EmitBeq(ctx, label, expected);
  219. }
  220. break;
  221. case ProtoTypeCode.TimeSpan:
  222. {
  223. TimeSpan ts = (TimeSpan)defaultValue;
  224. if (ts == TimeSpan.Zero)
  225. {
  226. ctx.LoadValue(typeof(TimeSpan).GetField("Zero"));
  227. }
  228. else
  229. {
  230. ctx.LoadValue(ts.Ticks);
  231. ctx.EmitCall(ctx.MapType(typeof(TimeSpan)).GetMethod("FromTicks"));
  232. }
  233. EmitBeq(ctx, label, expected);
  234. break;
  235. }
  236. case ProtoTypeCode.Guid:
  237. {
  238. ctx.LoadValue((Guid)defaultValue);
  239. EmitBeq(ctx, label, expected);
  240. break;
  241. }
  242. case ProtoTypeCode.DateTime:
  243. {
  244. #if FX11
  245. ctx.LoadValue(((DateTime)defaultValue).ToFileTime());
  246. ctx.EmitCall(typeof(DateTime).GetMethod("FromFileTime"));
  247. #else
  248. ctx.LoadValue(((DateTime)defaultValue).ToBinary());
  249. ctx.EmitCall(ctx.MapType(typeof(DateTime)).GetMethod("FromBinary"));
  250. #endif
  251. EmitBeq(ctx, label, expected);
  252. break;
  253. }
  254. default:
  255. throw new NotSupportedException("Type cannot be represented as a default value: " + expected.FullName);
  256. }
  257. }
  258. protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  259. {
  260. Tail.EmitRead(ctx, valueFrom);
  261. }
  262. #endif
  263. }
  264. }
  265. #endif