diff --git a/capnpc-csharp.tests/Properties/Resources.Designer.cs b/capnpc-csharp.tests/Properties/Resources.Designer.cs index 7c7c2e9..5148dec 100644 --- a/capnpc-csharp.tests/Properties/Resources.Designer.cs +++ b/capnpc-csharp.tests/Properties/Resources.Designer.cs @@ -90,6 +90,16 @@ namespace capnpc_csharp.Tests.Properties { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] UnitTest11_capnp { + get { + object obj = ResourceManager.GetObject("UnitTest11_capnp", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Byte[]. /// @@ -119,5 +129,15 @@ namespace capnpc_csharp.Tests.Properties { return ((byte[])(obj)); } } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] UnitTest4_capnp { + get { + object obj = ResourceManager.GetObject("UnitTest4_capnp", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/capnpc-csharp.tests/Properties/Resources.resx b/capnpc-csharp.tests/Properties/Resources.resx index 6d2bda3..b4ecfef 100644 --- a/capnpc-csharp.tests/Properties/Resources.resx +++ b/capnpc-csharp.tests/Properties/Resources.resx @@ -136,4 +136,10 @@ ..\Resources\schema-with-offsets.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\UnitTest11.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\UnitTest4.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/capnpc-csharp.tests/Resources/UnitTest11.capnp.bin b/capnpc-csharp.tests/Resources/UnitTest11.capnp.bin new file mode 100644 index 0000000..5363609 Binary files /dev/null and b/capnpc-csharp.tests/Resources/UnitTest11.capnp.bin differ diff --git a/capnpc-csharp.tests/Resources/UnitTest4.capnp.bin b/capnpc-csharp.tests/Resources/UnitTest4.capnp.bin new file mode 100644 index 0000000..f93feb2 Binary files /dev/null and b/capnpc-csharp.tests/Resources/UnitTest4.capnp.bin differ diff --git a/capnpc-csharp.tests/UnitTest11.capnp b/capnpc-csharp.tests/UnitTest11.capnp new file mode 100644 index 0000000..5fda369 --- /dev/null +++ b/capnpc-csharp.tests/UnitTest11.capnp @@ -0,0 +1,15 @@ +@0xe4369c441ab8df19; + +using Imported = import "UnitTest11b.capnp"; + +struct OuterStruct { + innerStruct @0: Imported.InnerStruct; + innerInterface @1: Imported.InnerInterface; +} + +interface OuterInterface { + struct Wrapper { + innerStruct @0: Imported.InnerStruct; + innerInterface @1: Imported.InnerInterface; + } +} diff --git a/capnpc-csharp.tests/UnitTest11b.capnp b/capnpc-csharp.tests/UnitTest11b.capnp new file mode 100644 index 0000000..c5b1926 --- /dev/null +++ b/capnpc-csharp.tests/UnitTest11b.capnp @@ -0,0 +1,9 @@ +@0xabc4e69d23fe287e; + +struct InnerStruct { + const constantS: Bool = false; +} + +interface InnerInterface { + const constantI: Bool = false; +} diff --git a/capnpc-csharp.tests/UnitTest4.capnp b/capnpc-csharp.tests/UnitTest4.capnp new file mode 100644 index 0000000..edfb8c7 --- /dev/null +++ b/capnpc-csharp.tests/UnitTest4.capnp @@ -0,0 +1,16 @@ +@0xf463d204f5208b43; +$import "/capnp/c++.capnp".namespace("UnitTest4"); + +interface Node { + getInfo @0 () -> Info; +} + +struct Info { + node @0 :Node; + classes @1 :Classes; +} + +struct Classes { + i1 @0 :import "UnitTest4b.capnp".I1.Classes; + i2 @1: Void; +} \ No newline at end of file diff --git a/capnpc-csharp.tests/UnitTest4b.capnp b/capnpc-csharp.tests/UnitTest4b.capnp new file mode 100644 index 0000000..3bfe83a --- /dev/null +++ b/capnpc-csharp.tests/UnitTest4b.capnp @@ -0,0 +1,15 @@ +@0x8151238e9f9884c8; +$import "/capnp/c++.capnp".namespace("UnitTest4"); + +using Base = import "UnitTest4.capnp"; + +interface I1 { + interface Node extends (Base.Node) {} + struct Classes { + sub @0: Sub; + } + struct Sub { + const prototype :Base.Classes = ( i1 = (sub = ()) ); + data @0: Bool; + } +} diff --git a/capnpc-csharp.tests/UnitTests.cs b/capnpc-csharp.tests/UnitTests.cs index c009ddd..f7d59fd 100644 --- a/capnpc-csharp.tests/UnitTests.cs +++ b/capnpc-csharp.tests/UnitTests.cs @@ -11,6 +11,7 @@ namespace CapnpC [TestClass] public class UnitTests { + static readonly Dictionary GeneratedCode = new Dictionary(); [TestMethod] public void Test00Enumerant() @@ -22,11 +23,9 @@ namespace CapnpC [TestMethod] public void Test01NestedClash() { - var model = Load(Resources.UnitTest1_capnp); - var structFoo = GetTypeDef(0x93db6ba5509bac24, model); - var codeGen = NewGeneratorFor(model); - codeGen.Transform(model.FilesToGenerate.First()); - var names = codeGen.GetNames(); + var run = LoadAndGenerate(Resources.UnitTest1_capnp, 1); + var structFoo = GetTypeDef(0x93db6ba5509bac24, run.Model); + var names = run.CodeGen.GetNames(); var fieldName = names.GetCodeIdentifier(structFoo.Fields[0]).ToString(); Assert.AreEqual("Foo", structFoo.Name); Assert.AreNotEqual(structFoo.Name, fieldName); @@ -35,30 +34,33 @@ namespace CapnpC [TestMethod] public void Test02ForwardInheritance() { - var model = Load(Resources.UnitTest2_capnp); - var codeGen = NewGeneratorFor(model); - codeGen.Transform(model.FilesToGenerate.First()); + LoadAndGenerate(Resources.UnitTest2_capnp, 2); // Should not throw } [TestMethod] public void Test03NonGeneratedNodeSkip() { - var model = Load(Resources.UnitTest3_capnp); + LoadAndGenerate(Resources.UnitTest3_capnp, 3); + // Should not throw + } + + [TestMethod] + public void Test04MutualDependencies() + { + LoadAndGenerate(Resources.UnitTest4_capnp, 4); // Should not throw } [TestMethod] public void Test10ImportedNamespaces() { - var model = Load(Resources.UnitTest10_capnp); - var codeGen = NewGeneratorFor(model); - var genFile = model.FilesToGenerate.First(); - var outerTypeDef = genFile.NestedTypes.First(); + var run = LoadAndGenerate(Resources.UnitTest10_capnp, 10); + var outerTypeDef = run.FirstFile.NestedTypes.First(); var outerType = Model.Types.FromDefinition(outerTypeDef); var innerType = outerTypeDef.Fields[0].Type; var innerTypeDef = innerType.Definition; - var names = codeGen.GetNames(); + var names = run.CodeGen.GetNames(); var outerNameSyntax = names.GetQName(outerType, outerTypeDef); var innerNameSyntax = names.GetQName(innerType, outerTypeDef); string[] outerNamespace = { "Foo", "Bar", "Baz" }; @@ -71,27 +73,50 @@ namespace CapnpC Assert.AreEqual($"{innerNSStr}.Inner", innerNameSyntax.ToString()); } + [TestMethod] + public void Test11ImportedConst() + { + LoadAndGenerate(Resources.UnitTest11_capnp, 11); + // Should not throw + } + [TestMethod] public void Test20AnnotationAndConst() { - var model = Load(Resources.UnitTest20_capnp); - var codeGen = NewGeneratorFor(model); - codeGen.Transform(model.FilesToGenerate.First()); + LoadAndGenerate(Resources.UnitTest20_capnp, 20); // Should not throw } [TestMethod] public void Test30SchemaCapnp() { - var model = Load(Resources.schema_with_offsets_capnp); - var codeGen = NewGeneratorFor(model); - codeGen.Transform(model.FilesToGenerate.First()); + LoadAndGenerate(Resources.schema_with_offsets_capnp); // Should not throw } + struct Run + { + public Model.SchemaModel Model; + public Generator.CodeGenerator CodeGen; + public Model.GenFile FirstFile; + public string Code; + } + static Generator.CodeGenerator NewGeneratorFor(Model.SchemaModel model) => new Generator.CodeGenerator(model, new Generator.GeneratorOptions()); + Run LoadAndGenerate(byte[] input, int? testNum = null) + { + var run = new Run(); + run.Model = Load(input); + run.CodeGen = NewGeneratorFor(run.Model); + run.FirstFile = run.Model.FilesToGenerate.First(); + run.Code = run.CodeGen.Transform(run.FirstFile); + if (testNum is int num) + GeneratedCode[num] = run.Code; + return run; + } + static Model.TypeDefinition GetTypeDef(ulong id, Model.SchemaModel model) { foreach (var defs in model.FilesToGenerate.Select(f => f.NestedTypes)) diff --git a/capnpc-csharp/Model/Annotation.cs b/capnpc-csharp/Model/Annotation.cs index 6dbbd9a..36191b6 100644 --- a/capnpc-csharp/Model/Annotation.cs +++ b/capnpc-csharp/Model/Annotation.cs @@ -1,9 +1,11 @@ - +using System.Diagnostics; + namespace CapnpC.Model { class Annotation : IDefinition { public ulong Id { get; } + public bool IsGenerated { get; } public TypeTag Tag { get => TypeTag.Annotation; } public IHasNestedDefinitions DeclaringElement { get; } @@ -11,7 +13,9 @@ namespace CapnpC.Model public Annotation(ulong id, IHasNestedDefinitions parent) { + Trace.Assert(parent != null); Id = id; + IsGenerated = (parent as IDefinition).IsGenerated; DeclaringElement = parent; parent.NestedDefinitions.Add(this); } diff --git a/capnpc-csharp/Model/Constant.cs b/capnpc-csharp/Model/Constant.cs index a992ca0..5879100 100644 --- a/capnpc-csharp/Model/Constant.cs +++ b/capnpc-csharp/Model/Constant.cs @@ -1,9 +1,11 @@ - +using System.Diagnostics; + namespace CapnpC.Model { class Constant : IDefinition { public ulong Id { get; } + public bool IsGenerated { get; } public TypeTag Tag { get => TypeTag.Const; } public IHasNestedDefinitions DeclaringElement { get; } @@ -11,7 +13,9 @@ namespace CapnpC.Model public Constant(ulong id, IHasNestedDefinitions parent) { + Trace.Assert(parent != null); Id = id; + IsGenerated = (parent as IDefinition).IsGenerated; DeclaringElement = parent; parent.NestedDefinitions.Add(this); } diff --git a/capnpc-csharp/Model/DefinitionManager.cs b/capnpc-csharp/Model/DefinitionManager.cs index 4a88fc6..95b9183 100644 --- a/capnpc-csharp/Model/DefinitionManager.cs +++ b/capnpc-csharp/Model/DefinitionManager.cs @@ -8,12 +8,12 @@ namespace CapnpC.Model { readonly Dictionary _id2def = new Dictionary(); - public GenFile CreateFile(ulong id) - => CreateId(id, () => new GenFile(id)); + public GenFile CreateFile(ulong id, bool isGenerated) + => CreateId(id, () => new GenFile(id, isGenerated)); public GenFile GetExistingFile(ulong id) => GetId(id, TypeTag.File); - public TypeDefinition CreateTypeDef(ulong id, TypeTag tag, IHasNestedDefinitions decl) + public TypeDefinition CreateTypeDef(ulong id, TypeTag tag, IHasNestedDefinitions decl) => CreateId(id, () => new TypeDefinition(tag, id, decl)); public TypeDefinition GetExistingTypeDef(ulong id, TypeTag tag) { diff --git a/capnpc-csharp/Model/GenFile.cs b/capnpc-csharp/Model/GenFile.cs index f912303..f889fce 100644 --- a/capnpc-csharp/Model/GenFile.cs +++ b/capnpc-csharp/Model/GenFile.cs @@ -5,8 +5,9 @@ namespace CapnpC.Model class GenFile: IDefinition, IHasNestedDefinitions { public ulong Id { get; } + public bool IsGenerated { get; } public TypeTag Tag { get => TypeTag.File; } - public IHasNestedDefinitions DeclaringElement { get; } + public IHasNestedDefinitions DeclaringElement { get => null; } public string Name { get; set; } public string[] Namespace { get; set; } @@ -15,9 +16,10 @@ namespace CapnpC.Model public ICollection NestedDefinitions { get; } = new List(); public ICollection Constants { get; } = new List(); - public GenFile(ulong id) + public GenFile(ulong id, bool isGenerated) { Id = id; + IsGenerated = isGenerated; } } } diff --git a/capnpc-csharp/Model/IDefinition.cs b/capnpc-csharp/Model/IDefinition.cs index e226d40..9bd8572 100644 --- a/capnpc-csharp/Model/IDefinition.cs +++ b/capnpc-csharp/Model/IDefinition.cs @@ -4,6 +4,7 @@ namespace CapnpC.Model interface IDefinition { ulong Id { get; } + bool IsGenerated { get; } TypeTag Tag { get; } IHasNestedDefinitions DeclaringElement { get; } } diff --git a/capnpc-csharp/Model/SchemaModel.cs b/capnpc-csharp/Model/SchemaModel.cs index 4ebafec..bd758c6 100644 --- a/capnpc-csharp/Model/SchemaModel.cs +++ b/capnpc-csharp/Model/SchemaModel.cs @@ -61,7 +61,6 @@ namespace CapnpC.Model struct Pass1State { public HashSet unprocessedNodes; - public bool isGenerated; public IHasNestedDefinitions parent; } @@ -74,17 +73,11 @@ namespace CapnpC.Model foreach (var node in _id2node.Values.Where(n => n.IsFile)) { GenFile file; - state.isGenerated = requestedFiles.TryGetValue(node.Id, out var req); - state.parent = null; - if (state.isGenerated) - { - file = (GenFile)ProcessNodePass1(node.Id, req.Filename, state); + bool isGenerated = requestedFiles.TryGetValue(node.Id, out var req); + var filename = isGenerated ? req.Filename : node.DisplayName; + file = ProcessFilePass1(node.Id, filename, state, isGenerated); + if (isGenerated) _generatedFiles.Add(file); - } - else - { - file = (GenFile)ProcessNodePass1(node.Id, node.DisplayName, state); - } } if (state.unprocessedNodes.Count != 0) { @@ -92,9 +85,20 @@ namespace CapnpC.Model } } + GenFile ProcessFilePass1(ulong id, string name, Pass1State state, bool isGenerated) + { + var file = _typeDefMgr.CreateFile(id, isGenerated); + var node = IdToNode(id); + state.parent = null; + file.Namespace = GetNamespaceAnnotation(node); + file.Name = name; + return ProcessNodePass1(id, name, state) as GenFile; + } + IDefinition ProcessNodePass1(ulong id, string name, Pass1State state) { - if (!(IdToNode(id, state.isGenerated) is Schema.Node.Reader node)) + bool mustExist = state.parent == null || (state.parent as IDefinition).IsGenerated; + if (!(IdToNode(id, mustExist) is Schema.Node.Reader node)) return null; if (!state.unprocessedNodes.Remove(id)) return null; @@ -112,10 +116,11 @@ namespace CapnpC.Model return _typeDefMgr.CreateConstant(id, state.parent); case NodeKind.File: if (state.parent != null) - throw new InvalidSchemaException("Did not expect file nodes to appear as nested nodes"); - var file = _typeDefMgr.CreateFile(id); + throw new InvalidSchemaException($"Did not expect a file node {node.StrId()} to be a nested node."); + var file = _typeDefMgr.GetExistingFile(id); file.Namespace = GetNamespaceAnnotation(node); file.Name = name; + state.parent = file; def = file; processNestedNodes = true; break; @@ -137,11 +142,12 @@ namespace CapnpC.Model if (def == null) { + Trace.Assert(state.parent != null, $"The {node.GetTypeTag().ToString()} node {node.StrId()} was expected to have a parent."); var typeDef = _typeDefMgr.CreateTypeDef(id, node.GetTypeTag(), state.parent); typeDef.Name = name; + state.parent = typeDef; def = typeDef; } - state.parent = def as IHasNestedDefinitions; if (processNestedNodes && node.NestedNodes != null) foreach (var nested in node.NestedNodes) @@ -186,7 +192,6 @@ namespace CapnpC.Model struct Pass2State { public Method currentMethod; - public bool isGenerated; public HashSet processedNodes; } @@ -196,16 +201,15 @@ namespace CapnpC.Model foreach (var file in _typeDefMgr.Files) { var node = IdToNode(file.Id); - state.isGenerated = requestedFiles.ContainsKey(file.Id); - ProcessNestedNodes(node.NestedNodes, state); + ProcessNestedNodes(node.NestedNodes, state, file.IsGenerated); } } - void ProcessNestedNodes(IEnumerable nestedNodes, Pass2State state) + void ProcessNestedNodes(IEnumerable nestedNodes, Pass2State state, bool mustExist) { foreach (var nestedNode in nestedNodes) { - ProcessNode(nestedNode.Id, state); + ProcessNode(nestedNode.Id, state, mustExist); } } @@ -533,7 +537,7 @@ namespace CapnpC.Model } } - ProcessNestedNodes(reader.NestedNodes, state); + ProcessNestedNodes(reader.NestedNodes, state, def.File.IsGenerated); ProcessFields(reader, def, def.Fields, state); @@ -682,18 +686,18 @@ namespace CapnpC.Model TypeDefinition ProcessTypeDef(ulong id, Pass2State state, TypeTag tag = default) { - var def = ProcessNode(id, state, tag); + var def = ProcessNode(id, state, true, tag); var typeDef = def as TypeDefinition; - if (def == null) + if (typeDef == null) throw new ArgumentException( $"Expected node {id.StrId()} to be a TypeDefinition but got {def.GetType().Name} instead.", nameof(id)); return typeDef; } - IDefinition ProcessNode(ulong id, Pass2State state, TypeTag tag = default) + IDefinition ProcessNode(ulong id, Pass2State state, bool mustExist, TypeTag tag = default) { - if (!(IdToNode(id, state.isGenerated) is Schema.Node.Reader node)) return null; + if (!(IdToNode(id, mustExist) is Schema.Node.Reader node)) return null; var kind = node.GetKind(); if (tag == TypeTag.Unknown) tag = kind.GetTypeTag(); var def = _typeDefMgr.GetExistingDef(id, tag); @@ -744,10 +748,10 @@ namespace CapnpC.Model => node.DisplayName.Substring((int)node.DisplayNamePrefixLength); public static string StrId(this Schema.Node.Reader node) - => $"0x{node.Id.ToString("X")}"; + => $"0x{node.Id:X}"; public static string StrId(this ulong nodeId) - => $"0x{nodeId.ToString("X")}"; + => $"0x{nodeId:X}"; public static NodeKind GetKind(this Schema.Node.Reader node) { diff --git a/capnpc-csharp/Model/Type.cs b/capnpc-csharp/Model/Type.cs index c1017df..e6e4fc3 100644 --- a/capnpc-csharp/Model/Type.cs +++ b/capnpc-csharp/Model/Type.cs @@ -67,7 +67,7 @@ namespace CapnpC.Model declaringType = (declaringType as TypeDefinition)?.DeclaringElement as IHasGenericParameters; } - ElementType?.InheritFreeParameters(declaringType); + ElementType?.InheritFreeParameters(declaringType); // BUG: this is always null } Type SubstituteGenerics(Type type) diff --git a/capnpc-csharp/Model/TypeDefinition.cs b/capnpc-csharp/Model/TypeDefinition.cs index 910d4bd..dcbe512 100644 --- a/capnpc-csharp/Model/TypeDefinition.cs +++ b/capnpc-csharp/Model/TypeDefinition.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace CapnpC.Model { @@ -18,8 +19,10 @@ namespace CapnpC.Model public TypeDefinition(TypeTag tag, ulong id, IHasNestedDefinitions parent) { + Trace.Assert(parent != null); Tag = tag; Id = id; + IsGenerated = (parent as IDefinition).IsGenerated; DeclaringElement = parent; if (tag == TypeTag.Group) ((TypeDefinition)parent).NestedGroups.Add(this); @@ -28,6 +31,7 @@ namespace CapnpC.Model } public ulong Id { get; } + public bool IsGenerated { get; } public IHasNestedDefinitions DeclaringElement { get; } public Method UsingMethod { get; set; }