Experimental support for initing the native vtptr ourselves (enable with -define:INIT_NATIVE_VTABLES)

Doing this ourselves instead of depending on the C++ ctor to do it will allow us to instantiate classes that have vtables and do not explicitly define any constructors (thus only having the C++ implicitly-defined default constructor, which isn't exported as a symbol in the lib).
This commit is contained in:
Alex Corrado 2011-12-05 01:10:21 -05:00
Родитель aefe6e0e2a
Коммит fb85e5a3ed
7 изменённых файлов: 184 добавлений и 6 удалений

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

@ -71,6 +71,7 @@ namespace Mono.Cxxi.Abi {
protected static readonly MethodInfo marshal_offsetof = typeof (Marshal).GetMethod ("OffsetOf");
protected static readonly MethodInfo marshal_structuretoptr = typeof (Marshal).GetMethod ("StructureToPtr");
protected static readonly MethodInfo marshal_ptrtostructure = typeof (Marshal).GetMethod ("PtrToStructure", BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (IntPtr), typeof (Type) }, null);
protected static readonly MethodInfo marshal_writeintptr = typeof (Marshal).GetMethod ("WriteIntPtr", BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (IntPtr), typeof (IntPtr) }, null);
protected static readonly FieldInfo intptr_zero = typeof (IntPtr).GetField ("Zero");
// These methods might be more commonly overridden for a given C++ ABI:
@ -89,6 +90,14 @@ namespace Mono.Cxxi.Abi {
return MethodType.Native;
}
// Implementing this is recommended..
// otherwise it is not possible to instantiate classes that have vtables and only implicitly defined ctor
// Return null if not implemented or if the symbol name of the vtable for the passed typeInfo cannot be mangled (i.e. because there's no vtable)
protected virtual string GetMangledVTableName (CppTypeInfo typeInfo)
{
return null;
}
// The members below must be implemented for a given C++ ABI:
public abstract CallingConvention? GetCallingConvention (MethodInfo methodInfo);
@ -120,7 +129,15 @@ namespace Mono.Cxxi.Abi {
DefineProperty (typeInfo, property);
typeInfo.emit_info.ctor_il.Emit (OpCodes.Ret);
return (ICppClass)Activator.CreateInstance (typeInfo.emit_info.type_builder.CreateType (), typeInfo);
var native_vtable = default (IntPtr);
#if INIT_NATIVE_VTABLES
var vtable_symbol = GetMangledVTableName (typeInfo);
if (vtable_symbol != null)
native_vtable = SymbolResolver.ResolveSymbol (SymbolResolver.LoadImage (ref typeInfo.Library.name), vtable_symbol);
#endif
return (ICppClass)Activator.CreateInstance (typeInfo.emit_info.type_builder.CreateType (), typeInfo, native_vtable);
}
throw new InvalidOperationException ("This type has already been implemented");
@ -180,9 +197,10 @@ namespace Mono.Cxxi.Abi {
impl_type.AddInterfaceImplementation (typeInfo.InterfaceType);
var typeinfo_field = impl_type.DefineField ("_typeInfo", typeof (CppTypeInfo), FieldAttributes.InitOnly | FieldAttributes.Private);
var native_vtable_field = impl_type.DefineField ("_nativeVTable", typeof (IntPtr), FieldAttributes.InitOnly | FieldAttributes.Private);
ConstructorBuilder ctor = impl_type.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard,
new Type[] { typeof (CppTypeInfo) });
new Type[] { typeof (CppTypeInfo), typeof (IntPtr) });
var ctor_il = ctor.GetILGenerator ();
@ -191,8 +209,14 @@ namespace Mono.Cxxi.Abi {
ctor_il.Emit (OpCodes.Ldarg_1);
ctor_il.Emit (OpCodes.Stfld, typeinfo_field);
// this._nativeVTable = (vtable ptr passed to constructor)
ctor_il.Emit (OpCodes.Ldarg_0);
ctor_il.Emit (OpCodes.Ldarg_2);
ctor_il.Emit (OpCodes.Stfld, native_vtable_field);
typeInfo.emit_info.ctor_il = ctor_il;
typeInfo.emit_info.typeinfo_field = typeinfo_field;
typeInfo.emit_info.native_vtable_field = native_vtable_field;
typeInfo.emit_info.type_builder = impl_type;
}
@ -479,8 +503,8 @@ namespace Mono.Cxxi.Abi {
protected virtual void EmitManagedAlloc (CppTypeInfo typeInfo, MethodInfo interfaceMethod)
{
var il = typeInfo.emit_info.current_il;
var cppip = il.DeclareLocal (typeof (CppInstancePtr));
// this._typeInfo.NativeSize
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field);
@ -492,6 +516,22 @@ namespace Mono.Cxxi.Abi {
il.Emit (OpCodes.Callvirt, typeinfo_nativesize);
il.Emit (OpCodes.Newobj, cppip_fromsize);
}
il.Emit (OpCodes.Stloc, cppip);
var unknown_native_vtable = il.DefineLabel ();
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field);
il.Emit (OpCodes.Brfalse_S, unknown_native_vtable);
il.Emit (OpCodes.Ldloca, cppip);
il.Emit (OpCodes.Call, cppip_native);
il.Emit (OpCodes.Ldarg_0);
il.Emit (OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field);
il.Emit (OpCodes.Call, marshal_writeintptr);
il.MarkLabel (unknown_native_vtable);
il.Emit (OpCodes.Ldloc, cppip);
}
protected virtual void EmitConstruct (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig,

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

@ -5,7 +5,7 @@ namespace Mono.Cxxi.Abi {
public class EmitInfo {
public TypeBuilder type_builder;
public FieldBuilder typeinfo_field;
public FieldBuilder typeinfo_field, native_vtable_field;
public ILGenerator ctor_il, current_il;
}
}

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

@ -6,6 +6,7 @@
// Andreia Gaita (shana@spoiledcat.net)
//
// Copyright (C) 2010-2011 Alexander Corrado
// Copyright 2011 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@ -197,6 +198,26 @@ namespace Mono.Cxxi.Abi {
return code.ToString ();
}
protected override string GetMangledVTableName (CppTypeInfo typeInfo)
{
var compressMap = new Dictionary<string, int> ();
var type = typeInfo.GetMangleType ();
var nm = new StringBuilder ("_ZTV", 30);
if (type.Namespaces != null) {
nm.Append ('N');
foreach (var ns in type.Namespaces)
nm.Append (GetIdentifier (compressMap, ns));
}
nm.Append (GetIdentifier (compressMap, type.ElementTypeName));
if (type.Namespaces != null)
nm.Append ('E');
return nm.ToString ();
}
string GetIdentifier (Dictionary<string, int> compressMap, string identifier)
{
int cid;

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

@ -0,0 +1,113 @@
//
// Mono.Cxxi.Abi.SymbolResolver.cs: Platform-independent dynamic symbol lookup
//
// Author:
// Alexander Corrado (alexander.corrado@gmail.com)
//
// Copyright 2011 Xamarin Inc (http://www.xamarin.com)
//
// 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.Runtime.InteropServices;
namespace Mono.Cxxi.Abi {
internal static class SymbolResolver {
static readonly string [] formats;
static readonly Func<string,IntPtr> load_image;
static readonly Func<IntPtr,string,IntPtr> resolve_symbol;
static SymbolResolver ()
{
switch (Environment.OSVersion.Platform) {
case PlatformID.Unix:
case PlatformID.MacOSX:
load_image = dlopen;
resolve_symbol = dlsym;
formats = new[] {
"{0}",
"{0}.so",
"{0}.dylib",
"lib{0}.so",
"lib{0}.dylib",
"{0}.bundle"
};
break;
default:
load_image = LoadLibrary;
resolve_symbol = GetProcAddress;
formats = new[] { "{0}", "{0}.dll" };
break;
}
}
// will fix up name with a more precise name to speed up later p/invokes (hopefully?)
public static IntPtr LoadImage (ref string name)
{
foreach (var format in formats) {
var attempted = string.Format (format, name);
var ptr = load_image (attempted);
if (ptr != IntPtr.Zero) {
name = attempted;
return ptr;
}
}
return IntPtr.Zero;
}
public static IntPtr ResolveSymbol (IntPtr image, string symbol)
{
if (image != IntPtr.Zero)
return resolve_symbol (image, symbol);
return IntPtr.Zero;
}
#region POSIX
static IntPtr dlopen (string path)
{
return dlopen (path, 0x0);
}
[DllImport ("dl", CharSet=CharSet.Ansi)]
static extern IntPtr dlopen (string path, int flags);
[DllImport ("dl", CharSet=CharSet.Ansi)]
static extern IntPtr dlsym (IntPtr handle, string symbol);
#endregion
#region Win32
[DllImport("kernel32", SetLastError=true)]
static extern IntPtr LoadLibrary (string lpFileName);
[DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)]
static extern IntPtr GetProcAddress (IntPtr hModule, string procName);
#endregion
}
}

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

@ -59,9 +59,11 @@ namespace Mono.Cxxi {
internal static ModuleBuilder interopModule;
public CppAbi Abi { get; private set; }
public string Name { get; private set; }
public InlineMethods InlineMethodPolicy { get; private set; }
internal string name;
public string Name { get { return name; } }
static CppLibrary ()
{
AssemblyName assemblyName = new AssemblyName ("__CppLibraryImplAssembly");
@ -89,7 +91,7 @@ namespace Mono.Cxxi {
if (abi == null)
throw new ArgumentNullException ("Abi cannot be NULL.");
this.Name = name;
this.name = name;
this.Abi = abi;
this.InlineMethodPolicy = inlinePolicy;
}

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

@ -49,6 +49,7 @@ FILES = \
Abi/Impl/ItaniumTypeInfo.cs \
Abi/Impl/MsvcAbi.cs \
Abi/MethodType.cs \
Abi/SymbolResolver.cs \
Abi/VTable.cs \
AssemblyInfo.cs \
Attributes.cs \

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

@ -78,6 +78,7 @@
<Compile Include="CppModifiers.cs" />
<Compile Include="Abi\Impl\ItaniumTypeInfo.cs" />
<Compile Include="Abi\EmitInfo.cs" />
<Compile Include="Abi\SymbolResolver.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>