From 9fdebe71e3526016a8709d19777cb76e31a5878b Mon Sep 17 00:00:00 2001 From: MeltyPlayer Date: Sat, 4 Nov 2023 03:41:52 -0500 Subject: [PATCH] Fixed some bugs around nullness and children and added more tests. --- .../build/PointerOrNullTests.cs | 75 +++++++++++++++++++ .../child_of/IChildOfGeneratorTests.cs | 2 + .../memory/WPointerToGeneratorOrNullTests.cs | 30 +++++--- .../memory/WPointerToGeneratorTests.cs | 1 + .../size/SizeOfMemberInBytesGeneratorTests.cs | 1 + .../generator/IfBooleanGeneratorTests.cs | 6 +- .../generator/NullableGeneratorTests.cs | 8 +- .../RAtPositionOrNullGeneratorTests.cs | 8 +- .../SequenceLengthSourceGeneratorTests.cs | 8 +- .../src/binary/BinarySchemaStructureParser.cs | 4 +- .../attributes/memory/IAtPositionAttribute.cs | 2 +- .../attributes/memory/IPointerToAttribute.cs | 2 +- .../attributes/memory/RAtPositionAttribute.cs | 2 +- .../memory/RAtPositionOrNullAttribute.cs | 4 +- .../attributes/memory/WPointerToAttribute.cs | 2 +- .../memory/WPointerToOrNullAttribute.cs | 5 +- .../dependencies/DependencyExtensions.cs | 10 +++ .../text/BinarySchemaWriterGenerator.cs | 54 ++++++++----- 18 files changed, 176 insertions(+), 48 deletions(-) create mode 100644 Schema Build Tests/build/PointerOrNullTests.cs diff --git a/Schema Build Tests/build/PointerOrNullTests.cs b/Schema Build Tests/build/PointerOrNullTests.cs new file mode 100644 index 0000000..524df74 --- /dev/null +++ b/Schema Build Tests/build/PointerOrNullTests.cs @@ -0,0 +1,75 @@ +using System.IO; +using System.Threading.Tasks; + +using NUnit.Framework; + +using schema.binary; +using schema.binary.attributes; +using schema.binary.testing; + + +namespace build { + internal partial class PointerToOrNullTests { + [BinarySchema] + public partial class ParentImpl : IBinaryConvertible { + public Child Child { get; } = new(); + + [RAtPositionOrNull(nameof(Child.FieldPointer), 123)] + public int? Field { get; set; } + } + + [BinarySchema] + public partial class Child : IChildOf, IBinaryConvertible { + public ParentImpl Parent { get; set; } + + [WPointerToOrNull(nameof(Parent.Field), 123)] + public byte FieldPointer { get; set; } + } + + + [Test] + public async Task TestReadNonnull() { + var ms = new MemoryStream(new byte[] { 1, 12, 0, 0, 0 }); + using var br = new SchemaBinaryReader(ms); + + var parent = br.ReadNew(); + + Assert.AreEqual(12, parent.Field.Value); + } + + [Test] + public async Task TestReadNull() { + var ms = new MemoryStream(new byte[] { 123 }); + using var br = new SchemaBinaryReader(ms); + + var parent = br.ReadNew(); + + Assert.IsNull(parent.Field); + } + + + [Test] + public async Task TestWriteNonnull() { + var parent = new ParentImpl(); + parent.Field = 12; + + var bw = new SchemaBinaryWriter(); + parent.Write(bw); + + var bytes = await BinarySchemaAssert.GetEndianBinaryWriterBytes(bw); + CollectionAssert.AreEqual(new byte[] { 1, 12, 0, 0, 0 }, bytes); + } + + [Test] + public async Task TestWriteNull() { + var parent = new ParentImpl(); + parent.Field = null; + + var bw = new SchemaBinaryWriter(); + parent.Write(bw); + + var bytes = await BinarySchemaAssert.GetEndianBinaryWriterBytes(bw); + CollectionAssert.AreEqual(new byte[] { 123 }, bytes); + } + } +} \ No newline at end of file diff --git a/Schema Tests/binary/attributes/child_of/IChildOfGeneratorTests.cs b/Schema Tests/binary/attributes/child_of/IChildOfGeneratorTests.cs index 9308c50..ebaebeb 100644 --- a/Schema Tests/binary/attributes/child_of/IChildOfGeneratorTests.cs +++ b/Schema Tests/binary/attributes/child_of/IChildOfGeneratorTests.cs @@ -81,6 +81,7 @@ public void Read(IBinaryReader br) { namespace foo.bar { public partial class Parent { public void Write(IBinaryWriter bw) { + this.Child.Parent = this; this.Child.Write(bw); } } @@ -173,6 +174,7 @@ public partial class Parent { public void Write(IBinaryWriter bw) { bw.WriteUInt32(this.Length); foreach (var e in this.Child) { + e.Parent = this; e.Write(bw); } } diff --git a/Schema Tests/binary/attributes/memory/WPointerToGeneratorOrNullTests.cs b/Schema Tests/binary/attributes/memory/WPointerToGeneratorOrNullTests.cs index 0d5eab4..f157bbe 100644 --- a/Schema Tests/binary/attributes/memory/WPointerToGeneratorOrNullTests.cs +++ b/Schema Tests/binary/attributes/memory/WPointerToGeneratorOrNullTests.cs @@ -13,9 +13,9 @@ namespace foo.bar { [BinarySchema] public partial class SizeWrapper : IBinaryConvertible { [WPointerToOrNull(nameof(Foo), 123)] - public uint? FooOffset { get; set; } + public uint FooOffset { get; set; } - public byte Foo; + public byte? Foo; } }", @"using System; @@ -31,15 +31,18 @@ public void Read(IBinaryReader br) { } ", @"using System; +using System.Threading.Tasks; using schema.binary; namespace foo.bar { public partial class SizeWrapper { public void Write(IBinaryWriter bw) { - bw.WriteUInt32Delayed((this.Foo == null ? Task.FromResult(123) : bw.GetPointerToMemberRelativeToScope(""Foo"")).ContinueWith(task => (uint) task.Result)); - bw.MarkStartOfMember(""Foo""); - bw.WriteByte(this.Foo); - bw.MarkEndOfMember(); + bw.WriteUInt32Delayed((this.Foo == null ? Task.FromResult(123L) : bw.GetPointerToMemberRelativeToScope(""Foo"")).ContinueWith(task => (uint) task.Result)); + if (this.Foo != null) { + bw.MarkStartOfMember(""Foo""); + bw.WriteByte(this.Foo.Value); + bw.MarkEndOfMember(); + } } } } @@ -79,12 +82,13 @@ public void Read(IBinaryReader br) { } ", @"using System; +using System.Threading.Tasks; using schema.binary; namespace foo.bar { public partial class SizeWrapper { public void Write(IBinaryWriter bw) { - bw.WriteUInt32Delayed((this.Foo.Bar == null ? Task.FromResult(0) : bw.GetPointerToMemberRelativeToScope(""Foo.Bar"")).ContinueWith(task => (uint) task.Result)); + bw.WriteUInt32Delayed((this.Foo.Bar == null ? Task.FromResult(0L) : bw.GetPointerToMemberRelativeToScope(""Foo.Bar"")).ContinueWith(task => (uint) task.Result)); bw.MarkStartOfMember(""Foo""); this.Foo.Write(bw); bw.MarkEndOfMember(); @@ -129,12 +133,13 @@ public void Read(IBinaryReader br) { } ", @"using System; +using System.Threading.Tasks; using schema.binary; namespace foo.bar { public partial class SizeWrapper { public void Write(IBinaryWriter bw) { - bw.WriteUInt32Delayed((this.Parent.Foo == null ? Task.FromResult(0) : bw.GetPointerToMemberRelativeToScope(""Foo"")).ContinueWith(task => (uint) task.Result)); + bw.WriteUInt32Delayed((this.Parent.Foo == null ? Task.FromResult(0L) : bw.GetPointerToMemberRelativeToScope(""Foo"")).ContinueWith(task => (uint) task.Result)); } } } @@ -159,10 +164,13 @@ public void Read(IBinaryReader br) { namespace foo.bar { public partial class ParentImpl { public void Write(IBinaryWriter bw) { + this.Child.Parent = this; this.Child.Write(bw); - bw.MarkStartOfMember(""Foo""); - bw.WriteByte(this.Foo.Value); - bw.MarkEndOfMember(); + if (this.Foo != null) { + bw.MarkStartOfMember(""Foo""); + bw.WriteByte(this.Foo.Value); + bw.MarkEndOfMember(); + } } } } diff --git a/Schema Tests/binary/attributes/memory/WPointerToGeneratorTests.cs b/Schema Tests/binary/attributes/memory/WPointerToGeneratorTests.cs index 251647b..41bde2d 100644 --- a/Schema Tests/binary/attributes/memory/WPointerToGeneratorTests.cs +++ b/Schema Tests/binary/attributes/memory/WPointerToGeneratorTests.cs @@ -159,6 +159,7 @@ public void Read(IBinaryReader br) { namespace foo.bar { public partial class ParentImpl { public void Write(IBinaryWriter bw) { + this.Child.Parent = this; this.Child.Write(bw); bw.MarkStartOfMember(""Foo""); bw.WriteByte(this.Foo); diff --git a/Schema Tests/binary/attributes/size/SizeOfMemberInBytesGeneratorTests.cs b/Schema Tests/binary/attributes/size/SizeOfMemberInBytesGeneratorTests.cs index 23ad0b3..15f9c09 100644 --- a/Schema Tests/binary/attributes/size/SizeOfMemberInBytesGeneratorTests.cs +++ b/Schema Tests/binary/attributes/size/SizeOfMemberInBytesGeneratorTests.cs @@ -159,6 +159,7 @@ public void Read(IBinaryReader br) { namespace foo.bar { public partial class ParentImpl { public void Write(IBinaryWriter bw) { + this.Child.Parent = this; this.Child.Write(bw); bw.MarkStartOfMember(""Foo""); bw.WriteByte(this.Foo); diff --git a/Schema Tests/binary/generator/IfBooleanGeneratorTests.cs b/Schema Tests/binary/generator/IfBooleanGeneratorTests.cs index 81982d3..13e3880 100644 --- a/Schema Tests/binary/generator/IfBooleanGeneratorTests.cs +++ b/Schema Tests/binary/generator/IfBooleanGeneratorTests.cs @@ -62,7 +62,7 @@ public void Write(IBinaryWriter bw) { this.ImmediateValue.Write(bw); } bw.WriteByte((byte) (this.Field ? 1 : 0)); - if (this.Field) { + if (this.OtherValue != null) { bw.WriteInt32(this.OtherValue.Value); } } @@ -113,7 +113,7 @@ namespace foo.bar { public partial class ByteWrapper { public void Write(IBinaryWriter bw) { bw.WriteByte((byte) (this.Field ? 1 : 0)); - if (this.Field) { + if (this.OtherValue != null) { bw.WriteInt32(this.OtherValue.Value); } } @@ -165,7 +165,7 @@ namespace foo.bar { public partial class ByteWrapper { public void Write(IBinaryWriter bw) { this.Field.Write(bw); - if (this.Field.Bool) { + if (this.OtherValue != null) { bw.WriteInt32(this.OtherValue.Value); } } diff --git a/Schema Tests/binary/generator/NullableGeneratorTests.cs b/Schema Tests/binary/generator/NullableGeneratorTests.cs index dc600bf..fd84ed5 100644 --- a/Schema Tests/binary/generator/NullableGeneratorTests.cs +++ b/Schema Tests/binary/generator/NullableGeneratorTests.cs @@ -35,8 +35,12 @@ public void Read(IBinaryReader br) { namespace foo.bar { public partial class NullableWrapper { public void Write(IBinaryWriter bw) { - bw.WriteByte((byte) (this.Field1.Value ? 1 : 0)); - bw.WriteInt32(this.Field2.Value); + if (this.Field1 != null) { + bw.WriteByte((byte) (this.Field1.Value ? 1 : 0)); + } + if (this.Field2 != null) { + bw.WriteInt32(this.Field2.Value); + } } } } diff --git a/Schema Tests/binary/generator/RAtPositionOrNullGeneratorTests.cs b/Schema Tests/binary/generator/RAtPositionOrNullGeneratorTests.cs index 8b8f921..384d6b9 100644 --- a/Schema Tests/binary/generator/RAtPositionOrNullGeneratorTests.cs +++ b/Schema Tests/binary/generator/RAtPositionOrNullGeneratorTests.cs @@ -45,7 +45,9 @@ namespace foo.bar { public partial class OffsetWrapper { public void Write(IBinaryWriter bw) { bw.WriteUInt32(this.Offset); - bw.WriteByte(this.Field.Value); + if (this.Field != null) { + bw.WriteByte(this.Field.Value); + } } } } @@ -98,7 +100,9 @@ public void Read(IBinaryReader br) { namespace foo.bar { public partial class OffsetWrapper { public void Write(IBinaryWriter bw) { - bw.WriteByte(this.Field.Value); + if (this.Field != null) { + bw.WriteByte(this.Field.Value); + } } } } diff --git a/Schema Tests/binary/generator/SequenceLengthSourceGeneratorTests.cs b/Schema Tests/binary/generator/SequenceLengthSourceGeneratorTests.cs index 9583413..6495647 100644 --- a/Schema Tests/binary/generator/SequenceLengthSourceGeneratorTests.cs +++ b/Schema Tests/binary/generator/SequenceLengthSourceGeneratorTests.cs @@ -71,11 +71,13 @@ namespace foo.bar { public partial class ConstLengthWrapper { public void Write(IBinaryWriter bw) { bw.WriteInt32s(this.Field); - bw.WriteInt32s(this.NullableField); - if (this.Toggle) { + if (this.NullableField != null) { + bw.WriteInt32s(this.NullableField); + } + if (this.IfBooleanArray != null) { bw.WriteInt32s(this.IfBooleanArray); } - if (this.Toggle) { + if (this.IfBooleanList != null) { for (var i = 0; i < this.IfBooleanList.Count; ++i) { bw.WriteInt32(this.IfBooleanList[i]); } diff --git a/Schema/src/binary/BinarySchemaStructureParser.cs b/Schema/src/binary/BinarySchemaStructureParser.cs index ee371b7..ccc3a36 100644 --- a/Schema/src/binary/BinarySchemaStructureParser.cs +++ b/Schema/src/binary/BinarySchemaStructureParser.cs @@ -71,7 +71,7 @@ public interface IGenericMemberType : IMemberType { public interface IOffset { ISchemaValueMember OffsetName { get; } - int? NullValue { get; } + long? NullValue { get; } } public interface IStringType : IMemberType { @@ -639,7 +639,7 @@ public class GenericMemberType : IGenericMemberType { public class Offset : IOffset { public ISchemaValueMember OffsetName { get; set; } - public int? NullValue { get; set; } + public long? NullValue { get; set; } } public class StringType : IStringType { diff --git a/Schema/src/binary/attributes/memory/IAtPositionAttribute.cs b/Schema/src/binary/attributes/memory/IAtPositionAttribute.cs index f7ffcc5..5fc722b 100644 --- a/Schema/src/binary/attributes/memory/IAtPositionAttribute.cs +++ b/Schema/src/binary/attributes/memory/IAtPositionAttribute.cs @@ -1,6 +1,6 @@ namespace schema.binary.attributes { public interface IAtPositionAttribute { string OffsetName { get; } - int? NullValue { get; } + long? NullValue { get; } } } \ No newline at end of file diff --git a/Schema/src/binary/attributes/memory/IPointerToAttribute.cs b/Schema/src/binary/attributes/memory/IPointerToAttribute.cs index e69e536..8a273ab 100644 --- a/Schema/src/binary/attributes/memory/IPointerToAttribute.cs +++ b/Schema/src/binary/attributes/memory/IPointerToAttribute.cs @@ -1,6 +1,6 @@ namespace schema.binary.attributes { public interface IPointerToAttribute { IChain AccessChainToOtherMember { get; } - int? NullValue { get; } + long? NullValue { get; } } } \ No newline at end of file diff --git a/Schema/src/binary/attributes/memory/RAtPositionAttribute.cs b/Schema/src/binary/attributes/memory/RAtPositionAttribute.cs index 5d0f988..d4888fc 100644 --- a/Schema/src/binary/attributes/memory/RAtPositionAttribute.cs +++ b/Schema/src/binary/attributes/memory/RAtPositionAttribute.cs @@ -9,6 +9,6 @@ public RAtPositionAttribute(string offsetName) { } public string OffsetName { get; } - public int? NullValue => null; + public long? NullValue => null; } } \ No newline at end of file diff --git a/Schema/src/binary/attributes/memory/RAtPositionOrNullAttribute.cs b/Schema/src/binary/attributes/memory/RAtPositionOrNullAttribute.cs index b3593a1..ad62dae 100644 --- a/Schema/src/binary/attributes/memory/RAtPositionOrNullAttribute.cs +++ b/Schema/src/binary/attributes/memory/RAtPositionOrNullAttribute.cs @@ -3,9 +3,9 @@ namespace schema.binary.attributes { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class RAtPositionOrNullAttribute(string offsetName, int nullValue = 0) + public class RAtPositionOrNullAttribute(string offsetName, long nullValue = 0) : Attribute, IAtPositionAttribute { public string OffsetName { get; } = offsetName; - public int? NullValue { get; } = nullValue; + public long? NullValue { get; } = nullValue; } } \ No newline at end of file diff --git a/Schema/src/binary/attributes/memory/WPointerToAttribute.cs b/Schema/src/binary/attributes/memory/WPointerToAttribute.cs index 3a531d6..f130258 100644 --- a/Schema/src/binary/attributes/memory/WPointerToAttribute.cs +++ b/Schema/src/binary/attributes/memory/WPointerToAttribute.cs @@ -26,6 +26,6 @@ public IChain AccessChainToOtherMember { private set; } - public int? NullValue => null; + public long? NullValue => null; } } \ No newline at end of file diff --git a/Schema/src/binary/attributes/memory/WPointerToOrNullAttribute.cs b/Schema/src/binary/attributes/memory/WPointerToOrNullAttribute.cs index 306af3e..75a3dde 100644 --- a/Schema/src/binary/attributes/memory/WPointerToOrNullAttribute.cs +++ b/Schema/src/binary/attributes/memory/WPointerToOrNullAttribute.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace schema.binary.attributes { @@ -12,7 +13,7 @@ public class WPointerToOrNullAttribute : BMemberAttribute, private readonly string otherMemberName_; public WPointerToOrNullAttribute(string otherMemberName, - int nullValue = 0) { + long nullValue = 0) { this.otherMemberName_ = otherMemberName; this.NullValue = nullValue; } @@ -29,6 +30,6 @@ public IChain AccessChainToOtherMember { private set; } - public int? NullValue { get; } + public long? NullValue { get; } } } \ No newline at end of file diff --git a/Schema/src/binary/dependencies/DependencyExtensions.cs b/Schema/src/binary/dependencies/DependencyExtensions.cs index 58106e8..e52d335 100644 --- a/Schema/src/binary/dependencies/DependencyExtensions.cs +++ b/Schema/src/binary/dependencies/DependencyExtensions.cs @@ -42,6 +42,16 @@ public static bool DependsOnSchemaUtilAsserts( LengthOfSequenceMembers.Length: > 1 }); + + public static bool DependsOnSystemThreadingTasks( + this IBinarySchemaContainer container) + => container + .Members + .OfType() + .Any(member => member.MemberType is IPrimitiveMemberType { + PointerToAttribute: { NullValue: { } } + }); + public static bool DependsOnCollectionsImports( this IBinarySchemaContainer container) => container diff --git a/Schema/src/binary/text/BinarySchemaWriterGenerator.cs b/Schema/src/binary/text/BinarySchemaWriterGenerator.cs index 87f1193..8deacd1 100644 --- a/Schema/src/binary/text/BinarySchemaWriterGenerator.cs +++ b/Schema/src/binary/text/BinarySchemaWriterGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data.SqlTypes; using System.IO; using System.Linq; using System.Text; @@ -13,6 +14,8 @@ using schema.util.symbols; using schema.util.text; +using static schema.binary.BinarySchemaContainerParser; + namespace schema.binary.text { public class BinarySchemaWriterGenerator { public const string WRITER = "bw"; @@ -39,6 +42,10 @@ public string Generate(IBinarySchemaContainer container) { dependencies.Add("schema.util.asserts"); } + if (container.DependsOnSystemThreadingTasks()) { + dependencies.Add("System.Threading.Tasks"); + } + dependencies.Sort(StringComparer.Ordinal); foreach (var dependency in dependencies) { cbsb.WriteLine($"using {dependency};"); @@ -119,15 +126,15 @@ private static void WriteValueMember_( return; } + var nullable = member.MemberType.TypeInfo.IsNullable; var ifBoolean = member.IfBoolean; - if (ifBoolean != null) { - if (ifBoolean.SourceType == IfBooleanSourceType.IMMEDIATE_VALUE) { - cbsb.WriteLine( - $"{GetWritePrimitiveText_(SchemaPrimitiveType.BOOLEAN, ifBoolean.ImmediateBooleanType.AsNumberType(), $"this.{member.Name} != null")};") - .EnterBlock($"if (this.{member.Name} != null)"); - } else { - cbsb.EnterBlock($"if (this.{ifBoolean.OtherMember.Name})"); - } + if (ifBoolean?.SourceType == IfBooleanSourceType.IMMEDIATE_VALUE) { + cbsb.WriteLine( + $"{GetWritePrimitiveText_(SchemaPrimitiveType.BOOLEAN, ifBoolean.ImmediateBooleanType.AsNumberType(), $"this.{member.Name} != null")};"); + } + + if (nullable) { + cbsb.EnterBlock($"if (this.{member.Name} != null)"); } var memberType = member.MemberType; @@ -144,8 +151,11 @@ private static void WriteValueMember_( BinarySchemaWriterGenerator.WriteString_(cbsb, member); break; } - case IContainerMemberType: { - BinarySchemaWriterGenerator.WriteContainer_(cbsb, member); + case IContainerMemberType containerMemberType: { + BinarySchemaWriterGenerator.WriteContainer_( + cbsb, + containerMemberType, + member); break; } case ISequenceMemberType: { @@ -157,7 +167,7 @@ private static void WriteValueMember_( throw new NotImplementedException(); } - if (ifBoolean != null) { + if (nullable) { cbsb.ExitBlock(); } } @@ -306,7 +316,7 @@ private static void WritePrimitive_( var nullValue = pointerToAttribute.NullValue; if (nullValue != null) { accessText = - $"(this.{accessChain.RawPath} == null ? Task.FromResult({nullValue.Value}) : {accessText})"; + $"(this.{accessChain.RawPath} == null ? Task.FromResult({nullValue.Value}L) : {accessText})"; } } else { accessText = $"{WRITER}.GetAbsoluteLength()"; @@ -377,13 +387,19 @@ private static void WriteString_( private static void WriteContainer_( ICurlyBracketTextWriter cbsb, + IContainerMemberType containerMemberType, ISchemaValueMember member) { + var memberName = member.Name; + if (containerMemberType.IsChild) { + cbsb.WriteLine($"this.{memberName}.Parent = this;"); + } + HandleMemberEndiannessAndTracking_( cbsb, member, () => { // TODO: Do value types need to be handled differently? - cbsb.WriteLine($"this.{member.Name}.Write({WRITER});"); + cbsb.WriteLine($"this.{memberName}.Write({WRITER});"); }); } @@ -456,10 +472,14 @@ private static void WriteIntoArray_(ICurlyBracketTextWriter cbsb, return; } - if (elementType is IContainerMemberType) { - cbsb.EnterBlock( - $"foreach (var e in this.{member.Name})") - .WriteLine($"e.Write({WRITER});") + if (elementType is IContainerMemberType containerElementType) { + cbsb.EnterBlock($"foreach (var e in this.{member.Name})"); + + if (containerElementType.IsChild) { + cbsb.WriteLine("e.Parent = this;"); + } + + cbsb.WriteLine($"e.Write({WRITER});") .ExitBlock(); return; }