Browse Source

Added the ability to expose any arbitrary managed class to C++ code (CppInstancePtr.ForManagedObject)

git-svn-id: https://mono-soc-2010.googlecode.com/svn/trunk/cppinterop@27 a470b8cb-0e6f-1642-1b45-71e107334c4b
pull/1/head
alexander.corrado 15 years ago
parent
commit
d3742aa29d
  1. 2
      CPPPOC/Main.cs
  2. 99
      Mono.VisualC.Interop/ABI/CppAbi.cs
  3. 4
      Mono.VisualC.Interop/ABI/Impl/Itanium.cs
  4. 38
      Mono.VisualC.Interop/ABI/Impl/VirtualOnlyAbi.cs
  5. 1
      Mono.VisualC.Interop/ABI/MethodType.cs
  6. 31
      Mono.VisualC.Interop/ABI/VTable.cs
  7. 8
      Mono.VisualC.Interop/ABI/VTableCOM.cs
  8. 12
      Mono.VisualC.Interop/ABI/VTableManaged.cs
  9. 36
      Mono.VisualC.Interop/CppInstancePtr.cs
  10. 8
      Mono.VisualC.Interop/CppLibrary.cs
  11. 17
      Mono.VisualC.Interop/Interfaces.cs
  12. 3
      Mono.VisualC.Interop/Mono.VisualC.Interop.csproj
  13. 14
      Mono.VisualC.Interop/Util.cs

2
CPPPOC/Main.cs

@ -13,7 +13,7 @@ namespace CPPPOC @@ -13,7 +13,7 @@ namespace CPPPOC
public static void Main(string[] args)
{
// bind all wrapper classes to their native implementations
CppLibrary cppTest = new CppLibrary("CPPTest", new Mono.VisualC.Interop.ABI.Itanium());
CppLibrary cppTest = new CppLibrary("CPPTest", new Mono.VisualC.Interop.ABI.ItaniumAbi());
CSimpleClass.Bind(cppTest);
CSimpleClass csc1 = new CSimpleClass(CreateCSimpleSubClass(10));

99
Mono.VisualC.Interop/ABI/CppAbi.cs

@ -31,11 +31,22 @@ namespace Mono.VisualC.Interop.ABI { @@ -31,11 +31,22 @@ namespace Mono.VisualC.Interop.ABI {
protected FieldBuilder vtableField;
protected ILGenerator ctorIL;
public virtual Iface ImplementClass<Iface, NLayout> (ModuleBuilder implModule, Type wrapperType, string lib, string className)
// default settings that subclasses can override:
protected MakeVTableDelegate makeVTableMethod = VTable.DefaultImplementation;
protected MemberFilter vtableOverrideFilter = VTable.BindToSignatureAndAttribute;
private struct EmptyNativeLayout { }
public Iface ImplementClass<Iface> (Type wrapperType, string lib, string className)
{
return this.ImplementClass<Iface,EmptyNativeLayout> (wrapperType, lib, className);
}
public virtual Iface ImplementClass<Iface, NLayout> (Type wrapperType, string lib, string className)
where NLayout : struct
//where Iface : ICppClassInstantiatable or ICppClassOverridable
{
this.implModule = implModule;
this.implModule = CppLibrary.interopModule;
this.library = lib;
this.className = className;
this.interfaceType = typeof (Iface);
@ -44,6 +55,15 @@ namespace Mono.VisualC.Interop.ABI { @@ -44,6 +55,15 @@ namespace Mono.VisualC.Interop.ABI {
DefineImplType ();
var properties = ( // get all properties defined on the interface
from property in interfaceType.GetProperties ()
select property
).Union( // ... as well as those defined on inherited interfaces
from iface in interfaceType.GetInterfaces ()
from property in iface.GetProperties ()
select property
);
var methods = ( // get all methods defined on the interface
from method in interfaceType.GetMethods ()
orderby method.MetadataToken
@ -57,9 +77,9 @@ namespace Mono.VisualC.Interop.ABI { @@ -57,9 +77,9 @@ namespace Mono.VisualC.Interop.ABI {
var managedOverrides = from method in methods
where Modifiers.IsVirtual (method)
orderby method.MetadataToken
select GetManagedOverrideTrampoline (method, VTable.BindToSignatureAndAttribute);
select GetManagedOverrideTrampoline (method, vtableOverrideFilter);
vtable = MakeVTable (managedOverrides.ToArray ());
vtable = makeVTableMethod (managedOverrides.ToArray ());
// Implement all methods
int vtableIndex = 0;
@ -75,9 +95,8 @@ namespace Mono.VisualC.Interop.ABI { @@ -75,9 +95,8 @@ namespace Mono.VisualC.Interop.ABI {
}
// Implement all properties
foreach (var property in interfaceType.GetProperties ())
DefineFieldProperty (property);
foreach (var property in properties)
DefineProperty (property);
ctorIL.Emit (OpCodes.Ret);
return (Iface)Activator.CreateInstance (implType.CreateType (), vtable);
@ -110,11 +129,6 @@ namespace Mono.VisualC.Interop.ABI { @@ -110,11 +129,6 @@ namespace Mono.VisualC.Interop.ABI {
}
}
protected virtual VTable MakeVTable (Delegate[] overrides)
{
return new VTableManaged (implModule, overrides);
}
// The members below must be implemented for a given C++ ABI:
public abstract string GetMangledMethodName (MethodInfo methodInfo);
@ -147,9 +161,17 @@ namespace Mono.VisualC.Interop.ABI { @@ -147,9 +161,17 @@ namespace Mono.VisualC.Interop.ABI {
// 1. Generate managed trampoline to call native method
MethodBuilder trampoline = GetMethodBuilder (interfaceMethod);
ILGenerator il = trampoline.GetILGenerator ();
if (methodType == MethodType.ManagedAlloc) {
if (methodType == MethodType.NoOp) {
// return NULL if method is supposed to return a value
// TODO: this will make value types explode?
if (!interfaceMethod.ReturnType.Equals (typeof (void)))
il.Emit (OpCodes.Ldnull);
il.Emit (OpCodes.Ret);
return trampoline;
} else if (methodType == MethodType.ManagedAlloc) {
EmitManagedAlloc (il);
il.Emit (OpCodes.Ret);
return trampoline;
@ -187,7 +209,7 @@ namespace Mono.VisualC.Interop.ABI { @@ -187,7 +209,7 @@ namespace Mono.VisualC.Interop.ABI {
return trampoline;
}
protected virtual PropertyBuilder DefineFieldProperty (PropertyInfo property)
protected virtual PropertyBuilder DefineProperty (PropertyInfo property)
{
if (property.CanWrite)
throw new InvalidProgramException ("Properties in C++ interface must be read-only.");
@ -196,29 +218,35 @@ namespace Mono.VisualC.Interop.ABI { @@ -196,29 +218,35 @@ namespace Mono.VisualC.Interop.ABI {
string methodName = imethod.Name;
string propName = property.Name;
Type retType = imethod.ReturnType;
if ((!retType.IsGenericType) || (!retType.GetGenericTypeDefinition ().Equals (typeof (CppField<>))))
FieldBuilder fieldData;
// C++ interface properties are either to return the VTable or to access C++ fields
if (retType.IsGenericType && retType.GetGenericTypeDefinition ().Equals (typeof (CppField<>))) {
// define a new field for the property
fieldData = implType.DefineField ("__" + propName + "_Data", retType, FieldAttributes.InitOnly | FieldAttributes.Private);
// init our field data with a new instance of CppField
// first, get field offset
ctorIL.Emit (OpCodes.Ldarg_0);
/* TODO: Code prolly should not emit hardcoded offsets n such, in case we end up saving these assemblies in the future.
* Something more like this perhaps? (need to figure out how to get field offset padding into this)
* ctorIL.Emit(OpCodes.Ldtoken, nativeLayout);
* ctorIL.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
* ctorIL.Emit(OpCodes.Ldstr, propName);
* ctorIL.Emit(OpCodes.Call, typeof(Marshal).GetMethod("OffsetOf"));
*/
int fieldOffset = ((int)Marshal.OffsetOf (layoutType, propName)) + FieldOffsetPadding;
ctorIL.Emit (OpCodes.Ldc_I4, fieldOffset);
ctorIL.Emit (OpCodes.Newobj, retType.GetConstructor (new Type[] { typeof(int) }));
ctorIL.Emit (OpCodes.Stfld, fieldData);
} else if (retType.Equals (typeof (VTable)))
fieldData = vtableField;
else
throw new InvalidProgramException ("Properties in C++ interface can only be of type CppField.");
PropertyBuilder fieldProp = implType.DefineProperty (propName, PropertyAttributes.None, retType, Type.EmptyTypes);
FieldBuilder fieldData = implType.DefineField ("__" + propName + "_Data", retType, FieldAttributes.InitOnly | FieldAttributes.Private);
// init our field data with a new instance of CppField
// first, get field offset
ctorIL.Emit (OpCodes.Ldarg_0);
/* TODO: Code prolly should not emit hardcoded offsets n such, in case we end up saving these assemblies in the future.
* Something more like this perhaps? (need to figure out how to get field offset padding into this)
* ctorIL.Emit(OpCodes.Ldtoken, nativeLayout);
* ctorIL.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
* ctorIL.Emit(OpCodes.Ldstr, propName);
* ctorIL.Emit(OpCodes.Call, typeof(Marshal).GetMethod("OffsetOf"));
*/
int fieldOffset = ((int)Marshal.OffsetOf (layoutType, propName)) + FieldOffsetPadding;
ctorIL.Emit (OpCodes.Ldc_I4, fieldOffset);
ctorIL.Emit (OpCodes.Newobj, retType.GetConstructor (new Type[] { typeof(int) }));
ctorIL.Emit (OpCodes.Stfld, fieldData);
MethodAttributes methodAttr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder fieldGetter = implType.DefineMethod (methodName, methodAttr, retType, Type.EmptyTypes);
@ -252,6 +280,7 @@ namespace Mono.VisualC.Interop.ABI { @@ -252,6 +280,7 @@ namespace Mono.VisualC.Interop.ABI {
DynamicMethod trampolineIn = new DynamicMethod (wrapperType.Name + "_" + interfaceMethod.Name + "_FromNative", interfaceMethod.ReturnType,
parameterTypes, typeof (CppInstancePtr).Module, true);
Util.ApplyMethodParameterAttributes (interfaceMethod, trampolineIn);
ILGenerator il = trampolineIn.GetILGenerator ();
// for static methods:
@ -301,7 +330,7 @@ namespace Mono.VisualC.Interop.ABI { @@ -301,7 +330,7 @@ namespace Mono.VisualC.Interop.ABI {
Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod, false);
MethodBuilder methodBuilder = implType.DefineMethod (interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual,
interfaceMethod.ReturnType, parameterTypes);
Util.ApplyMethodParameterAttributes (interfaceMethod, methodBuilder);
return methodBuilder;
}

4
Mono.VisualC.Interop/ABI/Impl/Itanium.cs

@ -6,9 +6,9 @@ using System.Runtime.InteropServices; @@ -6,9 +6,9 @@ using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
public class Itanium : CppAbi {
public class ItaniumAbi : CppAbi {
public Itanium() {}
public ItaniumAbi() {}
public override CallingConvention DefaultCallingConvention {
get {

38
Mono.VisualC.Interop/ABI/Impl/VirtualOnlyAbi.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using Mono.VisualC.Interop.ABI;
namespace Mono.VisualC.Interop {
public class VirtualOnlyAbi : CppAbi {
public VirtualOnlyAbi (MakeVTableDelegate makeVTable, MemberFilter vtableOverrideFilter)
{
this.makeVTableMethod = makeVTable;
this.vtableOverrideFilter = vtableOverrideFilter;
}
public VirtualOnlyAbi () { }
public override MethodType GetMethodType (MethodInfo imethod)
{
MethodType defaultType = base.GetMethodType (imethod);
if (defaultType == MethodType.NativeCtor || defaultType == MethodType.NativeDtor)
return MethodType.NoOp;
return defaultType;
}
public override string GetMangledMethodName (MethodInfo methodInfo)
{
throw new NotSupportedException ("Name mangling is not supported by this class. All C++ interface methods must be declared virtual.");
}
public override CallingConvention DefaultCallingConvention {
get {
throw new NotSupportedException ("This class does not support this property.");
}
}
}
}

1
Mono.VisualC.Interop/ABI/MethodType.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
namespace Mono.VisualC.Interop {
public enum MethodType {
NoOp,
Native,
NativeCtor,
NativeDtor,

31
Mono.VisualC.Interop/ABI/VTable.cs

@ -15,8 +15,19 @@ using System.Reflection.Emit; @@ -15,8 +15,19 @@ using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
public delegate VTable MakeVTableDelegate (Delegate[] overrides);
public abstract class VTable : IDisposable {
// The COM-interop-based implemenation is the default because it offers better
// performance (I think?) than the fully-managed implementation.
public static MakeVTableDelegate DefaultImplementation = VTableManaged.Implementation;
protected IntPtr basePtr, vtPtr;
// This is a bit of a misnomer since the Length of the array will be equal
// to the number of entries in the vtable; entries that aren't overridden
// will be NULL.
protected Delegate[] overrides;
public virtual int EntryCount {
@ -29,7 +40,7 @@ namespace Mono.VisualC.Interop.ABI { @@ -29,7 +40,7 @@ namespace Mono.VisualC.Interop.ABI {
public abstract MethodInfo PrepareVirtualCall (MethodInfo target, ILGenerator callsite, FieldInfo vtableField,
LocalBuilder native, int vtableIndex);
// Creates a new VTable
// Subclasses must allocate vtPtr!
public VTable (Delegate[] overrides)
{
this.overrides = overrides;
@ -54,17 +65,17 @@ namespace Mono.VisualC.Interop.ABI { @@ -54,17 +65,17 @@ namespace Mono.VisualC.Interop.ABI {
public virtual void InitInstance (IntPtr instance)
{
if (basePtr == IntPtr.Zero) {
if (basePtr == IntPtr.Zero) {
basePtr = Marshal.ReadIntPtr (instance);
if ((basePtr != IntPtr.Zero) && (basePtr != vtPtr)) {
int offset = 0;
for (int i = 0; i < EntryCount; i++) {
int offset = 0;
for (int i = 0; i < EntryCount; i++) {
IntPtr vtEntryPtr = Marshal.ReadIntPtr (vtPtr, offset);
if (vtEntryPtr == IntPtr.Zero)
Marshal.WriteIntPtr (vtPtr, offset, Marshal.ReadIntPtr (basePtr, offset));
if (overrides [i] == null)
Marshal.WriteIntPtr (vtPtr, offset, Marshal.ReadIntPtr (basePtr, offset));
offset += EntrySize;
offset += EntrySize;
}
}
}
@ -89,7 +100,7 @@ namespace Mono.VisualC.Interop.ABI { @@ -89,7 +100,7 @@ namespace Mono.VisualC.Interop.ABI {
}
// TODO: This WON'T usually be called because VTables are associated with classes
// and managed C++ class wrappers are staticly held?
// (not instances) and managed C++ class wrappers are staticly held?
public void Dispose ()
{
Dispose (true);

8
Mono.VisualC.Interop/ABI/VTableCOM.cs

@ -18,7 +18,13 @@ using System.Runtime.InteropServices; @@ -18,7 +18,13 @@ using System.Runtime.InteropServices;
namespace Mono.VisualC.Interop.ABI {
public class VTableCOM : VTable {
public VTableCOM (Delegate[] entries) : base(entries)
private static VTable MakeVTableCOM (Delegate[] entries)
{
return new VTableCOM (entries);
}
public static MakeVTableDelegate Implementation = MakeVTableCOM;
private VTableCOM (Delegate[] entries) : base(entries)
{
int managedOverrides = (from entry in entries
where entry != null

12
Mono.VisualC.Interop/ABI/VTableManaged.cs

@ -18,9 +18,15 @@ namespace Mono.VisualC.Interop.ABI { @@ -18,9 +18,15 @@ namespace Mono.VisualC.Interop.ABI {
public class VTableManaged : VTable {
private ModuleBuilder implModule;
public VTableManaged (ModuleBuilder implModule, Delegate[] entries) : base(entries)
private static VTable MakeVTableManaged (Delegate[] entries)
{
this.implModule = implModule;
return new VTableManaged (entries);
}
public static MakeVTableDelegate Implementation = MakeVTableManaged;
private VTableManaged (Delegate[] entries) : base(entries)
{
this.implModule = CppLibrary.interopModule;
this.vtPtr = Marshal.AllocHGlobal (EntryCount * EntrySize);
WriteOverrides (0);
@ -60,7 +66,7 @@ namespace Mono.VisualC.Interop.ABI { @@ -60,7 +66,7 @@ namespace Mono.VisualC.Interop.ABI {
}
public T GetDelegateForNative<T> (IntPtr native, int index) where T : class /*Delegate*/
public virtual T GetDelegateForNative<T> (IntPtr native, int index) where T : class /*Delegate*/
{
IntPtr vtable = Marshal.ReadIntPtr (native);
if (vtable == vtPtr) // do not return managed overrides

36
Mono.VisualC.Interop/CppInstancePtr.cs

@ -8,19 +8,53 @@ @@ -8,19 +8,53 @@
//
using System;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Mono.VisualC.Interop.ABI;
namespace Mono.VisualC.Interop {
public struct CppInstancePtr : ICppObject {
private IntPtr ptr;
private bool manageMemory;
private static Dictionary<Type,object> implCache = new Dictionary<Type,object> ();
public static CppInstancePtr ForManagedObject<Iface,TWrapper> (TWrapper managed)
where Iface : ICppClassOverridable<TWrapper>
{
object cachedImpl;
Iface impl;
if (!implCache.TryGetValue (typeof (Iface), out cachedImpl))
{
// Since we're only using the VTable to allow C++ code to call managed methods,
// there is no advantage to using VTableCOM. Also, VTableCOM is based on this.
VirtualOnlyAbi virtualABI = new VirtualOnlyAbi (VTableManaged.Implementation, VTable.BindToSignature);
impl = virtualABI.ImplementClass<Iface> (typeof (TWrapper), string.Empty, string.Empty);
implCache.Add (typeof (Iface), impl);
}
else
impl = (Iface)cachedImpl;
CppInstancePtr instance = impl.Alloc (managed);
impl.ClassVTable.InitInstance ((IntPtr)instance);
return instance;
}
// Alloc a new C++ instance
internal CppInstancePtr (int nativeSize, object managedWrapper)
{
// Under the hood, we're secretly subclassing this C++ class to store a
// handle to the managed wrapper.
ptr = Marshal.AllocHGlobal (nativeSize + Marshal.SizeOf (typeof (IntPtr)));
int allocSize = nativeSize + Marshal.SizeOf (typeof (IntPtr));
ptr = Marshal.AllocHGlobal (allocSize);
// zero memory for sanity
byte[] zeroArray = new byte [allocSize];
Marshal.Copy (zeroArray, 0, ptr, allocSize);
IntPtr handlePtr = GetGCHandle (managedWrapper);
Marshal.WriteIntPtr (ptr, nativeSize, handlePtr);

8
Mono.VisualC.Interop/CppLibrary.cs

@ -23,8 +23,8 @@ namespace Mono.VisualC.Interop @@ -23,8 +23,8 @@ namespace Mono.VisualC.Interop
public sealed class CppLibrary
{
private static AssemblyBuilder interopAssembly;
private static ModuleBuilder interopModule;
internal static AssemblyBuilder interopAssembly;
internal static ModuleBuilder interopModule;
private CppAbi abi;
private string name;
@ -64,7 +64,7 @@ namespace Mono.VisualC.Interop @@ -64,7 +64,7 @@ namespace Mono.VisualC.Interop
where NativeLayout : struct
{
return Abi.ImplementClass<Iface, NativeLayout> (interopModule, null, Name, className);
return Abi.ImplementClass<Iface, NativeLayout> (null, Name, className);
}
// For a class that may have fields and virtual methods to be overridden
@ -74,7 +74,7 @@ namespace Mono.VisualC.Interop @@ -74,7 +74,7 @@ namespace Mono.VisualC.Interop
where Managed : ICppObject
{
return Abi.ImplementClass<Iface, NativeLayout> (interopModule, typeof (Managed), Name, className);
return Abi.ImplementClass<Iface, NativeLayout> (typeof (Managed), Name, className);
}
// TODO: Define a method for pure virtual classes (no NativeLayout)?

17
Mono.VisualC.Interop/Interfaces.cs

@ -8,19 +8,26 @@ @@ -8,19 +8,26 @@
//
using System;
using Mono.VisualC.Interop.ABI;
namespace Mono.VisualC.Interop {
public interface ICppObject : IDisposable {
IntPtr Native { get; }
IntPtr Native { get; }
}
public interface ICppClassInstantiatable {
CppInstancePtr Alloc ();
public interface ICppClass {
VTable ClassVTable { get; }
void Destruct (CppInstancePtr instance);
}
public interface ICppClassOverridable<T> where T : ICppObject {
public interface ICppClassInstantiatable : ICppClass {
CppInstancePtr Alloc ();
}
// It is recommended that managed wrappers implement ICppObject, but
// I'm not making it required so that any arbitrary object can be exposed to
// C++ via CppInstancePtr.ForManagedObject.
public interface ICppClassOverridable<T> : ICppClass /* where T : ICppObject */ {
CppInstancePtr Alloc (T managed);
void Destruct (CppInstancePtr instance);
}
}

3
Mono.VisualC.Interop/Mono.VisualC.Interop.csproj

@ -54,7 +54,6 @@ @@ -54,7 +54,6 @@
<Compile Include="CppLibrary.cs" />
<Compile Include="AssemblyInfo.cs" />
<Compile Include="ABI\CppAbi.cs" />
<Compile Include="ABI\Impl\Itanium.cs" />
<Compile Include="Interfaces.cs" />
<Compile Include="ABI\VTableManaged.cs" />
<Compile Include="Attributes.cs" />
@ -64,6 +63,8 @@ @@ -64,6 +63,8 @@
<Compile Include="ABI\VTable.cs" />
<Compile Include="Util.cs" />
<Compile Include="ABI\MethodType.cs" />
<Compile Include="ABI\Impl\VirtualOnlyAbi.cs" />
<Compile Include="ABI\Impl\Itanium.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

14
Mono.VisualC.Interop/Util.cs

@ -64,5 +64,19 @@ namespace Mono.VisualC.Interop { @@ -64,5 +64,19 @@ namespace Mono.VisualC.Interop {
return parameterTypes;
}
public static void ApplyMethodParameterAttributes (MethodInfo source, MethodBuilder target)
{
ParameterInfo[] parameters = source.GetParameters ();
foreach (var parameter in parameters)
target.DefineParameter (parameter.Position, parameter.Attributes, parameter.Name);
}
public static void ApplyMethodParameterAttributes (MethodInfo source, DynamicMethod target)
{
ParameterInfo[] parameters = source.GetParameters ();
foreach (var parameter in parameters)
target.DefineParameter (parameter.Position, parameter.Attributes, parameter.Name);
}
}
}

Loading…
Cancel
Save