Browse Source

Generate delegates for non-typedef'd function pointers to prevent runtime exceptions during marshalling.

pull/440/head
Chris Spencer 11 years ago
parent
commit
0c535d361b
  1. 1
      src/AST/Declaration.cs
  2. 1
      src/Generator/Driver.cs
  3. 12
      src/Generator/Generators/CLI/CLIMarshal.cs
  4. 112
      src/Generator/Passes/GenerateAnonymousDelegatesPass.cs
  5. 17
      tests/Basic/Basic.Tests.cs
  6. 23
      tests/Basic/Basic.cpp
  7. 5
      tests/Basic/Basic.h

1
src/AST/Declaration.cs

@ -360,6 +360,7 @@ namespace CppSharp.AST
{ {
public Type Type { get { return QualifiedType.Type; } } public Type Type { get { return QualifiedType.Type; } }
public QualifiedType QualifiedType { get; set; } public QualifiedType QualifiedType { get; set; }
public bool IsSynthetized { get; set; }
public override T Visit<T>(IDeclVisitor<T> visitor) public override T Visit<T>(IDeclVisitor<T> visitor)
{ {

1
src/Generator/Driver.cs

@ -253,6 +253,7 @@ 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());
if (Options.GenerateConversionOperators) if (Options.GenerateConversionOperators)
TranslationUnitPasses.AddPass(new ConstructorToConversionOperatorPass()); TranslationUnitPasses.AddPass(new ConstructorToConversionOperatorPass());

12
src/Generator/Generators/CLI/CLIMarshal.cs

@ -586,7 +586,17 @@ namespace CppSharp.Generators.CLI
FunctionType func; FunctionType func;
if (decl.Type.IsPointerTo(out func)) if (decl.Type.IsPointerTo(out func))
{ {
VisitDelegateType(func, "::" + typedef.Declaration.QualifiedOriginalName); // Use the original typedef name if available, otherwise just use the function pointer type
string cppTypeName;
if (!decl.IsSynthetized)
cppTypeName = "::" + typedef.Declaration.QualifiedOriginalName;
else
{
var cppTypePrinter = new CppTypePrinter(Context.Driver.TypeDatabase);
cppTypeName = decl.Type.Visit(cppTypePrinter, quals);
}
VisitDelegateType(func, cppTypeName);
return true; return true;
} }

112
src/Generator/Passes/GenerateAnonymousDelegatesPass.cs

@ -0,0 +1,112 @@
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
{
/// <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 Dictionary<DeclarationContext, List<TypedefDecl>> allTypedefs =
new Dictionary<DeclarationContext, List<TypedefDecl>>();
public override bool VisitTranslationUnit(TranslationUnit unit)
{
bool result = base.VisitTranslationUnit(unit);
foreach (var typedef in allTypedefs)
typedef.Key.Declarations.AddRange(typedef.Value);
allTypedefs.Clear();
return result;
}
public override bool VisitFunctionDecl(Function function)
{
if (!base.VisitFunctionDecl(function))
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)
{
var pointerType = type.Type as PointerType;
if (pointerType == null)
return type;
var functionType = pointerType.Pointee as FunctionType;
if (functionType == null)
return type;
List<TypedefDecl> typedefs;
if (!allTypedefs.TryGetValue(@namespace, out typedefs))
{
typedefs = new List<TypedefDecl>();
allTypedefs.Add(@namespace, 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(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 TypedefDecl FindMatchingTypedef(List<TypedefDecl> typedefs, FunctionType functionType)
{
return (from typedef in typedefs
let type = (FunctionType)typedef.Type.GetPointee()
where type.ReturnType == functionType.ReturnType &&
type.Parameters.SequenceEqual(functionType.Parameters, new ParameterTypeComparer())
select typedef).SingleOrDefault();
}
}
}

17
tests/Basic/Basic.Tests.cs

@ -443,6 +443,23 @@ public class BasicTests : GeneratorTestFixture
new TestDelegates().MarshalUnattributedDelegate(i => i); new TestDelegates().MarshalUnattributedDelegate(i => i);
} }
[Test]
public void TestPassAnonymousDelegate()
{
var testDelegates = new TestDelegates();
int value = testDelegates.MarshalAnonymousDelegate(i => i * 2);
Assert.AreEqual(2, value);
}
[Test]
public void TestGetAnonymousDelegate()
{
var testDelegates = new TestDelegates();
var @delegate = testDelegates.MarshalAnonymousDelegate4();
int value = @delegate.Invoke(1);
Assert.AreEqual(2, value);
}
[Test] [Test]
public void TestFixedArrays() public void TestFixedArrays()
{ {

23
tests/Basic/Basic.cpp

@ -323,6 +323,29 @@ void TestDelegates::MarshalUnattributedDelegate(DelegateInGlobalNamespace del)
{ {
} }
int TestDelegates::MarshalAnonymousDelegate(int (*del)(int n))
{
return del(1);
}
void TestDelegates::MarshalAnonymousDelegate2(int (*del)(int n))
{
}
void TestDelegates::MarshalAnonymousDelegate3(float (*del)(float n))
{
}
int f(int n)
{
return n * 2;
}
int (*TestDelegates::MarshalAnonymousDelegate4())(int n)
{
return f;
}
std::string HasStdString::testStdString(std::string s) std::string HasStdString::testStdString(std::string s)
{ {
return s + "_test"; return s + "_test";

5
tests/Basic/Basic.h

@ -308,6 +308,11 @@ struct DLL_API TestDelegates
int CDecl(DelegateCDecl del) { return del(1); } int CDecl(DelegateCDecl del) { return del(1); }
void MarshalUnattributedDelegate(DelegateInGlobalNamespace del); void MarshalUnattributedDelegate(DelegateInGlobalNamespace del);
int MarshalAnonymousDelegate(int (*del)(int n));
void MarshalAnonymousDelegate2(int (*del)(int n));
void MarshalAnonymousDelegate3(float (*del)(float n));
int (*MarshalAnonymousDelegate4())(int n);
DelegateInClass A; DelegateInClass A;
DelegateInGlobalNamespace B; DelegateInGlobalNamespace B;
// As long as we can't marshal them make sure they're ignored // As long as we can't marshal them make sure they're ignored

Loading…
Cancel
Save