Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Alxandr committed Jul 24, 2015
1 parent 0f2daf0 commit 52feb9c
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 65 deletions.
37 changes: 37 additions & 0 deletions src/YoloDev.Dnx.NativeUtils.Generator/Extensions.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -17,5 +21,38 @@ public static string GetTypeName<T>(this Compilation compilation)
{
return GetType<T>(compilation).ToDisplayString();
}

public static AttributeData GetAttribute<TAttribute>(this ISymbol symbol, Compilation compilation, bool inherited = false)
where TAttribute : Attribute
{
var type = compilation.GetType<TAttribute>();
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<T>(this List<T> list, params T[] items)
{
list.AddRange(items);
}
}
}
171 changes: 106 additions & 65 deletions src/YoloDev.Dnx.NativeUtils.Generator/NativeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MemberDeclarationSyntax> _members = new List<MemberDeclarationSyntax>();
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<CompilerGeneratedAttribute>()));
}

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<NativeMethodAttribute>()));
var ctorArgs = attr.ConstructorArguments;
var namedArgs = attr.NamedArguments;
return SyntaxFactory.SyntaxTree(SyntaxFactory.CompilationUnit()
.WithMembers(new SyntaxList<MemberDeclarationSyntax>().Add(_ns.AddMembers(_class.AddMembers(_members.ToArray())))).NormalizeWhitespace());
}

var attrSyntax = SyntaxFactory.Attribute(SyntaxFactory.ParseName(compilation.GetTypeName<UnmanagedFunctionPointerAttribute>()));
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<NativeMethodAttribute>(_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<UnmanagedFunctionPointerAttribute>()))
.AddArgumentListArguments(attr.ConstructorArguments.Skip(1).Select(CreateAttributeArgument).ToArray())
.AddArgumentListArguments(attr.NamedArguments.Select(CreateAttributeArgument).ToArray());

var list = new SeparatedSyntaxList<ParameterSyntax>()
.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<ParameterSyntax>()
.AddRange(parameters);

var metaFieldName = $"_meta${library}${name}";
var newExpr = SyntaxFactory.ObjectCreationExpression(SyntaxFactory.ParseTypeName(compilation.GetTypeName<NativeMethodAttribute>()));
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<NativeMethodAttribute>()), 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<NativeMethodAttribute>()), 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<TAttribute>()
where TAttribute : Attribute
{
var type = _compilation.GetTypeName<TAttribute>();
return SyntaxFactory.Attribute(SyntaxFactory.ParseName(type));
}

static AttributeArgumentSyntax CreateAttributeArgument(TypedConstant constant)
{
return SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression(constant.ToCSharpString()));
}

static AttributeArgumentSyntax CreateAttributeArgument(KeyValuePair<string, TypedConstant> namedConstant)
{
return SyntaxFactory.AttributeArgument(SyntaxFactory.ParseExpression(namedConstant.Value.ToCSharpString()))
.WithNameEquals((SyntaxFactory.NameEquals(namedConstant.Key)));
}

return SyntaxFactory.SyntaxTree(
SyntaxFactory.CompilationUnit()
.WithMembers(
new SyntaxList<MemberDeclarationSyntax>()
.Add(
ns.WithMembers(
new SyntaxList<MemberDeclarationSyntax>()
.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)
Expand All @@ -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(
Expand Down Expand Up @@ -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;
}
}
}
13 changes: 13 additions & 0 deletions src/YoloDev.Dnx.NativeUtils/CustomMarshallerAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace YoloDev.Dnx.NativeUtils
{
public interface ICustomMarshaller<TFrom, TTo>
{
TTo Marshal(TFrom value);
void Free(TTo native);
}
}

0 comments on commit 52feb9c

Please sign in to comment.