Christian Köllner 75823c43b9 test & fix
2020-04-18 12:40:51 +02:00

953 lines
43 KiB
C#

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;
using Microsoft.CodeAnalysis;
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 when method.Results[0].Type.Tag == TypeTag.Struct:
return GenericName(nameof(Task)).AddTypeArgumentListArguments(
_names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NonNullable));
case 1:
return GenericName(nameof(Task)).AddTypeArgumentListArguments(
_names.MakeTypeSyntax(method.Results[0].Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef));
default:
return GenericName(nameof(Task)).AddTypeArgumentListArguments(
TupleType(SeparatedList(
method.Results.Select(
f => TupleElement(_names.MakeTypeSyntax(f.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef))))));
}
}
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, Nullability.NullableRef)));
}
else
{
foreach (var arg in method.Params)
{
list.Add(Parameter(Identifier(_names.GetCodeIdentifierLowerCamel(arg)))
.WithType(_names.MakeTypeSyntax(arg.Type, method.DeclaringInterface, TypeUsage.DomainClass, Nullability.NullableRef)));
}
}
}
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(
_names.MakeTypeDecorationAttributes(type.Id)
.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())
.AddConstraintClauses(MakeTypeParameterConstraints(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.DomainClass,
Nullability.NonNullable)));
}
}
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)
{
for (int i = 0; i < method.Params.Count; i++)
{
var methodParam = method.Params[i];
var field = method.ParamsStruct.Fields[i];
yield return AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
_names.GetCodeIdentifier(methodParam).IdentifierName,
IdentifierName(_names.GetCodeIdentifierLowerCamel(field)));
}
}
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, Nullability.NonNullable);
ExpressionSyntax createDomain = InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(nameof(Capnp.CapnpSerializable)),
GenericName(nameof(Capnp.CapnpSerializable.Create))
.AddTypeArgumentListArguments(MakeNonNullableType(domainType))))
.AddArgumentListArguments(
Argument(_names.DeserializerLocal.IdentifierName));
if (_names.NullableEnable)
{
createDomain = PostfixUnaryExpression(
SyntaxKind.SuppressNullableWarningExpression,
createDomain);
}
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)
.AddAttributeLists(_names.MakeTypeDecorationAttributes(type.Id))
.AddModifiers(Public)
.AddBaseListTypes(
SimpleBaseType(_names.Type<Capnp.Rpc.Proxy>(Nullability.NonNullable)),
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.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, Nullability.NonNullable))))))))))));
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,
Nullability.NonNullable))
.WithArgumentList(
ArgumentList())
.WithInitializer(
InitializerExpression(
SyntaxKind.ObjectInitializerExpression,
SeparatedList<ExpressionSyntax>(
CommonSnippetGen.MakeCommaSeparatedList(
MakeProxyCallInitializerAssignments(method)).ToArray())))))))));
}
bodyStmts.Add(ExpressionStatement(
ConditionalAccessExpression(
_names.AnonymousParameter.IdentifierName,
InvocationExpression(
MemberBindingExpression(_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(_names.Type<Capnp.DynamicSerializerState>(Nullability.NonNullable))))
.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(
UsingStatement(
Block(
MakeProxyCreateResult(method),
MakeProxyReturnResult(method)))
.WithExpression(_names.DeserializerLocal.IdentifierName)))));
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)));
bodyStmts.Add(UsingStatement(
Block(
MakeProxyCreateResult(method),
MakeProxyReturnResult(method)))
.WithDeclaration(VariableDeclaration(
IdentifierName("var"))
.AddVariables(
VariableDeclarator(
_names.DeserializerLocal.Identifier)
.WithInitializer(
EqualsValueClause(
AwaitExpression(whenReturned))))));
}
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(
_names.Type<Capnp.AnyPointer>(Nullability.NonNullable),
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(_names.GetCodeIdentifierLowerCamel(arg)));
}
}
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, Nullability.NonNullable)))))))))));
}
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,
Nullability.NonNullable))
.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)
{
SimpleNameSyntax methodName;
if (method.GenericParameters.Count == 0)
{
methodName = _names.GetCodeIdentifier(method).IdentifierName;
}
else
{
methodName = GenericName(_names.GetCodeIdentifier(method).Identifier)
.AddTypeArgumentListArguments(
method.GenericParameters.Select(
p => _names.GetGenericTypeParameter(p).IdentifierName)
.ToArray());
}
var call = InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(SkeletonWorder.ImplName),
methodName));
if (method.Params.Count > 0)
{
var paramsType = method.ParamsStruct;
var domainType = _names.MakeTypeSyntax(paramsType, method.ParamsStruct.Definition, TypeUsage.DomainClass, Nullability.NonNullable);
ExpressionSyntax createDomain = InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(nameof(Capnp.CapnpSerializable)),
GenericName(nameof(Capnp.CapnpSerializable.Create))
.AddTypeArgumentListArguments(MakeNonNullableType(domainType))))
.AddArgumentListArguments(
Argument(_names.DeserializerLocal.IdentifierName));
if (_names.NullableEnable)
{
createDomain = PostfixUnaryExpression(
SyntaxKind.SuppressNullableWarningExpression,
createDomain);
}
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(_names.GetCodeIdentifierLowerCamel(method.Results.Single()))),
MakeMaybeTailCallLambdaBody(method));
}
else
{
// CodeAnalysis.CSharp 3.2.1 has a bug which prevents us from using AddParameterListParameters. :-(
var paramList = new List<SyntaxNodeOrToken>();
foreach (var arg in method.Results)
{
if (paramList.Count > 0)
paramList.Add(Token(SyntaxKind.CommaToken));
paramList.Add(Parameter(Identifier(_names.GetCodeIdentifierLowerCamel(arg))));
}
lambdaArg = ParenthesizedLambdaExpression(
ParameterList(
SeparatedList<ParameterSyntax>(paramList)),
MakeMaybeTailCallLambdaBody(method));
}
}
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(
_names.Type<Task<Capnp.Rpc.AnswerOrCounterquestion>>(Nullability.NonNullable),
_names.GetCodeIdentifier(method).Identifier)
.AddParameterListParameters(
Parameter(_names.DeserializerLocal.Identifier)
.WithType(_names.Type<Capnp.DeserializerState>(Nullability.NonNullable)),
Parameter(_names.CancellationTokenParameter.Identifier)
.WithType(_names.Type<CancellationToken>(Nullability.NonNullable)))
.AddBodyStatements(
UsingStatement(
Block(
MakeSkeletonMethodBody(method).ToArray()))
.WithExpression(_names.DeserializerLocal.IdentifierName));
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)
.AddAttributeLists(_names.MakeTypeDecorationAttributes(type.Id))
.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(_names.Type<ulong>(Nullability.NonNullable), 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)>();
LocalFunctionStatementSyntax MakeLocalAwaitProxyFunction(Method method, IReadOnlyList<Field> path)
{
var members = new List<Name>();
IEnumerable<Field> fields = path;
if (method.Results.Count >= 2)
{
int index = Array.IndexOf(method.ResultStruct.Fields.ToArray(), path[0]) + 1;
members.Add(new Name($"Item{index}"));
fields = path.Skip(1);
}
foreach (var field in fields)
{
members.Add(_names.GetCodeIdentifier(field));
}
ExpressionSyntax memberAccess =
ParenthesizedExpression(
AwaitExpression(
_names.TaskParameter.IdentifierName));
memberAccess = MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
memberAccess,
members.First().IdentifierName);
foreach (var member in members.Skip(1))
{
memberAccess = ConditionalAccessExpression(
memberAccess,
MemberBindingExpression(member.IdentifierName));
}
var idisposable = _names.MakeNullableRefType(IdentifierName(nameof(IDisposable)));
return LocalFunctionStatement(
GenericName(
Identifier(nameof(Task)))
.WithTypeArgumentList(
TypeArgumentList(
SingletonSeparatedList<TypeSyntax>(
idisposable))),
_names.AwaitProxy.Identifier)
.WithModifiers(
TokenList(
Token(SyntaxKind.AsyncKeyword)))
.WithExpressionBody(
ArrowExpressionClause(memberAccess))
.WithSemicolonToken(
Token(SyntaxKind.SemicolonToken));
}
public IEnumerable<MemberDeclarationSyntax> MakePipeliningSupport(TypeDefinition type)
{
foreach (var method in type.Methods)
{
foreach (var path in ExpandPipeliningPaths(method))
{
if (path.Count == 1 && path[0].Offset == 0)
{
// The "trivial path" is already covered by the "Eager" extension method.
continue;
}
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, Nullability.NonNullable);
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(
MakeLocalAwaitProxyFunction(method, path),
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,
IdentifierName(nameof(Capnp.Rpc.Impatient)),
IdentifierName(nameof(Capnp.Rpc.Impatient.Access))))
.AddArgumentListArguments(
Argument(
_names.TaskParameter.IdentifierName),
Argument(
accessPath.IdentifierName),
Argument(
InvocationExpression(
_names.AwaitProxy.IdentifierName))))))));
yield return pathDecl;
yield return methodDecl;
}
}
}
}
}