feature(synclist): allow SyncList of primitive types (#622)

* feature(synclist): allow SyncList of primitive types

As it turns out this never really worked:
```cs
class SyncListByte : SyncList<byte> {};
```

You had to write your own serializeItem / deserialize item for native types.

This change lifts the restriction, now you can use a synclist of anything that mirror can serialize/deserialize

* fix: fix unit tests error messages
This commit is contained in:
Paul Pacheco 2019-03-22 05:51:20 -05:00 committed by GitHub
parent aa5e5a104f
commit 6f19489721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 72 deletions

View File

@ -60,39 +60,17 @@ static MethodReference GenerateSerialization(TypeDefinition td, TypeReference it
return null; return null;
} }
foreach (FieldDefinition field in itemType.Resolve().Fields) MethodReference writeFunc = Weaver.GetWriteFunc(itemType);
if (writeFunc != null)
{ {
if (field.IsStatic || field.IsPrivate || field.IsSpecialName) serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
continue; serWorker.Append(serWorker.Create(OpCodes.Ldarg_2));
serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
FieldReference importedField = Weaver.CurrentAssembly.MainModule.ImportReference(field); }
TypeDefinition ft = importedField.FieldType.Resolve(); else
{
if (ft.HasGenericParameters) Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + itemType + "/" + itemType.FullName + "]. [SyncList] member variables must be basic types.");
{ return null;
Weaver.Error("GenerateSerialization for " + td.Name + " [" + ft + "/" + ft.FullName + "]. [SyncList] member cannot have generic parameters.");
return null;
}
if (ft.IsInterface)
{
Weaver.Error("GenerateSerialization for " + td.Name + " [" + ft + "/" + ft.FullName + "]. [SyncList] member cannot be an interface.");
return null;
}
MethodReference writeFunc = Weaver.GetWriteFunc(field.FieldType);
if (writeFunc != null)
{
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_2));
serWorker.Append(serWorker.Create(OpCodes.Ldfld, importedField));
serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc));
}
else
{
Weaver.Error("GenerateSerialization for " + td.Name + " unknown type [" + ft + "/" + ft.FullName + "]. [SyncList] member variables must be basic types.");
return null;
}
} }
serWorker.Append(serWorker.Create(OpCodes.Ret)); serWorker.Append(serWorker.Create(OpCodes.Ret));
@ -109,50 +87,31 @@ static MethodReference GenerateDeserialization(TypeDefinition td, TypeReference
return m; return m;
} }
MethodDefinition serializeFunc = new MethodDefinition("DeserializeItem", MethodAttributes.Public | MethodDefinition deserializeFunction = new MethodDefinition("DeserializeItem", MethodAttributes.Public |
MethodAttributes.Virtual | MethodAttributes.Virtual |
MethodAttributes.Public | MethodAttributes.Public |
MethodAttributes.HideBySig, MethodAttributes.HideBySig,
itemType); itemType);
serializeFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); deserializeFunction.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType)));
ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); ILProcessor serWorker = deserializeFunction.Body.GetILProcessor();
serWorker.Body.InitLocals = true; MethodReference readerFunc = Weaver.GetReadFunc(itemType);
serWorker.Body.Variables.Add(new VariableDefinition(itemType)); if (readerFunc != null)
// init item instance
serWorker.Append(serWorker.Create(OpCodes.Ldloca, 0));
serWorker.Append(serWorker.Create(OpCodes.Initobj, itemType));
foreach (FieldDefinition field in itemType.Resolve().Fields)
{ {
if (field.IsStatic || field.IsPrivate || field.IsSpecialName) serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
continue; serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc));
serWorker.Append(serWorker.Create(OpCodes.Ret));
FieldReference importedField = Weaver.CurrentAssembly.MainModule.ImportReference(field); }
TypeDefinition ft = importedField.FieldType.Resolve(); else
{
MethodReference readerFunc = Weaver.GetReadFunc(field.FieldType); Weaver.Error("GenerateDeserialization for " + td.Name + " unknown type [" + itemType + "]. [SyncList] member variables must be basic types.");
if (readerFunc != null) return null;
{
serWorker.Append(serWorker.Create(OpCodes.Ldloca, 0));
serWorker.Append(serWorker.Create(OpCodes.Ldarg_1));
serWorker.Append(serWorker.Create(OpCodes.Call, readerFunc));
serWorker.Append(serWorker.Create(OpCodes.Stfld, importedField));
}
else
{
Weaver.Error("GenerateDeserialization for " + td.Name + " unknown type [" + ft + "]. [SyncList] member variables must be basic types.");
return null;
}
} }
serWorker.Append(serWorker.Create(OpCodes.Ldloc_0));
serWorker.Append(serWorker.Create(OpCodes.Ret));
td.Methods.Add(serializeFunc); td.Methods.Add(deserializeFunction);
return serializeFunc; return deserializeFunction;
} }
} }
} }

View File

@ -263,6 +263,13 @@ public void SyncListMissingParamlessCtor()
Assert.That(m_weaverErrors.Count, Is.EqualTo(1)); Assert.That(m_weaverErrors.Count, Is.EqualTo(1));
Assert.That(m_weaverErrors[0], Does.Match("Missing parameter-less constructor")); Assert.That(m_weaverErrors[0], Does.Match("Missing parameter-less constructor"));
} }
[Test]
public void SyncListByteValid() {
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
Assert.That(m_weaverErrors.Count, Is.EqualTo(0));
}
#endregion #endregion
#region SyncListStruct tests #region SyncListStruct tests
@ -285,25 +292,25 @@ public void SyncListStructGenericGeneric()
public void SyncListStructMemberGeneric() public void SyncListStructMemberGeneric()
{ {
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(2));
Assert.That(m_weaverErrors[0], Does.Match("member cannot have generic parameters")); Assert.That(m_weaverErrors[0], Is.EqualTo("Mirror.Weaver error: WriteReadFunc for potato [MirrorTest.MirrorTestPlayer/MyGenericStruct`1<System.Single>/MirrorTest.MirrorTestPlayer/MyGenericStruct`1<System.Single>]. Cannot have generic parameters."));
} }
[Test] [Test]
public void SyncListStructMemberInterface() public void SyncListStructMemberInterface()
{ {
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(2));
Assert.That(m_weaverErrors[0], Does.Match("member cannot be an interface")); Assert.That(m_weaverErrors[0], Is.EqualTo( "Mirror.Weaver error: WriteReadFunc for potato [MirrorTest.MirrorTestPlayer/IPotato/MirrorTest.MirrorTestPlayer/IPotato]. Cannot be an interface."));
} }
[Test] [Test]
public void SyncListStructMemberBasicType() public void SyncListStructMemberBasicType()
{ {
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True); Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
Assert.That(m_weaverErrors.Count, Is.EqualTo(2)); Assert.That(m_weaverErrors.Count, Is.EqualTo(3));
Assert.That(m_weaverErrors[0], Does.Match("please make sure to use a valid type")); Assert.That(m_weaverErrors[0], Does.Match("please make sure to use a valid type"));
Assert.That(m_weaverErrors[1], Does.Match("member variables must be basic types")); Assert.That(m_weaverErrors[1], Does.Match("Mirror.Weaver error: WriteReadFunc for nonbasicpotato type System.Object no supported"));
} }
#endregion #endregion