123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- #if !NO_RUNTIME
- using System;
- using ProtoBuf.Meta;
- #if FEAT_IKVM
- using Type = IKVM.Reflection.Type;
- using IKVM.Reflection;
- #else
- using System.Reflection;
- #endif
- namespace ProtoBuf.Serializers
- {
- sealed class DefaultValueDecorator : ProtoDecoratorBase
- {
- public override Type ExpectedType { get { return Tail.ExpectedType; } }
- public override bool RequiresOldValue { get { return Tail.RequiresOldValue; } }
- public override bool ReturnsValue { get { return Tail.ReturnsValue; } }
- private readonly object defaultValue;
- public DefaultValueDecorator(TypeModel model, object defaultValue, IProtoSerializer tail) : base(tail)
- {
- if (defaultValue == null) throw new ArgumentNullException("defaultValue");
- Type type = model.MapType(defaultValue.GetType());
- if (type != tail.ExpectedType
- #if FEAT_IKVM // in IKVM, we'll have the default value as an underlying type
- && !(tail.ExpectedType.IsEnum && type == tail.ExpectedType.GetEnumUnderlyingType())
- #endif
- )
- {
- throw new ArgumentException("Default value is of incorrect type", "defaultValue");
- }
- this.defaultValue = defaultValue;
- }
- #if !FEAT_IKVM
- public override void Write(object value, ProtoWriter dest)
- {
- if (!object.Equals(value, defaultValue))
- {
- Tail.Write(value, dest);
- }
- }
- public override object Read(object value, ProtoReader source)
- {
- return Tail.Read(value, source);
- }
- #endif
- #if FEAT_COMPILER
- protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
- {
- Compiler.CodeLabel done = ctx.DefineLabel();
- if (valueFrom == null)
- {
- ctx.CopyValue(); // on the stack
- Compiler.CodeLabel needToPop = ctx.DefineLabel();
- EmitBranchIfDefaultValue(ctx, needToPop);
- Tail.EmitWrite(ctx, null);
- ctx.Branch(done, true);
- ctx.MarkLabel(needToPop);
- ctx.DiscardValue();
- }
- else
- {
- ctx.LoadValue(valueFrom); // variable/parameter
- EmitBranchIfDefaultValue(ctx, done);
- Tail.EmitWrite(ctx, valueFrom);
- }
- ctx.MarkLabel(done);
- }
- private void EmitBeq(Compiler.CompilerContext ctx, Compiler.CodeLabel label, Type type)
- {
- switch (Helpers.GetTypeCode(type))
- {
- case ProtoTypeCode.Boolean:
- case ProtoTypeCode.Byte:
- case ProtoTypeCode.Char:
- case ProtoTypeCode.Double:
- case ProtoTypeCode.Int16:
- case ProtoTypeCode.Int32:
- case ProtoTypeCode.Int64:
- case ProtoTypeCode.SByte:
- case ProtoTypeCode.Single:
- case ProtoTypeCode.UInt16:
- case ProtoTypeCode.UInt32:
- case ProtoTypeCode.UInt64:
- ctx.BranchIfEqual(label, false);
- break;
- default:
- #if COREFX
- MethodInfo method = type.GetMethod("op_Equality", new Type[] { type, type });
- if (method == null || !method.IsPublic || !method.IsStatic) method = null;
- #else
- MethodInfo method = type.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static,
- null, new Type[] { type, type}, null);
- #endif
- if (method == null || method.ReturnType != ctx.MapType(typeof(bool)))
- {
- throw new InvalidOperationException("No suitable equality operator found for default-values of type: " + type.FullName);
- }
- ctx.EmitCall(method);
- ctx.BranchIfTrue(label, false);
- break;
- }
- }
- private void EmitBranchIfDefaultValue(Compiler.CompilerContext ctx, Compiler.CodeLabel label)
- {
- Type expected = ExpectedType;
- switch (Helpers.GetTypeCode(expected))
- {
- case ProtoTypeCode.Boolean:
- if ((bool)defaultValue)
- {
- ctx.BranchIfTrue(label, false);
- }
- else
- {
- ctx.BranchIfFalse(label, false);
- }
- break;
- case ProtoTypeCode.Byte:
- if ((byte)defaultValue == (byte)0)
- {
- ctx.BranchIfFalse(label, false);
- }
- else
- {
- ctx.LoadValue((int)(byte)defaultValue);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.SByte:
- if ((sbyte)defaultValue == (sbyte)0)
- {
- ctx.BranchIfFalse(label, false);
- }
- else
- {
- ctx.LoadValue((int)(sbyte)defaultValue);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.Int16:
- if ((short)defaultValue == (short)0)
- {
- ctx.BranchIfFalse(label, false);
- }
- else
- {
- ctx.LoadValue((int)(short)defaultValue);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.UInt16:
- if ((ushort)defaultValue == (ushort)0)
- {
- ctx.BranchIfFalse(label, false);
- }
- else
- {
- ctx.LoadValue((int)(ushort)defaultValue);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.Int32:
- if ((int)defaultValue == (int)0)
- {
- ctx.BranchIfFalse(label, false);
- }
- else
- {
- ctx.LoadValue((int)defaultValue);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.UInt32:
- if ((uint)defaultValue == (uint)0)
- {
- ctx.BranchIfFalse(label, false);
- }
- else
- {
- ctx.LoadValue((int)(uint)defaultValue);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.Char:
- if ((char)defaultValue == (char)0)
- {
- ctx.BranchIfFalse(label, false);
- }
- else
- {
- ctx.LoadValue((int)(char)defaultValue);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.Int64:
- ctx.LoadValue((long)defaultValue);
- EmitBeq(ctx, label, expected);
- break;
- case ProtoTypeCode.UInt64:
- ctx.LoadValue((long)(ulong)defaultValue);
- EmitBeq(ctx, label, expected);
- break;
- case ProtoTypeCode.Double:
- ctx.LoadValue((double)defaultValue);
- EmitBeq(ctx, label, expected);
- break;
- case ProtoTypeCode.Single:
- ctx.LoadValue((float)defaultValue);
- EmitBeq(ctx, label, expected);
- break;
- case ProtoTypeCode.String:
- ctx.LoadValue((string)defaultValue);
- EmitBeq(ctx, label, expected);
- break;
- case ProtoTypeCode.Decimal:
- {
- decimal d = (decimal)defaultValue;
- ctx.LoadValue(d);
- EmitBeq(ctx, label, expected);
- }
- break;
- case ProtoTypeCode.TimeSpan:
- {
- TimeSpan ts = (TimeSpan)defaultValue;
- if (ts == TimeSpan.Zero)
- {
- ctx.LoadValue(typeof(TimeSpan).GetField("Zero"));
- }
- else
- {
- ctx.LoadValue(ts.Ticks);
- ctx.EmitCall(ctx.MapType(typeof(TimeSpan)).GetMethod("FromTicks"));
- }
- EmitBeq(ctx, label, expected);
- break;
- }
- case ProtoTypeCode.Guid:
- {
- ctx.LoadValue((Guid)defaultValue);
- EmitBeq(ctx, label, expected);
- break;
- }
- case ProtoTypeCode.DateTime:
- {
- #if FX11
- ctx.LoadValue(((DateTime)defaultValue).ToFileTime());
- ctx.EmitCall(typeof(DateTime).GetMethod("FromFileTime"));
- #else
- ctx.LoadValue(((DateTime)defaultValue).ToBinary());
- ctx.EmitCall(ctx.MapType(typeof(DateTime)).GetMethod("FromBinary"));
- #endif
- EmitBeq(ctx, label, expected);
- break;
- }
- default:
- throw new NotSupportedException("Type cannot be represented as a default value: " + expected.FullName);
- }
- }
- protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
- {
- Tail.EmitRead(ctx, valueFrom);
- }
- #endif
- }
- }
- #endif
|