diff --git a/Capnp.Net.sln b/Capnp.Net.sln
index e12ef97..41f5968 100644
--- a/Capnp.Net.sln
+++ b/Capnp.Net.sln
@@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Std
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Core21", "Capnp.Net.Runtime.Tests.Core21\Capnp.Net.Runtime.Tests.Core21.csproj", "{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "capnpc-csharp.tests", "capnpc-csharp.tests\capnpc-csharp.tests.csproj", "{B77AC567-E232-4072-85C3-8689566BF3D4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B77AC567-E232-4072-85C3-8689566BF3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B77AC567-E232-4072-85C3-8689566BF3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B77AC567-E232-4072-85C3-8689566BF3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B77AC567-E232-4072-85C3-8689566BF3D4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/capnpc-csharp.tests/Properties/Resources.Designer.cs b/capnpc-csharp.tests/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..7c7c2e9
--- /dev/null
+++ b/capnpc-csharp.tests/Properties/Resources.Designer.cs
@@ -0,0 +1,123 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace capnpc_csharp.Tests.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("capnpc_csharp.Tests.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] schema_with_offsets_capnp {
+ get {
+ object obj = ResourceManager.GetObject("schema_with_offsets_capnp", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] UnitTest1_capnp {
+ get {
+ object obj = ResourceManager.GetObject("UnitTest1_capnp", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] UnitTest10_capnp {
+ get {
+ object obj = ResourceManager.GetObject("UnitTest10_capnp", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] UnitTest2_capnp {
+ get {
+ object obj = ResourceManager.GetObject("UnitTest2_capnp", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] UnitTest20_capnp {
+ get {
+ object obj = ResourceManager.GetObject("UnitTest20_capnp", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] UnitTest3_capnp {
+ get {
+ object obj = ResourceManager.GetObject("UnitTest3_capnp", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+ }
+}
diff --git a/capnpc-csharp.tests/Properties/Resources.resx b/capnpc-csharp.tests/Properties/Resources.resx
new file mode 100644
index 0000000..6d2bda3
--- /dev/null
+++ b/capnpc-csharp.tests/Properties/Resources.resx
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ ..\Resources\UnitTest10.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ ..\Resources\UnitTest20.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ ..\Resources\UnitTest3.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ ..\Resources\UnitTest2.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ ..\Resources\UnitTest1.capnp.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ ..\Resources\schema-with-offsets.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/UnitTest1.capnp.bin b/capnpc-csharp.tests/Resources/UnitTest1.capnp.bin
new file mode 100644
index 0000000..ae47486
Binary files /dev/null and b/capnpc-csharp.tests/Resources/UnitTest1.capnp.bin differ
diff --git a/capnpc-csharp.tests/Resources/UnitTest10.capnp.bin b/capnpc-csharp.tests/Resources/UnitTest10.capnp.bin
new file mode 100644
index 0000000..dade3aa
Binary files /dev/null and b/capnpc-csharp.tests/Resources/UnitTest10.capnp.bin differ
diff --git a/capnpc-csharp.tests/Resources/UnitTest2.capnp.bin b/capnpc-csharp.tests/Resources/UnitTest2.capnp.bin
new file mode 100644
index 0000000..5a2eafc
Binary files /dev/null and b/capnpc-csharp.tests/Resources/UnitTest2.capnp.bin differ
diff --git a/capnpc-csharp.tests/Resources/UnitTest20.capnp.bin b/capnpc-csharp.tests/Resources/UnitTest20.capnp.bin
new file mode 100644
index 0000000..48f5e96
Binary files /dev/null and b/capnpc-csharp.tests/Resources/UnitTest20.capnp.bin differ
diff --git a/capnpc-csharp.tests/Resources/UnitTest3.capnp.bin b/capnpc-csharp.tests/Resources/UnitTest3.capnp.bin
new file mode 100644
index 0000000..c0a1dae
Binary files /dev/null and b/capnpc-csharp.tests/Resources/UnitTest3.capnp.bin differ
diff --git a/capnpc-csharp.tests/Resources/schema-with-offsets.capnp.bin b/capnpc-csharp.tests/Resources/schema-with-offsets.capnp.bin
new file mode 100644
index 0000000..1b61d19
Binary files /dev/null and b/capnpc-csharp.tests/Resources/schema-with-offsets.capnp.bin differ
diff --git a/capnpc-csharp.tests/UnitTest1.capnp b/capnpc-csharp.tests/UnitTest1.capnp
new file mode 100644
index 0000000..f638014
--- /dev/null
+++ b/capnpc-csharp.tests/UnitTest1.capnp
@@ -0,0 +1,9 @@
+@0x93d3c4d19cd84a4c;
+
+enum Enumerant {
+ byte @0;
+}
+
+struct Foo {
+ foo @0: UInt8;
+}
diff --git a/capnpc-csharp.tests/UnitTest10.capnp b/capnpc-csharp.tests/UnitTest10.capnp
new file mode 100644
index 0000000..226e94b
--- /dev/null
+++ b/capnpc-csharp.tests/UnitTest10.capnp
@@ -0,0 +1,9 @@
+@0xbbfd48ae4b99d012;
+
+using Cxx = import "/capnp/c++.capnp";
+
+$Cxx.namespace("Foo::Bar::Baz");
+
+struct Outer {
+ inner @0: import "UnitTest10b.capnp".Inner ;
+}
diff --git a/capnpc-csharp.tests/UnitTest10b.capnp b/capnpc-csharp.tests/UnitTest10b.capnp
new file mode 100644
index 0000000..4d49fee
--- /dev/null
+++ b/capnpc-csharp.tests/UnitTest10b.capnp
@@ -0,0 +1,7 @@
+@0xaf95c1c78b01be97;
+
+using Cxx = import "/capnp/c++.capnp";
+
+$Cxx.namespace("Foo::Garf::Snarf");
+
+struct Inner {}
diff --git a/capnpc-csharp.tests/UnitTest2.capnp b/capnpc-csharp.tests/UnitTest2.capnp
new file mode 100644
index 0000000..e4cbcb9
--- /dev/null
+++ b/capnpc-csharp.tests/UnitTest2.capnp
@@ -0,0 +1,11 @@
+@0xf6041efc5e8b1e59;
+
+interface Interface1(V1) {}
+
+interface Interface {
+ method @0 () -> (arg : AnyPointer);
+ method1 @1 () -> (arg : Interface1(AnyPointer));
+ method2 @2 () -> (arg : Interface2(AnyPointer));
+}
+
+interface Interface2(V2) {}
diff --git a/capnpc-csharp.tests/UnitTest20.capnp b/capnpc-csharp.tests/UnitTest20.capnp
new file mode 100644
index 0000000..edf91c9
--- /dev/null
+++ b/capnpc-csharp.tests/UnitTest20.capnp
@@ -0,0 +1,5 @@
+@0xcebb7a61a86a7492;
+
+annotation anAnnotation(file): Text;
+
+const aConstant :UInt16 = 42;
diff --git a/capnpc-csharp.tests/UnitTest3.capnp b/capnpc-csharp.tests/UnitTest3.capnp
new file mode 100644
index 0000000..14abe12
--- /dev/null
+++ b/capnpc-csharp.tests/UnitTest3.capnp
@@ -0,0 +1,6 @@
+@0xb7158f7fa52b8db6;
+
+using Cxx = import "/capnp/c++.capnp";
+
+$Cxx.namespace("Foo.Bar.Baz");
+
diff --git a/capnpc-csharp.tests/UnitTests.cs b/capnpc-csharp.tests/UnitTests.cs
new file mode 100644
index 0000000..c009ddd
--- /dev/null
+++ b/capnpc-csharp.tests/UnitTests.cs
@@ -0,0 +1,129 @@
+using capnpc_csharp.Tests.Properties;
+using Capnp;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace CapnpC
+{
+ [TestClass]
+ public class UnitTests
+ {
+
+ [TestMethod]
+ public void Test00Enumerant()
+ {
+ var model = Load(Resources.UnitTest1_capnp);
+ Assert.AreEqual("@byte", GetTypeDef(0xc8461867c409f5d4, model).Enumerants[0].Literal);
+ }
+
+ [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 fieldName = names.GetCodeIdentifier(structFoo.Fields[0]).ToString();
+ Assert.AreEqual("Foo", structFoo.Name);
+ Assert.AreNotEqual(structFoo.Name, fieldName);
+ }
+
+ [TestMethod]
+ public void Test02ForwardInheritance()
+ {
+ var model = Load(Resources.UnitTest2_capnp);
+ var codeGen = NewGeneratorFor(model);
+ codeGen.Transform(model.FilesToGenerate.First());
+ // Should not throw
+ }
+
+ [TestMethod]
+ public void Test03NonGeneratedNodeSkip()
+ {
+ var model = Load(Resources.UnitTest3_capnp);
+ // 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 outerType = Model.Types.FromDefinition(outerTypeDef);
+ var innerType = outerTypeDef.Fields[0].Type;
+ var innerTypeDef = innerType.Definition;
+ var names = codeGen.GetNames();
+ var outerNameSyntax = names.GetQName(outerType, outerTypeDef);
+ var innerNameSyntax = names.GetQName(innerType, outerTypeDef);
+ string[] outerNamespace = { "Foo", "Bar", "Baz" };
+ string[] innerNamespace = { "Foo", "Garf", "Snarf" };
+ CollectionAssert.AreEqual(outerNamespace, (outerTypeDef.DeclaringElement as Model.GenFile).Namespace);
+ CollectionAssert.AreEqual(innerNamespace, (innerType.Definition.DeclaringElement as Model.GenFile).Namespace);
+ string outerNSStr = String.Join('.', outerNamespace);
+ string innerNSStr = String.Join('.', innerNamespace);
+ Assert.AreEqual($"{outerNSStr}.Outer", outerNameSyntax.ToString());
+ Assert.AreEqual($"{innerNSStr}.Inner", innerNameSyntax.ToString());
+ }
+
+ [TestMethod]
+ public void Test20AnnotationAndConst()
+ {
+ var model = Load(Resources.UnitTest20_capnp);
+ var codeGen = NewGeneratorFor(model);
+ codeGen.Transform(model.FilesToGenerate.First());
+ // Should not throw
+ }
+
+ [TestMethod]
+ public void Test30SchemaCapnp()
+ {
+ var model = Load(Resources.schema_with_offsets_capnp);
+ var codeGen = NewGeneratorFor(model);
+ codeGen.Transform(model.FilesToGenerate.First());
+ // Should not throw
+ }
+
+ static Generator.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
+ => new Generator.CodeGenerator(model, new Generator.GeneratorOptions());
+
+ static Model.TypeDefinition GetTypeDef(ulong id, Model.SchemaModel model)
+ {
+ foreach (var defs in model.FilesToGenerate.Select(f => f.NestedTypes))
+ {
+ if (GetTypeDef(id, defs) is Model.TypeDefinition def) return def;
+ }
+ return null;
+ }
+
+ static Model.TypeDefinition GetTypeDef(ulong id, IEnumerable defs)
+ {
+ foreach (var def in defs)
+ {
+ if (def.Id == id) return def;
+ var sub = GetTypeDef(id, def.NestedTypes);
+ if (sub != null) return sub;
+ }
+ return null;
+ }
+
+ static Model.SchemaModel Load(byte[] data)
+ {
+ WireFrame segments;
+ var input = new MemoryStream(data);
+ using (input)
+ {
+ segments = Framing.ReadSegments(input);
+ }
+ var dec = DeserializerState.CreateRoot(segments);
+ var reader = Schema.CodeGeneratorRequest.Reader.Create(dec);
+ var model = Model.SchemaModel.Create(reader);
+ return model;
+ }
+ }
+}
diff --git a/capnpc-csharp.tests/capnpc-csharp.tests.csproj b/capnpc-csharp.tests/capnpc-csharp.tests.csproj
new file mode 100644
index 0000000..3be689f
--- /dev/null
+++ b/capnpc-csharp.tests/capnpc-csharp.tests.csproj
@@ -0,0 +1,40 @@
+
+
+
+ netcoreapp2.2
+ capnpc_csharp.Tests
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+
diff --git a/capnpc-csharp/Generator/CodeGenerator.cs b/capnpc-csharp/Generator/CodeGenerator.cs
index 785a50f..e401b3a 100644
--- a/capnpc-csharp/Generator/CodeGenerator.cs
+++ b/capnpc-csharp/Generator/CodeGenerator.cs
@@ -33,6 +33,8 @@
_interfaceGen = new InterfaceSnippetGen(_names);
}
+ internal GenNames GetNames() => _names;
+
IEnumerable TransformEnum(TypeDefinition def)
{
yield return _commonGen.MakeEnum(def);
@@ -102,7 +104,7 @@
yield return _interfaceGen.MakePipeliningSupport(def);
}
- if (def.NestedTypes.Count > 0)
+ if (def.NestedTypes.Any())
{
var ns = ClassDeclaration(
_names.MakeTypeName(def, NameUsage.Namespace).ToString())
@@ -143,20 +145,11 @@
}
}
- string Transform(GenFile file)
+ internal string Transform(GenFile file)
{
- if (file.Namespace != null)
- {
- _names.TopNamespace = IdentifierName(MakeCamel(file.Namespace[0]));
+ NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace;
- foreach (string name in file.Namespace.Skip(1))
- {
- var temp = IdentifierName(MakeCamel(name));
- _names.TopNamespace = QualifiedName(_names.TopNamespace, temp);
- }
- }
-
- var ns = NamespaceDeclaration(_names.TopNamespace);
+ var ns = NamespaceDeclaration(topNamespace);
foreach (var def in file.NestedTypes)
{
diff --git a/capnpc-csharp/Generator/GenNames.cs b/capnpc-csharp/Generator/GenNames.cs
index f71a253..f842413 100644
--- a/capnpc-csharp/Generator/GenNames.cs
+++ b/capnpc-csharp/Generator/GenNames.cs
@@ -213,29 +213,24 @@ namespace CapnpC.Generator
}
}
- NameSyntax GetQName(TypeDefinition def)
+ public static NameSyntax NamespaceName(string[] @namespace)
{
- var stack = new Stack();
-
- stack.Push(MakeGenericTypeName(def, NameUsage.Default));
-
- while (def.DeclaringElement is TypeDefinition pdef)
+ NameSyntax ident = null;
+ if (@namespace != null)
{
- stack.Push(MakeGenericTypeName(pdef, NameUsage.Namespace));
- def = pdef;
+ ident = IdentifierName(SyntaxHelpers.MakeCamel(@namespace[0]));
+ foreach (string name in @namespace.Skip(1))
+ {
+ var temp = IdentifierName(SyntaxHelpers.MakeCamel(name));
+ ident = QualifiedName(ident, temp);
+ }
}
-
- var qtype = TopNamespace;
-
- foreach (var name in stack)
- {
- qtype = QualifiedName(qtype, name);
- }
-
- return qtype;
+ return ident;
}
- NameSyntax GetQName(Model.Type type, TypeDefinition scope)
+ NameSyntax GetNamespaceFor(TypeDefinition def) => NamespaceName(def?.File?.Namespace);
+
+ internal NameSyntax GetQName(Model.Type type, TypeDefinition scope)
{
// FIXME: With the help of the 'scope' parameter we will be able to generate abbreviated
// qualified names. Unfortunately the commented approach is too naive. It will fail if
@@ -262,7 +257,10 @@ namespace CapnpC.Generator
def = pdef;
}
- var qtype = TopNamespace;
+ var qtype =
+ GetNamespaceFor(type.Definition)
+ ?? GetNamespaceFor(scope)
+ ?? TopNamespace;
foreach (var name in stack)
{
@@ -563,7 +561,8 @@ namespace CapnpC.Generator
}
var typeNames = new HashSet(def.NestedTypes.Select(t => MakeTypeName(t)));
-
+ typeNames.Add(MakeTypeName(def));
+
foreach (var member in def.Fields)
{
var memberName = new Name(SyntaxHelpers.MakeCamel(member.Name));
diff --git a/capnpc-csharp/Model/Annotation.cs b/capnpc-csharp/Model/Annotation.cs
new file mode 100644
index 0000000..6dbbd9a
--- /dev/null
+++ b/capnpc-csharp/Model/Annotation.cs
@@ -0,0 +1,19 @@
+
+namespace CapnpC.Model
+{
+ class Annotation : IDefinition
+ {
+ public ulong Id { get; }
+ public TypeTag Tag { get => TypeTag.Annotation; }
+ public IHasNestedDefinitions DeclaringElement { get; }
+
+ public Type Type { get; set; }
+
+ public Annotation(ulong id, IHasNestedDefinitions parent)
+ {
+ Id = id;
+ DeclaringElement = parent;
+ parent.NestedDefinitions.Add(this);
+ }
+ }
+}
diff --git a/capnpc-csharp/Model/Constant.cs b/capnpc-csharp/Model/Constant.cs
new file mode 100644
index 0000000..a992ca0
--- /dev/null
+++ b/capnpc-csharp/Model/Constant.cs
@@ -0,0 +1,19 @@
+
+namespace CapnpC.Model
+{
+ class Constant : IDefinition
+ {
+ public ulong Id { get; }
+ public TypeTag Tag { get => TypeTag.Const; }
+ public IHasNestedDefinitions DeclaringElement { get; }
+
+ public Value Value { get; set; }
+
+ public Constant(ulong id, IHasNestedDefinitions parent)
+ {
+ Id = id;
+ DeclaringElement = parent;
+ parent.NestedDefinitions.Add(this);
+ }
+ }
+}
diff --git a/capnpc-csharp/Model/DefinitionManager.cs b/capnpc-csharp/Model/DefinitionManager.cs
new file mode 100644
index 0000000..4a88fc6
--- /dev/null
+++ b/capnpc-csharp/Model/DefinitionManager.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CapnpC.Model
+{
+ class DefinitionManager
+ {
+ readonly Dictionary _id2def = new Dictionary();
+
+ public GenFile CreateFile(ulong id)
+ => CreateId(id, () => new GenFile(id));
+ public GenFile GetExistingFile(ulong id)
+ => GetId(id, TypeTag.File);
+
+ public TypeDefinition CreateTypeDef(ulong id, TypeTag tag, IHasNestedDefinitions decl)
+ => CreateId(id, () => new TypeDefinition(tag, id, decl));
+ public TypeDefinition GetExistingTypeDef(ulong id, TypeTag tag)
+ {
+ var def = GetId(id, tag);
+ if (def.Tag == TypeTag.Unknown) def.Tag = tag;
+ return def;
+ }
+
+ public Annotation CreateAnnotation(ulong id, IHasNestedDefinitions decl)
+ => CreateId(id, () => new Annotation(id, decl));
+ public Annotation GetExistingAnnotation(ulong id)
+ => GetId(id, TypeTag.Annotation);
+
+ public Constant CreateConstant(ulong id, IHasNestedDefinitions decl)
+ => CreateId(id, () => new Constant(id, decl));
+ public Constant GetExistingConstant(ulong id)
+ => GetId(id, TypeTag.Const);
+
+ public IDefinition GetExistingDef(ulong id, TypeTag tag)
+ => GetId(id, tag);
+
+ public IEnumerable Files
+ {
+ get => _id2def.Values.Where(d => d.Tag == TypeTag.File).Select(f => f as GenFile);
+ }
+
+ T CreateId(ulong id, Func creator) where T : class, IDefinition
+ {
+ if (_id2def.TryGetValue(id, out var d))
+ {
+ throw new ArgumentException(nameof(id), $"Attempting to redefine {d.Tag.ToString()} {id.StrId()} (as {nameof(T)}).");
+ }
+ var def = creator();
+ _id2def.Add(id, def);
+ return def as T;
+ }
+
+ T GetId(ulong id, TypeTag tag) where T : IDefinition
+ {
+ if (!_id2def.TryGetValue(id, out var anyDef))
+ {
+ throw new ArgumentOutOfRangeException($"Attempting to retrieve nonexistent node {id.StrId()}.");
+ }
+ if (!(anyDef is T def) || (tag != TypeTag.Unknown && def.Tag != tag))
+ {
+ throw new ArgumentOutOfRangeException($"Attempting to retrieve {tag.ToString()} {id.StrId()}, but found {anyDef.Tag.ToString()} instead.");
+ }
+ return def;
+ }
+ }
+}
diff --git a/capnpc-csharp/Model/Enumerant.cs b/capnpc-csharp/Model/Enumerant.cs
index 99d53fc..96d9137 100644
--- a/capnpc-csharp/Model/Enumerant.cs
+++ b/capnpc-csharp/Model/Enumerant.cs
@@ -2,8 +2,12 @@
{
class Enumerant
{
+ string _literal;
public TypeDefinition TypeDefinition { get; set; }
- public string Literal { get; set; }
+ public string Literal {
+ get => _literal;
+ set => _literal = IdentifierRenamer.ToNonKeyword(value);
+ }
public ushort? Ordinal { get; set; }
public int CodeOrder { get; set; }
}
diff --git a/capnpc-csharp/Model/GenFile.cs b/capnpc-csharp/Model/GenFile.cs
index 045c975..f912303 100644
--- a/capnpc-csharp/Model/GenFile.cs
+++ b/capnpc-csharp/Model/GenFile.cs
@@ -1,15 +1,23 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using System.Collections.Generic;
namespace CapnpC.Model
{
- class GenFile: IHasNestedDefinitions
+ class GenFile: IDefinition, IHasNestedDefinitions
{
+ public ulong Id { get; }
+ public TypeTag Tag { get => TypeTag.File; }
+ public IHasNestedDefinitions DeclaringElement { get; }
+
public string Name { get; set; }
public string[] Namespace { get; set; }
- public List NestedTypes { get; } = new List();
- public List Constants { get; } = new List();
+ public IEnumerable NestedTypes { get => this.GetNestedTypes(); }
+ public ICollection NestedDefinitions { get; } = new List();
+ public ICollection Constants { get; } = new List();
+
+ public GenFile(ulong id)
+ {
+ Id = id;
+ }
}
}
diff --git a/capnpc-csharp/Model/IDefinition.cs b/capnpc-csharp/Model/IDefinition.cs
new file mode 100644
index 0000000..e226d40
--- /dev/null
+++ b/capnpc-csharp/Model/IDefinition.cs
@@ -0,0 +1,10 @@
+
+namespace CapnpC.Model
+{
+ interface IDefinition
+ {
+ ulong Id { get; }
+ TypeTag Tag { get; }
+ IHasNestedDefinitions DeclaringElement { get; }
+ }
+}
diff --git a/capnpc-csharp/Model/IHasNestedDefinitions.cs b/capnpc-csharp/Model/IHasNestedDefinitions.cs
index e9e030b..3885c60 100644
--- a/capnpc-csharp/Model/IHasNestedDefinitions.cs
+++ b/capnpc-csharp/Model/IHasNestedDefinitions.cs
@@ -1,10 +1,18 @@
using System.Collections.Generic;
+using System.Linq;
namespace CapnpC.Model
{
interface IHasNestedDefinitions
{
- List NestedTypes { get; }
- List Constants { get; }
+ IEnumerable NestedTypes { get; }
+ ICollection NestedDefinitions { get; }
+ ICollection Constants { get; }
+ }
+
+ static partial class Extensions
+ {
+ public static IEnumerable GetNestedTypes(this IHasNestedDefinitions def)
+ => def.NestedDefinitions.Select(d => d as TypeDefinition).Where(d => d != null);
}
}
diff --git a/capnpc-csharp/Model/IdentifierRenamer.cs b/capnpc-csharp/Model/IdentifierRenamer.cs
new file mode 100644
index 0000000..9c6ff07
--- /dev/null
+++ b/capnpc-csharp/Model/IdentifierRenamer.cs
@@ -0,0 +1,22 @@
+using Microsoft.CodeAnalysis.CSharp;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CapnpC.Model
+{
+ public class IdentifierRenamer
+ {
+ public static bool IsAnyKeyword(string str)
+ {
+ return SyntaxFacts.GetKeywordKind(str) != SyntaxKind.None
+ || SyntaxFacts.GetContextualKeywordKind(str) != SyntaxKind.None;
+ }
+ public static string ToNonKeyword(string str)
+ {
+ // Capnp schema identifiers should be already valid, but could be a keyword
+ if (IsAnyKeyword(str)) return $"@{str}";
+ return str;
+ }
+ }
+}
diff --git a/capnpc-csharp/Model/SchemaModel.cs b/capnpc-csharp/Model/SchemaModel.cs
index 0e6b5be..4ebafec 100644
--- a/capnpc-csharp/Model/SchemaModel.cs
+++ b/capnpc-csharp/Model/SchemaModel.cs
@@ -9,30 +9,30 @@ namespace CapnpC.Model
class SchemaModel
{
readonly Schema.CodeGeneratorRequest.Reader _request;
- readonly List _files = new List();
- readonly Stack _typeNest = new Stack();
- readonly TypeDefinitionManager _typeDefMgr = new TypeDefinitionManager();
- Method _currentMethod;
+ readonly List _generatedFiles = new List();
+ readonly DefinitionManager _typeDefMgr = new DefinitionManager();
- Dictionary _id2node;
+ readonly Dictionary _id2node = new Dictionary();
public SchemaModel(Schema.CodeGeneratorRequest.Reader request)
{
_request = request;
}
- public IReadOnlyList FilesToGenerate => _files;
+ public IReadOnlyList FilesToGenerate => _generatedFiles;
+
+ Schema.Node.Reader? IdToNode(ulong id, bool mustExist)
+ {
+ if (_id2node.TryGetValue(id, out var node))
+ return node;
+ if (mustExist)
+ throw new InvalidSchemaException($"Node with ID {id.StrId()} is required by the codegen backend but is missing.");
+ return null;
+ }
Schema.Node.Reader IdToNode(ulong id)
{
- try
- {
- return _id2node[id];
- }
- catch (KeyNotFoundException)
- {
- throw new InvalidSchemaException($"Node with ID {id} is missing");
- }
+ return (Schema.Node.Reader)IdToNode(id, true);
}
void Build()
@@ -42,69 +42,178 @@ namespace CapnpC.Model
throw new InvalidSchemaException("No nodes, nothing to generate");
}
- try
+ foreach (var node in _request.Nodes)
{
- _id2node = _request.Nodes.ToDictionary(n => n.Id);
- }
- catch (ArgumentException)
- {
- throw new InvalidSchemaException("Nodes with duplicate IDs detected");
- }
-
- foreach (var reqFile in _request.RequestedFiles)
- {
- var file = new GenFile()
+ if (_id2node.TryGetValue(node.Id, out var existingNode))
{
- Name = reqFile.Filename
- };
+ throw new InvalidSchemaException($"Node {node.StrId()} \"{node.DisplayName}\" has a duplicate ID, prior node was \"{existingNode.DisplayName}\"");
+ }
+ _id2node[node.Id] = node;
+ }
- _files.Add(file);
- _typeNest.Push(file);
+ var requestedFiles = _request.RequestedFiles.ToDictionary(req => req.Id);
+ BuildPass1(requestedFiles);
+ BuildPass2(requestedFiles);
+ }
- var fileNode = IdToNode(reqFile.Id);
+ // First pass: create type definitions for each node.
- if (!fileNode.IsFile)
- throw new InvalidSchemaException("Expected a file node");
+ struct Pass1State
+ {
+ public HashSet unprocessedNodes;
+ public bool isGenerated;
+ public IHasNestedDefinitions parent;
+ }
- ProcessFile(fileNode);
-
- _typeNest.Pop();
+ void BuildPass1(Dictionary requestedFiles)
+ {
+ Pass1State state = new Pass1State()
+ {
+ unprocessedNodes = new HashSet(_id2node.Keys)
+ };
+ 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);
+ _generatedFiles.Add(file);
+ }
+ else
+ {
+ file = (GenFile)ProcessNodePass1(node.Id, node.DisplayName, state);
+ }
+ }
+ if (state.unprocessedNodes.Count != 0)
+ {
+ throw new InvalidSchemaException("Unreferenced nodes were present in the schema.");
}
}
- void ProcessFile(Schema.Node.Reader fileReader)
+ IDefinition ProcessNodePass1(ulong id, string name, Pass1State state)
{
- foreach (var annotation in fileReader.Annotations)
+ if (!(IdToNode(id, state.isGenerated) is Schema.Node.Reader node))
+ return null;
+ if (!state.unprocessedNodes.Remove(id))
+ return null;
+
+ IDefinition def = null;
+ bool processNestedNodes = false;
+ bool processFields = false;
+ bool processInterfaceMethods = false;
+
+ switch (node.GetKind())
+ {
+ case NodeKind.Annotation:
+ return _typeDefMgr.CreateAnnotation(id, state.parent);
+ case NodeKind.Const:
+ 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);
+ file.Namespace = GetNamespaceAnnotation(node);
+ file.Name = name;
+ def = file;
+ processNestedNodes = true;
+ break;
+ case NodeKind.Enum:
+ break;
+ case NodeKind.Interface:
+ processNestedNodes = true;
+ processFields = true;
+ processInterfaceMethods = true;
+ break;
+ case NodeKind.Struct:
+ case NodeKind.Group:
+ processNestedNodes = true;
+ processFields = true;
+ break;
+ default:
+ throw new InvalidSchemaException($"Don't know how to process node {node.StrId()} \"{node.DisplayName}\"");
+ }
+
+ if (def == null)
+ {
+ var typeDef = _typeDefMgr.CreateTypeDef(id, node.GetTypeTag(), state.parent);
+ typeDef.Name = name;
+ def = typeDef;
+ }
+ state.parent = def as IHasNestedDefinitions;
+
+ if (processNestedNodes && node.NestedNodes != null)
+ foreach (var nested in node.NestedNodes)
+ {
+ ProcessNodePass1(nested.Id, nested.Name, state);
+ }
+ if (processFields && node.Fields != null)
+ foreach (var field in node.Fields.Where(f => f.IsGroup))
+ {
+ var group = IdToNode(field.Group_TypeId);
+ if (!group.IsStruct || !group.Struct_IsGroup)
+ {
+ throw new InvalidSchemaException($"Expected node with id {group.StrId()} to be a struct definition");
+ }
+ ProcessNodePass1(field.Group_TypeId, field.Name, state);
+ }
+ if (processInterfaceMethods && node.Interface_Methods != null)
+ foreach (var method in node.Interface_Methods)
+ {
+ var pnode = IdToNode(method.ParamStructType);
+ if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
+ pnode = IdToNode(method.ResultStructType);
+ if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
+ }
+ return def;
+ }
+
+ string[] GetNamespaceAnnotation(Schema.Node.Reader fileNode)
+ {
+ foreach (var annotation in fileNode.Annotations)
{
if (annotation.Id == 0xb9c6f99ebf805f2c) // Cxx namespace
{
- ((GenFile)_typeNest.Peek()).Namespace = annotation.Value.Text.Split("::");
+ return annotation.Value.Text.Split(new string[1] { "::" }, default);
}
}
+ return null;
+ }
- foreach (var nestedNode in fileReader.NestedNodes)
+ // 2nd pass: Generate types based on definitions
+
+ struct Pass2State
+ {
+ public Method currentMethod;
+ public bool isGenerated;
+ public HashSet processedNodes;
+ }
+
+ void BuildPass2(Dictionary requestedFiles)
+ {
+ var state = new Pass2State() { processedNodes = new HashSet() };
+ foreach (var file in _typeDefMgr.Files)
{
- var node = IdToNode(nestedNode.Id);
-
- ProcessNode(node, nestedNode.Name);
+ var node = IdToNode(file.Id);
+ state.isGenerated = requestedFiles.ContainsKey(file.Id);
+ ProcessNestedNodes(node.NestedNodes, state);
}
}
- TypeDefinition GetOrCreateTypeDef(ulong id, TypeTag tag) => _typeDefMgr.GetOrCreate(id, tag);
- TypeDefinition GetGroupTypeDef(ulong id, string name)
+ void ProcessNestedNodes(IEnumerable nestedNodes, Pass2State state)
{
- var nodeReader = _id2node[id];
-
- if (!nodeReader.IsStruct)
- throw new InvalidSchemaException($"Expected node with id {id} to be a struct definition");
-
- return ProcessStruct(nodeReader, name);
+ foreach (var nestedNode in nestedNodes)
+ {
+ ProcessNode(nestedNode.Id, state);
+ }
}
- void ProcessBrand(Schema.Brand.Reader brandReader, Type type)
+
+ void ProcessBrand(Schema.Brand.Reader brandReader, Type type, Pass2State state)
{
foreach (var scopeReader in brandReader.Scopes)
{
- var whatToBind = GetOrCreateTypeDef(scopeReader.ScopeId, TypeTag.Unknown);
+ var whatToBind = ProcessTypeDef(scopeReader.ScopeId, state);
int index = 0;
switch (0)
@@ -121,7 +230,7 @@ namespace CapnpC.Model
switch (0)
{
case 0 when bindingReader.IsType:
- type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type));
+ type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type, state));
break;
case 0 when bindingReader.IsUnbound:
@@ -146,7 +255,8 @@ namespace CapnpC.Model
}
}
}
- Type ProcessType(Schema.Type.Reader typeReader)
+
+ Type ProcessType(Schema.Type.Reader typeReader, Pass2State state)
{
Type result;
@@ -159,7 +269,7 @@ namespace CapnpC.Model
return Types.FromParameter(
new GenericParameter()
{
- DeclaringEntity = GetOrCreateTypeDef(typeReader.AnyPointer_Parameter_ScopeId, TypeTag.Unknown),
+ DeclaringEntity = ProcessTypeDef(typeReader.AnyPointer_Parameter_ScopeId, state),
Index = typeReader.AnyPointer_Parameter_ParameterIndex
});
@@ -167,7 +277,7 @@ namespace CapnpC.Model
return Types.FromParameter(
new GenericParameter()
{
- DeclaringEntity = _currentMethod ?? throw new InvalidOperationException("current method not set"),
+ DeclaringEntity = state.currentMethod ?? throw new InvalidOperationException("current method not set"),
Index = typeReader.AnyPointer_ImplicitMethodParameter_ParameterIndex
});
@@ -205,7 +315,7 @@ namespace CapnpC.Model
return Types.F64;
case 0 when typeReader.IsEnum:
- return Types.FromDefinition(GetOrCreateTypeDef(typeReader.Enum_TypeId, TypeTag.Enum));
+ return Types.FromDefinition(ProcessTypeDef(typeReader.Enum_TypeId, state, TypeTag.Enum));
case 0 when typeReader.IsFloat32:
return Types.F32;
@@ -223,16 +333,16 @@ namespace CapnpC.Model
return Types.S8;
case 0 when typeReader.IsInterface:
- result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Interface_TypeId, TypeTag.Interface));
- ProcessBrand(typeReader.Interface_Brand, result);
+ result = Types.FromDefinition(ProcessTypeDef(typeReader.Interface_TypeId, state, TypeTag.Interface));
+ ProcessBrand(typeReader.Interface_Brand, result, state);
return result;
case 0 when typeReader.IsList:
- return Types.List(ProcessType(typeReader.List_ElementType));
+ return Types.List(ProcessType(typeReader.List_ElementType, state));
case 0 when typeReader.IsStruct:
- result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Struct_TypeId, TypeTag.Struct));
- ProcessBrand(typeReader.Struct_Brand, result);
+ result = Types.FromDefinition(ProcessTypeDef(typeReader.Struct_TypeId, state, TypeTag.Struct));
+ ProcessBrand(typeReader.Struct_Brand, result, state);
return result;
case 0 when typeReader.IsText:
@@ -365,7 +475,7 @@ namespace CapnpC.Model
return value;
}
- void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List fields)
+ void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List fields, Pass2State state)
{
if (reader.Fields == null)
{
@@ -389,15 +499,15 @@ namespace CapnpC.Model
switch (0)
{
case 0 when fieldReader.IsGroup:
- field.Type = Types.FromDefinition(GetGroupTypeDef(
- fieldReader.Group_TypeId, fieldReader.Name));
+ var def = ProcessTypeDef(fieldReader.Group_TypeId, state, TypeTag.Group);
+ field.Type = Types.FromDefinition(def);
break;
case 0 when fieldReader.IsSlot:
field.DefaultValue = ProcessValue(fieldReader.Slot_DefaultValue);
field.DefaultValueIsExplicit = fieldReader.Slot_HadExplicitDefault;
field.Offset = fieldReader.Slot_Offset;
- field.Type = ProcessType(fieldReader.Slot_Type);
+ field.Type = ProcessType(fieldReader.Slot_Type, state);
field.DefaultValue.Type = field.Type;
break;
@@ -411,7 +521,7 @@ namespace CapnpC.Model
}
}
- void ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader)
+ TypeDefinition ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader, Pass2State state)
{
def.IsGeneric = reader.IsGeneric;
@@ -423,18 +533,9 @@ namespace CapnpC.Model
}
}
- _typeNest.Push(def);
+ ProcessNestedNodes(reader.NestedNodes, state);
- if (reader.NestedNodes != null)
- {
- foreach (var nestedReader in reader.NestedNodes)
- {
- var node = IdToNode(nestedReader.Id);
- ProcessNode(node, nestedReader.Name);
- }
- }
-
- ProcessFields(reader, def, def.Fields);
+ ProcessFields(reader, def, def.Fields, state);
if (reader.IsInterface)
{
@@ -450,12 +551,12 @@ namespace CapnpC.Model
{
method.GenericParameters.Add(implicitParameterReader.Name);
}
- _currentMethod = method;
+ state.currentMethod = method;
def.Methods.Add(method);
var paramNode = IdToNode(methodReader.ParamStructType);
- var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params);
+ var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params, state);
if (paramType != null)
{
paramType.SpecialName = SpecialName.MethodParamsStruct;
@@ -470,7 +571,7 @@ namespace CapnpC.Model
method.ParamsStruct.InheritFreeParameters(method);
var resultNode = IdToNode(methodReader.ResultStructType);
- var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results);
+ var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results, state);
if (resultType != null)
{
resultType.SpecialName = SpecialName.MethodResultStruct;
@@ -485,24 +586,13 @@ namespace CapnpC.Model
method.ResultStruct.InheritFreeParameters(method);
}
- _currentMethod = null;
+ state.currentMethod = null;
}
-
- _typeNest.Pop();
+ return def;
}
- TypeDefinition ProcessStruct(Schema.Node.Reader structReader, string name)
+ TypeDefinition ProcessStruct(Schema.Node.Reader structReader, TypeDefinition def, Pass2State state)
{
- var def = GetOrCreateTypeDef(
- structReader.Id,
- structReader.Struct_IsGroup ? TypeTag.Group : TypeTag.Struct);
-
- def.DeclaringElement = _typeNest.Peek();
- if (structReader.Struct_IsGroup)
- ((TypeDefinition)def.DeclaringElement).NestedGroups.Add(def);
- else
- def.DeclaringElement.NestedTypes.Add(def);
- def.Name = name;
def.StructDataWordCount = structReader.Struct_DataWordCount;
def.StructPointerCount = structReader.Struct_PointerCount;
@@ -513,12 +603,10 @@ namespace CapnpC.Model
16u * structReader.Struct_DiscriminantOffset);
}
- ProcessInterfaceOrStructTail(def, structReader);
-
- return def;
+ return ProcessInterfaceOrStructTail(def, structReader, state);
}
- TypeDefinition ProcessParameterList(Schema.Node.Reader reader, Schema.Brand.Reader brandReader, List list)
+ TypeDefinition ProcessParameterList(Schema.Node.Reader reader, Schema.Brand.Reader brandReader, List list, Pass2State state)
{
//# If a named parameter list was specified in the method
//# declaration (rather than a single struct parameter type) then a corresponding struct type is
@@ -533,58 +621,38 @@ namespace CapnpC.Model
throw new InvalidSchemaException("Expected a struct");
}
+ var def = ProcessTypeDef(reader.Id, state, TypeTag.Struct);
+
if (reader.ScopeId == 0)
{
// Auto-generated => Named parameter list
- ProcessFields(reader, null, list);
- return ProcessStruct(reader, null);
+ foreach (var field in def.Fields) list.Add(field);
+ return def;
}
else
{
// Single, anonymous, struct-typed parameter
- var def = GetOrCreateTypeDef(reader.Id, TypeTag.Struct);
var type = Types.FromDefinition(def);
- ProcessBrand(brandReader, type);
+ ProcessBrand(brandReader, type, state);
var anon = new Field() { Type = type };
list.Add(anon);
return null;
}
}
- TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, string name)
+ TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, TypeDefinition def, Pass2State state)
{
- var def = GetOrCreateTypeDef(
- ifaceReader.Id,
- TypeTag.Interface);
-
- def.DeclaringElement = _typeNest.Peek();
- def.DeclaringElement.NestedTypes.Add(def);
- def.Name = name;
-
foreach (var superClassReader in ifaceReader.Interface_Superclasses)
{
- var superClass = GetOrCreateTypeDef(
- superClassReader.Id,
- TypeTag.Interface);
-
+ var superClass = ProcessTypeDef(superClassReader.Id, state, TypeTag.Interface);
def.Superclasses.Add(Types.FromDefinition(superClass));
}
- ProcessInterfaceOrStructTail(def, ifaceReader);
-
- return def;
+ return ProcessInterfaceOrStructTail(def, ifaceReader, state);
}
- void ProcessEnum(Schema.Node.Reader enumReader, string name)
+ TypeDefinition ProcessEnum(Schema.Node.Reader enumReader, TypeDefinition def, Pass2State state)
{
- var def = GetOrCreateTypeDef(enumReader.Id, TypeTag.Enum);
-
- def.DeclaringElement = _typeNest.Peek();
- def.DeclaringElement.NestedTypes.Add(def);
- def.Name = name;
-
- _typeNest.Push(def);
-
foreach (var fieldReader in enumReader.Enumerants)
{
var field = new Enumerant()
@@ -601,46 +669,52 @@ namespace CapnpC.Model
def.Enumerants.Add(field);
}
-
- _typeNest.Pop();
+ return def;
}
- void ProcessConst(Schema.Node.Reader constReader, string name)
+ Constant ProcessConst(Schema.Node.Reader constReader, Constant @const, Pass2State state)
{
var value = ProcessValue(constReader.Const_Value);
- value.Type = ProcessType(constReader.Const_Type);
-
- _typeNest.Peek().Constants.Add(value);
+ value.Type = ProcessType(constReader.Const_Type, state);
+ @const.Value = value;
+ return @const;
}
- void ProcessNode(Schema.Node.Reader node, string name)
+ TypeDefinition ProcessTypeDef(ulong id, Pass2State state, TypeTag tag = default)
{
- switch (0)
+ var def = ProcessNode(id, state, tag);
+ var typeDef = def as TypeDefinition;
+ if (def == 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)
+ {
+ if (!(IdToNode(id, state.isGenerated) is Schema.Node.Reader node)) return null;
+ var kind = node.GetKind();
+ if (tag == TypeTag.Unknown) tag = kind.GetTypeTag();
+ var def = _typeDefMgr.GetExistingDef(id, tag);
+ if (state.processedNodes.Contains(id)) return def;
+ state.processedNodes.Add(id);
+
+ switch (def)
{
- case 0 when node.IsAnnotation:
- break;
-
- case 0 when node.IsConst:
- ProcessConst(node, name);
- break;
-
- case 0 when node.IsEnum:
- ProcessEnum(node, name);
- break;
-
- case 0 when node.IsFile:
- throw new InvalidSchemaException("Did not expect file nodes to appear as nested nodes");
-
- case 0 when node.IsInterface:
- ProcessInterface(node, name);
- break;
-
- case 0 when node.IsStruct:
- ProcessStruct(node, name);
- break;
-
+ case Annotation annotation:
+ return annotation;
+ case Constant constant:
+ def.DeclaringElement.Constants.Add(ProcessConst(node, constant, state));
+ return def;
+ case TypeDefinition typeDef when kind == NodeKind.Enum:
+ return ProcessEnum(node, typeDef, state);
+ case TypeDefinition typeDef when kind == NodeKind.Interface:
+ return ProcessInterface(node, typeDef, state);
+ case TypeDefinition typeDef when kind == NodeKind.Struct || kind == NodeKind.Group:
+ return ProcessStruct(node, typeDef, state);
default:
- throw new InvalidSchemaException($"Don't know how to process node {node.DisplayName}");
+ throw new InvalidProgramException($"An unexpected node {node.StrId()} was found during the 2nd schema model building pass.");
}
}
@@ -651,4 +725,55 @@ namespace CapnpC.Model
return model;
}
}
+
+ public enum NodeKind
+ {
+ Unknown,
+ Annotation,
+ Const,
+ Enum,
+ File,
+ Interface,
+ Struct,
+ Group
+ }
+
+ public static class SchemaExtensions
+ {
+ public static string GetName(this Schema.Node.Reader node)
+ => node.DisplayName.Substring((int)node.DisplayNamePrefixLength);
+
+ public static string StrId(this Schema.Node.Reader node)
+ => $"0x{node.Id.ToString("X")}";
+
+ public static string StrId(this ulong nodeId)
+ => $"0x{nodeId.ToString("X")}";
+
+ public static NodeKind GetKind(this Schema.Node.Reader node)
+ {
+ if (node.IsStruct)
+ return node.Struct_IsGroup ? NodeKind.Group : NodeKind.Struct;
+ if (node.IsInterface) return NodeKind.Interface;
+ if (node.IsEnum) return NodeKind.Enum;
+ if (node.IsConst) return NodeKind.Const;
+ if (node.IsAnnotation) return NodeKind.Annotation;
+ if (node.IsFile) return NodeKind.File;
+ return NodeKind.Unknown;
+ }
+
+ internal static TypeTag GetTypeTag(this NodeKind kind)
+ {
+ switch (kind)
+ {
+ case NodeKind.Enum: return TypeTag.Enum;
+ case NodeKind.Interface: return TypeTag.Interface;
+ case NodeKind.Struct: return TypeTag.Struct;
+ case NodeKind.Group: return TypeTag.Group;
+ default: return TypeTag.Unknown;
+ }
+ }
+
+ internal static TypeTag GetTypeTag(this Schema.Node.Reader node)
+ => node.GetKind().GetTypeTag();
+ }
}
diff --git a/capnpc-csharp/Model/Type.cs b/capnpc-csharp/Model/Type.cs
index 3512b2b..c1017df 100644
--- a/capnpc-csharp/Model/Type.cs
+++ b/capnpc-csharp/Model/Type.cs
@@ -9,9 +9,16 @@ namespace CapnpC.Model
{
class Type: AbstractType
{
+ // Representation of a type expression in the schema language
+
public TypeDefinition Definition { get; set; }
+ // The model for all nodes that are not file nodes - they define types
+
public GenericParameter Parameter { get; set; }
+ // A reference to type parameter in this scope
+
public Type ElementType { get; set; }
+ // The type of a list element, if this is a list.
readonly Dictionary _parameterBindings =
new Dictionary();
diff --git a/capnpc-csharp/Model/TypeDefinition.cs b/capnpc-csharp/Model/TypeDefinition.cs
index ce00e2c..910d4bd 100644
--- a/capnpc-csharp/Model/TypeDefinition.cs
+++ b/capnpc-csharp/Model/TypeDefinition.cs
@@ -2,7 +2,7 @@
using System.Linq;
namespace CapnpC.Model
{
- class TypeDefinition : AbstractType, IHasNestedDefinitions, IHasGenericParameters
+ class TypeDefinition : AbstractType, IDefinition, IHasNestedDefinitions, IHasGenericParameters
{
public class DiscriminationInfo
{
@@ -16,23 +16,30 @@ namespace CapnpC.Model
public uint TagOffset { get; }
}
- public TypeDefinition(TypeTag tag, ulong id)
+ public TypeDefinition(TypeTag tag, ulong id, IHasNestedDefinitions parent)
{
Tag = tag;
Id = id;
+ DeclaringElement = parent;
+ if (tag == TypeTag.Group)
+ ((TypeDefinition)parent).NestedGroups.Add(this);
+ else
+ parent.NestedDefinitions.Add(this);
}
public ulong Id { get; }
- public IHasNestedDefinitions DeclaringElement { get; set; }
+ public IHasNestedDefinitions DeclaringElement { get; }
+
public Method UsingMethod { get; set; }
public string Name { get; set; }
public SpecialName SpecialName { get; set; }
public DiscriminationInfo UnionInfo { get; set; }
public new List Fields => base.Fields;
public List Enumerants { get; } = new List();
- public List NestedTypes { get; } = new List();
+ public ICollection NestedDefinitions { get; } = new List();
+ public IEnumerable NestedTypes { get => this.GetNestedTypes(); }
public List NestedGroups { get; } = new List();
- public List Constants { get; } = new List();
+ public ICollection Constants { get; } = new List();
public List Methods { get; } = new List();
public List Superclasses { get; } = new List();
public List GenericParameters { get; } = new List();
@@ -54,6 +61,16 @@ namespace CapnpC.Model
}
}
+ public GenFile File
+ {
+ get
+ {
+ IHasNestedDefinitions cur = this;
+ while (cur is TypeDefinition def) cur = def.DeclaringElement;
+ return cur as GenFile;
+ }
+ }
+
public IEnumerable AllTypeParameters
{
get
diff --git a/capnpc-csharp/Model/TypeDefinitionManager.cs b/capnpc-csharp/Model/TypeDefinitionManager.cs
deleted file mode 100644
index 7c1562e..0000000
--- a/capnpc-csharp/Model/TypeDefinitionManager.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace CapnpC.Model
-{
- class TypeDefinitionManager
- {
- readonly Dictionary _id2def =
- new Dictionary();
-
- public TypeDefinition GetOrCreate(ulong id, TypeTag tag)
- {
- if (_id2def.TryGetValue(id, out var def))
- {
- if (def.Tag == TypeTag.Unknown)
- {
- def.Tag = tag;
- }
- else if (tag != TypeTag.Unknown && def.Tag != tag)
- {
- throw new ArgumentOutOfRangeException(nameof(tag), "Type tag does not match existing type");
- }
- }
- else
- {
- def = new TypeDefinition(tag, id);
- _id2def.Add(id, def);
- }
-
- return def;
- }
-
- public TypeDefinition GetExisting(ulong id)
- {
- return _id2def[id];
- }
- }
-}
diff --git a/capnpc-csharp/Model/TypeTag.cs b/capnpc-csharp/Model/TypeTag.cs
index 66b7d5e..122b6d7 100644
--- a/capnpc-csharp/Model/TypeTag.cs
+++ b/capnpc-csharp/Model/TypeTag.cs
@@ -28,6 +28,9 @@
Group,
Interface,
Enum,
- AnyEnum
+ AnyEnum,
+ Const,
+ Annotation,
+ File
}
}
diff --git a/capnpc-csharp/Program.cs b/capnpc-csharp/Program.cs
index 4da2ad2..fe758f2 100644
--- a/capnpc-csharp/Program.cs
+++ b/capnpc-csharp/Program.cs
@@ -1,6 +1,9 @@
using Capnp;
using System;
using System.IO;
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleToAttribute("capnpc-csharp.tests")]
namespace CapnpC
{
diff --git a/capnpc-csharp/Schema/schema-with-offsets.capnp b/capnpc-csharp/Schema/schema-with-offsets.capnp
index 709eaab..45fe760 100644
--- a/capnpc-csharp/Schema/schema-with-offsets.capnp
+++ b/capnpc-csharp/Schema/schema-with-offsets.capnp
@@ -1,6 +1,6 @@
-# F:/Downloads/capnproto-c++-win32-0.7.0/capnproto-c++-0.7.0/src/capnp/schema-priv.capnp
+# schema.capnp
@0xa93fc509624c72d9;
-$import "/F:/Downloads/capnproto-c++-win32-0.7.0/capnproto-c++-0.7.0/src/capnp/c++.capnp".namespace("capnp::schema");
+$import "/capnp/c++.capnp".namespace("capnp::schema");
struct Node @0xe682ab4cf923a417 { # 40 bytes, 6 ptrs
id @0 :UInt64; # bits[0, 64)
displayName @1 :Text; # ptr[0]