mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 06:41:50 +01:00
Factoring out capnpc-csharp into separate assembly
This commit is contained in:
parent
6b81abb0f0
commit
107d10e3f4
@ -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
|
||||
|
15
CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj
Normal file
15
CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj
Normal 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>
|
49
CapnpC.CSharp.Generator/CapnpCompilation.cs
Normal file
49
CapnpC.CSharp.Generator/CapnpCompilation.cs
Normal 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
190
CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs
Normal file
190
CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
91
CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs
Normal file
91
CapnpC.CSharp.Generator/CodeGen/CommonSnippetGen.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
970
CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs
Normal file
970
CapnpC.CSharp.Generator/CodeGen/DomainClassSnippetGen.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
600
CapnpC.CSharp.Generator/CodeGen/GenNames.cs
Normal file
600
CapnpC.CSharp.Generator/CodeGen/GenNames.cs
Normal 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)));
|
||||
}
|
||||
}
|
||||
}
|
38
CapnpC.CSharp.Generator/CodeGen/GeneratorOptions.cs
Normal file
38
CapnpC.CSharp.Generator/CodeGen/GeneratorOptions.cs
Normal 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";
|
||||
}
|
||||
}
|
843
CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs
Normal file
843
CapnpC.CSharp.Generator/CodeGen/InterfaceSnippetGen.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
33
CapnpC.CSharp.Generator/CodeGen/Name.cs
Normal file
33
CapnpC.CSharp.Generator/CodeGen/Name.cs
Normal 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();
|
||||
}
|
||||
}
|
713
CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs
Normal file
713
CapnpC.CSharp.Generator/CodeGen/ReaderSnippetGen.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
10
CapnpC.CSharp.Generator/CodeGen/SerializerStateWorder.cs
Normal file
10
CapnpC.CSharp.Generator/CodeGen/SerializerStateWorder.cs
Normal 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);
|
||||
}
|
||||
}
|
12
CapnpC.CSharp.Generator/CodeGen/SkeletonWorder.cs
Normal file
12
CapnpC.CSharp.Generator/CodeGen/SkeletonWorder.cs
Normal 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);
|
||||
}
|
||||
}
|
126
CapnpC.CSharp.Generator/CodeGen/SyntaxHelpers.cs
Normal file
126
CapnpC.CSharp.Generator/CodeGen/SyntaxHelpers.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
403
CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs
Normal file
403
CapnpC.CSharp.Generator/CodeGen/WriterSnippetGen.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
52
CapnpC.CSharp.Generator/FileGenerationResult.cs
Normal file
52
CapnpC.CSharp.Generator/FileGenerationResult.cs
Normal 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);
|
||||
}
|
||||
}
|
44
CapnpC.CSharp.Generator/GenerationResult.cs
Normal file
44
CapnpC.CSharp.Generator/GenerationResult.cs
Normal 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;
|
||||
}
|
||||
}
|
45
CapnpC.CSharp.Generator/Model/AbstractType.cs
Normal file
45
CapnpC.CSharp.Generator/Model/AbstractType.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
CapnpC.CSharp.Generator/Model/Annotation.cs
Normal file
23
CapnpC.CSharp.Generator/Model/Annotation.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
23
CapnpC.CSharp.Generator/Model/Constant.cs
Normal file
23
CapnpC.CSharp.Generator/Model/Constant.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
67
CapnpC.CSharp.Generator/Model/DefinitionManager.cs
Normal file
67
CapnpC.CSharp.Generator/Model/DefinitionManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
14
CapnpC.CSharp.Generator/Model/Enumerant.cs
Normal file
14
CapnpC.CSharp.Generator/Model/Enumerant.cs
Normal 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; }
|
||||
}
|
||||
}
|
52
CapnpC.CSharp.Generator/Model/Field.cs
Normal file
52
CapnpC.CSharp.Generator/Model/Field.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
25
CapnpC.CSharp.Generator/Model/GenFile.cs
Normal file
25
CapnpC.CSharp.Generator/Model/GenFile.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
26
CapnpC.CSharp.Generator/Model/GenericParameter.cs
Normal file
26
CapnpC.CSharp.Generator/Model/GenericParameter.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
19
CapnpC.CSharp.Generator/Model/HasGenericParameters.cs
Normal file
19
CapnpC.CSharp.Generator/Model/HasGenericParameters.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
CapnpC.CSharp.Generator/Model/IDefinition.cs
Normal file
11
CapnpC.CSharp.Generator/Model/IDefinition.cs
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
namespace CapnpC.CSharp.Generator.Model
|
||||
{
|
||||
interface IDefinition
|
||||
{
|
||||
ulong Id { get; }
|
||||
bool IsGenerated { get; }
|
||||
TypeTag Tag { get; }
|
||||
IHasNestedDefinitions DeclaringElement { get; }
|
||||
}
|
||||
}
|
9
CapnpC.CSharp.Generator/Model/IHasGenericParameters.cs
Normal file
9
CapnpC.CSharp.Generator/Model/IHasGenericParameters.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CapnpC.CSharp.Generator.Model
|
||||
{
|
||||
interface IHasGenericParameters
|
||||
{
|
||||
List<string> GenericParameters { get; }
|
||||
}
|
||||
}
|
18
CapnpC.CSharp.Generator/Model/IHasNestedDefinitions.cs
Normal file
18
CapnpC.CSharp.Generator/Model/IHasNestedDefinitions.cs
Normal 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);
|
||||
}
|
||||
}
|
22
CapnpC.CSharp.Generator/Model/IdentifierRenamer.cs
Normal file
22
CapnpC.CSharp.Generator/Model/IdentifierRenamer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
11
CapnpC.CSharp.Generator/Model/InvalidSchemaException.cs
Normal file
11
CapnpC.CSharp.Generator/Model/InvalidSchemaException.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace CapnpC.CSharp.Generator.Model
|
||||
{
|
||||
class InvalidSchemaException : Exception
|
||||
{
|
||||
public InvalidSchemaException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
16
CapnpC.CSharp.Generator/Model/Method.cs
Normal file
16
CapnpC.CSharp.Generator/Model/Method.cs
Normal 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>();
|
||||
}
|
||||
}
|
783
CapnpC.CSharp.Generator/Model/SchemaModel.cs
Normal file
783
CapnpC.CSharp.Generator/Model/SchemaModel.cs
Normal 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();
|
||||
}
|
||||
}
|
9
CapnpC.CSharp.Generator/Model/SpecialName.cs
Normal file
9
CapnpC.CSharp.Generator/Model/SpecialName.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace CapnpC.CSharp.Generator.Model
|
||||
{
|
||||
enum SpecialName
|
||||
{
|
||||
NothingSpecial,
|
||||
MethodParamsStruct,
|
||||
MethodResultStruct
|
||||
}
|
||||
}
|
201
CapnpC.CSharp.Generator/Model/Type.cs
Normal file
201
CapnpC.CSharp.Generator/Model/Type.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
8
CapnpC.CSharp.Generator/Model/TypeCategory.cs
Normal file
8
CapnpC.CSharp.Generator/Model/TypeCategory.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace CapnpC.CSharp.Generator.Model
|
||||
{
|
||||
enum TypeCategory
|
||||
{
|
||||
Value,
|
||||
Pointer
|
||||
}
|
||||
}
|
89
CapnpC.CSharp.Generator/Model/TypeDefinition.cs
Normal file
89
CapnpC.CSharp.Generator/Model/TypeDefinition.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
CapnpC.CSharp.Generator/Model/TypeTag.cs
Normal file
36
CapnpC.CSharp.Generator/Model/TypeTag.cs
Normal 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
|
||||
}
|
||||
}
|
56
CapnpC.CSharp.Generator/Model/Types.cs
Normal file
56
CapnpC.CSharp.Generator/Model/Types.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
300
CapnpC.CSharp.Generator/Model/Value.cs
Normal file
300
CapnpC.CSharp.Generator/Model/Value.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
1575
CapnpC.CSharp.Generator/Schema/SchemaSerialization.cs
Normal file
1575
CapnpC.CSharp.Generator/Schema/SchemaSerialization.cs
Normal file
File diff suppressed because it is too large
Load Diff
235
CapnpC.CSharp.Generator/Schema/schema-with-offsets.capnp
Normal file
235
CapnpC.CSharp.Generator/Schema/schema-with-offsets.capnp
Normal 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]
|
||||
}
|
||||
}
|
||||
}
|
@ -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())
|
||||
{
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user