diff --git a/src/Generator/AST/VTables.cs b/src/Generator/AST/VTables.cs index 4599aa6a..8f4017ff 100644 --- a/src/Generator/AST/VTables.cs +++ b/src/Generator/AST/VTables.cs @@ -62,7 +62,8 @@ namespace CppSharp.AST { case CppAbi.Microsoft: return (from table in @class.Layout.VFTables - let j = table.Layout.Components.FindIndex(m => m.Method == function) + let j = table.Layout.Components.FindIndex(m => m.Method == function) - + (table.Layout.Components.Any(c => c.Kind == VTableComponentKind.RTTI) ? 1 : 0) where j >= 0 select j).First(); default: diff --git a/src/Generator/Generators/CSharp/CSharpSources.cs b/src/Generator/Generators/CSharp/CSharpSources.cs index 1277afe8..28fb4be3 100644 --- a/src/Generator/Generators/CSharp/CSharpSources.cs +++ b/src/Generator/Generators/CSharp/CSharpSources.cs @@ -1639,15 +1639,11 @@ namespace CppSharp.Generators.CSharp for (int i = 0; i < @class.Layout.VFTables.Count; i++) { VFTableInfo vftable = @class.Layout.VFTables[i]; - int size = vftable.Layout.Components.Count; - string vfptr = $"vfptr{(destructorOnly ? "_dtor" : string.Empty)}{i}"; - WriteLine($@"var {vfptr} = Marshal.AllocHGlobal({size} * { - Context.TargetInfo.PointerWidth / 8});"); - WriteLine($"__handleManagedVTables.Add(new global::CppSharp.Runtime.SafeUnmanagedMemoryHandle({vfptr}, true));"); - WriteLine($"{managedVTables}[{i}] = {vfptr}.ToPointer();"); AllocateNewVTableEntries(vftable.Layout.Components, wrappedEntries, - @class.Layout.VTablePointers[i].Offset, i, destructorOnly); + @class.Layout.VTablePointers[i].Offset, i, + vftable.Layout.Components.Any(c => c.Kind == VTableComponentKind.RTTI) ? 1 : 0, + destructorOnly); } } @@ -1657,33 +1653,39 @@ namespace CppSharp.Generators.CSharp var managedVTables = destructorOnly ? "__ManagedVTablesDtorOnly" : "__ManagedVTables"; WriteLine($"{managedVTables} = new void*[1];"); - string suffix = destructorOnly ? "_dtor" : string.Empty; - int size = @class.Layout.Layout.Components.Count; - uint pointerSize = Context.TargetInfo.PointerWidth / 8; - WriteLine($"var vtptr{suffix} = Marshal.AllocHGlobal({size} * {pointerSize});"); - WriteLine($"__handleManagedVTables.Add(new global::CppSharp.Runtime.SafeUnmanagedMemoryHandle(vtptr{suffix}, true));"); - - WriteLine($@"var vfptr{suffix}0 = vtptr{suffix} + { - VTables.ItaniumOffsetToTopAndRTTI} * {pointerSize};"); - WriteLine($"{managedVTables}[0] = vfptr{suffix}0.ToPointer();"); - AllocateNewVTableEntries(@class.Layout.Layout.Components, - wrappedEntries, @class.Layout.VTablePointers[0].Offset, 0, destructorOnly); + wrappedEntries, @class.Layout.VTablePointers[0].Offset, 0, + VTables.ItaniumOffsetToTopAndRTTI, destructorOnly); } private void AllocateNewVTableEntries(IList entries, - IList wrappedEntries, uint vptrOffset, int tableIndex, bool destructorOnly) + IList wrappedEntries, uint vptrOffset, int tableIndex, + int offsetRTTI, bool destructorOnly) { - var pointerSize = Context.TargetInfo.PointerWidth / 8; + string suffix = (destructorOnly ? "_dtor" : string.Empty) + + (tableIndex == 0 ? string.Empty : tableIndex.ToString(CultureInfo.InvariantCulture)); + int size = entries.Count; + uint pointerSize = Context.TargetInfo.PointerWidth / 8; + string vtptr = $"vtptr{suffix}"; + // allocate memory for the new v-table + WriteLine($"var {vtptr} = Marshal.AllocHGlobal({size} * {pointerSize});"); + // ensure it's automatically freed when no longer needed + WriteLine($"__handleManagedVTables.Add(new global::CppSharp.Runtime.SafeUnmanagedMemoryHandle({vtptr}, true));"); + + string vfptr = $"vfptr{suffix}"; + // obtain a pointer in the table to the start of virtual functions + WriteLine($"var {vfptr} = {vtptr} + {offsetRTTI} * {pointerSize};"); + var managedVTables = destructorOnly ? "__ManagedVTablesDtorOnly" : "__ManagedVTables"; + WriteLine($"{managedVTables}[{tableIndex}] = {vfptr}.ToPointer();"); + + // fill the newly allocated v-table for (var i = 0; i < entries.Count; i++) { var entry = entries[i]; - var offset = pointerSize - * (i - (Context.ParserOptions.IsMicrosoftAbi ? 0 : VTables.ItaniumOffsetToTopAndRTTI)); + var offset = pointerSize * (i - offsetRTTI); var nativeVftableEntry = $@"*(void**) (new IntPtr(*(void**) { Helpers.InstanceIdentifier}) + {vptrOffset} + {offset})"; - string vfptr = $"vfptr{(destructorOnly ? "_dtor" : string.Empty)}{tableIndex}"; var managedVftableEntry = $"*(void**) ({vfptr} + {offset})"; if ((entry.Kind == VTableComponentKind.FunctionPointer || @@ -1691,8 +1693,10 @@ namespace CppSharp.Generators.CSharp !entry.IsIgnored() && (!destructorOnly || entry.Method.IsDestructor || Context.Options.ExplicitlyPatchedVirtualFunctions.Contains(entry.Method.QualifiedOriginalName))) + // patch with pointers to managed code where needed WriteLine("{0} = _Thunks[{1}];", managedVftableEntry, wrappedEntries.IndexOf(entry)); else + // and simply copy the rest WriteLine("{0} = {1};", managedVftableEntry, nativeVftableEntry); } } @@ -2620,8 +2624,8 @@ namespace CppSharp.Generators.CSharp @class = (Class) method.OriginalFunction.Namespace; if (Context.ParserOptions.IsMicrosoftAbi) - vtableIndex = @class.Layout.VFTables.IndexOf(@class.Layout.VFTables.Where( - v => v.Layout.Components.Any(c => c.Method == @virtual)).First()); + vtableIndex = @class.Layout.VFTables.IndexOf(@class.Layout.VFTables.First( + v => v.Layout.Components.Any(c => c.Method == @virtual))); WriteLine($@"var {Helpers.SlotIdentifier} = *(void**) ((IntPtr) { (thisParam != null ? $"{thisParam.Name}." diff --git a/tests/VTables/VTables.Tests.cs b/tests/VTables/VTables.Tests.cs index 974bf413..611543b7 100644 --- a/tests/VTables/VTables.Tests.cs +++ b/tests/VTables/VTables.Tests.cs @@ -102,4 +102,8 @@ public class VTablesTests : GeneratorTestFixture Assert.That(foo.S, Is.EqualTo("test")); } } + + [Test] + public void TestTypeNameRTTI() => + Assert.That(BaseClassVirtual.TypeName, Does.EndWith(nameof(BaseClassVirtual))); } diff --git a/tests/VTables/VTables.cpp b/tests/VTables/VTables.cpp index 7b084299..73f5b6e9 100644 --- a/tests/VTables/VTables.cpp +++ b/tests/VTables/VTables.cpp @@ -1,4 +1,5 @@ #include "VTables.h" +#include Foo::Foo() { @@ -66,6 +67,11 @@ BaseClassVirtual* BaseClassVirtual::getBasePtr() return new DerivedClassVirtual(); } +const char* BaseClassVirtual::getTypeName() +{ + return typeid(BaseClassVirtual).name(); +} + DerivedClassVirtual::DerivedClassVirtual() { } diff --git a/tests/VTables/VTables.cs b/tests/VTables/VTables.cs index 76c6b764..0511e955 100644 --- a/tests/VTables/VTables.cs +++ b/tests/VTables/VTables.cs @@ -12,6 +12,13 @@ namespace CppSharp.Tests { } + public override void Setup(Driver driver) + { + base.Setup(driver); + + driver.ParserOptions.EnableRTTI = true; + } + public override void SetupPasses(Driver driver) { driver.Context.TranslationUnitPasses.AddPass(new FunctionToInstanceMethodPass()); diff --git a/tests/VTables/VTables.h b/tests/VTables/VTables.h index 2aecc5c3..40fa8880 100644 --- a/tests/VTables/VTables.h +++ b/tests/VTables/VTables.h @@ -31,6 +31,7 @@ public: virtual int retInt(); static BaseClassVirtual getBase(); static BaseClassVirtual* getBasePtr(); + static const char* getTypeName(); }; class DLL_API DerivedClassVirtual : public BaseClassVirtual diff --git a/tests/VTables/premake4.lua b/tests/VTables/premake4.lua index c1ddf74c..e96d46ae 100644 --- a/tests/VTables/premake4.lua +++ b/tests/VTables/premake4.lua @@ -1,2 +1,5 @@ group "Tests/VTables" - SetupTestCSharp("VTables") \ No newline at end of file + SetupTestCSharp("VTables") + +project("VTables.Native") + rtti "On" \ No newline at end of file