From 24a8db6a3266cd708fdf4cba595ab3c7aa6f4052 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Sun, 2 Feb 2014 23:07:48 +0200 Subject: [PATCH] Fixed the generation of internal implementations of abstract classes to actually work. Signed-off-by: Dimitar Dobrev --- src/Generator/AST/VTables.cs | 3 +- .../Generators/CSharp/CSharpTextTemplate.cs | 122 +++++++++--------- .../GenerateAbstractImplementationsPass.cs | 36 ++---- src/Generator/Utils/Utils.cs | 8 -- tests/Basic/Basic.Tests.cs | 2 +- tests/Basic/Basic.cs | 1 + 6 files changed, 78 insertions(+), 94 deletions(-) diff --git a/src/Generator/AST/VTables.cs b/src/Generator/AST/VTables.cs index e4b560f2..d9730574 100644 --- a/src/Generator/AST/VTables.cs +++ b/src/Generator/AST/VTables.cs @@ -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; } } } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 20aec254..0f6e2fd8 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -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 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 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} _References;", dictionary); NewLine(); @@ -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(); @@ -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 } 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 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 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 // 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 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 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 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 } } - 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( diff --git a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs index 32af3fcb..840d7018 100644 --- a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs +++ b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs @@ -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 var internalImpl = new Class { Name = @class.Name + "Internal", - Access = @class.Access, + Access = AccessSpecifier.Private, Namespace = @class.Namespace }; @@ -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(); diff --git a/src/Generator/Utils/Utils.cs b/src/Generator/Utils/Utils.cs index ae900287..6a7ca369 100644 --- a/src/Generator/Utils/Utils.cs +++ b/src/Generator/Utils/Utils.cs @@ -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"; - } - } } diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index a4521679..983cac43 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -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(); diff --git a/tests/Basic/Basic.cs b/tests/Basic/Basic.cs index df89f9a3..7ff0c33d 100644 --- a/tests/Basic/Basic.cs +++ b/tests/Basic/Basic.cs @@ -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)