Beispiel #1
0
        public static (ClassDeclarationSyntax, GeneratedProxyDescription) Generate(
            LibraryTypes libraryTypes,
            InvokableInterfaceDescription interfaceDescription,
            MetadataModel metadataModel)
        {
            var generatedClassName = GetSimpleClassName(interfaceDescription);

            var ctors        = GenerateConstructors(generatedClassName, interfaceDescription.ProxyBaseType).ToArray();
            var proxyMethods = CreateProxyMethods(libraryTypes, interfaceDescription, metadataModel).ToArray();

            var classDeclaration = ClassDeclaration(generatedClassName)
                                   .AddBaseListTypes(
                SimpleBaseType(interfaceDescription.ProxyBaseType.ToTypeSyntax()),
                SimpleBaseType(interfaceDescription.InterfaceType.ToTypeSyntax()))
                                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                                   .AddAttributeLists(
                AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                                   .AddMembers(ctors)
                                   .AddMembers(proxyMethods);

            var typeParameters = interfaceDescription.TypeParameters;

            if (typeParameters.Count > 0)
            {
                classDeclaration = SyntaxFactoryUtility.AddGenericTypeParameters(classDeclaration, typeParameters);
            }

            return(classDeclaration, new GeneratedProxyDescription(interfaceDescription));
        }
Beispiel #2
0
        private static List <GeneratedFieldDescription> GetFieldDescriptions(
            InvokableInterfaceDescription interfaceDescription,
            MetadataModel metadataModel,
            LibraryTypes libraryTypes)
        {
            var fields = new List <GeneratedFieldDescription>();

            // Add a codec field for any method parameter which does not have a static codec.
            var allTypes = interfaceDescription.Methods
                           .Where(method => method.MethodTypeParameters.Count == 0)
                           .SelectMany(method => metadataModel.GeneratedInvokables[method].Members);

            fields.AddRange(GetCopierFieldDescriptions(allTypes, libraryTypes));
            return(fields);
        }
Beispiel #3
0
        private static IEnumerable <MemberDeclarationSyntax> CreateProxyMethods(
            LibraryTypes libraryTypes,
            InvokableInterfaceDescription interfaceDescription,
            MetadataModel metadataModel)
        {
            foreach (var methodDescription in interfaceDescription.Methods)
            {
                yield return(CreateProxyMethod(methodDescription));
            }

            MethodDeclarationSyntax CreateProxyMethod(MethodDescription methodDescription)
            {
                var method      = methodDescription.Method;
                var declaration = MethodDeclaration(method.ReturnType.ToTypeSyntax(methodDescription.TypeParameterSubstitutions), method.Name.EscapeIdentifier())
                                  .AddParameterListParameters(method.Parameters.Select((p, i) => GetParameterSyntax(i, p, methodDescription.TypeParameterSubstitutions)).ToArray())
                                  .WithBody(
                    CreateAsyncProxyMethodBody(libraryTypes, metadataModel, methodDescription));

                if (methodDescription.HasCollision)
                {
                    declaration = declaration.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)));

                    // Type parameter constrains are not valid on explicit interface definitions
                    var typeParameters = SyntaxFactoryUtility.GetTypeParameterConstraints(methodDescription.MethodTypeParameters);
                    foreach (var(name, constraints) in typeParameters)
                    {
                        if (constraints.Count > 0)
                        {
                            declaration = declaration.AddConstraintClauses(
                                TypeParameterConstraintClause(name).AddConstraints(constraints.ToArray()));
                        }
                    }
                }
                else
                {
                    var explicitInterfaceSpecifier = ExplicitInterfaceSpecifier(methodDescription.Method.ContainingType.ToNameSyntax());
                    declaration = declaration.WithExplicitInterfaceSpecifier(explicitInterfaceSpecifier);
                }

                if (methodDescription.MethodTypeParameters.Count > 0)
                {
                    declaration = declaration.WithTypeParameterList(
                        TypeParameterList(SeparatedList(methodDescription.MethodTypeParameters.Select(tp => TypeParameter(tp.Name)))));
                }

                return(declaration);
            }
        }
Beispiel #4
0
        public static ClassDeclarationSyntax GenerateMetadata(Compilation compilation, MetadataModel metadataModel, LibraryTypes libraryTypes)
        {
            var configParam         = "config".ToIdentifierName();
            var addSerializerMethod = configParam.Member("Serializers").Member("Add");
            var addCopierMethod     = configParam.Member("Copiers").Member("Add");
            var body = new List <StatementSyntax>();

            body.AddRange(
                metadataModel.SerializableTypes.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addSerializerMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(GetCodecTypeName(type)))))))
                    ));
            body.AddRange(
                metadataModel.SerializableTypes.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addCopierMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(GetCopierTypeName(type)))))))
                    ));
            body.AddRange(
                metadataModel.DetectedCopiers.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addCopierMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(type.ToOpenTypeSyntax()))))))
                    ));
            body.AddRange(
                metadataModel.DetectedSerializers.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addSerializerMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(type.ToOpenTypeSyntax()))))))
                    ));
            var addProxyMethod = configParam.Member("InterfaceProxies").Member("Add");

            body.AddRange(
                metadataModel.GeneratedProxies.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addProxyMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(type.TypeSyntax))))))
                    ));
            var addInvokableInterfaceMethod = configParam.Member("Interfaces").Member("Add");

            body.AddRange(
                metadataModel.InvokableInterfaces.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addInvokableInterfaceMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(type.InterfaceType.ToOpenTypeSyntax()))))))
                    ));
            var addInvokableInterfaceImplementationMethod = configParam.Member("InterfaceImplementations").Member("Add");

            body.AddRange(
                metadataModel.InvokableInterfaceImplementations.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addInvokableInterfaceImplementationMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(type.ToOpenTypeSyntax()))))))
                    ));

            var addActivatorMethod = configParam.Member("Activators").Member("Add");

            body.AddRange(
                metadataModel.ActivatableTypes.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addActivatorMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(GetActivatorTypeName(type)))))))
                    ));
            body.AddRange(
                metadataModel.DetectedActivators.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addActivatorMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(type.ToOpenTypeSyntax()))))))
                    ));

            var addWellKnownTypeIdMethod = configParam.Member("WellKnownTypeIds").Member("Add");

            body.AddRange(
                metadataModel.WellKnownTypeIds.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addWellKnownTypeIdMethod,
                            ArgumentList(SeparatedList(
                                             new[]
            {
                Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(type.Id))),
                Argument(TypeOfExpression(type.Type))
            }))))
                    ));

            var addTypeAliasMethod = configParam.Member("WellKnownTypeAliases").Member("Add");

            body.AddRange(
                metadataModel.TypeAliases.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addTypeAliasMethod,
                            ArgumentList(SeparatedList(
                                             new[]
            {
                Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(type.Alias))),
                Argument(TypeOfExpression(type.Type))
            }))))
                    ));

            var configType      = libraryTypes.TypeManifestOptions;
            var configureMethod = MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), "Configure")
                                  .AddModifiers(Token(SyntaxKind.PublicKeyword))
                                  .AddParameterListParameters(
                Parameter(configParam.Identifier).WithType(configType.ToTypeSyntax()))
                                  .AddBodyStatements(body.ToArray());

            var interfaceType = libraryTypes.ITypeManifestProvider;

            return(ClassDeclaration("Metadata_" + SyntaxGeneration.Identifier.SanitizeIdentifierName(compilation.AssemblyName))
                   .AddBaseListTypes(SimpleBaseType(interfaceType.ToTypeSyntax()))
                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                   .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                   .AddMembers(configureMethod));
        }
Beispiel #5
0
        public static List <AttributeListSyntax> GenerateSyntax(LibraryTypes wellKnownTypes, MetadataModel model)
        {
            var attributes = new List <AttributeListSyntax>();

            foreach (var assemblyName in model.ApplicationParts)
            {
                // Generate an assembly-level attribute with an instance of that class.
                var attribute = AttributeList(
                    AttributeTargetSpecifier(Token(SyntaxKind.AssemblyKeyword)),
                    SingletonSeparatedList(
                        Attribute(wellKnownTypes.ApplicationPartAttribute.ToNameSyntax())
                        .AddArgumentListArguments(AttributeArgument(assemblyName.GetLiteralExpression()))));
                attributes.Add(attribute);
            }

            return(attributes);
        }
Beispiel #6
0
        private static BlockSyntax CreateAsyncProxyMethodBody(
            LibraryTypes libraryTypes,
            MetadataModel metadataModel,
            MethodDescription methodDescription)
        {
            var statements         = new List <StatementSyntax>();
            var requestVar         = IdentifierName("request");
            var requestDescription = metadataModel.GeneratedInvokables[methodDescription];
            var createRequestExpr  = InvocationExpression(ThisExpression().Member("GetInvokable", 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(SyntaxFactoryUtility.GetSanitizedName(parameter, parameterIndex)))));

                parameterIndex++;
            }

            var invokeMethodName = "InvokeAsync";

            foreach (var attr in methodDescription.Method.GetAttributes())
            {
                if (attr.AttributeClass.GetAttributes(libraryTypes.InvokeMethodNameAttribute, out var attrs))
                {
                    foreach (var methodAttr in attrs)
                    {
                        invokeMethodName = (string)methodAttr.ConstructorArguments.First().Value;
                    }
                }
            }

            ITypeSymbol resultType;
            var         methodReturnType = (INamedTypeSymbol)methodDescription.Method.ReturnType;

            if (methodReturnType.TypeArguments.Length == 1)
            {
                // Task<T> / ValueTask<T>
                resultType = methodReturnType.TypeArguments[0];
            }
            else if (SymbolEqualityComparer.Default.Equals(methodReturnType, libraryTypes.Void))
            {
                // void
                resultType = libraryTypes.Object;
            }
            else
            {
                // Task / ValueTask
                resultType = libraryTypes.Object;
            }

            // C#: base.InvokeAsync<TReturn>(request);
            var invocationExpression =
                InvocationExpression(
                    BaseExpression().Member(invokeMethodName, resultType.ToTypeSyntax(methodDescription.TypeParameterSubstitutions)),
                    ArgumentList(SeparatedList(new[] { Argument(requestVar) })));

            var rt = methodReturnType.ConstructedFrom;

            if (SymbolEqualityComparer.Default.Equals(rt, libraryTypes.Task_1) || SymbolEqualityComparer.Default.Equals(methodReturnType, libraryTypes.Task))
            {
                // C#: return <invocation>.AsTask()
                statements.Add(ReturnStatement(InvocationExpression(invocationExpression.Member("AsTask"), ArgumentList())));
            }
            else if (SymbolEqualityComparer.Default.Equals(rt, libraryTypes.ValueTask_1))
            {
                // C#: return <invocation>
                statements.Add(ReturnStatement(invocationExpression));
            }
            else if (SymbolEqualityComparer.Default.Equals(methodReturnType, libraryTypes.ValueTask))
            {
                // C#: return new ValueTask(<invocation>)
                statements.Add(ReturnStatement(ObjectCreationExpression(libraryTypes.ValueTask.ToTypeSyntax()).WithArgumentList(ArgumentList(SeparatedList(new[]
                {
                    Argument(InvocationExpression(invocationExpression.Member("AsTask"), ArgumentList()))
                })))));
            }
            else
            {
                // C#: _ = <invocation>
                statements.Add(ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("_"), invocationExpression)));
            }

            return(Block(statements));
        }
Beispiel #7
0
        private static BlockSyntax CreateProxyMethodBody(
            LibraryTypes libraryTypes,
            MetadataModel metadataModel,
            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(SyntaxFactoryUtility.GetSanitizedName(parameter, parameterIndex)))));

                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(methodDescription.TypeParameterSubstitutions)))
                                       .WithArgumentList(ArgumentList(SeparatedList <ArgumentSyntax>()));

            statements.Add(
                LocalDeclarationStatement(
                    VariableDeclaration(
                        ParseTypeName("var"),
                        SingletonSeparatedList(
                            VariableDeclarator(
                                Identifier("completion"))
                            .WithInitializer(
                                EqualsValueClause(createCompletionExpr))))));

            var sendRequestMethodName = "SendRequest";

            foreach (var attr in methodDescription.Method.GetAttributes())
            {
                if (attr.AttributeClass.GetAttributes(libraryTypes.InvokeMethodNameAttribute, out var attrs))
                {
                    foreach (var methodAttr in attrs)
                    {
                        sendRequestMethodName = (string)methodAttr.ConstructorArguments.First().Value;
                    }
                }
            }

            // Issue request
            statements.Add(
                ExpressionStatement(
                    InvocationExpression(
                        BaseExpression().Member(sendRequestMethodName),
                        ArgumentList(SeparatedList(new[] { Argument(completionVar), Argument(requestVar) })))));

            // Return result
            string valueTaskMethodName;

            if (methodReturnType.TypeArguments.Length == 1)
            {
                valueTaskMethodName = "AsValueTask";
            }
            else if (SymbolEqualityComparer.Default.Equals(methodReturnType, libraryTypes.Void))
            {
                valueTaskMethodName = null;
            }
            else
            {
                valueTaskMethodName = "AsVoidValueTask";
            }

            if (valueTaskMethodName is not null)
            {
                var returnVal = InvocationExpression(completionVar.Member(valueTaskMethodName));

                if (SymbolEqualityComparer.Default.Equals(methodReturnType.ConstructedFrom, libraryTypes.Task_1) || SymbolEqualityComparer.Default.Equals(methodReturnType, libraryTypes.Task))
                {
                    returnVal = InvocationExpression(returnVal.Member("AsTask"));
                }

                statements.Add(ReturnStatement(returnVal));
            }

            return(Block(statements));
        }