151 lines
3.9 KiB
C#
151 lines
3.9 KiB
C#
using System;
|
|||
|
|
using ProtoBuf.Compiler;
|
||
|
|
using ProtoBuf.Meta;
|
||
|
|
|
||
|
|
namespace ProtoBuf.Serializers;
|
||
|
|
|
||
|
|
internal sealed class NullDecorator : ProtoDecoratorBase
|
||
|
|
{
|
||
|
|
private readonly Type expectedType;
|
||
|
|
|
||
|
|
public const int Tag = 1;
|
||
|
|
|
||
|
|
public override Type ExpectedType => expectedType;
|
||
|
|
|
||
|
|
public override bool ReturnsValue => true;
|
||
|
|
|
||
|
|
public override bool RequiresOldValue => true;
|
||
|
|
|
||
|
|
public NullDecorator(TypeModel model, IProtoSerializer tail)
|
||
|
|
: base(tail)
|
||
|
|
{
|
||
|
|
if (!tail.ReturnsValue)
|
||
|
|
{
|
||
|
|
throw new NotSupportedException("NullDecorator only supports implementations that return values");
|
||
|
|
}
|
||
|
|
Type type = tail.ExpectedType;
|
||
|
|
if (Helpers.IsValueType(type))
|
||
|
|
{
|
||
|
|
expectedType = model.MapType(typeof(Nullable<>)).MakeGenericType(type);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
expectedType = type;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void EmitRead(CompilerContext ctx, Local valueFrom)
|
||
|
|
{
|
||
|
|
using Local local = ctx.GetLocalWithValue(expectedType, valueFrom);
|
||
|
|
using Local local2 = new Local(ctx, ctx.MapType(typeof(SubItemToken)));
|
||
|
|
using Local local3 = new Local(ctx, ctx.MapType(typeof(int)));
|
||
|
|
ctx.LoadReaderWriter();
|
||
|
|
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem"));
|
||
|
|
ctx.StoreValue(local2);
|
||
|
|
CodeLabel label = ctx.DefineLabel();
|
||
|
|
CodeLabel label2 = ctx.DefineLabel();
|
||
|
|
CodeLabel label3 = ctx.DefineLabel();
|
||
|
|
ctx.MarkLabel(label);
|
||
|
|
ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
|
||
|
|
ctx.CopyValue();
|
||
|
|
ctx.StoreValue(local3);
|
||
|
|
ctx.LoadValue(1);
|
||
|
|
ctx.BranchIfEqual(label2, @short: true);
|
||
|
|
ctx.LoadValue(local3);
|
||
|
|
ctx.LoadValue(1);
|
||
|
|
ctx.BranchIfLess(label3, @short: false);
|
||
|
|
ctx.LoadReaderWriter();
|
||
|
|
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
|
||
|
|
ctx.Branch(label, @short: true);
|
||
|
|
ctx.MarkLabel(label2);
|
||
|
|
if (Tail.RequiresOldValue)
|
||
|
|
{
|
||
|
|
if (Helpers.IsValueType(expectedType))
|
||
|
|
{
|
||
|
|
ctx.LoadAddress(local, expectedType);
|
||
|
|
ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ctx.LoadValue(local);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Tail.EmitRead(ctx, null);
|
||
|
|
if (Helpers.IsValueType(expectedType))
|
||
|
|
{
|
||
|
|
ctx.EmitCtor(expectedType, Tail.ExpectedType);
|
||
|
|
}
|
||
|
|
ctx.StoreValue(local);
|
||
|
|
ctx.Branch(label, @short: false);
|
||
|
|
ctx.MarkLabel(label3);
|
||
|
|
ctx.LoadValue(local2);
|
||
|
|
ctx.LoadReaderWriter();
|
||
|
|
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem"));
|
||
|
|
ctx.LoadValue(local);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
|
||
|
|
{
|
||
|
|
using Local local = ctx.GetLocalWithValue(expectedType, valueFrom);
|
||
|
|
using Local local2 = new Local(ctx, ctx.MapType(typeof(SubItemToken)));
|
||
|
|
ctx.LoadNullRef();
|
||
|
|
ctx.LoadReaderWriter();
|
||
|
|
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
|
||
|
|
ctx.StoreValue(local2);
|
||
|
|
if (Helpers.IsValueType(expectedType))
|
||
|
|
{
|
||
|
|
ctx.LoadAddress(local, expectedType);
|
||
|
|
ctx.LoadValue(expectedType.GetProperty("HasValue"));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ctx.LoadValue(local);
|
||
|
|
}
|
||
|
|
CodeLabel label = ctx.DefineLabel();
|
||
|
|
ctx.BranchIfFalse(label, @short: false);
|
||
|
|
if (Helpers.IsValueType(expectedType))
|
||
|
|
{
|
||
|
|
ctx.LoadAddress(local, expectedType);
|
||
|
|
ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ctx.LoadValue(local);
|
||
|
|
}
|
||
|
|
Tail.EmitWrite(ctx, null);
|
||
|
|
ctx.MarkLabel(label);
|
||
|
|
ctx.LoadValue(local2);
|
||
|
|
ctx.LoadReaderWriter();
|
||
|
|
ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));
|
||
|
|
}
|
||
|
|
|
||
|
|
public override object Read(object value, ProtoReader source)
|
||
|
|
{
|
||
|
|
SubItemToken token = ProtoReader.StartSubItem(source);
|
||
|
|
int num;
|
||
|
|
while ((num = source.ReadFieldHeader()) > 0)
|
||
|
|
{
|
||
|
|
if (num == 1)
|
||
|
|
{
|
||
|
|
value = Tail.Read(value, source);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
source.SkipField();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ProtoReader.EndSubItem(token, source);
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void Write(object value, ProtoWriter dest)
|
||
|
|
{
|
||
|
|
SubItemToken token = ProtoWriter.StartSubItem(null, dest);
|
||
|
|
if (value != null)
|
||
|
|
{
|
||
|
|
Tail.Write(value, dest);
|
||
|
|
}
|
||
|
|
ProtoWriter.EndSubItem(token, dest);
|
||
|
|
}
|
||
|
|
}
|