Browse Source

Merge pull request #201 from ddobrev/master

Fixed the generation of internal implementations of abstract classes to actually work
pull/204/head
João Matos 11 years ago
parent
commit
40597b0bd3
  1. 3
      src/Generator/AST/VTables.cs
  2. 122
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  3. 36
      src/Generator/Passes/GenerateAbstractImplementationsPass.cs
  4. 8
      src/Generator/Utils/Utils.cs
  5. 2
      tests/Basic/Basic.Tests.cs
  6. 1
      tests/Basic/Basic.cs

3
src/Generator/AST/VTables.cs

@ -80,7 +80,8 @@ namespace CppSharp.AST @@ -80,7 +80,8 @@ namespace CppSharp.AST
where j >= 0
select j).First();
default:
return @class.Layout.Layout.Components.FindIndex(m => m.Method == method);
// ignore offset to top and RTTI
return @class.Layout.Layout.Components.FindIndex(m => m.Method == method) - 2;
}
}
}

122
src/Generator/Generators/CSharp/CSharpTextTemplate.cs

@ -900,7 +900,7 @@ namespace CppSharp.Generators.CSharp @@ -900,7 +900,7 @@ namespace CppSharp.Generators.CSharp
{
if (method.IsOverride && method.IsSynthetized)
{
GenerateVirtualTableFunctionCall(function, @class);
GenerateVirtualTableFunctionCall(method, @class);
}
else
{
@ -1026,7 +1026,7 @@ namespace CppSharp.Generators.CSharp @@ -1026,7 +1026,7 @@ namespace CppSharp.Generators.CSharp
var method = function as Method;
if (method != null && method.IsOverride && method.IsSynthetized)
{
GenerateVirtualTableFunctionCall(function, @class);
GenerateVirtualTableFunctionCall(method, @class);
}
else
{
@ -1321,9 +1321,9 @@ namespace CppSharp.Generators.CSharp @@ -1321,9 +1321,9 @@ namespace CppSharp.Generators.CSharp
const string dictionary = "System.Collections.Generic.Dictionary";
WriteLine("private static IntPtr[] _OldVTables;");
WriteLine("private static IntPtr[] _NewVTables;");
WriteLine("private static IntPtr[] _Thunks;");
WriteLine("private static void*[] _OldVTables;");
WriteLine("private static void*[] _NewVTables;");
WriteLine("private static void*[] _Thunks;");
WriteLine("private static {0}<IntPtr, WeakReference> _References;", dictionary);
NewLine();
@ -1372,8 +1372,8 @@ namespace CppSharp.Generators.CSharp @@ -1372,8 +1372,8 @@ namespace CppSharp.Generators.CSharp
// Get the _Thunks
WriteLine("if (_Thunks == null)");
WriteStartBraceIndent();
WriteLine("_Thunks = new IntPtr[{0}];", wrappedEntries.Count);
WriteStartBraceIndent();
WriteLine("_Thunks = new void*[{0}];", wrappedEntries.Count);
var uniqueEntries = new HashSet<VTableComponent>();
@ -1385,7 +1385,7 @@ namespace CppSharp.Generators.CSharp @@ -1385,7 +1385,7 @@ namespace CppSharp.Generators.CSharp
var instance = name + "Instance";
if (uniqueEntries.Add(entry))
WriteLine("{0} += {1}Hook;", instance, name);
WriteLine("_Thunks[{0}] = Marshal.GetFunctionPointerForDelegate({1});",
WriteLine("_Thunks[{0}] = Marshal.GetFunctionPointerForDelegate({1}).ToPointer();",
i, instance);
}
WriteCloseBraceIndent();
@ -1411,44 +1411,44 @@ namespace CppSharp.Generators.CSharp @@ -1411,44 +1411,44 @@ namespace CppSharp.Generators.CSharp
}
private void SaveOriginalVTablePointersMS(Class @class)
{
WriteLine("_OldVTables = new IntPtr[{0}];", @class.Layout.VFTables.Count);
{
WriteLine("_OldVTables = new void*[{0}];", @class.Layout.VFTables.Count);
for (int i = 0; i < @class.Layout.VFTables.Count; i++)
{
WriteLine("_OldVTables[{0}] = native->vfptr{0};", i);
{
WriteLine("_OldVTables[{0}] = native->vfptr{0}.ToPointer();", i);
}
}
private void SaveOriginalVTablePointersItanium()
{
WriteLine("_OldVTables = new IntPtr[1];");
WriteLine("_OldVTables[0] = native->vfptr0;");
{
WriteLine("_OldVTables = new void*[1];");
WriteLine("_OldVTables[0] = native->vfptr0.ToPointer();");
}
private void AllocateNewVTablesMS(Class @class)
{
WriteLine("_NewVTables = new IntPtr[{0}];", @class.Layout.VFTables.Count);
{
WriteLine("_NewVTables = new void*[{0}];", @class.Layout.VFTables.Count);
for (int tableIndex = 0; tableIndex < @class.Layout.VFTables.Count; tableIndex++)
{
var vfptr = @class.Layout.VFTables[tableIndex];
var size = vfptr.Layout.Components.Count;
WriteLine("var vfptr{0} = Marshal.AllocHGlobal({1} * {2});",
tableIndex, size, Driver.Options.Is32Bit ? 4 : 8);
WriteLine("_NewVTables[{0}] = vfptr{0};", tableIndex);
tableIndex, size, Driver.Options.Is32Bit ? 4 : 8);
WriteLine("_NewVTables[{0}] = vfptr{0}.ToPointer();", tableIndex);
for (int entryIndex = 0; entryIndex < vfptr.Layout.Components.Count; entryIndex++)
{
var entry = vfptr.Layout.Components[entryIndex];
var offsetInBytes = VTables.GetVTableComponentIndex(@class, entry)
* (Driver.Options.Is32Bit ? 4 : 8);
if (entry.Ignore)
WriteLine("*(IntPtr*)(vfptr{0} + {1}) = *(IntPtr*)(native->vfptr{0} + {1});",
if (entry.Ignore)
WriteLine("*(void**)(vfptr{0} + {1}) = *(void**)(native->vfptr{0} + {1});",
tableIndex, offsetInBytes);
else
WriteLine("*(IntPtr*)(vfptr{0} + {1}) = _Thunks[{2}];", tableIndex,
else
WriteLine("*(void**)(vfptr{0} + {1}) = _Thunks[{2}];", tableIndex,
offsetInBytes, entryIndex);
}
}
@ -1456,34 +1456,34 @@ namespace CppSharp.Generators.CSharp @@ -1456,34 +1456,34 @@ namespace CppSharp.Generators.CSharp
WriteCloseBraceIndent();
NewLine();
for (var i = 0; i < @class.Layout.VFTables.Count; i++)
WriteLine("native->vfptr{0} = _NewVTables[{0}];", i);
for (var i = 0; i < @class.Layout.VFTables.Count; i++)
WriteLine("native->vfptr{0} = new IntPtr(_NewVTables[{0}]);", i);
}
private void AllocateNewVTablesItanium(Class @class, IList<VTableComponent> entries)
{
WriteLine("_NewVTables = new IntPtr[1];");
WriteLine("_NewVTables = new void*[1];");
// reserve space for the offset-to-top and RTTI pointers as well
var size = entries.Count;
WriteLine("var vfptr{0} = Marshal.AllocHGlobal({1} * {2});", 0, size, Driver.Options.Is32Bit ? 4 : 8);
WriteLine("_NewVTables[{0}] = vfptr{0};", 0);
WriteLine("var vfptr{0} = Marshal.AllocHGlobal({1} * {2});", 0, size, Driver.Options.Is32Bit ? 4 : 8);
WriteLine("_NewVTables[{0}] = vfptr0.ToPointer();");
for (int i = 0; i < entries.Count; i++)
{
var entry = entries[i];
var offsetInBytes = VTables.GetVTableComponentIndex(@class, entry)
* (Driver.Options.Is32Bit ? 4 : 8);
if (entry.Ignore)
WriteLine("*(IntPtr*)(vfptr0 + {0}) = *(IntPtr*)(native->vfptr0 + {0});", offsetInBytes);
else
WriteLine("*(IntPtr*)(vfptr0 + {0}) = _Thunks[{1}];", offsetInBytes, i);
if (entry.Ignore)
WriteLine("*(void**)(vfptr0 + {0}) = *(void**)(native->vfptr0 + {0});", offsetInBytes);
else
WriteLine("*(void**)(vfptr0 + {0}) = _Thunks[{1}];", offsetInBytes, i);
}
WriteCloseBraceIndent();
NewLine();
WriteLine("native->vfptr0 = _NewVTables[0];");
NewLine();
WriteLine("native->vfptr0 = new IntPtr(_NewVTables[0]);");
}
private void GenerateVTableClassSetupCall(Class @class, bool addPointerGuard = false)
@ -1494,7 +1494,8 @@ namespace CppSharp.Generators.CSharp @@ -1494,7 +1494,8 @@ namespace CppSharp.Generators.CSharp
// called from internal ctors which may have been passed an IntPtr.Zero
if (addPointerGuard)
{
WriteLine("if ({0} != global::System.IntPtr.Zero)", Helpers.InstanceIdentifier);
WriteLine("if ({0} != global::System.IntPtr.Zero && !isInternalImpl)",
Helpers.InstanceIdentifier);
PushIndent();
}
WriteLine("SetupVTables({0});", Generator.GeneratedIdentifier("Instance"));
@ -1597,12 +1598,16 @@ namespace CppSharp.Generators.CSharp @@ -1597,12 +1598,16 @@ namespace CppSharp.Generators.CSharp
private void GenerateVTableMethodDelegates(Class @class, Method method)
{
PushBlock(CSharpBlockKind.VTableDelegate);
var cleanSig = method.Signature.ReplaceLineBreaks("");
cleanSig = Regex.Replace(cleanSig, @"\s+", " ");
WriteLine("// {0}", cleanSig);
PushBlock(CSharpBlockKind.VTableDelegate);
// This works around a parser bug, see https://github.com/mono/CppSharp/issues/202
if (method.Signature != null)
{
var cleanSig = method.Signature.ReplaceLineBreaks("");
cleanSig = Regex.Replace(cleanSig, @"\s+", " ");
WriteLine("// {0}", cleanSig);
}
WriteLine("[SuppressUnmanagedCodeSecurity]");
WriteLine("[UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.{0})]",
Helpers.ToCSharpCallConv(method.CallingConvention));
@ -1610,19 +1615,19 @@ namespace CppSharp.Generators.CSharp @@ -1610,19 +1615,19 @@ namespace CppSharp.Generators.CSharp
CSharpTypePrinterResult retType;
var @params = GatherInternalParams(method, out retType);
var delegateName = GetVTableMethodDelegateName(method);
WriteLine("delegate {0} {1}({2});", retType, delegateName,
var vTableMethodDelegateName = GetVTableMethodDelegateName(method);
WriteLine("internal delegate {0} {1}({2});", retType, vTableMethodDelegateName,
string.Join(", ", @params));
WriteLine("static {0} {0}Instance;", delegateName);
WriteLine("private static {0} {0}Instance;", vTableMethodDelegateName);
NewLine();
WriteLine("static {0} {1}Hook({2})", retType, delegateName,
WriteLine("private static {0} {1}Hook({2})", retType, vTableMethodDelegateName,
string.Join(", ", @params));
WriteStartBraceIndent();
WriteLine("if (!_References.ContainsKey(instance))");
WriteLineIndent("throw new Exception(\"No managed instance was found\");");
WriteLineIndent("throw new global::System.Exception(\"No managed instance was found\");");
NewLine();
WriteLine("var target = ({0}) _References[instance].Target;", @class.Name);
@ -1904,12 +1909,14 @@ namespace CppSharp.Generators.CSharp @@ -1904,12 +1909,14 @@ namespace CppSharp.Generators.CSharp
PopBlock(NewLineKind.BeforeNextBlock);
PushBlock(CSharpBlockKind.Method);
WriteLine("public {0}(global::System.IntPtr native){1}", safeIdentifier,
@class.IsValueType ? " : this()" : string.Empty);
WriteLine("public {0}(global::System.IntPtr native, bool isInternalImpl = false){1}",
safeIdentifier, @class.IsValueType ? " : this()" : string.Empty);
var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType;
if (hasBaseClass)
WriteLineIndent(": base(native)");
WriteLineIndent(": base(native{0})",
@class.Methods.Any(m => !m.IsPure && m.IsOverride && m.IsSynthetized) ?
", true" : string.Empty);
WriteStartBraceIndent();
@ -2107,28 +2114,23 @@ namespace CppSharp.Generators.CSharp @@ -2107,28 +2114,23 @@ namespace CppSharp.Generators.CSharp
}
}
private void GenerateVirtualTableFunctionCall(Function method, Class @class)
private void GenerateVirtualTableFunctionCall(Method method, Class @class)
{
string delegateId;
Write(GetVirtualCallDelegate(method, @class, Driver.Options.Is32Bit, out delegateId));
GenerateFunctionCall(delegateId, method.Parameters, method);
}
public static string GetVirtualCallDelegate(Function method, Class @class,
public string GetVirtualCallDelegate(Method method, Class @class,
bool is32Bit, out string delegateId)
{
var virtualCallBuilder = new StringBuilder();
virtualCallBuilder.AppendFormat("void* vtable = *((void**) {0}.ToPointer());",
Helpers.InstanceIdentifier);
virtualCallBuilder.AppendLine();
var i = VTables.GetVTableIndex(method, @class);
virtualCallBuilder.AppendFormat(
"void* slot = *((void**) vtable + {0} * {1});", i, is32Bit ? 4 : 8);
virtualCallBuilder.AppendFormat("void* slot = *(void**) ((({0}.Internal*) {1})->vfptr0 + {2} * {3});",
@class.BaseClass.Name, Helpers.InstanceIdentifier, i, is32Bit ? 4 : 8);
virtualCallBuilder.AppendLine();
string @delegate = ASTHelpers.GetDelegateName(method);
string @delegate = GetVTableMethodDelegateName((Method) method.OriginalFunction);
delegateId = Generator.GeneratedIdentifier(@delegate);
virtualCallBuilder.AppendFormat(

36
src/Generator/Passes/GenerateAbstractImplementationsPass.cs

@ -58,32 +58,20 @@ namespace CppSharp.Passes @@ -58,32 +58,20 @@ namespace CppSharp.Passes
var abstractMethods = GetRelevantAbstractMethods(@class);
foreach (var abstractMethod in abstractMethods)
{
var method = new Method(abstractMethod) { Namespace = internalImpl };
internalImpl.Methods.Add(method);
var @delegate = new TypedefDecl
{
Name = ASTHelpers.GetDelegateName(abstractMethod),
QualifiedType = abstractMethod.GetFunctionType(),
IgnoreFlags = abstractMethod.IgnoreFlags,
Namespace = internalImpl,
Access = AccessSpecifier.Private
};
internalImpl.Typedefs.Add(@delegate);
}
internalImpl.Methods.AddRange(
from method in abstractMethods
select new Method(method)
{
Namespace = internalImpl,
OriginalFunction = method,
IsPure = false,
IsOverride = true,
IsSynthetized = true
});
internalImpl.Layout = new ClassLayout(@class.Layout);
FillVTable(@class, abstractMethods, internalImpl);
foreach (var method in internalImpl.Methods)
{
method.IsPure = false;
method.IsOverride = true;
method.IsSynthetized = true;
}
return internalImpl;
}
@ -92,7 +80,7 @@ namespace CppSharp.Passes @@ -92,7 +80,7 @@ namespace CppSharp.Passes
var internalImpl = new Class
{
Name = @class.Name + "Internal",
Access = @class.Access,
Access = AccessSpecifier.Private,
Namespace = @class.Namespace
};
@ -187,7 +175,7 @@ namespace CppSharp.Passes @@ -187,7 +175,7 @@ namespace CppSharp.Passes
var j = vtableComponents.FindIndex(v => v.Method == abstractMethods[i]);
var vtableComponent = vtableComponents[j];
vtableComponent.Declaration = internalImplementation.Methods[i];
vtableComponents[j] = vtableComponents[j];
vtableComponents[j] = vtableComponent;
}
internalImplementation.Layout.Layout.Components.Clear();

8
src/Generator/Utils/Utils.cs

@ -235,12 +235,4 @@ namespace CppSharp @@ -235,12 +235,4 @@ namespace CppSharp
return assembly.GetTypes().Where(baseType.IsAssignableFrom);
}
}
public static class ASTHelpers
{
public static string GetDelegateName(Function method)
{
return "_" + char.ToLowerInvariant(method.Name[0]) + method.Name.Substring(1) + "Delegate";
}
}
}

2
tests/Basic/Basic.Tests.cs

@ -88,7 +88,7 @@ public class BasicTests : GeneratorTestFixture @@ -88,7 +88,7 @@ public class BasicTests : GeneratorTestFixture
Assert.That(result.C, Is.EqualTo(16));
}
[Test, Ignore]
[Test]
public void TestAbstractReturnType()
{
var returnsAbstractFoo = new ReturnsAbstractFoo();

1
tests/Basic/Basic.cs

@ -16,6 +16,7 @@ namespace CppSharp.Tests @@ -16,6 +16,7 @@ namespace CppSharp.Tests
{
if (driver.Options.IsCSharpGenerator)
driver.Options.GenerateAbstractImpls = true;
driver.Options.GenerateVirtualTables = true;
}
public override void Preprocess(Driver driver, ASTContext ctx)

Loading…
Cancel
Save