Factoring out capnpc-csharp into separate assembly

This commit is contained in:
Christian Köllner 2019-09-06 19:25:54 +02:00
parent 6b81abb0f0
commit 107d10e3f4
46 changed files with 7890 additions and 5 deletions

View File

@ -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

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.2.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Capnp.Net.Runtime\Capnp.Net.Runtime.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,49 @@
using Capnp;
using System;
using System.Diagnostics;
using System.IO;
namespace CapnpC.CSharp.Generator
{
/// <summary>
/// Provides methods for controlling both the C# code generator backend and the frontend "capnpc"
/// </summary>
public static class CapnpCompilation
{
/// <summary>
/// Generates C# code from given input stream
/// </summary>
/// <param name="input">input stream containing the binary code generation request, which the frontend capnpc emits</param>
/// <returns>generation result</returns>
/// <exception cref="ArgumentNullException">if <paramref name="input"/> is null</exception>
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()
{
}
}
}

View File

@ -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<MemberDeclarationSyntax> TransformEnum(TypeDefinition def)
{
yield return _commonGen.MakeEnum(def);
}
IEnumerable<TypeParameterSyntax> MakeTypeParameters(TypeDefinition def)
{
foreach (string name in def.GenericParameters)
{
yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier);
}
}
IEnumerable<TypeParameterConstraintClauseSyntax> MakeTypeParameterConstraints(TypeDefinition def)
{
foreach (string name in def.GenericParameters)
{
yield return TypeParameterConstraintClause(
_names.GetGenericTypeParameter(name).IdentifierName)
.AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint));
}
}
IEnumerable<MemberDeclarationSyntax> TransformStruct(TypeDefinition def)
{
var topDecl = ClassDeclaration(_names.MakeTypeName(def).Identifier)
.AddModifiers(Public)
.AddBaseListTypes(SimpleBaseType(Type<Capnp.ICapnpSerializable>()));
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<MemberDeclarationSyntax> 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<MemberDeclarationSyntax> 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<FileGenerationResult> Generate()
{
var result = new List<FileGenerationResult>();
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;
}
}
}

View File

@ -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<ushort>()));
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<ushort>()));
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<SyntaxNodeOrToken> MakeCommaSeparatedList(IEnumerable<ExpressionSyntax> expressions)
{
bool first = true;
foreach (var expr in expressions)
{
if (first)
first = false;
else
yield return Token(SyntaxKind.CommaToken);
yield return expr;
}
}
}
}

View File

@ -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<object>())
.WithVariables(
SingletonSeparatedList<VariableDeclaratorSyntax>(
VariableDeclarator(_names.UnionContentField.Identifier))))
.AddModifiers(Private);
}
IEnumerable<ExpressionSyntax> 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<ArrayRankSpecifierSyntax>(
ArrayRankSpecifier(
SingletonSeparatedList<ExpressionSyntax>(
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<ArrayRankSpecifierSyntax>(
ArrayRankSpecifier(
SingletonSeparatedList<ExpressionSyntax>(
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<SwitchSectionSyntax> MakeUnionDiscriminatorSetter(TypeDefinition def)
{
var unionFields = def.Fields.Where(f => f.DiscValue.HasValue);
foreach (var unionField in unionFields)
{
var section = SwitchSection()
.WithLabels(
SingletonList<SwitchLabelSyntax>(
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<int>.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<int>.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<int>.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<object>(), 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<SwitchSectionSyntax> 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<int>.Value)));
}
section = section.AddStatements(MakeSerializeMethodFieldAssignment(unionField));
}
section = section.AddStatements(BreakStatement());
yield return section;
}
}
IEnumerable<SwitchSectionSyntax> 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<StatementSyntax> 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<StatementSyntax> 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<Capnp.SerializerState>()))
.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<StatementSyntax> 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<StatementSyntax>();
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<Capnp.DeserializerState>()))
.AddBodyStatements(stmts.ToArray());
}
IEnumerable<MemberDeclarationSyntax> 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();
}
}
}

View File

@ -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<Field, Name> _fieldNameMap = new Dictionary<Field, Name>();
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<TypeDefinition>();
//while (scope != null)
//{
// scopeSet.Add(scope);
// scope = scope.DeclaringElement as TypeDefinition;
//}
if (type.Definition != null)
{
var stack = new Stack<SimpleNameSyntax>();
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<Capnp.ListOfPointersSerializer<Capnp.DynamicSerializerState>>();
case TypeTag.CapabilityPointer:
return SyntaxHelpers.Type<Capnp.ListOfCapsSerializer<Capnp.Rpc.BareProxy>>();
case TypeTag.Data:
return SyntaxHelpers.Type<Capnp.ListOfPointersSerializer<
Capnp.ListOfPrimitivesSerializer<byte>>>();
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<Capnp.ListOfTextSerializer>();
case TypeTag.Void:
return SyntaxHelpers.Type<Capnp.ListOfEmptySerializer>();
case TypeTag.Bool:
return SyntaxHelpers.Type<Capnp.ListOfBitsSerializer>();
case TypeTag.F32:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<float>>();
case TypeTag.F64:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<double>>();
case TypeTag.S8:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<sbyte>>();
case TypeTag.U8:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<byte>>();
case TypeTag.S16:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<short>>();
case TypeTag.U16:
case TypeTag.AnyEnum:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<ushort>>();
case TypeTag.S32:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<int>>();
case TypeTag.U32:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<uint>>();
case TypeTag.S64:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<long>>();
case TypeTag.U64:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<ulong>>();
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<ushort>(), usage);
case TypeTag.CapabilityPointer:
if (type.Parameter != null)
{
return GetQName(type, scope);
}
else
{
return SyntaxHelpers.Type<Capnp.Rpc.BareProxy>();
}
case TypeTag.AnyPointer:
case TypeTag.StructPointer:
switch (usage)
{
case TypeUsage.Reader:
return SyntaxHelpers.Type<Capnp.DeserializerState>();
case TypeUsage.Writer:
return SyntaxHelpers.Type<Capnp.DynamicSerializerState>();
case TypeUsage.DomainClass:
case TypeUsage.DomainClassNullable:
if (type.Parameter != null)
{
return GetQName(type, scope);
}
else
{
return SyntaxHelpers.Type<Capnp.AnyPointer>();
}
default:
throw new NotImplementedException();
}
case TypeTag.Bool:
return MaybeNullableValueType(SyntaxHelpers.Type<bool>(), usage);
case TypeTag.Data:
switch (usage)
{
case TypeUsage.Reader:
case TypeUsage.DomainClass:
case TypeUsage.DomainClassNullable:
return SyntaxHelpers.Type<IReadOnlyList<byte>>();
case TypeUsage.Writer:
return SyntaxHelpers.Type<Capnp.ListOfPrimitivesSerializer<byte>>();
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<float>(), usage);
case TypeTag.F64:
return MaybeNullableValueType(SyntaxHelpers.Type<double>(), usage);
case TypeTag.List when type.ElementType.Tag == TypeTag.Void && usage != TypeUsage.Writer:
return MaybeNullableValueType(SyntaxHelpers.Type<int>(), 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<Capnp.SerializerState>();
case TypeUsage.Reader:
return SyntaxHelpers.Type<IReadOnlyList<Capnp.DeserializerState>>();
case TypeUsage.DomainClass:
case TypeUsage.DomainClassNullable:
return SyntaxHelpers.Type<IReadOnlyList<object>>();
default:
throw new NotImplementedException();
}
case TypeTag.S16:
return MaybeNullableValueType(SyntaxHelpers.Type<short>(), usage);
case TypeTag.S32:
return MaybeNullableValueType(SyntaxHelpers.Type<int>(), usage);
case TypeTag.S64:
return MaybeNullableValueType(SyntaxHelpers.Type<long>(), usage);
case TypeTag.S8:
return MaybeNullableValueType(SyntaxHelpers.Type<sbyte>(), usage);
case TypeTag.Text:
return SyntaxHelpers.Type<string>();
case TypeTag.U16:
return MaybeNullableValueType(SyntaxHelpers.Type<ushort>(), usage);
case TypeTag.U32:
return MaybeNullableValueType(SyntaxHelpers.Type<uint>(), usage);
case TypeTag.U64:
return MaybeNullableValueType(SyntaxHelpers.Type<ulong>(), usage);
case TypeTag.U8:
return MaybeNullableValueType(SyntaxHelpers.Type<byte>(), 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<Name>(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<Field> 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<Field> path)
{
return new Name(string.Format(MemberAccessPathNameFormat,
method.Name,
MakePipeliningSupportExtensionMethodName(path)));
}
}
}

View File

@ -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";
}
}

View File

@ -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<ParameterSyntax>();
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<TypeParameterSyntax> MakeTypeParameters(TypeDefinition def)
{
foreach (string name in def.GenericParameters)
{
yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier);
}
}
IEnumerable<TypeParameterConstraintClauseSyntax> 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<Model.Type> 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<Model.Type>()));
}
IEnumerable<ExpressionSyntax> MakeProxyCallInitializerAssignments(Method method)
{
foreach (var methodParam in method.Params)
{
yield return AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
_names.GetCodeIdentifier(methodParam).IdentifierName,
IdentifierName(methodParam.Name));
}
}
IEnumerable<ArgumentSyntax> 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<TypeParameterSyntax> MakeTypeParameters(Method method)
{
foreach (string name in method.GenericParameters)
{
yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier);
}
}
IEnumerable<TypeParameterConstraintClauseSyntax> 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<Capnp.Rpc.Proxy>()),
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<StatementSyntax>();
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<VariableDeclaratorSyntax>(
VariableDeclarator(
_names.AnonymousParameter.Identifier)
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(
_names.MakeTypeSyntax(
method.ParamsStruct,
method.ParamsStruct.Definition,
TypeUsage.DomainClass))
.WithArgumentList(
ArgumentList())
.WithInitializer(
InitializerExpression(
SyntaxKind.ObjectInitializerExpression,
SeparatedList<ExpressionSyntax>(
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<Capnp.DynamicSerializerState>())))
.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<ArgumentSyntax> 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<Capnp.AnyPointer>(),
method.GenericParameters.Count).ToArray()));
}
else
{
yield return Argument(_names.GetCodeIdentifier(method).IdentifierName);
}
}
}
IEnumerable<ExpressionSyntax> MakeSkeletonMethodResultStructInitializer(Method method)
{
foreach (var arg in method.Results)
{
yield return AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
_names.GetCodeIdentifier(arg).IdentifierName,
IdentifierName(arg.Name));
}
}
IEnumerable<ArgumentSyntax> 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<StatementSyntax> 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<MemberDeclarationSyntax> MakeSkeletonMethods(TypeDefinition def)
{
foreach (var method in def.Methods)
{
var methodDecl = MethodDeclaration(
Type<Task<Capnp.Rpc.AnswerOrCounterquestion>>(),
_names.GetCodeIdentifier(method).Identifier)
.AddParameterListParameters(
Parameter(_names.DeserializerLocal.Identifier)
.WithType(Type<Capnp.DeserializerState>()),
Parameter(_names.CancellationTokenParameter.Identifier)
.WithType(Type<CancellationToken>()))
.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<ulong>(), nameof(Capnp.Rpc.Skeleton<object>.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<IReadOnlyList<Field>> ExpandPipeliningPaths(Method method)
{
var stack = new Stack<List<Field>>();
foreach (var field in method.ResultStruct.Fields)
{
stack.Push(new List<Field>() { 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<Field>();
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;
}
}
}

View File

@ -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();
}
}

View File

@ -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<ParameterSyntax>(
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<ParameterSyntax>(
Parameter(_names.ReaderContextField.Identifier)
.WithType(Type<Capnp.DeserializerState>()))))
.WithExpressionBody(
ArrowExpressionClause(
ObjectCreationExpression(_names.ReaderStruct.IdentifierName)
.WithArgumentList(
ArgumentList(
SingletonSeparatedList<ArgumentSyntax>(
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<Capnp.DeserializerState>()))))
.WithExpressionBody(
ArrowExpressionClause(
ObjectCreationExpression(_names.ReaderStruct.IdentifierName)
.AddArgumentListArguments(Argument(_names.ContextParameter.IdentifierName))))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
}
IEnumerable<MemberDeclarationSyntax> MakeReaderStructMembers()
{
yield return FieldDeclaration(
VariableDeclaration(
Type<Capnp.DeserializerState>())
.AddVariables(_names.ReaderContextField.VariableDeclarator))
.AddModifiers(Readonly);
yield return ConstructorDeclaration(_names.ReaderStruct.Identifier)
.AddModifiers(Public)
.WithParameterList(
ParameterList(
SingletonSeparatedList(
Parameter(_names.ContextParameter.Identifier)
.WithType(Type<Capnp.DeserializerState>()))))
.WithBody(
Block(
SingletonList<StatementSyntax>(
ExpressionStatement(
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
ThisExpression(),
_names.ReaderContextField.IdentifierName),
_names.ContextParameter.IdentifierName)))));
yield return MakeReaderCreateMethod();
yield return MakeReaderImplicitConversionOperator1();
yield return MakeReaderImplicitConversionOperator2();
}
IEnumerable<MemberDeclarationSyntax> MakeGroupReaderStructMembers()
{
yield return FieldDeclaration(
VariableDeclaration(
Type<Capnp.DeserializerState>())
.AddVariables(_names.ReaderContextField.VariableDeclarator))
.AddModifiers(Readonly);
yield return ConstructorDeclaration(_names.ReaderStruct.Identifier)
.AddModifiers(Public)
.WithParameterList(
ParameterList(
SingletonSeparatedList(Parameter(_names.GroupReaderContextArg.Identifier)
.WithType(Type<Capnp.DeserializerState>()))))
.WithBody(
Block(
SingletonList<StatementSyntax>(
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<ExpressionSyntax, ExpressionSyntax> MakeCastFunc(TypeSyntax type) =>
x => CastExpression(type, x);
static Func<ExpressionSyntax, ExpressionSyntax> 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<ExpressionSyntax, ExpressionSyntax> MakeListCastFuncWithCons(
string castName, ExpressionSyntax cons)
{
return x => InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
x,
IdentifierName(castName))
).AddArgumentListArguments(Argument(cons));
}
static Func<ExpressionSyntax, ExpressionSyntax> 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<ExpressionSyntax, ExpressionSyntax> 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<ExpressionSyntax, ExpressionSyntax> cast, bool cond)
{
return MakeReadProperty(type, name, IdentifierName(readName), indexOrBitOffset, secondArg, cast, cond);
}
PropertyDeclarationSyntax MakeReadPrimitiveProperty<T>(Field field, string readName)
{
return MakeReadProperty(Type<T>(), _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<string>(), _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<IReadOnlyList<object>>();
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<IReadOnlyList<Capnp.DeserializerState>>();
impl = context;
return;
}
case TypeTag.Void:
{
listType = Type<int>();
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<bool>(field,
nameof(Capnp.SerializerExtensions.ReadDataBool));
case TypeTag.S8:
return MakeReadPrimitiveProperty<sbyte>(field,
nameof(Capnp.SerializerExtensions.ReadDataSByte));
case TypeTag.U8:
return MakeReadPrimitiveProperty<byte>(field,
nameof(Capnp.SerializerExtensions.ReadDataByte));
case TypeTag.S16:
return MakeReadPrimitiveProperty<short>(field,
nameof(Capnp.SerializerExtensions.ReadDataShort));
case TypeTag.U16:
return MakeReadPrimitiveProperty<ushort>(field,
nameof(Capnp.SerializerExtensions.ReadDataUShort));
case TypeTag.S32:
return MakeReadPrimitiveProperty<int>(field,
nameof(Capnp.SerializerExtensions.ReadDataInt));
case TypeTag.U32:
return MakeReadPrimitiveProperty<uint>(field,
nameof(Capnp.SerializerExtensions.ReadDataUInt));
case TypeTag.S64:
return MakeReadPrimitiveProperty<long>(field,
nameof(Capnp.SerializerExtensions.ReadDataLong));
case TypeTag.U64:
return MakeReadPrimitiveProperty<ulong>(field,
nameof(Capnp.SerializerExtensions.ReadDataULong));
case TypeTag.F32:
return MakeReadPrimitiveProperty<float>(field,
nameof(Capnp.SerializerExtensions.ReadDataFloat));
case TypeTag.F64:
return MakeReadPrimitiveProperty<double>(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;
}
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace CapnpC.CSharp.Generator.CodeGen
{
class SkeletonWorder : Capnp.Rpc.Skeleton<object>
{
public override ulong InterfaceId => throw new NotImplementedException();
public const string SetMethodTableName = nameof(SkeletonWorder.SetMethodTable);
public const string ImplName = nameof(SkeletonWorder.Impl);
}
}

View File

@ -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<T>() => 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<sbyte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
case byte x:
return CastExpression(Type<byte>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
case short x:
return CastExpression(Type<short>(), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(x)));
case ushort x:
return CastExpression(Type<ushort>(), 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();
}
}
}
}

View File

@ -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<MemberDeclarationSyntax> 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<MemberDeclarationSyntax> 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<T>(Field field, string readName)
{
return MakeProperty(Type<T>(), 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<ushort>(),
_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<string>(), 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<ushort>(),
_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<bool>(field,
nameof(Capnp.SerializerExtensions.ReadDataBool));
case TypeTag.S8:
return MakePrimitiveProperty<sbyte>(field,
nameof(Capnp.SerializerExtensions.ReadDataSByte));
case TypeTag.U8:
return MakePrimitiveProperty<byte>(field,
nameof(Capnp.SerializerExtensions.ReadDataByte));
case TypeTag.S16:
return MakePrimitiveProperty<short>(field,
nameof(Capnp.SerializerExtensions.ReadDataShort));
case TypeTag.U16:
return MakePrimitiveProperty<ushort>(field,
nameof(Capnp.SerializerExtensions.ReadDataUShort));
case TypeTag.S32:
return MakePrimitiveProperty<int>(field,
nameof(Capnp.SerializerExtensions.ReadDataInt));
case TypeTag.U32:
return MakePrimitiveProperty<uint>(field,
nameof(Capnp.SerializerExtensions.ReadDataUInt));
case TypeTag.S64:
return MakePrimitiveProperty<long>(field,
nameof(Capnp.SerializerExtensions.ReadDataLong));
case TypeTag.U64:
return MakePrimitiveProperty<ulong>(field,
nameof(Capnp.SerializerExtensions.ReadDataULong));
case TypeTag.F32:
return MakePrimitiveProperty<float>(field,
nameof(Capnp.SerializerExtensions.ReadDataFloat));
case TypeTag.F64:
return MakePrimitiveProperty<double>(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;
}
}
}

View File

@ -0,0 +1,52 @@
using System;
namespace CapnpC.CSharp.Generator
{
/// <summary>
/// Represents the generation result of a single .capnp file
/// </summary>
public class FileGenerationResult
{
/// <summary>
/// Constructs an instance in case of successful generation
/// </summary>
/// <param name="capnpFilePath">path to .capnp file</param>
/// <param name="generatedContent">generated C# code</param>
public FileGenerationResult(string capnpFilePath, string generatedContent)
{
CapnpFilePath = capnpFilePath;
GeneratedContent = generatedContent;
}
/// <summary>
/// Constructs an instance in case of unsuccessful generation
/// </summary>
/// <param name="capnpFilePath">path to .capnp file</param>
/// <param name="exception">Exception giving details on the error which prevented generation</param>
public FileGenerationResult(string capnpFilePath, Exception exception)
{
CapnpFilePath = capnpFilePath;
Exception = exception;
}
/// <summary>
/// Path to .capnp file
/// </summary>
public string CapnpFilePath { get; }
/// <summary>
/// Generated C# or null if generation failed
/// </summary>
public string GeneratedContent { get; }
/// <summary>
/// Exception giving details on the error which prevented generation
/// </summary>
public Exception Exception { get; }
/// <summary>
/// true iff generation was successful
/// </summary>
public bool IsSuccess => !string.IsNullOrEmpty(GeneratedContent);
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
namespace CapnpC.CSharp.Generator
{
/// <summary>
/// Represents a .capnp -> code generator result
/// </summary>
public class GenerationResult
{
/// <summary>
/// Constructs an instance in case of at least partially successful generation.
/// </summary>
/// <param name="generatedFiles">Generation result per file to generate</param>
public GenerationResult(IReadOnlyList<FileGenerationResult> generatedFiles)
{
GeneratedFiles = generatedFiles;
}
/// <summary>
/// Constructs an instance in case of total failure.
/// </summary>
/// <param name="exception">Exception with details on error</param>
public GenerationResult(Exception exception)
{
Exception = exception;
}
/// <summary>
/// Generation result per file to generate or null in case of total failure
/// </summary>
public IReadOnlyList<FileGenerationResult> GeneratedFiles { get; }
/// <summary>
/// Exception with details on error or null in case of success
/// </summary>
public Exception Exception { get; }
/// <summary>
/// true iff generation was successful
/// </summary>
public bool IsSuccess => GeneratedFiles != null;
}
}

View File

@ -0,0 +1,45 @@
using System.Collections.Generic;
namespace CapnpC.CSharp.Generator.Model
{
abstract class AbstractType
{
public TypeTag Tag { get; set; }
protected List<Field> Fields { get; } = new List<Field>();
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;
}
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace CapnpC.CSharp.Generator.Model
{
class DefinitionManager
{
readonly Dictionary<ulong, IDefinition> _id2def = new Dictionary<ulong, IDefinition>();
public GenFile CreateFile(ulong id, bool isGenerated)
=> CreateId<GenFile>(id, () => new GenFile(id, isGenerated));
public GenFile GetExistingFile(ulong id)
=> GetId<GenFile>(id, TypeTag.File);
public TypeDefinition CreateTypeDef(ulong id, TypeTag tag, IHasNestedDefinitions decl)
=> CreateId<TypeDefinition>(id, () => new TypeDefinition(tag, id, decl));
public TypeDefinition GetExistingTypeDef(ulong id, TypeTag tag)
{
var def = GetId<TypeDefinition>(id, tag);
if (def.Tag == TypeTag.Unknown) def.Tag = tag;
return def;
}
public Annotation CreateAnnotation(ulong id, IHasNestedDefinitions decl)
=> CreateId<Annotation>(id, () => new Annotation(id, decl));
public Annotation GetExistingAnnotation(ulong id)
=> GetId<Annotation>(id, TypeTag.Annotation);
public Constant CreateConstant(ulong id, IHasNestedDefinitions decl)
=> CreateId<Constant>(id, () => new Constant(id, decl));
public Constant GetExistingConstant(ulong id)
=> GetId<Constant>(id, TypeTag.Const);
public IDefinition GetExistingDef(ulong id, TypeTag tag)
=> GetId<IDefinition>(id, tag);
public IEnumerable<GenFile> Files
{
get => _id2def.Values.Where(d => d.Tag == TypeTag.File).Select(f => f as GenFile);
}
T CreateId<T>(ulong id, Func<IDefinition> 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<T>(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;
}
}
}

View File

@ -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; }
}
}

View File

@ -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();
}
}
}

View File

@ -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<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();
public ICollection<Constant> Constants { get; } = new List<Constant>();
public GenFile(ulong id, bool isGenerated)
{
Id = id;
IsGenerated = isGenerated;
}
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace CapnpC.CSharp.Generator.Model
{
static class HasGenericParameters
{
public static IEnumerable<GenericParameter> GetLocalTypeParameters(this IHasGenericParameters me)
{
for (int i = 0; i < me.GenericParameters.Count; i++)
{
yield return new GenericParameter()
{
DeclaringEntity = me,
Index = i
};
}
}
}
}

View File

@ -0,0 +1,11 @@

namespace CapnpC.CSharp.Generator.Model
{
interface IDefinition
{
ulong Id { get; }
bool IsGenerated { get; }
TypeTag Tag { get; }
IHasNestedDefinitions DeclaringElement { get; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace CapnpC.CSharp.Generator.Model
{
interface IHasGenericParameters
{
List<string> GenericParameters { get; }
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
namespace CapnpC.CSharp.Generator.Model
{
interface IHasNestedDefinitions
{
IEnumerable<TypeDefinition> NestedTypes { get; }
ICollection<IDefinition> NestedDefinitions { get; }
ICollection<Constant> Constants { get; }
}
static partial class Extensions
{
public static IEnumerable<TypeDefinition> GetNestedTypes(this IHasNestedDefinitions def)
=> def.NestedDefinitions.Select(d => d as TypeDefinition).Where(d => d != null);
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace CapnpC.CSharp.Generator.Model
{
class InvalidSchemaException : Exception
{
public InvalidSchemaException(string message) : base(message)
{
}
}
}

View File

@ -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<Field> Params { get; } = new List<Field>();
public List<Field> Results { get; } = new List<Field>();
public Type ParamsStruct { get; set; }
public Type ResultStruct { get; set; }
public List<string> GenericParameters { get; } = new List<string>();
}
}

View File

@ -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<GenFile> _generatedFiles = new List<GenFile>();
readonly DefinitionManager _typeDefMgr = new DefinitionManager();
readonly Dictionary<ulong, Schema.Node.Reader> _id2node = new Dictionary<ulong, Schema.Node.Reader>();
public SchemaModel(Schema.CodeGeneratorRequest.Reader request)
{
_request = request;
}
public IReadOnlyList<GenFile> 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<ulong> unprocessedNodes;
public IHasNestedDefinitions parent;
}
void BuildPass1(Dictionary<ulong, Schema.CodeGeneratorRequest.RequestedFile.Reader> requestedFiles)
{
Pass1State state = new Pass1State()
{
unprocessedNodes = new HashSet<ulong>(_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<ulong> processedNodes;
}
void BuildPass2(Dictionary<ulong, Schema.CodeGeneratorRequest.RequestedFile.Reader> requestedFiles)
{
var state = new Pass2State() { processedNodes = new HashSet<ulong>() };
foreach (var file in _typeDefMgr.Files)
{
var node = IdToNode(file.Id);
ProcessNestedNodes(node.NestedNodes, state, file.IsGenerated);
}
}
void ProcessNestedNodes(IEnumerable<Schema.Node.NestedNode.Reader> 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<Field> 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<Field> 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();
}
}

View File

@ -0,0 +1,9 @@
namespace CapnpC.CSharp.Generator.Model
{
enum SpecialName
{
NothingSpecial,
MethodParamsStruct,
MethodResultStruct
}
}

View File

@ -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<GenericParameter, Type> _parameterBindings =
new Dictionary<GenericParameter, Type>();
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<Field> 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<Type> AllImplementedClasses
{
get
{
var stk = new Stack<Type>();
stk.Push(this);
var set = new HashSet<Type>();
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;
}
}
}

View File

@ -0,0 +1,8 @@
namespace CapnpC.CSharp.Generator.Model
{
enum TypeCategory
{
Value,
Pointer
}
}

View File

@ -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<Field> Fields => base.Fields;
public List<Enumerant> Enumerants { get; } = new List<Enumerant>();
public ICollection<IDefinition> NestedDefinitions { get; } = new List<IDefinition>();
public IEnumerable<TypeDefinition> NestedTypes { get => this.GetNestedTypes(); }
public List<TypeDefinition> NestedGroups { get; } = new List<TypeDefinition>();
public ICollection<Constant> Constants { get; } = new List<Constant>();
public List<Method> Methods { get; } = new List<Method>();
public List<Type> Superclasses { get; } = new List<Type>();
public List<string> GenericParameters { get; } = new List<string>();
public bool IsGeneric { get; set; }
public ushort StructDataWordCount { get; set; }
public ushort StructPointerCount { get; set; }
public IEnumerable<TypeDefinition> 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<GenericParameter> AllTypeParameters
{
get
{
return from def in DefinitionHierarchy
from p in def.GetLocalTypeParameters()
select p;
}
}
}
}

View File

@ -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
}
}

View File

@ -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
};
}
}
}

View File

@ -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>(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<Value> Items { get; } = new List<Value>();
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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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]
}
}
}

View File

@ -21,8 +21,10 @@ namespace Capnpc.Csharp.MsBuild.Generation
public IEnumerable<string> GenerateFilesForProject(
string projectPath,
List<string> capnpFiles,
string projectFolder)
List<string> capnpFiles,
string projectFolder,
string workingDirectory,
string additionalOptions)
{
using (var capnpCodeBehindGenerator = new CapnpCodeBehindGenerator())
{

View File

@ -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();

View File

@ -4,6 +4,6 @@ namespace Capnpc.Csharp.MsBuild.Generation
{
public interface ICapnpcCsharpGenerator
{
IEnumerable<string> GenerateFilesForProject(string projectPath, List<string> capnpFiles, string projectFolder);
IEnumerable<string> GenerateFilesForProject(string projectPath, List<string> capnpFiles, string projectFolder, string workingDirectory, string additionalOptions);
}
}

View File

@ -25,10 +25,19 @@
</StringProperty.DataSource>
</StringProperty>
<!--<BoolProperty Name="Visible" Visible="true" />-->
<StringProperty Name="DependentUpon" Visible="false" />
<StringProperty Name="Link" Visible="false" />
<StringProperty Name="Generator" Visible="true" DisplayName="Custom Tool"/>
<StringProperty Name="WorkingDirectory" DisplayName="Working Directory" ReadOnly="false" Category="Misc">
<StringProperty.DataSource>
<DataSource Persistence="ProjectFile" ItemType="WorkingDirectory" PersistedName="WorkingDirectory" />
</StringProperty.DataSource>
</StringProperty>
<StringProperty Name="AdditionalOptions" DisplayName="Additional command line options" ReadOnly="false" Category="Misc">
<StringProperty.DataSource>
<DataSource Persistence="ProjectFile" ItemType="AdditionalOptions" PersistedName="AdditionalOptions" />
</StringProperty.DataSource>
</StringProperty>
</Rule>