Beispiel #1
0
        public static (ClassDeclarationSyntax, IGeneratedProxyDescription) Generate(
            LibraryTypes libraryTypes,
            IInvokableInterfaceDescription interfaceDescription,
            MetadataModel metadataModel)
        {
            var generatedClassName = GetSimpleClassName(interfaceDescription.InterfaceType);

            var ctors        = GenerateConstructors(generatedClassName, interfaceDescription).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);

            if (interfaceDescription.InterfaceType.TypeParameters.Length > 0)
            {
                classDeclaration = AddGenericTypeConstraints(classDeclaration, interfaceDescription.InterfaceType);
            }

            return(classDeclaration, new GeneratedProxyDescription(interfaceDescription));
        }
Beispiel #2
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));
        }
Beispiel #3
0
        private static IEnumerable <MemberDeclarationSyntax> CreateProxyMethods(
            LibraryTypes libraryTypes,
            IInvokableInterfaceDescription 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(), method.Name)
                                  .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.AsyncKeyword))
                                  .AddParameterListParameters(method.Parameters.Select(GetParameterSyntax).ToArray())
                                  .WithBody(
                    CreateProxyMethodBody(libraryTypes, metadataModel, interfaceDescription, methodDescription));

                var typeParameters = GetTypeParametersWithConstraints(method.TypeParameters);

                foreach (var(name, constraints) in typeParameters)
                {
                    if (constraints.Count > 0)
                    {
                        declaration = declaration.AddConstraintClauses(
                            TypeParameterConstraintClause(name).AddConstraints(constraints.ToArray()));
                    }
                }

                if (typeParameters.Count > 0)
                {
                    declaration = declaration.WithTypeParameterList(
                        TypeParameterList(SeparatedList(typeParameters.Select(tp => TypeParameter(tp.Item1)))));
                }

                return(declaration);
            }
        }
Beispiel #4
0
        private async Task <MetadataModel> GenerateMetadataModel(CancellationToken cancellationToken)
        {
            var metadataModel = new MetadataModel();

            foreach (var syntaxTree in this.compilation.SyntaxTrees)
            {
                var semanticModel = this.compilation.GetSemanticModel(syntaxTree, ignoreAccessibility: false);
                var rootNode      = await syntaxTree.GetRootAsync(cancellationToken);

                foreach (var node in GetTypeDeclarations(rootNode))
                {
                    var symbol = semanticModel.GetDeclaredSymbol(node);

                    bool ShouldGenerateSerializer(INamedTypeSymbol t)
                    {
                        if (!semanticModel.IsAccessible(0, t))
                        {
                            return(false);
                        }
                        if (this.HasAttribute(t, this.libraryTypes.GenerateSerializerAttribute, inherited: true) != null)
                        {
                            return(true);
                        }
                        if (this.generateSerializerAttributes != null)
                        {
                            foreach (var attr in this.generateSerializerAttributes)
                            {
                                if (this.HasAttribute(t, attr, inherited: true) != null)
                                {
                                    return(true);
                                }
                            }
                        }

                        return(false);
                    }

                    if (ShouldGenerateSerializer(symbol))
                    {
                        var typeDescription = new SerializableTypeDescription(semanticModel, symbol, this.GetDataMembers(symbol));
                        metadataModel.SerializableTypes.Add(typeDescription);
                    }

                    if (symbol.TypeKind == TypeKind.Interface)
                    {
                        var attribute = this.HasAttribute(
                            symbol,
                            this.libraryTypes.GenerateMethodSerializersAttribute,
                            inherited: true);
                        if (attribute != null)
                        {
                            var baseClass   = (INamedTypeSymbol)attribute.ConstructorArguments[0].Value;
                            var isExtension = (bool)attribute.ConstructorArguments[1].Value;
                            var description = new InvokableInterfaceDescription(
                                this.libraryTypes,
                                semanticModel,
                                symbol,
                                this.GetMethods(symbol),
                                baseClass,
                                isExtension);
                            metadataModel.InvokableInterfaces.Add(description);
                        }
                    }
                }
            }

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

            body.AddRange(
                metadataModel.SerializableTypes.Select(
                    type =>
                    (StatementSyntax)ExpressionStatement(
                        InvocationExpression(
                            addSerializerMethod,
                            ArgumentList(
                                SingletonSeparatedList(
                                    Argument(TypeOfExpression(GetPartialSerializerTypeName(type)))))))
                    ));
            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 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 configType      = libraryTypes.SerializerConfiguration;
            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.ConfigurationProvider.Construct(configType);

            return(ClassDeclaration(CodeGenerator.CodeGeneratorName + "_Metadata_" + compilation.AssemblyName.Replace('.', '_'))
                   .AddBaseListTypes(SimpleBaseType(interfaceType.ToTypeSyntax()))
                   .AddModifiers(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.SealedKeyword))
                   .AddAttributeLists(AttributeList(SingletonSeparatedList(CodeGenerator.GetGeneratedCodeAttributeSyntax())))
                   .AddMembers(configureMethod));
        }
Beispiel #6
0
        private MetadataModel GenerateMetadataModel(CancellationToken cancellationToken)
        {
            var metadataModel = new MetadataModel();

            foreach (var syntaxTree in _compilation.SyntaxTrees)
            {
                var semanticModel = _compilation.GetSemanticModel(syntaxTree, ignoreAccessibility: false);
                var rootNode      = syntaxTree.GetRoot(cancellationToken);
                foreach (var node in GetTypeDeclarations(rootNode))
                {
                    var symbolRaw = semanticModel.GetDeclaredSymbol(node, cancellationToken: cancellationToken);
                    if (symbolRaw is not INamedTypeSymbol symbol)
                    {
                        continue;
                    }

                    bool ShouldGenerateSerializer(INamedTypeSymbol t)
                    {
                        if (!semanticModel.IsAccessible(0, t))
                        {
                            return(false);
                        }

                        if (HasAttribute(t, _libraryTypes.GenerateSerializerAttribute, inherited: true) != null)
                        {
                            return(true);
                        }

                        if (_generateSerializerAttributes != null)
                        {
                            foreach (var attr in _generateSerializerAttributes)
                            {
                                if (HasAttribute(t, attr, inherited: true) != null)
                                {
                                    return(true);
                                }
                            }
                        }

                        return(false);
                    }

                    if (GetWellKnownTypeId(symbol) is uint wellKnownTypeId)
                    {
                        metadataModel.WellKnownTypeIds.Add((symbol, wellKnownTypeId));
                    }

                    if (GetTypeAlias(symbol) is string typeAlias)
                    {
                        metadataModel.TypeAliases.Add((symbol, typeAlias));
                    }

                    if (ShouldGenerateSerializer(symbol))
                    {
                        var typeDescription = new SerializableTypeDescription(semanticModel, symbol, GetDataMembers(symbol), _libraryTypes);
                        metadataModel.SerializableTypes.Add(typeDescription);
                    }

                    if (symbol.TypeKind == TypeKind.Interface)
                    {
                        var attribute = HasAttribute(
                            symbol,
                            _libraryTypes.GenerateMethodSerializersAttribute,
                            inherited: true);
                        if (attribute != null)
                        {
                            var baseClass   = (INamedTypeSymbol)attribute.ConstructorArguments[0].Value;
                            var isExtension = (bool)attribute.ConstructorArguments[1].Value;
                            var description = new InvokableInterfaceDescription(
                                _libraryTypes,
                                semanticModel,
                                symbol,
                                GetMethods(symbol),
                                baseClass,
                                isExtension);
                            metadataModel.InvokableInterfaces.Add(description);
                        }
                    }

                    if ((symbol.TypeKind == TypeKind.Class || symbol.TypeKind == TypeKind.Struct) && !symbol.IsAbstract && (symbol.DeclaredAccessibility == Accessibility.Public || symbol.DeclaredAccessibility == Accessibility.Internal))
                    {
                        if (symbol.HasAttribute(_libraryTypes.RegisterSerializerAttribute))
                        {
                            metadataModel.DetectedSerializers.Add(symbol);
                        }

                        if (symbol.HasAttribute(_libraryTypes.RegisterActivatorAttribute))
                        {
                            metadataModel.DetectedActivators.Add(symbol);
                        }
                    }
                }
            }

            return(metadataModel);
        }
Beispiel #7
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));
        }