Browse Source

Merged Delegate Pass and pass for Anonymous Delegates and added the following improvements alongwith.

1. Got rid of Anonymous Names.
2. Merged the common code for Delegate Generation.
3. The delegate pass also works for C++/CLI now.
4. Fixed the calling conventions of delegates for both, C++/CLI and C#.
5. Ensures that if a delegate is used for a virtual as well as something else, it finally ends up as public.
6. Fixed the code generation when the return type of a method is a function pointer that has been used somewhere else as well.
7. Added Access and Calling convention to the delegate definition.

The only thing left is to get rid of the hack used, i.e move the code in VisitFunctionDecl to VisitParametersDecl. Somehow, it's not working right now.

Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
pull/921/head
Mohit Mohta 8 years ago committed by Dimitar Dobrev
parent
commit
8a2e813445
  1. 4
      src/AST/Function.cs
  2. 1
      src/AST/Type.cs
  3. 5
      src/Generator/BindingContext.cs
  4. 3
      src/Generator/Driver.cs
  5. 28
      src/Generator/Generators/CLI/CLIHeaders.cs
  6. 4
      src/Generator/Generators/CSharp/CSharpMarshal.cs
  7. 4
      src/Generator/Generators/CSharp/CSharpSources.cs
  8. 3
      src/Generator/Generators/CSharp/CSharpTypePrinter.cs
  9. 290
      src/Generator/Passes/DelegatesPass.cs
  10. 129
      src/Generator/Passes/GenerateAnonymousDelegatesPass.cs
  11. 6
      tests/CSharp/CSharp.cpp
  12. 6
      tests/CSharp/CSharp.h
  13. 2
      tests/Common/Common.Tests.cs

4
src/AST/Function.cs

@ -258,7 +258,7 @@ namespace CppSharp.AST
return visitor.VisitFunctionDecl(this); return visitor.VisitFunctionDecl(this);
} }
public Type Type => OriginalReturnType.Type; public Type Type => ReturnType.Type;
public QualifiedType QualifiedType => OriginalReturnType; public QualifiedType QualifiedType => ReturnType;
} }
} }

1
src/AST/Type.cs

@ -260,6 +260,7 @@ namespace CppSharp.AST
ReturnType = new QualifiedType((Type) type.ReturnType.Type.Clone(), type.ReturnType.Qualifiers); ReturnType = new QualifiedType((Type) type.ReturnType.Type.Clone(), type.ReturnType.Qualifiers);
Parameters.AddRange(type.Parameters.Select(p => new Parameter(p)).ToList()); Parameters.AddRange(type.Parameters.Select(p => new Parameter(p)).ToList());
CallingConvention = type.CallingConvention; CallingConvention = type.CallingConvention;
IsDependent = type.IsDependent;
} }
public override T Visit<T>(ITypeVisitor<T> visitor, TypeQualifiers quals = new TypeQualifiers()) public override T Visit<T>(ITypeVisitor<T> visitor, TypeQualifiers quals = new TypeQualifiers())

5
src/Generator/BindingContext.cs

@ -2,7 +2,6 @@ using CppSharp.AST;
using CppSharp.Passes; using CppSharp.Passes;
using CppSharp.Types; using CppSharp.Types;
using CppSharp.Parser; using CppSharp.Parser;
using System.Collections.Generic;
namespace CppSharp.Generators namespace CppSharp.Generators
{ {
@ -20,16 +19,12 @@ namespace CppSharp.Generators
public PassBuilder<TranslationUnitPass> TranslationUnitPasses { get; private set; } public PassBuilder<TranslationUnitPass> TranslationUnitPasses { get; private set; }
public PassBuilder<GeneratorOutputPass> GeneratorOutputPasses { get; private set; } public PassBuilder<GeneratorOutputPass> GeneratorOutputPasses { get; private set; }
public Dictionary<Declaration, DelegatesPass.DelegateDefinition> Delegates { get; private set; }
public BindingContext(DriverOptions options, ParserOptions parserOptions = null) public BindingContext(DriverOptions options, ParserOptions parserOptions = null)
{ {
Options = options; Options = options;
ParserOptions = parserOptions; ParserOptions = parserOptions;
Symbols = new SymbolContext(); Symbols = new SymbolContext();
Delegates = new Dictionary<Declaration, DelegatesPass.DelegateDefinition>();
TypeMaps = new TypeMapDatabase(); TypeMaps = new TypeMapDatabase();
TranslationUnitPasses = new PassBuilder<TranslationUnitPass>(this); TranslationUnitPasses = new PassBuilder<TranslationUnitPass>(this);

3
src/Generator/Driver.cs

@ -295,7 +295,6 @@ namespace CppSharp
TranslationUnitPasses.AddPass(new CheckStaticClass()); TranslationUnitPasses.AddPass(new CheckStaticClass());
TranslationUnitPasses.AddPass(new MoveOperatorToClassPass()); TranslationUnitPasses.AddPass(new MoveOperatorToClassPass());
TranslationUnitPasses.AddPass(new MoveFunctionToClassPass()); TranslationUnitPasses.AddPass(new MoveFunctionToClassPass());
TranslationUnitPasses.AddPass(new GenerateAnonymousDelegatesPass());
TranslationUnitPasses.AddPass(new ConstructorToConversionOperatorPass()); TranslationUnitPasses.AddPass(new ConstructorToConversionOperatorPass());
TranslationUnitPasses.AddPass(new MarshalPrimitivePointersAsRefTypePass()); TranslationUnitPasses.AddPass(new MarshalPrimitivePointersAsRefTypePass());
TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions());
@ -321,8 +320,8 @@ namespace CppSharp
} }
TranslationUnitPasses.AddPass(new MultipleInheritancePass()); TranslationUnitPasses.AddPass(new MultipleInheritancePass());
TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass()); TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass());
TranslationUnitPasses.AddPass(new DelegatesPass());
} }
TranslationUnitPasses.AddPass(new DelegatesPass());
TranslationUnitPasses.AddPass(new GetterSetterToPropertyPass()); TranslationUnitPasses.AddPass(new GetterSetterToPropertyPass());
TranslationUnitPasses.AddPass(new StripUnusedSystemTypesPass()); TranslationUnitPasses.AddPass(new StripUnusedSystemTypesPass());

28
src/Generator/Generators/CLI/CLIHeaders.cs

@ -745,31 +745,27 @@ namespace CppSharp.Generators.CLI
if (!typedef.IsGenerated) if (!typedef.IsGenerated)
return false; return false;
FunctionType function; var functionType = typedef.Type as FunctionType;
if (typedef.Type.IsPointerTo(out function)) if (functionType != null || typedef.Type.IsPointerTo(out functionType))
{ {
PushBlock(BlockKind.Typedef, typedef); PushBlock(BlockKind.Typedef, typedef);
GenerateDeclarationCommon(typedef); GenerateDeclarationCommon(typedef);
var insideClass = typedef.Namespace is Class; var insideClass = typedef.Namespace is Class;
var attributedType = typedef.Type.GetPointee() as AttributedType; var attributedType = typedef.Type.GetPointee().Desugar();
if (attributedType != null) var callingConvention = attributedType == null && functionType != null
{ ? functionType.CallingConvention
var equivalentFunctionType = attributedType.Equivalent.Type as FunctionType; : ((FunctionType)attributedType).CallingConvention;
var callingConvention = equivalentFunctionType.CallingConvention.ToInteropCallConv(); var interopCallConv = callingConvention.ToInteropCallConv();
if (callingConvention != System.Runtime.InteropServices.CallingConvention.Winapi) if (interopCallConv != System.Runtime.InteropServices.CallingConvention.Winapi)
{ WriteLine("[System::Runtime::InteropServices::UnmanagedFunctionPointer" +
WriteLine("[{0}({1}::{2})] ", "(System::Runtime::InteropServices::CallingConvention::{0})] ",
"System::Runtime::InteropServices::UnmanagedFunctionPointer", interopCallConv);
"System::Runtime::InteropServices::CallingConvention",
callingConvention);
}
}
WriteLine("{0}{1};", WriteLine("{0}{1};",
!insideClass ? "public " : "", !insideClass ? "public " : "",
string.Format(TypePrinter.VisitDelegate(function).ToString(), string.Format(TypePrinter.VisitDelegate(functionType).ToString(),
typedef.Name)); typedef.Name));
PopBlock(NewLineKind.BeforeNextBlock); PopBlock(NewLineKind.BeforeNextBlock);

4
src/Generator/Generators/CSharp/CSharpMarshal.cs

@ -256,8 +256,8 @@ namespace CppSharp.Generators.CSharp
var decl = typedef.Declaration; var decl = typedef.Declaration;
FunctionType function; var functionType = decl.Type as FunctionType;
if (decl.Type.IsPointerTo(out function)) if (functionType != null || decl.Type.IsPointerTo(out functionType))
{ {
var ptrName = Generator.GeneratedIdentifier("ptr") + var ptrName = Generator.GeneratedIdentifier("ptr") +
Context.ParameterIndex; Context.ParameterIndex;

4
src/Generator/Generators/CSharp/CSharpSources.cs

@ -1688,7 +1688,7 @@ namespace CppSharp.Generators.CSharp
TypePrinter.PopMarshalKind(); TypePrinter.PopMarshalKind();
WriteLine("private static {0} {1}Instance;", WriteLine("private static {0} {1}Instance;",
Context.Delegates[method].Signature, method.FunctionType.ToString(),
vTableMethodDelegateName); vTableMethodDelegateName);
NewLine(); NewLine();
@ -2490,7 +2490,7 @@ namespace CppSharp.Generators.CSharp
var delegateId = Generator.GeneratedIdentifier(@delegate); var delegateId = Generator.GeneratedIdentifier(@delegate);
WriteLine("var {0} = ({1}) Marshal.GetDelegateForFunctionPointer(new IntPtr({2}), typeof({1}));", WriteLine("var {0} = ({1}) Marshal.GetDelegateForFunctionPointer(new IntPtr({2}), typeof({1}));",
delegateId, Context.Delegates[method].Signature, delegateId, method.FunctionType.ToString(),
Helpers.SlotIdentifier); Helpers.SlotIdentifier);
return delegateId; return delegateId;

3
src/Generator/Generators/CSharp/CSharpTypePrinter.cs

@ -657,9 +657,6 @@ namespace CppSharp.Generators.CSharp
typeBuilder.Append(printedType); typeBuilder.Append(printedType);
var type = typeBuilder.ToString(); var type = typeBuilder.ToString();
if (Context.Delegates.ContainsKey(param))
return $"{Context.Delegates[param].Signature} {param.Name}";
if (ContextKind == TypePrinterContextKind.Native) if (ContextKind == TypePrinterContextKind.Native)
return $"{type} {param.Name}"; return $"{type} {param.Name}";

290
src/Generator/Passes/DelegatesPass.cs

@ -3,32 +3,15 @@ using System.Linq;
using System.Text; using System.Text;
using CppSharp.AST; using CppSharp.AST;
using CppSharp.AST.Extensions; using CppSharp.AST.Extensions;
using CppSharp.Generators;
using CppSharp.Generators.CSharp; using CppSharp.Generators.CSharp;
namespace CppSharp.Passes namespace CppSharp.Passes
{ {
public class DelegatesPass : TranslationUnitPass public class DelegatesPass : TranslationUnitPass
{ {
public const string DelegatesNamespace = "Delegates";
public class DelegateDefinition
{
public DelegateDefinition(string @namespace, string signature)
{
Namespace = @namespace;
Signature = signature;
}
public string Namespace { get; }
public string Signature { get; }
}
public DelegatesPass() public DelegatesPass()
{ {
VisitOptions.VisitClassBases = false; VisitOptions.VisitClassBases = false;
VisitOptions.VisitFunctionParameters = false;
VisitOptions.VisitFunctionReturnType = false; VisitOptions.VisitFunctionReturnType = false;
VisitOptions.VisitNamespaceEnums = false; VisitOptions.VisitNamespaceEnums = false;
VisitOptions.VisitNamespaceTemplates = false; VisitOptions.VisitNamespaceTemplates = false;
@ -37,177 +20,194 @@ namespace CppSharp.Passes
public override bool VisitASTContext(ASTContext context) public override bool VisitASTContext(ASTContext context)
{ {
foreach (var module in Options.Modules) bool result = base.VisitASTContext(context);
libsDelegates[module] = new Dictionary<string, DelegateDefinition>();
foreach (var @delegate in delegates)
@delegate.Namespace.Declarations.Add(@delegate);
delegates.Clear();
var unit = context.TranslationUnits.GetGenerated().LastOrDefault(); if (!Options.IsCSharpGenerator)
return result;
var generatedUnits = context.TranslationUnits.GetGenerated();
var unit = generatedUnits.LastOrDefault();
if (unit == null) if (unit == null)
return false; return false;
var result = base.VisitASTContext(context); foreach (var module in Options.Modules.Where(namespacesDelegates.ContainsKey))
{
foreach (var module in Options.Modules.Where(m => namespacesDelegates.ContainsKey(m))) var @namespace = namespacesDelegates[module];
namespacesDelegates[module].Namespace.Declarations.Add(namespacesDelegates[module]); @namespace.Namespace.Declarations.Add(@namespace);
}
return result; return result;
} }
private void AddDelegatesToDictionary(Declaration decl, Module module) public override bool VisitMethodDecl(Method method)
{ {
CallingConvention callingConvention; if (!base.VisitMethodDecl(method) || !method.IsVirtual || method.Ignore)
bool isDependent = false; return false;
QualifiedType returnType;
List<Parameter> @params;
if (decl is Method)
{
var method = (Method) decl;
@params = method.GatherInternalParams(Context.ParserOptions.IsItaniumLikeAbi, true).ToList();
callingConvention = method.CallingConvention;
isDependent = method.IsDependent;
returnType = method.ReturnType;
}
else
{
var param = (Parameter) decl;
var funcTypeParam = param.Type.Desugar().GetFinalPointee().Desugar() as FunctionType;
@params = funcTypeParam.Parameters;
callingConvention = funcTypeParam.CallingConvention;
isDependent = funcTypeParam.IsDependent;
returnType = funcTypeParam.ReturnType;
}
var delegateName = GenerateDelegateSignature(@params, returnType); method.FunctionType = CheckForDelegate(method.FunctionType, method);
Namespace namespaceDelegates;
if (namespacesDelegates.ContainsKey(module)) return true;
{ }
namespaceDelegates = namespacesDelegates[module];
}
else
{
Namespace parent = null;
if (string.IsNullOrEmpty(module.OutputNamespace))
{
var group = module.Units.SelectMany(u => u.Declarations).OfType<Namespace>(
).GroupBy(d => d.Name).Where(g => g.Any(d => d.HasDeclarations)).ToList();
if (group.Count == 1)
parent = group.Last().Last();
}
if (parent == null)
parent = module.Units.Last();
namespaceDelegates = new Namespace
{
Name = DelegatesNamespace,
Namespace = parent
};
namespacesDelegates.Add(module, namespaceDelegates);
}
var functionType = new FunctionType public override bool VisitFunctionDecl(Function function)
{ {
CallingConvention = callingConvention, if (!base.VisitFunctionDecl(function) || function.Ignore)
IsDependent = isDependent, return false;
ReturnType = returnType
};
functionType.Parameters.AddRange(@params);
var @delegate = new TypedefDecl
{
Name = delegateName,
QualifiedType = new QualifiedType(
new PointerType(new QualifiedType(functionType))),
Namespace = namespaceDelegates,
IsSynthetized = true,
Access = decl is Method ? AccessSpecifier.Private : AccessSpecifier.Public
};
var delegateString = @delegate.Visit(TypePrinter).Type; function.ReturnType = CheckForDelegate(function.ReturnType, function);
var existingDelegate = GetExistingDelegate( return true;
module, delegateString); }
if (existingDelegate != null) public override bool VisitParameterDecl(Parameter parameter)
{ {
Context.Delegates.Add(decl, existingDelegate); if (!base.VisitParameterDecl(parameter) || parameter.Namespace == null ||
return; parameter.Namespace.Ignore)
} return false;
parameter.QualifiedType = CheckForDelegate(parameter.QualifiedType, parameter);
return true;
}
private QualifiedType CheckForDelegate(QualifiedType type, ITypedDecl decl)
{
if (type.Type is TypedefType)
return type;
var desugared = type.Type.Desugar();
if (desugared.IsDependent)
return type;
existingDelegate = new DelegateDefinition(module.OutputNamespace, delegateString); Type pointee = desugared.GetPointee() ?? desugared;
Context.Delegates.Add(decl, existingDelegate); if (pointee is TypedefType)
return type;
libsDelegates[module].Add(delegateString, existingDelegate); var functionType = pointee.Desugar() as FunctionType;
if (functionType == null)
return type;
namespaceDelegates.Declarations.Add(@delegate); TypedefDecl @delegate = GetDelegate(type, decl);
return new QualifiedType(new TypedefType { Declaration = @delegate });
} }
private void VisitFunctionTypeParameters(Function function) private TypedefDecl GetDelegate(QualifiedType type, ITypedDecl decl)
{ {
foreach (var param in from param in function.Parameters FunctionType newFunctionType = GetNewFunctionType(decl, type);
where !(param.Type is TypedefType)
let paramType = param.Type.Desugar() var delegateName = GetDelegateName(newFunctionType);
where paramType.IsAddress() && var access = decl is Method ? AccessSpecifier.Private : AccessSpecifier.Public;
paramType.GetPointee() is FunctionType var existingDelegate = delegates.SingleOrDefault(t => t.Name == delegateName);
select param) if (existingDelegate != null)
{ {
var module = function.TranslationUnit.Module; // Ensure a delegate used for a virtual method and a type is public
AddDelegatesToDictionary(param, module); if (existingDelegate.Access == AccessSpecifier.Private &&
access == AccessSpecifier.Public)
existingDelegate.Access = access;
// Check if there is an existing delegate with a different calling convention
if (((FunctionType) existingDelegate.Type.GetPointee()).CallingConvention ==
newFunctionType.CallingConvention)
return existingDelegate;
// Add a new delegate with the calling convention appended to its name
delegateName += newFunctionType.CallingConvention;
} }
var namespaceDelegates = GetDeclContextForDelegates(((Declaration) decl).Namespace);
var delegateType = new QualifiedType(new PointerType(new QualifiedType(newFunctionType)));
existingDelegate = new TypedefDecl
{
Access = access,
Name = delegateName,
Namespace = namespaceDelegates,
QualifiedType = delegateType,
IsSynthetized = true
};
delegates.Add(existingDelegate);
return existingDelegate;
} }
public override bool VisitFunctionDecl(Function function) private FunctionType GetNewFunctionType(ITypedDecl decl, QualifiedType type)
{ {
if (!base.VisitFunctionDecl(function) || function.Ignore) var functionType = new FunctionType();
return false; var method = decl as Method;
if (method != null && method.FunctionType == type)
{
functionType.Parameters.AddRange(
method.GatherInternalParams(Context.ParserOptions.IsItaniumLikeAbi, true));
functionType.CallingConvention = method.CallingConvention;
functionType.IsDependent = method.IsDependent;
functionType.ReturnType = method.ReturnType;
}
else
{
var funcTypeParam = (FunctionType) decl.Type.Desugar().GetFinalPointee().Desugar();
functionType = new FunctionType(funcTypeParam);
}
// HACK : We should shift this call to VisitFunctionTypeParameters in VisitParameter. We can't do it now for (int i = 0; i < functionType.Parameters.Count; i++)
// because we need to use the module and a since a parameter's namespace is null, we can't reach the functionType.Parameters[i].Name = $"_{i}";
// translation unit and thus the module
VisitFunctionTypeParameters(function);
return true; return functionType;
} }
public override bool VisitMethodDecl(Method method) private DeclarationContext GetDeclContextForDelegates(DeclarationContext @namespace)
{ {
if (!base.VisitMethodDecl(method) || !method.IsVirtual || method.Ignore) if (Options.IsCLIGenerator)
return false; return @namespace is Function ? @namespace.Namespace : @namespace;
var module = method.TranslationUnit.Module;
AddDelegatesToDictionary(method, module);
return true; var module = @namespace.TranslationUnit.Module;
} if (namespacesDelegates.ContainsKey(module))
return namespacesDelegates[module];
private DelegateDefinition GetExistingDelegate(Module module, string delegateString)
{
if (module.Libraries.Count == 0)
return Context.Delegates.Values.FirstOrDefault(t => t.Signature == delegateString);
DelegateDefinition @delegate = null; Namespace parent = null;
if (new[] { module }.Union(module.Dependencies).Any( if (string.IsNullOrEmpty(module.OutputNamespace))
m => libsDelegates.ContainsKey(m) && libsDelegates[m].TryGetValue( {
delegateString, out @delegate))) var groups = module.Units.SelectMany(u => u.Declarations).OfType<Namespace>(
return @delegate; ).GroupBy(d => d.Name).Where(g => g.Any(d => d.HasDeclarations)).ToList();
if (groups.Count == 1)
parent = groups.Last().Last();
}
if (parent == null)
parent = module.Units.Last();
return null; var namespaceDelegates = new Namespace
{
Name = "Delegates",
Namespace = parent
};
namespacesDelegates.Add(module, namespaceDelegates);
return namespaceDelegates;
} }
private string GenerateDelegateSignature(IEnumerable<Parameter> @params, QualifiedType returnType) private string GetDelegateName(FunctionType functionType)
{ {
var typesBuilder = new StringBuilder(); var typesBuilder = new StringBuilder();
if (!returnType.Type.IsPrimitiveType(PrimitiveType.Void)) if (!functionType.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void))
{ {
typesBuilder.Insert(0, returnType.Visit(TypePrinter)); typesBuilder.Insert(0, functionType.ReturnType.Visit(TypePrinter));
typesBuilder.Append('_'); typesBuilder.Append('_');
} }
foreach (var parameter in @params)
foreach (var parameter in functionType.Parameters)
{ {
typesBuilder.Append(parameter.Visit(TypePrinter)); typesBuilder.Append(parameter.Visit(TypePrinter));
typesBuilder.Append('_'); typesBuilder.Append('_');
} }
if (typesBuilder.Length > 0) if (typesBuilder.Length > 0)
typesBuilder.Remove(typesBuilder.Length - 1, 1); typesBuilder.Remove(typesBuilder.Length - 1, 1);
var delegateName = FormatTypesStringForIdentifier(typesBuilder); var delegateName = FormatTypesStringForIdentifier(typesBuilder);
if (returnType.Type.IsPrimitiveType(PrimitiveType.Void)) if (functionType.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void))
delegateName.Insert(0, "Action_"); delegateName.Insert(0, "Action_");
else else
delegateName.Insert(0, "Func_"); delegateName.Insert(0, "Func_");
@ -239,9 +239,13 @@ namespace CppSharp.Passes
} }
} }
private Dictionary<Module, Namespace> namespacesDelegates = new Dictionary<Module, Namespace>(); private Dictionary<Module, DeclarationContext> namespacesDelegates = new Dictionary<Module, DeclarationContext>();
private CSharpTypePrinter typePrinter; private CSharpTypePrinter typePrinter;
private static readonly Dictionary<Module, Dictionary<string, DelegateDefinition>> libsDelegates =
new Dictionary<Module, Dictionary<string, DelegateDefinition>>(); /// <summary>
/// The generated typedefs. The tree can't be modified while
/// iterating over it, so we collect all the typedefs and add them at the end.
/// </summary>
private readonly List<TypedefDecl> delegates = new List<TypedefDecl>();
} }
} }

129
src/Generator/Passes/GenerateAnonymousDelegatesPass.cs

@ -1,129 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;
namespace CppSharp.Passes
{
/// <summary>
/// C++ function pointers are converted to delegates. If the function pointer is typedef'd then a delegate is
/// generated with that name, otherwise the generic Action or Func delegates are used. The delegates are marhsalled
/// using the GetDelegateForFunctionPointer and GetFunctionPointerForDelegate methods; unfortunately, these methods
/// don't support generic types, so marshalling fails when function pointers are used without typedefs. This pass
/// generates explicit delegates for these function pointers when used as function arguments or return types.
/// </summary>
public class GenerateAnonymousDelegatesPass : TranslationUnitPass
{
private struct Typedef
{
public DeclarationContext Context;
public TypedefDecl Declaration;
}
/// <summary>
/// The generated typedefs keyed by the qualified declaration context name. The tree can't be modified while
/// iterating over it, so we collect all the typedefs and add them at the end.
/// </summary>
private readonly Dictionary<string, List<Typedef>> allTypedefs = new Dictionary<string, List<Typedef>>();
public override bool VisitASTContext(ASTContext context)
{
bool result = base.VisitASTContext(context);
foreach (var typedef in allTypedefs)
{
foreach (var foo in typedef.Value)
{
foo.Context.Declarations.Add(foo.Declaration);
}
}
allTypedefs.Clear();
return result;
}
public override bool VisitFunctionDecl(Function function)
{
if (!base.VisitFunctionDecl(function) || function.Ignore)
return false;
function.ReturnType = CheckType(function.Namespace, function.ReturnType);
foreach (var parameter in function.Parameters)
{
parameter.QualifiedType = CheckType(function.Namespace, parameter.QualifiedType);
}
return true;
}
/// <summary>
/// Generates a new typedef for the given type if necessary and returns the new type.
/// </summary>
/// <param name="namespace">The namespace the typedef will be added to.</param>
/// <param name="type">The type to check.</param>
/// <returns>The new type.</returns>
private QualifiedType CheckType(DeclarationContext @namespace, QualifiedType type)
{
if (type.Type.IsDependent)
return type;
var pointerType = type.Type as PointerType;
if (pointerType == null)
return type;
var functionType = pointerType.Pointee as FunctionType;
if (functionType == null)
return type;
List<Typedef> typedefs;
if (!allTypedefs.TryGetValue(@namespace.QualifiedName, out typedefs))
{
typedefs = new List<Typedef>();
allTypedefs.Add(@namespace.QualifiedName, typedefs);
}
var typedef = FindMatchingTypedef(typedefs, functionType);
if (typedef == null)
{
for (int i = 0; i < functionType.Parameters.Count; i++)
{
functionType.Parameters[i].Name = string.Format("_{0}", i);
}
typedef = new TypedefDecl
{
Access = AccessSpecifier.Public,
Name = string.Format("__AnonymousDelegate{0}", typedefs.Count),
Namespace = @namespace,
QualifiedType = type,
IsSynthetized = true
};
typedefs.Add(new Typedef
{
Context = @namespace,
Declaration = typedef
});
}
var typedefType = new TypedefType
{
Declaration = typedef
};
return new QualifiedType(typedefType);
}
/// <summary>
/// Finds a typedef with the same return type and parameter types.
/// </summary>
/// <param name="typedefs">The typedef list to search.</param>
/// <param name="functionType">The function to match.</param>
/// <returns>The matching typedef, or null if not found.</returns>
private static TypedefDecl FindMatchingTypedef(IEnumerable<Typedef> typedefs, FunctionType functionType)
{
return (from typedef in typedefs
let type = (FunctionType)typedef.Declaration.Type.GetPointee()
where type.ReturnType == functionType.ReturnType &&
type.Parameters.SequenceEqual(functionType.Parameters, ParameterTypeComparer.Instance)
select typedef.Declaration).SingleOrDefault();
}
}
}

6
tests/CSharp/CSharp.cpp

@ -1382,6 +1382,12 @@ int funcWithTypedefedFuncPtrAsParam(typedefedFuncPtr* func)
return func(a, b); return func(a, b);
} }
typedefedFuncPtr* TestDuplicateDelegate::testDuplicateDelegate(int a)
{
typedefedFuncPtr* func;
return func;
}
void InlineNamespace::FunctionInsideInlineNamespace() void InlineNamespace::FunctionInsideInlineNamespace()
{ {
} }

6
tests/CSharp/CSharp.h

@ -1225,6 +1225,12 @@ void useStdStringJustAsParameter(std::string s);
typedef int (typedefedFuncPtr)(Foo* a, Bar b); typedef int (typedefedFuncPtr)(Foo* a, Bar b);
int DLL_API funcWithTypedefedFuncPtrAsParam(typedefedFuncPtr* func); int DLL_API funcWithTypedefedFuncPtrAsParam(typedefedFuncPtr* func);
class DLL_API TestDuplicateDelegate
{
public:
virtual typedefedFuncPtr* testDuplicateDelegate(int a);
};
inline namespace InlineNamespace inline namespace InlineNamespace
{ {

2
tests/Common/Common.Tests.cs

@ -556,7 +556,7 @@ public class CommonTests : GeneratorTestFixture
new TestDelegates().MarshalUnattributedDelegate(i => i); new TestDelegates().MarshalUnattributedDelegate(i => i);
} }
[Test, Platform(Exclude = "Win", Reason = "This test crashes our Windows build, possibly a problem with the NUnit runner there.")] [Test]
public void TestPassAnonymousDelegate() public void TestPassAnonymousDelegate()
{ {
var testDelegates = new TestDelegates(); var testDelegates = new TestDelegates();

Loading…
Cancel
Save