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)); }
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 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); } }
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); }
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)); }
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); }
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)); }