Fixes for virtual dispatch for multiple inheritance.

+ Fix Itanium vtable layout: derived type's virtual methods come after primary base's but before the rest of the base classes'
+ Wrapper cleanup: move common init code to methods, cache all casted base wrappers right away in preparation for patching base-in-derived vtptrs
+ CppInstancePtr: read native_vtptr when constructed with native ptr-- will need hash lookup to detect managed instances
+ Clean/refactor LazyGeneratedList
This commit is contained in:
Alex Corrado 2011-06-23 03:19:37 -04:00
Родитель 0febce15f3
Коммит 6a8bec014e
11 изменённых файлов: 444 добавлений и 504 удалений

Просмотреть файл

@ -54,7 +54,7 @@ namespace Mono.VisualC.Interop.ABI {
protected static readonly MethodInfo cppip_managedalloc = typeof (CppInstancePtr).GetProperty ("IsManagedAlloc").GetGetMethod ();
protected static readonly MethodInfo cppip_getmanaged = typeof (CppInstancePtr).GetMethod ("GetManaged", BindingFlags.Static | BindingFlags.NonPublic);
protected static readonly ConstructorInfo cppip_fromnative = typeof (CppInstancePtr).GetConstructor (new Type [] { typeof (IntPtr) });
protected static readonly ConstructorInfo cppip_fromsize = typeof (CppInstancePtr).GetConstructor (new Type [] { typeof (int) });
protected static readonly ConstructorInfo cppip_fromsize = typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null, new Type [] { typeof (int) }, null);
protected static readonly ConstructorInfo cppip_fromsize_managed = typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null,
new Type[] { typeof (int), typeof (object) }, null);
protected static readonly ConstructorInfo notimplementedexception = typeof (NotImplementedException).GetConstructor (new Type [] { typeof (string) });

Просмотреть файл

@ -38,25 +38,38 @@ namespace Mono.VisualC.Interop.ABI {
{
}
// 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)
protected override void AddBase (CppTypeInfo baseType, bool addVTable)
{
if (TypeComplete)
return;
if (BaseClasses.Count > 0 && baseType.VirtualMethods.Any ()) { // already have a primary base
// 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
if (addVTable) {
// FIXME: virtual inheritance
virtual_methods.Insert (BaseVTableSlots, null);
virtual_methods.Insert (BaseVTableSlots, null);
BaseVTableSlots += 2;
virtual_methods.Add (null);
virtual_methods.Add (null);
vt_overrides.Add (2);
vt_delegate_types.Add (2);
}
base.AddBase (baseType);
base.AddBase (baseType, addVTable);
}
protected override bool OnVTableDuplicate (ref int iter, PInvokeSignature sig, PInvokeSignature dup)
{
var isOverride = base.OnVTableDuplicate (ref iter, sig, dup);
if (isOverride && sig.Type == MethodType.NativeDtor) {
// also remove that pesky extra dtor
virtual_methods.RemoveAt (iter + 1);
vt_overrides.Remove (1);
vt_delegate_types.Remove (1);
return true;
}
return false;
}
}

Просмотреть файл

@ -49,7 +49,7 @@ namespace Mono.VisualC.Interop.ABI {
protected override CppTypeInfo MakeTypeInfo (IEnumerable<PInvokeSignature> methods)
{
return new MsvcTypeInfo (this, methods.Where (m => IsVirtual (m.OrigMethod)), layout_type, wrapper_type);
return new CppTypeInfo (this, methods.Where (m => IsVirtual (m.OrigMethod)), layout_type, wrapper_type);
}
public override CallingConvention? GetCallingConvention (MethodInfo methodInfo)

Просмотреть файл

@ -1,56 +0,0 @@
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
// Andreia Gaita (shana@spoiledcat.net)
//
// Copyright (C) 2010 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 MsvcTypeInfo : CppTypeInfo {
public MsvcTypeInfo (MsvcAbi abi, IEnumerable<PInvokeSignature> virtualMethods, Type nativeLayout, Type wrapperType)
: base (abi, virtualMethods, nativeLayout, wrapperType)
{
}
// AFIK, MSVC places only its first base's virtual methods in the derived class's
// primary vtable. Subsequent base classes (each?) get another vtable pointer
public override void AddBase (CppTypeInfo baseType)
{
if (TypeComplete)
return;
if (BaseClasses.Count == 0)
base.AddBase (baseType, false);
else
base.AddBase (baseType, true);
}
}
}

Просмотреть файл

@ -70,6 +70,7 @@ namespace Mono.VisualC.Interop {
}
// Alloc a new C++ instance
// NOTE: native_vtptr will be set later after native ctor is called
internal CppInstancePtr (int nativeSize, object managedWrapper)
{
// Under the hood, we're secretly subclassing this C++ class to store a
@ -89,7 +90,7 @@ namespace Mono.VisualC.Interop {
}
// Alloc a new C++ instance when there is no managed wrapper.
public CppInstancePtr (int nativeSize)
internal CppInstancePtr (int nativeSize)
{
ptr = Marshal.AllocHGlobal (nativeSize);
manage_memory = true;
@ -101,6 +102,11 @@ namespace Mono.VisualC.Interop {
if (native == IntPtr.Zero)
throw new ArgumentOutOfRangeException ("native cannot be null pointer");
// Kludge! CppInstancePtr doesn't know whether this class is virtual or not, but we'll just assume that either
// way it's at least sizeof(void*) and read what would be the vtptr anyway. Supposedly, if it's not virtual,
// the wrappers won't use this field anyway...
native_vtptr = Marshal.ReadIntPtr (native);
ptr = native;
manage_memory = false;
}

Просмотреть файл

@ -103,9 +103,9 @@ namespace Mono.VisualC.Interop {
public virtual void AddBase (CppTypeInfo baseType)
{
// by default, do not add another vtable for this new base class
AddBase (baseType, false);
// by default, if we already have base class(es), this base's virtual methods are not included before the derived class's
var addVTable = base_classes.Count >= 1;
AddBase (baseType, addVTable);
}
protected virtual void AddBase (CppTypeInfo baseType, bool addVTable)
@ -114,34 +114,38 @@ namespace Mono.VisualC.Interop {
return;
base_classes.Add (baseType);
bool addVTablePointer = addVTable || base_classes.Count > 1;
int baseVMethodCount = baseType.virtual_methods.Count;
if (addVTable) {
// If we are adding a new vtable, don't skew the offsets of the of this subclass's methods.
// Instead append the new virtual methods to the end.
for (int i = 0; i < baseVMethodCount; i++)
virtual_methods.Add (baseType.virtual_methods [i]);
vt_delegate_types.Add (baseVMethodCount);
vt_overrides.Add (baseVMethodCount);
} else {
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 baseVMethodCount = baseType.virtual_methods.Count;
for (int i = 0; i < baseVMethodCount; i++)
virtual_methods.Insert (BaseVTableSlots + i, baseType.virtual_methods [i]);
BaseVTableSlots += baseVMethodCount;
vt_delegate_types.PrependLast (baseType.vt_delegate_types);
//vt_overrides.PrependLast (baseType.vt_overrides);
vt_delegate_types.Add (baseVMethodCount);
vt_overrides.Add (baseVMethodCount);
} else {
// FIXME: Implement this when we get around to msvc again ?
}
field_offset_padding_without_vtptr += baseType.native_size +
(addVTablePointer? baseType.FieldOffsetPadding : baseType.field_offset_padding_without_vtptr);
}
public virtual TBase Cast<TBase> (ICppObject instance)
where TBase : ICppObject
public virtual CppInstancePtr Cast (ICppObject instance, Type targetType)
{
var targetType = typeof (TBase);
var found = false;
int offset = 0;
@ -162,11 +166,7 @@ namespace Mono.VisualC.Interop {
// 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);
return new CppInstancePtr (new IntPtr (instance.Native.Native.ToInt64 () + offset));
}
public int CountBases (Func<CppTypeInfo, bool> predicate)
@ -189,14 +189,10 @@ namespace Mono.VisualC.Interop {
TypeComplete = 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));
RemoveVTableDuplicates ();
}
protected virtual void RemoveVTableDuplicates (Func<PInvokeSignature,PInvokeSignature,bool> pred)
protected virtual void RemoveVTableDuplicates ()
{
// check that any virtual methods overridden in a subclass are only included once
var vsignatures = new Dictionary<MethodSignature,PInvokeSignature> (MethodSignature.EqualityComparer);
@ -208,14 +204,29 @@ namespace Mono.VisualC.Interop {
PInvokeSignature existing;
if (vsignatures.TryGetValue (sig, out existing)) {
if (pred (sig, existing))
virtual_methods.RemoveAt (i--);
OnVTableDuplicate (ref i, sig, existing);
} else {
vsignatures.Add (sig, sig);
}
}
}
protected virtual bool OnVTableDuplicate (ref int iter, PInvokeSignature sig, PInvokeSignature dup)
{
// This 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.
if (!sig.OrigMethod.Equals (dup.OrigMethod)) {
virtual_methods.RemoveAt (iter--);
vt_overrides.Remove (1);
vt_delegate_types.Remove (1);
return true;
}
return false;
}
public virtual T GetAdjustedVirtualCall<T> (CppInstancePtr instance, int derivedVirtualMethodIndex)
where T : class /* Delegate */
{

Просмотреть файл

@ -48,7 +48,6 @@ FILES = \
ABI/Impl/ItaniumAbi.cs \
ABI/Impl/ItaniumTypeInfo.cs \
ABI/Impl/MsvcAbi.cs \
ABI/Impl/MsvcTypeInfo.cs \
ABI/Impl/VirtualOnlyAbi.cs \
ABI/MethodType.cs \
ABI/VTable.cs \

Просмотреть файл

@ -76,7 +76,6 @@
<Compile Include="Util\LazyGeneratedList.cs" />
<Compile Include="Util\DelegateTypeCache.cs" />
<Compile Include="Util\ReflectionHelper.cs" />
<Compile Include="ABI\Impl\MsvcTypeInfo.cs" />
<Compile Include="Util\MethodSignature.cs" />
<Compile Include="CppModifiers.cs" />
<Compile Include="ABI\Impl\ItaniumTypeInfo.cs" />

Просмотреть файл

@ -32,50 +32,19 @@ using System.Collections.Generic;
namespace Mono.VisualC.Interop.Util {
public class LazyGeneratedList<TItem> : IList<TItem> where TItem : class {
private class Node<TItem> where TItem : class {
public LazyGeneratedList<TItem> List { get; set; }
public Node<TItem> Next { get; set; }
public Node (LazyGeneratedList<TItem> list, Node<TItem> next)
{
this.List = list;
this.Next = next;
}
public void AppendNext (Node<TItem> 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];
}
}
}
public class LazyGeneratedList<TItem> : IList<TItem>
where TItem : class
{
private TItem [] cache;
private Func<int, TItem> generator;
private Node<TItem> previous;
private Node<TItem> next;
private int lead, content, follow;
private HashSet<int> removed;
private int count;
public LazyGeneratedList (int count, Func<int, TItem> generator)
{
this.cache = new TItem [count];
this.generator = generator;
this.lead = 0;
this.content = count;
this.follow = 0;
this.removed = new HashSet<int> ();
this.count = count;
}
public IEnumerator<TItem> GetEnumerator ()
@ -89,86 +58,37 @@ namespace Mono.VisualC.Interop.Util {
}
public int Count {
get { return lead + content + follow; }
get { return count; }
}
public bool IsReadOnly {
get { return true; }
}
public TItem this [int index] {
public TItem this [int i] {
get {
return ForIndex (index, i => previous [i], i => (cache [i] == null? (cache [i] = generator (i)) : cache [i]), i => next [i]);
// access to cache [i] will throw the IndexOutOfRange exception for us
return cache [i] == null? (cache [i] = generator (i)) : cache [i];
}
set {
throw new NotSupportedException ("This IList is read only");
}
}
private TItem ForIndex (int index, Func<int,TItem> leadAction, Func<int,TItem> contentAction, Func<int,TItem> followAction)
public void Add (int count)
{
if (removed.Contains (index))
index++;
if (index < lead) {
if (previous != null && index >= 0)
return leadAction (index);
throw new IndexOutOfRangeException (index.ToString ());
}
int realIndex = index - lead;
if (realIndex >= content) {
int followIndex = realIndex - content;
if (next != null && followIndex < follow)
return followAction (followIndex);
throw new IndexOutOfRangeException (index.ToString ());
}
return contentAction (realIndex);
this.count += count;
// flush cache
cache = new TItem [this.count];
}
public void Remove (int count)
{
this.count -= count;
// flush cache
cache = new TItem [this.count];
}
/* Visual aid for the behavior of the following methods:
|<Prepended Range><Generated Range><Appended Range>|
^ ^ ^ ^
PrependFirst PrependLast AppendFirst AppendLast
*/
public void AppendFirst (LazyGeneratedList<TItem> list)
{
follow += list.Count;
next = new Node<TItem> (list, next);
}
public void AppendLast (LazyGeneratedList<TItem> list)
{
var node = new Node<TItem> (list, null);
if (next == null)
next = node;
else
next.AppendNext (node);
follow += list.Count;
}
public void PrependLast (LazyGeneratedList<TItem> list)
{
var node = new Node<TItem> (list, null);
if (previous == null)
previous = node;
else
previous.AppendNext (node);
lead += list.Count;
}
public void PrependFirst (LazyGeneratedList<TItem> list)
{
lead += list.Count;
previous = new Node<TItem> (list, previous);
}
// FIXME: Should probably implement these 3 at some point
// FIXME: Should probably implement these at some point
public bool Contains (TItem item)
{
throw new NotImplementedException ();
@ -181,24 +101,13 @@ namespace Mono.VisualC.Interop.Util {
{
throw new NotImplementedException ();
}
public void Insert (int index, TItem item)
{
throw new NotImplementedException ();
}
public void RemoveAt (int index)
{
while (removed.Contains (index))
index++;
removed.Add (index);
content--;
}
public void Add (int count)
{
content += count;
Array.Resize (ref cache, content);
throw new NotImplementedException ();
}
public void Add (TItem item)
{

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -8,14 +8,8 @@
var wrapper = Class.Name;
var iface = "I" + Class.Name;
var layout = "_" + Class.Name;
var initBases = "{";
if (hasBase) {
initBases = "\t: base (impl.TypeInfo) {";
foreach (var nonPrimaryBase in Class.BaseClasses.Skip (1)) {
initBases = string.Format ("{0}\n\t\t\tnew {1} (impl.TypeInfo);", initBases, nonPrimaryBase.Name);
}
}
var layoutClass = (hasBase? "\t: base (impl.TypeInfo)\n\t\t{" : "{") + "\n\t\t\tLayoutClass ();";
var initBases = (Class.BaseClasses.Count > 1 ? "\tInitBases ();\n\t\t}" : "}");
#>
// -------------------------------------------------------------------------
// Managed wrapper for <#= Class.Name #>
@ -94,26 +88,25 @@ namespace <#= Generator.Namespace #> {
impl.<#= field.Name #> [Native] = value;
}
}
<# }
ClearIndent(); #>
<# /* Native constructor */ #>
public <#= wrapper #> (CppInstancePtr native)
<#= initBases #>
Native = native;
}
<# } ClearIndent(); #>
<# /* Subclass constructor */ #>
public <#= wrapper #> (CppTypeInfo subClass)
<#= initBases #>
<#= layoutClass #>
subClass.AddBase (impl.TypeInfo);
}
<#= initBases #>
<# /* Native constructor */ #>
public <#= wrapper #> (CppInstancePtr native)
<#= layoutClass #>
Native = native;
<#= initBases #>
<# /* Wrapper methods */ #>
<# PushIndent ("\t\t");
foreach (var method in Class.Methods.Where (m => m.GenWrapperMethod)) {
WriteMethodHeader (method, initBases);
WriteMethodHeader (method, layoutClass);
if (method.IsConstructor)
Write ("Native = ");
@ -129,8 +122,13 @@ namespace <#= Generator.Namespace #> {
}
WriteParameters (method.Parameters, false, false);
Write (");\n");
PopIndent ();
Write (");\n{0}}}\n\n", CurrentIndent);
if (method.IsConstructor)
WriteLine (initBases);
else
WriteLine ("}");
}
ClearIndent (); #>
@ -179,23 +177,29 @@ namespace <#= Generator.Namespace #> {
}
ClearIndent (); #>
<# /* Make this wrapper castable to non-primary bases */
foreach (var npBase in Class.BaseClasses.Skip (1)) {
var prop = npBase.Name;
var field = prop + "_lazy";
#>
// Non-primary base class implementation for <#= npBase.Name #>:
private <#= npBase.Name #> <#= field #>;
public <#= npBase.Name #> <#= prop #> {
get {
if (<#= field #> == null)
<#= field #> = impl.TypeInfo.Cast<<#= npBase.Name #>> (this);
return <#= field #>;
}
public <#= hasBase? "override" : "virtual" #> void Dispose ()
{
<# if (Class.Methods.Any (m => m.IsDestructor && !m.IsArtificial)) { #>
impl.Destruct (Native);
<# } #>
Native.Dispose ();
}
private void LayoutClass ()
{
<# foreach (var npBase in Class.BaseClasses.Skip (1)) { #>
new <#= npBase.Name #> (impl.TypeInfo);
<# } #>
impl.TypeInfo.CompleteType ();
}
<# /* Make this wrapper castable to non-primary bases */
foreach (var npBase in Class.BaseClasses.Skip (1)) { #>
// Non-primary base class implementation for <#= npBase.Name #>:
public <#= npBase.Name #> <#= npBase.Name #> { get; protected set; }
public static implicit operator <#= npBase.Name #>(<#= wrapper #> subClass)
{
return subClass.<#= prop #>;
return subClass.<#= npBase.Name #>;
}
<# PushIndent ("\t\t");
@ -208,28 +212,27 @@ foreach (var npBase in Class.BaseClasses.Skip (1)) {
WriteMethodHeader (method, initBases);
Write ("{0}.{1} (", prop, method.FormattedName);
Write ("{0}.{1} (", npBase.Name, method.FormattedName);
WriteParameters (method.Parameters, false, false);
PopIndent ();
Write (");\n{0}}}\n\n", CurrentIndent);
}
ClearIndent ();
} #>
public <#= hasBase? "override" : "virtual" #> void Dispose ()
}
if (Class.BaseClasses.Count > 1) { #>
private void InitBases ()
{
<# if (Class.Methods.Any (m => m.IsDestructor && !m.IsArtificial)) { #>
impl.Destruct (Native);
<# } #>
Native.Dispose ();
<# foreach (var npBase in Class.BaseClasses.Skip (1)) { #>
<#= npBase.Name #> = new <#= npBase.Name #> (impl.TypeInfo.Cast (this, typeof (<#= npBase.Name #>)));
<# } #>
}
<# } #>
}
}
<#+
private void WriteMethodHeader (Method method, string initBases)
private void WriteMethodHeader (Method method, string layoutClass)
{
var returnType = GetCSharpType (method.ReturnType);
@ -253,7 +256,7 @@ private void WriteMethodHeader (Method method, string initBases)
Write (")\n");
if (method.IsConstructor)
WriteLine (initBases);
WriteLine (layoutClass);
else
WriteLine ("{");