Browse Source

Fixed the generation of internal implementations of abstract classes to actually work.

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

36
src/Generator/Passes/GenerateAbstractImplementationsPass.cs

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

8
src/Generator/Utils/Utils.cs

@ -235,12 +235,4 @@ namespace CppSharp
return assembly.GetTypes().Where(baseType.IsAssignableFrom); 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
Assert.That(result.C, Is.EqualTo(16)); Assert.That(result.C, Is.EqualTo(16));
} }
[Test, Ignore] [Test]
public void TestAbstractReturnType() public void TestAbstractReturnType()
{ {
var returnsAbstractFoo = new ReturnsAbstractFoo(); var returnsAbstractFoo = new ReturnsAbstractFoo();

1
tests/Basic/Basic.cs

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

Loading…
Cancel
Save