1
Fork 0
mirror of https://github.com/Steffo99/better-tee.git synced 2024-11-27 00:54:20 +00:00
better-tee/Assets/Packages/Mirror/Editor/Weaver/Readers.cs

372 lines
14 KiB
C#
Raw Normal View History

2019-09-15 22:28:36 +00:00
using System.Collections.Generic;
using Mono.CecilX;
using Mono.CecilX.Cil;
using Mono.CecilX.Rocks;
namespace Mirror.Weaver
{
public static class Readers
{
const int MaxRecursionCount = 128;
static Dictionary<string, MethodReference> readFuncs;
public static void Init()
{
readFuncs = new Dictionary<string, MethodReference>();
}
internal static void Register(TypeReference dataType, MethodReference methodReference)
{
readFuncs[dataType.FullName] = methodReference;
}
public static MethodReference GetReadFunc(TypeReference variable, int recursionCount = 0)
{
if (readFuncs.TryGetValue(variable.FullName, out MethodReference foundFunc))
{
return foundFunc;
}
TypeDefinition td = variable.Resolve();
if (td == null)
{
Weaver.Error($"{variable} is not a supported type");
return null;
}
if (variable.IsByReference)
{
// error??
Weaver.Error($"{variable} is not a supported reference type");
return null;
}
MethodDefinition newReaderFunc;
if (variable.IsArray)
{
newReaderFunc = GenerateArrayReadFunc(variable, recursionCount);
}
else if (td.IsEnum)
{
return GetReadFunc(td.GetEnumUnderlyingType(), recursionCount);
}
else if (variable.FullName.StartsWith("System.ArraySegment`1", System.StringComparison.Ordinal))
{
newReaderFunc = GenerateArraySegmentReadFunc(variable, recursionCount);
}
else
{
newReaderFunc = GenerateStructReadFunction(variable, recursionCount);
}
if (newReaderFunc == null)
{
Weaver.Error($"{variable} is not a supported type");
return null;
}
RegisterReadFunc(variable.FullName, newReaderFunc);
return newReaderFunc;
}
static void RegisterReadFunc(string name, MethodDefinition newReaderFunc)
{
readFuncs[name] = newReaderFunc;
Weaver.WeaveLists.generatedReadFunctions.Add(newReaderFunc);
Weaver.ConfirmGeneratedCodeClass();
Weaver.WeaveLists.generateContainerClass.Methods.Add(newReaderFunc);
}
static MethodDefinition GenerateArrayReadFunc(TypeReference variable, int recursionCount)
{
if (!variable.IsArrayType())
{
Weaver.Error($"{variable} is an unsupported type. Jagged and multidimensional arrays are not supported");
return null;
}
TypeReference elementType = variable.GetElementType();
MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1);
if (elementReadFunc == null)
{
return null;
}
string functionName = "_ReadArray" + variable.GetElementType().Name + "_";
if (variable.DeclaringType != null)
{
functionName += variable.DeclaringType.Name;
}
else
{
functionName += "None";
}
// create new reader for this type
MethodDefinition readerFunc = new MethodDefinition(functionName,
MethodAttributes.Public |
MethodAttributes.Static |
MethodAttributes.HideBySig,
variable);
readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type));
readerFunc.Body.Variables.Add(new VariableDefinition(variable));
readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type));
readerFunc.Body.InitLocals = true;
ILProcessor worker = readerFunc.Body.GetILProcessor();
// int length = reader.ReadPackedInt32();
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, GetReadFunc(Weaver.int32Type)));
worker.Append(worker.Create(OpCodes.Stloc_0));
// if (length < 0) {
// return null
// }
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Ldc_I4_0));
Instruction labelEmptyArray = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Bge, labelEmptyArray));
// return null
worker.Append(worker.Create(OpCodes.Ldnull));
worker.Append(worker.Create(OpCodes.Ret));
worker.Append(labelEmptyArray);
// T value = new T[length];
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Newarr, variable.GetElementType()));
worker.Append(worker.Create(OpCodes.Stloc_1));
// for (int i=0; i< length ; i++) {
worker.Append(worker.Create(OpCodes.Ldc_I4_0));
worker.Append(worker.Create(OpCodes.Stloc_2));
Instruction labelHead = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Br, labelHead));
// loop body
Instruction labelBody = worker.Create(OpCodes.Nop);
worker.Append(labelBody);
// value[i] = reader.ReadT();
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldelema, variable.GetElementType()));
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, elementReadFunc));
worker.Append(worker.Create(OpCodes.Stobj, variable.GetElementType()));
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldc_I4_1));
worker.Append(worker.Create(OpCodes.Add));
worker.Append(worker.Create(OpCodes.Stloc_2));
// loop while check
worker.Append(labelHead);
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Blt, labelBody));
// return value;
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Ret));
return readerFunc;
}
static MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, int recursionCount)
{
GenericInstanceType genericInstance = (GenericInstanceType)variable;
TypeReference elementType = genericInstance.GenericArguments[0];
MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1);
if (elementReadFunc == null)
{
return null;
}
string functionName = "_ReadArraySegment_" + variable.GetElementType().Name + "_";
if (variable.DeclaringType != null)
{
functionName += variable.DeclaringType.Name;
}
else
{
functionName += "None";
}
// create new reader for this type
MethodDefinition readerFunc = new MethodDefinition(functionName,
MethodAttributes.Public |
MethodAttributes.Static |
MethodAttributes.HideBySig,
variable);
readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
// int lengh
readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type));
// T[] array
readerFunc.Body.Variables.Add(new VariableDefinition(elementType.MakeArrayType()));
// int i;
readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type));
readerFunc.Body.InitLocals = true;
ILProcessor worker = readerFunc.Body.GetILProcessor();
// int length = reader.ReadPackedInt32();
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, GetReadFunc(Weaver.int32Type)));
worker.Append(worker.Create(OpCodes.Stloc_0));
// T[] array = new int[length]
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Newarr, elementType));
worker.Append(worker.Create(OpCodes.Stloc_1));
// loop through array and deserialize each element
// generates code like this
// for (int i=0; i< length ; i++)
// {
// value[i] = reader.ReadXXX();
// }
worker.Append(worker.Create(OpCodes.Ldc_I4_0));
worker.Append(worker.Create(OpCodes.Stloc_2));
Instruction labelHead = worker.Create(OpCodes.Nop);
worker.Append(worker.Create(OpCodes.Br, labelHead));
// loop body
Instruction labelBody = worker.Create(OpCodes.Nop);
worker.Append(labelBody);
{
// value[i] = reader.ReadT();
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldelema, elementType));
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, elementReadFunc));
worker.Append(worker.Create(OpCodes.Stobj, elementType));
}
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldc_I4_1));
worker.Append(worker.Create(OpCodes.Add));
worker.Append(worker.Create(OpCodes.Stloc_2));
// loop while check
worker.Append(labelHead);
worker.Append(worker.Create(OpCodes.Ldloc_2));
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Blt, labelBody));
// return new ArraySegment<T>(array);
worker.Append(worker.Create(OpCodes.Ldloc_1));
worker.Append(worker.Create(OpCodes.Newobj, Weaver.ArraySegmentConstructorReference.MakeHostInstanceGeneric(genericInstance)));
worker.Append(worker.Create(OpCodes.Ret));
return readerFunc;
}
static MethodDefinition GenerateStructReadFunction(TypeReference variable, int recursionCount)
{
if (recursionCount > MaxRecursionCount)
{
Weaver.Error($"{variable} can't be deserialized because it references itself");
return null;
}
if (!Weaver.IsValidTypeToGenerate(variable.Resolve()))
{
return null;
}
string functionName = "_Read" + variable.Name + "_";
if (variable.DeclaringType != null)
{
functionName += variable.DeclaringType.Name;
}
else
{
functionName += "None";
}
// create new reader for this type
MethodDefinition readerFunc = new MethodDefinition(functionName,
MethodAttributes.Public |
MethodAttributes.Static |
MethodAttributes.HideBySig,
variable);
// create local for return value
readerFunc.Body.Variables.Add(new VariableDefinition(variable));
readerFunc.Body.InitLocals = true;
readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
ILProcessor worker = readerFunc.Body.GetILProcessor();
if (variable.IsValueType)
{
// structs are created with Initobj
worker.Append(worker.Create(OpCodes.Ldloca, 0));
worker.Append(worker.Create(OpCodes.Initobj, variable));
}
else
{
// classes are created with their constructor
MethodDefinition ctor = Resolvers.ResolveDefaultPublicCtor(variable);
if (ctor == null)
{
Weaver.Error($"{variable} can't be deserialized bcause i has no default constructor");
return null;
}
worker.Append(worker.Create(OpCodes.Newobj, ctor));
worker.Append(worker.Create(OpCodes.Stloc_0));
}
uint fields = 0;
foreach (FieldDefinition field in variable.Resolve().Fields)
{
if (field.IsStatic || field.IsPrivate)
continue;
// mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime
OpCode opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc;
worker.Append(worker.Create(opcode, 0));
MethodReference readFunc = GetReadFunc(field.FieldType, recursionCount + 1);
if (readFunc != null)
{
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Call, readFunc));
}
else
{
Weaver.Error($"{field} has an unsupported type");
return null;
}
worker.Append(worker.Create(OpCodes.Stfld, field));
fields++;
}
if (fields == 0)
{
Log.Warning($"{variable} has no public or non-static fields to deserialize");
}
worker.Append(worker.Create(OpCodes.Ldloc_0));
worker.Append(worker.Create(OpCodes.Ret));
return readerFunc;
}
}
}