// // Mono.VisualC.Interop.ABI.CppAbi.cs: Represents an abstract C++ ABI // // Author: // Alexander Corrado (alexander.corrado@gmail.com) // // Copyright (C) 2010 Alexander Corrado // using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; using System.Runtime.InteropServices; namespace Mono.VisualC.Interop.ABI { //FIXME: Exception handling, operator overloading etc. //FIXME: Allow interface to override default calling convention //FIXME: Better interface validation- for example, throw exception // when [VirtualDestructor] is applied to base interface but not derived public abstract partial class CppAbi { protected ModuleBuilder impl_module; protected TypeBuilder impl_type; protected Type interface_type, layout_type, wrapper_type; protected string library, class_name; protected VTable vtable; protected FieldBuilder vtable_field, native_size_field; protected ILGenerator ctor_il; // Default settings that subclasses can override: protected MakeVTableDelegate make_vtable_method = VTable.DefaultImplementation; protected MemberFilter vtable_override_filter = VTable.BindToSignatureAndAttribute; // These methods might be more commonly overridden for a given C++ ABI: public virtual MethodType GetMethodType (MethodInfo imethod) { if (imethod.Name.Equals (class_name)) return MethodType.NativeCtor; else if (imethod.Name.Equals ("Alloc")) return MethodType.ManagedAlloc; else if (imethod.Name.Equals ("Destruct")) return MethodType.NativeDtor; return MethodType.Native; } public virtual int FieldOffsetPadding { get { return Marshal.SizeOf (typeof (IntPtr)); } } protected virtual int NativeSize { get { // By default: native size = C++ class size + field offset padding (usually just vtable pointer) // FIXME: Only include vtable ptr if there are virtual functions? Here I guess it doesn't really matter, // we're just allocing extra memory. return Marshal.SizeOf (layout_type) + FieldOffsetPadding; } } // The members below must be implemented for a given C++ ABI: public abstract string GetMangledMethodName (MethodInfo methodInfo); public abstract CallingConvention? GetCallingConvention (MethodInfo methodInfo); private struct EmptyNativeLayout { } // The ImplementClass overrides are the main entry point to the Abi API: public Iface ImplementClass (Type wrapperType, string lib, string className) { return this.ImplementClass (wrapperType, lib, className); } public virtual Iface ImplementClass (Type wrapperType, string lib, string className) where NLayout : struct //where Iface : ICppClassInstantiatable or ICppClassOverridable { this.impl_module = CppLibrary.interopModule; this.library = lib; this.class_name = className; this.interface_type = typeof (Iface); this.layout_type = typeof (NLayout); this.wrapper_type = wrapperType; DefineImplType (); var properties = GetProperties (); var methods = GetMethods (interface_type); var bases = GetBasesRecursive (interface_type); IEnumerable baseVirtualMethods = Enumerable.Empty (); if (bases.Any ()) // FIXME: We're assuming that first declared base is non-virtual primary base (and thus shares primary vtable) baseVirtualMethods = from method in GetVirtualMethods (bases.First ()) select method; var vtableSlots = from method in baseVirtualMethods.Concat (GetVirtualMethods (interface_type)) let delegateType = DefineVTableDelegate (method) select new { DelegateType = delegateType, Override = GetManagedOverrideTrampoline (method, delegateType, vtable_override_filter) }; var virtualDtorSlots = from iface in bases.With (interface_type) where iface.IsDefined (typeof (VirtualDestructorAttribute), false) from i in new int [] { 0, 1 } select new { DelegateType = (Type)null, Override = (Delegate)null }; vtableSlots = vtableSlots.Concat (virtualDtorSlots); // ONLY make vtable if there are virtual methods if (vtableSlots.Any ()) vtable = make_vtable_method (vtableSlots.Select (s => s.DelegateType).ToList (), vtableSlots.Select (s => s.Override).ToArray ()); else vtable = null; int vtableIndex = baseVirtualMethods.Count (); // Implement all methods foreach (var method in methods) { DefineMethod (method, vtableIndex); if (IsVirtual (method)) vtableIndex++; } // Implement all properties foreach (var property in properties) DefineProperty (property); ctor_il.Emit (OpCodes.Ret); return (Iface)Activator.CreateInstance (impl_type.CreateType (), vtable, NativeSize); } protected virtual IEnumerable GetProperties () { return ( // get all properties defined on the interface from property in interface_type.GetProperties () select property ).Union ( // ... as well as those defined on inherited interfaces from iface in interface_type.GetInterfaces () from property in iface.GetProperties () select property ); } protected virtual IEnumerable GetMethods (Type interfaceType) { // get all methods defined on inherited interfaces first var methods = ( from iface in interfaceType.GetInterfaces () from method in iface.GetMethods () where !method.IsSpecialName select method ).Concat ( from method in interfaceType.GetMethods () where !method.IsSpecialName orderby method.MetadataToken select method ); return methods; } protected virtual IEnumerable GetVirtualMethods (Type interfaceType) { var delegates = ( from method in GetMethods (interfaceType) where IsVirtual (method) select method ); return delegates; } protected virtual IEnumerable GetBasesRecursive (Type searchStart) { var immediateBases = ( from baseIface in searchStart.GetInterfaces () where baseIface.Name.Equals ("Base`1") from iface in baseIface.GetGenericArguments () select iface ); if (!immediateBases.Any ()) return Enumerable.Empty (); List allBases = new List (); foreach (var baseInterface in immediateBases) { allBases.AddRange (GetBasesRecursive (baseInterface)); allBases.Add (baseInterface); } return allBases; } protected virtual void DefineImplType () { string implTypeName = interface_type.Name + "_" + layout_type.Name + "_" + this.GetType ().Name + "_Impl"; impl_type = impl_module.DefineType (implTypeName, TypeAttributes.Class | TypeAttributes.Sealed); 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); ConstructorBuilder ctor = impl_type.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof (VTable), typeof (int) }); ctor_il = ctor.GetILGenerator (); // 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, int index) { // 0. Introspect method MethodType methodType = GetMethodType (interfaceMethod); Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod); // 1. Generate managed trampoline to call native method MethodBuilder trampoline = GetMethodBuilder (interfaceMethod); ILGenerator il = trampoline.GetILGenerator (); if (methodType == MethodType.NoOp) { // return NULL if method is supposed to return a value // FIXME: 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, interfaceMethod); il.Emit (OpCodes.Ret); return trampoline; } bool isStatic = true; LocalBuilder cppInstancePtr = null; LocalBuilder nativePtr = null; // If we're not a static method, do the following ... if (!IsStatic (interfaceMethod)) { isStatic = false; if (parameterTypes.Length < 1) throw new ArgumentException ("First argument to non-static C++ method must be IntPtr or CppInstancePtr."); // 2. Load the native C++ instance pointer EmitLoadInstancePtr (il, parameterTypes[0], out cppInstancePtr, out nativePtr); // 3. Make sure our native pointer is a valid reference. If not, throw ObjectDisposedException EmitCheckDisposed (il, nativePtr, methodType); } MethodInfo nativeMethod; bool isVirtual; if (IsVirtual (interfaceMethod)) { isVirtual = true; nativeMethod = vtable.PrepareVirtualCall (interfaceMethod, GetCallingConvention (interfaceMethod), il, nativePtr, vtable_field, index); } else { isVirtual = false; nativeMethod = GetPInvokeForMethod (interfaceMethod); } switch (methodType) { case MethodType.NativeCtor: EmitConstruct (il, nativeMethod, parameterTypes, nativePtr); break; case MethodType.NativeDtor: EmitDestruct (il, nativeMethod, isVirtual, parameterTypes, cppInstancePtr, nativePtr); break; default: // regular native method EmitCallNative (il, nativeMethod, isStatic, parameterTypes, nativePtr); break; } il.Emit (OpCodes.Ret); return trampoline; } protected virtual PropertyBuilder DefineProperty (PropertyInfo property) { if (property.CanWrite) throw new InvalidProgramException ("Properties in C++ interface must be read-only."); MethodInfo imethod = property.GetGetMethod (); string methodName = imethod.Name; string propName = property.Name; Type retType = imethod.ReturnType; FieldBuilder fieldData; // C++ interface properties are either to return the VTable, get NativeSize, or to access C++ fields if (retType.IsGenericType && retType.GetGenericTypeDefinition ().Equals (typeof (CppField<>))) { // define a new field for the property fieldData = impl_type.DefineField ("__" + propName + "_Data", retType, FieldAttributes.InitOnly | FieldAttributes.Private); // init our field data with a new instance of CppField // first, get field offset ctor_il.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 (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); } else if (retType.Equals (typeof (VTable))) fieldData = vtable_field; else if (retType.Equals (typeof (int))) fieldData = native_size_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); MethodAttributes methodAttr = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.SpecialName | MethodAttributes.HideBySig; MethodBuilder fieldGetter = impl_type.DefineMethod (methodName, methodAttr, retType, Type.EmptyTypes); ILGenerator il = fieldGetter.GetILGenerator (); il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldfld, fieldData); il.Emit (OpCodes.Ret); fieldProp.SetGetMethod (fieldGetter); return fieldProp; } /** * Implements the managed trampoline that will be invoked from the vtable by native C++ code when overriding * the specified C++ virtual method with the specified managed one. */ protected virtual Delegate GetManagedOverrideTrampoline (MethodInfo interfaceMethod, Type delegateType, MemberFilter binder) { if (wrapper_type == null) return null; MethodInfo targetMethod = FindManagedOverrideTarget (interfaceMethod, binder); if (targetMethod == null) return null; Type[] parameterTypes = GetParameterTypesForPInvoke (interfaceMethod).ToArray (); // 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. // This does not appear to hold true, so we also disable JIT visibility checks. DynamicMethod trampolineIn = new DynamicMethod (wrapper_type.Name + "_" + interfaceMethod.Name + "_FromNative", interfaceMethod.ReturnType, parameterTypes, typeof (CppInstancePtr).Module, true); Util.ApplyMethodParameterAttributes (interfaceMethod, trampolineIn, true); ILGenerator il = trampolineIn.GetILGenerator (); // for static methods: OpCode callInstruction = OpCodes.Call; int argLoadStart = 1; // for instance methods, we need an instance to call them on! if (!targetMethod.IsStatic) { callInstruction = OpCodes.Callvirt; //argLoadStart = 1; il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldc_I4, NativeSize); MethodInfo getManagedObj = typeof (CppInstancePtr).GetMethod ("GetManaged", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod (wrapper_type); il.Emit (OpCodes.Call, getManagedObj); } for (int i = argLoadStart; i < parameterTypes.Length; i++) { il.Emit (OpCodes.Ldarg, i); } il.Emit (OpCodes.Tailcall); il.Emit (callInstruction, targetMethod); il.Emit (OpCodes.Ret); return trampolineIn.CreateDelegate (delegateType); } protected virtual MethodInfo FindManagedOverrideTarget (MethodInfo interfaceMethod, MemberFilter filter) { MemberInfo[] possibleMembers = wrapper_type.FindMembers (MemberTypes.Method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, filter, interfaceMethod); if (possibleMembers.Length > 1) throw new InvalidProgramException ("More than one possible override found when binding virtual method: " + interfaceMethod.Name); else if (possibleMembers.Length == 0) return null; return (MethodInfo)possibleMembers [0]; } /** * Defines a new MethodBuilder with the same signature as the passed MethodInfo */ protected virtual MethodBuilder GetMethodBuilder (MethodInfo interfaceMethod) { Type[] parameterTypes = Util.GetMethodParameterTypes (interfaceMethod); MethodBuilder methodBuilder = impl_type.DefineMethod (interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, interfaceMethod.ReturnType, parameterTypes); Util.ApplyMethodParameterAttributes (interfaceMethod, methodBuilder, false); return methodBuilder; } /** * Defines a new MethodBuilder that calls the specified C++ (non-virtual) method using its mangled name */ protected virtual MethodBuilder GetPInvokeForMethod (MethodInfo signature) { string entryPoint = GetMangledMethodName (signature); if (entryPoint == null) throw new NotSupportedException ("Could not mangle method name."); Type[] parameterTypes = GetParameterTypesForPInvoke (signature).ToArray (); MethodBuilder builder = impl_type.DefinePInvokeMethod ("__$" + signature.Name + "_Impl", library, entryPoint, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.PinvokeImpl, CallingConventions.Standard, signature.ReturnType, parameterTypes, GetCallingConvention (signature).Value, CharSet.Ansi); builder.SetImplementationFlags (builder.GetMethodImplementationFlags () | MethodImplAttributes.PreserveSig); Util.ApplyMethodParameterAttributes (signature, builder, true); return builder; } protected virtual Type DefineVTableDelegate (MethodInfo targetMethod) { // FIXME: Actually return the same delegate type instead of creating a new one if // a suitable type already exists?? CallingConvention? callingConvention = GetCallingConvention (targetMethod); string delTypeName = "_" + targetMethod.DeclaringType.Name + "_" + targetMethod.Name + "_VTdel"; while (CppLibrary.interopModule.GetType (delTypeName) != null) delTypeName += "_"; TypeAttributes typeAttr = TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass; TypeBuilder del = CppLibrary.interopModule.DefineType (delTypeName, typeAttr, typeof(MulticastDelegate)); if (callingConvention.HasValue) { ConstructorInfo ufpa = typeof (UnmanagedFunctionPointerAttribute).GetConstructor (new Type [] { typeof (CallingConvention) }); CustomAttributeBuilder unmanagedPointer = new CustomAttributeBuilder (ufpa, new object [] { callingConvention.Value }); del.SetCustomAttribute (unmanagedPointer); } MethodAttributes ctorAttr = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public; ConstructorBuilder ctor = del.DefineConstructor (ctorAttr, CallingConventions.Standard, new Type[] { typeof(object), typeof(System.IntPtr) }); ctor.SetImplementationFlags (MethodImplAttributes.Runtime | MethodImplAttributes.Managed); Type[] parameterTypes = GetParameterTypesForPInvoke (targetMethod).ToArray (); MethodAttributes methodAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; MethodBuilder invokeMethod = del.DefineMethod ("Invoke", methodAttr, targetMethod.ReturnType, parameterTypes); invokeMethod.SetImplementationFlags (MethodImplAttributes.Runtime | MethodImplAttributes.Managed); return del.CreateType (); } /** * Emits IL to allocate the memory for a new instance of the C++ class. * To complete method, emit OpCodes.Ret. */ protected virtual void EmitManagedAlloc (ILGenerator il, MethodInfo interfaceMethod) { Type paramType = interfaceMethod.GetParameters () [0].ParameterType; if (typeof (ICppObject).IsAssignableFrom (paramType)) { // TODO: This is probably going to be causing us to alloc too much memory // if the ICppObject is returning impl.NativeSize + base.NativeSize. (We get FieldOffsetPadding // each time). il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Callvirt, typeof (ICppObject).GetProperty ("NativeSize").GetGetMethod ()); } else il.Emit (OpCodes.Ldfld, native_size_field); if (wrapper_type != null) { // load managed object il.Emit (OpCodes.Ldarg_1); //new CppInstancePtr (Abi.GetNativeSize (typeof (NativeLayout)), managedWrapper); il.Emit (OpCodes.Newobj, typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof (int), typeof (object) }, null)); } else il.Emit (OpCodes.Newobj, typeof (CppInstancePtr).GetConstructor (BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof (int) }, null)); } protected virtual void EmitConstruct (ILGenerator il, MethodInfo nativeMethod, Type [] parameterTypes, LocalBuilder nativePtr) { EmitCallNative (il, nativeMethod, false, parameterTypes, nativePtr); EmitInitVTable (il, nativePtr); } protected virtual void EmitDestruct (ILGenerator il, MethodInfo nativeMethod, bool isVirtual, Type [] parameterTypes, LocalBuilder cppInstancePtr, LocalBuilder nativePtr) { // bail if we weren't alloc'd by managed code Label bail = il.DefineLabel (); Label retNormal = il.DefineLabel (); il.Emit (OpCodes.Ldloca_S, cppInstancePtr); il.Emit (OpCodes.Brfalse_S, bail); // <- FIXME? (would this ever branch?) il.Emit (OpCodes.Ldloca_S, cppInstancePtr); il.Emit (OpCodes.Call, typeof (CppInstancePtr).GetProperty ("IsManagedAlloc").GetGetMethod ()); il.Emit (OpCodes.Brfalse_S, bail); EmitResetVTable (il, nativePtr); EmitCallNative (il, nativeMethod, false, parameterTypes, nativePtr); il.Emit (OpCodes.Br_S, retNormal); // before we bail, we have to pop the vtbl delegate from the stack if this // is a virtual destructor il.MarkLabel (bail); if (isVirtual) il.Emit (OpCodes.Pop); il.MarkLabel (retNormal); } /** * Emits IL to call the native method. nativeMethod should be either a method obtained by * GetPInvokeForMethod or the MethodInfo of a vtable method. * To complete method, emit OpCodes.Ret. */ protected virtual void EmitCallNative (ILGenerator il, MethodInfo nativeMethod, bool isStatic, Type [] parameterTypes, LocalBuilder nativePtr) { int argLoadStart = 1; // For static methods, just strip off arg0 (.net this pointer) if (!isStatic) { argLoadStart = 2; // For instance methods, strip off CppInstancePtr and pass the corresponding IntPtr il.Emit (OpCodes.Ldloc_S, nativePtr); } for (int i = argLoadStart; i <= parameterTypes.Length; i++) { EmitSpecialParameterMarshal (il, parameterTypes [i - 1]); il.Emit (OpCodes.Ldarg, i); } il.Emit (OpCodes.Call, nativeMethod); } // Note: when this is modified, usually GetParameterTypesForPInvoke types should be updated too protected virtual void EmitSpecialParameterMarshal (ILGenerator il, Type parameterType) { // auto marshal bool to C++ bool type (0 = false , 1 = true ) // auto marshal ICppObject } protected virtual IEnumerable GetParameterTypesForPInvoke (MethodInfo method) { var originalTypes = Util.GetMethodParameterTypes (method); var pinvokeTypes = originalTypes.Transform ( // CppInstancePtr implements ICppObject For.InputsWhere ((Type t) => typeof (ICppObject).IsAssignableFrom (t)).Emit (typeof (IntPtr)), For.UnmatchedInput ().Emit (t => t) ); return pinvokeTypes; } /** * Emits IL to set the vtable pointer of the instance (if class has a vtable). * This should usually happen in the managed wrapper of the C++ instance constructor. */ protected virtual void EmitInitVTable (ILGenerator il, LocalBuilder nativePtr) { // this._vtable.InitInstance (nativePtr); il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldfld, vtable_field); il.Emit (OpCodes.Ldloc_S, nativePtr); EmitVTableOp (il, typeof (VTable).GetMethod ("InitInstance"), 2, false); } protected virtual void EmitResetVTable (ILGenerator il, LocalBuilder nativePtr) { // this._vtable.ResetInstance (nativePtr); il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldfld, vtable_field); il.Emit (OpCodes.Ldloc_S, nativePtr); EmitVTableOp (il, typeof(VTable).GetMethod ("ResetInstance"), 2, false); } /** * A utility function to emit the IL for a vtable-dependant operation. * In other words, classes with no virtual methods will not have vtables, * so this method emits code to check for that and either throw an exception * or do nothing if no vtable exists. To use, push the arguments to the method you * want to call and pass the stackHeight for the call. If no vtable exists, this method * will emit code to pop the arguments off the stack. */ protected virtual void EmitVTableOp(ILGenerator il, MethodInfo method, int stackHeight, bool throwOnNoVTable) { // prepare a jump; do not call vtable method if no vtable Label noVirt = il.DefineLabel (); Label dontPushOrThrow = il.DefineLabel (); il.Emit (OpCodes.Ldarg_0); // load this il.Emit (OpCodes.Ldfld, vtable_field); // load this._vtable il.Emit (OpCodes.Brfalse_S, noVirt); // if (vtableInfo == null) goto noVirt il.Emit (OpCodes.Callvirt, method); // call method il.Emit (OpCodes.Br_S, dontPushOrThrow); // goto dontPushOrThrow il.MarkLabel (noVirt); // noVirt: // since there is no vtable, we did not make the method call. // pop arguments for (int i = 0; i < stackHeight; i++) il.Emit (OpCodes.Pop); // if the method was supposed to return a value, we must // still push something onto the stack // TODO: This is a kludge. What about value types? if (!method.ReturnType.Equals (typeof (void))) il.Emit (OpCodes.Ldnull); if (throwOnNoVTable) { il.Emit (OpCodes.Ldstr, "Native class has no VTable."); il.Emit (OpCodes.Newobj, typeof (InvalidOperationException).GetConstructor(new Type[] {typeof (string)})); il.Emit (OpCodes.Throw); } il.MarkLabel (dontPushOrThrow); } protected virtual void EmitLoadInstancePtr (ILGenerator il, Type firstParamType, out LocalBuilder cppip, out LocalBuilder native) { cppip = null; native = null; il.Emit (OpCodes.Ldarg_1); if (firstParamType.Equals (typeof (CppInstancePtr))) { cppip = il.DeclareLocal (typeof (CppInstancePtr)); native = il.DeclareLocal (typeof (IntPtr)); il.Emit (OpCodes.Stloc_S, cppip); il.Emit (OpCodes.Ldloca_S, cppip); il.Emit (OpCodes.Call, typeof (CppInstancePtr).GetProperty ("Native").GetGetMethod ()); il.Emit (OpCodes.Stloc_S, native); } else if (firstParamType.Equals (typeof (IntPtr))) { native = il.DeclareLocal (typeof (IntPtr)); il.Emit (OpCodes.Stloc_S, native); } else if (firstParamType.IsByRef) { native = il.DeclareLocal (firstParamType); il.Emit (OpCodes.Stloc_S, native); } else throw new ArgumentException ("First argument to non-static C++ method must be byref, IntPtr or CppInstancePtr."); } protected virtual void EmitCheckManagedAlloc (ILGenerator il, LocalBuilder cppip) { // make sure we were allocated by managed code // if not, return Label managedAlloc = il.DefineLabel (); il.Emit (OpCodes.Ldloca_S, cppip); il.Emit (OpCodes.Call, typeof (CppInstancePtr).GetProperty ("IsManagedAlloc").GetGetMethod ()); il.Emit (OpCodes.Brtrue_S, managedAlloc); il.Emit (OpCodes.Ret); il.MarkLabel (managedAlloc); } /** * throw ObjectDisposedException if we have a null pointer for native * however, allow destructor to be called even if we're disposed (just return immediately) */ protected virtual void EmitCheckDisposed (ILGenerator il, LocalBuilder native, MethodType methodType) { Label validRef = il.DefineLabel (); il.Emit (OpCodes.Ldloc_S, native); il.Emit (OpCodes.Brtrue_S, validRef); if (methodType == MethodType.NativeDtor) { il.Emit (OpCodes.Ret); il.MarkLabel (validRef); } else { il.Emit (OpCodes.Ldstr, String.Empty); il.Emit (OpCodes.Newobj, typeof (ObjectDisposedException).GetConstructor (new Type[] { typeof(string) })); il.Emit (OpCodes.Throw); il.MarkLabel (validRef); } } } }