From a74d9393e6b4360381129244b7ad15949ff9273d Mon Sep 17 00:00:00 2001 From: Alex Corrado Date: Tue, 7 Jun 2011 19:48:17 -0400 Subject: [PATCH] Initial support for calling virtual methods of non-primary base classes --- src/Mono.VisualC.Interop/ABI/CppAbi.cs | 3 + .../ABI/Impl/ItaniumAbi.cs | 2 +- .../ABI/Impl/ItaniumTypeInfo.cs | 64 +++++++++++++++++++ src/Mono.VisualC.Interop/CppTypeInfo.cs | 62 ++++++++++++++---- src/Mono.VisualC.Interop/Makefile.am | 1 + .../Mono.VisualC.Interop.csproj | 1 + .../Util/LazyGeneratedList.cs | 53 ++++++++++----- .../Util/MethodSignature.cs | 12 ++++ 8 files changed, 167 insertions(+), 31 deletions(-) create mode 100644 src/Mono.VisualC.Interop/ABI/Impl/ItaniumTypeInfo.cs diff --git a/src/Mono.VisualC.Interop/ABI/CppAbi.cs b/src/Mono.VisualC.Interop/ABI/CppAbi.cs index 12b1a321..94ad2a5c 100644 --- a/src/Mono.VisualC.Interop/ABI/CppAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/CppAbi.cs @@ -343,6 +343,9 @@ namespace Mono.VisualC.Interop.ABI { return null; var sig = typeInfo.VirtualMethods [vtableIndex]; + if (sig == null) + return null; + var interfaceMethod = sig.OrigMethod; var targetMethod = FindManagedOverrideTarget (interfaceMethod); if (targetMethod == null) diff --git a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs index 29c7e92a..827c0ba3 100644 --- a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs @@ -55,7 +55,7 @@ namespace Mono.VisualC.Interop.ABI { protected override CppTypeInfo MakeTypeInfo (IEnumerable methods) { - return new CppTypeInfo (this, GetVirtualMethodSlots (methods), layout_type, wrapper_type); + return new ItaniumTypeInfo (this, GetVirtualMethodSlots (methods), layout_type, wrapper_type); } private IEnumerable GetVirtualMethodSlots (IEnumerable methods) diff --git a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumTypeInfo.cs b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumTypeInfo.cs new file mode 100644 index 00000000..741a9f64 --- /dev/null +++ b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumTypeInfo.cs @@ -0,0 +1,64 @@ +// Author: +// Alexander Corrado (alexander.corrado@gmail.com) +// +// Copyright (C) 2011 Alexander Corrado +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Reflection; + +using Mono.VisualC.Interop.Util; + +namespace Mono.VisualC.Interop.ABI { + + public class ItaniumTypeInfo : CppTypeInfo { + public ItaniumTypeInfo (ItaniumAbi abi, IEnumerable virtualMethods, Type nativeLayout, Type wrapperType) + : base (abi, virtualMethods, nativeLayout, wrapperType) + { + } + + + // When adding a non-primary base class's complete vtable, we need to reserve space for + // the stuff before the address point of the vtptr.. + // Includes vbase & vcall offsets (virtual inheritance), offset to top, and RTTI info + public override void AddBase (CppTypeInfo baseType) + { + if (TypeComplete) + return; + + if (BaseClasses.Count > 0 && baseType.VirtualMethods.Any ()) { // already have a primary base + + // FIXME: virtual inheritance + virtual_methods.Insert (BaseVTableSlots, null); + virtual_methods.Insert (BaseVTableSlots, null); + BaseVTableSlots += 2; + + } + + base.AddBase (baseType); + } + + } +} + diff --git a/src/Mono.VisualC.Interop/CppTypeInfo.cs b/src/Mono.VisualC.Interop/CppTypeInfo.cs index 601fe323..204bc21b 100644 --- a/src/Mono.VisualC.Interop/CppTypeInfo.cs +++ b/src/Mono.VisualC.Interop/CppTypeInfo.cs @@ -91,7 +91,7 @@ namespace Mono.VisualC.Interop { BaseVTableSlots = 0; TypeComplete = false; - native_size = Marshal.SizeOf (nativeLayout); + native_size = nativeLayout.GetFields ().Any ()? Marshal.SizeOf (nativeLayout) : 0; field_offset_padding_without_vtptr = 0; lazy_vtable = null; @@ -104,19 +104,21 @@ namespace Mono.VisualC.Interop { public virtual void AddBase (CppTypeInfo baseType) { - // by default, do not add another vtable pointer for this new base class + // by default, do not add another vtable for this new base class AddBase (baseType, false); } - protected virtual void AddBase (CppTypeInfo baseType, bool addVTablePointer) + protected virtual void AddBase (CppTypeInfo baseType, bool addVTable) { if (TypeComplete) return; base_classes.Add (baseType); - if (!addVTablePointer) { - // If we're not adding a vtptr, then all this base class's virtual methods go in primary vtable + bool addVTablePointer = addVTable || base_classes.Count > 1; + + if (!addVTable) { + // If we're not adding a new vtable, then all this base class's virtual methods go in primary vtable // Skew the offsets of this subclass's vmethods to account for the new base vmethods. int newVirtualMethodCount = baseType.virtual_methods.Count; @@ -126,6 +128,9 @@ namespace Mono.VisualC.Interop { BaseVTableSlots += newVirtualMethodCount; vt_delegate_types.PrependLast (baseType.vt_delegate_types); vt_overrides.PrependLast (baseType.vt_overrides); + + } else { + // FIXME: Implement this when we get around to msvc again ? } field_offset_padding_without_vtptr += baseType.native_size + @@ -135,7 +140,32 @@ namespace Mono.VisualC.Interop { public virtual TBase Cast (ICppObject instance) where TBase : ICppObject { - throw new NotImplementedException (); + var targetType = typeof (TBase); + var found = false; + int offset = 0; + + foreach (var baseClass in base_classes) { + if (baseClass.WrapperType.Equals (targetType)) { + found = true; + break; + } + + offset += baseClass.NativeSize; + } + + if (!found) + throw new InvalidCastException ("Cannot cast an instance of " + instance.GetType () + " to " + targetType); + + // Construct a new targetType wrapper, passing in our offset this ptr. + // FIXME: If the object was alloc'd by managed code, the allocating wrapper (i.e. "instance") will still free the memory when + // it is Disposed, even if wrappers created here still exist and are pointing to it. :/ + // The casted wrapper created here may be Disposed safely though. + // FIXME: On NET_4_0 use IntPtr.Add + var cppip = new CppInstancePtr (new IntPtr (instance.Native.Native.ToInt64 () + offset)); + + // Ugh, this requires boxing of cppip AND some slow reflection.. please cache the result of this! + // FIXME: Perhaps Reflection.Emit all possible base casts and eliminate this beast? + return (TBase)Activator.CreateInstance (targetType, cppip); } public int CountBases (Func predicate) @@ -157,24 +187,30 @@ namespace Mono.VisualC.Interop { baseClass.CompleteType (); TypeComplete = true; - RemoveVTableDuplicates (ms => true); + + // Tthis predicate ensures that duplicates are only removed + // if declared in different classes (i.e. overridden methods). + // We usually want to allow the same exact virtual methods to appear + // multiple times, in the case of nonvirtual diamond inheritance, for example. + RemoveVTableDuplicates ((pi1, pi2) => !pi1.OrigMethod.Equals (pi2.OrigMethod)); } - protected virtual void RemoveVTableDuplicates (Predicate pred) + protected virtual void RemoveVTableDuplicates (Func pred) { // check that any virtual methods overridden in a subclass are only included once - var vsignatures = new HashSet (); + var vsignatures = new Dictionary (MethodSignature.EqualityComparer); for (int i = 0; i < virtual_methods.Count; i++) { var sig = virtual_methods [i]; if (sig == null) continue; - if (vsignatures.Contains (sig)) { - if (pred (sig)) + PInvokeSignature existing; + if (vsignatures.TryGetValue (sig, out existing)) { + if (pred (sig, existing)) virtual_methods.RemoveAt (i--); } else { - vsignatures.Add (sig); + vsignatures.Add (sig, sig); } } } @@ -184,8 +220,6 @@ namespace Mono.VisualC.Interop { return VTable.GetVirtualCallDelegate (native, BaseVTableSlots + derivedVirtualMethodIndex); } - //public virtual long GetFieldOffset ( - public virtual VTable VTable { get { CompleteType (); diff --git a/src/Mono.VisualC.Interop/Makefile.am b/src/Mono.VisualC.Interop/Makefile.am index 8056af0e..4473b658 100644 --- a/src/Mono.VisualC.Interop/Makefile.am +++ b/src/Mono.VisualC.Interop/Makefile.am @@ -46,6 +46,7 @@ all: $(ASSEMBLY) $(PROGRAMFILES) $(LINUX_PKGCONFIG) FILES = \ ABI/CppAbi.cs \ ABI/Impl/ItaniumAbi.cs \ + ABI/Impl/ItaniumTypeInfo.cs \ ABI/Impl/MsvcAbi.cs \ ABI/Impl/MsvcTypeInfo.cs \ ABI/Impl/VirtualOnlyAbi.cs \ diff --git a/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj b/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj index 23a9dcb3..d29c3e54 100644 --- a/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj +++ b/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj @@ -80,6 +80,7 @@ + diff --git a/src/Mono.VisualC.Interop/Util/LazyGeneratedList.cs b/src/Mono.VisualC.Interop/Util/LazyGeneratedList.cs index 44338ea6..f4e5dd59 100644 --- a/src/Mono.VisualC.Interop/Util/LazyGeneratedList.cs +++ b/src/Mono.VisualC.Interop/Util/LazyGeneratedList.cs @@ -34,11 +34,36 @@ namespace Mono.VisualC.Interop.Util { public class LazyGeneratedList : IList where TItem : class { + private class Node where TItem : class { + + public LazyGeneratedList List { get; set; } + public Node Next { get; set; } + public Node (LazyGeneratedList list, Node next) + { + this.List = list; + this.Next = next; + } + public void AppendNext (Node node) + { + if (Next == null) + Next = node; + else + Next.AppendNext (node); + } + public TItem this [int index] { + get { + if (index >= List.Count) + return Next [index - List.Count]; + return List [index]; + } + } + } + private TItem [] cache; private Func generator; - private LazyGeneratedList previous; - private LazyGeneratedList next; + private Node previous; + private Node next; private int lead, content, follow; private HashSet removed; @@ -112,39 +137,35 @@ namespace Mono.VisualC.Interop.Util { public void AppendFirst (LazyGeneratedList list) { follow += list.Count; - if (next != null) - list.AppendLast (next); - - next = list; + next = new Node (list, next); } public void AppendLast (LazyGeneratedList list) { + var node = new Node (list, null); if (next == null) - next = list; + next = node; else - next.AppendLast (list); + next.AppendNext (node); follow += list.Count; } - public void PrependFirst (LazyGeneratedList list) + public void PrependLast (LazyGeneratedList list) { + var node = new Node (list, null); if (previous == null) - previous = list; + previous = node; else - previous.PrependFirst (list); + previous.AppendNext (node); lead += list.Count; } - public void PrependLast (LazyGeneratedList list) + public void PrependFirst (LazyGeneratedList list) { lead += list.Count; - if (previous != null) - list.PrependFirst (previous); - - previous = list; + previous = new Node (list, previous); } // FIXME: Should probably implement these 3 at some point diff --git a/src/Mono.VisualC.Interop/Util/MethodSignature.cs b/src/Mono.VisualC.Interop/Util/MethodSignature.cs index afc1d7a1..efd559ad 100644 --- a/src/Mono.VisualC.Interop/Util/MethodSignature.cs +++ b/src/Mono.VisualC.Interop/Util/MethodSignature.cs @@ -92,6 +92,8 @@ namespace Mono.VisualC.Interop.Util { } public class MethodSignature : BasicSignature { + public static readonly IEqualityComparer EqualityComparer = new MethodSignatureEqualityComparer (); + public string Name { get; set; } public MethodType Type { get; set; } @@ -116,6 +118,16 @@ namespace Mono.VisualC.Interop.Util { } } + private class MethodSignatureEqualityComparer : IEqualityComparer { + public bool Equals (MethodSignature x, MethodSignature y) + { + return x.Equals (y); + } + public int GetHashCode (MethodSignature obj) + { + return obj.GetHashCode (); + } + } } public class PInvokeSignature : MethodSignature {