Example #1
0
        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));
        }
Example #2
0
 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));
Example #3
0
        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));
        }
Example #4
0
        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()));
        }
Example #5
0
            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();
            }
Example #6
0
        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))));
        }
Example #7
0
        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))));
        }
Example #8
0
        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))));
        }
Example #9
0
        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()));
        }
Example #10
0
        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));
        }