diff --git a/src/Mono.VisualC.Interop/ABI/CppAbi.cs b/src/Mono.VisualC.Interop/ABI/CppAbi.cs index 1629e7df..01fac00d 100644 --- a/src/Mono.VisualC.Interop/ABI/CppAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/CppAbi.cs @@ -20,32 +20,6 @@ using Mono.VisualC.Interop.Util; 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 ParameterTypes { get; set; } - public List 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: Allow interface to override default calling convention public abstract partial class CppAbi { @@ -53,7 +27,8 @@ namespace Mono.VisualC.Interop.ABI { protected TypeBuilder impl_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 ILGenerator ctor_il; @@ -61,17 +36,33 @@ namespace Mono.VisualC.Interop.ABI { protected MemberFilter vtable_override_filter = VTable.BindToSignatureAndAttribute; // Cache some frequently used methodinfos: - 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_adjvcall = typeof (CppTypeInfo).GetMethod ("GetAdjustedVirtualCall"); - private static readonly MethodInfo vtable_initinstance = typeof (VTable).GetMethod ("InitInstance"); - private static readonly MethodInfo vtable_resetinstance = typeof (VTable).GetMethod ("ResetInstance"); + 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_adjvcall = typeof (CppTypeInfo).GetMethod ("GetAdjustedVirtualCall"); + private static readonly MethodInfo typeinfo_fieldoffset = typeof (CppTypeInfo).GetProperty ("FieldOffsetPadding").GetGetMethod (); + 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: 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; else if (imethod.Name.Equals ("Alloc")) return MethodType.ManagedAlloc; @@ -94,12 +85,12 @@ namespace Mono.VisualC.Interop.ABI { // The ImplementClass overrides are the main entry point to the Abi API: private struct EmptyNativeLayout { } - public Iface ImplementClass (Type wrapperType, string lib, string className) + public Iface ImplementClass (Type wrapperType, CppLibrary lib, string className) { return this.ImplementClass (wrapperType, lib, className); } - public virtual Iface ImplementClass (Type wrapperType, string lib, string className) + public virtual Iface ImplementClass (Type wrapperType, CppLibrary lib, string className) where NLayout : struct //where Iface : ICppClassInstantiatable or ICppClassOverridable { @@ -113,8 +104,8 @@ namespace Mono.VisualC.Interop.ABI { DefineImplType (); var properties = GetProperties (); - var methods = GetMethods (); - CppTypeInfo typeInfo = MakeTypeInfo (methods, methods.Where (m => IsVirtual (m))); + var methods = GetMethods ().Select (m => GetPInvokeSignature (m)); + var typeInfo = MakeTypeInfo (methods); // Implement all methods int vtableIndex = 0; @@ -130,9 +121,9 @@ namespace Mono.VisualC.Interop.ABI { return (Iface)Activator.CreateInstance (impl_type.CreateType (), typeInfo); } - protected virtual CppTypeInfo MakeTypeInfo (IEnumerable methods, IEnumerable virtualMethods) + protected virtual CppTypeInfo MakeTypeInfo (IEnumerable methods) { - return new CppTypeInfo (this, virtualMethods, layout_type); + return new CppTypeInfo (this, methods.Where (m => IsVirtual (m.OrigMethod)), layout_type); } protected virtual IEnumerable GetProperties () @@ -171,8 +162,6 @@ namespace Mono.VisualC.Interop.ABI { 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); typeinfo_field = impl_type.DefineField ("_typeInfo", typeof (CppTypeInfo), FieldAttributes.InitOnly | FieldAttributes.Private); 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_1); 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 - MethodType methodType = GetMethodType (interfaceMethod); - Type [] parameterTypes = ReflectionHelper.GetMethodParameterTypes (interfaceMethod); + var interfaceMethod = psig.OrigMethod; // 1. Generate managed trampoline to call native method MethodBuilder trampoline = GetMethodBuilder (interfaceMethod); - 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 - // 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) { + + case MethodType.ManagedAlloc: EmitManagedAlloc (il, interfaceMethod); il.Emit (OpCodes.Ret); return trampoline; @@ -224,93 +207,43 @@ namespace Mono.VisualC.Interop.ABI { bool isStatic = IsStatic (interfaceMethod); LocalBuilder cppInstancePtr = null; LocalBuilder nativePtr = null; - LocalBuilder retVal = null; - LocalBuilder retArg = null; // If we're an instance method, load up the "this" pointer if (!isStatic) { - if (parameterTypes.Length < 1) + if (psig.ParameterTypes.Count == 0) 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); + EmitLoadInstancePtr (il, interfaceMethod.GetParameters () [0].ParameterType, out cppInstancePtr, out nativePtr); // 3. Make sure our native pointer is a valid reference. If not, throw ObjectDisposedException - EmitCheckDisposed (il, nativePtr, methodType); + EmitCheckDisposed (il, nativePtr, psig.Type); } MethodInfo nativeMethod; - var psig = GetPInvokeSignature (typeInfo, interfaceMethod); - - if (psig.ReturnClass) { - Debug.Assert (wrapper_type != null); - retVal = il.DeclareLocal (wrapper_type); - // 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 (IsVirtual (interfaceMethod) && psig.Type != MethodType.NativeDtor) { + nativeMethod = EmitPrepareVirtualCall (il, typeInfo, nativePtr, vtableIndex++); + } else { + if (IsVirtual (interfaceMethod)) + vtableIndex++; - if (psig.ReturnByAddr) { - // - // 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); + nativeMethod = GetPInvokeForMethod (psig); } - if (IsVirtual (interfaceMethod) && methodType != MethodType.NativeDtor) - nativeMethod = EmitPrepareVirtualCall (il, typeInfo, nativePtr, vtableIndex++); - else - nativeMethod = GetPInvokeForMethod (interfaceMethod, psig); - - switch (methodType) { + switch (psig.Type) { case MethodType.NativeCtor: EmitConstruct (il, nativeMethod, psig, nativePtr); break; - case MethodType.NativeDtor: EmitDestruct (il, nativeMethod, psig, cppInstancePtr, nativePtr); break; - default: - EmitNativeCall (il, nativeMethod, isStatic, psig, nativePtr, retArg); + EmitNativeCall (il, nativeMethod, psig, nativePtr); 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); return trampoline; } @@ -320,50 +253,67 @@ namespace Mono.VisualC.Interop.ABI { 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; + var imethod = property.GetGetMethod (); + var methodName = imethod.Name; + var propName = property.Name; + var retType = imethod.ReturnType; + + 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 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); + 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 - //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); - */ - 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."); + // = ((int)Marshal.OffsetOf (layout_type, propName)) + FieldOffsetPadding; + il.Emit(OpCodes.Ldtoken, layout_type); + il.Emit(OpCodes.Call, type_gettypefromhandle); + il.Emit(OpCodes.Ldstr, propName); + il.Emit(OpCodes.Call, marshal_offsetof); - 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; - MethodBuilder fieldGetter = impl_type.DefineMethod (methodName, methodAttr, retType, Type.EmptyTypes); - ILGenerator il = fieldGetter.GetILGenerator (); + il.Emit (OpCodes.Add); - il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, fieldData); - il.Emit (OpCodes.Ret); + // new CppField () + il.Emit (OpCodes.Newobj, retType.GetConstructor (new Type[] { typeof(int) })); + + 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); @@ -379,43 +329,47 @@ namespace Mono.VisualC.Interop.ABI { if (wrapper_type == null) return null; - MethodInfo interfaceMethod = typeInfo.VirtualMethods [vtableIndex]; - MethodInfo targetMethod = FindManagedOverrideTarget (interfaceMethod); + var sig = typeInfo.VirtualMethods [vtableIndex]; + var interfaceMethod = sig.OrigMethod; + var targetMethod = FindManagedOverrideTarget (interfaceMethod); if (targetMethod == 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 // 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", psig.ReturnType, - psig.ParameterTypes.ToArray (), typeof (CppInstancePtr).Module, true); + var trampolineIn = new DynamicMethod (wrapper_type.Name + "_" + interfaceMethod.Name + "_FromNative", sig.ReturnType, + nativeArgs, typeof (CppInstancePtr).Module, true); ReflectionHelper.ApplyMethodParameterAttributes (interfaceMethod, trampolineIn, true); ILGenerator il = trampolineIn.GetILGenerator (); - // for static methods: + // for static (target) methods: 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 managed instance to call them on! if (!targetMethod.IsStatic) { callInstruction = OpCodes.Callvirt; - //argLoadStart = 1; + argLoadStart = 1; il.Emit (OpCodes.Ldarg_0); 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); } - for (int i = argLoadStart; i < psig.ParameterTypes.Count; i++) { + for (int i = argLoadStart; i < interfaceArgs.Length; i++) { il.Emit (OpCodes.Ldarg, i); + EmitInboundMarshal (il, nativeArgs [i], interfaceArgs [i]); } - il.Emit (OpCodes.Tailcall); + il.Emit (callInstruction, targetMethod); + EmitOutboundMarshal (il, targetMethod.ReturnType, sig.ReturnType); il.Emit (OpCodes.Ret); return trampolineIn.CreateDelegate (typeInfo.VTableDelegateTypes [vtableIndex]); @@ -423,6 +377,9 @@ namespace Mono.VisualC.Interop.ABI { protected virtual MethodInfo FindManagedOverrideTarget (MethodInfo interfaceMethod) { + if (interfaceMethod == null) + return null; + // FIXME: Does/should this look in superclasses? MemberInfo [] possibleMembers = wrapper_type.FindMembers (MemberTypes.Method, BindingFlags.Public | BindingFlags.NonPublic | 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, interfaceMethod.ReturnType, parameterTypes); ReflectionHelper.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, PInvokeSignature psig) + protected virtual MethodBuilder GetPInvokeForMethod (PInvokeSignature sig) { - string entryPoint = GetMangledMethodName (signature); + string entryPoint = sig.Name; if (entryPoint == null) throw new NotSupportedException ("Could not mangle method name."); string lib; - if (IsInline (signature)) - lib = library + "-inline"; + if (IsInline (sig.OrigMethod) && library.InlineMethodPolicy == InlineMethods.SurrogateLib) + lib = library.Name + "-inline"; else - lib = library; + lib = library.Name; - MethodBuilder builder = impl_type.DefinePInvokeMethod ("__$" + signature.Name + "_Impl", lib, entryPoint, - MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.PinvokeImpl, - CallingConventions.Standard, psig.ReturnType, psig.ParameterTypes.ToArray (), - GetCallingConvention (signature).Value, CharSet.Ansi); + MethodBuilder builder = impl_type.DefinePInvokeMethod (entryPoint, lib, entryPoint, + MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.PinvokeImpl, + CallingConventions.Standard, sig.ReturnType, sig.ParameterTypes.ToArray (), + sig.CallingConvention.Value, CharSet.Ansi); builder.SetImplementationFlags (builder.GetMethodImplementationFlags () | MethodImplAttributes.PreserveSig); - ReflectionHelper.ApplyMethodParameterAttributes (signature, builder, true); + ReflectionHelper.ApplyMethodParameterAttributes (sig.OrigMethod, builder, true); return builder; } @@ -502,38 +458,41 @@ namespace Mono.VisualC.Interop.ABI { il.Emit (OpCodes.Ldfld, typeinfo_field); il.Emit (OpCodes.Callvirt, typeinfo_nativesize); - if (wrapper_type != null) { + if (wrapper_type != null && interfaceMethod.GetParameters ().Any ()) { // load managed wrapper 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 - 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, - PInvokeSignature psig, - LocalBuilder nativePtr) + protected virtual void EmitConstruct (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { - EmitNativeCall (il, nativeMethod, false, psig, nativePtr, null); - // FIXME: Why is this needed ? The c++ ctor initializes it - //EmitInitVTable (il, nativePtr); + Debug.Assert (psig.Type == MethodType.NativeCtor); + EmitNativeCall (il, nativeMethod, psig, nativePtr); + EmitInitVTable (il, nativePtr); } - protected virtual void EmitDestruct (ILGenerator il, MethodInfo nativeMethod, - PInvokeSignature psig, - LocalBuilder cppInstancePtr, LocalBuilder nativePtr) + protected virtual void EmitDestruct (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, + 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 Label bail = 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.Call, cppip_managedalloc); il.Emit (OpCodes.Brfalse_S, bail); EmitResetVTable (il, nativePtr); - EmitNativeCall (il, nativeMethod, false, psig, nativePtr, null); + EmitNativeCall (il, nativeMethod, psig, nativePtr); il.MarkLabel (bail); } @@ -543,142 +502,206 @@ namespace Mono.VisualC.Interop.ABI { * GetPInvokeForMethod or the MethodInfo of a vtable method. * To complete method, emit OpCodes.Ret. */ - protected virtual void EmitNativeCall (ILGenerator il, MethodInfo nativeMethod, - bool isStatic, PInvokeSignature psig, - LocalBuilder nativePtr, LocalBuilder retArg) + protected virtual void EmitNativeCall (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { - // - // The managed signature looks like this: - // (<.net this>, @this (if not static), - // The native signature looks like this: - // (, @this, ) - // - // 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; - } + var interfaceMethod = psig.OrigMethod; + var interfaceArgs = interfaceMethod.GetParameters (); - Type ptype = psig.ParameterTypes [pindex]; - switch (psig.ParameterMarshallers [pindex]) { - case ParameterMarshal.Default: - // FIXME: Why is this needed ? - // auto marshal bool to C++ bool type (0 = false , 1 = true ) - if (ptype.Equals (typeof (bool))) { - args [pindex] = il.DeclareLocal (typeof (bool)); - Label isTrue = il.DefineLabel (); - Label done = il.DefineLabel (); - il.Emit (OpCodes.Ldarg, aindex); - il.Emit (OpCodes.Brtrue_S, isTrue); - il.Emit (OpCodes.Ldc_I4_0); - il.Emit (OpCodes.Br_S, done); - il.MarkLabel (isTrue); - il.Emit (OpCodes.Ldc_I4_1); - il.MarkLabel (done); - il.Emit (OpCodes.Stloc, args [pindex]); - //il.Emit (OpCodes.Conv_I1); - } - break; - case ParameterMarshal.ClassByRef: { - args [pindex] = il.DeclareLocal (typeof (IntPtr)); - // Pass the native pointer of the class - // Null check - Label isNull = il.DefineLabel (); - Label contLabel = il.DefineLabel (); - il.Emit (OpCodes.Ldarg, aindex); - il.Emit (OpCodes.Brfalse_S, isNull); - // Non-null case - il.Emit (OpCodes.Ldarg, aindex); - EmitLoadNativePtr (il); - // FIXME: Dispose check - il.Emit (OpCodes.Br_S, contLabel); - // Null case - il.MarkLabel (isNull); - il.Emit (OpCodes.Ldnull); - il.Emit (OpCodes.Conv_I); - // Common case - il.MarkLabel (contLabel); - il.Emit (OpCodes.Stloc, args [pindex]); - break; + int argLoadStart = 1; // For static methods, just strip off arg0 (.net this pointer) + if (!IsStatic (interfaceMethod)) + { + argLoadStart = 2; // For instance methods, strip off CppInstancePtr and pass the corresponding IntPtr + il.Emit (OpCodes.Ldloc_S, nativePtr); + } + + // load and marshal arguments + for (int i = argLoadStart; i <= interfaceArgs.Length; i++) { + il.Emit (OpCodes.Ldarg, i); + EmitOutboundMarshal (il, interfaceArgs [i - 1].ParameterType, psig.ParameterTypes [i - 1]); + } + + il.Emit (OpCodes.Call, nativeMethod); + + // Marshal return value + EmitInboundMarshal (il, psig.ReturnType, interfaceMethod.ReturnType); + } + + + public virtual PInvokeSignature GetPInvokeSignature (MethodInfo method) + { + var parameters = method.GetParameters (); + var pinvokeTypes = new List (parameters.Length); + + foreach (var pi in parameters) { + pinvokeTypes.Add (ToPInvokeType (pi.ParameterType, pi)); + } + + return new PInvokeSignature { + OrigMethod = method, + Name = GetMangledMethodName (method), + Type = GetMethodType (method), + CallingConvention = GetCallingConvention (method), + ParameterTypes = pinvokeTypes, + ReturnType = ToPInvokeType (method.ReturnType, method.ReturnTypeCustomAttributes) + }; + } + + public virtual Type ToPInvokeType (Type t, ICustomAttributeProvider icap) + { + 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 ? + 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); - Label normalLabel = il.DefineLabel (); - il.Emit (OpCodes.Ldarg, aindex); - il.Emit (OpCodes.Brtrue_S, normalLabel); + } + + return t; + } + + // 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 - 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 '" + managedType + "' to c++ by value"); il.Emit (OpCodes.Newobj, typeof (ArgumentException).GetConstructor (new Type[] { typeof(string) })); 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) { - pindexStart ++; - il.Emit (OpCodes.Ldloc, retArg); - } + // This method marshals from C++ -> managed + // The value it is marshaling will be on the stack. + protected virtual void EmitInboundMarshal (ILGenerator il, Type nativeType, Type targetType) + { + var next = il.DefineLabel (); - for (int pindex = pindexStart; pindex < psig.ParameterTypes.Count; pindex++) { - // The first argument is the .net this argument - if (args [pindex] != null) - il.Emit (OpCodes.Ldloc, args [pindex]); - else - il.Emit (OpCodes.Ldarg, aindex); + // marshal IntPtr -> ICppObject + if (nativeType == typeof (IntPtr) && typeof (ICppObject).IsAssignableFrom (targetType)) { - 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) { - var originalTypes = ReflectionHelper.GetMethodParameterTypes (method); + il.MarkLabel (isNull); + 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 ( - For.AnyInputIn (typeof (bool)).Emit (typeof (byte)), + il.Emit (OpCodes.Call, marshal_structuretoptr); - // CppInstancePtr implements ICppObject - For.InputsWhere ((Type t) => typeof (ICppObject).IsAssignableFrom (t)).Emit (typeof (IntPtr)), + il.Emit (OpCodes.Ldloc, ptr); + EmitCreateCppObjectFromNative (il, targetType); + } + + il.MarkLabel (next); + } - For.UnmatchedInput ().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, - out LocalBuilder native) + out LocalBuilder native) { cppip = null; native = null; @@ -767,7 +790,7 @@ namespace Mono.VisualC.Interop.ABI { 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.Call, cppip_native); il.Emit (OpCodes.Stloc_S, native); } else if (firstParamType.Equals (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."); } - // 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) { // make sure we were allocated by managed code @@ -822,4 +834,5 @@ namespace Mono.VisualC.Interop.ABI { } } } + } diff --git a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs index a570b08d..71a1e119 100644 --- a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs @@ -30,6 +30,7 @@ using System; using System.Linq; using System.Text; using System.Reflection; +using System.Reflection.Emit; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -42,21 +43,36 @@ namespace Mono.VisualC.Interop.ABI { { } - protected override CppTypeInfo MakeTypeInfo (IEnumerable methods, IEnumerable virtualMethods) + protected override CppTypeInfo MakeTypeInfo (IEnumerable methods) { - bool hasExplicitCopyCtor = false; - bool hasExplicitDtor = false; - - foreach (var m in methods) { - if (m.IsDefined (typeof (CopyConstructorAttribute), false) && !m.IsDefined (typeof (ArtificialAttribute), false)) - hasExplicitCopyCtor = true; - if (m.IsDefined (typeof (DestructorAttribute), false) && !m.IsDefined (typeof (ArtificialAttribute), false)) - hasExplicitDtor = true; - } + return new CppTypeInfo (this, GetVirtualMethodSlots (methods), layout_type); + } + + private IEnumerable GetVirtualMethodSlots (IEnumerable methods) + { + foreach (var method in methods) { + if (!IsVirtual (method.OrigMethod)) + continue; + + yield return method; - return new ItaniumTypeInfo (this, virtualMethods, layout_type) { HasExplicitCopyCtor = hasExplicitCopyCtor, HasExplicitDtor = hasExplicitDtor }; + // Itanium has extra slot for virt dtor + if (method.Type == MethodType.NativeDtor) + yield return null; + } } + protected override MethodBuilder DefineMethod (PInvokeSignature sig, CppTypeInfo typeInfo, ref int vtableIndex) + { + var builder = base.DefineMethod (sig, typeInfo, ref vtableIndex); + + // increment vtableIndex an extra time for that extra vdtor slot (already incremented once in base) + if (IsVirtual (sig.OrigMethod) && sig.Type == MethodType.NativeDtor) + vtableIndex++; + + return builder; + } + public override CallingConvention? GetCallingConvention (MethodInfo methodInfo) { return CallingConvention.Cdecl; @@ -71,6 +87,10 @@ namespace Mono.VisualC.Interop.ABI { ParameterInfo [] parameters = methodInfo.GetParameters (); StringBuilder nm = new StringBuilder ("_ZN", 30); + + if (IsConst (methodInfo)) + nm.Append ('K'); + nm.Append (class_name.Length).Append (class_name); compressMap [class_name] = compressMap.Count; @@ -158,64 +178,7 @@ namespace Mono.VisualC.Interop.ABI { return code.ToString (); } - public override PInvokeSignature GetPInvokeSignature (CppTypeInfo typeInfo, MethodInfo method) { - Type returnType = method.ReturnType; - - bool retClass = false; - bool retByAddr = false; - - if (typeof (ICppObject).IsAssignableFrom (returnType)) { - retClass = true; - if ((typeInfo as ItaniumTypeInfo).HasExplicitCopyCtor || - (typeInfo as ItaniumTypeInfo).HasExplicitDtor) { - // Section 3.1.4: - // Classes with non-default copy ctors/destructors are returned using a - // hidden argument - retByAddr = true; - } - } - - ParameterInfo[] parameters = method.GetParameters (); - - var ptypes = new List (); - var pmarshal = new List (); - // FIXME: Handle ByVal attributes - foreach (var pi in parameters) { - Type t = pi.ParameterType; - Type ptype = t; - ParameterMarshal m = ParameterMarshal.Default; - - if (t == typeof (bool)) { - ptype = typeof (byte); - } else if (typeof (ICppObject).IsAssignableFrom (t) && t != typeof (CppInstancePtr)) { - if (pi.IsDefined (typeof (ByValAttribute), false)) { - m = ParameterMarshal.ClassByVal; - // Can't use an interface/cattr since the native layout type is private - // Pass it as a type argument to I ? - 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."); - ptype = (Type)f.GetValue (null); - } else { - ptype = typeof (IntPtr); - m = ParameterMarshal.ClassByRef; - } - } else if (typeof (ICppObject).IsAssignableFrom (t)) { - // CppInstancePtr implements ICppObject - ptype = typeof (IntPtr); - } - ptypes.Add (ptype); - pmarshal.Add (m); - } - - if (retByAddr) { - ptypes.Insert (0, typeof (IntPtr)); - pmarshal.Insert (0, ParameterMarshal.Default); - } - - return new PInvokeSignature { OrigMethod = method, ParameterTypes = ptypes, ParameterMarshallers = pmarshal, ReturnType = returnType, ReturnClass = retClass, ReturnByAddr = retByAddr }; - } } -} +} \ No newline at end of file diff --git a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumTypeInfo.cs b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumTypeInfo.cs deleted file mode 100644 index 8564aaac..00000000 --- a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumTypeInfo.cs +++ /dev/null @@ -1,79 +0,0 @@ -// -// Author: -// Andreia Gaita (shana@spoiledcat.net) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -using System; -using System.Linq; -using System.Collections.Generic; -using System.Reflection; - -namespace Mono.VisualC.Interop.ABI { - public class ItaniumTypeInfo : CppTypeInfo { - - private bool hasVirtualDtor; - - public ItaniumTypeInfo (ItaniumAbi abi, IEnumerable virtualMethods, Type nativeLayout) - : base (abi, virtualMethods, nativeLayout) - { - // Remove all virtual destructors from their declared position in the vtable - for (int i = 0; i < VirtualMethods.Count; i++) { - if (Abi.GetMethodType (VirtualMethods [i]) != MethodType.NativeDtor) - continue; - - hasVirtualDtor = true; - VirtualMethods.RemoveAt (i); - VTableDelegateTypes.RemoveAt (i); - VTableOverrides.RemoveAt (i); - break; - } - } - - public bool HasVirtualDestructor { - get { return hasVirtualDtor; } - } - - public bool HasExplicitCopyCtor { get; set; } - public bool HasExplicitDtor { get; set; } - - public override void AddBase (CppTypeInfo baseType) - { - if (TypeComplete) - return; - - hasVirtualDtor |= ((ItaniumTypeInfo)baseType).HasVirtualDestructor; - base.AddBase (baseType, false); - } - - // Itanium puts all its virtual dtors at the bottom of the vtable (2 slots each). - // Since we will never be in a position of calling a dtor virtually, we can just pad the vtable out. - public override int VTableBottomPadding { - get { - if (!hasVirtualDtor) - return 0; - - return 2 + CountBases (b => ((ItaniumTypeInfo)b).HasVirtualDestructor) * 2; - } - } - - } -} - diff --git a/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs b/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs index 144b7b10..483194d4 100644 --- a/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs @@ -44,9 +44,9 @@ namespace Mono.VisualC.Interop.ABI { { } - protected override CppTypeInfo MakeTypeInfo (IEnumerable methods, IEnumerable virtualMethods) + protected override CppTypeInfo MakeTypeInfo (IEnumerable methods) { - return new MsvcTypeInfo (this, virtualMethods, layout_type); + return new MsvcTypeInfo (this, methods.Where (m => IsVirtual (m.OrigMethod)), layout_type); } public override CallingConvention? GetCallingConvention (MethodInfo methodInfo) @@ -209,7 +209,6 @@ namespace Mono.VisualC.Interop.ABI { code.Append(mangleType.ElementTypeName); code.Append ("@@"); break; - } return code.ToString (); diff --git a/src/Mono.VisualC.Interop/ABI/Impl/MsvcTypeInfo.cs b/src/Mono.VisualC.Interop/ABI/Impl/MsvcTypeInfo.cs index 1372b48a..9511d147 100644 --- a/src/Mono.VisualC.Interop/ABI/Impl/MsvcTypeInfo.cs +++ b/src/Mono.VisualC.Interop/ABI/Impl/MsvcTypeInfo.cs @@ -29,9 +29,11 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Reflection; +using Mono.VisualC.Interop.Util; + namespace Mono.VisualC.Interop.ABI { public class MsvcTypeInfo : CppTypeInfo { - public MsvcTypeInfo (MsvcAbi abi, IEnumerable virtualMethods, Type nativeLayout) + public MsvcTypeInfo (MsvcAbi abi, IEnumerable virtualMethods, Type nativeLayout) : base (abi, virtualMethods, nativeLayout) { } diff --git a/src/Mono.VisualC.Interop/ABI/MethodType.cs b/src/Mono.VisualC.Interop/ABI/MethodType.cs index b6ca646c..fe52866a 100644 --- a/src/Mono.VisualC.Interop/ABI/MethodType.cs +++ b/src/Mono.VisualC.Interop/ABI/MethodType.cs @@ -27,6 +27,7 @@ using System; namespace Mono.VisualC.Interop { public enum MethodType { NoOp, + NotImplemented, Native, NativeCtor, NativeDtor, diff --git a/src/Mono.VisualC.Interop/Attributes.cs b/src/Mono.VisualC.Interop/Attributes.cs index 0e6aafa0..19058b58 100644 --- a/src/Mono.VisualC.Interop/Attributes.cs +++ b/src/Mono.VisualC.Interop/Attributes.cs @@ -49,7 +49,7 @@ namespace Mono.VisualC.Interop { [AttributeUsage (AttributeTargets.Method)] public class CopyConstructorAttribute : Attribute {} - [AttributeUsage (AttributeTargets.Parameter)] + [AttributeUsage (AttributeTargets.Parameter | AttributeTargets.ReturnValue)] public class ByValAttribute : Attribute {} [AttributeUsage (AttributeTargets.Parameter | AttributeTargets.ReturnValue)] @@ -119,6 +119,18 @@ using Mono.VisualC.Interop; { return method.IsDefined (typeof (InlineAttribute), false); } + public virtual bool IsArtificial (MethodInfo method) + { + return method.IsDefined (typeof (ArtificialAttribute), false); + } + public virtual bool IsCopyConstructor (MethodInfo method) + { + return method.IsDefined (typeof (CopyConstructorAttribute), false); + } + public virtual bool IsByVal (ICustomAttributeProvider icap) + { + return icap.IsDefined (typeof (ByValAttribute), false); + } public virtual CppType GetMangleType (ICustomAttributeProvider icap, Type managedType) { diff --git a/src/Mono.VisualC.Interop/CppInstancePtr.cs b/src/Mono.VisualC.Interop/CppInstancePtr.cs index 07b2962a..49728c33 100644 --- a/src/Mono.VisualC.Interop/CppInstancePtr.cs +++ b/src/Mono.VisualC.Interop/CppInstancePtr.cs @@ -55,7 +55,7 @@ namespace Mono.VisualC.Interop { if (!implCache.TryGetValue (typeof (Iface), out cachedImpl)) { VirtualOnlyAbi virtualABI = new VirtualOnlyAbi (VTable.BindToSignature); - impl = virtualABI.ImplementClass (typeof (TWrapper), string.Empty, string.Empty); + impl = virtualABI.ImplementClass (typeof (TWrapper), new CppLibrary (string.Empty), string.Empty); implCache.Add (typeof (Iface), impl); } else @@ -86,7 +86,7 @@ namespace Mono.VisualC.Interop { } // Alloc a new C++ instance when there is no managed wrapper. - internal CppInstancePtr (int nativeSize) + public CppInstancePtr (int nativeSize) { ptr = Marshal.AllocHGlobal (nativeSize); manage_memory = true; @@ -127,12 +127,6 @@ namespace Mono.VisualC.Interop { get { return this; } } - public int NativeSize { - get { - throw new NotImplementedException (); - } - } - public bool IsManagedAlloc { get { return manage_memory; } } diff --git a/src/Mono.VisualC.Interop/CppLibrary.cs b/src/Mono.VisualC.Interop/CppLibrary.cs index de25d66f..0ca55a35 100644 --- a/src/Mono.VisualC.Interop/CppLibrary.cs +++ b/src/Mono.VisualC.Interop/CppLibrary.cs @@ -38,12 +38,29 @@ using System.Reflection.Emit; using Mono.VisualC.Interop.ABI; namespace Mono.VisualC.Interop { + + public enum InlineMethods { + + // Normally, C++ inline methods are not exported from the library, so C++ interop cannot call them. + // This is the default option. It throws a NotImplementedException if you try to call the native version of one of these methods. + // Use this if you reimplement the inline methods in managed code, or if they are not to be available in the bindings. + NotPresent, + + // Expect the inline methods to be present in the specified library + // For example, if the library was compiled with GCC's -fkeep-inline-functions option + Present, + + // Expect the inline methods to be exported in a separate library named %name%-inline + SurrogateLib, + } + public sealed class CppLibrary { internal static AssemblyBuilder interopAssembly; internal static ModuleBuilder interopModule; public CppAbi Abi { get; private set; } public string Name { get; private set; } + public InlineMethods InlineMethodPolicy { get; private set; } static CppLibrary () { @@ -55,17 +72,17 @@ namespace Mono.VisualC.Interop { } public CppLibrary (string name) + : this (name, InlineMethods.NotPresent) { - if (name == null) - throw new ArgumentNullException ("Name cannot be NULL."); - - this.Name = name; + } - // FIXME: This is where we'd auto detect the ABI - this.Abi = new ItaniumAbi (); + public CppLibrary (string name, InlineMethods inlinePolicy) + : this (name, new ItaniumAbi (), inlinePolicy) + { + //FIXME: Ideally we would auto-detect ABI here. } - public CppLibrary (string name, CppAbi abi) + public CppLibrary (string name, CppAbi abi, InlineMethods inlinePolicy) { if (name == null) throw new ArgumentNullException ("Name cannot be NULL."); @@ -74,6 +91,7 @@ namespace Mono.VisualC.Interop { this.Name = name; this.Abi = abi; + this.InlineMethodPolicy = inlinePolicy; } // Mainly for debugging at this point @@ -87,7 +105,7 @@ namespace Mono.VisualC.Interop { public Iface GetClass (string className) where Iface : ICppClass { - return Abi.ImplementClass (null, Name, className); + return Abi.ImplementClass (null, this, className); } // For instantiating or working with a class that may have fields @@ -96,7 +114,7 @@ namespace Mono.VisualC.Interop { where Iface : ICppClassInstantiatable where NativeLayout : struct { - return Abi.ImplementClass (null, Name, className); + return Abi.ImplementClass (null, this, className); } /* The most powerful override. Allows the following from managed code: @@ -109,7 +127,7 @@ namespace Mono.VisualC.Interop { where NativeLayout : struct where Managed : ICppObject { - return Abi.ImplementClass (typeof (Managed), Name, className); + return Abi.ImplementClass (typeof (Managed), this, className); } } diff --git a/src/Mono.VisualC.Interop/CppTypeInfo.cs b/src/Mono.VisualC.Interop/CppTypeInfo.cs index 06e9039b..fecfe4ae 100644 --- a/src/Mono.VisualC.Interop/CppTypeInfo.cs +++ b/src/Mono.VisualC.Interop/CppTypeInfo.cs @@ -45,7 +45,7 @@ namespace Mono.VisualC.Interop { public CppAbi Abi { get; private set; } public Type NativeLayout {get; private set; } - public IList VirtualMethods { get; private set; } + public IList VirtualMethods { get; private set; } public LazyGeneratedList VTableDelegateTypes { get; private set; } public LazyGeneratedList VTableOverrides { get; private set; } @@ -62,14 +62,14 @@ namespace Mono.VisualC.Interop { private VTable lazy_vtable; - public CppTypeInfo (CppAbi abi, IEnumerable virtualMethods, Type nativeLayout) + public CppTypeInfo (CppAbi abi, IEnumerable virtualMethods, Type nativeLayout) { Abi = abi; NativeLayout = nativeLayout; - VirtualMethods = new List (virtualMethods); - VTableDelegateTypes = new LazyGeneratedList (VirtualMethods.Count, VTableDelegateTypeGenerator); - VTableOverrides = new LazyGeneratedList (VirtualMethods.Count, VTableOverrideGenerator); + VirtualMethods = new List (virtualMethods); + VTableDelegateTypes = new LazyGeneratedList (VirtualMethods.Count, i => DelegateTypeCache.GetDelegateType (VirtualMethods [i])); + VTableOverrides = new LazyGeneratedList (VirtualMethods.Count, i => Abi.GetManagedOverrideTrampoline (this, i)); BaseClasses = new List (); BaseVTableSlots = 0; @@ -83,8 +83,6 @@ namespace Mono.VisualC.Interop { public virtual void AddBase (CppTypeInfo baseType) { - if (TypeComplete) - return; // by default, do not add another vtable pointer for this new base class AddBase (baseType, false); @@ -92,6 +90,9 @@ namespace Mono.VisualC.Interop { protected virtual void AddBase (CppTypeInfo baseType, bool addVTablePointer) { + if (TypeComplete) + return; + BaseClasses.Add (baseType); if (!addVTablePointer) { @@ -121,7 +122,6 @@ namespace Mono.VisualC.Interop { return count; } - // FIXME: Make this thread safe? public virtual void CompleteType () { if (TypeComplete) @@ -131,16 +131,25 @@ namespace Mono.VisualC.Interop { baseClass.CompleteType (); TypeComplete = true; + RemoveVTableDuplicates (ms => true); + } + protected virtual void RemoveVTableDuplicates (Predicate pred) + { // check that any virtual methods overridden in a subclass are only included once - HashSet vsignatures = new HashSet (); - for (int i = 0; i < VirtualMethods.Count; i++) { - MethodSignature sig = GetVTableMethodSignature (i); + var vsignatures = new HashSet (); - if (vsignatures.Contains (sig)) - VirtualMethods.RemoveAt (i--); - else + for (int i = 0; i < VirtualMethods.Count; i++) { + var sig = VirtualMethods [i]; + if (sig == null) + continue; + + if (vsignatures.Contains (sig)) { + if (pred (sig)) + VirtualMethods.RemoveAt (i--); + } else { vsignatures.Add (sig); + } } } @@ -186,33 +195,6 @@ namespace Mono.VisualC.Interop { public virtual int VTableBottomPadding { get { return 0; } } - - public virtual MethodSignature GetVTableMethodSignature (int index) - { - MethodInfo method = VirtualMethods [index]; - return new MethodSignature () { Name = method.Name, - Type = Abi.GetMethodType (method), - Signature = GetVTableDelegateSignature (index) }; - } - - public virtual DelegateSignature GetVTableDelegateSignature (int index) - { - MethodInfo method = VirtualMethods [index]; - var psig = Abi.GetPInvokeSignature (this, method); - return new DelegateSignature () { ParameterTypes = psig.ParameterTypes, - ReturnType = method.ReturnType, - CallingConvention = Abi.GetCallingConvention (method) }; - } - - private Type VTableDelegateTypeGenerator (int index) - { - return DelegateTypeCache.GetDelegateType (GetVTableDelegateSignature (index)); - } - - private Delegate VTableOverrideGenerator (int index) - { - return Abi.GetManagedOverrideTrampoline (this, index); - } } } diff --git a/src/Mono.VisualC.Interop/Interfaces.cs b/src/Mono.VisualC.Interop/Interfaces.cs index e12925e7..792262a5 100644 --- a/src/Mono.VisualC.Interop/Interfaces.cs +++ b/src/Mono.VisualC.Interop/Interfaces.cs @@ -30,6 +30,8 @@ using System; using Mono.VisualC.Interop.ABI; namespace Mono.VisualC.Interop { + + // Part of the contract for ICppObject is a public constructor that takes CppInstancePtr (native constructor) public interface ICppObject : IDisposable { CppInstancePtr Native { get; } } @@ -47,7 +49,9 @@ namespace Mono.VisualC.Interop { // 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 : ICppClass /* where T : ICppObject */ { - CppInstancePtr Alloc (T managed); + public interface ICppClassOverridable : ICppClassInstantiatable + /* where TManaged : ICppObject */ + { + CppInstancePtr Alloc (TManaged managed); } } diff --git a/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj b/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj index a036d105..c426417c 100644 --- a/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj +++ b/src/Mono.VisualC.Interop/Mono.VisualC.Interop.csproj @@ -74,7 +74,6 @@ - diff --git a/src/Mono.VisualC.Interop/Util/DelegateTypeCache.cs b/src/Mono.VisualC.Interop/Util/DelegateTypeCache.cs index afd18113..57ede9e0 100644 --- a/src/Mono.VisualC.Interop/Util/DelegateTypeCache.cs +++ b/src/Mono.VisualC.Interop/Util/DelegateTypeCache.cs @@ -37,7 +37,7 @@ namespace Mono.VisualC.Interop.Util { public static class DelegateTypeCache { - private static Dictionary type_cache; + private static Dictionary type_cache; public static Type GetDelegateType (MethodInfo signature, CallingConvention? callingConvention) { @@ -45,13 +45,13 @@ namespace Mono.VisualC.Interop.Util { } public static Type GetDelegateType (IEnumerable parameterTypes, Type returnType, CallingConvention? callingConvention) { - return GetDelegateType (new DelegateSignature () { ParameterTypes = parameterTypes, ReturnType = returnType, CallingConvention = callingConvention }); + return GetDelegateType (new BasicSignature { ParameterTypes = parameterTypes.ToList (), ReturnType = returnType, CallingConvention = callingConvention }); } - public static Type GetDelegateType (DelegateSignature signature) + public static Type GetDelegateType (BasicSignature signature) { Type delegateType; if (type_cache == null) - type_cache = new Dictionary (); + type_cache = new Dictionary (); if (!type_cache.TryGetValue (signature, out delegateType)) { delegateType = CreateDelegateType (signature); @@ -61,7 +61,7 @@ namespace Mono.VisualC.Interop.Util { return delegateType; } - private static Type CreateDelegateType (DelegateSignature signature) + private static Type CreateDelegateType (BasicSignature signature) { string delTypeName = signature.UniqueName; diff --git a/src/Mono.VisualC.Interop/Util/MethodSignature.cs b/src/Mono.VisualC.Interop/Util/MethodSignature.cs index 4b6cd81a..1364529c 100644 --- a/src/Mono.VisualC.Interop/Util/MethodSignature.cs +++ b/src/Mono.VisualC.Interop/Util/MethodSignature.cs @@ -35,9 +35,9 @@ using System.Collections.Generic; namespace Mono.VisualC.Interop.Util { - public struct DelegateSignature { + public class BasicSignature { public CallingConvention? CallingConvention { get; set; } - public IEnumerable ParameterTypes { get; set; } + public List ParameterTypes { get; set; } public Type ReturnType { get; set; } private string uniqueName; @@ -64,18 +64,23 @@ namespace Mono.VisualC.Interop.Util { } } + public bool IsCompatibleWith (BasicSignature other) + { + return CallingConvention == other.CallingConvention && + ((ParameterTypes == null && other.ParameterTypes == null) || + ParameterTypes.SequenceEqual (other.ParameterTypes)) && + ReturnType.Equals (other.ReturnType); + } + public override bool Equals (object obj) { if (obj == null) return false; - if (obj.GetType () != typeof(DelegateSignature)) + if (obj.GetType () != typeof(BasicSignature)) return false; - DelegateSignature other = (DelegateSignature)obj; + BasicSignature other = (BasicSignature)obj; - return CallingConvention == other.CallingConvention && - ((ParameterTypes == null && other.ParameterTypes == null) || - ParameterTypes.SequenceEqual (other.ParameterTypes)) && - ReturnType.Equals (other.ReturnType); + return IsCompatibleWith (other); } public override int GetHashCode () @@ -88,10 +93,9 @@ namespace Mono.VisualC.Interop.Util { } } - public struct MethodSignature { + public class MethodSignature : BasicSignature { public string Name { get; set; } public MethodType Type { get; set; } - public DelegateSignature Signature { get; set; } public override bool Equals (object obj) { @@ -100,21 +104,27 @@ namespace Mono.VisualC.Interop.Util { if (obj.GetType () != typeof(MethodSignature)) return false; MethodSignature other = (MethodSignature)obj; - return Signature.Equals (other.Signature) && + + return IsCompatibleWith (other) && Type == other.Type && - (Type != MethodType.Native || Name.Equals (other.Name)); + (Name.Equals (other.Name) || Type != MethodType.Native); } public override int GetHashCode () { unchecked { - return Signature.GetHashCode () ^ + return base.GetHashCode () ^ Type.GetHashCode () ^ (Type == MethodType.Native? Name.GetHashCode () : 0); } } } + + public class PInvokeSignature : MethodSignature { + // The original c# method this signature was generated from + public MethodInfo OrigMethod; + } }