Files
2026-08-27 10:56:38 -06:00

339 lines
8.9 KiB
C#

using System;
using System.Reflection;
using ProtoBuf.Compiler;
using ProtoBuf.Meta;
namespace ProtoBuf.Serializers;
internal sealed class TupleSerializer : IProtoTypeSerializer, IProtoSerializer
{
private readonly MemberInfo[] members;
private readonly ConstructorInfo ctor;
private IProtoSerializer[] tails;
public Type ExpectedType => ctor.DeclaringType;
public bool RequiresOldValue => true;
public bool ReturnsValue => false;
public TupleSerializer(RuntimeTypeModel model, ConstructorInfo ctor, MemberInfo[] members)
{
this.ctor = ctor ?? throw new ArgumentNullException("ctor");
this.members = members ?? throw new ArgumentNullException("members");
tails = new IProtoSerializer[members.Length];
ParameterInfo[] parameters = ctor.GetParameters();
for (int i = 0; i < members.Length; i++)
{
Type parameterType = parameters[i].ParameterType;
Type itemType = null;
Type defaultType = null;
MetaType.ResolveListTypes(model, parameterType, ref itemType, ref defaultType);
Type type = (((object)itemType == null) ? parameterType : itemType);
bool asReference = false;
int num = model.FindOrAddAuto(type, demand: false, addWithContractOnly: true, addEvenIfAutoDisabled: false);
if (num >= 0)
{
asReference = model[type].AsReferenceDefault;
}
IProtoSerializer protoSerializer = ValueMember.TryGetCoreSerializer(model, DataFormat.Default, type, out var defaultWireType, asReference, dynamicType: false, overwriteList: false, allowComplexTypes: true);
if (protoSerializer == null)
{
throw new InvalidOperationException("No serializer defined for type: " + type.FullName);
}
protoSerializer = new TagDecorator(i + 1, defaultWireType, strict: false, protoSerializer);
IProtoSerializer protoSerializer2 = (((object)itemType != null) ? ((!parameterType.IsArray) ? ((ProtoDecoratorBase)ListDecorator.Create(model, parameterType, defaultType, protoSerializer, i + 1, writePacked: false, defaultWireType, returnList: true, overwriteList: false, supportNull: false)) : ((ProtoDecoratorBase)new ArrayDecorator(model, protoSerializer, i + 1, writePacked: false, defaultWireType, parameterType, overwriteList: false, supportNull: false))) : protoSerializer);
tails[i] = protoSerializer2;
}
}
public bool HasCallbacks(TypeModel.CallbackType callbackType)
{
return false;
}
public void EmitCallback(CompilerContext ctx, Local valueFrom, TypeModel.CallbackType callbackType)
{
}
void IProtoTypeSerializer.Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
{
}
object IProtoTypeSerializer.CreateInstance(ProtoReader source)
{
throw new NotSupportedException();
}
private object GetValue(object obj, int index)
{
if (members[index] is PropertyInfo propertyInfo)
{
if (obj == null)
{
if (!Helpers.IsValueType(propertyInfo.PropertyType))
{
return null;
}
return Activator.CreateInstance(propertyInfo.PropertyType);
}
return propertyInfo.GetValue(obj, null);
}
if (members[index] is FieldInfo fieldInfo)
{
if (obj == null)
{
if (!Helpers.IsValueType(fieldInfo.FieldType))
{
return null;
}
return Activator.CreateInstance(fieldInfo.FieldType);
}
return fieldInfo.GetValue(obj);
}
throw new InvalidOperationException();
}
public object Read(object value, ProtoReader source)
{
object[] array = new object[members.Length];
bool flag = false;
if (value == null)
{
flag = true;
}
for (int i = 0; i < array.Length; i++)
{
array[i] = GetValue(value, i);
}
int num;
while ((num = source.ReadFieldHeader()) > 0)
{
flag = true;
if (num <= tails.Length)
{
IProtoSerializer protoSerializer = tails[num - 1];
array[num - 1] = tails[num - 1].Read(protoSerializer.RequiresOldValue ? array[num - 1] : null, source);
}
else
{
source.SkipField();
}
}
if (!flag)
{
return value;
}
return ctor.Invoke(array);
}
public void Write(object value, ProtoWriter dest)
{
for (int i = 0; i < tails.Length; i++)
{
object value2 = GetValue(value, i);
if (value2 != null)
{
tails[i].Write(value2, dest);
}
}
}
private Type GetMemberType(int index)
{
Type memberType = Helpers.GetMemberType(members[index]);
if ((object)memberType == null)
{
throw new InvalidOperationException();
}
return memberType;
}
bool IProtoTypeSerializer.CanCreateInstance()
{
return false;
}
public void EmitWrite(CompilerContext ctx, Local valueFrom)
{
using Local local = ctx.GetLocalWithValue(ctor.DeclaringType, valueFrom);
for (int i = 0; i < tails.Length; i++)
{
Type memberType = GetMemberType(i);
ctx.LoadAddress(local, ExpectedType);
if (members[i] is FieldInfo)
{
ctx.LoadValue((FieldInfo)members[i]);
}
else if (members[i] is PropertyInfo)
{
ctx.LoadValue((PropertyInfo)members[i]);
}
ctx.WriteNullCheckedTail(memberType, tails[i], null);
}
}
void IProtoTypeSerializer.EmitCreateInstance(CompilerContext ctx)
{
throw new NotSupportedException();
}
public void EmitRead(CompilerContext ctx, Local incoming)
{
using Local local = ctx.GetLocalWithValue(ExpectedType, incoming);
Local[] array = new Local[members.Length];
try
{
for (int i = 0; i < array.Length; i++)
{
Type memberType = GetMemberType(i);
bool flag = true;
array[i] = new Local(ctx, memberType);
if (Helpers.IsValueType(ExpectedType))
{
continue;
}
if (Helpers.IsValueType(memberType))
{
switch (Helpers.GetTypeCode(memberType))
{
case ProtoTypeCode.Boolean:
case ProtoTypeCode.SByte:
case ProtoTypeCode.Byte:
case ProtoTypeCode.Int16:
case ProtoTypeCode.UInt16:
case ProtoTypeCode.Int32:
case ProtoTypeCode.UInt32:
ctx.LoadValue(0);
break;
case ProtoTypeCode.Int64:
case ProtoTypeCode.UInt64:
ctx.LoadValue(0L);
break;
case ProtoTypeCode.Single:
ctx.LoadValue(0f);
break;
case ProtoTypeCode.Double:
ctx.LoadValue(0.0);
break;
case ProtoTypeCode.Decimal:
ctx.LoadValue(0m);
break;
case ProtoTypeCode.Guid:
ctx.LoadValue(Guid.Empty);
break;
default:
ctx.LoadAddress(array[i], memberType);
ctx.EmitCtor(memberType);
flag = false;
break;
}
}
else
{
ctx.LoadNullRef();
}
if (flag)
{
ctx.StoreValue(array[i]);
}
}
CodeLabel label = (Helpers.IsValueType(ExpectedType) ? default(CodeLabel) : ctx.DefineLabel());
if (!Helpers.IsValueType(ExpectedType))
{
ctx.LoadAddress(local, ExpectedType);
ctx.BranchIfFalse(label, @short: false);
}
for (int j = 0; j < members.Length; j++)
{
ctx.LoadAddress(local, ExpectedType);
if (members[j] is FieldInfo)
{
ctx.LoadValue((FieldInfo)members[j]);
}
else if (members[j] is PropertyInfo)
{
ctx.LoadValue((PropertyInfo)members[j]);
}
ctx.StoreValue(array[j]);
}
if (!Helpers.IsValueType(ExpectedType))
{
ctx.MarkLabel(label);
}
using (Local local2 = new Local(ctx, ctx.MapType(typeof(int))))
{
CodeLabel label2 = ctx.DefineLabel();
CodeLabel label3 = ctx.DefineLabel();
CodeLabel label4 = ctx.DefineLabel();
ctx.Branch(label2, @short: false);
CodeLabel[] array2 = new CodeLabel[members.Length];
for (int k = 0; k < members.Length; k++)
{
array2[k] = ctx.DefineLabel();
}
ctx.MarkLabel(label3);
ctx.LoadValue(local2);
ctx.LoadValue(1);
ctx.Subtract();
ctx.Switch(array2);
ctx.Branch(label4, @short: false);
for (int l = 0; l < array2.Length; l++)
{
ctx.MarkLabel(array2[l]);
IProtoSerializer protoSerializer = tails[l];
Local valueFrom = (protoSerializer.RequiresOldValue ? array[l] : null);
ctx.ReadNullCheckedTail(array[l].Type, protoSerializer, valueFrom);
if (protoSerializer.ReturnsValue)
{
if (Helpers.IsValueType(array[l].Type))
{
ctx.StoreValue(array[l]);
}
else
{
CodeLabel label5 = ctx.DefineLabel();
CodeLabel label6 = ctx.DefineLabel();
ctx.CopyValue();
ctx.BranchIfTrue(label5, @short: true);
ctx.DiscardValue();
ctx.Branch(label6, @short: true);
ctx.MarkLabel(label5);
ctx.StoreValue(array[l]);
ctx.MarkLabel(label6);
}
}
ctx.Branch(label2, @short: false);
}
ctx.MarkLabel(label4);
ctx.LoadReaderWriter();
ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
ctx.MarkLabel(label2);
ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
ctx.CopyValue();
ctx.StoreValue(local2);
ctx.LoadValue(0);
ctx.BranchIfGreater(label3, @short: false);
}
for (int m = 0; m < array.Length; m++)
{
ctx.LoadValue(array[m]);
}
ctx.EmitCtor(ctor);
ctx.StoreValue(local);
}
finally
{
for (int n = 0; n < array.Length; n++)
{
if (array[n] != null)
{
array[n].Dispose();
}
}
}
}
}