From fbb2f941c49ee5d034880cd38f1935964d128fa8 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Thu, 29 Oct 2015 23:42:01 +0200 Subject: [PATCH] Based on Abhinav Tripathi's work: extracted delegates in order to reuse them. Signed-off-by: Dimitar Dobrev --- src/AST/FunctionExtensions.cs | 76 ++++++++++ src/Generator/Driver.cs | 3 + .../Generators/CSharp/CSharpMarshal.cs | 35 ++--- .../Generators/CSharp/CSharpTextTemplate.cs | 133 ++---------------- .../Generators/CSharp/CSharpTypePrinter.cs | 4 +- src/Generator/Passes/DelegatesPass.cs | 131 +++++++++++++++++ src/Generator/Passes/RenameRootNamespaces.cs | 2 + 7 files changed, 239 insertions(+), 145 deletions(-) create mode 100644 src/AST/FunctionExtensions.cs create mode 100644 src/Generator/Passes/DelegatesPass.cs diff --git a/src/AST/FunctionExtensions.cs b/src/AST/FunctionExtensions.cs new file mode 100644 index 00000000..50e9678b --- /dev/null +++ b/src/AST/FunctionExtensions.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Linq; + +namespace CppSharp.AST +{ + public static class FunctionExtensions + { + public static IEnumerable GatherInternalParams(this Function function, + bool isItaniumLikeAbi, bool universalDelegate = false) + { + var @params = new List(); + + var method = function as Method; + var isInstanceMethod = method != null && !method.IsStatic; + + var pointer = new QualifiedType(new PointerType(new QualifiedType(new BuiltinType(PrimitiveType.Void)))); + + if (isInstanceMethod && !isItaniumLikeAbi) + { + @params.Add(new Parameter + { + QualifiedType = pointer, + Name = "instance" + }); + } + + if (!function.HasIndirectReturnTypeParameter && + isInstanceMethod && isItaniumLikeAbi) + { + @params.Add(new Parameter + { + QualifiedType = pointer, + Name = "instance" + }); + } + + var i = 0; + foreach (var param in function.Parameters.Where(p => p.Kind != ParameterKind.OperatorParameter)) + { + @params.Add(new Parameter + { + QualifiedType = universalDelegate && param.Kind == ParameterKind.IndirectReturnType ? + pointer : param.QualifiedType, + Kind = param.Kind, + Usage = param.Usage, + Name = universalDelegate ? "arg" + ++i : param.Name + }); + + if (param.Kind == ParameterKind.IndirectReturnType && + isInstanceMethod && isItaniumLikeAbi) + { + @params.Add(new Parameter + { + QualifiedType = pointer, + Name = "instance" + }); + } + } + + if (method != null && method.IsConstructor) + { + var @class = (Class) method.Namespace; + if (!isItaniumLikeAbi && @class.Layout.HasVirtualBases) + { + @params.Add(new Parameter + { + QualifiedType = new QualifiedType(new BuiltinType(PrimitiveType.Int)), + Name = "__forBases" + }); + } + } + + return @params; + } + } +} diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index e415de5f..f6ce2a47 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -288,6 +288,9 @@ namespace CppSharp TranslationUnitPasses.AddPass(new CheckVTableComponentsPass()); + if (Options.IsCSharpGenerator) + TranslationUnitPasses.AddPass(new DelegatesPass()); + if (Options.GenerateProperties) TranslationUnitPasses.AddPass(new GetterSetterToPropertyPass()); diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index d5cbdb28..7aba8cda 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text; using CppSharp.AST; @@ -56,29 +55,11 @@ namespace CppSharp.Generators.CSharp public CSharpMarshalNativeToManagedPrinter(CSharpMarshalContext context) : base(context) { + typePrinter = new CSharpTypePrinter(context.Driver); } public bool MarshalsParameter { get; set; } - public static string QualifiedIdentifier(Declaration decl) - { - var names = new List { decl.Name }; - - var ctx = decl.Namespace; - while (ctx != null) - { - if (!string.IsNullOrWhiteSpace(ctx.Name)) - names.Add(ctx.Name); - ctx = ctx.Namespace; - } - - //if (Options.GenerateLibraryNamespace) - // names.Add(Options.OutputNamespace); - - names.Reverse(); - return string.Join(".", names); - } - public override bool VisitType(Type type, TypeQualifiers quals) { TypeMap typeMap; @@ -269,10 +250,10 @@ namespace CppSharp.Generators.CSharp Type returnType = Context.ReturnType.Type.Desugar(); // if the class is an abstract impl, use the original for the object map - var qualifiedClass = QualifiedIdentifier(originalClass); + var qualifiedClass = originalClass.Visit(typePrinter); if (returnType.IsAddress()) - Context.Return.Write(HandleReturnedPointer(@class, qualifiedClass)); + Context.Return.Write(HandleReturnedPointer(@class, qualifiedClass.Type)); else Context.Return.Write("{0}.{1}({2})", qualifiedClass, Helpers.CreateInstanceIdentifier, Context.ReturnVarName); @@ -318,7 +299,7 @@ namespace CppSharp.Generators.CSharp { var originalClass = @class.OriginalClass ?? @class; var ret = Generator.GeneratedIdentifier("result") + Context.ParameterIndex; - var qualifiedIdentifier = QualifiedIdentifier(@class); + var qualifiedIdentifier = @class.Visit(typePrinter); Context.SupportBefore.WriteLine("{0} {1};", qualifiedIdentifier, ret); Context.SupportBefore.WriteLine("if ({0} == IntPtr.Zero) {1} = {2};", Context.ReturnVarName, ret, originalClass.IsRefType ? "null" : string.Format("new {0}()", qualifiedClass)); @@ -351,6 +332,8 @@ namespace CppSharp.Generators.CSharp } return ret; } + + private readonly CSharpTypePrinter typePrinter; } public class CSharpMarshalManagedToNativePrinter : CSharpMarshalPrinter @@ -358,6 +341,7 @@ namespace CppSharp.Generators.CSharp public CSharpMarshalManagedToNativePrinter(CSharpMarshalContext context) : base(context) { + typePrinter = new CSharpTypePrinter(context.Driver); } public override bool VisitType(Type type, TypeQualifiers quals) @@ -666,8 +650,7 @@ namespace CppSharp.Generators.CSharp return; } - var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier( - @class.OriginalClass ?? @class); + var qualifiedIdentifier = (@class.OriginalClass ?? @class).Visit(typePrinter); Context.Return.Write( "ReferenceEquals({0}, null) ? new {1}.Internal() : *({1}.Internal*) ({0}.{2})", param, qualifiedIdentifier, Helpers.InstanceIdentifier); @@ -721,6 +704,8 @@ namespace CppSharp.Generators.CSharp { return template.TemplatedFunction.Visit(this); } + + private readonly CSharpTypePrinter typePrinter; } public static class CSharpMarshalExtensions diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 166dc707..d2807852 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -8,6 +8,7 @@ using System.Text.RegularExpressions; using System.Web.Util; using CppSharp.AST; using CppSharp.AST.Extensions; +using CppSharp.Passes; using CppSharp.Types; using CppSharp.Utils; using Attribute = CppSharp.AST.Attribute; @@ -111,42 +112,6 @@ namespace CppSharp.Generators.CSharp #region Identifiers - // Takes a declaration (type, class etc.) that is referenced from a context, and the context. - // If the referenced name needs a qualification in the context, add it. Otherwise, return just the name. - private static string QualifiedIdentifierIfNeeded(Declaration reference) - { - var refNames = new Stack(); - - var refCtx = reference; - while (refCtx != null) - { - if (!string.IsNullOrWhiteSpace(refCtx.Name)) - refNames.Push(refCtx.Name); - refCtx = refCtx.Namespace; - } - - return string.Join(".", refNames); - } - - public string QualifiedIdentifier(Declaration decl) - { - var names = new List { decl.Name }; - - var ctx = decl.Namespace; - while (ctx != null) - { - if (!string.IsNullOrWhiteSpace(ctx.Name)) - names.Add(ctx.Name); - ctx = ctx.Namespace; - } - - if (decl.GenerationKind == GenerationKind.Generate && Options.GenerateLibraryNamespace) - names.Add(Options.OutputNamespace); - - names.Reverse(); - return string.Join(".", names); - } - public static string GeneratedIdentifier(string id) { return Generator.GeneratedIdentifier(id); @@ -597,72 +562,6 @@ namespace CppSharp.Generators.CSharp return functions; } - private IEnumerable GatherInternalParams(Function function, bool useOriginalParamNames = true) - { - var @params = new List(); - - var method = function as Method; - var isInstanceMethod = method != null && !method.IsStatic; - - var pointer = new QualifiedType(new PointerType(new QualifiedType(new BuiltinType(PrimitiveType.Void)))); - - if (isInstanceMethod && Options.IsMicrosoftAbi) - { - @params.Add(new Parameter - { - QualifiedType = pointer, - Name = "instance" - }); - } - - if (!function.HasIndirectReturnTypeParameter && - isInstanceMethod && Options.IsItaniumLikeAbi) - { - @params.Add(new Parameter - { - QualifiedType = pointer, - Name = "instance" - }); - } - - var i = 0; - foreach (var param in function.Parameters.Where(p => p.Kind != ParameterKind.OperatorParameter)) - { - @params.Add(new Parameter - { - QualifiedType = param.QualifiedType, - Kind = param.Kind, - Usage = param.Usage, - Name = useOriginalParamNames ? param.Name : "arg" + ++i - }); - - if (param.Kind == ParameterKind.IndirectReturnType && - isInstanceMethod && Options.IsItaniumLikeAbi) - { - @params.Add(new Parameter - { - QualifiedType = pointer, - Name = "instance" - }); - } - } - - if (method != null && method.IsConstructor) - { - var @class = (Class) method.Namespace; - if (Options.IsMicrosoftAbi && @class.Layout.HasVirtualBases) - { - @params.Add(new Parameter - { - QualifiedType = new QualifiedType(new BuiltinType(PrimitiveType.Int)), - Name = GeneratedIdentifier("forBases") - }); - } - } - - return @params; - } - private IEnumerable GatherInternalParams(Function function, out CSharpTypePrinterResult retType) { TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); @@ -670,7 +569,7 @@ namespace CppSharp.Generators.CSharp var retParam = new Parameter { QualifiedType = function.ReturnType }; retType = retParam.CSharpType(TypePrinter); - var @params = GatherInternalParams(function).Select(p => + var @params = function.GatherInternalParams(Driver.Options.IsItaniumLikeAbi).Select(p => string.Format("{0} {1}", p.CSharpType(TypePrinter), p.Name)).ToList(); TypePrinter.PopContext(); @@ -722,7 +621,7 @@ namespace CppSharp.Generators.CSharp bases.AddRange( from @base in @class.Bases where @base.IsClass - select QualifiedIdentifierIfNeeded(@base.Class)); + select @base.Class.Visit(TypePrinter).Type); } if (@class.IsGenerated) @@ -1602,18 +1501,14 @@ namespace CppSharp.Generators.CSharp WriteLine("// {0}", cleanSig); } - WriteLine("[SuppressUnmanagedCodeSecurity]"); - WriteLine("[UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.{0})]", - method.CallingConvention.ToInteropCallConv()); CSharpTypePrinterResult retType; var @params = GatherInternalParams(method, out retType); var vTableMethodDelegateName = GetVTableMethodDelegateName(method); - WriteLine("internal delegate {0} {1}({2});", retType, vTableMethodDelegateName, - string.Join(", ", @params)); - WriteLine("private static {0} {0}Instance;", vTableMethodDelegateName); + WriteLine("private static {0} {1}Instance;", DelegatesPass.Delegates[method].Visit(TypePrinter), + vTableMethodDelegateName); NewLine(); WriteLine("private static {0} {1}Hook({2})", retType, vTableMethodDelegateName, @@ -1866,7 +1761,7 @@ namespace CppSharp.Generators.CSharp // The local var must be of the exact type in the object map because of TryRemove WriteLine("{0} {1};", - QualifiedIdentifierIfNeeded(@interface ?? (@base.IsAbstractImpl ? @base.BaseClass : @base)), + (@interface ?? (@base.IsAbstractImpl ? @base.BaseClass : @base)).Visit(TypePrinter), Helpers.DummyIdentifier); WriteLine("NativeToManagedMap.TryRemove({0}, out {1});", Helpers.InstanceIdentifier, Helpers.DummyIdentifier); @@ -1943,7 +1838,7 @@ namespace CppSharp.Generators.CSharp var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; if (hasBaseClass) - WriteLineIndent(": base(({0}.Internal*) null)", QualifiedIdentifierIfNeeded(@class.BaseClass)); + WriteLineIndent(": base(({0}.Internal*) null)", @class.BaseClass.Visit(TypePrinter)); WriteStartBraceIndent(); @@ -2013,7 +1908,7 @@ namespace CppSharp.Generators.CSharp // Allocate memory for a new native object and call the ctor. WriteLine("var ret = Marshal.AllocHGlobal({0});", @class.Layout.Size); WriteLine("{0}.Internal.{1}(ret, new global::System.IntPtr(&native));", - QualifiedIdentifierIfNeeded(@class), GetFunctionNativeIdentifier(copyCtorMethod)); + @class.Visit(TypePrinter), GetFunctionNativeIdentifier(copyCtorMethod)); WriteLine("return ({0}.Internal*) ret;", className); } else @@ -2391,8 +2286,8 @@ namespace CppSharp.Generators.CSharp delegateId = Generator.GeneratedIdentifier(@delegate); virtualCallBuilder.AppendFormat( - "var {1} = ({0}) Marshal.GetDelegateForFunctionPointer(new IntPtr({2}), typeof({0}));", - @delegate, delegateId, Helpers.SlotIdentifier); + "var {0} = ({1}) Marshal.GetDelegateForFunctionPointer(new IntPtr({2}), typeof({1}));", + delegateId, DelegatesPass.Delegates[function].Visit(TypePrinter), Helpers.SlotIdentifier); virtualCallBuilder.AppendLine(); return virtualCallBuilder.ToString(); @@ -2533,7 +2428,7 @@ namespace CppSharp.Generators.CSharp if (construct == null) { WriteLine("var {0} = new {1}.Internal();", Helpers.ReturnIdentifier, - QualifiedIdentifierIfNeeded(retClass.OriginalClass ?? retClass)); + (retClass.OriginalClass ?? retClass).Visit(TypePrinter)); } else { @@ -2777,8 +2672,7 @@ namespace CppSharp.Generators.CSharp Class @class; if ((paramType.GetFinalPointee() ?? paramType).Desugar().TryGetClass(out @class)) { - var qualifiedIdentifier = CSharpMarshalNativeToManagedPrinter.QualifiedIdentifier( - @class.OriginalClass ?? @class); + var qualifiedIdentifier = (@class.OriginalClass ?? @class).Visit(TypePrinter); WriteLine("{0} = new {1}();", name, qualifiedIdentifier); } } @@ -2865,7 +2759,8 @@ namespace CppSharp.Generators.CSharp var interopCallConv = callingConvention.ToInteropCallConv(); if (interopCallConv != System.Runtime.InteropServices.CallingConvention.Winapi) WriteLine( - "[UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.{0})]", + "[SuppressUnmanagedCodeSecurity, " + + "UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.{0})]", interopCallConv); WriteLine("{0}unsafe {1};", Helpers.GetAccess(typedef.Access), diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index 7de041fd..27a65aa1 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -589,7 +589,7 @@ namespace CppSharp.Generators.CSharp return GetNestedQualifiedName(@enum); } - static private string GetNestedQualifiedName(Declaration decl) + private string GetNestedQualifiedName(Declaration decl) { var names = new List { decl.Name }; @@ -603,6 +603,8 @@ namespace CppSharp.Generators.CSharp } names.Reverse(); + if (names[0] == driver.Options.OutputNamespace) + names.RemoveAt(0); return string.Join(".", names); } diff --git a/src/Generator/Passes/DelegatesPass.cs b/src/Generator/Passes/DelegatesPass.cs new file mode 100644 index 00000000..b93c2332 --- /dev/null +++ b/src/Generator/Passes/DelegatesPass.cs @@ -0,0 +1,131 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using CppSharp.AST; +using CppSharp.AST.Extensions; +using CppSharp.Generators.CSharp; + +namespace CppSharp.Passes +{ + public class DelegatesPass : TranslationUnitPass + { + public const string DelegatesNamespace = "Delegates"; + + public DelegatesPass() + { + Options.VisitClassBases = false; + Options.VisitFunctionParameters = false; + Options.VisitFunctionReturnType = false; + Options.VisitNamespaceEnums = false; + Options.VisitNamespaceTemplates = false; + Options.VisitTemplateArguments = false; + } + + public static Dictionary Delegates + { + get { return delegates; } + } + + public override bool VisitLibrary(ASTContext context) + { + foreach (var library in Driver.Options.Libraries.Where(l => !libsDelegates.ContainsKey(l))) + libsDelegates.Add(library, new Dictionary()); + + var unit = context.TranslationUnits.Last(u => u.IsValid && u.IsGenerated && + !u.IsSystemHeader && u.HasDeclarations); + namespaceDelegates = new Namespace { Name = DelegatesNamespace, Namespace = unit }; + + var result = base.VisitLibrary(context); + + if (namespaceDelegates.Declarations.Count > 0) + unit.Declarations.Add(namespaceDelegates); + + return result; + } + + public override bool VisitMethodDecl(Method method) + { + if (!base.VisitMethodDecl(method) || !method.IsVirtual || method.Ignore) + return false; + + var @params = method.GatherInternalParams(Driver.Options.IsItaniumLikeAbi, true).ToList(); + var delegateName = GenerateDelegateSignature(@params, method.ReturnType); + var existingDelegate = GetExistingDelegate(delegateName); + if (existingDelegate != null) + { + delegates.Add(method, existingDelegate); + return true; + } + + var @delegate = new TypedefDecl + { + Name = delegateName, + QualifiedType = new QualifiedType( + new PointerType( + new QualifiedType( + new FunctionType + { + CallingConvention = method.CallingConvention, + IsDependent = method.IsDependent, + Parameters = @params, + ReturnType = method.ReturnType + }))), + Namespace = namespaceDelegates + }; + delegates.Add(method, @delegate); + foreach (var library in Driver.Options.Libraries) + libsDelegates[library].Add(delegateName, @delegate); + + namespaceDelegates.Declarations.Add(@delegate); + + return true; + } + + private TypedefDecl GetExistingDelegate(string delegateName) + { + TypedefDecl @delegate = null; + + if (Driver.Options.Libraries.Count == 0) + return @delegates.Values.FirstOrDefault(v => v.Name == delegateName); + + if (Driver.Options.Libraries.Union(Driver.Symbols.Libraries.SelectMany(l => l.Dependencies)).Any( + l => libsDelegates.ContainsKey(l) && libsDelegates[l].TryGetValue(delegateName, out @delegate))) + return @delegate; + + return null; + } + + private string GenerateDelegateSignature(IEnumerable @params, QualifiedType returnType) + { + var typePrinter = new CSharpTypePrinter(Driver); + typePrinter.PushContext(CSharpTypePrinterContextKind.Native); + + var typesBuilder = new StringBuilder(); + if (!returnType.Type.IsPrimitiveType(PrimitiveType.Void)) + { + typesBuilder.Insert(0, returnType.Type.CSharpType(typePrinter)); + typesBuilder.Append('_'); + } + foreach (var parameter in @params) + { + typesBuilder.Append(parameter.CSharpType(typePrinter)); + typesBuilder.Append('_'); + } + if (typesBuilder.Length > 0) + typesBuilder.Remove(typesBuilder.Length - 1, 1); + var delegateName = typesBuilder.Replace("global::System.", string.Empty).Replace( + "*", "Ptr").Replace('.', '_').ToString(); + if (returnType.Type.IsPrimitiveType(PrimitiveType.Void)) + delegateName = "Action_" + delegateName; + else + delegateName = "Func_" + delegateName; + + typePrinter.PopContext(); + return delegateName; + } + + private Namespace namespaceDelegates; + private static readonly Dictionary> libsDelegates = new Dictionary>(); + private static readonly Dictionary delegates = new Dictionary(); + } +} diff --git a/src/Generator/Passes/RenameRootNamespaces.cs b/src/Generator/Passes/RenameRootNamespaces.cs index 8177d19d..1643807c 100644 --- a/src/Generator/Passes/RenameRootNamespaces.cs +++ b/src/Generator/Passes/RenameRootNamespaces.cs @@ -23,6 +23,8 @@ namespace CppSharp.Passes } else if (unit.GenerationKind == GenerationKind.Generate) { + if (Driver.Options.IsCSharpGenerator) + unit.Name = Driver.Options.OutputNamespace; rootNamespaceRenames.Add(fileName, Driver.Options.OutputNamespace); } return true;