From 52feb9cd8bb202a01c4f8aff456f1a92de8519f7 Mon Sep 17 00:00:00 2001 From: Alxandr Date: Thu, 23 Jul 2015 21:41:37 +0200 Subject: [PATCH] Refactoring --- .../Extensions.cs | 37 ++++ .../NativeGenerator.cs | 171 +++++++++++------- .../CustomMarshallerAttribute.cs | 13 ++ 3 files changed, 156 insertions(+), 65 deletions(-) create mode 100644 src/YoloDev.Dnx.NativeUtils/CustomMarshallerAttribute.cs diff --git a/src/YoloDev.Dnx.NativeUtils.Generator/Extensions.cs b/src/YoloDev.Dnx.NativeUtils.Generator/Extensions.cs index 7beedce..c99887a 100644 --- a/src/YoloDev.Dnx.NativeUtils.Generator/Extensions.cs +++ b/src/YoloDev.Dnx.NativeUtils.Generator/Extensions.cs @@ -1,5 +1,9 @@ using Microsoft.CodeAnalysis; using System.Reflection; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; namespace YoloDev.Dnx.NativeUtils.Generator { @@ -17,5 +21,38 @@ public static string GetTypeName(this Compilation compilation) { return GetType(compilation).ToDisplayString(); } + + public static AttributeData GetAttribute(this ISymbol symbol, Compilation compilation, bool inherited = false) + where TAttribute : Attribute + { + var type = compilation.GetType(); + var attributes = symbol.GetAttributes(); + foreach (var attr in attributes) + { + var attrType = attr.AttributeClass; + if (attrType.Equals(type) || (inherited && attrType.Inherits(type))) + { + return attr; + } + } + + return null; + } + + public static bool Inherits(this ITypeSymbol child, ITypeSymbol baseType) + { + if (child.Equals(baseType)) + return true; + + if (child.AllInterfaces.Any(iface => iface.Inherits(baseType))) + return true; + + return child.BaseType?.Inherits(baseType) ?? false; + } + + public static void AddRange(this List list, params T[] items) + { + list.AddRange(items); + } } } diff --git a/src/YoloDev.Dnx.NativeUtils.Generator/NativeGenerator.cs b/src/YoloDev.Dnx.NativeUtils.Generator/NativeGenerator.cs index 1a89217..6d1947b 100644 --- a/src/YoloDev.Dnx.NativeUtils.Generator/NativeGenerator.cs +++ b/src/YoloDev.Dnx.NativeUtils.Generator/NativeGenerator.cs @@ -6,90 +6,109 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace YoloDev.Dnx.NativeUtils.Generator { - static class NativeGenerator + class NativeGenerator { - internal static SyntaxTree Generate(NativeModel api, Compilation compilation) + static readonly NamespaceDeclarationSyntax _ns = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("YoloDev.Dnx.NativeUtils.Generated")); + + readonly NativeModel _api; + readonly Compilation _compilation; + + readonly List _members = new List(); + readonly ClassDeclarationSyntax _class; + + public NativeGenerator(NativeModel api, Compilation compilation) { - var ns = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("YoloDev.Dnx.NativeUtils.Generated")); - var dec = SyntaxFactory.ClassDeclaration($"NativeMethods${api.Symbol.MetadataName}"); - dec = dec.WithBaseList(SyntaxFactory.BaseList().AddTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(api.Symbol.ToDisplayString())))); + _api = api; + _compilation = compilation; + _class = SyntaxFactory.ClassDeclaration($"NativeMethods${api.Symbol.MetadataName}") + .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(api.Symbol.ToDisplayString()))) + .AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(CreateAttribute())); + } - foreach (var method in api.Methods) + SyntaxTree Generate() + { + foreach (var method in _api.Methods) { - var name = method.Name; - var returnType = method.ReturnsVoid ? - SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)) : - SyntaxFactory.ParseTypeName(method.ReturnType.ToDisplayString()); + GenerateMethod(method); + } - var attr = method.GetAttributes().Single(a => a.AttributeClass.Equals(compilation.GetType())); - var ctorArgs = attr.ConstructorArguments; - var namedArgs = attr.NamedArguments; + return SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit() + .WithMembers(new SyntaxList().Add(_ns.AddMembers(_class.AddMembers(_members.ToArray())))).NormalizeWhitespace()); + } - var attrSyntax = SyntaxFactory.Attribute(SyntaxFactory.ParseName(compilation.GetTypeName())); - var library = (string)ctorArgs.First().Value; + void GenerateMethod(IMethodSymbol method) + { + var name = method.Name; + var returnType = method.ReturnsVoid ? + SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)) : + SyntaxFactory.ParseTypeName(method.ReturnType.ToDisplayString()); - attrSyntax = attrSyntax.AddArgumentListArguments(ctorArgs.Skip(1).Select(val => SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression(val.ToCSharpString()))).ToArray()); - attrSyntax = attrSyntax.AddArgumentListArguments(namedArgs.Select(val => SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression(val.Value.ToCSharpString())).WithNameEquals(SyntaxFactory.NameEquals(val.Key))).ToArray()); + var attr = method.GetAttribute(_compilation); + if (attr == null) + throw new InvalidOperationException("Native method attribute not found"); - var dlgName = $"dlg${library}${name}"; - var dlg = SyntaxFactory.DelegateDeclaration( - returnType, - dlgName) - .AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(attrSyntax)) - .NormalizeWhitespace(); + var library = (string)attr.ConstructorArguments.First().Value; - if (method.Parameters.Length > 0) - { - var parameters = method.Parameters.Select( - p => SyntaxFactory.Parameter(SyntaxFactory.Identifier(p.Name)) - .WithType(SyntaxFactory.ParseTypeName(p.Type.ToDisplayString()))); + var attrSyntax = SyntaxFactory.Attribute(SyntaxFactory.ParseName(_compilation.GetTypeName())) + .AddArgumentListArguments(attr.ConstructorArguments.Skip(1).Select(CreateAttributeArgument).ToArray()) + .AddArgumentListArguments(attr.NamedArguments.Select(CreateAttributeArgument).ToArray()); - var list = new SeparatedSyntaxList() - .AddRange(parameters); + var dlgName = $"dlg${library}${name}"; + var dlg = SyntaxFactory.DelegateDeclaration( + returnType, + dlgName) + .AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(attrSyntax)); - dlg = dlg.WithParameterList(SyntaxFactory.ParameterList(list)); - } + if (method.Parameters.Length > 0) + { + var parameters = method.Parameters.Select( + p => SyntaxFactory.Parameter(SyntaxFactory.Identifier(p.Name)) + .WithType(SyntaxFactory.ParseTypeName(p.Type.ToDisplayString()))); - var fieldName = $"_${library}${name}"; - var field = CreateField(SyntaxFactory.IdentifierName(dlg.Identifier), fieldName); + var list = new SeparatedSyntaxList() + .AddRange(parameters); - var metaFieldName = $"_meta${library}${name}"; - var newExpr = SyntaxFactory.ObjectCreationExpression(SyntaxFactory.ParseTypeName(compilation.GetTypeName())); - newExpr = newExpr.AddArgumentListArguments(ctorArgs.Select(val => SyntaxFactory.Argument(SyntaxFactory.ParseExpression(val.ToCSharpString()))).ToArray()); - if (namedArgs.Length > 0) - { - var newInit = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression); - foreach (var arg in namedArgs) - { - var memberAccess = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(metaFieldName), SyntaxFactory.IdentifierName(arg.Key)); - var valueExpr = SyntaxFactory.ParseExpression(arg.Value.ToCSharpString()); - var assignment = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName(arg.Key), valueExpr); - newInit = newInit.AddExpressions(assignment); - } - - newExpr = newExpr.WithInitializer(newInit); - } + dlg = dlg.WithParameterList(SyntaxFactory.ParameterList(list)); + } - var metaField = CreateField(SyntaxFactory.ParseTypeName(compilation.GetTypeName()), metaFieldName, newExpr); + var fieldName = $"_${library}${name}"; + var field = CreateField(SyntaxFactory.IdentifierName(dlg.Identifier), fieldName); - var methodImpl = CreateMethod(method, dlg, SyntaxFactory.IdentifierName(dlg.Identifier), SyntaxFactory.IdentifierName(fieldName), SyntaxFactory.IdentifierName(metaFieldName)); + var metaFieldName = $"_meta${library}${name}"; + var newExpr = InstansiateAttribute(attr); - dec = dec.AddMembers(field, metaField, methodImpl, dlg); - } + var metaField = CreateField(SyntaxFactory.ParseTypeName(_compilation.GetTypeName()), metaFieldName, newExpr); + var methodImpl = CreateMethod(method, dlg, SyntaxFactory.IdentifierName(dlg.Identifier), SyntaxFactory.IdentifierName(fieldName), SyntaxFactory.IdentifierName(metaFieldName)); + + _members.AddRange(field, metaField, methodImpl, dlg); + } + + AttributeSyntax CreateAttribute() + where TAttribute : Attribute + { + var type = _compilation.GetTypeName(); + return SyntaxFactory.Attribute(SyntaxFactory.ParseName(type)); + } + + static AttributeArgumentSyntax CreateAttributeArgument(TypedConstant constant) + { + return SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression(constant.ToCSharpString())); + } + + static AttributeArgumentSyntax CreateAttributeArgument(KeyValuePair namedConstant) + { + return SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression(namedConstant.Value.ToCSharpString())) + .WithNameEquals((SyntaxFactory.NameEquals(namedConstant.Key))); + } - return SyntaxFactory.SyntaxTree( - SyntaxFactory.CompilationUnit() - .WithMembers( - new SyntaxList() - .Add( - ns.WithMembers( - new SyntaxList() - .Add(dec) - ).NormalizeWhitespace())) - ); + internal static SyntaxTree Generate(NativeModel api, Compilation compilation) + { + var generator = new NativeGenerator(api, compilation); + return generator.Generate(); } static FieldDeclarationSyntax CreateField(TypeSyntax type, string identifier, ExpressionSyntax initializer = null) @@ -111,7 +130,7 @@ static MethodDeclarationSyntax CreateMethod(IMethodSymbol baseSymbol, DelegateDe var modifiers = declaration.Modifiers; modifiers = modifiers.Replace(modifiers.Single(t => t.IsKind(SyntaxKind.AbstractKeyword)), SyntaxFactory.Token(SyntaxKind.OverrideKeyword)); method = method.WithModifiers(modifiers); - + // Get delegate var dlgExpr = (InvocationExpressionSyntax)SyntaxFactory.ParseExpression($"base.{nameof(NativeMethods.GetNativeMethodDelegate)}()"); dlgExpr = dlgExpr.AddArgumentListArguments( @@ -144,5 +163,27 @@ static MethodDeclarationSyntax CreateMethod(IMethodSymbol baseSymbol, DelegateDe method = method.AddBodyStatements(statements.ToArray()); return method; } + + static ExpressionSyntax InstansiateAttribute(AttributeData attr) + { + var expr = SyntaxFactory.ObjectCreationExpression(SyntaxFactory.ParseTypeName(attr.AttributeClass.ToDisplayString())); + var args = attr.ConstructorArguments.Select(c => SyntaxFactory.Argument(SyntaxFactory.ParseExpression(c.ToCSharpString()))); + expr = expr.AddArgumentListArguments(args.ToArray()); + + if(attr.NamedArguments.Length > 0) + { + var initializer = SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression); + foreach (var arg in attr.NamedArguments) + { + var valueExpr = SyntaxFactory.ParseExpression(arg.Value.ToCSharpString()); + var assignment = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName(arg.Key), valueExpr); + initializer = initializer.AddExpressions(assignment); + } + + expr = expr.WithInitializer(initializer); + } + + return expr; + } } } diff --git a/src/YoloDev.Dnx.NativeUtils/CustomMarshallerAttribute.cs b/src/YoloDev.Dnx.NativeUtils/CustomMarshallerAttribute.cs new file mode 100644 index 0000000..7825583 --- /dev/null +++ b/src/YoloDev.Dnx.NativeUtils/CustomMarshallerAttribute.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace YoloDev.Dnx.NativeUtils +{ + public interface ICustomMarshaller + { + TTo Marshal(TFrom value); + void Free(TTo native); + } +}