From 107d10e3f43d260655b0f33f9b549e450fefb637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6llner?= Date: Fri, 6 Sep 2019 19:25:54 +0200 Subject: [PATCH] Factoring out capnpc-csharp into separate assembly --- Capnp.Net.sln | 6 + .../CapnpC.CSharp.Generator.csproj | 15 + CapnpC.CSharp.Generator/CapnpCompilation.cs | 49 + .../CodeGen/CodeGenerator.cs | 190 ++ .../CodeGen/CommonSnippetGen.cs | 91 + .../CodeGen/DomainClassSnippetGen.cs | 970 ++++++++++ CapnpC.CSharp.Generator/CodeGen/GenNames.cs | 600 +++++++ .../CodeGen/GeneratorOptions.cs | 38 + .../CodeGen/InterfaceSnippetGen.cs | 843 +++++++++ CapnpC.CSharp.Generator/CodeGen/Name.cs | 33 + .../CodeGen/ReaderSnippetGen.cs | 713 ++++++++ .../CodeGen/SerializerStateWorder.cs | 10 + .../CodeGen/SkeletonWorder.cs | 12 + .../CodeGen/SyntaxHelpers.cs | 126 ++ .../CodeGen/WriterSnippetGen.cs | 403 +++++ .../FileGenerationResult.cs | 52 + CapnpC.CSharp.Generator/GenerationResult.cs | 44 + CapnpC.CSharp.Generator/Model/AbstractType.cs | 45 + CapnpC.CSharp.Generator/Model/Annotation.cs | 23 + CapnpC.CSharp.Generator/Model/Constant.cs | 23 + .../Model/DefinitionManager.cs | 67 + CapnpC.CSharp.Generator/Model/Enumerant.cs | 14 + CapnpC.CSharp.Generator/Model/Field.cs | 52 + CapnpC.CSharp.Generator/Model/GenFile.cs | 25 + .../Model/GenericParameter.cs | 26 + .../Model/HasGenericParameters.cs | 19 + CapnpC.CSharp.Generator/Model/IDefinition.cs | 11 + .../Model/IHasGenericParameters.cs | 9 + .../Model/IHasNestedDefinitions.cs | 18 + .../Model/IdentifierRenamer.cs | 22 + .../Model/InvalidSchemaException.cs | 11 + CapnpC.CSharp.Generator/Model/Method.cs | 16 + CapnpC.CSharp.Generator/Model/SchemaModel.cs | 783 ++++++++ CapnpC.CSharp.Generator/Model/SpecialName.cs | 9 + CapnpC.CSharp.Generator/Model/Type.cs | 201 +++ CapnpC.CSharp.Generator/Model/TypeCategory.cs | 8 + .../Model/TypeDefinition.cs | 89 + CapnpC.CSharp.Generator/Model/TypeTag.cs | 36 + CapnpC.CSharp.Generator/Model/Types.cs | 56 + CapnpC.CSharp.Generator/Model/Value.cs | 300 ++++ .../Schema/SchemaSerialization.cs | 1575 +++++++++++++++++ .../Schema/schema-with-offsets.capnp | 235 +++ .../CapnpFileCodeBehindGenerator.cs | 6 +- .../GenerateCapnpFileCodeBehindTask.cs | 8 +- .../ICapnpCsharpGenerator.cs | 2 +- .../CPS/Buildsystem/Rules/CapnpFileType.xaml | 11 +- 46 files changed, 7890 insertions(+), 5 deletions(-) create mode 100644 CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj create mode 100644 CapnpC.CSharp.Generator/CapnpCompilation.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/GenNames.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/GeneratorOptions.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/Name.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/SerializerStateWorder.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/SkeletonWorder.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/SyntaxHelpers.cs create mode 100644 CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs create mode 100644 CapnpC.CSharp.Generator/FileGenerationResult.cs create mode 100644 CapnpC.CSharp.Generator/GenerationResult.cs create mode 100644 CapnpC.CSharp.Generator/Model/AbstractType.cs create mode 100644 CapnpC.CSharp.Generator/Model/Annotation.cs create mode 100644 CapnpC.CSharp.Generator/Model/Constant.cs create mode 100644 CapnpC.CSharp.Generator/Model/DefinitionManager.cs create mode 100644 CapnpC.CSharp.Generator/Model/Enumerant.cs create mode 100644 CapnpC.CSharp.Generator/Model/Field.cs create mode 100644 CapnpC.CSharp.Generator/Model/GenFile.cs create mode 100644 CapnpC.CSharp.Generator/Model/GenericParameter.cs create mode 100644 CapnpC.CSharp.Generator/Model/HasGenericParameters.cs create mode 100644 CapnpC.CSharp.Generator/Model/IDefinition.cs create mode 100644 CapnpC.CSharp.Generator/Model/IHasGenericParameters.cs create mode 100644 CapnpC.CSharp.Generator/Model/IHasNestedDefinitions.cs create mode 100644 CapnpC.CSharp.Generator/Model/IdentifierRenamer.cs create mode 100644 CapnpC.CSharp.Generator/Model/InvalidSchemaException.cs create mode 100644 CapnpC.CSharp.Generator/Model/Method.cs create mode 100644 CapnpC.CSharp.Generator/Model/SchemaModel.cs create mode 100644 CapnpC.CSharp.Generator/Model/SpecialName.cs create mode 100644 CapnpC.CSharp.Generator/Model/Type.cs create mode 100644 CapnpC.CSharp.Generator/Model/TypeCategory.cs create mode 100644 CapnpC.CSharp.Generator/Model/TypeDefinition.cs create mode 100644 CapnpC.CSharp.Generator/Model/TypeTag.cs create mode 100644 CapnpC.CSharp.Generator/Model/Types.cs create mode 100644 CapnpC.CSharp.Generator/Model/Value.cs create mode 100644 CapnpC.CSharp.Generator/Schema/SchemaSerialization.cs create mode 100644 CapnpC.CSharp.Generator/Schema/schema-with-offsets.capnp diff --git a/Capnp.Net.sln b/Capnp.Net.sln index 8e20bf5..15732e6 100644 --- a/Capnp.Net.sln +++ b/Capnp.Net.sln @@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "capnpc-csharp.tests", "capn EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnpc.Csharp.MsBuild.Generation", "Capnpc.Csharp.MsBuild.Generation\Capnpc.Csharp.MsBuild.Generation.csproj", "{1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapnpC.CSharp.Generator", "CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj", "{C3A3BB49-356E-4762-A190-76D877BE18F7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}.Release|Any CPU.Build.0 = Release|Any CPU + {C3A3BB49-356E-4762-A190-76D877BE18F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3A3BB49-356E-4762-A190-76D877BE18F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3A3BB49-356E-4762-A190-76D877BE18F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3A3BB49-356E-4762-A190-76D877BE18F7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj b/CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj new file mode 100644 index 0000000..53233fd --- /dev/null +++ b/CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.1 + + + + + + + + + + + diff --git a/CapnpC.CSharp.Generator/CapnpCompilation.cs b/CapnpC.CSharp.Generator/CapnpCompilation.cs new file mode 100644 index 0000000..9f1a0a4 --- /dev/null +++ b/CapnpC.CSharp.Generator/CapnpCompilation.cs @@ -0,0 +1,49 @@ +using Capnp; +using System; +using System.Diagnostics; +using System.IO; + +namespace CapnpC.CSharp.Generator +{ + /// + /// Provides methods for controlling both the C# code generator backend and the frontend "capnpc" + /// + public static class CapnpCompilation + { + /// + /// Generates C# code from given input stream + /// + /// input stream containing the binary code generation request, which the frontend capnpc emits + /// generation result + /// if is null + public static GenerationResult GenerateFromStream(Stream input) + { + if (input == null) + throw new ArgumentNullException(nameof(input)); + + try + { + WireFrame segments; + + using (input) + { + segments = Framing.ReadSegments(input); + } + + var dec = DeserializerState.CreateRoot(segments); + var reader = Schema.CodeGeneratorRequest.Reader.Create(dec); + var model = Model.SchemaModel.Create(reader); + var codeGen = new CodeGen.CodeGenerator(model, new CodeGen.GeneratorOptions()); + return new GenerationResult(codeGen.Generate()); + } + catch (Exception exception) + { + return new GenerationResult(exception); + } + } + + public static GenerationResult InvokeCapnpcAndGenerate() + { + } + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs b/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs new file mode 100644 index 0000000..c983ccc --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs @@ -0,0 +1,190 @@ +namespace CapnpC.CSharp.Generator.CodeGen +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using CapnpC.CSharp.Generator.Model; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + using static SyntaxHelpers; + + class CodeGenerator + { + readonly SchemaModel _model; + readonly GenNames _names; + readonly CommonSnippetGen _commonGen; + readonly DomainClassSnippetGen _domClassGen; + readonly ReaderSnippetGen _readerGen; + readonly WriterSnippetGen _writerGen; + readonly InterfaceSnippetGen _interfaceGen; + + public CodeGenerator(SchemaModel model, GeneratorOptions options) + { + _model = model; + _names = new GenNames(options); + _commonGen = new CommonSnippetGen(_names); + _domClassGen = new DomainClassSnippetGen(_names); + _readerGen = new ReaderSnippetGen(_names); + _writerGen = new WriterSnippetGen(_names); + _interfaceGen = new InterfaceSnippetGen(_names); + } + + internal GenNames GetNames() => _names; + + IEnumerable TransformEnum(TypeDefinition def) + { + yield return _commonGen.MakeEnum(def); + } + + IEnumerable MakeTypeParameters(TypeDefinition def) + { + foreach (string name in def.GenericParameters) + { + yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier); + } + } + + IEnumerable MakeTypeParameterConstraints(TypeDefinition def) + { + foreach (string name in def.GenericParameters) + { + yield return TypeParameterConstraintClause( + _names.GetGenericTypeParameter(name).IdentifierName) + .AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint)); + } + } + + IEnumerable TransformStruct(TypeDefinition def) + { + var topDecl = ClassDeclaration(_names.MakeTypeName(def).Identifier) + .AddModifiers(Public) + .AddBaseListTypes(SimpleBaseType(Type())); + + if (def.GenericParameters.Count > 0) + { + topDecl = topDecl + .AddTypeParameterListParameters(MakeTypeParameters(def).ToArray()) + .AddConstraintClauses(MakeTypeParameterConstraints(def).ToArray()); + } + + if (def.UnionInfo != null) + { + topDecl = topDecl.AddMembers(_commonGen.MakeUnionSelectorEnum(def)); + } + + topDecl = topDecl.AddMembers(_domClassGen.MakeDomainClassMembers(def)); + topDecl = topDecl.AddMembers(_readerGen.MakeReaderStruct(def)); + topDecl = topDecl.AddMembers(_writerGen.MakeWriterStruct(def)); + + foreach (var nestedGroup in def.NestedGroups) + { + topDecl = topDecl.AddMembers(Transform(nestedGroup).ToArray()); + } + + foreach (var nestedDef in def.NestedTypes) + { + topDecl = topDecl.AddMembers(Transform(nestedDef).ToArray()); + } + + yield return topDecl; + } + + IEnumerable TransformInterface(TypeDefinition def) + { + yield return _interfaceGen.MakeInterface(def); + yield return _interfaceGen.MakeProxy(def); + yield return _interfaceGen.MakeSkeleton(def); + + if (_interfaceGen.RequiresPipeliningSupport(def)) + { + yield return _interfaceGen.MakePipeliningSupport(def); + } + + if (def.NestedTypes.Any()) + { + var ns = ClassDeclaration( + _names.MakeTypeName(def, NameUsage.Namespace).ToString()) + .AddModifiers(Public, Static); + + if (def.GenericParameters.Count > 0) + { + ns = ns + .AddTypeParameterListParameters(MakeTypeParameters(def).ToArray()) + .AddConstraintClauses(MakeTypeParameterConstraints(def).ToArray()); + } + + foreach (var nestedDef in def.NestedTypes) + { + ns = ns.AddMembers(Transform(nestedDef).ToArray()); + } + + yield return ns; + } + } + + IEnumerable Transform(TypeDefinition def) + { + switch (def.Tag) + { + case TypeTag.Enum: + return TransformEnum(def); + + case TypeTag.Group: + case TypeTag.Struct: + return TransformStruct(def); + + case TypeTag.Interface: + return TransformInterface(def); + + default: + throw new NotSupportedException($"Cannot declare type of kind {def.Tag} here"); + } + } + + internal string Transform(GenFile file) + { + NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace; + + var ns = NamespaceDeclaration(topNamespace); + + foreach (var def in file.NestedTypes) + { + ns = ns.AddMembers(Transform(def).ToArray()); + } + + var cu = CompilationUnit().AddUsings( + UsingDirective(ParseName("Capnp")), + UsingDirective(ParseName("Capnp.Rpc")), + UsingDirective(ParseName("System")), + UsingDirective(ParseName("System.Collections.Generic")), + UsingDirective(ParseName("System.Threading")), + UsingDirective(ParseName("System.Threading.Tasks"))); + + cu = cu.AddMembers(ns); + + return cu.NormalizeWhitespace().ToFullString(); + } + + public IReadOnlyList Generate() + { + var result = new List(); + + foreach (var file in _model.FilesToGenerate) + { + try + { + result.Add(new FileGenerationResult(file.Name, Transform(file))); + } + catch (System.Exception exception) + { + result.Add(new FileGenerationResult(file.Name, exception)); + } + } + + return result; + } + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs new file mode 100644 index 0000000..d89b873 --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.Linq; +using CapnpC.CSharp.Generator.Model; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class CommonSnippetGen + { + readonly GenNames _names; + + public CommonSnippetGen(GenNames names) + { + _names = names; + } + + public EnumDeclarationSyntax MakeUnionSelectorEnum(TypeDefinition def) + { + var whichEnum = EnumDeclaration(_names.UnionDiscriminatorEnum.ToString()) + .AddModifiers(Public) + .AddBaseListTypes(SimpleBaseType(Type())); + + var discFields = def.Fields.Where(f => f.DiscValue.HasValue); + + foreach (var discField in discFields) + { + whichEnum = whichEnum.AddMembers( + EnumMemberDeclaration(_names.GetCodeIdentifier(discField).Identifier) + .WithEqualsValue( + EqualsValueClause(LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(discField.DiscValue.Value))))); + } + + var ndecl = EnumMemberDeclaration(_names.UnionDiscriminatorUndefined.ToString()).WithEqualsValue( + EqualsValueClause( + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(Schema.Field.Reader.NoDiscriminant)))); + + whichEnum = whichEnum.AddMembers(ndecl); + + return whichEnum; + } + + public EnumDeclarationSyntax MakeEnum(TypeDefinition def) + { + var decl = EnumDeclaration(def.Name) + .AddModifiers(Public) + .AddBaseListTypes(SimpleBaseType(Type())); + + foreach (var enumerant in def.Enumerants.OrderBy(e => e.CodeOrder)) + { + var mdecl = EnumMemberDeclaration(enumerant.Literal); + + if (enumerant.Ordinal.HasValue) + { + mdecl = mdecl.WithEqualsValue( + EqualsValueClause( + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(enumerant.Ordinal.Value)))); + } + + decl = decl.AddMembers(mdecl); + } + + return decl; + } + + public static IEnumerable MakeCommaSeparatedList(IEnumerable expressions) + { + bool first = true; + + foreach (var expr in expressions) + { + if (first) + first = false; + else + yield return Token(SyntaxKind.CommaToken); + + yield return expr; + } + } + + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs new file mode 100644 index 0000000..fb41086 --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs @@ -0,0 +1,970 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CapnpC.CSharp.Generator.Model; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class DomainClassSnippetGen + { + readonly GenNames _names; + + public DomainClassSnippetGen(GenNames names) + { + _names = names; + } + + MemberDeclarationSyntax MakeUnionField(Field field) + { + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClassNullable); + + switch (field.Type.Tag) + { + case TypeTag.Void: + return null; + + default: + return PropertyDeclaration(type, + _names.GetCodeIdentifier(field).Identifier) + .AddModifiers(Public).AddAccessorListAccessors( + AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithExpressionBody( + ArrowExpressionClause( + ConditionalExpression( + BinaryExpression( + SyntaxKind.EqualsExpression, + _names.UnionDiscriminatorField.IdentifierName, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName)), + CastExpression(type, + _names.UnionContentField.IdentifierName), + LiteralExpression( + SyntaxKind.NullLiteralExpression)))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken)), + AccessorDeclaration( + SyntaxKind.SetAccessorDeclaration) + .WithBody( + Block( + ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.UnionDiscriminatorField.IdentifierName, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName))), + ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.UnionContentField.IdentifierName, + IdentifierName("value")))))); + } + } + + MemberDeclarationSyntax MakeStructField(Field field) + { + if (field.Type.Tag == TypeTag.Void) + { + return null; + } + + var prop = PropertyDeclaration(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass), + _names.GetCodeIdentifier(field).Identifier) + .AddModifiers(Public).AddAccessorListAccessors( + AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); + + if (field.DefaultValueIsExplicit && field.Type.IsValueType) + { + prop = prop.WithInitializer( + EqualsValueClause(MakeDefaultValue(field))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); + } + + return prop; + } + + MemberDeclarationSyntax MakeUnionDiscriminatorField() + { + return FieldDeclaration( + VariableDeclaration(_names.UnionDiscriminatorEnum.IdentifierName) + .AddVariables( + VariableDeclarator(_names.UnionDiscriminatorField.Identifier) + .WithInitializer( + EqualsValueClause( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + _names.UnionDiscriminatorUndefined.IdentifierName))))) + .AddModifiers(Private); + } + + MemberDeclarationSyntax MakeUnionContentField() + { + return FieldDeclaration( + VariableDeclaration(SyntaxHelpers.Type()) + .WithVariables( + SingletonSeparatedList( + VariableDeclarator(_names.UnionContentField.Identifier)))) + .AddModifiers(Private); + } + + IEnumerable MakeInitializerAssignments(Value structValue, TypeDefinition scope) + { + foreach (var fieldValue in structValue.Fields) + { + var valueExpr = MakeValue(fieldValue.Item2, scope); + if (valueExpr == null) + continue; + + yield return AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.GetCodeIdentifier(fieldValue.Item1).IdentifierName, + valueExpr); + } + } + + ExpressionSyntax MakeValue(Value value, TypeDefinition scope) + { + switch (value.Type.Tag) + { + case TypeTag.AnyEnum: + return LiteralExpression( + SyntaxKind.NumericLiteralExpression, Literal((ushort)value.ScalarValue)); + + case TypeTag.Bool: + + if ((bool)value.ScalarValue) + return LiteralExpression(SyntaxKind.TrueLiteralExpression); + else + return LiteralExpression(SyntaxKind.FalseLiteralExpression); + + case TypeTag.Data: + return ArrayCreationExpression(ArrayType( + PredefinedType(Token(SyntaxKind.ByteKeyword))) + .WithRankSpecifiers( + SingletonList( + ArrayRankSpecifier( + SingletonSeparatedList( + OmittedArraySizeExpression()))))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ArrayInitializerExpression) + .AddExpressions(value.Items.Select(v => MakeValue(v, scope)).ToArray())); + + case TypeTag.Enum: + return MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + _names.MakeTypeSyntax(value.Type, scope, TypeUsage.NotRelevant), + IdentifierName(value.GetEnumerant().Literal)); + + case TypeTag.F32: + switch ((float)value.ScalarValue) + { + case float.NaN: + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("float"), + IdentifierName(nameof(float.NaN))); + + case float.NegativeInfinity: + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("float"), + IdentifierName(nameof(float.NegativeInfinity))); + + case float.PositiveInfinity: + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("float"), + IdentifierName(nameof(float.PositiveInfinity))); + + default: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((float)value.ScalarValue)); + } + + case TypeTag.F64: + switch ((double)value.ScalarValue) + { + case double.NaN: + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("double"), + IdentifierName(nameof(double.NaN))); + + case double.NegativeInfinity: + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("double"), + IdentifierName(nameof(double.NegativeInfinity))); + + case double.PositiveInfinity: + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("double"), + IdentifierName(nameof(double.PositiveInfinity))); + + default: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((double)value.ScalarValue)); + } + + case TypeTag.S8: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((sbyte)value.ScalarValue)); + + case TypeTag.S16: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((short)value.ScalarValue)); + + case TypeTag.S32: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((int)value.ScalarValue)); + + case TypeTag.S64: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((long)value.ScalarValue)); + + case TypeTag.U8: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((byte)value.ScalarValue)); + + case TypeTag.U16: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((ushort)value.ScalarValue)); + + case TypeTag.U32: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((uint)value.ScalarValue)); + + case TypeTag.U64: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal((ulong)value.ScalarValue)); + + case TypeTag.Text: + value.Decode(); + return value.ScalarValue == null ? + LiteralExpression(SyntaxKind.NullLiteralExpression) : + LiteralExpression(SyntaxKind.StringLiteralExpression, + Literal((string)value.ScalarValue)); + + case TypeTag.Group: + case TypeTag.Struct: + value.Decode(); + + return ObjectCreationExpression( + _names.MakeTypeSyntax(value.Type, scope, TypeUsage.DomainClass)) + .WithArgumentList(ArgumentList()) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression) + .AddExpressions(MakeInitializerAssignments(value, scope).ToArray())); + + case TypeTag.ListPointer: + // TBD + return LiteralExpression(SyntaxKind.NullLiteralExpression); + + case TypeTag.List when value.Type.ElementType.Tag == TypeTag.Void: + value.Decode(); + + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal((int)value.VoidListCount)); + + case TypeTag.List: + value.Decode(); + + return ArrayCreationExpression(ArrayType( + _names.MakeTypeSyntax(value.Type.ElementType, scope, TypeUsage.DomainClass)) + .WithRankSpecifiers( + SingletonList( + ArrayRankSpecifier( + SingletonSeparatedList( + OmittedArraySizeExpression()))))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ArrayInitializerExpression) + .AddExpressions(value.Items.Select(v => MakeValue(v, scope)).ToArray())); + + case TypeTag.AnyPointer: + case TypeTag.CapabilityPointer: + // TBD + return null; + + case TypeTag.Interface: + return null; + + default: + throw new NotImplementedException(); + } + } + + ExpressionSyntax MakeDefaultValue(Field field) + { + if (field.DefaultValueIsExplicit) + { + return MakeValue(field.DefaultValue, field.DeclaringType); + } + else + { + switch (field.Type.Tag) + { + case TypeTag.AnyEnum: + case TypeTag.S16: + case TypeTag.S32: + case TypeTag.S64: + case TypeTag.S8: + case TypeTag.U16: + case TypeTag.U32: + case TypeTag.U64: + case TypeTag.U8: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0)); + + case TypeTag.AnyPointer: + case TypeTag.CapabilityPointer: + case TypeTag.Data: + case TypeTag.Group: + case TypeTag.Interface: + case TypeTag.List: + case TypeTag.ListPointer: + case TypeTag.Struct: + case TypeTag.StructPointer: + case TypeTag.Text: + return LiteralExpression(SyntaxKind.NullLiteralExpression); + + case TypeTag.Bool: + return LiteralExpression(SyntaxKind.FalseLiteralExpression); + + case TypeTag.Enum: + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant), + _names.UnionDiscriminatorUndefined.IdentifierName); + + case TypeTag.F32: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0.0f)); + + case TypeTag.F64: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0.0)); + + default: + throw new NotImplementedException(); + } + } + } + + IEnumerable MakeUnionDiscriminatorSetter(TypeDefinition def) + { + var unionFields = def.Fields.Where(f => f.DiscValue.HasValue); + + foreach (var unionField in unionFields) + { + var section = SwitchSection() + .WithLabels( + SingletonList( + CaseSwitchLabel(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + _names.GetCodeIdentifier(unionField).IdentifierName)))); + + if (unionField.Type.Tag != TypeTag.Void) + { + section = section.AddStatements( + ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.UnionContentField.IdentifierName, + MakeDefaultValue(unionField)))); + } + + section = section.AddStatements(BreakStatement()); + + yield return section; + } + } + + MemberDeclarationSyntax MakeUnionDiscriminatorProperty(TypeDefinition def) + { + return PropertyDeclaration(_names.UnionDiscriminatorEnum.IdentifierName, + _names.UnionDiscriminatorProp.Identifier) + .AddModifiers(Public).AddAccessorListAccessors( + AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithExpressionBody( + ArrowExpressionClause(_names.UnionDiscriminatorField.IdentifierName)) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), + AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithBody( + Block( + IfStatement( + BinaryExpression( + SyntaxKind.EqualsExpression, + IdentifierName("value"), + _names.UnionDiscriminatorField.IdentifierName), + ReturnStatement()), + ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.UnionDiscriminatorField.IdentifierName, + IdentifierName("value"))), + SwitchStatement(IdentifierName("value")) + .WithOpenParenToken( + Token(SyntaxKind.OpenParenToken)) + .WithCloseParenToken( + Token(SyntaxKind.CloseParenToken)) + .AddSections(MakeUnionDiscriminatorSetter(def).ToArray())))); + } + + MemberDeclarationSyntax MakeField(Field field) + { + if (field.DiscValue.HasValue) + return MakeUnionField(field); + else + return MakeStructField(field); + } + + ExpressionSyntax MakeListSerializeParticle(Model.Type type, ExpressionSyntax writer, ExpressionSyntax domain) + { + string s = $"_s{type.GetRank().Item1}"; + string v = $"_v{type.GetRank().Item1}"; + + switch (type.ElementType?.Tag) + { + case TypeTag.List: + case TypeTag.ListPointer: + case TypeTag.Struct: + case TypeTag.Group: + case TypeTag.StructPointer: + case TypeTag.Data: + + return InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + writer, + IdentifierName(nameof(Capnp.ListOfPrimitivesSerializer.Init)))) + .AddArgumentListArguments( + Argument(domain), + Argument( + ParenthesizedLambdaExpression( + MakeComplexSerializeParticle( + type.ElementType, + IdentifierName(s), + IdentifierName(v))) + .AddParameterListParameters( + Parameter(Identifier(s)), + Parameter(Identifier(v))))); + + default: + return InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + writer, + IdentifierName(nameof(Capnp.ListOfPrimitivesSerializer.Init)))) + .AddArgumentListArguments(Argument(domain)); + } + } + + ExpressionSyntax MakeComplexSerializeParticle(Model.Type type, ExpressionSyntax writer, ExpressionSyntax domain) + { + switch (type.Tag) + { + case TypeTag.Data: + case TypeTag.List: + return MakeListSerializeParticle(type, writer, domain); + + case TypeTag.Struct: + case TypeTag.Group: + return ConditionalAccessExpression(domain, + InvocationExpression(MemberBindingExpression(_names.SerializeMethod.IdentifierName)) + .AddArgumentListArguments(Argument(writer))); + + default: + throw new NotImplementedException(); + } + } + + StatementSyntax MakeSerializeMethodFieldAssignment(Field field) + { + var writerProp = MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.WriterParameter.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName); + + switch (field.Type.Tag) + { + case TypeTag.Bool: + case TypeTag.Enum: + case TypeTag.F32: + case TypeTag.F64: + case TypeTag.S16: + case TypeTag.S32: + case TypeTag.S64: + case TypeTag.S8: + case TypeTag.U16: + case TypeTag.U32: + case TypeTag.U64: + case TypeTag.U8: + case TypeTag.AnyEnum: + case TypeTag.List when field.Type.Tag == TypeTag.Void: + if (field.DiscValue.HasValue) + { + return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + writerProp, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.GetCodeIdentifier(field).IdentifierName, + IdentifierName(nameof(Nullable.Value))))); + } + else + { + return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + writerProp, + _names.GetCodeIdentifier(field).IdentifierName)); + } + + case TypeTag.AnyPointer: + case TypeTag.ListPointer: + case TypeTag.StructPointer: + return ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.WriterParameter.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName), + IdentifierName(nameof(Capnp.DynamicSerializerState.SetObject)))) + .AddArgumentListArguments( + Argument(_names.GetCodeIdentifier(field).IdentifierName))); + + case TypeTag.CapabilityPointer: + case TypeTag.Interface: + return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + writerProp, + _names.GetCodeIdentifier(field).IdentifierName)); + + case TypeTag.Text: + return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + writerProp, + _names.GetCodeIdentifier(field).IdentifierName)); + + case TypeTag.Data: + case TypeTag.List: + case TypeTag.Struct: + case TypeTag.Group: + return ExpressionStatement( + MakeComplexSerializeParticle( + field.Type, + writerProp, + _names.GetCodeIdentifier(field).IdentifierName)); + + case TypeTag.Void: + return null; + + default: + throw new NotImplementedException(); + } + } + + StatementSyntax MakeApplyDefaultsMethodFieldAssignment(Field field) + { + var lhs = _names.GetCodeIdentifier(field).IdentifierName; + var rhs = MakeDefaultValue(field); + + if (rhs == null) + { + return null; + } + + return ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + lhs, + BinaryExpression(SyntaxKind.CoalesceExpression, + lhs, rhs))); + } + + ExpressionSyntax MakeInnerStructListConversion(ExpressionSyntax context, TypeSyntax elementType) + { + return InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) + .AddArgumentListArguments(Argument( + SimpleLambdaExpression(Parameter(Identifier("_")), + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.CapnpSerializable)), + GenericName(nameof(Capnp.CapnpSerializable.Create)) + .AddTypeArgumentListArguments(elementType))) + .AddArgumentListArguments(Argument(IdentifierName("_")))))); + } + + ExpressionSyntax MakeStructListConversion(ExpressionSyntax context, TypeSyntax elementType, int rank) + { + if (rank == 1) + { + return MakeInnerStructListConversion(context, elementType); + } + + string lambdaVarName = $"_{rank}"; + + return InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) + .AddArgumentListArguments(Argument( + SimpleLambdaExpression( + Parameter(Identifier(lambdaVarName)), + MakeStructListConversion(IdentifierName(lambdaVarName), elementType, rank - 1)))); + } + + ExpressionSyntax MakeAnyListConversion(ExpressionSyntax context) + { + return InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ReadOnlyListExtensions.ToReadOnlyList)))) + .AddArgumentListArguments(Argument( + SimpleLambdaExpression( + Parameter(Identifier("_")), + CastExpression(Type(), IdentifierName("_"))))); + } + + ExpressionSyntax MakeDeserializeMethodRightHandSide(Field field) + { + switch (field.Type.Tag) + { + case TypeTag.Struct: + case TypeTag.Group: + case TypeTag.StructPointer: + case TypeTag.AnyPointer: + return InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.CapnpSerializable)), + GenericName(nameof(Capnp.CapnpSerializable.Create)) + .AddTypeArgumentListArguments( + _names.MakeTypeSyntax( + field.Type, + field.DeclaringType, + TypeUsage.DomainClass)))) + .AddArgumentListArguments(Argument(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderParameter.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName))); + + case TypeTag.Void: + return null; + + case TypeTag.List: + (var rank, var elementType) = field.Type.GetRank(); + if (elementType.Tag != TypeTag.Struct) + break; + + return MakeStructListConversion( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderParameter.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName), + _names.MakeTypeSyntax(elementType, field.DeclaringType, TypeUsage.DomainClass), + rank); + + case TypeTag.ListPointer: + return MakeAnyListConversion( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderParameter.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName)); + } + + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderParameter.IdentifierName, + _names.GetCodeIdentifier(field).IdentifierName); + } + + IEnumerable MakeSerializeMethodSwitchSections(TypeDefinition def) + { + var unionFields = def.Fields.Where(f => f.DiscValue.HasValue); + + foreach (var unionField in unionFields) + { + var section = SwitchSection() + .AddLabels( + CaseSwitchLabel(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + _names.GetCodeIdentifier(unionField).IdentifierName))); + + if (unionField.Type.Tag != TypeTag.Void) + { + ExpressionSyntax right = _names.GetCodeIdentifier(unionField).IdentifierName; + + var syntax = _names.MakeTypeSyntax(unionField.Type, unionField.DeclaringType, TypeUsage.DomainClassNullable); + + if (syntax is NullableTypeSyntax) + { + right = MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + right, + IdentifierName(nameof(Nullable.Value))); + } + + section = section.AddStatements(MakeSerializeMethodFieldAssignment(unionField)); + } + + section = section.AddStatements(BreakStatement()); + + yield return section; + } + } + + IEnumerable MakeDeserializeMethodSwitchSections(TypeDefinition def) + { + var unionFields = def.Fields.Where(f => f.DiscValue.HasValue); + + foreach (var unionField in unionFields) + { + var section = SwitchSection() + .AddLabels( + CaseSwitchLabel(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + _names.GetCodeIdentifier(unionField).IdentifierName))); + + switch (unionField.Type.Tag) + { + case TypeTag.Void: + section = section.AddStatements( + ExpressionStatement(AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.UnionDiscriminatorProp.IdentifierName, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderParameter.IdentifierName, + _names.UnionDiscriminatorProp.IdentifierName)))); + break; + + default: + section = section.AddStatements( + ExpressionStatement(AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.GetCodeIdentifier(unionField).IdentifierName, + MakeDeserializeMethodRightHandSide(unionField)))); + break; + + } + + section = section.AddStatements(BreakStatement()); + + yield return section; + } + } + + IEnumerable MakeSerializeStatements(TypeDefinition def) + { + if (def.UnionInfo != null) + { + yield return ExpressionStatement(AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.WriterParameter.IdentifierName, + _names.UnionDiscriminatorProp.IdentifierName), + _names.UnionDiscriminatorProp.IdentifierName)); + + yield return SwitchStatement(_names.UnionDiscriminatorProp.IdentifierName) + .WithOpenParenToken(Token(SyntaxKind.OpenParenToken)) + .WithCloseParenToken(Token(SyntaxKind.CloseParenToken)) + .AddSections(MakeSerializeMethodSwitchSections(def).ToArray()); + } + + var nondiscFields = def.Fields.Where(f => !f.DiscValue.HasValue && f.Type.Tag != TypeTag.Void); + + foreach (var field in nondiscFields) + { + var asmt = MakeSerializeMethodFieldAssignment(field); + + if (asmt != null) + { + yield return asmt; + } + } + } + + IEnumerable MakeApplyDefaultsStatements(TypeDefinition def) + { + var relevantFields = def.Fields.Where( + f => !f.DiscValue.HasValue && + f.Type.Tag != TypeTag.Void && + f.DefaultValueIsExplicit && + !f.Type.IsValueType); + + foreach (var field in relevantFields) + { + var asmt = MakeApplyDefaultsMethodFieldAssignment(field); + + if (asmt != null) + { + yield return asmt; + } + } + } + + MemberDeclarationSyntax MakeSerializeMethod(TypeDefinition def) + { + return MethodDeclaration(PredefinedType( + Token(SyntaxKind.VoidKeyword)), + _names.SerializeMethod.Identifier) + .AddModifiers(Public) + .AddParameterListParameters( + Parameter(_names.WriterParameter.Identifier) + .WithType(_names.WriterStruct.IdentifierName)) + .AddBodyStatements(MakeSerializeStatements(def).ToArray()); + } + + MemberDeclarationSyntax MakeSerializeInterfaceMethod() + { + return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), + Identifier(nameof(Capnp.ICapnpSerializable.Serialize))) + .WithExplicitInterfaceSpecifier( + ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) + .AddParameterListParameters( + Parameter(_names.AnonymousParameter.Identifier) + .WithType(Type())) + .AddBodyStatements( + ExpressionStatement( + InvocationExpression(_names.SerializeMethod.IdentifierName) + .AddArgumentListArguments( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.AnonymousParameter.IdentifierName, + GenericName(Identifier(nameof(Capnp.SerializerState.Rewrap))) + .AddTypeArgumentListArguments(_names.WriterStruct.IdentifierName))))))); + } + + MemberDeclarationSyntax MakeApplyDefaultsMethod(TypeDefinition def) + { + return MethodDeclaration(PredefinedType( + Token(SyntaxKind.VoidKeyword)), + _names.ApplyDefaultsMethod.Identifier) + .AddModifiers(Public) + .AddBodyStatements(MakeApplyDefaultsStatements(def).ToArray()); + } + + IEnumerable MakeDeserializeStatements(TypeDefinition def) + { + var relevantFields = def.Fields.Where( + f => !f.DiscValue.HasValue && + f.Type.Tag != TypeTag.Void); + + foreach (var field in relevantFields) + { + var rhs = MakeDeserializeMethodRightHandSide(field); + + if (rhs != null) + { + yield return ExpressionStatement(AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.GetCodeIdentifier(field).IdentifierName, + rhs)); + } + } + } + + MemberDeclarationSyntax MakeDeserializeMethod(TypeDefinition def) + { + var stmts = new List(); + + stmts.Add(LocalDeclarationStatement( + VariableDeclaration(IdentifierName("var")) + .AddVariables( + VariableDeclarator(_names.ReaderParameter.Identifier) + .WithInitializer( + EqualsValueClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderStruct.IdentifierName, + _names.ReaderCreateMethod.IdentifierName)) + .AddArgumentListArguments( + Argument(_names.AnonymousParameter.IdentifierName))))))); + + + if (def.UnionInfo != null) + { + stmts.Add(SwitchStatement( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderParameter.IdentifierName, + _names.UnionDiscriminatorProp.IdentifierName)) + .WithOpenParenToken(Token(SyntaxKind.OpenParenToken)) + .WithCloseParenToken(Token(SyntaxKind.CloseParenToken)) + .AddSections(MakeDeserializeMethodSwitchSections(def).ToArray())); + } + + stmts.AddRange(MakeDeserializeStatements(def)); + stmts.Add(ExpressionStatement(InvocationExpression( + _names.ApplyDefaultsMethod.IdentifierName))); + + return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), + Identifier(nameof(Capnp.ICapnpSerializable.Deserialize))) + .WithExplicitInterfaceSpecifier( + ExplicitInterfaceSpecifier(IdentifierName(nameof(Capnp.ICapnpSerializable)))) + .AddParameterListParameters( + Parameter(_names.AnonymousParameter.Identifier) + .WithType(Type())) + .AddBodyStatements(stmts.ToArray()); + } + + IEnumerable EnumerateDomainClassMembers(TypeDefinition def) + { + yield return MakeDeserializeMethod(def); + + if (def.UnionInfo != null) + { + yield return MakeUnionDiscriminatorField(); + yield return MakeUnionContentField(); + yield return MakeUnionDiscriminatorProperty(def); + } + + yield return MakeSerializeMethod(def); + yield return MakeSerializeInterfaceMethod(); + yield return MakeApplyDefaultsMethod(def); + + foreach (var field in def.Fields) + { + var decl = MakeField(field); + + if (decl != null) + yield return decl; + } + } + + public MemberDeclarationSyntax[] MakeDomainClassMembers(TypeDefinition def) + { + return EnumerateDomainClassMembers(def).ToArray(); + } + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/GenNames.cs b/CapnpC.CSharp.Generator/CodeGen/GenNames.cs new file mode 100644 index 0000000..b9282ab --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/GenNames.cs @@ -0,0 +1,600 @@ +using CapnpC.CSharp.Generator.Model; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Linq; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + enum NameUsage + { + Default, + Interface, + Proxy, + Skeleton, + Namespace + } + + enum TypeUsage + { + NotRelevant, + DomainClass, + DomainClassNullable, + Reader, + Writer + } + + class GenNames + { + readonly Dictionary _fieldNameMap = new Dictionary(); + + public NameSyntax TopNamespace { get; set; } + public Name ReaderStruct { get; } + public Name ReaderParameter { get; } + public Name WriterParameter { get; } + public Name WriterStruct { get; } + public Name ReaderCreateMethod { get; } + public Name ReaderContextField { get; } + public Name ContextParameter { get; } + public Name GroupReaderContextArg { get; } + public Name GroupWriterContextArg { get; } + public Name UnionDiscriminatorEnum { get; } + public Name UnionDiscriminatorProp { get; } + public Name UnionDiscriminatorUndefined { get; } + public Name UnionDiscriminatorField { get; } + public Name UnionContentField { get; } + public Name AnonymousParameter { get; } + public Name CancellationTokenParameter { get; } + public Name ParamsLocal { get; } + public Name DeserializerLocal { get; } + public Name SerializerLocal { get; } + public Name ResultLocal { get; } + public Name SerializeMethod { get; } + public Name ApplyDefaultsMethod { get; } + public Name InstLocalName { get; } + public string ParamsStructFormat { get; } + public string ResultStructFormat { get; } + public string PropertyNamedLikeTypeRenameFormat { get; } + public string GenericTypeParameterFormat { get; } + public Name PipeliningExtensionsClassName { get; } + public string MemberAccessPathNameFormat { get; } + public Name TaskParameter { get; } + public Name EagerMethod { get; } + + public GenNames(GeneratorOptions options) + { + TopNamespace = new Name(options.TopNamespaceName).IdentifierName; + ReaderStruct = new Name(options.ReaderStructName); + WriterStruct = new Name(options.WriterStructName); + ReaderParameter = new Name(options.ReaderParameterName); + WriterParameter = new Name(options.WriterParameterName); + ReaderCreateMethod = new Name(options.ReaderCreateMethodName); + ReaderContextField = new Name(options.ReaderContextFieldName); + ContextParameter = new Name(options.ContextParameterName); + GroupReaderContextArg = new Name(options.GroupReaderContextArgName); + GroupWriterContextArg = new Name(options.GroupWriterContextArgName); + UnionDiscriminatorEnum = new Name(options.UnionDisciminatorEnumName); + UnionDiscriminatorProp = new Name(options.UnionDiscriminatorPropName); + UnionDiscriminatorUndefined = new Name(options.UnionDisciminatorUndefinedName); + UnionDiscriminatorField = new Name(options.UnionDiscriminatorFieldName); + UnionContentField = new Name(options.UnionContentFieldName); + SerializeMethod = new Name(options.SerializeMethodName); + ApplyDefaultsMethod = new Name(options.ApplyDefaultsMethodName); + AnonymousParameter = new Name(options.AnonymousParameterName); + CancellationTokenParameter = new Name(options.CancellationTokenParameterName); + ParamsLocal = new Name(options.ParamsLocalName); + DeserializerLocal = new Name(options.DeserializerLocalName); + SerializerLocal = new Name(options.SerializerLocalName); + ResultLocal = new Name(options.ResultLocalName); + InstLocalName = new Name(options.InstLocalName); + ParamsStructFormat = options.ParamsStructFormat; + ResultStructFormat = options.ResultStructFormat; + PropertyNamedLikeTypeRenameFormat = options.PropertyNamedLikeTypeRenameFormat; + GenericTypeParameterFormat = options.GenericTypeParameterFormat; + PipeliningExtensionsClassName = new Name(options.PipeliningExtensionsClassName); + MemberAccessPathNameFormat = options.MemberAccessPathNameFormat; + TaskParameter = new Name(options.TaskParameterName); + EagerMethod = new Name(options.EagerMethodName); + } + + public Name MakeTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default) + { + if (def.Tag == TypeTag.Group) + { + return new Name(SyntaxHelpers.MakeAllLower(def.Name)); + } + else + { + string name; + + switch (usage) + { + case NameUsage.Default: + if (def.Tag == TypeTag.Interface) + goto case NameUsage.Interface; + + switch (def.SpecialName) + { + case SpecialName.NothingSpecial: + name = def.Name; + break; + + case SpecialName.MethodParamsStruct: + name = MakeParamsStructName(def.UsingMethod); + break; + + case SpecialName.MethodResultStruct: + name = MakeResultStructName(def.UsingMethod); + break; + + default: + throw new NotImplementedException(); + } + break; + + case NameUsage.Namespace: + name = def.Name; + break; + + case NameUsage.Interface: + name = "I" + def.Name; + break; + + case NameUsage.Proxy: + name = def.Name + "Proxy"; + break; + + case NameUsage.Skeleton: + name = def.Name + "Skeleton"; + break; + + default: + throw new NotImplementedException(); + } + + return new Name(name); + } + } + + public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, NameUsage usage = NameUsage.Default) + { + var name = MakeTypeName(def, usage); + + if (def.GenericParameters.Count > 0) + { + return GenericName(name.Identifier) + .AddTypeArgumentListArguments(def + .GenericParameters + .Select(p => GetGenericTypeParameter(p).IdentifierName).ToArray()); + } + else + { + return name.IdentifierName; + } + } + + TypeSyntax ResolveGenericParameter(GenericParameter p, Model.Type boundType, TypeDefinition def) + { + var type = boundType.ResolveGenericParameter(p); + return MakeTypeSyntax(type, def, TypeUsage.DomainClass); + } + + public SimpleNameSyntax MakeGenericTypeName(TypeDefinition def, Model.Type boundType, NameUsage usage = NameUsage.Default) + { + var name = MakeTypeName(def, usage); + + if (def.GenericParameters.Count > 0) + { + return GenericName(name.Identifier) + .AddTypeArgumentListArguments(def + .GetLocalTypeParameters() + .Select(p => ResolveGenericParameter(p, boundType, def)).ToArray()); + } + else + { + return name.IdentifierName; + } + } + + public SimpleNameSyntax MakeGenericTypeNameForAttribute(TypeDefinition def, NameUsage usage) + { + var name = MakeTypeName(def, usage); + + if (def.GenericParameters.Count > 0) + { + return GenericName(name.Identifier).AddTypeArgumentListArguments(); + } + else + { + return name.IdentifierName; + } + } + + public static NameSyntax NamespaceName(string[] @namespace) + { + NameSyntax ident = null; + if (@namespace != null) + { + ident = IdentifierName(SyntaxHelpers.MakeCamel(@namespace[0])); + foreach (string name in @namespace.Skip(1)) + { + var temp = IdentifierName(SyntaxHelpers.MakeCamel(name)); + ident = QualifiedName(ident, temp); + } + } + return ident; + } + + 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 + // there are multiple objects with identical name up the hierarchy. We will need a more + // sophisticated algorithm. + + var scopeSet = new HashSet(); + //while (scope != null) + //{ + // scopeSet.Add(scope); + // scope = scope.DeclaringElement as TypeDefinition; + //} + + if (type.Definition != null) + { + var stack = new Stack(); + + var def = type.Definition; + stack.Push(MakeGenericTypeName(def, type, NameUsage.Default)); + + while (def.DeclaringElement is TypeDefinition pdef && !scopeSet.Contains(pdef)) + { + stack.Push(MakeGenericTypeName(pdef, type, NameUsage.Namespace)); + def = pdef; + } + + var qtype = + GetNamespaceFor(type.Definition) + ?? GetNamespaceFor(scope) + ?? TopNamespace; + + foreach (var name in stack) + { + qtype = QualifiedName(qtype, name); + } + + return qtype; + } + else + { + return GetGenericTypeParameter(type.Parameter.Name).IdentifierName; + } + } + + public TypeSyntax MakeListSerializerSyntax(Model.Type elementType, TypeDefinition scope) + { + switch (elementType.Tag) + { + case TypeTag.AnyPointer: + case TypeTag.StructPointer: + case TypeTag.ListPointer: + return SyntaxHelpers.Type>(); + + case TypeTag.CapabilityPointer: + return SyntaxHelpers.Type>(); + + case TypeTag.Data: + return SyntaxHelpers.Type>>(); + + case TypeTag.Enum: + return GenericName("ListOfPrimitivesSerializer") + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + + case TypeTag.Group: + case TypeTag.Struct: + return GenericName("ListOfStructsSerializer") + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + + case TypeTag.Interface: + return GenericName("ListOfCapsSerializer") + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + + case TypeTag.List: + return GenericName("ListOfPointersSerializer") + .AddTypeArgumentListArguments(MakeTypeSyntax(elementType, scope, TypeUsage.Writer)); + + case TypeTag.Text: + return SyntaxHelpers.Type(); + + case TypeTag.Void: + return SyntaxHelpers.Type(); + + case TypeTag.Bool: + return SyntaxHelpers.Type(); + + case TypeTag.F32: + return SyntaxHelpers.Type>(); + + case TypeTag.F64: + return SyntaxHelpers.Type>(); + + case TypeTag.S8: + return SyntaxHelpers.Type>(); + + case TypeTag.U8: + return SyntaxHelpers.Type>(); + + case TypeTag.S16: + return SyntaxHelpers.Type>(); + + case TypeTag.U16: + case TypeTag.AnyEnum: + return SyntaxHelpers.Type>(); + + case TypeTag.S32: + return SyntaxHelpers.Type>(); + + case TypeTag.U32: + return SyntaxHelpers.Type>(); + + case TypeTag.S64: + return SyntaxHelpers.Type>(); + + case TypeTag.U64: + return SyntaxHelpers.Type>(); + + default: + throw new NotImplementedException("Unexpected type tag, don't know how to deal with this"); + } + } + + TypeSyntax MaybeNullableValueType(TypeSyntax typeSyntax, TypeUsage usage) + { + switch (usage) + { + case TypeUsage.DomainClassNullable: + return NullableType(typeSyntax); + + default: + return typeSyntax; + } + } + + public TypeSyntax MakeTypeSyntax(Model.Type type, TypeDefinition scope, TypeUsage usage) + { + switch (type.Tag) + { + case TypeTag.AnyEnum: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.CapabilityPointer: + if (type.Parameter != null) + { + return GetQName(type, scope); + } + else + { + return SyntaxHelpers.Type(); + } + + case TypeTag.AnyPointer: + case TypeTag.StructPointer: + switch (usage) + { + case TypeUsage.Reader: + return SyntaxHelpers.Type(); + + case TypeUsage.Writer: + return SyntaxHelpers.Type(); + + case TypeUsage.DomainClass: + case TypeUsage.DomainClassNullable: + if (type.Parameter != null) + { + return GetQName(type, scope); + } + else + { + return SyntaxHelpers.Type(); + } + + default: + throw new NotImplementedException(); + } + + case TypeTag.Bool: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.Data: + switch (usage) + { + case TypeUsage.Reader: + case TypeUsage.DomainClass: + case TypeUsage.DomainClassNullable: + return SyntaxHelpers.Type>(); + + case TypeUsage.Writer: + return SyntaxHelpers.Type>(); + + default: + throw new NotImplementedException(); + } + + case TypeTag.Enum: + return MaybeNullableValueType(GetQName(type, scope), usage); + + case TypeTag.Interface: + return GetQName(type, scope); + + case TypeTag.Struct: + case TypeTag.Group: + switch (usage) + { + case TypeUsage.Writer: + return QualifiedName(GetQName(type, scope), WriterStruct.IdentifierName); + + case TypeUsage.Reader: + return QualifiedName(GetQName(type, scope), ReaderStruct.IdentifierName); + + case TypeUsage.DomainClass: + case TypeUsage.DomainClassNullable: + return GetQName(type, scope); + + default: + throw new NotImplementedException(); + } + + case TypeTag.F32: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.F64: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.List when type.ElementType.Tag == TypeTag.Void && usage != TypeUsage.Writer: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.List: + switch (usage) + { + case TypeUsage.Writer: + return MakeListSerializerSyntax(type.ElementType, scope); + + case TypeUsage.Reader: + return GenericName(Identifier("IReadOnlyList")) + .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.Reader)); + + case TypeUsage.DomainClass: + case TypeUsage.DomainClassNullable: + return GenericName(Identifier("IReadOnlyList")) + .AddTypeArgumentListArguments(MakeTypeSyntax(type.ElementType, scope, TypeUsage.DomainClass)); + + default: + throw new NotImplementedException(); + } + + case TypeTag.ListPointer: + switch (usage) + { + case TypeUsage.Writer: + return SyntaxHelpers.Type(); + + case TypeUsage.Reader: + return SyntaxHelpers.Type>(); + + case TypeUsage.DomainClass: + case TypeUsage.DomainClassNullable: + return SyntaxHelpers.Type>(); + + default: + throw new NotImplementedException(); + } + + case TypeTag.S16: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.S32: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.S64: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.S8: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.Text: + return SyntaxHelpers.Type(); + + case TypeTag.U16: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.U32: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.U64: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.U8: + return MaybeNullableValueType(SyntaxHelpers.Type(), usage); + + case TypeTag.Void: + return PredefinedType(Token(SyntaxKind.VoidKeyword)); + + default: + throw new NotImplementedException("Unexpected type tag, don't know how to deal with this"); + } + } + + public string MakeParamsStructName(Method method) + { + return string.Format(ParamsStructFormat, method.Name); + } + + public string MakeResultStructName(Method method) + { + return string.Format(ResultStructFormat, method.Name); + } + + public Name GetCodeIdentifier(Method method) + { + return new Name(SyntaxHelpers.MakeCamel(method.Name)); + } + + public Name GetCodeIdentifier(Field field) + { + if (_fieldNameMap.TryGetValue(field, out var name)) + { + return name; + } + + var def = field.DeclaringType; + + if (def == null) + { + // Method parameters are internally represented with the same class "Field". + // They do not have a declaring type. Anyway, they don't suffer from the field-name-equals-nested-type-name problem. + return new Name(SyntaxHelpers.MakeCamel(field.Name)); + } + + 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)); + + while (typeNames.Contains(memberName)) + { + memberName = new Name(string.Format(PropertyNamedLikeTypeRenameFormat, memberName.ToString())); + } + + _fieldNameMap.Add(member, memberName); + } + + return _fieldNameMap[field]; + } + + public Name GetGenericTypeParameter(string name) + { + return new Name(string.Format(GenericTypeParameterFormat, name)); + } + + public Name MakePipeliningSupportExtensionMethodName(IReadOnlyList path) + { + if (path.Count == 1 && path[0].Offset == 0) + return EagerMethod; + else + return new Name(string.Join("_", path.Select(f => GetCodeIdentifier(f).ToString()))); + } + + public Name MakeMemberAccessPathFieldName(Method method, IReadOnlyList path) + { + return new Name(string.Format(MemberAccessPathNameFormat, + method.Name, + MakePipeliningSupportExtensionMethodName(path))); + } + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/GeneratorOptions.cs b/CapnpC.CSharp.Generator/CodeGen/GeneratorOptions.cs new file mode 100644 index 0000000..e534f2b --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/GeneratorOptions.cs @@ -0,0 +1,38 @@ +namespace CapnpC.CSharp.Generator.CodeGen +{ + class GeneratorOptions + { + public string TopNamespaceName { get; set; } = "CapnpGen"; + public string ReaderStructName { get; set; } = "READER"; + public string WriterStructName { get; set; } = "WRITER"; + public string ReaderParameterName { get; set; } = "reader"; + public string WriterParameterName { get; set; } = "writer"; + public string ReaderCreateMethodName { get; set; } = "create"; + public string ReaderContextFieldName { get; set; } = "ctx"; + public string ContextParameterName { get; set; } = "ctx"; + public string GroupReaderContextArgName { get; set; } = "ctx"; + public string GroupWriterContextArgName { get; set; } = "ctx"; + public string UnionDisciminatorEnumName { get; set; } = "WHICH"; + public string UnionDiscriminatorPropName { get; set; } = "which"; + public string UnionDiscriminatorFieldName { get; set; } = "_which"; + public string UnionDisciminatorUndefinedName { get; set; } = "undefined"; + public string UnionContentFieldName { get; set; } = "_content"; + public string SerializeMethodName { get; set; } = "serialize"; + public string ApplyDefaultsMethodName { get; set; } = "applyDefaults"; + public string AnonymousParameterName { get; set; } = "arg_"; + public string CancellationTokenParameterName { get; set; } = "cancellationToken_"; + public string ParamsLocalName { get; set; } = "in_"; + public string DeserializerLocalName { get; set; } = "d_"; + public string SerializerLocalName { get; set; } = "s_"; + public string ResultLocalName { get; set; } = "r_"; + public string ParamsStructFormat { get; set; } = "Params_{0}"; + public string ResultStructFormat { get; set; } = "Result_{0}"; + public string PropertyNamedLikeTypeRenameFormat { get; set; } = "The{0}"; + public string InstLocalName { get; set; } = "inst"; + public string GenericTypeParameterFormat { get; set; } = "T{0}"; + public string PipeliningExtensionsClassName { get; set; } = "PipeliningSupportExtensions"; + public string MemberAccessPathNameFormat { get; set; } = "Path_{0}_{1}"; + public string TaskParameterName { get; set; } = "task"; + public string EagerMethodName { get; set; } = "Eager"; + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs new file mode 100644 index 0000000..11d6d36 --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs @@ -0,0 +1,843 @@ +using CapnpC.CSharp.Generator.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class InterfaceSnippetGen + { + readonly GenNames _names; + + public InterfaceSnippetGen(GenNames names) + { + _names = names; + } + + TypeSyntax TransformReturnType(Method method) + { + switch (method.Results.Count) + { + case 0: + return IdentifierName(nameof(Task)); + + case 1: + return GenericName(nameof(Task)).AddTypeArgumentListArguments( + _names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass)); + + default: + return GenericName(nameof(Task)).AddTypeArgumentListArguments( + TupleType(SeparatedList( + method.Results.Select( + f => TupleElement(_names.MakeTypeSyntax(f.Type, method.DeclaringInterface, TypeUsage.DomainClass)))))); + } + } + + ParameterSyntax[] TransformParameters(Method method) + { + var list = new List(); + + if (method.Params.Count > 0) + { + var arg0 = method.Params[0]; + + if (arg0.Name == null) + { + list.Add(Parameter(_names.AnonymousParameter.Identifier) + .WithType(_names.MakeTypeSyntax(arg0.Type, method.DeclaringInterface, TypeUsage.DomainClass))); + } + else + { + foreach (var arg in method.Params) + { + list.Add(Parameter(Identifier(arg.Name)) + .WithType(_names.MakeTypeSyntax(arg.Type, method.DeclaringInterface, TypeUsage.DomainClass))); + } + } + } + + list.Add(Parameter(_names.CancellationTokenParameter.Identifier) + .WithType(IdentifierName(nameof(CancellationToken))) + .WithDefault(EqualsValueClause(LiteralExpression( + SyntaxKind.DefaultLiteralExpression, + Token(SyntaxKind.DefaultKeyword))))); + + return list.ToArray(); + } + + IEnumerable MakeTypeParameters(TypeDefinition def) + { + foreach (string name in def.GenericParameters) + { + yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier); + } + } + + IEnumerable MakeTypeParameterConstraints(TypeDefinition def) + { + foreach (string name in def.GenericParameters) + { + yield return TypeParameterConstraintClause( + _names.GetGenericTypeParameter(name).IdentifierName) + .AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint)); + } + } + + public MemberDeclarationSyntax MakeInterface(TypeDefinition type) + { + var ifaceDecl = InterfaceDeclaration(_names.MakeTypeName(type, NameUsage.Interface).Identifier) + .AddModifiers(Public) + .AddAttributeLists( + AttributeList() + .AddAttributes( + Attribute(IdentifierName("Proxy")) + .AddArgumentListArguments( + AttributeArgument( + TypeOfExpression(_names.MakeGenericTypeNameForAttribute(type, NameUsage.Proxy)))), + Attribute(IdentifierName("Skeleton")) + .AddArgumentListArguments( + AttributeArgument( + TypeOfExpression(_names.MakeGenericTypeNameForAttribute(type, NameUsage.Skeleton)))))); + + if (type.GenericParameters.Count > 0) + { + ifaceDecl = ifaceDecl.AddTypeParameterListParameters(MakeTypeParameters(type).ToArray()); + } + + if (type.Superclasses.Count == 0) + { + ifaceDecl = ifaceDecl.AddBaseListTypes(SimpleBaseType(IdentifierName(nameof(IDisposable)))); + } + else + { + foreach (var superClass in type.Superclasses) + { + ifaceDecl = ifaceDecl.AddBaseListTypes( + SimpleBaseType(_names.MakeTypeSyntax( + superClass, type, + TypeUsage.NotRelevant))); + } + } + + foreach (var method in type.Methods) + { + var methodDecl = MethodDeclaration( + TransformReturnType(method), + _names.GetCodeIdentifier(method).Identifier) + .AddParameterListParameters(TransformParameters(method)) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); + + if (method.GenericParameters.Count > 0) + { + methodDecl = methodDecl + .AddTypeParameterListParameters(MakeTypeParameters(method).ToArray()) + .AddConstraintClauses(MakeTypeParameterConstraints(method).ToArray()); + + } + + ifaceDecl = ifaceDecl.AddMembers(methodDecl); + } + + return ifaceDecl; + } + + bool IsSubjectToPipelining(Model.Type type, HashSet visited) + { + if (!visited.Add(type)) + return false; + + switch (type.Tag) + { + case TypeTag.AnyPointer: + case TypeTag.CapabilityPointer: + case TypeTag.Interface: + case TypeTag.ListPointer: + case TypeTag.StructPointer: + return true; + + case TypeTag.List: + return IsSubjectToPipelining(type.ElementType, visited); + + case TypeTag.Struct: + return type.Fields.Any(f => IsSubjectToPipelining(f.Type, visited)); + + default: + return false; + } + } + + bool IsSubjectToPipelining(Method method) + { + return method.Results.Any(r => IsSubjectToPipelining(r.Type, new HashSet())); + } + + IEnumerable MakeProxyCallInitializerAssignments(Method method) + { + foreach (var methodParam in method.Params) + { + yield return AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.GetCodeIdentifier(methodParam).IdentifierName, + IdentifierName(methodParam.Name)); + } + } + + IEnumerable MakeProxyReturnResultTupleElements(Method method) + { + foreach (var item in method.ResultStruct.Fields) + { + yield return Argument(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ResultLocal.IdentifierName, + _names.GetCodeIdentifier(item).IdentifierName)); + } + } + + StatementSyntax MakeProxyReturnResult(Method method) + { + if (method.ResultStruct.Definition.SpecialName == SpecialName.MethodResultStruct) + { + if (method.ResultStruct.Fields.Count == 0) + { + return ReturnStatement(); + } + else + { + return ReturnStatement(TupleExpression() + .AddArguments(MakeProxyReturnResultTupleElements(method).ToArray())); + } + + } + else + { + return ReturnStatement(_names.ResultLocal.IdentifierName); + } + } + + StatementSyntax MakeProxyCreateResult(Method method) + { + var resultType = method.ResultStruct; + var domainType = _names.MakeTypeSyntax(resultType, method.DeclaringInterface, TypeUsage.DomainClass); + + var createDomain = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.CapnpSerializable)), + GenericName(nameof(Capnp.CapnpSerializable.Create)) + .AddTypeArgumentListArguments(domainType))) + .AddArgumentListArguments( + Argument(_names.DeserializerLocal.IdentifierName)); + + return LocalDeclarationStatement( + VariableDeclaration( + IdentifierName("var")) + .WithVariables( + SingletonSeparatedList( + VariableDeclarator( + _names.ResultLocal.Identifier) + .WithInitializer( + EqualsValueClause(createDomain))))); + } + + IEnumerable MakeTypeParameters(Method method) + { + foreach (string name in method.GenericParameters) + { + yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier); + } + } + + IEnumerable MakeTypeParameterConstraints(Method method) + { + foreach (string name in method.GenericParameters) + { + yield return TypeParameterConstraintClause( + _names.GetGenericTypeParameter(name).IdentifierName) + .AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint)); + } + } + + public MemberDeclarationSyntax MakeProxy(TypeDefinition type) + { + var classDecl = ClassDeclaration(_names.MakeTypeName(type, NameUsage.Proxy).Identifier) + .AddModifiers(Public) + .AddBaseListTypes( + SimpleBaseType(Type()), + SimpleBaseType(_names.MakeGenericTypeName(type, NameUsage.Interface))); + + if (type.GenericParameters.Count > 0) + { + classDecl = classDecl + .AddTypeParameterListParameters(MakeTypeParameters(type).ToArray()) + .AddConstraintClauses(MakeTypeParameterConstraints(type).ToArray()); + } + + var allMethods = + from c in Types.FromDefinition(type).AllImplementedClasses + from m in c.Definition.Methods + select m; + + foreach (var method in allMethods) + { + var bodyStmts = new List(); + + bodyStmts.Add(LocalDeclarationStatement( + VariableDeclaration( + IdentifierName("var")) + .WithVariables( + SingletonSeparatedList( + VariableDeclarator( + _names.ParamsLocal.Identifier) + .WithInitializer( + EqualsValueClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.SerializerState)), + GenericName( + Identifier(nameof(Capnp.SerializerState.CreateForRpc))) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + _names.MakeTypeSyntax( + method.ParamsStruct, + method.ParamsStruct.Definition, + TypeUsage.Writer)))))))))))); + + if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct) + { + bodyStmts.Add(LocalDeclarationStatement( + VariableDeclaration( + IdentifierName("var")) + .WithVariables( + SingletonSeparatedList( + VariableDeclarator( + _names.AnonymousParameter.Identifier) + .WithInitializer( + EqualsValueClause( + ObjectCreationExpression( + _names.MakeTypeSyntax( + method.ParamsStruct, + method.ParamsStruct.Definition, + TypeUsage.DomainClass)) + .WithArgumentList( + ArgumentList()) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + CommonSnippetGen.MakeCommaSeparatedList( + MakeProxyCallInitializerAssignments(method)).ToArray()))))))))); + } + + bodyStmts.Add(ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.AnonymousParameter.IdentifierName, + _names.SerializeMethod.IdentifierName)) + .AddArgumentListArguments( + Argument(_names.ParamsLocal.IdentifierName)))); + + var call = InvocationExpression(IdentifierName(nameof(Capnp.Rpc.BareProxy.Call))) + .AddArgumentListArguments( + Argument( + LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal(method.DeclaringInterface.Id))), + Argument( + LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(method.Id))), + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ParamsLocal.IdentifierName, + GenericName(nameof(Capnp.SerializerState.Rewrap)) + .AddTypeArgumentListArguments(Type()))) + .AddArgumentListArguments()), + Argument( + LiteralExpression(SyntaxKind.FalseLiteralExpression)), + Argument( + _names.CancellationTokenParameter.IdentifierName)); + + MethodDeclarationSyntax methodDecl; + + if (IsSubjectToPipelining(method)) + { + methodDecl = MethodDeclaration( + TransformReturnType(method), + _names.GetCodeIdentifier(method).Identifier) + .AddParameterListParameters(TransformParameters(method)) + .AddModifiers(Public); + + var pipelineAwareCall = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.Rpc.Impatient)), + IdentifierName(nameof(Capnp.Rpc.Impatient.MakePipelineAware)))) + .AddArgumentListArguments( + Argument(call), + Argument(SimpleLambdaExpression( + Parameter(_names.DeserializerLocal.Identifier), + Block( + MakeProxyCreateResult(method), + MakeProxyReturnResult(method))))); + + bodyStmts.Add(ReturnStatement(pipelineAwareCall)); + } + else + { + methodDecl = MethodDeclaration( + TransformReturnType(method), + _names.GetCodeIdentifier(method).Identifier) + .AddParameterListParameters(TransformParameters(method)) + .AddModifiers(Public, Token(SyntaxKind.AsyncKeyword)); + + var whenReturned = MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + call, + IdentifierName(nameof(Capnp.Rpc.IPromisedAnswer.WhenReturned))); + + var assignAwaited = LocalDeclarationStatement( + VariableDeclaration( + IdentifierName("var")) + .AddVariables( + VariableDeclarator( + _names.DeserializerLocal.Identifier) + .WithInitializer( + EqualsValueClause( + AwaitExpression(whenReturned))))); + + bodyStmts.Add(assignAwaited); + bodyStmts.Add(MakeProxyCreateResult(method)); + bodyStmts.Add(MakeProxyReturnResult(method)); + } + + if (method.GenericParameters.Count > 0) + { + methodDecl = methodDecl + .AddTypeParameterListParameters(MakeTypeParameters(method).ToArray()) + .AddConstraintClauses(MakeTypeParameterConstraints(method).ToArray()); + } + + methodDecl = methodDecl.AddBodyStatements(bodyStmts.ToArray()); + + classDecl = classDecl.AddMembers(methodDecl); + } + + return classDecl; + } + + IEnumerable MakeSkeletonSetMethodTableArguments(TypeDefinition def) + { + foreach (var method in def.Methods) + { + if (method.GenericParameters.Count > 0) + { + yield return Argument( + GenericName(_names.GetCodeIdentifier(method).ToString()) + .AddTypeArgumentListArguments( + Enumerable.Repeat( + Type(), + method.GenericParameters.Count).ToArray())); + } + else + { + yield return Argument(_names.GetCodeIdentifier(method).IdentifierName); + } + } + } + + IEnumerable MakeSkeletonMethodResultStructInitializer(Method method) + { + foreach (var arg in method.Results) + { + yield return AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + _names.GetCodeIdentifier(arg).IdentifierName, + IdentifierName(arg.Name)); + } + } + + IEnumerable MakeSkeletonMethodCallArgs(Method method) + { + foreach (var arg in method.ParamsStruct.Fields) + { + yield return Argument( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ParamsLocal.IdentifierName, + _names.GetCodeIdentifier(arg).IdentifierName)); + } + } + + StatementSyntax MakeSkeletonMethodSerializerLocalDeclaration(Method method) + { + return LocalDeclarationStatement( + VariableDeclaration( + IdentifierName("var")) + .WithVariables( + SingletonSeparatedList( + VariableDeclarator( + _names.SerializerLocal.Identifier) + .WithInitializer( + EqualsValueClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.SerializerState)), + GenericName( + Identifier(nameof(Capnp.SerializerState.CreateForRpc))) + .WithTypeArgumentList( + TypeArgumentList( + SingletonSeparatedList( + _names.MakeTypeSyntax( + method.ResultStruct, + method.ResultStruct.Definition, + TypeUsage.Writer))))))))))); + + } + + CSharpSyntaxNode MakeMaybeTailCallLambdaBody(Method method) + { + var block = Block( + MakeSkeletonMethodSerializerLocalDeclaration(method)); + + if (method.ResultStruct.Definition.SpecialName == SpecialName.MethodResultStruct) + { + block = block.AddStatements( + LocalDeclarationStatement( + VariableDeclaration( + IdentifierName("var")) + .AddVariables( + VariableDeclarator(_names.ResultLocal.Identifier) + .WithInitializer(EqualsValueClause(ObjectCreationExpression( + _names.MakeTypeSyntax( + method.ResultStruct, + method.ResultStruct.Definition, + TypeUsage.DomainClass)) + .WithInitializer( + InitializerExpression(SyntaxKind.ObjectInitializerExpression) + .AddExpressions( + MakeSkeletonMethodResultStructInitializer(method).ToArray()))))))); + } + + if (method.Results.Count > 0) + { + block = block.AddStatements( + ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ResultLocal.IdentifierName, + _names.SerializeMethod.IdentifierName)) + .AddArgumentListArguments( + Argument(_names.SerializerLocal.IdentifierName)))); + } + + block = block.AddStatements( + ReturnStatement(_names.SerializerLocal.IdentifierName)); + + return block; + } + + IEnumerable MakeSkeletonMethodBody(Method method) + { + var call = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(SkeletonWorder.ImplName), + _names.GetCodeIdentifier(method).IdentifierName)); + + if (method.Params.Count > 0) + { + var paramsType = method.ParamsStruct; + var domainType = _names.MakeTypeSyntax(paramsType, method.ParamsStruct.Definition, TypeUsage.DomainClass); + + var createDomain = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.CapnpSerializable)), + GenericName(nameof(Capnp.CapnpSerializable.Create)) + .AddTypeArgumentListArguments(domainType))) + .AddArgumentListArguments( + Argument(_names.DeserializerLocal.IdentifierName)); + + if (method.ParamsStruct.Definition.SpecialName == SpecialName.MethodParamsStruct) + { + yield return LocalDeclarationStatement( + VariableDeclaration( + IdentifierName("var")) + .AddVariables( + VariableDeclarator(_names.ParamsLocal.Identifier) + .WithInitializer(EqualsValueClause(createDomain)))); + + call = call.AddArgumentListArguments( + MakeSkeletonMethodCallArgs(method).ToArray()); + } + else + { + call = call.AddArgumentListArguments( + Argument(createDomain)); + } + } + + call = call.AddArgumentListArguments( + Argument( + _names.CancellationTokenParameter.IdentifierName)); + + if (method.Results.Count == 0) + { + var awaitCall = AwaitExpression(call); + yield return ExpressionStatement(awaitCall); + yield return MakeSkeletonMethodSerializerLocalDeclaration(method); + yield return ReturnStatement(_names.SerializerLocal.IdentifierName); + } + else + { + ExpressionSyntax lambdaArg; + + if (method.ResultStruct.Definition.SpecialName == SpecialName.MethodResultStruct) + { + if (method.Results.Count == 1) + { + lambdaArg = SimpleLambdaExpression( + Parameter(Identifier(method.Results.Single().Name)), + MakeMaybeTailCallLambdaBody(method)); + } + else + { + lambdaArg = ParenthesizedLambdaExpression( + MakeMaybeTailCallLambdaBody(method)) + .AddParameterListParameters( + method.Results.Select(arg => Parameter(Identifier(arg.Name))).ToArray()); + } + } + else + { + lambdaArg = SimpleLambdaExpression( + Parameter(_names.ResultLocal.Identifier), + MakeMaybeTailCallLambdaBody(method)); + + } + + var maybeTailCall = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.Rpc.Impatient)), + IdentifierName(nameof(Capnp.Rpc.Impatient.MaybeTailCall)))) + .AddArgumentListArguments( + Argument(call), + Argument(lambdaArg)); + + yield return ReturnStatement(maybeTailCall); + } + } + + IEnumerable MakeSkeletonMethods(TypeDefinition def) + { + foreach (var method in def.Methods) + { + var methodDecl = MethodDeclaration( + Type>(), + _names.GetCodeIdentifier(method).Identifier) + .AddParameterListParameters( + Parameter(_names.DeserializerLocal.Identifier) + .WithType(Type()), + Parameter(_names.CancellationTokenParameter.Identifier) + .WithType(Type())) + .AddBodyStatements( + MakeSkeletonMethodBody(method).ToArray()); + + if (method.Results.Count == 0) + { + methodDecl = methodDecl.AddModifiers(Async); + } + + if (method.GenericParameters.Count > 0) + { + methodDecl = methodDecl + .AddTypeParameterListParameters(MakeTypeParameters(method).ToArray()) + .AddConstraintClauses(MakeTypeParameterConstraints(method).ToArray()); + } + + yield return methodDecl; + } + } + + public MemberDeclarationSyntax MakeSkeleton(TypeDefinition type) + { + var name = _names.MakeTypeName(type, NameUsage.Skeleton).Identifier; + var classDecl = ClassDeclaration(name) + .AddModifiers(Public) + .AddBaseListTypes( + SimpleBaseType( + GenericName(nameof(Capnp.Rpc.Skeleton)) + .AddTypeArgumentListArguments( + _names.MakeGenericTypeName(type, NameUsage.Interface)))) + .AddMembers( + // C'tor + ConstructorDeclaration(name) + .AddModifiers(Public) + .AddBodyStatements( + ExpressionStatement( + InvocationExpression( + IdentifierName(SkeletonWorder.SetMethodTableName)) + .AddArgumentListArguments( + MakeSkeletonSetMethodTableArguments(type).ToArray()))), + // InterfaceId + PropertyDeclaration(Type(), nameof(Capnp.Rpc.Skeleton.InterfaceId)) + .AddModifiers(Public, Override) + .WithExpressionBody( + ArrowExpressionClause( + ValueOf(type.Id))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken))); + + if (type.GenericParameters.Count > 0) + { + classDecl = classDecl + .AddTypeParameterListParameters(MakeTypeParameters(type).ToArray()) + .AddConstraintClauses(MakeTypeParameterConstraints(type).ToArray()); + } + + classDecl = classDecl.AddMembers(MakeSkeletonMethods(type).ToArray()); + + return classDecl; + } + + public bool RequiresPipeliningSupport(TypeDefinition type) + { + return type.Methods.Any(m => ExpandPipeliningPaths(m).Any()); + } + + IEnumerable> ExpandPipeliningPaths(Method method) + { + var stack = new Stack>(); + foreach (var field in method.ResultStruct.Fields) + { + stack.Push(new List() { field }); + } + + while (stack.Count > 0) + { + var path = stack.Pop(); + var last = path[path.Count - 1]; + + switch (last.Type.Tag) + { + case TypeTag.Interface: + case TypeTag.CapabilityPointer: + yield return path; + break; + + case TypeTag.Struct: + foreach (var field in last.Type.Fields) + { + if (path.Contains(field)) + { + // Recursive structs protection + continue; + } + + var copy = new List(); + copy.AddRange(path); + copy.Add(field); + stack.Push(copy); + } + break; + } + } + } + + readonly HashSet<(string, string)> _existingExtensionMethods = new HashSet<(string, string)>(); + + public MemberDeclarationSyntax MakePipeliningSupport(TypeDefinition type) + { + var classDecl = ClassDeclaration(_names.PipeliningExtensionsClassName.Identifier) + .AddModifiers(Public, Static, Partial); + + foreach (var method in type.Methods) + { + foreach (var path in ExpandPipeliningPaths(method)) + { + var accessPath = _names.MakeMemberAccessPathFieldName(method, path); + var methodName = _names.MakePipeliningSupportExtensionMethodName(path); + var capType = path[path.Count - 1].Type; + var capTypeSyntax = _names.MakeTypeSyntax(capType, null, TypeUsage.DomainClass); + + if (!_existingExtensionMethods.Add((capTypeSyntax.ToString(), methodName.ToString()))) + { + continue; + } + + var pathDecl = FieldDeclaration( + VariableDeclaration( + IdentifierName(nameof(Capnp.Rpc.MemberAccessPath))) + .AddVariables( + VariableDeclarator( + accessPath.Identifier) + .WithInitializer( + EqualsValueClause( + ObjectCreationExpression( + IdentifierName(nameof(Capnp.Rpc.MemberAccessPath))) + .AddArgumentListArguments( + path.Select( + f => Argument( + LiteralExpression(SyntaxKind.NumericLiteralExpression, + Literal(f.Offset)))).ToArray()))))) + .AddModifiers(Static, Readonly); + + + var methodDecl = MethodDeclaration( + capTypeSyntax, + methodName.Identifier) + .AddModifiers(Public, Static) + .AddParameterListParameters( + Parameter( + _names.TaskParameter.Identifier) + .AddModifiers(This) + .WithType(TransformReturnType(method))) + .AddBodyStatements( + ReturnStatement( + CastExpression( + capTypeSyntax, + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.Rpc.CapabilityReflection)), + GenericName( + Identifier(nameof(Capnp.Rpc.CapabilityReflection.CreateProxy))) + .AddTypeArgumentListArguments( + capTypeSyntax))) + .AddArgumentListArguments( + Argument( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName(nameof(Capnp.Rpc.Impatient)), + IdentifierName(nameof(Capnp.Rpc.Impatient.GetAnswer)))) + .AddArgumentListArguments( + Argument( + _names.TaskParameter.IdentifierName)), + IdentifierName(nameof(Capnp.Rpc.IPromisedAnswer.Access)))) + .AddArgumentListArguments( + Argument( + accessPath.IdentifierName))))))); + + classDecl = classDecl.AddMembers(pathDecl, methodDecl); + } + } + + return classDecl; + } + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/Name.cs b/CapnpC.CSharp.Generator/CodeGen/Name.cs new file mode 100644 index 0000000..5b8e877 --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/Name.cs @@ -0,0 +1,33 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class Name + { + readonly string _name; + + public Name(string name) + { + _name = name ?? throw new ArgumentNullException(nameof(name)); + IdentifierName = SyntaxFactory.IdentifierName(_name); + Identifier = SyntaxFactory.Identifier(_name); + VariableDeclarator = SyntaxFactory.VariableDeclarator(_name); + } + + public IdentifierNameSyntax IdentifierName { get; } + public SyntaxToken Identifier { get; } + public VariableDeclaratorSyntax VariableDeclarator { get; } + + public override string ToString() => _name; + + public override bool Equals(object obj) + { + return obj is Name other && _name == other._name; + } + + public override int GetHashCode() => _name.GetHashCode(); + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs new file mode 100644 index 0000000..869acf1 --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs @@ -0,0 +1,713 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CapnpC.CSharp.Generator.Model; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class ReaderSnippetGen + { + readonly GenNames _names; + + public ReaderSnippetGen(GenNames names) + { + _names = names; + } + + MemberDeclarationSyntax MakeReaderImplicitConversionOperator1() + { + return ConversionOperatorDeclaration( + Token(SyntaxKind.ImplicitKeyword), + IdentifierName(nameof(Capnp.DeserializerState))) + .WithModifiers( + TokenList( + new[]{ + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword)})) + .WithParameterList( + ParameterList( + SingletonSeparatedList( + Parameter(_names.ReaderParameter.Identifier) + .WithType(_names.ReaderStruct.IdentifierName)))) + .WithExpressionBody( + ArrowExpressionClause( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderParameter.IdentifierName, + _names.ReaderContextField.IdentifierName))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken)); + } + + MemberDeclarationSyntax MakeReaderImplicitConversionOperator2() + { + return ConversionOperatorDeclaration( + Token(SyntaxKind.ImplicitKeyword), + _names.ReaderStruct.IdentifierName) + .WithModifiers( + TokenList( + new[] { + Token(SyntaxKind.PublicKeyword), + Token(SyntaxKind.StaticKeyword) })) + .WithParameterList( + ParameterList( + SingletonSeparatedList( + Parameter(_names.ReaderContextField.Identifier) + .WithType(Type())))) + .WithExpressionBody( + ArrowExpressionClause( + ObjectCreationExpression(_names.ReaderStruct.IdentifierName) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument(_names.ReaderContextField.IdentifierName)))))) + .WithSemicolonToken( + Token(SyntaxKind.SemicolonToken)); + } + + MemberDeclarationSyntax MakeReaderCreateMethod() + { + return MethodDeclaration(_names.ReaderStruct.IdentifierName, _names.ReaderCreateMethod.Identifier) + .AddModifiers(Public, Static) + .WithParameterList( + ParameterList( + SingletonSeparatedList( + Parameter(_names.ContextParameter.Identifier) + .WithType( + Type())))) + .WithExpressionBody( + ArrowExpressionClause( + ObjectCreationExpression(_names.ReaderStruct.IdentifierName) + .AddArgumentListArguments(Argument(_names.ContextParameter.IdentifierName)))) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); + } + + IEnumerable MakeReaderStructMembers() + { + yield return FieldDeclaration( + VariableDeclaration( + Type()) + .AddVariables(_names.ReaderContextField.VariableDeclarator)) + .AddModifiers(Readonly); + + yield return ConstructorDeclaration(_names.ReaderStruct.Identifier) + .AddModifiers(Public) + .WithParameterList( + ParameterList( + SingletonSeparatedList( + Parameter(_names.ContextParameter.Identifier) + .WithType(Type())))) + .WithBody( + Block( + SingletonList( + ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ThisExpression(), + _names.ReaderContextField.IdentifierName), + _names.ContextParameter.IdentifierName))))); + + yield return MakeReaderCreateMethod(); + yield return MakeReaderImplicitConversionOperator1(); + yield return MakeReaderImplicitConversionOperator2(); + } + + IEnumerable MakeGroupReaderStructMembers() + { + yield return FieldDeclaration( + VariableDeclaration( + Type()) + .AddVariables(_names.ReaderContextField.VariableDeclarator)) + .AddModifiers(Readonly); + + yield return ConstructorDeclaration(_names.ReaderStruct.Identifier) + .AddModifiers(Public) + .WithParameterList( + ParameterList( + SingletonSeparatedList(Parameter(_names.GroupReaderContextArg.Identifier) + .WithType(Type())))) + .WithBody( + Block( + SingletonList( + ExpressionStatement( + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ThisExpression(), + _names.ReaderContextField.IdentifierName), + _names.GroupReaderContextArg.IdentifierName))))); + + yield return MakeReaderCreateMethod(); + yield return MakeReaderImplicitConversionOperator1(); + yield return MakeReaderImplicitConversionOperator2(); + } + + PropertyDeclarationSyntax MakeReaderProperty(TypeSyntax type, string name, ExpressionSyntax right, bool cond) + { + if (cond) + { + right = ConditionalExpression( + BinaryExpression( + SyntaxKind.EqualsExpression, + _names.UnionDiscriminatorProp.IdentifierName, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + IdentifierName(name))), + right, + LiteralExpression( + SyntaxKind.DefaultLiteralExpression, + Token(SyntaxKind.DefaultKeyword))); + } + + return PropertyDeclaration(type, name) + .AddModifiers(Public) + .WithExpressionBody(ArrowExpressionClause(right)) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); + } + + static Func MakeCastFunc(TypeSyntax type) => + x => CastExpression(type, x); + + static Func MakeListCastFunc(string castName, Field field) + { + // Insight: List may have complex default values (e.g. [true, false, false, true] as a + // valid default value for a list of bools. This does not yet fit the author's mindset. + + //if (field.DefaultValueIsExplicit) + //{ + // return x => InvocationExpression( + // MemberAccessExpression( + // SyntaxKind.SimpleMemberAccessExpression, + // x, + // IdentifierName(castName)) + // ) + // .AddArgumentListArguments( + // Argument(ValueOf(field.DefaultValue))); + //} + //else + + { + return x => InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + x, + IdentifierName(castName)) + ); + } + } + + static Func MakeListCastFuncWithCons( + string castName, ExpressionSyntax cons) + { + return x => InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + x, + IdentifierName(castName)) + ).AddArgumentListArguments(Argument(cons)); + } + + static Func MakeGenericListCastFunc(string castName, TypeSyntax genericArgument) + { + return x => InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + x, + GenericName(castName).AddTypeArgumentListArguments(genericArgument)) + ); + } + + PropertyDeclarationSyntax MakeReadProperty(TypeSyntax type, string name, SimpleNameSyntax readName, + object indexOrBitOffset, ExpressionSyntax secondArg, + Func cast, bool cond) + { + var right = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderContextField.IdentifierName, + readName)) + .AddArgumentListArguments( + Argument(ValueOf(indexOrBitOffset))); + + if (secondArg != null) + { + right = right.AddArgumentListArguments(Argument(secondArg)); + } + + ExpressionSyntax expr = right; + + if (cast != null) + { + expr = cast(expr); + } + + return MakeReaderProperty(type, name, expr, cond); + } + + PropertyDeclarationSyntax MakeReadProperty(TypeSyntax type, string name, string readName, + object indexOrBitOffset, ExpressionSyntax secondArg, + Func cast, bool cond) + { + return MakeReadProperty(type, name, IdentifierName(readName), indexOrBitOffset, secondArg, cast, cond); + } + + PropertyDeclarationSyntax MakeReadPrimitiveProperty(Field field, string readName) + { + return MakeReadProperty(Type(), _names.GetCodeIdentifier(field).ToString(), readName, field.BitOffset.Value, + ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadEnumProperty(Field field) + { + var typeSyntax = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + return MakeReadProperty(typeSyntax, + _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.SerializerExtensions.ReadDataUShort), field.BitOffset.Value, + ValueOf(field.DefaultValue.ScalarValue), + x => CastExpression(typeSyntax, x), + field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadTextProperty(Field field) + { + return MakeReadProperty(Type(), _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.DeserializerState.ReadText), (int)field.Offset, + ValueOf(field.DefaultValue.ScalarValue), null, field.DiscValue.HasValue); + } + + MemberAccessExpressionSyntax MakeReaderCreator(TypeSyntax qtype) + { + return MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + qtype, + _names.ReaderCreateMethod.IdentifierName); + } + + PropertyDeclarationSyntax MakeReadStructProperty(Field field) + { + var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + var creator = MakeReaderCreator(qtype); + + return MakeReadProperty(qtype, _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.DeserializerState.ReadStruct), (int)field.Offset, + creator, null, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadGroupProperty(Field field) + { + var type = QualifiedName( + _names.MakeTypeName(field.Type.Definition).IdentifierName, + _names.ReaderStruct.IdentifierName); + + var right = ObjectCreationExpression(type) + .WithArgumentList( + ArgumentList( + SingletonSeparatedList( + Argument(_names.ReaderContextField.IdentifierName)))); + + return MakeReaderProperty(type, _names.GetCodeIdentifier(field).ToString(), right, field.DiscValue.HasValue); + } + + void MakeReadListPropertyImpl(Model.Type elementType, TypeDefinition scope, ExpressionSyntax context, int depth, + out TypeSyntax listType, out ExpressionSyntax impl) + { + var elementTypeSyntax = _names.MakeTypeSyntax(elementType, scope, TypeUsage.Reader); + listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(elementTypeSyntax); + + if (elementType.Tag == TypeTag.Interface || + elementType.Tag == TypeTag.CapabilityPointer) + { + if (depth == 0) + { + impl = InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderContextField.IdentifierName, + GenericName(nameof(Capnp.DeserializerState.ReadCapList)) + .AddTypeArgumentListArguments(elementTypeSyntax) + )).AddArgumentListArguments(Argument(context)); + } + else + { + impl = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + context, + GenericName(nameof(Capnp.DeserializerState.RequireCapList)) + .AddTypeArgumentListArguments(elementTypeSyntax) + )); + } + + return; + } + + if (depth == 0) + { + context = InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderContextField.IdentifierName, + IdentifierName(nameof(Capnp.DeserializerState.ReadList)))) + .AddArgumentListArguments(Argument(context)); + } + else + { + context = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.DeserializerState.RequireList)) + )); + } + + string lambdaParamName = "_" + depth; + var lambdaParam = Parameter(Identifier(lambdaParamName)); + var lambdaArg = IdentifierName(lambdaParamName); + string castFuncName; + + switch (elementType.Tag) + { + case TypeTag.List: + { + + MakeReadListPropertyImpl( + elementType.ElementType, + scope, + lambdaArg, + depth + 1, + out var innerListType, + out var innerImpl); + + listType = GenericName("IReadOnlyList").AddTypeArgumentListArguments(innerListType); + + impl = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ListDeserializer.Cast)))) + .AddArgumentListArguments( + Argument(SimpleLambdaExpression(lambdaParam, innerImpl))); + + return; + } + + case TypeTag.ListPointer: + { + listType = Type>(); + + context = InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.DeserializerState.RequireList)) + )).AddArgumentListArguments(Argument(lambdaArg)); + + impl = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ReadOnlyListExtensions.LazyListSelect)))) + .AddArgumentListArguments( + Argument(SimpleLambdaExpression(lambdaParam, context))); + + return; + } + + case TypeTag.Struct: + { + impl = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ListDeserializer.Cast)))) + .AddArgumentListArguments( + Argument(MakeReaderCreator(elementTypeSyntax))); + + return; + } + + case TypeTag.Enum: + { + var cons = SimpleLambdaExpression( + lambdaParam, + CastExpression(elementTypeSyntax, lambdaArg)); + + impl = InvocationExpression( + MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ListDeserializer.CastEnums)))) + .AddArgumentListArguments( + Argument(cons)); + + return; + } + + case TypeTag.AnyPointer: + case TypeTag.StructPointer: + { + listType = Type>(); + impl = context; + return; + } + + case TypeTag.Void: + { + listType = Type(); + impl = MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ListDeserializer.Count))); + return; + } + + case TypeTag.Data: + castFuncName = nameof(Capnp.ListDeserializer.CastData); + break; + + case TypeTag.Text: + castFuncName = nameof(Capnp.ListDeserializer.CastText2); + break; + + case TypeTag.Bool: + castFuncName = nameof(Capnp.ListDeserializer.CastBool); + break; + + case TypeTag.F32: + castFuncName = nameof(Capnp.ListDeserializer.CastFloat); + break; + + case TypeTag.F64: + castFuncName = nameof(Capnp.ListDeserializer.CastDouble); + break; + + case TypeTag.S8: + castFuncName = nameof(Capnp.ListDeserializer.CastSByte); + break; + + case TypeTag.U8: + castFuncName = nameof(Capnp.ListDeserializer.CastByte); + break; + + case TypeTag.S16: + castFuncName = nameof(Capnp.ListDeserializer.CastShort); + break; + + case TypeTag.U16: + case TypeTag.AnyEnum: + castFuncName = nameof(Capnp.ListDeserializer.CastUShort); + break; + + case TypeTag.S32: + castFuncName = nameof(Capnp.ListDeserializer.CastInt); + break; + + case TypeTag.U32: + castFuncName = nameof(Capnp.ListDeserializer.CastUInt); + break; + + case TypeTag.S64: + castFuncName = nameof(Capnp.ListDeserializer.CastLong); + break; + + case TypeTag.U64: + castFuncName = nameof(Capnp.ListDeserializer.CastULong); + break; + + default: + throw new NotImplementedException("Unexpected type tag, don't know how to deal with this"); + } + + impl = InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(castFuncName))); + } + + PropertyDeclarationSyntax MakeReadListProperty(Field field) + { + var elementType = field.Type.ElementType; + var context = ValueOf((int)field.Offset); + MakeReadListPropertyImpl(elementType, field.DeclaringType, context, 0, out var listType, out var impl); + return MakeReaderProperty(listType, _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadAnyListProperty(Field field) + { + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + + return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.DeserializerState.ReadList), + (int)field.Offset, null, x => CastExpression(type, x), field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadCapProperty(Field field) + { + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + var readName = GenericName(nameof(Capnp.DeserializerState.ReadCap)) + .AddTypeArgumentListArguments(type); + + return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), + readName, + (int)field.Offset, null, null, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadAnyCapProperty(Field field) + { + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader); + + return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.DeserializerState.ReadCap), + (int)field.Offset, null, null, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadDataProperty(Field field) + { + var context = InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.ReaderContextField.IdentifierName, + IdentifierName(nameof(Capnp.DeserializerState.ReadList)))) + .AddArgumentListArguments(Argument(ValueOf((int)field.Offset))); + var impl = InvocationExpression(MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + context, + IdentifierName(nameof(Capnp.ListDeserializer.CastByte)))); + + return MakeReaderProperty( + _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Reader), + _names.GetCodeIdentifier(field).ToString(), impl, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReadAnyPointerProperty(Field field) + { + var type = IdentifierName(nameof(Capnp.DeserializerState)); + + return MakeReadProperty(type, _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.DeserializerState.StructReadPointer), + (int)field.Offset, null, null, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeReaderUnionSelector(TypeDefinition def) + { + var type = _names.UnionDiscriminatorEnum.IdentifierName; + return MakeReadProperty( + type, + _names.UnionDiscriminatorProp.ToString(), + nameof(Capnp.SerializerExtensions.ReadDataUShort), + def.UnionInfo.TagOffset, + ValueOf(default(ushort)), + MakeCastFunc(type), false); + } + + PropertyDeclarationSyntax MakeReaderFieldProperty(Field field) + { + switch (field.Type.Tag) + { + case TypeTag.Bool: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataBool)); + + case TypeTag.S8: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataSByte)); + + case TypeTag.U8: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataByte)); + + case TypeTag.S16: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataShort)); + + case TypeTag.U16: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataUShort)); + + case TypeTag.S32: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataInt)); + + case TypeTag.U32: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataUInt)); + + case TypeTag.S64: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataLong)); + + case TypeTag.U64: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataULong)); + + case TypeTag.F32: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataFloat)); + + case TypeTag.F64: + return MakeReadPrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataDouble)); + + case TypeTag.Enum: + return MakeReadEnumProperty(field); + + case TypeTag.Text: + return MakeReadTextProperty(field); + + case TypeTag.Struct: + return MakeReadStructProperty(field); + + case TypeTag.Group: + return MakeReadGroupProperty(field); + + case TypeTag.List: + return MakeReadListProperty(field); + + case TypeTag.Interface: + return MakeReadCapProperty(field); + + case TypeTag.CapabilityPointer: + return MakeReadAnyCapProperty(field); + + case TypeTag.ListPointer: + return MakeReadAnyListProperty(field); + + case TypeTag.AnyPointer: + case TypeTag.StructPointer: + return MakeReadAnyPointerProperty(field); + + case TypeTag.Data: + return MakeReadDataProperty(field); + + default: + return null; + } + } + + public StructDeclarationSyntax MakeReaderStruct(TypeDefinition def) + { + var readerDecl = StructDeclaration(_names.ReaderStruct.ToString()).AddModifiers(Public); + + var members = def.Tag == TypeTag.Group ? + MakeGroupReaderStructMembers() : + MakeReaderStructMembers(); + + readerDecl = readerDecl.AddMembers(members.ToArray()); + + if (def.UnionInfo != null) + { + readerDecl = readerDecl.AddMembers(MakeReaderUnionSelector(def)); + } + + foreach (var field in def.Fields) + { + var propDecl = MakeReaderFieldProperty(field); + + if (propDecl != null) + { + readerDecl = readerDecl.AddMembers(propDecl); + } + } + + return readerDecl; + } + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/SerializerStateWorder.cs b/CapnpC.CSharp.Generator/CodeGen/SerializerStateWorder.cs new file mode 100644 index 0000000..29cb72a --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/SerializerStateWorder.cs @@ -0,0 +1,10 @@ +using Capnp; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class SerializerStateWorder: SerializerState + { + public const string LinkName = nameof(SerializerStateWorder.Link); + public const string SetStructName = nameof(SerializerStateWorder.SetStruct); + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/SkeletonWorder.cs b/CapnpC.CSharp.Generator/CodeGen/SkeletonWorder.cs new file mode 100644 index 0000000..703ee3b --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/SkeletonWorder.cs @@ -0,0 +1,12 @@ +using System; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class SkeletonWorder : Capnp.Rpc.Skeleton + { + public override ulong InterfaceId => throw new NotImplementedException(); + + public const string SetMethodTableName = nameof(SkeletonWorder.SetMethodTable); + public const string ImplName = nameof(SkeletonWorder.Impl); + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/SyntaxHelpers.cs b/CapnpC.CSharp.Generator/CodeGen/SyntaxHelpers.cs new file mode 100644 index 0000000..56f258e --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/SyntaxHelpers.cs @@ -0,0 +1,126 @@ +using System; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + static class SyntaxHelpers + { + public static string MakeCamel(string name) => $"{char.ToUpperInvariant(name[0])}{name.Substring(1)}"; + public static string MakeAllLower(string name) => $"@{name}"; + + public static readonly SyntaxToken Async = Token(SyntaxKind.AsyncKeyword); + public static readonly SyntaxToken Public = Token(SyntaxKind.PublicKeyword); + public static readonly SyntaxToken Private = Token(SyntaxKind.PrivateKeyword); + public static readonly SyntaxToken Readonly = Token(SyntaxKind.ReadOnlyKeyword); + public static readonly SyntaxToken Static = Token(SyntaxKind.StaticKeyword); + public static readonly SyntaxToken Override = Token(SyntaxKind.OverrideKeyword); + public static readonly SyntaxToken Partial = Token(SyntaxKind.PartialKeyword); + public static readonly SyntaxToken This = Token(SyntaxKind.ThisKeyword); + + public static TypeSyntax Type(Type type) + { + switch (0) + { + case 0 when type == typeof(bool): + return PredefinedType(Token(SyntaxKind.BoolKeyword)); + + case 0 when type == typeof(sbyte): + return PredefinedType(Token(SyntaxKind.SByteKeyword)); + + case 0 when type == typeof(byte): + return PredefinedType(Token(SyntaxKind.ByteKeyword)); + + case 0 when type == typeof(short): + return PredefinedType(Token(SyntaxKind.ShortKeyword)); + + case 0 when type == typeof(ushort): + return PredefinedType(Token(SyntaxKind.UShortKeyword)); + + case 0 when type == typeof(int): + return PredefinedType(Token(SyntaxKind.IntKeyword)); + + case 0 when type == typeof(uint): + return PredefinedType(Token(SyntaxKind.UIntKeyword)); + + case 0 when type == typeof(long): + return PredefinedType(Token(SyntaxKind.LongKeyword)); + + case 0 when type == typeof(ulong): + return PredefinedType(Token(SyntaxKind.ULongKeyword)); + + case 0 when type == typeof(float): + return PredefinedType(Token(SyntaxKind.FloatKeyword)); + + case 0 when type == typeof(double): + return PredefinedType(Token(SyntaxKind.DoubleKeyword)); + + case 0 when type == typeof(string): + return PredefinedType(Token(SyntaxKind.StringKeyword)); + + case 0 when type == typeof(object): + return PredefinedType(Token(SyntaxKind.ObjectKeyword)); + + case 0 when type.IsGenericType: + return GenericName(type.Name.Substring(0, type.Name.IndexOf('`'))) + .AddTypeArgumentListArguments(type.GetGenericArguments().Select(Type).ToArray()); + + default: + return ParseTypeName(type.Name); + } + } + + public static TypeSyntax Type() => Type(typeof(T)); + + public static ExpressionSyntax ValueOf(object value) + { + switch (value) + { + case bool x: + return LiteralExpression(x ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression); + + case sbyte x: + return CastExpression(Type(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); + + case byte x: + return CastExpression(Type(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); + + case short x: + return CastExpression(Type(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); + + case ushort x: + return CastExpression(Type(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x))); + + case int x: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)); + + case uint x: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)); + + case long x: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)); + + case ulong x: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)); + + case float x: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)); + + case double x: + return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)); + + case string x: + return LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(x)); + + case null: + return LiteralExpression(SyntaxKind.NullLiteralExpression); + + default: + throw new NotImplementedException(); + } + } + } +} diff --git a/CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs b/CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs new file mode 100644 index 0000000..262b8eb --- /dev/null +++ b/CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs @@ -0,0 +1,403 @@ +using System.Collections.Generic; +using System.Linq; +using CapnpC.CSharp.Generator.Model; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using static CapnpC.CSharp.Generator.CodeGen.SyntaxHelpers; + +namespace CapnpC.CSharp.Generator.CodeGen +{ + class WriterSnippetGen + { + readonly GenNames _names; + + public WriterSnippetGen(GenNames names) + { + _names = names; + } + + IEnumerable MakeWriterStructMembers(TypeDefinition structType) + { + yield return ConstructorDeclaration(_names.WriterStruct.Identifier) + .AddModifiers(Public) + .WithBody( + Block( + ExpressionStatement( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ThisExpression(), + IdentifierName(SerializerStateWorder.SetStructName))) + .AddArgumentListArguments( + Argument( + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(structType.StructDataWordCount))), + Argument( + LiteralExpression( + SyntaxKind.NumericLiteralExpression, + Literal(structType.StructPointerCount))))))); + } + + IEnumerable MakeGroupWriterStructMembers() + { + yield return ConstructorDeclaration(_names.WriterStruct.Identifier) + .AddModifiers(Public) + .WithBody(Block()); + } + + PropertyDeclarationSyntax MakeWriterProperty( + TypeSyntax type, + string name, + ExpressionSyntax getter, + ExpressionSyntax setter, + bool cast, + bool cond) + { + if (cast) + { + getter = CastExpression(type, getter); + } + + if (cond) + { + getter = ConditionalExpression( + BinaryExpression( + SyntaxKind.EqualsExpression, + _names.UnionDiscriminatorProp.IdentifierName, + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + _names.UnionDiscriminatorEnum.IdentifierName, + IdentifierName(name))), + getter, + LiteralExpression( + SyntaxKind.DefaultLiteralExpression, + Token(SyntaxKind.DefaultKeyword))); + } + + var accessors = new AccessorDeclarationSyntax[setter != null ? 2 : 1]; + + accessors[0] = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithExpressionBody(ArrowExpressionClause(getter)) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); + + if (setter != null) + { + accessors[1] = AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithExpressionBody(ArrowExpressionClause(setter)) + .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); + } + + return PropertyDeclaration(type, name) + .AddModifiers(Public) + .AddAccessorListAccessors(accessors); + } + + ExpressionSyntax MakePointerSyntax(TypeSyntax type, object index) => + InvocationExpression( + GenericName(nameof(Capnp.SerializerState.BuildPointer)) + .AddTypeArgumentListArguments(type)) + .AddArgumentListArguments( + Argument(ValueOf(index))); + + ExpressionSyntax MakeReadCapSyntax(TypeSyntax type, object index) => + InvocationExpression( + GenericName(nameof(Capnp.SerializerState.ReadCap)) + .AddTypeArgumentListArguments(type)) + .AddArgumentListArguments( + Argument(ValueOf(index))); + + ExpressionSyntax MakeTypedPointerSyntax(object index, TypeSyntax type) => + InvocationExpression( + GenericName(nameof(Capnp.SerializerState.BuildPointer)) + .AddTypeArgumentListArguments(type)) + .AddArgumentListArguments( + Argument(ValueOf(index))); + + ExpressionSyntax MakeLinkSyntax(object index) => + InvocationExpression( + IdentifierName(SerializerStateWorder.LinkName)) + .AddArgumentListArguments( + Argument(ValueOf(index)), + Argument(IdentifierName("value"))); + + ExpressionSyntax MakeLinkObjectSyntax(object index) => + InvocationExpression( + IdentifierName(nameof(Capnp.SerializerState.LinkObject))) + .AddArgumentListArguments( + Argument(ValueOf(index)), + Argument(IdentifierName("value"))); + + PropertyDeclarationSyntax MakePointerProperty(TypeSyntax type, string name, object index, bool cast, bool cond) + { + ExpressionSyntax getter = MakePointerSyntax(type, index); + ExpressionSyntax setter = MakeLinkSyntax(index); + + return MakeWriterProperty(type, name, getter, setter, cast, cond); + } + + PropertyDeclarationSyntax MakePointerAsStructProperty( + TypeSyntax type, string name, object index, + bool cast, bool cond) + { + ExpressionSyntax getter = MakeTypedPointerSyntax(index, type); + ExpressionSyntax setter = MakeLinkSyntax(index); + + return MakeWriterProperty(type, name, getter, setter, cast, cond); + } + + PropertyDeclarationSyntax MakeProperty( + TypeSyntax outerType, + TypeSyntax innerType, + string name, + string readName, + string writeName, + object indexOrBitOffset, + ExpressionSyntax secondArg, + bool cast, + bool cond, + bool pasd) + { + var getter = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ThisExpression(), + IdentifierName(readName))) + .AddArgumentListArguments( + Argument(ValueOf(indexOrBitOffset)), + Argument(secondArg)); + + ExpressionSyntax value = IdentifierName("value"); + + if (cast) + { + value = CastExpression(innerType, value); + } + + var setter = InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + ThisExpression(), + IdentifierName(writeName))) + .AddArgumentListArguments( + Argument(ValueOf(indexOrBitOffset)), + Argument(value), + Argument(secondArg)); + + if (pasd) + { + setter.AddArgumentListArguments(Argument(secondArg)); + } + + return MakeWriterProperty(outerType, name, getter, setter, cast, cond); + } + + PropertyDeclarationSyntax MakePrimitiveProperty(Field field, string readName) + { + return MakeProperty(Type(), null, _names.GetCodeIdentifier(field).ToString(), + readName, + nameof(Capnp.SerializerExtensions.WriteData), + field.BitOffset.Value, + ValueOf(field.DefaultValue.ScalarValue), + false, + field.DiscValue.HasValue, + true); + } + + PropertyDeclarationSyntax MakeEnumProperty(Field field, string readName) + { + return MakeProperty(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.NotRelevant), Type(), + _names.GetCodeIdentifier(field).ToString(), + readName, + nameof(Capnp.SerializerExtensions.WriteData), + field.BitOffset.Value, + ValueOf(field.DefaultValue.ScalarValue), + true, + field.DiscValue.HasValue, + true); + } + + PropertyDeclarationSyntax MakeTextProperty(Field field) + { + return MakeProperty(Type(), null, + _names.GetCodeIdentifier(field).ToString(), + nameof(Capnp.SerializerState.ReadText), + nameof(Capnp.SerializerState.WriteText), + (int)field.Offset, + ValueOf(field.DefaultValue.ScalarValue), + false, + field.DiscValue.HasValue, + false); + } + + PropertyDeclarationSyntax MakeStructProperty(Field field) + { + var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); + + return MakePointerAsStructProperty(qtype, _names.GetCodeIdentifier(field).ToString(), + (int)field.Offset, false, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeGroupProperty(Field field) + { + var type = QualifiedName( + _names.MakeTypeName(field.Type.Definition).IdentifierName, + _names.WriterStruct.IdentifierName); + + var getter = InvocationExpression( + GenericName(nameof(Capnp.SerializerState.Rewrap)) + .AddTypeArgumentListArguments(type)); + + return MakeWriterProperty(type, _names.GetCodeIdentifier(field).ToString(), getter, null, false, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeListProperty(Field field) + { + var qtype = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); + + return MakePointerProperty(qtype, _names.GetCodeIdentifier(field).ToString(), + (int)field.Offset, false, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakePointerProperty(Field field) + { + var type = IdentifierName(nameof(Capnp.DynamicSerializerState)); + + return MakePointerProperty(type, _names.GetCodeIdentifier(field).ToString(), (int)field.Offset, false, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeCapProperty(Field field) + { + var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.Writer); + int index = (int)field.Offset; + string name = _names.GetCodeIdentifier(field).ToString(); + ExpressionSyntax getter = MakeReadCapSyntax(type, index); + ExpressionSyntax setter = MakeLinkObjectSyntax(index); + + return MakeWriterProperty(type, name, getter, setter, false, field.DiscValue.HasValue); + } + + PropertyDeclarationSyntax MakeWriterUnionSelector(TypeDefinition def) + { + return MakeProperty( + _names.UnionDiscriminatorEnum.IdentifierName, + Type(), + _names.UnionDiscriminatorProp.ToString(), + nameof(Capnp.SerializerExtensions.ReadDataUShort), + nameof(Capnp.SerializerExtensions.WriteData), + def.UnionInfo.TagOffset, + ValueOf(default(ushort)), + true, false, true); + } + + PropertyDeclarationSyntax MakeWriterFieldProperty(Field field) + { + switch (field.Type.Tag) + { + case TypeTag.Bool: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataBool)); + + case TypeTag.S8: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataSByte)); + + case TypeTag.U8: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataByte)); + + case TypeTag.S16: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataShort)); + + case TypeTag.U16: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataUShort)); + + case TypeTag.S32: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataInt)); + + case TypeTag.U32: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataUInt)); + + case TypeTag.S64: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataLong)); + + case TypeTag.U64: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataULong)); + + case TypeTag.F32: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataFloat)); + + case TypeTag.F64: + return MakePrimitiveProperty(field, + nameof(Capnp.SerializerExtensions.ReadDataDouble)); + + case TypeTag.Enum: + return MakeEnumProperty(field, nameof(Capnp.SerializerExtensions.ReadDataUShort)); + + case TypeTag.Text: + return MakeTextProperty(field); + + case TypeTag.Struct: + return MakeStructProperty(field); + + case TypeTag.Group: + return MakeGroupProperty(field); + + case TypeTag.List: + case TypeTag.Data: + return MakeListProperty(field); + + case TypeTag.AnyPointer: + case TypeTag.StructPointer: + case TypeTag.ListPointer: + return MakePointerProperty(field); + + case TypeTag.CapabilityPointer: + case TypeTag.Interface: + return MakeCapProperty(field); + + default: + return null; + } + } + + public ClassDeclarationSyntax MakeWriterStruct(TypeDefinition def) + { + var WriterDecl = ClassDeclaration(_names.WriterStruct.ToString()) + .AddModifiers(Public) + .AddBaseListTypes( + SimpleBaseType(IdentifierName(nameof(Capnp.SerializerState)))); + + var members = def.Tag == TypeTag.Group ? + MakeGroupWriterStructMembers() : + MakeWriterStructMembers(def); + + WriterDecl = WriterDecl.AddMembers(members.ToArray()); + + if (def.UnionInfo != null) + { + WriterDecl = WriterDecl.AddMembers(MakeWriterUnionSelector(def)); + } + + foreach (var field in def.Fields) + { + var propDecl = MakeWriterFieldProperty(field); + + if (propDecl != null) + { + WriterDecl = WriterDecl.AddMembers(propDecl); + } + } + + return WriterDecl; + } + } +} diff --git a/CapnpC.CSharp.Generator/FileGenerationResult.cs b/CapnpC.CSharp.Generator/FileGenerationResult.cs new file mode 100644 index 0000000..6d035e0 --- /dev/null +++ b/CapnpC.CSharp.Generator/FileGenerationResult.cs @@ -0,0 +1,52 @@ +using System; + +namespace CapnpC.CSharp.Generator +{ + /// + /// Represents the generation result of a single .capnp file + /// + public class FileGenerationResult + { + /// + /// Constructs an instance in case of successful generation + /// + /// path to .capnp file + /// generated C# code + public FileGenerationResult(string capnpFilePath, string generatedContent) + { + CapnpFilePath = capnpFilePath; + GeneratedContent = generatedContent; + } + + /// + /// Constructs an instance in case of unsuccessful generation + /// + /// path to .capnp file + /// Exception giving details on the error which prevented generation + public FileGenerationResult(string capnpFilePath, Exception exception) + { + CapnpFilePath = capnpFilePath; + Exception = exception; + } + + /// + /// Path to .capnp file + /// + public string CapnpFilePath { get; } + + /// + /// Generated C# or null if generation failed + /// + public string GeneratedContent { get; } + + /// + /// Exception giving details on the error which prevented generation + /// + public Exception Exception { get; } + + /// + /// true iff generation was successful + /// + public bool IsSuccess => !string.IsNullOrEmpty(GeneratedContent); + } +} diff --git a/CapnpC.CSharp.Generator/GenerationResult.cs b/CapnpC.CSharp.Generator/GenerationResult.cs new file mode 100644 index 0000000..3f40fc5 --- /dev/null +++ b/CapnpC.CSharp.Generator/GenerationResult.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace CapnpC.CSharp.Generator +{ + /// + /// Represents a .capnp -> code generator result + /// + public class GenerationResult + { + /// + /// Constructs an instance in case of at least partially successful generation. + /// + /// Generation result per file to generate + public GenerationResult(IReadOnlyList generatedFiles) + { + GeneratedFiles = generatedFiles; + } + + /// + /// Constructs an instance in case of total failure. + /// + /// Exception with details on error + public GenerationResult(Exception exception) + { + Exception = exception; + } + + /// + /// Generation result per file to generate or null in case of total failure + /// + public IReadOnlyList GeneratedFiles { get; } + + /// + /// Exception with details on error or null in case of success + /// + public Exception Exception { get; } + + /// + /// true iff generation was successful + /// + public bool IsSuccess => GeneratedFiles != null; + } +} diff --git a/CapnpC.CSharp.Generator/Model/AbstractType.cs b/CapnpC.CSharp.Generator/Model/AbstractType.cs new file mode 100644 index 0000000..6b6d4c7 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/AbstractType.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace CapnpC.CSharp.Generator.Model +{ + abstract class AbstractType + { + public TypeTag Tag { get; set; } + protected List Fields { get; } = new List(); + + public uint? FixedBitWidth + { + get + { + switch (Tag) + { + case TypeTag.Bool: + return 1; + + case TypeTag.U8: + case TypeTag.S8: + return 8; + + case TypeTag.U16: + case TypeTag.S16: + case TypeTag.Enum: + case TypeTag.AnyEnum: + return 16; + + case TypeTag.U32: + case TypeTag.S32: + case TypeTag.F32: + return 32; + + case TypeTag.U64: + case TypeTag.S64: + case TypeTag.F64: + return 64; + + default: + return null; + } + } + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/Annotation.cs b/CapnpC.CSharp.Generator/Model/Annotation.cs new file mode 100644 index 0000000..8b5d40b --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Annotation.cs @@ -0,0 +1,23 @@ +using System.Diagnostics; + +namespace CapnpC.CSharp.Generator.Model +{ + class Annotation : IDefinition + { + public ulong Id { get; } + public bool IsGenerated { get; } + public TypeTag Tag { get => TypeTag.Annotation; } + public IHasNestedDefinitions DeclaringElement { get; } + + public Type Type { get; set; } + + 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.Generator/Model/Constant.cs b/CapnpC.CSharp.Generator/Model/Constant.cs new file mode 100644 index 0000000..a01ac7d --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Constant.cs @@ -0,0 +1,23 @@ +using System.Diagnostics; + +namespace CapnpC.CSharp.Generator.Model +{ + class Constant : IDefinition + { + public ulong Id { get; } + public bool IsGenerated { get; } + public TypeTag Tag { get => TypeTag.Const; } + public IHasNestedDefinitions DeclaringElement { get; } + + public Value Value { get; set; } + + 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.Generator/Model/DefinitionManager.cs b/CapnpC.CSharp.Generator/Model/DefinitionManager.cs new file mode 100644 index 0000000..bd638f7 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/DefinitionManager.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CapnpC.CSharp.Generator.Model +{ + class DefinitionManager + { + readonly Dictionary _id2def = new Dictionary(); + + 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) + => 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.Generator/Model/Enumerant.cs b/CapnpC.CSharp.Generator/Model/Enumerant.cs new file mode 100644 index 0000000..4ba17f6 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Enumerant.cs @@ -0,0 +1,14 @@ +namespace CapnpC.CSharp.Generator.Model +{ + class Enumerant + { + string _literal; + public TypeDefinition TypeDefinition { 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.Generator/Model/Field.cs b/CapnpC.CSharp.Generator/Model/Field.cs new file mode 100644 index 0000000..017112c --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Field.cs @@ -0,0 +1,52 @@ +namespace CapnpC.CSharp.Generator.Model +{ + class Field + { + public TypeDefinition DeclaringType { get; set; } + public Field Parent { get; set; } + public string Name { get; set; } + public Type Type { get; set; } + public Value DefaultValue { get; set; } + public bool DefaultValueIsExplicit { get; set; } + public ushort? DiscValue { get; set; } + public uint Offset { get; set; } + public int CodeOrder { get; set; } + + public ulong? BitOffset => (ulong)Offset * Type?.FixedBitWidth; + + public Field Clone() + { + var field = new Field() + { + DeclaringType = DeclaringType, + Parent = Parent, + Name = Name, + Type = Type, + DefaultValue = DefaultValue, + DefaultValueIsExplicit = DefaultValueIsExplicit, + DiscValue = DiscValue, + Offset = Offset, + CodeOrder = CodeOrder, + }; + field.InheritFreeGenericParameters(); + return field; + } + + public void InheritFreeGenericParameters() + { + Type.InheritFreeParameters(DeclaringType); + } + + public override bool Equals(object obj) + { + return obj is Field other && + DeclaringType == other.DeclaringType && + Name == other.Name; + } + + public override int GetHashCode() + { + return (DeclaringType, Name).GetHashCode(); + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/GenFile.cs b/CapnpC.CSharp.Generator/Model/GenFile.cs new file mode 100644 index 0000000..217a760 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/GenFile.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace CapnpC.CSharp.Generator.Model +{ + class GenFile: IDefinition, IHasNestedDefinitions + { + public ulong Id { get; } + public bool IsGenerated { get; } + public TypeTag Tag { get => TypeTag.File; } + public IHasNestedDefinitions DeclaringElement { get => null; } + + public string Name { get; set; } + public string[] Namespace { get; set; } + + public IEnumerable NestedTypes { get => this.GetNestedTypes(); } + public ICollection NestedDefinitions { get; } = new List(); + public ICollection Constants { get; } = new List(); + + public GenFile(ulong id, bool isGenerated) + { + Id = id; + IsGenerated = isGenerated; + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/GenericParameter.cs b/CapnpC.CSharp.Generator/Model/GenericParameter.cs new file mode 100644 index 0000000..eaca7b9 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/GenericParameter.cs @@ -0,0 +1,26 @@ +namespace CapnpC.CSharp.Generator.Model +{ + class GenericParameter + { + public IHasGenericParameters DeclaringEntity { get; set; } + public int Index { get; set; } + public string Name => DeclaringEntity.GenericParameters[Index]; + + public override bool Equals(object obj) + { + // Instead of equality by Name, we could instead take (DeclaringEntity, Index), but there is a caveat: + // Since methods can also own generic parameters, we have different classes of declaring entities involved. + // Both the method will define generic parameters, and the Cap'n'p-generated params/result structs. + // Therefore we end in two GenericParameter instances, one with the Method as declaring entity, the + // other one with the params/result type definition as declaring entity. They are semantically the same, + // and the easy way to match them is by Name. Equality by Name is the only working choice, even though + // it feels a little less reboust than by matching declaring entity + parameter position. + return obj is GenericParameter other && Name == other.Name; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/HasGenericParameters.cs b/CapnpC.CSharp.Generator/Model/HasGenericParameters.cs new file mode 100644 index 0000000..55531b0 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/HasGenericParameters.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace CapnpC.CSharp.Generator.Model +{ + static class HasGenericParameters + { + public static IEnumerable GetLocalTypeParameters(this IHasGenericParameters me) + { + for (int i = 0; i < me.GenericParameters.Count; i++) + { + yield return new GenericParameter() + { + DeclaringEntity = me, + Index = i + }; + } + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/IDefinition.cs b/CapnpC.CSharp.Generator/Model/IDefinition.cs new file mode 100644 index 0000000..70b8501 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/IDefinition.cs @@ -0,0 +1,11 @@ + +namespace CapnpC.CSharp.Generator.Model +{ + interface IDefinition + { + ulong Id { get; } + bool IsGenerated { get; } + TypeTag Tag { get; } + IHasNestedDefinitions DeclaringElement { get; } + } +} diff --git a/CapnpC.CSharp.Generator/Model/IHasGenericParameters.cs b/CapnpC.CSharp.Generator/Model/IHasGenericParameters.cs new file mode 100644 index 0000000..fa71f35 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/IHasGenericParameters.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace CapnpC.CSharp.Generator.Model +{ + interface IHasGenericParameters + { + List GenericParameters { get; } + } +} diff --git a/CapnpC.CSharp.Generator/Model/IHasNestedDefinitions.cs b/CapnpC.CSharp.Generator/Model/IHasNestedDefinitions.cs new file mode 100644 index 0000000..06051c8 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/IHasNestedDefinitions.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Linq; + +namespace CapnpC.CSharp.Generator.Model +{ + interface IHasNestedDefinitions + { + 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.Generator/Model/IdentifierRenamer.cs b/CapnpC.CSharp.Generator/Model/IdentifierRenamer.cs new file mode 100644 index 0000000..0c0a6b5 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/IdentifierRenamer.cs @@ -0,0 +1,22 @@ +using Microsoft.CodeAnalysis.CSharp; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CapnpC.CSharp.Generator.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.Generator/Model/InvalidSchemaException.cs b/CapnpC.CSharp.Generator/Model/InvalidSchemaException.cs new file mode 100644 index 0000000..839119f --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/InvalidSchemaException.cs @@ -0,0 +1,11 @@ +using System; + +namespace CapnpC.CSharp.Generator.Model +{ + class InvalidSchemaException : Exception + { + public InvalidSchemaException(string message) : base(message) + { + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/Method.cs b/CapnpC.CSharp.Generator/Model/Method.cs new file mode 100644 index 0000000..1d1ba7b --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Method.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace CapnpC.CSharp.Generator.Model +{ + class Method: IHasGenericParameters + { + public TypeDefinition DeclaringInterface { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public List Params { get; } = new List(); + public List Results { get; } = new List(); + public Type ParamsStruct { get; set; } + public Type ResultStruct { get; set; } + public List GenericParameters { get; } = new List(); + } +} diff --git a/CapnpC.CSharp.Generator/Model/SchemaModel.cs b/CapnpC.CSharp.Generator/Model/SchemaModel.cs new file mode 100644 index 0000000..9d9e223 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/SchemaModel.cs @@ -0,0 +1,783 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +namespace CapnpC.CSharp.Generator.Model +{ + class SchemaModel + { + readonly Schema.CodeGeneratorRequest.Reader _request; + readonly List _generatedFiles = new List(); + readonly DefinitionManager _typeDefMgr = new DefinitionManager(); + + readonly Dictionary _id2node = new Dictionary(); + + public SchemaModel(Schema.CodeGeneratorRequest.Reader request) + { + _request = request; + } + + 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) + { + return (Schema.Node.Reader)IdToNode(id, true); + } + + void Build() + { + if (_request.Nodes == null || _request.Nodes.Count == 0) + { + throw new InvalidSchemaException("No nodes, nothing to generate"); + } + + foreach (var node in _request.Nodes) + { + if (_id2node.TryGetValue(node.Id, out var existingNode)) + { + throw new InvalidSchemaException($"Node {node.StrId()} \"{node.DisplayName}\" has a duplicate ID, prior node was \"{existingNode.DisplayName}\""); + } + _id2node[node.Id] = node; + } + + var requestedFiles = _request.RequestedFiles.ToDictionary(req => req.Id); + BuildPass1(requestedFiles); + BuildPass2(requestedFiles); + } + + // First pass: create type definitions for each node. + + struct Pass1State + { + public HashSet unprocessedNodes; + public IHasNestedDefinitions parent; + } + + 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; + 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); + } + if (state.unprocessedNodes.Count != 0) + { + throw new InvalidSchemaException("Unreferenced nodes were present in the schema."); + } + } + + 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) + { + 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; + + 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 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; + 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) + { + 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; + } + + 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 + { + return annotation.Value.Text.Split(new string[1] { "::" }, default); + } + } + return null; + } + + // 2nd pass: Generate types based on definitions + + struct Pass2State + { + public Method currentMethod; + public HashSet processedNodes; + } + + void BuildPass2(Dictionary requestedFiles) + { + var state = new Pass2State() { processedNodes = new HashSet() }; + foreach (var file in _typeDefMgr.Files) + { + var node = IdToNode(file.Id); + ProcessNestedNodes(node.NestedNodes, state, file.IsGenerated); + } + } + + void ProcessNestedNodes(IEnumerable nestedNodes, Pass2State state, bool mustExist) + { + foreach (var nestedNode in nestedNodes) + { + ProcessNode(nestedNode.Id, state, mustExist); + } + } + + void ProcessBrand(Schema.Brand.Reader brandReader, Type type, Pass2State state) + { + foreach (var scopeReader in brandReader.Scopes) + { + var whatToBind = ProcessTypeDef(scopeReader.ScopeId, state); + int index = 0; + + switch (0) + { + case 0 when scopeReader.IsBind: + foreach (var bindingReader in scopeReader.Bind) + { + var typeParameter = new GenericParameter() + { + DeclaringEntity = whatToBind, + Index = index++ + }; + + switch (0) + { + case 0 when bindingReader.IsType: + type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type, state)); + break; + + case 0 when bindingReader.IsUnbound: + type.BindGenericParameter(typeParameter, Types.FromParameter(typeParameter)); + break; + } + } + break; + + case 0 when scopeReader.IsInherit: + for (index = 0; index < type.DeclaringType.Definition.GenericParameters.Count; index++) + { + var typeParameter = new GenericParameter() + { + DeclaringEntity = whatToBind, + Index = index + }; + + type.BindGenericParameter(typeParameter, Types.FromParameter(typeParameter)); + } + break; + } + } + } + + Type ProcessType(Schema.Type.Reader typeReader, Pass2State state) + { + Type result; + + switch (0) + { + case 0 when typeReader.IsAnyPointer: + switch (0) + { + case 0 when typeReader.AnyPointer_IsParameter: + return Types.FromParameter( + new GenericParameter() + { + DeclaringEntity = ProcessTypeDef(typeReader.AnyPointer_Parameter_ScopeId, state), + Index = typeReader.AnyPointer_Parameter_ParameterIndex + }); + + case 0 when typeReader.AnyPointer_IsImplicitMethodParameter: + return Types.FromParameter( + new GenericParameter() + { + DeclaringEntity = state.currentMethod ?? throw new InvalidOperationException("current method not set"), + Index = typeReader.AnyPointer_ImplicitMethodParameter_ParameterIndex + }); + + case 0 when typeReader.AnyPointer_IsUnconstrained: + + switch (0) + { + case 0 when typeReader.AnyPointer_Unconstrained_IsAnyKind: + return Types.AnyPointer; + + case 0 when typeReader.AnyPointer_Unconstrained_IsCapability: + return Types.CapabilityPointer; + + case 0 when typeReader.AnyPointer_Unconstrained_IsList: + return Types.ListPointer; + + case 0 when typeReader.AnyPointer_Unconstrained_IsStruct: + return Types.StructPointer; + + default: + throw new NotImplementedException(); + } + + default: + throw new NotImplementedException(); + } + + case 0 when typeReader.IsBool: + return Types.Bool; + + case 0 when typeReader.IsData: + return Types.Data; + + case 0 when typeReader.IsFloat64: + return Types.F64; + + case 0 when typeReader.IsEnum: + return Types.FromDefinition(ProcessTypeDef(typeReader.Enum_TypeId, state, TypeTag.Enum)); + + case 0 when typeReader.IsFloat32: + return Types.F32; + + case 0 when typeReader.IsInt16: + return Types.S16; + + case 0 when typeReader.IsInt32: + return Types.S32; + + case 0 when typeReader.IsInt64: + return Types.S64; + + case 0 when typeReader.IsInt8: + return Types.S8; + + case 0 when typeReader.IsInterface: + 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, state)); + + case 0 when typeReader.IsStruct: + result = Types.FromDefinition(ProcessTypeDef(typeReader.Struct_TypeId, state, TypeTag.Struct)); + ProcessBrand(typeReader.Struct_Brand, result, state); + return result; + + case 0 when typeReader.IsText: + return Types.Text; + + case 0 when typeReader.IsUInt16: + return Types.U16; + + case 0 when typeReader.IsUInt32: + return Types.U32; + + case 0 when typeReader.IsUInt64: + return Types.U64; + + case 0 when typeReader.IsUInt8: + return Types.U8; + + case 0 when typeReader.IsVoid: + return Types.Void; + + default: + throw new NotImplementedException(); + } + } + + Value ProcessValue(Schema.Value.Reader valueReader) + { + var value = new Value(); + + switch (0) + { + case 0 when valueReader.IsAnyPointer: + value.ScalarValue = valueReader.AnyPointer; + value.Type = Types.AnyPointer; + break; + + case 0 when valueReader.IsBool: + value.ScalarValue = valueReader.Bool; + value.Type = Types.Bool; + break; + + case 0 when valueReader.IsData: + value.Items.AddRange(valueReader.Data.CastByte().Select(Value.Scalar)); + value.Type = Types.Data; + break; + + case 0 when valueReader.IsEnum: + value.ScalarValue = valueReader.Enum; + value.Type = Types.AnyEnum; + break; + + case 0 when valueReader.IsFloat32: + value.ScalarValue = valueReader.Float32; + value.Type = Types.F32; + break; + + case 0 when valueReader.IsFloat64: + value.ScalarValue = valueReader.Float64; + value.Type = Types.F64; + break; + + case 0 when valueReader.IsInt16: + value.ScalarValue = valueReader.Int16; + value.Type = Types.S16; + break; + + case 0 when valueReader.IsInt32: + value.ScalarValue = valueReader.Int32; + value.Type = Types.S32; + break; + + case 0 when valueReader.IsInt64: + value.ScalarValue = valueReader.Int64; + value.Type = Types.S64; + break; + + case 0 when valueReader.IsInt8: + value.ScalarValue = valueReader.Int8; + value.Type = Types.S8; + break; + + case 0 when valueReader.IsInterface: + value.ScalarValue = null; + value.Type = Types.CapabilityPointer; + break; + + case 0 when valueReader.IsList: + value.RawValue = valueReader.List; + value.Type = Types.ListPointer; + break; + + case 0 when valueReader.IsStruct: + value.RawValue = valueReader.Struct; + value.Type = Types.StructPointer; + break; + + case 0 when valueReader.IsText: + value.ScalarValue = valueReader.Text; + value.Type = Types.Text; + break; + + case 0 when valueReader.IsUInt16: + value.ScalarValue = valueReader.UInt16; + value.Type = Types.U16; + break; + + case 0 when valueReader.IsUInt32: + value.ScalarValue = valueReader.UInt32; + value.Type = Types.U32; + break; + + case 0 when valueReader.IsUInt64: + value.ScalarValue = valueReader.UInt64; + value.Type = Types.U64; + break; + + case 0 when valueReader.IsUInt8: + value.ScalarValue = valueReader.UInt8; + value.Type = Types.U8; + break; + + case 0 when valueReader.IsVoid: + value.Type = Types.Void; + break; + + default: + throw new NotImplementedException(); + } + + return value; + } + + void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List fields, Pass2State state) + { + if (reader.Fields == null) + { + return; + } + + foreach (var fieldReader in reader.Fields) + { + var field = new Field() + { + DeclaringType = declaringType, + Name = fieldReader.Name, + CodeOrder = fieldReader.CodeOrder + }; + + if (fieldReader.DiscriminantValue != Schema.Field.Reader.NoDiscriminant) + { + field.DiscValue = fieldReader.DiscriminantValue; + } + + switch (0) + { + case 0 when fieldReader.IsGroup: + 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, state); + field.DefaultValue.Type = field.Type; + break; + + default: + throw new NotImplementedException(); + } + + field.InheritFreeGenericParameters(); + + fields.Add(field); + } + } + + TypeDefinition ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader, Pass2State state) + { + def.IsGeneric = reader.IsGeneric; + + if (def.IsGeneric) + { + foreach (var paramReader in reader.Parameters) + { + def.GenericParameters.Add(paramReader.Name); + } + } + + ProcessNestedNodes(reader.NestedNodes, state, def.File.IsGenerated); + + ProcessFields(reader, def, def.Fields, state); + + if (reader.IsInterface) + { + foreach (var methodReader in reader.Interface_Methods) + { + var method = new Method() + { + DeclaringInterface = def, + Id = def.Methods.Count, + Name = methodReader.Name + }; + foreach (var implicitParameterReader in methodReader.ImplicitParameters) + { + method.GenericParameters.Add(implicitParameterReader.Name); + } + state.currentMethod = method; + + def.Methods.Add(method); + + var paramNode = IdToNode(methodReader.ParamStructType); + var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params, state); + if (paramType != null) + { + paramType.SpecialName = SpecialName.MethodParamsStruct; + paramType.UsingMethod = method; + method.ParamsStruct = Types.FromDefinition(paramType); + } + else + { + method.ParamsStruct = method.Params[0].Type; + } + method.ParamsStruct.InheritFreeParameters(def); + method.ParamsStruct.InheritFreeParameters(method); + + var resultNode = IdToNode(methodReader.ResultStructType); + var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results, state); + if (resultType != null) + { + resultType.SpecialName = SpecialName.MethodResultStruct; + resultType.UsingMethod = method; + method.ResultStruct = Types.FromDefinition(resultType); + } + else + { + method.ResultStruct = method.Results[0].Type; + } + method.ResultStruct.InheritFreeParameters(def); + method.ResultStruct.InheritFreeParameters(method); + } + + state.currentMethod = null; + } + return def; + } + + TypeDefinition ProcessStruct(Schema.Node.Reader structReader, TypeDefinition def, Pass2State state) + { + def.StructDataWordCount = structReader.Struct_DataWordCount; + def.StructPointerCount = structReader.Struct_PointerCount; + + if (structReader.Struct_DiscriminantCount > 0) + { + def.UnionInfo = new TypeDefinition.DiscriminationInfo( + structReader.Struct_DiscriminantCount, + 16u * structReader.Struct_DiscriminantOffset); + } + + return ProcessInterfaceOrStructTail(def, structReader, state); + } + + 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 +//# auto-generated. Such an auto-generated type will not be listed in the interface's +//# `nestedNodes` and its `scopeId` will be zero -- it is completely detached from the namespace. +//# (Awkwardly, it does of course inherit generic parameters from the method's scope, which makes +//# this a situation where you can't just climb the scope chain to find where a particular +//# generic parameter was introduced. Making the `scopeId` zero was a mistake.) + + if (!reader.IsStruct) + { + throw new InvalidSchemaException("Expected a struct"); + } + + var def = ProcessTypeDef(reader.Id, state, TypeTag.Struct); + + if (reader.ScopeId == 0) + { + // Auto-generated => Named parameter list + foreach (var field in def.Fields) list.Add(field); + return def; + } + else + { + // Single, anonymous, struct-typed parameter + var type = Types.FromDefinition(def); + ProcessBrand(brandReader, type, state); + var anon = new Field() { Type = type }; + list.Add(anon); + return null; + } + } + + TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, TypeDefinition def, Pass2State state) + { + foreach (var superClassReader in ifaceReader.Interface_Superclasses) + { + var superClass = ProcessTypeDef(superClassReader.Id, state, TypeTag.Interface); + def.Superclasses.Add(Types.FromDefinition(superClass)); + } + + return ProcessInterfaceOrStructTail(def, ifaceReader, state); + } + + TypeDefinition ProcessEnum(Schema.Node.Reader enumReader, TypeDefinition def, Pass2State state) + { + foreach (var fieldReader in enumReader.Enumerants) + { + var field = new Enumerant() + { + TypeDefinition = def, + Literal = fieldReader.Name, + CodeOrder = fieldReader.CodeOrder + }; + + if (fieldReader.Ordinal_IsExplicit) + { + field.Ordinal = fieldReader.Ordinal_Explicit; + } + + def.Enumerants.Add(field); + } + return def; + } + + Constant ProcessConst(Schema.Node.Reader constReader, Constant @const, Pass2State state) + { + var value = ProcessValue(constReader.Const_Value); + value.Type = ProcessType(constReader.Const_Type, state); + @const.Value = value; + return @const; + } + + TypeDefinition ProcessTypeDef(ulong id, Pass2State state, TypeTag tag = default) + { + var def = ProcessNode(id, state, true, tag); + var typeDef = def as TypeDefinition; + 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, bool mustExist, TypeTag tag = default) + { + 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); + if (state.processedNodes.Contains(id)) return def; + state.processedNodes.Add(id); + + switch (def) + { + 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 InvalidProgramException($"An unexpected node {node.StrId()} was found during the 2nd schema model building pass."); + } + } + + public static SchemaModel Create(Schema.CodeGeneratorRequest.Reader request) + { + var model = new SchemaModel(request); + model.Build(); + 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:X}"; + + public static string StrId(this ulong nodeId) + => $"0x{nodeId: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.Generator/Model/SpecialName.cs b/CapnpC.CSharp.Generator/Model/SpecialName.cs new file mode 100644 index 0000000..86887ab --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/SpecialName.cs @@ -0,0 +1,9 @@ +namespace CapnpC.CSharp.Generator.Model +{ + enum SpecialName + { + NothingSpecial, + MethodParamsStruct, + MethodResultStruct + } +} diff --git a/CapnpC.CSharp.Generator/Model/Type.cs b/CapnpC.CSharp.Generator/Model/Type.cs new file mode 100644 index 0000000..087f507 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Type.cs @@ -0,0 +1,201 @@ +using Capnp; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +namespace CapnpC.CSharp.Generator.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(); + public Type(TypeTag tag) + { + Tag = tag; + } + + public bool IsValueType + { + get + { + switch (Tag) + { + case TypeTag.AnyPointer: + case TypeTag.CapabilityPointer: + case TypeTag.Data: + case TypeTag.Group: + case TypeTag.Interface: + case TypeTag.List when ElementType.Tag != TypeTag.Void: + case TypeTag.ListPointer: + case TypeTag.Struct: + case TypeTag.StructPointer: + case TypeTag.Text: + case TypeTag.Void: + return false; + + default: + return true; + } + } + } + + public void InheritFreeParameters(IHasGenericParameters declaringType) + { + while (declaringType != null) + { + foreach (var p in declaringType.GetLocalTypeParameters()) + { + if (!_parameterBindings.ContainsKey(p)) + { + _parameterBindings[p] = Types.FromParameter(p); + } + } + + declaringType = (declaringType as TypeDefinition)?.DeclaringElement as IHasGenericParameters; + } + } + + Type SubstituteGenerics(Type type) + { + if (type == null) + { + return null; + } + + if (type.Parameter != null) + { + if (_parameterBindings.TryGetValue(type.Parameter, out var boundType)) + { + return boundType; + } + else + { + return Types.AnyPointer; + } + } + + var stype = new Type(type.Tag) + { + Definition = type.Definition, + ElementType = SubstituteGenerics(type.ElementType) + }; + + foreach (var kvp in type._parameterBindings) + { + var p = kvp.Value.Parameter; + + if (p != null && _parameterBindings.TryGetValue(p, out var boundType)) + { + stype._parameterBindings[kvp.Key] = boundType; + } + else + { + stype._parameterBindings[kvp.Key] = kvp.Value; + } + } + + return stype; + } + + Field SubstituteGenerics(Field field) + { + var result = field.Clone(); + result.Type = SubstituteGenerics(result.Type); + return result; + } + + public new IReadOnlyList Fields => Definition.Fields.LazyListSelect(SubstituteGenerics); + + public Type DeclaringType + { + get + { + var parentDef = Definition?.DeclaringElement as TypeDefinition; + // FIXME: Will become more sophisticated as soon as generics are implemented + return parentDef != null ? Types.FromDefinition(parentDef) : null; + } + } + + public (int, Type) GetRank() + { + var cur = this; + int rank = 0; + + while (cur.Tag == TypeTag.List) + { + cur = cur.ElementType; + ++rank; + } + + return (rank, cur); + } + + public IEnumerable AllImplementedClasses + { + get + { + var stk = new Stack(); + stk.Push(this); + var set = new HashSet(); + while (stk.Count > 0) + { + var def = stk.Pop(); + + if (def == null) + { + break; + } + + if (set.Add(def)) + { + foreach (var super in def.Definition.Superclasses) + { + stk.Push(super); + } + } + } + return set; + } + } + + public Type ResolveGenericParameter(GenericParameter genericParameter) + { + if (_parameterBindings.TryGetValue(genericParameter, out var type)) + { + return type; + } + else + { + return Types.AnyPointer; + } + } + + public void BindGenericParameter(GenericParameter genericParameter, Type boundType) + { + _parameterBindings.Add(genericParameter, boundType); + } + + public override bool Equals(object obj) + { + return obj is Type other && Definition == other.Definition; + } + + public override int GetHashCode() + { + return Definition?.GetHashCode() ?? 0; + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/TypeCategory.cs b/CapnpC.CSharp.Generator/Model/TypeCategory.cs new file mode 100644 index 0000000..507659e --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/TypeCategory.cs @@ -0,0 +1,8 @@ +namespace CapnpC.CSharp.Generator.Model +{ + enum TypeCategory + { + Value, + Pointer + } +} diff --git a/CapnpC.CSharp.Generator/Model/TypeDefinition.cs b/CapnpC.CSharp.Generator/Model/TypeDefinition.cs new file mode 100644 index 0000000..9a0557c --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/TypeDefinition.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace CapnpC.CSharp.Generator.Model +{ + class TypeDefinition : AbstractType, IDefinition, IHasNestedDefinitions, IHasGenericParameters + { + public class DiscriminationInfo + { + public DiscriminationInfo(ushort numOptions, uint tagOffset) + { + NumOptions = numOptions; + TagOffset = tagOffset; + } + + public ushort NumOptions { get; } + public uint TagOffset { get; } + } + + 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); + else + parent.NestedDefinitions.Add(this); + } + + public ulong Id { get; } + public bool IsGenerated { get; } + 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 ICollection NestedDefinitions { get; } = new List(); + public IEnumerable NestedTypes { get => this.GetNestedTypes(); } + public List NestedGroups { 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(); + public bool IsGeneric { get; set; } + public ushort StructDataWordCount { get; set; } + public ushort StructPointerCount { get; set; } + + public IEnumerable DefinitionHierarchy + { + get + { + IHasNestedDefinitions cur = this; + + while (cur is TypeDefinition def) + { + yield return def; + cur = def.DeclaringElement; + } + } + } + + public GenFile File + { + get + { + IHasNestedDefinitions cur = this; + while (cur is TypeDefinition def) cur = def.DeclaringElement; + return cur as GenFile; + } + } + + public IEnumerable AllTypeParameters + { + get + { + return from def in DefinitionHierarchy + from p in def.GetLocalTypeParameters() + select p; + } + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/TypeTag.cs b/CapnpC.CSharp.Generator/Model/TypeTag.cs new file mode 100644 index 0000000..756e229 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/TypeTag.cs @@ -0,0 +1,36 @@ +namespace CapnpC.CSharp.Generator.Model +{ + enum TypeTag + { + Unknown, + Void, + Bool, + S8, + U8, + S16, + U16, + S32, + U32, + S64, + U64, + F32, + F64, + List, + Data, + Text, + AnyPointer, + StructPointer, + ListPointer, + CapabilityPointer, + ParameterPointer, + ImplicitMethodParameterPointer, + Struct, + Group, + Interface, + Enum, + AnyEnum, + Const, + Annotation, + File + } +} diff --git a/CapnpC.CSharp.Generator/Model/Types.cs b/CapnpC.CSharp.Generator/Model/Types.cs new file mode 100644 index 0000000..c655010 --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Types.cs @@ -0,0 +1,56 @@ +using System; + +namespace CapnpC.CSharp.Generator.Model +{ + static class Types + { + public static readonly Type Void = new Type(TypeTag.Void); + public static readonly Type Bool = new Type(TypeTag.Bool); + public static readonly Type S8 = new Type(TypeTag.S8); + public static readonly Type U8 = new Type(TypeTag.U8); + public static readonly Type S16 = new Type(TypeTag.S16); + public static readonly Type U16 = new Type(TypeTag.U16); + public static readonly Type S32 = new Type(TypeTag.S32); + public static readonly Type U32 = new Type(TypeTag.U32); + public static readonly Type S64 = new Type(TypeTag.S64); + public static readonly Type U64 = new Type(TypeTag.U64); + public static readonly Type F32 = new Type(TypeTag.F32); + public static readonly Type F64 = new Type(TypeTag.F64); + public static readonly Type AnyPointer = new Type(TypeTag.AnyPointer); + public static readonly Type StructPointer = new Type(TypeTag.StructPointer); + public static readonly Type ListPointer = new Type(TypeTag.ListPointer); + public static readonly Type CapabilityPointer = new Type(TypeTag.CapabilityPointer); + public static readonly Type Data = new Type(TypeTag.Data); + public static readonly Type Text = new Type(TypeTag.Text); + public static readonly Type AnyEnum = new Type(TypeTag.AnyEnum); + + public static Type List(Type elementType) + { + return new Type(TypeTag.List) + { + ElementType = elementType + }; + } + + public static Type FromDefinition(TypeDefinition def) + { + if (def.Tag == TypeTag.Unknown) + { + throw new InvalidOperationException("Oops, type definition is not yet valid, cannot create type"); + } + + return new Type(def.Tag) + { + Definition = def + }; + } + + public static Type FromParameter(GenericParameter genericParameter) + { + return new Type(TypeTag.AnyPointer) + { + Parameter = genericParameter + }; + } + } +} diff --git a/CapnpC.CSharp.Generator/Model/Value.cs b/CapnpC.CSharp.Generator/Model/Value.cs new file mode 100644 index 0000000..21c27aa --- /dev/null +++ b/CapnpC.CSharp.Generator/Model/Value.cs @@ -0,0 +1,300 @@ +using Capnp; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CapnpC.CSharp.Generator.Model +{ + class Value + { + public static Value Scalar(T scalar) + { + var value = new Value() + { + ScalarValue = scalar + }; + + if (typeof(T) == typeof(bool)) + value.Type = Types.Bool; + else if (typeof(T) == typeof(float)) + value.Type = Types.F32; + else if (typeof(T) == typeof(double)) + value.Type = Types.F64; + else if (typeof(T) == typeof(sbyte)) + value.Type = Types.S8; + else if (typeof(T) == typeof(byte)) + value.Type = Types.U8; + else if (typeof(T) == typeof(short)) + value.Type = Types.S16; + else if (typeof(T) == typeof(ushort)) + value.Type = Types.U16; + else if (typeof(T) == typeof(int)) + value.Type = Types.S32; + else if (typeof(T) == typeof(uint)) + value.Type = Types.U32; + else if (typeof(T) == typeof(long)) + value.Type = Types.S64; + else if (typeof(T) == typeof(ulong)) + value.Type = Types.U64; + else if (typeof(T) == typeof(string)) + value.Type = Types.Text; + else + throw new NotImplementedException(); + + return value; + } + + public Type Type { get; set; } + public object ScalarValue { get; set; } + public Capnp.DeserializerState RawValue { get; set; } + public List Items { get; } = new List(); + public int VoidListCount { get; set; } + public ushort DiscriminatorValue { get; private set; } + public List<(Field, Value)> Fields { get; } = new List<(Field, Value)>(); + + public Enumerant GetEnumerant() + { + if (Type.Tag != TypeTag.Enum) + throw new InvalidOperationException(); + + if (Type.Definition.Enumerants[0].Ordinal.HasValue) + return Type.Definition.Enumerants.Single(e => e.Ordinal == (ushort)ScalarValue); + else + return Type.Definition.Enumerants[(ushort)ScalarValue]; + } + + void DecodeStruct() + { + if (RawValue.Kind != Capnp.ObjectKind.Struct) + { + throw new NotSupportedException(); + } + + var def = Type.Definition; + + if (def.UnionInfo != null) + { + DiscriminatorValue = RawValue.ReadDataUShort(def.UnionInfo.TagOffset, ushort.MaxValue); + } + + foreach (var field in Type.Fields) + { + if (field.DiscValue.HasValue && field.DiscValue.Value != DiscriminatorValue) + continue; + + Value value = new Value() + { + Type = field.Type + }; + + switch (field.Type.Tag) + { + case TypeTag.AnyEnum: + value.ScalarValue = field.DefaultValue?.ScalarValue as ushort? ?? 0; + break; + + case TypeTag.AnyPointer: + value.RawValue = RawValue.StructReadPointer((int)field.Offset); + break; + + case TypeTag.Bool: + value.ScalarValue = RawValue.ReadDataBool(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as bool?) ?? false); + break; + + case TypeTag.CapabilityPointer: + case TypeTag.Interface: + continue; + + case TypeTag.Data: + case TypeTag.Group: + case TypeTag.Struct: + case TypeTag.List: + case TypeTag.Text: + value.RawValue = RawValue.StructReadPointer((int)field.Offset); + value.Decode(); + break; + + case TypeTag.ListPointer: + case TypeTag.StructPointer: + value.RawValue = RawValue.StructReadPointer((int)field.Offset); + break; + + case TypeTag.Enum: + value.ScalarValue = field.DefaultValue?.ScalarValue as ushort? ?? ushort.MaxValue; + break; + + case TypeTag.F32: + value = Scalar(RawValue.ReadDataFloat(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as float?) ?? 0.0f)); + break; + + case TypeTag.F64: + value = Scalar(RawValue.ReadDataDouble(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as double?) ?? 0.0f)); + break; + + case TypeTag.S16: + value = Scalar(RawValue.ReadDataShort(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as short?) ?? 0)); + break; + + case TypeTag.S32: + value = Scalar(RawValue.ReadDataInt(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as int?) ?? 0)); + break; + + case TypeTag.S64: + value = Scalar(RawValue.ReadDataLong(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as long?) ?? 0)); + break; + + case TypeTag.S8: + value = Scalar(RawValue.ReadDataSByte(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as sbyte?) ?? 0)); + break; + + case TypeTag.U16: + value = Scalar(RawValue.ReadDataUShort(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as ushort?) ?? 0)); + break; + + case TypeTag.U32: + value = Scalar(RawValue.ReadDataUInt(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as uint?) ?? 0)); + break; + + case TypeTag.U64: + value = Scalar(RawValue.ReadDataULong(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as ulong?) ?? 0)); + break; + + case TypeTag.U8: + value = Scalar(RawValue.ReadDataByte(field.BitOffset.Value, (field.DefaultValue?.ScalarValue as byte?) ?? 0)); + break; + + case TypeTag.Void: + continue; + + default: + throw new NotImplementedException(); + } + + Fields.Add((field, value)); + } + } + + void DecodeList() + { + switch (Type.Tag) + { + case TypeTag.Data: + Items.AddRange(RawValue.RequireList().CastByte().Select(Scalar)); + break; + + case TypeTag.List: + switch (Type.ElementType.Tag) + { + case TypeTag.AnyEnum: + case TypeTag.Enum: + Items.AddRange(RawValue.RequireList().CastUShort().Select(u => { + var v = Scalar(u); + v.Type = Type.ElementType; + return v; })); + break; + + case TypeTag.AnyPointer: + Items.AddRange(RawValue.RequireList().Cast(d => new Value() { Type = Type.ElementType, RawValue = d })); + break; + + case TypeTag.Bool: + Items.AddRange(RawValue.RequireList().CastBool().Select(Scalar)); + break; + + case TypeTag.Data: + case TypeTag.Group: + case TypeTag.Struct: + case TypeTag.List: + Items.AddRange(RawValue.RequireList().Cast(d => { + var v = new Value() { Type = Type.ElementType, RawValue = d }; + v.Decode(); + return v; + })); + break; + + case TypeTag.Text: + Items.AddRange(RawValue.RequireList().CastText2().Select(Scalar)); + break; + + case TypeTag.F32: + Items.AddRange(RawValue.RequireList().CastFloat().Select(Scalar)); + break; + + case TypeTag.F64: + Items.AddRange(RawValue.RequireList().CastDouble().Select(Scalar)); + break; + + case TypeTag.S8: + Items.AddRange(RawValue.RequireList().CastSByte().Select(Scalar)); + break; + + case TypeTag.S16: + Items.AddRange(RawValue.RequireList().CastShort().Select(Scalar)); + break; + + case TypeTag.S32: + Items.AddRange(RawValue.RequireList().CastInt().Select(Scalar)); + break; + + case TypeTag.S64: + Items.AddRange(RawValue.RequireList().CastLong().Select(Scalar)); + break; + + case TypeTag.Void: + VoidListCount = RawValue.RequireList().Count; + break; + + case TypeTag.U16: + Items.AddRange(RawValue.RequireList().CastUShort().Select(Scalar)); + break; + + case TypeTag.U32: + Items.AddRange(RawValue.RequireList().CastUInt().Select(Scalar)); + break; + + case TypeTag.U64: + Items.AddRange(RawValue.RequireList().CastULong().Select(Scalar)); + break; + + case TypeTag.U8: + Items.AddRange(RawValue.RequireList().CastByte().Select(Scalar)); + break; + + default: + throw new NotImplementedException(); + } + break; + + case TypeTag.ListPointer: + Items.AddRange(RawValue.RequireList().Cast(d => new Value() { Type = Type.ElementType, RawValue = d })); + break; + + case TypeTag.Text: + ScalarValue = RawValue.RequireList().CastText(); + break; + } + } + + public void Decode() + { + if (RawValue.Kind == ObjectKind.Nil) return; + + switch (Type.Tag) + { + case TypeTag.Group: + case TypeTag.Struct: + DecodeStruct(); + break; + + case TypeTag.List: + case TypeTag.ListPointer: + case TypeTag.Text: + case TypeTag.Data: + DecodeList(); + break; + } + + RawValue = default(Capnp.DeserializerState); + } + } +} diff --git a/CapnpC.CSharp.Generator/Schema/SchemaSerialization.cs b/CapnpC.CSharp.Generator/Schema/SchemaSerialization.cs new file mode 100644 index 0000000..0655f7d --- /dev/null +++ b/CapnpC.CSharp.Generator/Schema/SchemaSerialization.cs @@ -0,0 +1,1575 @@ +using Capnp; +using System.Collections.Generic; + +namespace CapnpC.CSharp.Generator.Schema +{ + namespace Superclass + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ulong Id => State.ReadDataULong(0); + public Brand.Reader Brand => State.ReadStruct(0, Schema.Brand.Reader.Create); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 1); + } + + public ulong Id + { + get => this.ReadDataULong(0); + set => this.WriteData(0, value); + } + + public Brand.Writer Brand + { + get => BuildPointer(0); + set => Link(0, value); + } + } + } + + namespace Method + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public string Name => State.ReadText(0); + public ushort CodeOrder => State.ReadDataUShort(0); + public IReadOnlyList ImplicitParameters => State.ReadListOfStructs(4, Node.Parameter.Reader.Create); + public ulong ParamStructType => State.ReadDataULong(64); + public Brand.Reader ParamBrand => State.ReadStruct(2, Brand.Reader.Create); + public ulong ResultStructType => State.ReadDataULong(128); + public Brand.Reader ResultBrand => State.ReadStruct(3, Brand.Reader.Create); + public IReadOnlyList Annotations => State.ReadListOfStructs(1, Annotation.Reader.Create); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(5, 3); + } + + public string Name + { + get => ReadText(0); + set => WriteText(0, value); + } + + public ushort CodeOrder + { + get => this.ReadDataUShort(0); + set => this.WriteData(0, value); + } + + public ListOfStructsSerializer ImplicitParameters + { + get => BuildPointer>(4); + set => Link(4, value); + } + + public ref ulong ParamStructType => ref this.RefData(8); + + public Brand.Writer ParamBrand + { + get => BuildPointer(2); + set => Link(2, value); + } + + public ulong ResultStructType + { + get => this.ReadDataULong(128); + set => this.WriteData(128, value); + } + + public Brand.Writer ResultBrand + { + get => BuildPointer(3); + set => Link(3, value); + } + + public ListOfStructsSerializer Annotations + { + get => BuildPointer>(1); + set => Link(1, value); + } + } + } + + namespace Type + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ushort Tag => State.ReadDataUShort(0); + public bool IsVoid => Tag == 0; + public bool IsBool => Tag == 1; + public bool IsInt8 => Tag == 2; + public bool IsInt16 => Tag == 3; + public bool IsInt32 => Tag == 4; + public bool IsInt64 => Tag == 5; + public bool IsUInt8 => Tag == 6; + public bool IsUInt16 => Tag == 7; + public bool IsUInt32 => Tag == 8; + public bool IsUInt64 => Tag == 9; + public bool IsFloat32 => Tag == 10; + public bool IsFloat64 => Tag == 11; + public bool IsText => Tag == 12; + public bool IsData => Tag == 13; + public bool IsList => Tag == 14; + public Reader List_ElementType => IsList ? State.ReadStruct(0, Create) : default; + public bool IsEnum => Tag == 15; + public ulong Enum_TypeId => IsEnum ? State.ReadDataULong(64) : 0; + public Brand.Reader Enum_Brand => IsEnum ? State.ReadStruct(0, Brand.Reader.Create) : default; + public bool IsStruct => Tag == 16; + public ulong Struct_TypeId => IsStruct ? State.ReadDataULong(64) : 0; + public Brand.Reader Struct_Brand => IsStruct ? State.ReadStruct(0, Brand.Reader.Create) : default; + public bool IsInterface => Tag == 17; + public ulong Interface_TypeId => IsInterface ? State.ReadDataULong(64) : 0; + public Brand.Reader Interface_Brand => IsInterface ? State.ReadStruct(0, Brand.Reader.Create) : default; + public bool IsAnyPointer => Tag == 18; + public ushort AnyPointer_Tag => IsAnyPointer ? State.ReadDataUShort(64) : default; + public bool AnyPointer_IsUnconstrained => IsAnyPointer && AnyPointer_Tag == 0; + public ushort AnyPointer_Unconstrained_Tag => AnyPointer_IsUnconstrained ? State.ReadDataUShort(80) : (ushort)0; + public bool AnyPointer_Unconstrained_IsAnyKind => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 0; + public bool AnyPointer_Unconstrained_IsStruct => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 1; + public bool AnyPointer_Unconstrained_IsList => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 2; + public bool AnyPointer_Unconstrained_IsCapability => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 3; + public bool AnyPointer_IsParameter => IsAnyPointer && AnyPointer_Tag == 1; + public ulong AnyPointer_Parameter_ScopeId => AnyPointer_IsParameter ? State.ReadDataULong(128) : 0; + public ushort AnyPointer_Parameter_ParameterIndex => AnyPointer_IsParameter ? State.ReadDataUShort(80) : (ushort)0; + public bool AnyPointer_IsImplicitMethodParameter => AnyPointer_Tag == 2; + public ushort AnyPointer_ImplicitMethodParameter_ParameterIndex => AnyPointer_IsImplicitMethodParameter ? State.ReadDataUShort(80) : default; + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(3, 1); + } + + public ref ushort Tag => ref this.RefData(0); + + public bool IsVoid + { + get => Tag == 0; + set => Tag = 0; + } + + public bool IsBool + { + get => Tag == 1; + set => Tag = 1; + } + + public bool IsInt8 + { + get => Tag == 2; + set => Tag = 2; + } + + public bool IsInt16 + { + get => Tag == 3; + set => Tag = 3; + } + + public bool IsInt32 + { + get => Tag == 4; + set => Tag = 4; + } + + public bool IsInt64 + { + get => Tag == 5; + set => Tag = 5; + } + + public bool IsUInt8 + { + get => Tag == 6; + set => Tag = 6; + } + + public bool IsUInt16 + { + get => Tag == 7; + set => Tag = 7; + } + + public bool IsUInt32 + { + get => Tag == 8; + set => Tag = 8; + } + + public bool IsUInt64 + { + get => Tag == 9; + set => Tag = 9; + } + + public bool IsFloat32 + { + get => Tag == 10; + set => Tag = 10; + } + + public bool IsFloat64 + { + get => Tag == 11; + set => Tag = 11; + } + + public bool IsText + { + get => Tag == 12; + set => Tag = 12; + } + + public bool IsData + { + get => Tag == 13; + set => Tag = 13; + } + + public bool IsList + { + get => Tag == 14; + set => Tag = 14; + } + + public Writer List_ElementType + { + get => IsList ? BuildPointer(0) : default; + set { Link(0, value); } + } + + public bool IsEnum + { + get => Tag == 15; + set => Tag = 15; + } + + public ulong Enum_TypeId + { + get => IsEnum ? this.ReadDataULong(64) : 0; + set { this.WriteData(64, value); } + } + + public Brand.Writer Enum_Brand + { + get => IsEnum ? BuildPointer(0) : default; + set => Link(0, value); + } + + public bool IsStruct + { + get => Tag == 16; + set => Tag = 16; + } + + public ulong Struct_TypeId + { + get => IsStruct ? this.ReadDataULong(64) : 0; + set => this.WriteData(64, value); + } + + public Brand.Writer Struct_Brand + { + get => IsStruct ? BuildPointer(0) : default; + set => Link(0, value); + } + + public bool IsInterface + { + get => Tag == 17; + set => Tag = 17; + } + + public ulong Interface_TypeId + { + get => IsStruct ? this.ReadDataULong(64) : 0; + set => this.WriteData(64, value); + } + + public Brand.Writer Interface_Brand + { + get => IsStruct ? BuildPointer(0) : default; + set => Link(0, value); + } + + public bool IsAnyPointer + { + get => Tag == 18; + set => Tag = 18; + } + + public ushort AnyPointer_Tag + { + get => IsAnyPointer ? this.ReadDataUShort(64) : default; + set => this.WriteData(64, value); + } + + public bool AnyPointer_IsUnconstrained + { + get => IsAnyPointer && AnyPointer_Tag == 0; + set => AnyPointer_Tag = 0; + } + + public ushort AnyPointer_Unconstrained_Tag + { + get => AnyPointer_IsUnconstrained ? this.ReadDataUShort(80) : (ushort)0; + set => this.WriteData(80, value); + } + + public bool AnyPointer_Unconstrained_IsAnyKind + { + get => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 0; + set => AnyPointer_Unconstrained_Tag = 0; + } + + public bool AnyPointer_Unconstrained_IsStruct + { + get => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 1; + set => AnyPointer_Unconstrained_Tag = 1; + } + + public bool AnyPointer_Unconstrained_IsList + { + get => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 2; + set => AnyPointer_Unconstrained_Tag = 2; + } + + public bool AnyPointer_Unconstrained_IsCapability + { + get => AnyPointer_IsUnconstrained && AnyPointer_Unconstrained_Tag == 3; + set => AnyPointer_Unconstrained_Tag = 3; + } + + public bool AnyPointer_IsParameter + { + get => IsAnyPointer && AnyPointer_Tag == 1; + set => AnyPointer_Tag = 1; + } + + public ulong AnyPointer_Parameter_ScopeId + { + get => AnyPointer_IsParameter ? this.ReadDataULong(128) : 0; + set => this.WriteData(128, value); + } + + public ushort AnyPointer_Parameter_ParameterIndex + { + get => AnyPointer_IsParameter ? this.ReadDataUShort(80) : (ushort)0; + set => this.WriteData(80, value); + } + + public bool AnyPointer_IsImplicitMethodParameter + { + get => AnyPointer_Tag == 2; + set => AnyPointer_Tag = 2; + } + + public ushort AnyPointer_ImplicitMethodParameter_ParameterIndex + { + get => AnyPointer_IsImplicitMethodParameter ? this.ReadDataUShort(80) : default; + set => this.WriteData(80, value); + } + } + } + + namespace Brand + { + namespace Scope + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ulong ScopeId => State.ReadDataULong(0); + public ushort Tag => State.ReadDataUShort(64); + public bool IsBind => Tag == 0; + public IReadOnlyList Bind => IsBind ? State.ReadListOfStructs(0, Binding.Reader.Create) : null; + public bool IsInherit => Tag == 1; + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(2, 1); + } + + public ulong ScopeId + { + get => this.ReadDataULong(0); + set => this.WriteData(0, value); + } + + public ushort Tag + { + get => this.ReadDataUShort(64); + set => this.WriteData(64, value); + } + + public bool IsBind + { + get => Tag == 0; + set => Tag = 0; + } + + public ListOfStructsSerializer Bind + { + get => IsBind ? BuildPointer>(0) : default; + set => Link(0, value); + } + + public bool IsInherit + { + get => Tag == 1; + set => Tag = 1; + } + } + + namespace Binding + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ushort Tag => State.ReadDataUShort(0); + public bool IsUnbound => Tag == 0; + public bool IsType => Tag == 1; + public Type.Reader Type => IsType ? State.ReadStruct(0, Schema.Type.Reader.Create) : default; + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 1); + } + + public ushort Tag + { + get => this.ReadDataUShort(0); + set => this.WriteData(0, value); + } + + public bool IsUnbound + { + get => Tag == 0; + set => Tag = 0; + } + + public bool IsType + { + get => Tag == 1; + set => Tag = 1; + } + + public Type.Writer Type + { + get => IsType ? BuildPointer(0) : default; + set => Link(0, value); + } + } + } + } + + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public IReadOnlyList Scopes => State.ReadListOfStructs(0, Scope.Reader.Create); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(0, 1); + } + + public ListOfStructsSerializer Scopes + { + get => BuildPointer>(0); + set => Link(0, value); + } + } + } + + namespace Value + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ushort Tag => State.ReadDataUShort(0); + public bool IsVoid => Tag == 0; + public bool IsBool => Tag == 1; + public bool Bool => IsBool ? State.ReadDataBool(16) : default; + public bool IsInt8 => Tag == 2; + public sbyte Int8 => IsInt8 ? State.ReadDataSByte(16) : default; + public bool IsInt16 => Tag == 3; + public short Int16 => IsInt16 ? State.ReadDataShort(16) : default; + public bool IsInt32 => Tag == 4; + public int Int32 => IsInt32 ? State.ReadDataInt(32) : default; + public bool IsInt64 => Tag == 5; + public long Int64 => IsInt64 ? State.ReadDataLong(64) : default; + public bool IsUInt8 => Tag == 6; + public byte UInt8 => IsUInt8 ? State.ReadDataByte(16) : default; + public bool IsUInt16 => Tag == 7; + public ushort UInt16 => IsUInt16 ? State.ReadDataUShort(16) : default; + public bool IsUInt32 => Tag == 8; + public uint UInt32 => IsUInt32 ? State.ReadDataUInt(32) : default; + public bool IsUInt64 => Tag == 9; + public ulong UInt64 => IsUInt64 ? State.ReadDataULong(64) : default; + public bool IsFloat32 => Tag == 10; + public float Float32 => IsFloat32 ? State.ReadDataFloat(32) : default; + public bool IsFloat64 => Tag == 11; + public double Float64 => IsFloat64 ? State.ReadDataDouble(64) : default; + public bool IsText => Tag == 12; + public string Text => IsText ? State.ReadText(0) : default; + public bool IsData => Tag == 13; + public ListDeserializer Data => IsData ? State.ReadList(0) : default; + public bool IsList => Tag == 14; + public DeserializerState List => IsList ? State.StructReadPointer(0) : default; + public bool IsEnum => Tag == 15; + public ushort Enum => IsEnum ? State.ReadDataUShort(16) : default; + public bool IsStruct => Tag == 16; + public DeserializerState Struct => IsStruct ? State.StructReadPointer(0) : default; + public bool IsInterface => Tag == 17; + public bool IsAnyPointer => Tag == 18; + public DeserializerState AnyPointer => IsAnyPointer ? State.StructReadPointer(0) : default; + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(2, 1); + } + + public ushort Tag + { + get => this.ReadDataUShort(0); + set => this.WriteData(0, value); + } + + public bool IsVoid + { + get => Tag == 0; + set => Tag = 0; + } + + public bool IsBool + { + get => Tag == 1; + set => Tag = 1; + } + + public bool Bool + { + get => IsBool ? this.ReadDataBool(16) : default; + set => this.WriteData(16, value); + } + + public bool IsInt8 + { + get => Tag == 2; + set => Tag = 2; + } + + public sbyte Int8 + { + get => IsInt8 ? this.ReadDataSByte(16) : default; + set => this.WriteData(16, value); + } + + public bool IsInt16 + { + get => Tag == 3; + set => Tag = 3; + } + + public short Int16 + { + get => IsInt16 ? this.ReadDataShort(16) : default; + set => this.WriteData(16, value); + } + + public bool IsInt32 + { + get => Tag == 4; + set => Tag = 4; + } + + public int Int32 + { + get => IsInt32 ? this.ReadDataInt(32) : default; + set => this.WriteData(32, value); + } + + public bool IsInt64 + { + get => Tag == 5; + set => Tag = 5; + } + + public long Int64 + { + get => IsInt64 ? this.ReadDataLong(64) : default; + set => this.WriteData(64, value); + } + + public bool IsUInt8 + { + get => Tag == 6; + set => Tag = 6; + } + + public byte UInt8 + { + get => IsUInt8 ? this.ReadDataByte(16) : default; + set => this.WriteData(16, value); + } + + public bool IsUInt16 + { + get => Tag == 7; + set => Tag = 7; + } + + public ushort UInt16 + { + get => IsUInt16 ? this.ReadDataUShort(16) : default; + set => this.WriteData(16, value); + } + + public bool IsUInt32 + { + get => Tag == 8; + set => Tag = 8; + } + + public uint UInt32 + { + get => IsUInt32 ? this.ReadDataUInt(32) : default; + set => this.WriteData(32, value); + } + + public bool IsUInt64 + { + get => Tag == 9; + set => Tag = 9; + } + + public ulong UInt64 + { + get => IsUInt64 ? this.ReadDataULong(64) : default; + set => this.WriteData(64, value); + } + + public bool IsFloat32 + { + get => Tag == 10; + set => Tag = 10; + } + + public float Float32 + { + get => IsFloat32 ? this.ReadDataFloat(32) : default; + set => this.WriteData(32, value); + } + + public bool IsFloat64 + { + get => Tag == 11; + set => Tag = 11; + } + + public double Float64 + { + get => IsFloat64 ? this.ReadDataDouble(64) : default; + set => this.WriteData(64, value); + } + + public bool IsText + { + get => Tag == 12; + set => Tag = 12; + } + + public string Text + { + get => IsText ? ReadText(0) : default; + set => WriteText(0, value); + } + + public bool IsData + { + get => Tag == 13; + set => Tag = 13; + } + + public SerializerState Data + { + get => IsData ? BuildPointer(0) : default; + set => Link(0, value); + } + + public bool IsList + { + get => Tag == 14; + set => Tag = 14; + } + + public SerializerState List + { + get => IsList ? BuildPointer(0) : default; + set => Link(0, value); + } + + public bool IsEnum + { + get => Tag == 15; + set => Tag = 15; + } + + public ushort Enum + { + get => IsEnum ? this.ReadDataUShort(16) : default; + set => this.WriteData(16, value); + } + + public bool IsStruct + { + get => Tag == 16; + set => Tag = 16; + } + + public SerializerState Struct + { + get => IsStruct ? BuildPointer(0) : default; + set => Link(0, value); + } + + public bool IsInterface + { + get => Tag == 17; + set => Tag = 17; + } + + public bool IsAnyPointer + { + get => Tag == 18; + set => Tag = 18; + } + + public SerializerState AnyPointer + { + get => IsAnyPointer ? BuildPointer(0) : default; + set => Link(0, value); + } + } + } + + namespace Annotation + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ulong Id => State.ReadDataULong(0); + public Brand.Reader Brand => State.ReadStruct(1, Schema.Brand.Reader.Create); + public Value.Reader Value => State.ReadStruct(0, Schema.Value.Reader.Create); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 2); + } + + public ref ulong Id => ref this.RefData(0); + + public Brand.Writer Brand + { + get => BuildPointer(1); + set => Link(1, value); + } + + public Value.Writer Value + { + get => BuildPointer(0); + set => Link(0, value); + } + } + } + + public enum ElementSize: ushort + { + Empty = 0, + Bit = 1, + Byte = 2, + TwoBytes = 3, + FourBytes = 4, + EightBytes = 5, + Pointer = 6, + InlineComposite = 7 + } + + namespace Field + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public string Name => State.ReadText(0); + public ushort CodeOrder => State.ReadDataUShort(0); + public IReadOnlyList Annotations => State.ReadListOfStructs(1, Annotation.Reader.Create); + public ushort DiscriminantValue => State.ReadDataUShort(16, 65535); + public ushort Tag => State.ReadDataUShort(64); + public bool IsSlot => Tag == 0; + public uint Slot_Offset => IsSlot ? State.ReadDataUInt(32) : default; + public Type.Reader Slot_Type => IsSlot ? State.ReadStruct(2, Type.Reader.Create) : default; + public Value.Reader Slot_DefaultValue => IsSlot ? State.ReadStruct(3, Value.Reader.Create) : default; + public bool Slot_HadExplicitDefault => IsSlot ? State.ReadDataBool(128) : default; + public bool IsGroup => Tag == 1; + public ulong Group_TypeId => IsGroup ? State.ReadDataULong(128) : default; + public ushort Ordinal_Tag => State.ReadDataUShort(80); + public bool Ordinal_IsImplicit => Ordinal_Tag == 0; + public bool Ordinal_IsExplicit => Ordinal_Tag == 1; + public ushort Ordinal_Explicit => Ordinal_IsExplicit ? State.ReadDataUShort(96) : default; + + public const ushort NoDiscriminant = 0xffff; + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(3, 3); + } + + public string Name + { + get => ReadText(0); + set => WriteText(0, value); + } + + public ref ushort CodeOrder => ref this.RefData(0); + + public ListOfStructsSerializer Annotations + { + get => BuildPointer>(1); + set => Link(1, value); + } + + public ushort DiscriminantValue + { + get => this.ReadDataUShort(16, 65535); + set => this.WriteData(16, value, (ushort)65535); + } + + public ref ushort Tag => ref this.RefData(8); + + public bool IsSlot + { + get => Tag == 0; + set => Tag = 0; + } + + public uint Slot_Offset + { + get => IsSlot ? this.ReadDataUInt(32) : default; + set => this.WriteData(32, value); + } + + public Type.Writer Slot_Type + { + get => IsSlot ? BuildPointer(2) : default; + set => Link(2, value); + } + + public Value.Writer Slot_DefaultValue + { + get => IsSlot ? BuildPointer(3) : default; + set => Link(3, value); + } + + public bool Slot_HadExplicitDefault + { + get => IsSlot ? this.ReadDataBool(128) : default; + set => this.WriteData(128, value); + } + + public bool IsGroup + { + get => Tag == 1; + set => Tag = 1; + } + + public ref ulong Group_TypeId => ref this.RefData(2); + + public ref ushort Ordinal_Tag => ref this.RefData(5); + + public bool Ordinal_IsImplicit + { + get => Ordinal_Tag == 0; + set => Ordinal_Tag = 0; + } + + public bool Ordinal_IsExplicit + { + get => Ordinal_Tag == 1; + set => Ordinal_Tag = 1; + } + + public ref ushort Ordinal_Explicit => ref this.RefData(6); + } + } + + namespace Node + { + namespace Parameter + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public string Name => State.ReadText(0); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(0, 1); + } + + public string Name + { + get => ReadText(0); + set => WriteText(0, value); + } + } + } + + namespace NestedNode + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public string Name => State.ReadText(0); + public ulong Id => State.ReadDataULong(0); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 1); + } + + public string Name + { + get => ReadText(0); + set => WriteText(0, value); + } + + public ref ulong Id => ref this.RefData(0); + } + } + + namespace SourceInfo + { + namespace Member + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public string DocComment => State.ReadText(0); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(0, 1); + } + + public string DocComment + { + get => ReadText(0); + set => WriteText(0, value); + } + } + } + + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ulong Id => State.ReadDataULong(0); + public string DocComment => State.ReadText(0); + public IReadOnlyList Members => State.ReadListOfStructs(1, Member.Reader.Create); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 2); + } + + public ref ulong Id => ref this.RefData(0); + + public string DocComment + { + get => ReadText(0); + set => WriteText(0, value); + } + + public ListOfStructsSerializer Members + { + get => BuildPointer>(1); + set => Link(1, value); + } + } + } + + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ulong Id => State.ReadDataULong(0); + public string DisplayName => State.ReadText(0); + public uint DisplayNamePrefixLength => State.ReadDataUInt(64); + public ulong ScopeId => State.ReadDataULong(128); + public IReadOnlyList Parameters => State.ReadListOfStructs(5, Parameter.Reader.Create); + public bool IsGeneric => State.ReadDataBool(288); + public IReadOnlyList NestedNodes => State.ReadListOfStructs(1, NestedNode.Reader.Create); + public IReadOnlyList Annotations => State.ReadListOfStructs(2, Annotation.Reader.Create); + public ushort Tag => State.ReadDataUShort(96); + public bool IsFile => Tag == 0; + public bool IsStruct => Tag == 1; + public ushort Struct_DataWordCount => IsStruct ? State.ReadDataUShort(112) : default; + public ushort Struct_PointerCount => IsStruct ? State.ReadDataUShort(192) : default; + public ElementSize Struct_PreferredListEncoding => IsStruct ? (ElementSize)State.ReadDataUShort(208) : default; + public bool Struct_IsGroup => IsStruct ? State.ReadDataBool(224) : default; + public ushort Struct_DiscriminantCount => IsStruct ? State.ReadDataUShort(240) : default; + public uint Struct_DiscriminantOffset => IsStruct ? State.ReadDataUInt(256) : default; + public IReadOnlyList Fields => IsStruct ? State.ReadListOfStructs(3, Field.Reader.Create) : default; + public bool IsEnum => Tag == 2; + public IReadOnlyList Enumerants => IsEnum ? State.ReadListOfStructs(3, Field.Reader.Create) : default; + public bool IsInterface => Tag == 3; + public IReadOnlyList Interface_Methods => IsInterface ? State.ReadListOfStructs(3, Method.Reader.Create) : default; + public IReadOnlyList Interface_Superclasses => IsInterface ? State.ReadListOfStructs(4, Superclass.Reader.Create) : default; + public bool IsConst => Tag == 4; + public Type.Reader Const_Type => IsConst ? State.ReadStruct(3, Type.Reader.Create) : default; + public Value.Reader Const_Value => IsConst ? State.ReadStruct(4, Value.Reader.Create) : default; + public bool IsAnnotation => Tag == 5; + public Type.Reader Annotation_Type => IsAnnotation ? State.ReadStruct(3, Type.Reader.Create) : default; + public bool Annotation_TargetsFile => IsAnnotation ? State.ReadDataBool(112) : default; + public bool Annotation_TargetsConst => IsAnnotation ? State.ReadDataBool(113) : default; + public bool Annotation_TargetsEnum => IsAnnotation ? State.ReadDataBool(114) : default; + public bool Annotation_TargetsEnumerant => IsAnnotation ? State.ReadDataBool(115) : default; + public bool Annotation_TargetsStruct => IsAnnotation ? State.ReadDataBool(116) : default; + public bool Annotation_TargetsField => IsAnnotation ? State.ReadDataBool(117) : default; + public bool Annotation_TargetsUnion => IsAnnotation ? State.ReadDataBool(118) : default; + public bool Annotation_TargetsGroup => IsAnnotation ? State.ReadDataBool(119) : default; + public bool Annotation_TargetsInterface => IsAnnotation ? State.ReadDataBool(120) : default; + public bool Annotation_TargetsMethod => IsAnnotation ? State.ReadDataBool(121) : default; + public bool Annotation_TargetsParam => IsAnnotation ? State.ReadDataBool(122) : default; + public bool Annotation_TargetsAnnotation => IsAnnotation ? State.ReadDataBool(123) : default; + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(5, 6); + } + + public ulong Id + { + get => this.ReadDataULong(0); + set => this.WriteData(0, value); + } + + public string DisplayName + { + get => ReadText(0); + set => WriteText(0, value); + } + + public ref uint DisplayNamePrefixLength => ref this.RefData(2); + + public ref ulong ScopeId => ref this.RefData(2); + + public ListOfStructsSerializer Parameters + { + get => BuildPointer>(5); + set => Link(5, value); + } + + public bool IsGeneric + { + get => this.ReadDataBool(288); + set => this.WriteData(288, value); + } + + public ListOfStructsSerializer NestedNodes + { + get => BuildPointer>(1); + set => Link(1, value); + } + + public ListOfStructsSerializer Annotations + { + get => BuildPointer>(2); + set => Link(2, value); + } + + public ref ushort Tag => ref this.RefData(6); + + public bool IsFile + { + get => Tag == 0; + set => Tag = 0; + } + + public bool IsStruct + { + get => Tag == 1; + set => Tag = 1; + } + + public ref ushort Struct_DataWordCount => ref this.RefData(7); + + public ref ushort Struct_PointerCount => ref this.RefData(12); + + public ref ElementSize Struct_PreferredListEncoding => ref this.RefData(13); + + public bool Struct_IsGroup + { + get => IsStruct ? this.ReadDataBool(224) : default; + set => this.WriteData(224, value); + } + + public ref ushort Struct_DiscriminantCount => ref this.RefData(15); + + public ref uint Struct_DiscriminantOffset => ref this.RefData(8); + + public ListOfStructsSerializer Fields + { + get => BuildPointer>(3); + set => Link(3, value); + } + + public bool IsEnum + { + get => Tag == 2; + set => Tag = 2; + } + + public ListOfStructsSerializer Enumerants + { + get => BuildPointer>(3); + set => Link(3, value); + } + + public bool IsInterface + { + get => Tag == 3; + set => Tag = 3; + } + + public ListOfStructsSerializer Interface_Methods + { + get => BuildPointer>(3); + set => Link(3, value); + } + + public ListOfStructsSerializer Interface_Superclasses + { + get => IsInterface ? BuildPointer>(4) : default; + set => Link(4, value); + } + + public bool IsConst + { + get => Tag == 4; + set => Tag = 4; + } + + public Type.Writer Const_Type + { + get => IsConst ? BuildPointer(3) : default; + set => Link(3, value); + } + + public Value.Writer Const_Value + { + get => IsConst ? BuildPointer(4) : default; + set => Link(4, value); + } + + public bool IsAnnotation + { + get => Tag == 5; + set => Tag = 5; + } + + public Type.Writer Annotation_Type + { + get => IsAnnotation ? BuildPointer(3) : default; + set => Link(3, value); + } + + public bool Annotation_TargetsFile + { + get => IsAnnotation ? this.ReadDataBool(112) : default; + set => this.WriteData(112, value); + } + + public bool Annotation_TargetsConst + { + get => IsAnnotation ? this.ReadDataBool(113) : default; + set => this.WriteData(113, value); + } + + public bool Annotation_TargetsEnum + { + get => IsAnnotation ? this.ReadDataBool(114) : default; + set => this.WriteData(114, value); + } + + public bool Annotation_TargetsEnumerant + { + get => IsAnnotation ? this.ReadDataBool(115) : default; + set => this.WriteData(115, value); + } + + public bool Annotation_TargetsStruct + { + get => IsAnnotation ? this.ReadDataBool(116) : default; + set => this.WriteData(116, value); + } + + public bool Annotation_TargetsField + { + get => IsAnnotation ? this.ReadDataBool(117) : default; + set => this.WriteData(117, value); + } + + public bool Annotation_TargetsUnion + { + get => IsAnnotation ? this.ReadDataBool(118) : default; + set => this.WriteData(118, value); + } + + public bool Annotation_TargetsGroup + { + get => IsAnnotation ? this.ReadDataBool(119) : default; + set => this.WriteData(119, value); + } + + public bool Annotation_TargetsInterface + { + get => IsAnnotation ? this.ReadDataBool(120) : default; + set => this.WriteData(120, value); + } + + public bool Annotation_TargetsMethod + { + get => IsAnnotation ? this.ReadDataBool(121) : default; + set => this.WriteData(121, value); + } + + public bool Annotation_TargetsParam + { + get => IsAnnotation ? this.ReadDataBool(122) : default; + set => this.WriteData(122, value); + } + + public bool Annotation_TargetsAnnotation + { + get => IsAnnotation ? this.ReadDataBool(123) : default; + set => this.WriteData(123, value); + } + } + } + + namespace CapnpVersion + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ushort Major => State.ReadDataUShort(0); + public byte Minor => State.ReadDataByte(16); + public byte Micro => State.ReadDataByte(24); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 0); + } + + public ref ushort Major => ref this.RefData(0); + public ref byte Minor => ref this.RefData(2); + public ref byte Micro => ref this.RefData(3); + } + } + + namespace CodeGeneratorRequest + { + namespace RequestedFile + { + namespace Import + { + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ulong Id => State.ReadDataULong(0); + public string Name => State.ReadText(0); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 1); + } + + public ref ulong Id => ref this.RefData(0); + + public string Name + { + get => ReadText(0); + set => WriteText(0, value); + } + } + } + + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public ulong Id => State.ReadDataULong(0); + public string Filename => State.ReadText(0); + public IReadOnlyList Imports => State.ReadListOfStructs(1, Import.Reader.Create); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(1, 2); + } + + public ref ulong Id => ref this.RefData(0); + + public string Filename + { + get => ReadText(0); + set => WriteText(0, value); + } + + public ListOfStructsSerializer Imports + { + get => BuildPointer>(1); + set => Link(1, value); + } + } + } + + public struct Reader + { + public DeserializerState State { get; } + + public Reader(DeserializerState ctx) + { + State = ctx; + } + + public static Reader Create(DeserializerState ctx) => new Reader(ctx); + + public CapnpVersion.Reader CapnpVersion => State.ReadStruct(2, Schema.CapnpVersion.Reader.Create); + public IReadOnlyList Nodes => State.ReadListOfStructs(0, Node.Reader.Create); + public IReadOnlyList SourceInfo => State.ReadListOfStructs(3, Node.SourceInfo.Reader.Create); + public IReadOnlyList RequestedFiles => State.ReadListOfStructs(1, RequestedFile.Reader.Create); + } + + public class Writer: SerializerState + { + public Writer() + { + SetStruct(0, 3); + } + + public CapnpVersion.Writer CapnpVersion + { + get => BuildPointer(2); + set => Link(2, value); + } + + public ListOfStructsSerializer Nodes + { + get => BuildPointer>(0); + set => Link(0, value); + } + + public ListOfStructsSerializer SourceInfo + { + get => BuildPointer>(3); + set => Link(3, value); + } + + public ListOfStructsSerializer RequestedFiles + { + get => BuildPointer>(1); + set => Link(1, value); + } + } + } +} diff --git a/CapnpC.CSharp.Generator/Schema/schema-with-offsets.capnp b/CapnpC.CSharp.Generator/Schema/schema-with-offsets.capnp new file mode 100644 index 0000000..45fe760 --- /dev/null +++ b/CapnpC.CSharp.Generator/Schema/schema-with-offsets.capnp @@ -0,0 +1,235 @@ +# schema.capnp +@0xa93fc509624c72d9; +$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] + displayNamePrefixLength @2 :UInt32; # bits[64, 96) + scopeId @3 :UInt64; # bits[128, 192) + parameters @32 :List(Parameter); # ptr[5] + isGeneric @33 :Bool; # bits[288, 289) + nestedNodes @4 :List(NestedNode); # ptr[1] + annotations @5 :List(Annotation); # ptr[2] + union { # tag bits [96, 112) + file @6 :Void; # bits[0, 0), union tag = 0 + struct :group { # union tag = 1 + dataWordCount @7 :UInt16; # bits[112, 128) + pointerCount @8 :UInt16; # bits[192, 208) + preferredListEncoding @9 :ElementSize; # bits[208, 224) + isGroup @10 :Bool; # bits[224, 225) + discriminantCount @11 :UInt16; # bits[240, 256) + discriminantOffset @12 :UInt32; # bits[256, 288) + fields @13 :List(Field); # ptr[3] + } + enum :group { # union tag = 2 + enumerants @14 :List(Enumerant); # ptr[3] + } + interface :group { # union tag = 3 + methods @15 :List(Method); # ptr[3] + superclasses @31 :List(Superclass); # ptr[4] + } + const :group { # union tag = 4 + type @16 :Type; # ptr[3] + value @17 :Value; # ptr[4] + } + annotation :group { # union tag = 5 + type @18 :Type; # ptr[3] + targetsFile @19 :Bool; # bits[112, 113) + targetsConst @20 :Bool; # bits[113, 114) + targetsEnum @21 :Bool; # bits[114, 115) + targetsEnumerant @22 :Bool; # bits[115, 116) + targetsStruct @23 :Bool; # bits[116, 117) + targetsField @24 :Bool; # bits[117, 118) + targetsUnion @25 :Bool; # bits[118, 119) + targetsGroup @26 :Bool; # bits[119, 120) + targetsInterface @27 :Bool; # bits[120, 121) + targetsMethod @28 :Bool; # bits[121, 122) + targetsParam @29 :Bool; # bits[122, 123) + targetsAnnotation @30 :Bool; # bits[123, 124) + } + } + struct Parameter @0xb9521bccf10fa3b1 { # 0 bytes, 1 ptrs + name @0 :Text; # ptr[0] + } + struct NestedNode @0xdebf55bbfa0fc242 { # 8 bytes, 1 ptrs + name @0 :Text; # ptr[0] + id @1 :UInt64; # bits[0, 64) + } + struct SourceInfo @0xf38e1de3041357ae { # 8 bytes, 2 ptrs + id @0 :UInt64; # bits[0, 64) + docComment @1 :Text; # ptr[0] + members @2 :List(Member); # ptr[1] + struct Member @0xc2ba9038898e1fa2 { # 0 bytes, 1 ptrs + docComment @0 :Text; # ptr[0] + } + } +} +struct Field @0x9aad50a41f4af45f { # 24 bytes, 4 ptrs + name @0 :Text; # ptr[0] + codeOrder @1 :UInt16; # bits[0, 16) + annotations @2 :List(Annotation); # ptr[1] + discriminantValue @3 :UInt16 = 65535; # bits[16, 32) + union { # tag bits [64, 80) + slot :group { # union tag = 0 + offset @4 :UInt32; # bits[32, 64) + type @5 :Type; # ptr[2] + defaultValue @6 :Value; # ptr[3] + hadExplicitDefault @10 :Bool; # bits[128, 129) + } + group :group { # union tag = 1 + typeId @7 :UInt64; # bits[128, 192) + } + } + ordinal :group { + union { # tag bits [80, 96) + implicit @8 :Void; # bits[0, 0), union tag = 0 + explicit @9 :UInt16; # bits[96, 112), union tag = 1 + } + } + const noDiscriminant @0x97b14cbe7cfec712 :UInt16 = 65535; +} +struct Enumerant @0x978a7cebdc549a4d { # 8 bytes, 2 ptrs + name @0 :Text; # ptr[0] + codeOrder @1 :UInt16; # bits[0, 16) + annotations @2 :List(Annotation); # ptr[1] +} +struct Superclass @0xa9962a9ed0a4d7f8 { # 8 bytes, 1 ptrs + id @0 :UInt64; # bits[0, 64) + brand @1 :Brand; # ptr[0] +} +struct Method @0x9500cce23b334d80 { # 24 bytes, 5 ptrs + name @0 :Text; # ptr[0] + codeOrder @1 :UInt16; # bits[0, 16) + implicitParameters @7 :List(Node.Parameter); # ptr[4] + paramStructType @2 :UInt64; # bits[64, 128) + paramBrand @5 :Brand; # ptr[2] + resultStructType @3 :UInt64; # bits[128, 192) + resultBrand @6 :Brand; # ptr[3] + annotations @4 :List(Annotation); # ptr[1] +} +struct Type @0xd07378ede1f9cc60 { # 24 bytes, 1 ptrs + union { # tag bits [0, 16) + void @0 :Void; # bits[0, 0), union tag = 0 + bool @1 :Void; # bits[0, 0), union tag = 1 + int8 @2 :Void; # bits[0, 0), union tag = 2 + int16 @3 :Void; # bits[0, 0), union tag = 3 + int32 @4 :Void; # bits[0, 0), union tag = 4 + int64 @5 :Void; # bits[0, 0), union tag = 5 + uint8 @6 :Void; # bits[0, 0), union tag = 6 + uint16 @7 :Void; # bits[0, 0), union tag = 7 + uint32 @8 :Void; # bits[0, 0), union tag = 8 + uint64 @9 :Void; # bits[0, 0), union tag = 9 + float32 @10 :Void; # bits[0, 0), union tag = 10 + float64 @11 :Void; # bits[0, 0), union tag = 11 + text @12 :Void; # bits[0, 0), union tag = 12 + data @13 :Void; # bits[0, 0), union tag = 13 + list :group { # union tag = 14 + elementType @14 :Type; # ptr[0] + } + enum :group { # union tag = 15 + typeId @15 :UInt64; # bits[64, 128) + brand @21 :Brand; # ptr[0] + } + struct :group { # union tag = 16 + typeId @16 :UInt64; # bits[64, 128) + brand @22 :Brand; # ptr[0] + } + interface :group { # union tag = 17 + typeId @17 :UInt64; # bits[64, 128) + brand @23 :Brand; # ptr[0] + } + anyPointer :group { # union tag = 18 + union { # tag bits [64, 80) + unconstrained :group { # union tag = 0 + union { # tag bits [80, 96) + anyKind @18 :Void; # bits[0, 0), union tag = 0 + struct @25 :Void; # bits[0, 0), union tag = 1 + list @26 :Void; # bits[0, 0), union tag = 2 + capability @27 :Void; # bits[0, 0), union tag = 3 + } + } + parameter :group { # union tag = 1 + scopeId @19 :UInt64; # bits[128, 192) + parameterIndex @20 :UInt16; # bits[80, 96) + } + implicitMethodParameter :group { # union tag = 2 + parameterIndex @24 :UInt16; # bits[80, 96) + } + } + } + } +} +struct Brand @0x903455f06065422b { # 0 bytes, 1 ptrs + scopes @0 :List(Scope); # ptr[0] + struct Scope @0xabd73485a9636bc9 { # 16 bytes, 1 ptrs + scopeId @0 :UInt64; # bits[0, 64) + union { # tag bits [64, 80) + bind @1 :List(Binding); # ptr[0], union tag = 0 + inherit @2 :Void; # bits[0, 0), union tag = 1 + } + } + struct Binding @0xc863cd16969ee7fc { # 8 bytes, 1 ptrs + union { # tag bits [0, 16) + unbound @0 :Void; # bits[0, 0), union tag = 0 + type @1 :Type; # ptr[0], union tag = 1 + } + } +} +struct Value @0xce23dcd2d7b00c9b { # 16 bytes, 1 ptrs + union { # tag bits [0, 16) + void @0 :Void; # bits[0, 0), union tag = 0 + bool @1 :Bool; # bits[16, 17), union tag = 1 + int8 @2 :Int8; # bits[16, 24), union tag = 2 + int16 @3 :Int16; # bits[16, 32), union tag = 3 + int32 @4 :Int32; # bits[32, 64), union tag = 4 + int64 @5 :Int64; # bits[64, 128), union tag = 5 + uint8 @6 :UInt8; # bits[16, 24), union tag = 6 + uint16 @7 :UInt16; # bits[16, 32), union tag = 7 + uint32 @8 :UInt32; # bits[32, 64), union tag = 8 + uint64 @9 :UInt64; # bits[64, 128), union tag = 9 + float32 @10 :Float32; # bits[32, 64), union tag = 10 + float64 @11 :Float64; # bits[64, 128), union tag = 11 + text @12 :Text; # ptr[0], union tag = 12 + data @13 :Data; # ptr[0], union tag = 13 + list @14 :AnyPointer; # ptr[0], union tag = 14 + enum @15 :UInt16; # bits[16, 32), union tag = 15 + struct @16 :AnyPointer; # ptr[0], union tag = 16 + interface @17 :Void; # bits[0, 0), union tag = 17 + anyPointer @18 :AnyPointer; # ptr[0], union tag = 18 + } +} +struct Annotation @0xf1c8950dab257542 { # 8 bytes, 2 ptrs + id @0 :UInt64; # bits[0, 64) + brand @2 :Brand; # ptr[1] + value @1 :Value; # ptr[0] +} +enum ElementSize @0xd1958f7dba521926 { + empty @0; + bit @1; + byte @2; + twoBytes @3; + fourBytes @4; + eightBytes @5; + pointer @6; + inlineComposite @7; +} +struct CapnpVersion @0xd85d305b7d839963 { # 8 bytes, 0 ptrs + major @0 :UInt16; # bits[0, 16) + minor @1 :UInt8; # bits[16, 24) + micro @2 :UInt8; # bits[24, 32) +} +struct CodeGeneratorRequest @0xbfc546f6210ad7ce { # 0 bytes, 4 ptrs + capnpVersion @2 :CapnpVersion; # ptr[2] + nodes @0 :List(Node); # ptr[0] + sourceInfo @3 :List(Node.SourceInfo); # ptr[3] + requestedFiles @1 :List(RequestedFile); # ptr[1] + struct RequestedFile @0xcfea0eb02e810062 { # 8 bytes, 2 ptrs + id @0 :UInt64; # bits[0, 64) + filename @1 :Text; # ptr[0] + imports @2 :List(Import); # ptr[1] + struct Import @0xae504193122357e5 { # 8 bytes, 1 ptrs + id @0 :UInt64; # bits[0, 64) + name @1 :Text; # ptr[0] + } + } +} diff --git a/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs index ff6d30e..6802c12 100644 --- a/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs +++ b/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs @@ -21,8 +21,10 @@ namespace Capnpc.Csharp.MsBuild.Generation public IEnumerable GenerateFilesForProject( string projectPath, - List capnpFiles, - string projectFolder) + List capnpFiles, + string projectFolder, + string workingDirectory, + string additionalOptions) { using (var capnpCodeBehindGenerator = new CapnpCodeBehindGenerator()) { diff --git a/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs b/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs index c120448..b4b9269 100644 --- a/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs +++ b/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs @@ -25,6 +25,10 @@ namespace Capnpc.Csharp.MsBuild.Generation public ITaskItem[] CapnpFiles { get; set; } + public string WorkingDirectory { get; set; } + + public string AdditionalOptions { get; set; } + [Output] public ITaskItem[] GeneratedFiles { get; private set; } @@ -59,7 +63,9 @@ namespace Capnpc.Csharp.MsBuild.Generation var generatedFiles = generator.GenerateFilesForProject( ProjectPath, capnpFiles, - ProjectFolder); + ProjectFolder, + WorkingDirectory, + AdditionalOptions); GeneratedFiles = generatedFiles.Select(file => new TaskItem { ItemSpec = file }).ToArray(); diff --git a/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs index 4213d8d..d55ed1e 100644 --- a/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs +++ b/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs @@ -4,6 +4,6 @@ namespace Capnpc.Csharp.MsBuild.Generation { public interface ICapnpcCsharpGenerator { - IEnumerable GenerateFilesForProject(string projectPath, List capnpFiles, string projectFolder); + IEnumerable GenerateFilesForProject(string projectPath, List capnpFiles, string projectFolder, string workingDirectory, string additionalOptions); } } \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml index 51e0ab0..ec90cbe 100644 --- a/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml +++ b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml @@ -25,10 +25,19 @@ - + + + + + + + + + + \ No newline at end of file