@ -20,32 +20,6 @@ using Mono.VisualC.Interop.Util;
namespace Mono.VisualC.Interop.ABI {
namespace Mono.VisualC.Interop.ABI {
//
// Marshalling to be done for a single parameter
//
public enum ParameterMarshal {
Default = 0 ,
ClassByRef = 1 ,
ClassByVal = 2
}
//
// Describes the signature of the pinvoke wrapper of a c++ method, along with
// marshalling information
//
public class PInvokeSignature {
// The original c# method this signature was generated from
public MethodInfo OrigMethod ;
public List < Type > ParameterTypes { get ; set ; }
public List < ParameterMarshal > ParameterMarshallers { get ; set ; }
public Type ReturnType { get ; set ; }
// Whenever to return a class value
public bool ReturnClass { get ; set ; }
// Whenever to return a class value by passing a hidden first argument
// Used by the itanium c++ abi
public bool ReturnByAddr { get ; set ; }
}
//FIXME: Exception handling, operator overloading etc.
//FIXME: Exception handling, operator overloading etc.
//FIXME: Allow interface to override default calling convention
//FIXME: Allow interface to override default calling convention
public abstract partial class CppAbi {
public abstract partial class CppAbi {
@ -53,7 +27,8 @@ namespace Mono.VisualC.Interop.ABI {
protected TypeBuilder impl_type ;
protected TypeBuilder impl_type ;
protected Type interface_type , layout_type , wrapper_type ;
protected Type interface_type , layout_type , wrapper_type ;
protected string library , class_name ;
protected CppLibrary library ;
protected string class_name ;
protected FieldBuilder typeinfo_field ;
protected FieldBuilder typeinfo_field ;
protected ILGenerator ctor_il ;
protected ILGenerator ctor_il ;
@ -61,17 +36,33 @@ namespace Mono.VisualC.Interop.ABI {
protected MemberFilter vtable_override_filter = VTable . BindToSignatureAndAttribute ;
protected MemberFilter vtable_override_filter = VTable . BindToSignatureAndAttribute ;
// Cache some frequently used methodinfos:
// Cache some frequently used methodinfos:
private static readonly MethodInfo typeinfo_nativesize = typeof ( CppTypeInfo ) . GetProperty ( "NativeSize" ) . GetGetMethod ( ) ;
private static readonly MethodInfo typeinfo_nativesize = typeof ( CppTypeInfo ) . GetProperty ( "NativeSize" ) . GetGetMethod ( ) ;
private static readonly MethodInfo typeinfo_vtable = typeof ( CppTypeInfo ) . GetProperty ( "VTable" ) . GetGetMethod ( ) ;
private static readonly MethodInfo typeinfo_vtable = typeof ( CppTypeInfo ) . GetProperty ( "VTable" ) . GetGetMethod ( ) ;
private static readonly MethodInfo typeinfo_adjvcall = typeof ( CppTypeInfo ) . GetMethod ( "GetAdjustedVirtualCall" ) ;
private static readonly MethodInfo typeinfo_adjvcall = typeof ( CppTypeInfo ) . GetMethod ( "GetAdjustedVirtualCall" ) ;
private static readonly MethodInfo vtable_initinstance = typeof ( VTable ) . GetMethod ( "InitInstance" ) ;
private static readonly MethodInfo typeinfo_fieldoffset = typeof ( CppTypeInfo ) . GetProperty ( "FieldOffsetPadding" ) . GetGetMethod ( ) ;
private static readonly MethodInfo vtable_resetinstance = typeof ( VTable ) . GetMethod ( "ResetInstance" ) ;
private static readonly MethodInfo vtable_initinstance = typeof ( VTable ) . GetMethod ( "InitInstance" ) ;
private static readonly MethodInfo vtable_resetinstance = typeof ( VTable ) . GetMethod ( "ResetInstance" ) ;
private static readonly MethodInfo cppobj_native = typeof ( ICppObject ) . GetProperty ( "Native" ) . GetGetMethod ( ) ;
private static readonly MethodInfo cppip_native = typeof ( CppInstancePtr ) . GetProperty ( "Native" ) . GetGetMethod ( ) ;
private static readonly MethodInfo cppip_managedalloc = typeof ( CppInstancePtr ) . GetProperty ( "IsManagedAlloc" ) . GetGetMethod ( ) ;
private static readonly MethodInfo cppip_getmanaged = typeof ( CppInstancePtr ) . GetMethod ( "GetManaged" , BindingFlags . Static | BindingFlags . NonPublic ) ;
private static readonly ConstructorInfo cppip_fromnative = typeof ( CppInstancePtr ) . GetConstructor ( new Type [ ] { typeof ( IntPtr ) } ) ;
private static readonly ConstructorInfo cppip_fromsize = typeof ( CppInstancePtr ) . GetConstructor ( new Type [ ] { typeof ( int ) } ) ;
private static readonly ConstructorInfo cppip_fromsize_managed = typeof ( CppInstancePtr ) . GetConstructor ( BindingFlags . Instance | BindingFlags . NonPublic , null ,
new Type [ ] { typeof ( int ) , typeof ( object ) } , null ) ;
private static readonly ConstructorInfo notimplementedexception = typeof ( NotImplementedException ) . GetConstructor ( new Type [ ] { typeof ( string ) } ) ;
private static readonly MethodInfo type_gettypefromhandle = typeof ( Type ) . GetMethod ( "GetTypeFromHandle" ) ;
private static readonly MethodInfo marshal_offsetof = typeof ( Marshal ) . GetMethod ( "OffsetOf" ) ;
private static readonly MethodInfo marshal_structuretoptr = typeof ( Marshal ) . GetMethod ( "StructureToPtr" ) ;
// These methods might be more commonly overridden for a given C++ ABI:
// These methods might be more commonly overridden for a given C++ ABI:
public virtual MethodType GetMethodType ( MethodInfo imethod )
public virtual MethodType GetMethodType ( MethodInfo imethod )
{
{
if ( imethod . IsDefined ( typeof ( ConstructorAttribute ) , false ) )
if ( IsInline ( imethod ) & & library . InlineMethodPolicy = = InlineMethods . NotPresent )
return MethodType . NotImplemented ;
else if ( imethod . IsDefined ( typeof ( ConstructorAttribute ) , false ) )
return MethodType . NativeCtor ;
return MethodType . NativeCtor ;
else if ( imethod . Name . Equals ( "Alloc" ) )
else if ( imethod . Name . Equals ( "Alloc" ) )
return MethodType . ManagedAlloc ;
return MethodType . ManagedAlloc ;
@ -94,12 +85,12 @@ namespace Mono.VisualC.Interop.ABI {
// The ImplementClass overrides are the main entry point to the Abi API:
// The ImplementClass overrides are the main entry point to the Abi API:
private struct EmptyNativeLayout { }
private struct EmptyNativeLayout { }
public Iface ImplementClass < Iface > ( Type wrapperType , string lib , string className )
public Iface ImplementClass < Iface > ( Type wrapperType , CppLibrary lib , string className )
{
{
return this . ImplementClass < Iface , EmptyNativeLayout > ( wrapperType , lib , className ) ;
return this . ImplementClass < Iface , EmptyNativeLayout > ( wrapperType , lib , className ) ;
}
}
public virtual Iface ImplementClass < Iface , NLayout > ( Type wrapperType , string lib , string className )
public virtual Iface ImplementClass < Iface , NLayout > ( Type wrapperType , CppLibrary lib , string className )
where NLayout : struct
where NLayout : struct
//where Iface : ICppClassInstantiatable or ICppClassOverridable
//where Iface : ICppClassInstantiatable or ICppClassOverridable
{
{
@ -113,8 +104,8 @@ namespace Mono.VisualC.Interop.ABI {
DefineImplType ( ) ;
DefineImplType ( ) ;
var properties = GetProperties ( ) ;
var properties = GetProperties ( ) ;
var methods = GetMethods ( ) ;
var methods = GetMethods ( ) . Select ( m = > GetPInvokeSignature ( m ) ) ;
CppTypeInfo typeInfo = MakeTypeInfo ( methods , methods . Where ( m = > IsVirtual ( m ) ) ) ;
var typeInfo = MakeTypeInfo ( methods ) ;
// Implement all methods
// Implement all methods
int vtableIndex = 0 ;
int vtableIndex = 0 ;
@ -130,9 +121,9 @@ namespace Mono.VisualC.Interop.ABI {
return ( Iface ) Activator . CreateInstance ( impl_type . CreateType ( ) , typeInfo ) ;
return ( Iface ) Activator . CreateInstance ( impl_type . CreateType ( ) , typeInfo ) ;
}
}
protected virtual CppTypeInfo MakeTypeInfo ( IEnumerable < MethodInfo > methods , IEnumerable < MethodInfo > virtualM ethods)
protected virtual CppTypeInfo MakeTypeInfo ( IEnumerable < PInvokeSignature > m ethods)
{
{
return new CppTypeInfo ( this , virtualMethods , layout_type ) ;
return new CppTypeInfo ( this , methods . Where ( m = > IsVirtual ( m . OrigMethod ) ) , layout_type ) ;
}
}
protected virtual IEnumerable < PropertyInfo > GetProperties ( )
protected virtual IEnumerable < PropertyInfo > GetProperties ( )
@ -171,8 +162,6 @@ namespace Mono.VisualC.Interop.ABI {
impl_type = impl_module . DefineType ( implTypeName , TypeAttributes . Class | TypeAttributes . Sealed ) ;
impl_type = impl_module . DefineType ( implTypeName , TypeAttributes . Class | TypeAttributes . Sealed ) ;
impl_type . AddInterfaceImplementation ( interface_type ) ;
impl_type . AddInterfaceImplementation ( interface_type ) ;
//vtable_field = impl_type.DefineField ("_vtable", typeof (VTable), FieldAttributes.InitOnly | FieldAttributes.Private);
//native_size_field = impl_type.DefineField ("_nativeSize", typeof (int), FieldAttributes.InitOnly | FieldAttributes.Private);
typeinfo_field = impl_type . DefineField ( "_typeInfo" , typeof ( CppTypeInfo ) , FieldAttributes . InitOnly | FieldAttributes . Private ) ;
typeinfo_field = impl_type . DefineField ( "_typeInfo" , typeof ( CppTypeInfo ) , FieldAttributes . InitOnly | FieldAttributes . Private ) ;
ConstructorBuilder ctor = impl_type . DefineConstructor ( MethodAttributes . Public , CallingConventions . Standard ,
ConstructorBuilder ctor = impl_type . DefineConstructor ( MethodAttributes . Public , CallingConventions . Standard ,
@ -184,38 +173,32 @@ namespace Mono.VisualC.Interop.ABI {
ctor_il . Emit ( OpCodes . Ldarg_0 ) ;
ctor_il . Emit ( OpCodes . Ldarg_0 ) ;
ctor_il . Emit ( OpCodes . Ldarg_1 ) ;
ctor_il . Emit ( OpCodes . Ldarg_1 ) ;
ctor_il . Emit ( OpCodes . Stfld , typeinfo_field ) ;
ctor_il . Emit ( OpCodes . Stfld , typeinfo_field ) ;
/ *
// this._vtable = (VTable passed to constructor)
ctor_il . Emit ( OpCodes . Ldarg_0 ) ;
ctor_il . Emit ( OpCodes . Ldarg_1 ) ;
ctor_il . Emit ( OpCodes . Stfld , vtable_field ) ;
// this._nativeSize = (native size passed to constructor)
ctor_il . Emit ( OpCodes . Ldarg_0 ) ;
ctor_il . Emit ( OpCodes . Ldarg_2 ) ;
ctor_il . Emit ( OpCodes . Stfld , native_size_field ) ;
* /
}
}
protected virtual MethodBuilder DefineMethod ( MethodInfo interfaceMethod , CppTypeInfo typeInfo , ref int vtableIndex )
protected virtual MethodBuilder DefineMethod ( PInvokeSignature psig , CppTypeInfo typeInfo , ref int vtableIndex )
{
{
// 0. Introspect method
var interfaceMethod = psig . OrigMethod ;
MethodType methodType = GetMethodType ( interfaceMethod ) ;
Type [ ] parameterTypes = ReflectionHelper . GetMethodParameterTypes ( interfaceMethod ) ;
// 1. Generate managed trampoline to call native method
// 1. Generate managed trampoline to call native method
MethodBuilder trampoline = GetMethodBuilder ( interfaceMethod ) ;
MethodBuilder trampoline = GetMethodBuilder ( interfaceMethod ) ;
ILGenerator il = trampoline . GetILGenerator ( ) ;
ILGenerator il = trampoline . GetILGenerator ( ) ;
if ( methodType = = MethodType . NoOp ) {
switch ( psig . Type ) {
case MethodType . NotImplemented :
il . Emit ( OpCodes . Ldstr , "This method is not available." ) ;
il . Emit ( OpCodes . Newobj , notimplementedexception ) ;
il . Emit ( OpCodes . Throw ) ;
goto case MethodType . NoOp ; // fallthrough
case MethodType . NoOp :
// return NULL if method is supposed to return a value
// return NULL if method is supposed to return a value
// FIXME: this will make value types explode?
if ( ! interfaceMethod . ReturnType . Equals ( typeof ( void ) ) )
if ( ! interfaceMethod . ReturnType . Equals ( typeof ( void ) ) )
il . Emit ( OpCodes . Ldnull ) ;
il . Emit ( OpCodes . Ldnull ) ;
il . Emit ( OpCodes . Ret ) ;
il . Emit ( OpCodes . Ret ) ;
return trampoline ;
return trampoline ;
} else if ( methodType = = MethodType . ManagedAlloc ) {
case MethodType . ManagedAlloc :
EmitManagedAlloc ( il , interfaceMethod ) ;
EmitManagedAlloc ( il , interfaceMethod ) ;
il . Emit ( OpCodes . Ret ) ;
il . Emit ( OpCodes . Ret ) ;
return trampoline ;
return trampoline ;
@ -224,93 +207,43 @@ namespace Mono.VisualC.Interop.ABI {
bool isStatic = IsStatic ( interfaceMethod ) ;
bool isStatic = IsStatic ( interfaceMethod ) ;
LocalBuilder cppInstancePtr = null ;
LocalBuilder cppInstancePtr = null ;
LocalBuilder nativePtr = null ;
LocalBuilder nativePtr = null ;
LocalBuilder retVal = null ;
LocalBuilder retArg = null ;
// If we're an instance method, load up the "this" pointer
// If we're an instance method, load up the "this" pointer
if ( ! isStatic )
if ( ! isStatic )
{
{
if ( parameterTypes . Length < 1 )
if ( psig . P arameterTypes . Count = = 0 )
throw new ArgumentException ( "First argument to non-static C++ method must be IntPtr or CppInstancePtr." ) ;
throw new ArgumentException ( "First argument to non-static C++ method must be IntPtr or CppInstancePtr." ) ;
// 2. Load the native C++ instance pointer
// 2. Load the native C++ instance pointer
EmitLoadInstancePtr ( il , parameterTypes [ 0 ] , out cppInstancePtr , out nativePtr ) ;
EmitLoadInstancePtr ( il , interfaceMethod . GetParameters ( ) [ 0 ] . ParameterType , out cppInstancePtr , out nativePtr ) ;
// 3. Make sure our native pointer is a valid reference. If not, throw ObjectDisposedException
// 3. Make sure our native pointer is a valid reference. If not, throw ObjectDisposedException
EmitCheckDisposed ( il , nativePtr , method Type) ;
EmitCheckDisposed ( il , nativePtr , psig . Type ) ;
}
}
MethodInfo nativeMethod ;
MethodInfo nativeMethod ;
var psig = GetPInvokeSignature ( typeInfo , interfaceMethod ) ;
if ( IsVirtual ( interfaceMethod ) & & psig . Type ! = MethodType . NativeDtor ) {
nativeMethod = EmitPrepareVirtualCall ( il , typeInfo , nativePtr , vtableIndex + + ) ;
if ( psig . ReturnClass ) {
} else {
Debug . Assert ( wrapper_type ! = null ) ;
if ( IsVirtual ( interfaceMethod ) )
retVal = il . DeclareLocal ( wrapper_type ) ;
vtableIndex + + ;
// Construct the manager wrapper object
var ctor = wrapper_type . GetConstructor ( BindingFlags . Instance | BindingFlags . NonPublic , null , new Type [ ] { typeof ( CppLibrary ) } , null ) ;
Debug . Assert ( ctor ! = null ) ;
il . Emit ( OpCodes . Ldnull ) ;
il . Emit ( OpCodes . Newobj , ctor ) ;
il . Emit ( OpCodes . Stloc , retVal ) ;
psig . ReturnType = layout_type ;
}
if ( psig . ReturnByAddr ) {
nativeMethod = GetPInvokeForMethod ( psig ) ;
//
// When using the Itanium c++ abi, some classes are returned by passing a
// hidden first argument.
//
// Put the address of the native return memory into a local
retArg = il . DeclareLocal ( typeof ( IntPtr ) ) ;
il . Emit ( OpCodes . Ldloc , retVal ) ;
EmitLoadNativePtr ( il ) ;
il . Emit ( OpCodes . Stloc , retArg ) ;
psig . ReturnType = typeof ( void ) ;
}
}
if ( IsVirtual ( interfaceMethod ) & & methodType ! = MethodType . NativeDtor )
switch ( psig . Type ) {
nativeMethod = EmitPrepareVirtualCall ( il , typeInfo , nativePtr , vtableIndex + + ) ;
else
nativeMethod = GetPInvokeForMethod ( interfaceMethod , psig ) ;
switch ( methodType ) {
case MethodType . NativeCtor :
case MethodType . NativeCtor :
EmitConstruct ( il , nativeMethod , psig , nativePtr ) ;
EmitConstruct ( il , nativeMethod , psig , nativePtr ) ;
break ;
break ;
case MethodType . NativeDtor :
case MethodType . NativeDtor :
EmitDestruct ( il , nativeMethod , psig , cppInstancePtr , nativePtr ) ;
EmitDestruct ( il , nativeMethod , psig , cppInstancePtr , nativePtr ) ;
break ;
break ;
default :
default :
EmitNativeCall ( il , nativeMethod , isStatic , psig , nativePtr , retArg ) ;
EmitNativeCall ( il , nativeMethod , psig , nativePtr ) ;
break ;
break ;
}
}
if ( psig . ReturnClass ) {
if ( ! psig . ReturnByAddr ) {
//
// The method return a struct in native format which is on the stack,
// have to copy into the native memory belonging to our object
//
// Save the method return value
var rval = il . DeclareLocal ( layout_type ) ;
il . Emit ( OpCodes . Stloc , rval ) ;
// Load the ptr to the native memory (dest)
il . Emit ( OpCodes . Ldloc , retVal ) ;
EmitLoadNativePtr ( il ) ;
// Load the address of the method return value (src)
il . Emit ( OpCodes . Ldloca , rval ) ;
// Copy
il . Emit ( OpCodes . Cpobj , layout_type ) ;
}
il . Emit ( OpCodes . Ldloc , retVal ) ;
}
il . Emit ( OpCodes . Ret ) ;
il . Emit ( OpCodes . Ret ) ;
return trampoline ;
return trampoline ;
}
}
@ -320,50 +253,67 @@ namespace Mono.VisualC.Interop.ABI {
if ( property . CanWrite )
if ( property . CanWrite )
throw new InvalidProgramException ( "Properties in C++ interface must be read-only." ) ;
throw new InvalidProgramException ( "Properties in C++ interface must be read-only." ) ;
MethodInfo imethod = property . GetGetMethod ( ) ;
var imethod = property . GetGetMethod ( ) ;
string methodName = imethod . Name ;
var methodName = imethod . Name ;
string propName = property . Name ;
var propName = property . Name ;
Type retType = imethod . ReturnType ;
var retType = imethod . ReturnType ;
FieldBuilder fieldData ;
var fieldProp = impl_type . DefineProperty ( propName , PropertyAttributes . None , retType , Type . EmptyTypes ) ;
var methodAttr = MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . SpecialName | MethodAttributes . HideBySig ;
var fieldGetter = impl_type . DefineMethod ( methodName , methodAttr , retType , Type . EmptyTypes ) ;
var il = fieldGetter . GetILGenerator ( ) ;
// C++ interface properties are either to return the CppTypeInfo or to access C++ fields
// C++ interface properties are either to return the CppTypeInfo or to access C++ fields
if ( retType . IsGenericType & & retType . GetGenericTypeDefinition ( ) . Equals ( typeof ( CppField < > ) ) ) {
if ( retType . IsGenericType & & retType . GetGenericTypeDefinition ( ) . Equals ( typeof ( CppField < > ) ) ) {
// define a new field for the property
// define a new field for the property
// fieldData = impl_type.DefineField ("__" + propName + "_Data", retType, FieldAttributes.InitOnly | FieldAttributes.Private);
var fieldData = impl_type . DefineField ( "__" + propName + "_Data" , retType , FieldAttributes . InitOnly | FieldAttributes . Private ) ;
// we need to lazy init the field because we don't have accurate field offset until after
// all base classes have been added (by ctor)
var lazyInit = il . DefineLabel ( ) ;
il . Emit ( OpCodes . Ldarg_0 ) ;
il . Emit ( OpCodes . Ldfld , fieldData ) ;
il . Emit ( OpCodes . Dup ) ;
il . Emit ( OpCodes . Brfalse_S , lazyInit ) ;
il . Emit ( OpCodes . Ret ) ;
// init new CppField
il . MarkLabel ( lazyInit ) ;
il . Emit ( OpCodes . Pop ) ;
il . Emit ( OpCodes . Ldarg_0 ) ;
// init our field data with a new instance of CppField
// first, get field offset
// first, get field offset
//ctor_il.Emit (OpCodes.Ldarg_0);
// = ((int)Marshal.OffsetOf (layout_type, propName)) + FieldOffsetPadding;
il . Emit ( OpCodes . Ldtoken , layout_type ) ;
/ * TODO : Code prolly should not emit hardcoded offsets n such , in case we end up saving these assemblies in the future .
il . Emit ( OpCodes . Call , type_gettypefromhandle ) ;
* Something more like this perhaps ? ( need to figure out how to get field offset padding into this )
il . Emit ( OpCodes . Ldstr , propName ) ;
* ctorIL . Emit ( OpCodes . Ldtoken , nativeLayout ) ;
il . Emit ( OpCodes . Call , marshal_offsetof ) ;
* 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 ( layout_type , propName ) ) + FieldOffsetPadding ;
ctor_il . Emit ( OpCodes . Ldc_I4 , fieldOffset ) ;
ctor_il . Emit ( OpCodes . Newobj , retType . GetConstructor ( new Type [ ] { typeof ( int ) } ) ) ;
ctor_il . Emit ( OpCodes . Stfld , fieldData ) ;
* /
throw new NotImplementedException ( "CppFields need to be reimplemented to use CppTypeInfo." ) ;
} else if ( retType . Equals ( typeof ( CppTypeInfo ) ) )
fieldData = typeinfo_field ;
else
throw new InvalidProgramException ( "Properties in C++ interface can only be of type CppField." ) ;
PropertyBuilder fieldProp = impl_type . DefineProperty ( propName , PropertyAttributes . None , retType , Type . EmptyTypes ) ;
il . Emit ( OpCodes . Ldarg_0 ) ;
il . Emit ( OpCodes . Ldfld , typeinfo_field ) ;
il . Emit ( OpCodes . Callvirt , typeinfo_fieldoffset ) ;
MethodAttributes methodAttr = MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . SpecialName | MethodAttributes . HideBySig ;
il . Emit ( OpCodes . Add ) ;
MethodBuilder fieldGetter = impl_type . DefineMethod ( methodName , methodAttr , retType , Type . EmptyTypes ) ;
ILGenerator il = fieldGetter . GetILGenerator ( ) ;
il . Emit ( OpCodes . Ldarg_0 ) ;
// new CppField<T> (<field offset>)
il . Emit ( OpCodes . Ldfld , fieldData ) ;
il . Emit ( OpCodes . Newobj , retType . GetConstructor ( new Type [ ] { typeof ( int ) } ) ) ;
il . Emit ( OpCodes . Ret ) ;
il . Emit ( OpCodes . Stfld , fieldData ) ;
il . Emit ( OpCodes . Ldarg_0 ) ;
il . Emit ( OpCodes . Ldfld , fieldData ) ;
il . Emit ( OpCodes . Ret ) ;
} else if ( retType . Equals ( typeof ( CppTypeInfo ) ) ) {
il . Emit ( OpCodes . Ldarg_0 ) ;
il . Emit ( OpCodes . Ldfld , typeinfo_field ) ;
il . Emit ( OpCodes . Ret ) ;
} else
throw new InvalidProgramException ( "Properties in C++ interface can only be of type CppField." ) ;
fieldProp . SetGetMethod ( fieldGetter ) ;
fieldProp . SetGetMethod ( fieldGetter ) ;
@ -379,43 +329,47 @@ namespace Mono.VisualC.Interop.ABI {
if ( wrapper_type = = null )
if ( wrapper_type = = null )
return null ;
return null ;
MethodInfo interfaceMethod = typeInfo . VirtualMethods [ vtableIndex ] ;
var sig = typeInfo . VirtualMethods [ vtableIndex ] ;
MethodInfo targetMethod = FindManagedOverrideTarget ( interfaceMethod ) ;
var interfaceMethod = sig . OrigMethod ;
var targetMethod = FindManagedOverrideTarget ( interfaceMethod ) ;
if ( targetMethod = = null )
if ( targetMethod = = null )
return null ;
return null ;
var psig = GetPInvokeSignature ( typeInfo , interfaceMethod ) ;
var interfaceArgs = ReflectionHelper . GetMethodParameterTypes ( interfaceMethod ) ;
var nativeArgs = sig . ParameterTypes . ToArray ( ) ;
// TODO: According to http://msdn.microsoft.com/en-us/library/w16z8yc4.aspx
// TODO: According to http://msdn.microsoft.com/en-us/library/w16z8yc4.aspx
// The dynamic method created with this constructor has access to public and internal members of all the types contained in module m.
// The dynamic method created with this constructor has access to public and internal members of all the types contained in module m.
// This does not appear to hold true, so we also disable JIT visibility checks.
// This does not appear to hold true, so we also disable JIT visibility checks.
DynamicMethod trampolineIn = new DynamicMethod ( wrapper_type . Name + "_" + interfaceMethod . Name + "_FromNative" , p sig. ReturnType ,
var trampolineIn = new DynamicMethod ( wrapper_type . Name + "_" + interfaceMethod . Name + "_FromNative" , sig . ReturnType ,
psig . ParameterTypes . ToArray ( ) , typeof ( CppInstancePtr ) . Module , true ) ;
nativeArgs , typeof ( CppInstancePtr ) . Module , true ) ;
ReflectionHelper . ApplyMethodParameterAttributes ( interfaceMethod , trampolineIn , true ) ;
ReflectionHelper . ApplyMethodParameterAttributes ( interfaceMethod , trampolineIn , true ) ;
ILGenerator il = trampolineIn . GetILGenerator ( ) ;
ILGenerator il = trampolineIn . GetILGenerator ( ) ;
// for static methods:
// for static (target) methods:
OpCode callInstruction = OpCodes . Call ;
OpCode callInstruction = OpCodes . Call ;
int argLoadStart = 1 ;
int argLoadStart = IsStatic ( interfaceMethod ) ? 0 : 1 ; // chop off C++ instance ptr if there is one
// for instance methods, we need an instance to call them on!
// for instance methods, we need a ma naged instance to call them on!
if ( ! targetMethod . IsStatic ) {
if ( ! targetMethod . IsStatic ) {
callInstruction = OpCodes . Callvirt ;
callInstruction = OpCodes . Callvirt ;
//argLoadStart = 1;
argLoadStart = 1 ;
il . Emit ( OpCodes . Ldarg_0 ) ;
il . Emit ( OpCodes . Ldarg_0 ) ;
il . Emit ( OpCodes . Ldc_I4 , typeInfo . NativeSize ) ;
il . Emit ( OpCodes . Ldc_I4 , typeInfo . NativeSize ) ;
MethodInfo getManagedObj = typeof ( CppInstancePtr ) . GetMethod ( "GetManaged" , BindingFlags . Static | BindingFlags . NonPublic ) . MakeGenericMethod ( wrapper_type ) ;
MethodInfo getManagedObj = cppip_getmanaged . MakeGenericMethod ( wrapper_type ) ;
il . Emit ( OpCodes . Call , getManagedObj ) ;
il . Emit ( OpCodes . Call , getManagedObj ) ;
}
}
for ( int i = argLoadStart ; i < psig . ParameterTypes . Count ; i + + ) {
for ( int i = argLoadStart ; i < interfaceArgs . Length ; i + + ) {
il . Emit ( OpCodes . Ldarg , i ) ;
il . Emit ( OpCodes . Ldarg , i ) ;
EmitInboundMarshal ( il , nativeArgs [ i ] , interfaceArgs [ i ] ) ;
}
}
il . Emit ( OpCodes . Tailcall ) ;
il . Emit ( callInstruction , targetMethod ) ;
il . Emit ( callInstruction , targetMethod ) ;
EmitOutboundMarshal ( il , targetMethod . ReturnType , sig . ReturnType ) ;
il . Emit ( OpCodes . Ret ) ;
il . Emit ( OpCodes . Ret ) ;
return trampolineIn . CreateDelegate ( typeInfo . VTableDelegateTypes [ vtableIndex ] ) ;
return trampolineIn . CreateDelegate ( typeInfo . VTableDelegateTypes [ vtableIndex ] ) ;
@ -423,6 +377,9 @@ namespace Mono.VisualC.Interop.ABI {
protected virtual MethodInfo FindManagedOverrideTarget ( MethodInfo interfaceMethod )
protected virtual MethodInfo FindManagedOverrideTarget ( MethodInfo interfaceMethod )
{
{
if ( interfaceMethod = = null )
return null ;
// FIXME: Does/should this look in superclasses?
// FIXME: Does/should this look in superclasses?
MemberInfo [ ] possibleMembers = wrapper_type . FindMembers ( MemberTypes . Method , BindingFlags . Public | BindingFlags . NonPublic |
MemberInfo [ ] possibleMembers = wrapper_type . FindMembers ( MemberTypes . Method , BindingFlags . Public | BindingFlags . NonPublic |
BindingFlags . Instance | BindingFlags . Static , vtable_override_filter , interfaceMethod ) ;
BindingFlags . Instance | BindingFlags . Static , vtable_override_filter , interfaceMethod ) ;
@ -444,31 +401,30 @@ namespace Mono.VisualC.Interop.ABI {
MethodBuilder methodBuilder = impl_type . DefineMethod ( interfaceMethod . Name , MethodAttributes . Public | MethodAttributes . Virtual ,
MethodBuilder methodBuilder = impl_type . DefineMethod ( interfaceMethod . Name , MethodAttributes . Public | MethodAttributes . Virtual ,
interfaceMethod . ReturnType , parameterTypes ) ;
interfaceMethod . ReturnType , parameterTypes ) ;
ReflectionHelper . ApplyMethodParameterAttributes ( interfaceMethod , methodBuilder , false ) ;
ReflectionHelper . ApplyMethodParameterAttributes ( interfaceMethod , methodBuilder , false ) ;
return methodBuilder ;
return methodBuilder ;
}
}
/ * *
/ * *
* Defines a new MethodBuilder that calls the specified C + + ( non - virtual ) method using its mangled name
* Defines a new MethodBuilder that calls the specified C + + ( non - virtual ) method using its mangled name
* /
* /
protected virtual MethodBuilder GetPInvokeForMethod ( MethodInfo signature , PInvokeSignature p sig)
protected virtual MethodBuilder GetPInvokeForMethod ( PInvokeSignature sig )
{
{
string entryPoint = GetMangledMethodName ( signature ) ;
string entryPoint = sig . Name ;
if ( entryPoint = = null )
if ( entryPoint = = null )
throw new NotSupportedException ( "Could not mangle method name." ) ;
throw new NotSupportedException ( "Could not mangle method name." ) ;
string lib ;
string lib ;
if ( IsInline ( signature ) )
if ( IsInline ( sig . OrigMethod ) & & library . InlineMethodPolicy = = InlineMethods . SurrogateLib )
lib = library + "-inline" ;
lib = library . Name + "-inline" ;
else
else
lib = library ;
lib = library . Name ;
MethodBuilder builder = impl_type . DefinePInvokeMethod ( "__$" + signature . Name + "_Impl" , lib , entryPoint ,
MethodBuilder builder = impl_type . DefinePInvokeMethod ( entryPoint , lib , entryPoint ,
MethodAttributes . Private | MethodAttributes . Static | MethodAttributes . PinvokeImpl ,
MethodAttributes . Private | MethodAttributes . Static | MethodAttributes . PinvokeImpl ,
CallingConventions . Standard , p sig. ReturnType , p sig. ParameterTypes . ToArray ( ) ,
CallingConventions . Standard , sig . ReturnType , sig . ParameterTypes . ToArray ( ) ,
GetCallingConvention ( signature ) . Value , CharSet . Ansi ) ;
sig . CallingConvention . Value , CharSet . Ansi ) ;
builder . SetImplementationFlags ( builder . GetMethodImplementationFlags ( ) | MethodImplAttributes . PreserveSig ) ;
builder . SetImplementationFlags ( builder . GetMethodImplementationFlags ( ) | MethodImplAttributes . PreserveSig ) ;
ReflectionHelper . ApplyMethodParameterAttributes ( signature , builder , true ) ;
ReflectionHelper . ApplyMethodParameterAttributes ( sig . OrigMethod , builder , true ) ;
return builder ;
return builder ;
}
}
@ -502,38 +458,41 @@ namespace Mono.VisualC.Interop.ABI {
il . Emit ( OpCodes . Ldfld , typeinfo_field ) ;
il . Emit ( OpCodes . Ldfld , typeinfo_field ) ;
il . Emit ( OpCodes . Callvirt , typeinfo_nativesize ) ;
il . Emit ( OpCodes . Callvirt , typeinfo_nativesize ) ;
if ( wrapper_type ! = null ) {
if ( wrapper_type ! = null & & interfaceMethod . GetParameters ( ) . Any ( ) ) {
// load managed wrapper
// load managed wrapper
il . Emit ( OpCodes . Ldarg_1 ) ;
il . Emit ( OpCodes . Ldarg_1 ) ;
il . Emit ( OpCodes . Newobj , typeof ( CppInstancePtr ) . GetConstructor ( BindingFlags . Instance | BindingFlags . NonPublic , null , new Type [ ] { typeof ( int ) , typeof ( object ) } , null ) ) ;
il . Emit ( OpCodes . Newobj , cppip_fromsize_managed ) ;
} else
} else
il . Emit ( OpCodes . Newobj , typeof ( CppInstancePtr ) . GetConstructor ( BindingFlags . Instance | BindingFlags . NonPublic , null , new Type [ ] { typeof ( int ) } , null ) ) ;
il . Emit ( OpCodes . Newobj , cppip_fromsize ) ;
}
}
protected virtual void EmitConstruct ( ILGenerator il , MethodInfo nativeMethod ,
protected virtual void EmitConstruct ( ILGenerator il , MethodInfo nativeMethod , PInvokeSignature psig , LocalBuilder nativePtr )
PInvokeSignature psig ,
LocalBuilder nativePtr )
{
{
EmitNativeCall ( il , nativeMethod , false , psig , nativePtr , null ) ;
Debug . Assert ( psig . Type = = MethodType . NativeCtor ) ;
// FIXME: Why is this needed ? The c++ ctor initializes it
EmitNativeCall ( il , nativeMethod , psig , nativePtr ) ;
//EmitInitVTable (il, nativePtr);
EmitInitVTable ( il , nativePtr ) ;
}
}
protected virtual void EmitDestruct ( ILGenerator il , MethodInfo nativeMethod ,
protected virtual void EmitDestruct ( ILGenerator il , MethodInfo nativeMethod , PInvokeSignature psig ,
PInvokeSignature psig ,
LocalBuilder cppInstancePtr , LocalBuilder nativePtr )
LocalBuilder cppInstancePtr , LocalBuilder nativePtr )
{
{
Debug . Assert ( psig . Type = = MethodType . NativeDtor ) ;
// we don't do anything if the object wasn't managed alloc
if ( cppInstancePtr = = null )
return ;
// bail if we weren't alloc'd by managed code
// bail if we weren't alloc'd by managed code
Label bail = il . DefineLabel ( ) ;
Label bail = il . DefineLabel ( ) ;
il . Emit ( OpCodes . Ldloca_S , cppInstancePtr ) ;
il . Emit ( OpCodes . Ldloca_S , cppInstancePtr ) ;
il . Emit ( OpCodes . Brfalse_S , bail ) ; // <- FIXME? (would this ever branch?)
il . Emit ( OpCodes . Brfalse_S , bail ) ; // <- FIXME? (would this ever branch?)
il . Emit ( OpCodes . Ldloca_S , cppInstancePtr ) ;
il . Emit ( OpCodes . Ldloca_S , cppInstancePtr ) ;
il . Emit ( OpCodes . Call , typeof ( CppInstancePtr ) . GetProperty ( "IsManagedAlloc" ) . GetGetMethod ( ) ) ;
il . Emit ( OpCodes . Call , cppip_managedalloc ) ;
il . Emit ( OpCodes . Brfalse_S , bail ) ;
il . Emit ( OpCodes . Brfalse_S , bail ) ;
EmitResetVTable ( il , nativePtr ) ;
EmitResetVTable ( il , nativePtr ) ;
EmitNativeCall ( il , nativeMethod , false , psig , nativePtr , null ) ;
EmitNativeCall ( il , nativeMethod , psig , nativePtr ) ;
il . MarkLabel ( bail ) ;
il . MarkLabel ( bail ) ;
}
}
@ -543,142 +502,206 @@ namespace Mono.VisualC.Interop.ABI {
* GetPInvokeForMethod or the MethodInfo of a vtable method .
* GetPInvokeForMethod or the MethodInfo of a vtable method .
* To complete method , emit OpCodes . Ret .
* To complete method , emit OpCodes . Ret .
* /
* /
protected virtual void EmitNativeCall ( ILGenerator il , MethodInfo nativeMethod ,
protected virtual void EmitNativeCall ( ILGenerator il , MethodInfo nativeMethod , PInvokeSignature psig , LocalBuilder nativePtr )
bool isStatic , PInvokeSignature psig ,
LocalBuilder nativePtr , LocalBuilder retArg )
{
{
//
var interfaceMethod = psig . OrigMethod ;
// The managed signature looks like this:
var interfaceArgs = interfaceMethod . GetParameters ( ) ;
// (<.net this>, @this (if not static), <additional args>
// The native signature looks like this:
// (<hidden retarg if present>, @this, <additional args>)
//
// The managed argument index, skip .net this
int aindex = 1 ;
// Do conversions
LocalBuilder [ ] args = new LocalBuilder [ psig . ParameterTypes . Count ] ;
aindex = 1 ;
for ( int pindex = 0 ; pindex < psig . ParameterTypes . Count ; pindex + + ) {
if ( ! isStatic & & pindex = = 0 ) {
// For instance methods, strip off CppInstancePtr and pass the corresponding IntPtr
args [ pindex ] = nativePtr ;
aindex + + ;
continue ;
}
Type ptype = psig . ParameterTypes [ pindex ] ;
int argLoadStart = 1 ; // For static methods, just strip off arg0 (.net this pointer)
switch ( psig . ParameterMarshallers [ pindex ] ) {
if ( ! IsStatic ( interfaceMethod ) )
case ParameterMarshal . Default :
{
// FIXME: Why is this needed ?
argLoadStart = 2 ; // For instance methods, strip off CppInstancePtr and pass the corresponding IntPtr
// auto marshal bool to C++ bool type (0 = false , 1 = true )
il . Emit ( OpCodes . Ldloc_S , nativePtr ) ;
if ( ptype . Equals ( typeof ( bool ) ) ) {
}
args [ pindex ] = il . DeclareLocal ( typeof ( bool ) ) ;
Label isTrue = il . DefineLabel ( ) ;
// load and marshal arguments
Label done = il . DefineLabel ( ) ;
for ( int i = argLoadStart ; i < = interfaceArgs . Length ; i + + ) {
il . Emit ( OpCodes . Ldarg , aindex ) ;
il . Emit ( OpCodes . Ldarg , i ) ;
il . Emit ( OpCodes . Brtrue_S , isTrue ) ;
EmitOutboundMarshal ( il , interfaceArgs [ i - 1 ] . ParameterType , psig . ParameterTypes [ i - 1 ] ) ;
il . Emit ( OpCodes . Ldc_I4_0 ) ;
}
il . Emit ( OpCodes . Br_S , done ) ;
il . MarkLabel ( isTrue ) ;
il . Emit ( OpCodes . Call , nativeMethod ) ;
il . Emit ( OpCodes . Ldc_I4_1 ) ;
il . MarkLabel ( done ) ;
// Marshal return value
il . Emit ( OpCodes . Stloc , args [ pindex ] ) ;
EmitInboundMarshal ( il , psig . ReturnType , interfaceMethod . ReturnType ) ;
//il.Emit (OpCodes.Conv_I1);
}
}
break ;
case ParameterMarshal . ClassByRef : {
public virtual PInvokeSignature GetPInvokeSignature ( MethodInfo method )
args [ pindex ] = il . DeclareLocal ( typeof ( IntPtr ) ) ;
{
// Pass the native pointer of the class
var parameters = method . GetParameters ( ) ;
// Null check
var pinvokeTypes = new List < Type > ( parameters . Length ) ;
Label isNull = il . DefineLabel ( ) ;
Label contLabel = il . DefineLabel ( ) ;
foreach ( var pi in parameters ) {
il . Emit ( OpCodes . Ldarg , aindex ) ;
pinvokeTypes . Add ( ToPInvokeType ( pi . ParameterType , pi ) ) ;
il . Emit ( OpCodes . Brfalse_S , isNull ) ;
}
// Non-null case
il . Emit ( OpCodes . Ldarg , aindex ) ;
return new PInvokeSignature {
EmitLoadNativePtr ( il ) ;
OrigMethod = method ,
// FIXME: Dispose check
Name = GetMangledMethodName ( method ) ,
il . Emit ( OpCodes . Br_S , contLabel ) ;
Type = GetMethodType ( method ) ,
// Null case
CallingConvention = GetCallingConvention ( method ) ,
il . MarkLabel ( isNull ) ;
ParameterTypes = pinvokeTypes ,
il . Emit ( OpCodes . Ldnull ) ;
ReturnType = ToPInvokeType ( method . ReturnType , method . ReturnTypeCustomAttributes )
il . Emit ( OpCodes . Conv_I ) ;
} ;
// Common case
}
il . MarkLabel ( contLabel ) ;
il . Emit ( OpCodes . Stloc , args [ pindex ] ) ;
public virtual Type ToPInvokeType ( Type t , ICustomAttributeProvider icap )
break ;
{
if ( t = = typeof ( bool ) ) {
return typeof ( byte ) ;
} else if ( typeof ( ICppObject ) . IsAssignableFrom ( t ) ) {
if ( IsByVal ( icap ) ) {
// Can't use an interface/cattr since the native layout type is private
// Pass it as a type argument to I<Foo> ?
var f = t . GetField ( "native_layout" , BindingFlags . Static | BindingFlags . NonPublic ) ;
if ( f = = null | | f . FieldType ! = typeof ( Type ) )
throw new NotImplementedException ( "Type '" + t + "' needs to have a 'native_layout' field before it can be passed by value." ) ;
t = ( Type ) f . GetValue ( null ) ;
if ( ! t . IsValueType )
throw new NotSupportedException ( "The value for 'native_layout' for type '" + t + "' must be a value type." ) ;
return t ;
} else { // by ref
return typeof ( IntPtr ) ;
}
}
case ParameterMarshal . ClassByVal : {
}
// Pass a copy of the native memory of the class
args [ pindex ] = il . DeclareLocal ( ptype ) ;
return t ;
Label normalLabel = il . DefineLabel ( ) ;
}
il . Emit ( OpCodes . Ldarg , aindex ) ;
il . Emit ( OpCodes . Brtrue_S , normalLabel ) ;
// The above should return parameter/return types that
// correspond with the marshalling implemented in the next 2 methods.
// This method marshals from managed -> C++
// The value it is marshaling will be on the stack.
protected virtual void EmitOutboundMarshal ( ILGenerator il , Type managedType , Type targetType )
{
var nextArg = il . DefineLabel ( ) ;
// FIXME: Why is this needed ?
// auto marshal bool to C++ bool type (0 = false , 1 = true )
if ( managedType . Equals ( typeof ( bool ) ) ) {
Label isTrue = il . DefineLabel ( ) ;
Label done = il . DefineLabel ( ) ;
il . Emit ( OpCodes . Brtrue , isTrue ) ;
il . Emit ( OpCodes . Ldc_I4_0 ) ;
il . Emit ( OpCodes . Br , done ) ;
il . MarkLabel ( isTrue ) ;
il . Emit ( OpCodes . Ldc_I4_1 ) ;
il . MarkLabel ( done ) ;
//il.Emit (OpCodes.Conv_I1);
}
// auto marshal ICppObject
if ( typeof ( ICppObject ) . IsAssignableFrom ( managedType ) ) {
var nullCase = il . DefineLabel ( ) ;
var param = il . DeclareLocal ( typeof ( CppInstancePtr ) ) ;
// check for null
il . Emit ( OpCodes . Dup ) ;
il . Emit ( OpCodes . Brfalse_S , nullCase ) ;
// FIXME: ICppObject could be implemented by a value type
il . Emit ( OpCodes . Callvirt , cppobj_native ) ;
il . Emit ( OpCodes . Stloc , param ) ;
if ( targetType = = typeof ( IntPtr ) ) { // by ref
il . Emit ( OpCodes . Ldloca , param ) ;
il . Emit ( OpCodes . Call , cppip_native ) ;
il . MarkLabel ( nullCase ) ;
} else { // by val
var val = il . DeclareLocal ( targetType ) ;
il . Emit ( OpCodes . Ldloca , val ) ; // dest
il . Emit ( OpCodes . Ldloca , param ) ;
il . Emit ( OpCodes . Call , cppip_native ) ; // src
il . Emit ( OpCodes . Cpobj , targetType ) ;
il . Emit ( OpCodes . Ldloc , val ) ;
il . Emit ( OpCodes . Br_S , nextArg ) ;
il . MarkLabel ( nullCase ) ;
// Null case
// Null case
il . Emit ( OpCodes . Ldstr , "Cannot pass null object of type '" + ptype . DeclaringType + "' to c++ by value" ) ;
il . Emit ( OpCodes . Ldstr , "Cannot pass null object of type '" + managed Type + "' to c++ by value" ) ;
il . Emit ( OpCodes . Newobj , typeof ( ArgumentException ) . GetConstructor ( new Type [ ] { typeof ( string ) } ) ) ;
il . Emit ( OpCodes . Newobj , typeof ( ArgumentException ) . GetConstructor ( new Type [ ] { typeof ( string ) } ) ) ;
il . Emit ( OpCodes . Throw ) ;
il . Emit ( OpCodes . Throw ) ;
// Non-null case
il . MarkLabel ( normalLabel ) ;
// Dest
il . Emit ( OpCodes . Ldloca , args [ pindex ] ) ;
// Load the native ptr of the object (src)
il . Emit ( OpCodes . Ldarg , aindex ) ;
EmitLoadNativePtr ( il ) ;
// FIXME: Dispose check
// Copy
il . Emit ( OpCodes . Cpobj , ptype ) ;
break ;
}
}
default :
throw new NotImplementedException ( ) ;
}
aindex + + ;
}
}
// Pass arguments
il . MarkLabel ( nextArg ) ;
}
aindex = 1 ;
int pindexStart = 0 ;
if ( retArg ! = null ) {
// This method marshals from C++ -> managed
pindexStart + + ;
// The value it is marshaling will be on the stack.
il . Emit ( OpCodes . Ldloc , retArg ) ;
protected virtual void EmitInboundMarshal ( ILGenerator il , Type nativeType , Type targetType )
}
{
var next = il . DefineLabel ( ) ;
for ( int pindex = pindexStart ; pindex < psig . ParameterTypes . Count ; pindex + + ) {
// marshal IntPtr -> ICppObject
// The first argument is the .net this argument
if ( nativeType = = typeof ( IntPtr ) & & typeof ( ICppObject ) . IsAssignableFrom ( targetType ) ) {
if ( args [ pindex ] ! = null )
il . Emit ( OpCodes . Ldloc , args [ pindex ] ) ;
else
il . Emit ( OpCodes . Ldarg , aindex ) ;
aindex + + ;
var isNull = il . DefineLabel ( ) ;
}
// Make the call
// first, we check for null
il . Emit ( OpCodes . Dup ) ;
il . Emit ( OpCodes . Brfalse_S , isNull ) ;
il . Emit ( OpCodes . Call , nativeMethod ) ;
il . Emit ( OpCodes . Newobj , cppip_fromnative ) ;
}
EmitCreateCppObjectFromNative ( il , targetType ) ;
public virtual PInvokeSignature GetPInvokeSignature ( CppTypeInfo typeInfo , MethodInfo method ) {
il . MarkLabel ( isNull ) ;
var originalTypes = ReflectionHelper . GetMethodParameterTypes ( method ) ;
il . Emit ( OpCodes . Pop ) ;
il . Emit ( OpCodes . Ldnull ) ;
} else if ( nativeType . IsValueType & & typeof ( ICppObject ) . IsAssignableFrom ( targetType ) ) {
// marshal value type -> ICppObject
// Obviously, we lose all managed overrides if we pass by value,
// but this "slicing" happens in vanilla C++ as well
var ptr = il . DeclareLocal ( typeof ( CppInstancePtr ) ) ;
il . Emit ( OpCodes . Box , nativeType ) ; // structure
il . Emit ( OpCodes . Sizeof , nativeType ) ;
il . Emit ( OpCodes . Newobj , cppip_fromsize ) ;
il . Emit ( OpCodes . Stloc , ptr ) ;
il . Emit ( OpCodes . Ldloca , ptr ) ;
il . Emit ( OpCodes . Call , cppip_native ) ; // ptr
il . Emit ( OpCodes . Ldc_I4_0 ) ; // fDeleteOld
var pinvokeTypes = originalTypes . Transform (
il . Emit ( OpCodes . Call , marshal_structuretoptr ) ;
For . AnyInputIn ( typeof ( bool ) ) . Emit ( typeof ( byte ) ) ,
// CppInstancePtr implements ICppObject
il . Emit ( OpCodes . Ldloc , ptr ) ;
For . InputsWhere ( ( Type t ) = > typeof ( ICppObject ) . IsAssignableFrom ( t ) ) . Emit ( typeof ( IntPtr ) ) ,
EmitCreateCppObjectFromNative ( il , targetType ) ;
}
il . MarkLabel ( next ) ;
}
For . UnmatchedInput < Type > ( ) . Emit ( t = > t )
// Expects CppInstancePtr on stack. No null check performed
) ;
protected virtual void EmitCreateCppObjectFromNative ( ILGenerator il , Type targetType )
{
if ( targetType = = typeof ( ICppObject ) )
targetType = typeof ( CppInstancePtr ) ;
Type returnType = method . ReturnType ;
// check for a native constructor (i.e. a public ctor in the wrapper that takes CppInstancePtr)
var ctor = targetType . GetConstructor ( BindingFlags . ExactBinding | BindingFlags . Public | BindingFlags . Instance , null , new Type [ ] { typeof ( CppInstancePtr ) } , null ) ;
if ( ctor = = null )
throw new InvalidProgramException ( string . Format ( "Type `{0}' implements ICppObject but does not contain a public constructor that takes CppInstancePtr" , targetType ) ) ;
return new PInvokeSignature { OrigMethod = method , ParameterTypes = pinvokeTypes . ToList ( ) , ReturnType = returnType } ;
il . Emit ( OpCodes . Newobj , ctor ) ;
}
}
/ * *
/ * *
@ -756,7 +779,7 @@ namespace Mono.VisualC.Interop.ABI {
}
}
protected virtual void EmitLoadInstancePtr ( ILGenerator il , Type firstParamType , out LocalBuilder cppip ,
protected virtual void EmitLoadInstancePtr ( ILGenerator il , Type firstParamType , out LocalBuilder cppip ,
out LocalBuilder native )
out LocalBuilder native )
{
{
cppip = null ;
cppip = null ;
native = null ;
native = null ;
@ -767,7 +790,7 @@ namespace Mono.VisualC.Interop.ABI {
native = il . DeclareLocal ( typeof ( IntPtr ) ) ;
native = il . DeclareLocal ( typeof ( IntPtr ) ) ;
il . Emit ( OpCodes . Stloc_S , cppip ) ;
il . Emit ( OpCodes . Stloc_S , cppip ) ;
il . Emit ( OpCodes . Ldloca_S , cppip ) ;
il . Emit ( OpCodes . Ldloca_S , cppip ) ;
il . Emit ( OpCodes . Call , typeof ( CppInstancePtr ) . GetProperty ( "Native" ) . GetGetMethod ( ) ) ;
il . Emit ( OpCodes . Call , cppip_native ) ;
il . Emit ( OpCodes . Stloc_S , native ) ;
il . Emit ( OpCodes . Stloc_S , native ) ;
} else if ( firstParamType . Equals ( typeof ( IntPtr ) ) ) {
} else if ( firstParamType . Equals ( typeof ( IntPtr ) ) ) {
native = il . DeclareLocal ( typeof ( IntPtr ) ) ;
native = il . DeclareLocal ( typeof ( IntPtr ) ) ;
@ -779,17 +802,6 @@ namespace Mono.VisualC.Interop.ABI {
throw new ArgumentException ( "First argument to non-static C++ method must be byref, IntPtr or CppInstancePtr." ) ;
throw new ArgumentException ( "First argument to non-static C++ method must be byref, IntPtr or CppInstancePtr." ) ;
}
}
// Emit obj.Native.Native
// On enter, the stack should contain a reference to a CppInstancePtr
// On exit, it contains an IntPtr
void EmitLoadNativePtr ( ILGenerator il ) {
LocalBuilder cppip = il . DeclareLocal ( typeof ( CppInstancePtr ) ) ;
il . Emit ( OpCodes . Call , typeof ( ICppObject ) . GetMethod ( "get_Native" ) ) ;
il . Emit ( OpCodes . Stloc_S , cppip ) ;
il . Emit ( OpCodes . Ldloca_S , cppip ) ;
il . Emit ( OpCodes . Call , typeof ( CppInstancePtr ) . GetProperty ( "Native" ) . GetGetMethod ( ) ) ;
}
protected virtual void EmitCheckManagedAlloc ( ILGenerator il , LocalBuilder cppip )
protected virtual void EmitCheckManagedAlloc ( ILGenerator il , LocalBuilder cppip )
{
{
// make sure we were allocated by managed code
// make sure we were allocated by managed code
@ -822,4 +834,5 @@ namespace Mono.VisualC.Interop.ABI {
}
}
}
}
}
}
}
}