From 179207fb18326fa895ca2864d00ac671ec0386ae Mon Sep 17 00:00:00 2001 From: Alex Corrado Date: Fri, 27 May 2011 22:25:22 -0400 Subject: [PATCH] Implement better metadata sharing among types whilst they are being emitted. Implement correct Itanium pass-by-value and return-by-value semantics. --- src/Mono.VisualC.Interop/ABI/CppAbi.cs | 103 +++++++++++++----- .../ABI/Impl/ItaniumAbi.cs | 71 +++++++++++- src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs | 5 +- src/Mono.VisualC.Interop/CppLibrary.cs | 2 +- src/Mono.VisualC.Interop/CppTypeInfo.cs | 15 +++ 5 files changed, 162 insertions(+), 34 deletions(-) diff --git a/src/Mono.VisualC.Interop/ABI/CppAbi.cs b/src/Mono.VisualC.Interop/ABI/CppAbi.cs index 01fac00d..78e9643f 100644 --- a/src/Mono.VisualC.Interop/ABI/CppAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/CppAbi.cs @@ -22,7 +22,12 @@ namespace Mono.VisualC.Interop.ABI { //FIXME: Exception handling, operator overloading etc. //FIXME: Allow interface to override default calling convention + + // Subclasses should be singletons public abstract partial class CppAbi { + // (other part of this partial class in Attributes.cs) + + // These fields are specific to the class we happen to be implementing: protected ModuleBuilder impl_module; protected TypeBuilder impl_type; @@ -33,27 +38,31 @@ namespace Mono.VisualC.Interop.ABI { protected FieldBuilder typeinfo_field; protected ILGenerator ctor_il; + // These fields are specific to the ABI: + protected Dictionary wrapper_to_typeinfo = new Dictionary (); 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 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, + protected static readonly MethodInfo typeinfo_nativesize = typeof (CppTypeInfo).GetProperty ("NativeSize").GetGetMethod (); + protected static readonly MethodInfo typeinfo_vtable = typeof (CppTypeInfo).GetProperty ("VTable").GetGetMethod (); + protected static readonly MethodInfo typeinfo_adjvcall = typeof (CppTypeInfo).GetMethod ("GetAdjustedVirtualCall"); + protected static readonly MethodInfo typeinfo_fieldoffset = typeof (CppTypeInfo).GetProperty ("FieldOffsetPadding").GetGetMethod (); + protected static readonly MethodInfo vtable_initinstance = typeof (VTable).GetMethod ("InitInstance"); + protected static readonly MethodInfo vtable_resetinstance = typeof (VTable).GetMethod ("ResetInstance"); + protected static readonly MethodInfo cppobj_native = typeof (ICppObject).GetProperty ("Native").GetGetMethod (); + protected static readonly MethodInfo cppip_native = typeof (CppInstancePtr).GetProperty ("Native").GetGetMethod (); + protected static readonly MethodInfo cppip_managedalloc = typeof (CppInstancePtr).GetProperty ("IsManagedAlloc").GetGetMethod (); + protected static readonly MethodInfo cppip_getmanaged = typeof (CppInstancePtr).GetMethod ("GetManaged", BindingFlags.Static | BindingFlags.NonPublic); + protected static readonly ConstructorInfo cppip_fromnative = typeof (CppInstancePtr).GetConstructor (new Type [] { typeof (IntPtr) }); + protected static readonly ConstructorInfo cppip_fromsize = typeof (CppInstancePtr).GetConstructor (new Type [] { typeof (int) }); + protected 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"); + protected static readonly ConstructorInfo notimplementedexception = typeof (NotImplementedException).GetConstructor (new Type [] { typeof (string) }); + protected static readonly MethodInfo type_gettypefromhandle = typeof (Type).GetMethod ("GetTypeFromHandle"); + protected static readonly MethodInfo marshal_offsetof = typeof (Marshal).GetMethod ("OffsetOf"); + protected static readonly MethodInfo marshal_structuretoptr = typeof (Marshal).GetMethod ("StructureToPtr"); + protected static readonly ConstructorInfo dummytypeinfo_ctor = typeof (DummyCppTypeInfo).GetConstructor (Type.EmptyTypes); + protected static readonly MethodInfo dummytypeinfo_getbase = typeof (DummyCppTypeInfo).GetProperty ("BaseTypeInfo").GetGetMethod (); // These methods might be more commonly overridden for a given C++ ABI: @@ -86,13 +95,14 @@ namespace Mono.VisualC.Interop.ABI { private struct EmptyNativeLayout { } public Iface ImplementClass (Type wrapperType, CppLibrary lib, string className) + where Iface : ICppClass { return this.ImplementClass (wrapperType, lib, className); } public virtual Iface ImplementClass (Type wrapperType, CppLibrary lib, string className) where NLayout : struct - //where Iface : ICppClassInstantiatable or ICppClassOverridable + where Iface : ICppClass { this.impl_module = CppLibrary.interopModule; this.library = lib; @@ -105,7 +115,10 @@ namespace Mono.VisualC.Interop.ABI { var properties = GetProperties (); var methods = GetMethods ().Select (m => GetPInvokeSignature (m)); + var typeInfo = MakeTypeInfo (methods); + if (wrapperType != null) + wrapper_to_typeinfo.Add (wrapperType, typeInfo); // Implement all methods int vtableIndex = 0; @@ -555,15 +568,8 @@ namespace Mono.VisualC.Interop.ABI { 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; + var typeInfo = GetTypeInfo (t); + return typeInfo != null? typeInfo.NativeLayout : layout_type; } else { // by ref @@ -645,6 +651,9 @@ namespace Mono.VisualC.Interop.ABI { // The value it is marshaling will be on the stack. protected virtual void EmitInboundMarshal (ILGenerator il, Type nativeType, Type targetType) { + if (nativeType == typeof (void)) + return; // <- yes, this is necessary + var next = il.DefineLabel (); // marshal IntPtr -> ICppObject @@ -690,6 +699,46 @@ namespace Mono.VisualC.Interop.ABI { il.MarkLabel (next); } + // Gets a typeinfo for another ICppObject. + // Might return null for the type we're currently emitting. + protected virtual CppTypeInfo GetTypeInfo (Type otherWrapperType) + { + CppTypeInfo info; + if (wrapper_to_typeinfo.TryGetValue (otherWrapperType, out info)) + return info; + + if (otherWrapperType == wrapper_type) + return null; + + // pass a "dummy" type info to subclass ctor to trigger the creation of the real one + try { + Activator.CreateInstance (otherWrapperType, (CppTypeInfo)(new DummyCppTypeInfo ())); + + } catch (MissingMethodException mme) { + + throw new InvalidProgramException (string.Format ("Type `{0}' implements ICppObject but does not contain a public constructor that takes CppTypeInfo", otherWrapperType)); + } + + return wrapper_to_typeinfo [otherWrapperType]; + } + + // Does the above passthru at runtime. + // This is perhaps a kludgy/roundabout way for pulling the type info for another + // ICppObject at runtime, but I think this keeps the wrappers clean. + protected virtual void EmitGetTypeInfo (ILGenerator il, Type targetType) + { + // check for a subclass constructor (i.e. a public ctor in the wrapper that takes CppTypeInfo) + var ctor = targetType.GetConstructor (BindingFlags.ExactBinding | BindingFlags.Public | BindingFlags.Instance, null, new Type [] { typeof (CppTypeInfo) }, null); + if (ctor == null) + throw new InvalidProgramException (string.Format ("Type `{0}' implements ICppObject but does not contain a public constructor that takes CppTypeInfo", targetType)); + + il.Emit (OpCodes.Newobj, dummytypeinfo_ctor); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Newobj, ctor); + il.Emit (OpCodes.Pop); + il.Emit (OpCodes.Call, dummytypeinfo_getbase); + } + // Expects CppInstancePtr on stack. No null check performed protected virtual void EmitCreateCppObjectFromNative (ILGenerator il, Type targetType) { diff --git a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs index 71a1e119..e7686ea8 100644 --- a/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs @@ -39,8 +39,18 @@ using Mono.VisualC.Interop.Util; namespace Mono.VisualC.Interop.ABI { public class ItaniumAbi : CppAbi { - public ItaniumAbi () + public static readonly ItaniumAbi Instance = new ItaniumAbi (); + + private bool? hasNonDefaultCopyCtorOrDtor; + + private ItaniumAbi () + { + } + + public override Iface ImplementClass (Type wrapperType, CppLibrary lib, string className) { + hasNonDefaultCopyCtorOrDtor = null; + return base.ImplementClass (wrapperType, lib, className); } protected override CppTypeInfo MakeTypeInfo (IEnumerable methods) @@ -142,8 +152,8 @@ namespace Mono.VisualC.Interop.ABI { // they modify the type pointed to by pointer or reference. Choose.TopOne ( For.AllInputsIn (CppModifiers.Volatile, CppModifiers.Const).InAnyOrder ().After (ptrOrRef).Emit ("VK"), - For.AnyInputIn(CppModifiers.Volatile).After (ptrOrRef).Emit ("V"), - For.AnyInputIn(CppModifiers.Const).After (ptrOrRef).Emit ("K") + For.AnyInputIn (CppModifiers.Volatile).After (ptrOrRef).Emit ("V"), + For.AnyInputIn (CppModifiers.Const).After (ptrOrRef).Emit ("K") ) ); code.Append (string.Join(string.Empty, modifierCode.ToArray ())); @@ -168,8 +178,8 @@ namespace Mono.VisualC.Interop.ABI { else throw new NotImplementedException (); } else { - code.Append(mangleType.ElementTypeName.Length); - code.Append(mangleType.ElementTypeName); + code.Append (mangleType.ElementTypeName.Length); + code.Append (mangleType.ElementTypeName); } break; } @@ -178,6 +188,57 @@ namespace Mono.VisualC.Interop.ABI { return code.ToString (); } + // Section 3.1.4: + // Classes with non-default copy ctors/destructors are returned using a hidden + // argument + bool ReturnByHiddenArgument (MethodInfo method) + { + if (!IsByVal (method.ReturnTypeCustomAttributes)) + return false; + + if (hasNonDefaultCopyCtorOrDtor == null) + hasNonDefaultCopyCtorOrDtor = GetMethods ().Any (m => (IsCopyConstructor (m) || GetMethodType (m) == MethodType.NativeDtor) && !IsArtificial (m)); + + return hasNonDefaultCopyCtorOrDtor.Value; + } + + public override PInvokeSignature GetPInvokeSignature (MethodInfo method) + { + var psig = base.GetPInvokeSignature (method); + + if (ReturnByHiddenArgument (method)) { + psig.ParameterTypes.Insert (0, typeof (IntPtr)); + psig.ReturnType = typeof (void); + } + + return psig; + } + + protected override void EmitNativeCall (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) + { + var method = psig.OrigMethod; + LocalBuilder returnValue = null; + var hiddenReturnByValue = ReturnByHiddenArgument (method); + + if (hiddenReturnByValue) + { + returnValue = il.DeclareLocal (typeof (CppInstancePtr)); + + EmitGetTypeInfo (il, method.ReturnType); + il.Emit (OpCodes.Call, typeinfo_nativesize); + il.Emit (OpCodes.Newobj, cppip_fromsize); + il.Emit (OpCodes.Stloc, returnValue); + il.Emit (OpCodes.Ldloca, returnValue); + il.Emit (OpCodes.Call, cppip_native); + } + + base.EmitNativeCall (il, nativeMethod, psig, nativePtr); + + if (hiddenReturnByValue) { + il.Emit (OpCodes.Ldloc, returnValue); + EmitCreateCppObjectFromNative (il, method.ReturnType); + } + } } diff --git a/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs b/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs index 483194d4..f7c1f230 100644 --- a/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs +++ b/src/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs @@ -40,7 +40,10 @@ namespace Mono.VisualC.Interop.ABI { // FIXME: No 64-bit support public class MsvcAbi : CppAbi { - public MsvcAbi () + + public static readonly MsvcAbi Instance = new MsvcAbi (); + + private MsvcAbi () { } diff --git a/src/Mono.VisualC.Interop/CppLibrary.cs b/src/Mono.VisualC.Interop/CppLibrary.cs index 0ca55a35..b115f31d 100644 --- a/src/Mono.VisualC.Interop/CppLibrary.cs +++ b/src/Mono.VisualC.Interop/CppLibrary.cs @@ -77,7 +77,7 @@ namespace Mono.VisualC.Interop { } public CppLibrary (string name, InlineMethods inlinePolicy) - : this (name, new ItaniumAbi (), inlinePolicy) + : this (name, ItaniumAbi.Instance, inlinePolicy) { //FIXME: Ideally we would auto-detect ABI here. } diff --git a/src/Mono.VisualC.Interop/CppTypeInfo.cs b/src/Mono.VisualC.Interop/CppTypeInfo.cs index 91957139..8c35d6b6 100644 --- a/src/Mono.VisualC.Interop/CppTypeInfo.cs +++ b/src/Mono.VisualC.Interop/CppTypeInfo.cs @@ -95,6 +95,10 @@ namespace Mono.VisualC.Interop { lazy_vtable = null; } + protected CppTypeInfo () + { + } + public virtual void AddBase (CppTypeInfo baseType) { @@ -211,5 +215,16 @@ namespace Mono.VisualC.Interop { } } + + // This is used internally by CppAbi: + internal class DummyCppTypeInfo : CppTypeInfo { + + public CppTypeInfo BaseTypeInfo { get; set; } + + public override void AddBase (CppTypeInfo baseType) + { + BaseTypeInfo = baseType; + } + } }