private static BlockSyntax CreateProxyMethodBody( LibraryTypes libraryTypes, MetadataModel metadataModel, IInvokableInterfaceDescription interfaceDescription, MethodDescription methodDescription) { var statements = new List <StatementSyntax>(); // Create request object var requestVar = IdentifierName("request"); var requestDescription = metadataModel.GeneratedInvokables[methodDescription]; var createRequestExpr = ObjectCreationExpression(requestDescription.TypeSyntax) .WithArgumentList(ArgumentList(SeparatedList <ArgumentSyntax>())); statements.Add( LocalDeclarationStatement( VariableDeclaration( ParseTypeName("var"), SingletonSeparatedList( VariableDeclarator( Identifier("request")) .WithInitializer( EqualsValueClause(createRequestExpr)))))); // Set request object fields from method parameters. var parameterIndex = 0; foreach (var parameter in methodDescription.Method.Parameters) { statements.Add( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, requestVar.Member($"arg{parameterIndex}"), IdentifierName(parameter.Name)))); parameterIndex++; } // Issue request statements.Add( ExpressionStatement( AwaitExpression( InvocationExpression( BaseExpression().Member("Invoke"), ArgumentList(SingletonSeparatedList(Argument(requestVar))))))); // Return result if (methodDescription.Method.ReturnType is INamedTypeSymbol named && named.TypeParameters.Length == 1) { statements.Add(ReturnStatement(requestVar.Member("result"))); } return(Block(statements)); }
private static MemberDeclarationSyntax GenerateGetArgumentCount( LibraryTypes libraryTypes, MethodDescription methodDescription) => PropertyDeclaration(libraryTypes.Int32.ToTypeSyntax(), "ArgumentCount") .WithExpressionBody( ArrowExpressionClause( LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(methodDescription.Method.Parameters.Length)))) .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword))) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
public static TypeSyntax GetInvokableTypeName(this MethodDescription method) { var genericArity = method.Method.TypeParameters.Length + method.Method.ContainingType.TypeParameters.Length; var name = InvokableGenerator.GetSimpleClassName(method.Method); if (genericArity > 0) { name += $"<{new string(',', genericArity - 1)}>"; } return(ParseTypeName(name)); }
public static (ClassDeclarationSyntax, IGeneratedInvokerDescription) Generate( Compilation compilation, LibraryTypes libraryTypes, IInvokableInterfaceDescription interfaceDescription, MethodDescription methodDescription) { var method = methodDescription.Method; var generatedClassName = GetSimpleClassName(method); var fieldDescriptions = GetFieldDescriptions(methodDescription.Method, interfaceDescription); var fields = GetFieldDeclarations(fieldDescriptions, libraryTypes); var ctor = GenerateConstructor(generatedClassName, fieldDescriptions); var targetField = fieldDescriptions.OfType <TargetFieldDescription>().Single(); var resultField = fieldDescriptions.OfType <ResultFieldDescription>().FirstOrDefault(); var classDeclaration = ClassDeclaration(generatedClassName) .AddBaseListTypes(SimpleBaseType(libraryTypes.Invokable.ToTypeSyntax())) .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword)) .AddAttributeLists( AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax()))) .AddMembers(fields) .AddMembers(ctor) .AddMembers( GenerateGetArgumentCount(libraryTypes, methodDescription), GenerateSetTargetMethod(libraryTypes, interfaceDescription, targetField), GenerateGetTargetMethod(libraryTypes, targetField), GenerateResetMethod(libraryTypes, fieldDescriptions), GenerateGetArgumentMethod(libraryTypes, methodDescription, fieldDescriptions), GenerateSetArgumentMethod(libraryTypes, methodDescription, fieldDescriptions), GenerateInvokeMethod(libraryTypes, methodDescription, fieldDescriptions, targetField, resultField), GenerateSetResultProperty(libraryTypes, resultField), GenerateGetResultProperty(libraryTypes, resultField)); var typeParameters = interfaceDescription.InterfaceType.TypeParameters.Select(tp => (tp, tp.Name)) .Concat(method.TypeParameters.Select(tp => (tp, tp.Name))) .ToList(); if (typeParameters.Count > 0) { classDeclaration = AddGenericTypeConstraints(classDeclaration, typeParameters); } return(classDeclaration, new GeneratedInvokerDescription( interfaceDescription, methodDescription, generatedClassName, fieldDescriptions.OfType <IMemberDescription>().ToList())); }
public GeneratedInvokerDescription( IInvokableInterfaceDescription interfaceDescription, MethodDescription methodDescription, string generatedClassName, List <IMemberDescription> members) { this.InterfaceDescription = interfaceDescription; this.methodDescription = methodDescription; this.Name = generatedClassName; this.Members = members; this.TypeParameters = interfaceDescription.InterfaceType.TypeParameters .Concat(this.methodDescription.Method.TypeParameters) .ToImmutableArray(); }
private static MemberDeclarationSyntax GenerateInvokeInnerMethod( LibraryTypes libraryTypes, MethodDescription method, List <FieldDescription> fields, TargetFieldDescription target) { var resultTask = IdentifierName("resultTask"); // C# var resultTask = this.target.{Method}({params}); var args = SeparatedList( fields.OfType <MethodParameterFieldDescription>() .OrderBy(p => p.ParameterOrdinal) .Select(p => Argument(ThisExpression().Member(p.FieldName)))); ExpressionSyntax methodCall; if (method.Method.TypeParameters.Length > 0) { methodCall = MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, ThisExpression().Member(target.FieldName), GenericName( Identifier(method.Method.Name), TypeArgumentList( SeparatedList <TypeSyntax>( method.Method.TypeParameters.Select(p => IdentifierName(p.Name)))))); } else { methodCall = ThisExpression().Member(target.FieldName).Member(method.Method.Name); } return(MethodDeclaration(method.Method.ReturnType.ToTypeSyntax(), "InvokeInner") .WithParameterList(ParameterList()) .WithExpressionBody(ArrowExpressionClause(InvocationExpression(methodCall, ArgumentList(args)))) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) .WithModifiers(TokenList(Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.OverrideKeyword)))); }
private static MemberDeclarationSyntax GenerateInvokeMethod( LibraryTypes libraryTypes, MethodDescription method, List <FieldDescription> fields, TargetFieldDescription target, ResultFieldDescription result) { var body = new List <StatementSyntax>(); var resultTask = IdentifierName("resultTask"); // C# var resultTask = this.target.{Method}({params}); var args = SeparatedList( fields.OfType <MethodParameterFieldDescription>() .OrderBy(p => p.ParameterOrdinal) .Select(p => Argument(ThisExpression().Member(p.FieldName)))); ExpressionSyntax methodCall; if (method.Method.TypeParameters.Length > 0) { methodCall = MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, ThisExpression().Member(target.FieldName), GenericName( Identifier(method.Method.Name), TypeArgumentList( SeparatedList <TypeSyntax>( method.Method.TypeParameters.Select(p => IdentifierName(p.Name)))))); } else { methodCall = ThisExpression().Member(target.FieldName).Member(method.Method.Name); } body.Add( LocalDeclarationStatement( VariableDeclaration( ParseTypeName("var"), SingletonSeparatedList( VariableDeclarator(resultTask.Identifier) .WithInitializer( EqualsValueClause( InvocationExpression( methodCall, ArgumentList(args)))))))); // C#: if (resultTask.IsCompleted) // Even if it failed. // C#: { // C#: this.result = resultTask.GetAwaiter().GetResult(); // C#: return default; // default(ValueTask) is a successfully completed ValueTask. // C#: } var synchronousCompletionBody = new List <StatementSyntax>(); if (result != null) { synchronousCompletionBody.Add( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, ThisExpression().Member(result.FieldName), InvocationExpression( InvocationExpression(resultTask.Member("GetAwaiter")).Member("GetResult"))))); } synchronousCompletionBody.Add(ReturnStatement(DefaultExpression(libraryTypes.ValueTask.ToTypeSyntax()))); body.Add(IfStatement(resultTask.Member("IsCompleted"), Block(synchronousCompletionBody))); // C#: async ValueTask InvokeAsync(ValueTask<int> asyncValue) // C#: { // C#: this.result = await asyncValue.ConfigureAwait(false); // C#: } var invokeAsyncParam = IdentifierName("asyncTask"); var invokeAsyncBody = new List <StatementSyntax>(); var awaitExpression = AwaitExpression( InvocationExpression( invokeAsyncParam.Member("ConfigureAwait"), ArgumentList( SingletonSeparatedList(Argument(LiteralExpression(SyntaxKind.FalseLiteralExpression)))))); if (result != null) { invokeAsyncBody.Add( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, ThisExpression().Member(result.FieldName), awaitExpression))); } else { invokeAsyncBody.Add(ExpressionStatement(AwaitExpression(invokeAsyncParam))); } var invokeAsync = LocalFunctionStatement(libraryTypes.ValueTask.ToTypeSyntax(), "InvokeAsync") .WithModifiers(TokenList(Token(SyntaxKind.AsyncKeyword))) .WithParameterList( ParameterList( SingletonSeparatedList( Parameter(invokeAsyncParam.Identifier).WithType(method.Method.ReturnType.ToTypeSyntax())))) .WithBody(Block(invokeAsyncBody)); // C#: return InvokeAsync(resultTask); body.Add( ReturnStatement( InvocationExpression( IdentifierName("InvokeAsync"), ArgumentList(SingletonSeparatedList(Argument(resultTask)))))); body.Add(invokeAsync); return(MethodDeclaration(libraryTypes.ValueTask.ToTypeSyntax(), "Invoke") .WithParameterList(ParameterList()) .WithBody(Block(body)) .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))); }
private static MemberDeclarationSyntax GenerateSetArgumentMethod( LibraryTypes libraryTypes, MethodDescription methodDescription, List <FieldDescription> fields) { var index = IdentifierName("index"); var value = IdentifierName("value"); var type = IdentifierName("TArgument"); var typeToken = Identifier("TArgument"); var cases = new List <SwitchSectionSyntax>(); foreach (var field in fields) { if (!(field is MethodParameterFieldDescription parameter)) { continue; } // C#: case {index}: {field} = (TField)(object)value; return; var label = CaseSwitchLabel( LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(parameter.ParameterOrdinal))); cases.Add( SwitchSection( SingletonList <SwitchLabelSyntax>(label), new SyntaxList <StatementSyntax>( new StatementSyntax[] { ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, ThisExpression().Member(parameter.FieldName), CastExpression( parameter.FieldType.ToTypeSyntax(), CastExpression( libraryTypes.Object.ToTypeSyntax(), value )))), ReturnStatement() }))); } // C#: default: return HagarGeneratedCodeHelper.InvokableThrowArgumentOutOfRange<TArgument>(index, {maxArgs}) var maxArgs = Math.Max(0, methodDescription.Method.Parameters.Length - 1); var throwHelperMethod = MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("HagarGeneratedCodeHelper"), GenericName("InvokableThrowArgumentOutOfRange") .WithTypeArgumentList( TypeArgumentList( SingletonSeparatedList <TypeSyntax>(type)))); cases.Add( SwitchSection( SingletonList <SwitchLabelSyntax>(DefaultSwitchLabel()), new SyntaxList <StatementSyntax>( new StatementSyntax[] { ExpressionStatement( InvocationExpression( throwHelperMethod, ArgumentList( SeparatedList( new[] { Argument(index), Argument( LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(maxArgs))) })))), ReturnStatement() }))); var body = SwitchStatement(ParenthesizedExpression(index), new SyntaxList <SwitchSectionSyntax>(cases)); return(MethodDeclaration(libraryTypes.Void.ToTypeSyntax(), "SetArgument") .WithTypeParameterList(TypeParameterList(SingletonSeparatedList(TypeParameter(typeToken)))) .WithParameterList( ParameterList( SeparatedList( new[] { Parameter(Identifier("index")).WithType(libraryTypes.Int32.ToTypeSyntax()), Parameter(Identifier("value")) .WithType(type) .WithModifiers(TokenList(Token(SyntaxKind.InKeyword))) } ))) .WithBody(Block(body)) .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))); }
public static (ClassDeclarationSyntax, IGeneratedInvokerDescription) Generate( Compilation compilation, LibraryTypes libraryTypes, IInvokableInterfaceDescription interfaceDescription, MethodDescription methodDescription) { var method = methodDescription.Method; var generatedClassName = GetSimpleClassName(method); var fieldDescriptions = GetFieldDescriptions(methodDescription.Method, interfaceDescription); var fields = GetFieldDeclarations(fieldDescriptions, libraryTypes); var ctor = GenerateConstructor(generatedClassName, fieldDescriptions); var targetField = fieldDescriptions.OfType <TargetFieldDescription>().Single(); var methodReturnType = (INamedTypeSymbol)method.ReturnType; ITypeSymbol baseClassType; if (methodReturnType.TypeArguments.Length == 1) { if (methodReturnType.ConstructedFrom.Equals(libraryTypes.ValueTask_1)) { baseClassType = libraryTypes.Request_1.Construct(methodReturnType.TypeArguments[0]); } else if (methodReturnType.ConstructedFrom.Equals(libraryTypes.Task_1)) { baseClassType = libraryTypes.TaskRequest_1.Construct(methodReturnType.TypeArguments[0]); } else { throw new InvalidOperationException($"Unsupported return type {methodReturnType}. Constructed from: {methodReturnType.ConstructedFrom}"); } } else { if (methodReturnType.Equals(libraryTypes.ValueTask)) { baseClassType = libraryTypes.Request; } else if (methodReturnType.Equals(libraryTypes.Task)) { baseClassType = libraryTypes.TaskRequest; } else { throw new InvalidOperationException($"Unsupported return type {methodReturnType}"); } } var classDeclaration = ClassDeclaration(generatedClassName) .AddBaseListTypes(SimpleBaseType(baseClassType.ToTypeSyntax())) .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword)) .AddAttributeLists( AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax()))) .AddMembers(fields) .AddMembers(ctor) .AddMembers( GenerateGetArgumentCount(libraryTypes, methodDescription), GenerateSetTargetMethod(libraryTypes, interfaceDescription, targetField), GenerateGetTargetMethod(libraryTypes, targetField), GenerateDisposeMethod(libraryTypes, fieldDescriptions), GenerateGetArgumentMethod(libraryTypes, methodDescription, fieldDescriptions), GenerateSetArgumentMethod(libraryTypes, methodDescription, fieldDescriptions), GenerateInvokeInnerMethod(libraryTypes, methodDescription, fieldDescriptions, targetField)); var typeParameters = interfaceDescription.InterfaceType.TypeParameters.Select(tp => (tp, tp.Name)) .Concat(method.TypeParameters.Select(tp => (tp, tp.Name))) .ToList(); if (typeParameters.Count > 0) { classDeclaration = AddGenericTypeConstraints(classDeclaration, typeParameters); } return(classDeclaration, new GeneratedInvokerDescription( interfaceDescription, methodDescription, generatedClassName, fieldDescriptions.OfType <IMemberDescription>().ToList())); }
private static BlockSyntax CreateProxyMethodBody( LibraryTypes libraryTypes, MetadataModel metadataModel, IInvokableInterfaceDescription interfaceDescription, MethodDescription methodDescription) { var statements = new List <StatementSyntax>(); var completionVar = IdentifierName("completion"); var requestVar = IdentifierName("request"); var requestDescription = metadataModel.GeneratedInvokables[methodDescription]; var createRequestExpr = InvocationExpression(libraryTypes.InvokablePool.ToTypeSyntax().Member("Get", requestDescription.TypeSyntax)) .WithArgumentList(ArgumentList(SeparatedList <ArgumentSyntax>())); statements.Add( LocalDeclarationStatement( VariableDeclaration( ParseTypeName("var"), SingletonSeparatedList( VariableDeclarator( Identifier("request")) .WithInitializer( EqualsValueClause(createRequestExpr)))))); // Set request object fields from method parameters. var parameterIndex = 0; foreach (var parameter in methodDescription.Method.Parameters) { statements.Add( ExpressionStatement( AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, requestVar.Member($"arg{parameterIndex}"), IdentifierName(parameter.Name)))); parameterIndex++; } ITypeSymbol returnType; var methodReturnType = (INamedTypeSymbol)methodDescription.Method.ReturnType; if (methodReturnType.TypeParameters.Length == 1) { returnType = methodReturnType.TypeArguments[0]; } else { returnType = libraryTypes.Object; } var createCompletionExpr = InvocationExpression(libraryTypes.ResponseCompletionSourcePool.ToTypeSyntax().Member("Get", returnType.ToTypeSyntax())) .WithArgumentList(ArgumentList(SeparatedList <ArgumentSyntax>())); statements.Add( LocalDeclarationStatement( VariableDeclaration( ParseTypeName("var"), SingletonSeparatedList( VariableDeclarator( Identifier("completion")) .WithInitializer( EqualsValueClause(createCompletionExpr)))))); // Issue request statements.Add( ExpressionStatement( InvocationExpression( BaseExpression().Member("SendRequest"), ArgumentList(SeparatedList(new[] { Argument(completionVar), Argument(requestVar) }))))); // Return result string valueTaskMethodName; if (methodReturnType.TypeArguments.Length == 1) { valueTaskMethodName = "AsValueTask"; } else { valueTaskMethodName = "AsVoidValueTask"; } var returnVal = InvocationExpression(completionVar.Member(valueTaskMethodName)); if (methodReturnType.ConstructedFrom.Equals(libraryTypes.Task_1) || methodReturnType.Equals(libraryTypes.Task)) { returnVal = InvocationExpression(returnVal.Member("AsTask")); } statements.Add(ReturnStatement(returnVal)); return(Block(statements)); }