synclist code generator is now reusable (#624)

* refactor: Made serializer generation reusable

* refactor: SyncList code generator is reusable for other structs
This commit is contained in:
Paul Pacheco 2019-03-22 06:44:57 -05:00 committed by GitHub
parent 1d166c699a
commit b7e977a7a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 142 additions and 106 deletions

View File

@ -6,112 +6,13 @@ namespace Mirror.Weaver
{ {
static class SyncListProcessor static class SyncListProcessor
{ {
/// <summary>
/// Generates serialization methods for synclists
/// </summary>
/// <param name="td">The synclist class</param>
public static void Process(TypeDefinition td) public static void Process(TypeDefinition td)
{ {
// find item type SyncObjectProcessor.GenerateSerialization(td, 0, "SerializeItem", "DeserializeItem");
GenericInstanceType gt = (GenericInstanceType)td.BaseType;
if (gt.GenericArguments.Count == 0)
{
Weaver.Error("SyncListProcessor no generic args");
return;
}
TypeReference itemType = Weaver.CurrentAssembly.MainModule.ImportReference(gt.GenericArguments[0]);
Weaver.DLog(td, "SyncListProcessor Start item:" + itemType.FullName);
Weaver.ResetRecursionCount();
MethodReference writeItemFunc = GenerateSerialization(td, itemType);
if (Weaver.WeavingFailed)
{
return;
}
MethodReference readItemFunc = GenerateDeserialization(td, itemType);
if (readItemFunc == null || writeItemFunc == null)
return;
Weaver.DLog(td, "SyncListProcessor Done");
}
// serialization of individual element
static MethodReference GenerateSerialization(TypeDefinition td, TypeReference itemType)
{
Weaver.DLog(td, " GenerateSerialization");
foreach (var m in td.Methods)
{
if (m.Name == "SerializeItem")
return m;
}
MethodDefinition serializeFunc = new MethodDefinition("SerializeItem", MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Public |
MethodAttributes.HideBySig,
Weaver.voidType);
serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType));
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
if (itemType.IsGenericInstance)
{
Weaver.Error("GenerateSerialization for " + Helpers.PrettyPrintType(itemType) + " failed. Struct passed into SyncList<T> can't have generic parameters");
return null;
}
MethodReference writeFunc = Weaver.GetWriteFunc(itemType);
if (writeFunc != null)
{
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_2));
serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
}
else
{
Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + itemType + "/" + itemType.FullName + "]. [SyncList] member variables must be basic types.");
return null;
}
serWorker.Append(serWorker.Create(OpCodes.Ret));
td.Methods.Add(serializeFunc);
return serializeFunc;
}
static MethodReference GenerateDeserialization(TypeDefinition td, TypeReference itemType)
{
Weaver.DLog(td, " GenerateDeserialization");
foreach (var m in td.Methods)
{
if (m.Name == "DeserializeItem")
return m;
}
MethodDefinition deserializeFunction = new MethodDefinition("DeserializeItem", MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Public |
MethodAttributes.HideBySig,
itemType);
deserializeFunction.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
ILProcessor serWorker = deserializeFunction.Body.GetILProcessor();
MethodReference readerFunc = Weaver.GetReadFunc(itemType);
if (readerFunc != null)
{
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc));
serWorker.Append(serWorker.Create(OpCodes.Ret));
}
else
{
Weaver.Error("GenerateDeserialization for " + td.Name + " unknown type [" + itemType + "]. [SyncList] member variables must be basic types.");
return null;
}
td.Methods.Add(deserializeFunction);
return deserializeFunction;
} }
} }
} }

View File

@ -0,0 +1,124 @@
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace Mirror.Weaver
{
public static class SyncObjectProcessor
{
/// <summary>
/// Generates the serialization and deserialization methods for a specified generic argument
/// </summary>
/// <param name="td">The type of the class that needs serialization methods</param>
/// <param name="genericArgument">Which generic argument to serialize, 0 is the first one</param>
/// <param name="serializeMethod">The name of the serialize method</param>
/// <param name="deserializeMethod">The name of the deserialize method</param>
public static void GenerateSerialization(TypeDefinition td, int genericArgument, string serializeMethod, string deserializeMethod)
{
// find item type
GenericInstanceType gt = (GenericInstanceType)td.BaseType;
if (gt.GenericArguments.Count <= genericArgument)
{
Weaver.Error("SyncObjectProcessor no generic args");
return;
}
TypeReference itemType = Weaver.CurrentAssembly.MainModule.ImportReference(gt.GenericArguments[genericArgument]);
Weaver.DLog(td, "SyncObjectProcessor Start item:" + itemType.FullName);
Weaver.ResetRecursionCount();
MethodReference writeItemFunc = GenerateSerialization(serializeMethod, td, itemType);
if (Weaver.WeavingFailed)
{
return;
}
MethodReference readItemFunc = GenerateDeserialization(deserializeMethod, td, itemType);
if (readItemFunc == null || writeItemFunc == null)
return;
Weaver.DLog(td, "SyncObjectProcessor Done");
}
// serialization of individual element
static MethodReference GenerateSerialization(string methodName, TypeDefinition td, TypeReference itemType)
{
Weaver.DLog(td, " GenerateSerialization");
foreach (var m in td.Methods)
{
if (m.Name == methodName)
return m;
}
MethodDefinition serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Public |
MethodAttributes.HideBySig,
Weaver.voidType);
serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType)));
serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType));
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
if (itemType.IsGenericInstance)
{
Weaver.Error("GenerateSerialization for " + Helpers.PrettyPrintType(itemType) + " failed. Can't have generic parameters");
return null;
}
MethodReference writeFunc = Weaver.GetWriteFunc(itemType);
if (writeFunc != null)
{
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_2));
serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
}
else
{
Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + itemType + "/" + itemType.FullName + "]. Member variables must be basic types.");
return null;
}
serWorker.Append(serWorker.Create(OpCodes.Ret));
td.Methods.Add(serializeFunc);
return serializeFunc;
}
static MethodReference GenerateDeserialization(string methodName, TypeDefinition td, TypeReference itemType)
{
Weaver.DLog(td, " GenerateDeserialization");
foreach (var m in td.Methods)
{
if (m.Name == methodName)
return m;
}
MethodDefinition deserializeFunction = new MethodDefinition(methodName, MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Public |
MethodAttributes.HideBySig,
itemType);
deserializeFunction.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
ILProcessor serWorker = deserializeFunction.Body.GetILProcessor();
MethodReference readerFunc = Weaver.GetReadFunc(itemType);
if (readerFunc != null)
{
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc));
serWorker.Append(serWorker.Create(OpCodes.Ret));
}
else
{
Weaver.Error("GenerateDeserialization for " + td.Name + " unknown type [" + itemType + "]. Member variables must be basic types.");
return null;
}
td.Methods.Add(deserializeFunction);
return deserializeFunction;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78f71efc83cde4917b7d21efa90bcc9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -285,7 +285,7 @@ public void SyncListStructGenericGeneric()
{ {
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True); Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(1)); Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Does.Match("Struct passed into SyncList<T> can't have generic parameters")); Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: GenerateSerialization for MyGenericStruct<Single> failed. Can't have generic parameters"));
} }
[Test] [Test]