From abbcad5411c63f30b9541233577108e99b51dc7d Mon Sep 17 00:00:00 2001 From: Alexander Corrado Date: Fri, 22 Jul 2011 02:52:05 -0400 Subject: [PATCH] Refactor CppAbi to move fields related to the type being emitted into CppTypeInfo --- src/Mono.Cxxi/Abi/CppAbi.cs | 286 +++++++++++----------- src/Mono.Cxxi/Abi/EmitInfo.cs | 12 + src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs | 59 ++--- src/Mono.Cxxi/Abi/Impl/ItaniumTypeInfo.cs | 7 +- src/Mono.Cxxi/Abi/Impl/MsvcAbi.cs | 11 +- src/Mono.Cxxi/Abi/Impl/VirtualOnlyAbi.cs | 6 +- src/Mono.Cxxi/CppInstancePtr.cs | 8 +- src/Mono.Cxxi/CppLibrary.cs | 9 +- src/Mono.Cxxi/CppTypeInfo.cs | 57 +++-- src/Mono.Cxxi/Makefile.am | 1 + src/Mono.Cxxi/Mono.Cxxi.csproj | 1 + 11 files changed, 242 insertions(+), 215 deletions(-) create mode 100644 src/Mono.Cxxi/Abi/EmitInfo.cs diff --git a/src/Mono.Cxxi/Abi/CppAbi.cs b/src/Mono.Cxxi/Abi/CppAbi.cs index 443dbb46..b1677c43 100644 --- a/src/Mono.Cxxi/Abi/CppAbi.cs +++ b/src/Mono.Cxxi/Abi/CppAbi.cs @@ -45,18 +45,6 @@ namespace Mono.Cxxi.Abi { 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; - - protected Type interface_type, layout_type, wrapper_type; - protected CppLibrary library; - protected string class_name; - - 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; @@ -80,15 +68,14 @@ namespace Mono.Cxxi.Abi { 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 (); + protected static readonly MethodInfo marshal_ptrtostructure = typeof (Marshal).GetMethod ("PtrToStructure", BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (IntPtr), typeof (Type) }, null); protected static readonly FieldInfo intptr_zero = typeof (IntPtr).GetField ("Zero"); // These methods might be more commonly overridden for a given C++ ABI: - public virtual MethodType GetMethodType (MethodInfo imethod) + public virtual MethodType GetMethodType (CppTypeInfo typeInfo, MethodInfo imethod) { - if (IsInline (imethod) && library.InlineMethodPolicy == InlineMethods.NotPresent) + if (IsInline (imethod) && typeInfo.Library.InlineMethodPolicy == InlineMethods.NotPresent) return MethodType.NotImplemented; else if (imethod.IsDefined (typeof (ConstructorAttribute), false)) return MethodType.NativeCtor; @@ -103,83 +90,63 @@ namespace Mono.Cxxi.Abi { // The members below must be implemented for a given C++ ABI: public abstract CallingConvention? GetCallingConvention (MethodInfo methodInfo); - protected abstract string GetMangledMethodName (MethodInfo methodInfo); - public string GetMangledMethodName (string className, MethodInfo methodInfo) - { - class_name = className; - return GetMangledMethodName (methodInfo); - } + protected abstract string GetMangledMethodName (CppTypeInfo typeInfo, MethodInfo methodInfo); - // The ImplementClass overrides are the main entry point to the Abi API: + // --------------------------------- 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 : ICppClass + public virtual ICppClass ImplementClass (CppTypeInfo typeInfo) { - 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 (); + if (typeInfo.WrapperType == null || !wrapper_to_typeinfo.ContainsKey (typeInfo.WrapperType)) { - var properties = GetProperties (); - var methods = GetMethods ().Select (m => GetPInvokeSignature (m)); + if (typeInfo.WrapperType != null) + wrapper_to_typeinfo.Add (typeInfo.WrapperType, typeInfo); - var typeInfo = MakeTypeInfo (methods); - if (wrapperType != null) - wrapper_to_typeinfo.Add (wrapperType, typeInfo); + DefineImplType (typeInfo); - // Implement all methods - int vtableIndex = 0; - foreach (var method in methods) - DefineMethod (method, typeInfo, ref vtableIndex); + var properties = GetProperties (typeInfo.InterfaceType); + var methods = GetMethods (typeInfo.InterfaceType).Select (m => GetPInvokeSignature (typeInfo, m)); + + // Implement all methods + int vtableIndex = 0; + foreach (var method in methods) + DefineMethod (typeInfo, method, ref vtableIndex); - // Implement all properties - foreach (var property in properties) - DefineProperty (property); + // Implement all properties + foreach (var property in properties) + DefineProperty (typeInfo, property); - ctor_il.Emit (OpCodes.Ret); + typeInfo.emit_info.ctor_il.Emit (OpCodes.Ret); + return (ICppClass)Activator.CreateInstance (typeInfo.emit_info.type_builder.CreateType (), typeInfo); + } - return (Iface)Activator.CreateInstance (impl_type.CreateType (), typeInfo); + throw new InvalidOperationException ("This type has already been implemented"); } - protected virtual CppTypeInfo MakeTypeInfo (IEnumerable methods) + public virtual CppTypeInfo MakeTypeInfo (CppLibrary lib, string typeName, Type interfaceType, Type layoutType/*?*/, Type/*?*/ wrapperType) { - return new CppTypeInfo (this, methods.Where (m => IsVirtual (m.OrigMethod)), layout_type, wrapper_type); + Debug.Assert (lib.Abi == this); + return new CppTypeInfo (lib, typeName, interfaceType, layoutType ?? typeof (EmptyNativeLayout), wrapperType); } - protected virtual IEnumerable GetProperties () + public virtual IEnumerable GetVirtualMethodSlots (CppTypeInfo typeInfo, Type interfaceType) { - 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 - ); + return from m in GetMethods (interfaceType) + where IsVirtual (m) + select GetPInvokeSignature (typeInfo, m); } - protected virtual IEnumerable GetMethods () + protected virtual IEnumerable GetMethods (Type interfaceType) { // get all methods defined on inherited interfaces first var methods = ( - from iface in interface_type.GetInterfaces () + from iface in interfaceType.GetInterfaces () from method in iface.GetMethods () where !method.IsSpecialName select method ).Concat ( - from method in interface_type.GetMethods () + from method in interfaceType.GetMethods () where !method.IsSpecialName orderby method.MetadataToken select method @@ -188,32 +155,52 @@ namespace Mono.Cxxi.Abi { return methods; } - protected virtual void DefineImplType () + protected virtual IEnumerable GetProperties (Type interfaceType) + { + return ( // get all properties defined on the interface + from property in interfaceType.GetProperties () + select property + ).Union ( // ... as well as those defined on inherited interfaces + from iface in interfaceType.GetInterfaces () + from property in iface.GetProperties () + select property + ); + } + + protected virtual void DefineImplType (CppTypeInfo typeInfo) { - 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); + string implTypeName = typeInfo.InterfaceType.Name + "_"; + if (typeInfo.NativeLayout != null) + implTypeName += typeInfo.NativeLayout.Name + "_"; + implTypeName += this.GetType ().Name + "_Impl"; - typeinfo_field = impl_type.DefineField ("_typeInfo", typeof (CppTypeInfo), FieldAttributes.InitOnly | FieldAttributes.Private); + var impl_type = CppLibrary.interopModule.DefineType (implTypeName, TypeAttributes.Class | TypeAttributes.Sealed); + impl_type.AddInterfaceImplementation (typeInfo.InterfaceType); + + var typeinfo_field = impl_type.DefineField ("_typeInfo", typeof (CppTypeInfo), FieldAttributes.InitOnly | FieldAttributes.Private); ConstructorBuilder ctor = impl_type.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof (CppTypeInfo) }); - ctor_il = ctor.GetILGenerator (); + var ctor_il = ctor.GetILGenerator (); // this._typeInfo = (CppTypeInfo passed to constructor) ctor_il.Emit (OpCodes.Ldarg_0); ctor_il.Emit (OpCodes.Ldarg_1); ctor_il.Emit (OpCodes.Stfld, typeinfo_field); + + typeInfo.emit_info.ctor_il = ctor_il; + typeInfo.emit_info.typeinfo_field = typeinfo_field; + typeInfo.emit_info.type_builder = impl_type; } - protected virtual MethodBuilder DefineMethod (PInvokeSignature psig, CppTypeInfo typeInfo, ref int vtableIndex) + protected virtual MethodBuilder DefineMethod (CppTypeInfo typeInfo, PInvokeSignature psig, ref int vtableIndex) { var interfaceMethod = psig.OrigMethod; // 1. Generate managed trampoline to call native method - MethodBuilder trampoline = GetMethodBuilder (interfaceMethod); - ILGenerator il = trampoline.GetILGenerator (); + var trampoline = GetMethodBuilder (typeInfo, interfaceMethod); + var il = typeInfo.emit_info.current_il = trampoline.GetILGenerator (); switch (psig.Type) { @@ -231,12 +218,12 @@ namespace Mono.Cxxi.Abi { return trampoline; case MethodType.ManagedAlloc: - EmitManagedAlloc (il, interfaceMethod); + EmitManagedAlloc (typeInfo, interfaceMethod); il.Emit (OpCodes.Ret); return trampoline; } - bool isStatic = IsStatic (interfaceMethod); + var isStatic = IsStatic (interfaceMethod); LocalBuilder cppInstancePtr = null; LocalBuilder nativePtr = null; @@ -256,23 +243,23 @@ namespace Mono.Cxxi.Abi { MethodInfo nativeMethod; if (IsVirtual (interfaceMethod) && psig.Type != MethodType.NativeDtor) { - nativeMethod = EmitPrepareVirtualCall (il, typeInfo, cppInstancePtr, vtableIndex++); + nativeMethod = EmitPrepareVirtualCall (typeInfo, cppInstancePtr, vtableIndex++); } else { if (IsVirtual (interfaceMethod)) vtableIndex++; - nativeMethod = GetPInvokeForMethod (psig); + nativeMethod = GetPInvokeForMethod (typeInfo, psig); } switch (psig.Type) { case MethodType.NativeCtor: - EmitConstruct (il, nativeMethod, psig, cppInstancePtr, nativePtr); + EmitConstruct (typeInfo, nativeMethod, psig, cppInstancePtr, nativePtr); break; case MethodType.NativeDtor: - EmitDestruct (il, nativeMethod, psig, cppInstancePtr, nativePtr); + EmitDestruct (typeInfo, nativeMethod, psig, cppInstancePtr, nativePtr); break; default: - EmitNativeCall (il, nativeMethod, psig, nativePtr); + EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); break; } @@ -280,7 +267,7 @@ namespace Mono.Cxxi.Abi { return trampoline; } - protected virtual PropertyBuilder DefineProperty (PropertyInfo property) + protected virtual PropertyBuilder DefineProperty (CppTypeInfo typeInfo, PropertyInfo property) { if (property.CanWrite) throw new InvalidProgramException ("Properties in C++ interface must be read-only."); @@ -290,17 +277,17 @@ namespace Mono.Cxxi.Abi { var propName = property.Name; var retType = imethod.ReturnType; - var fieldProp = impl_type.DefineProperty (propName, PropertyAttributes.None, retType, Type.EmptyTypes); + var fieldProp = typeInfo.emit_info.type_builder.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 fieldGetter = typeInfo.emit_info.type_builder.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 - var fieldData = impl_type.DefineField ("__" + propName + "_Data", retType, FieldAttributes.InitOnly | FieldAttributes.Private); + var fieldData = typeInfo.emit_info.type_builder.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) @@ -320,13 +307,13 @@ namespace Mono.Cxxi.Abi { // first, get field offset // = ((int)Marshal.OffsetOf (layout_type, propName)) + FieldOffsetPadding; - il.Emit(OpCodes.Ldtoken, layout_type); + il.Emit(OpCodes.Ldtoken, typeInfo.NativeLayout); il.Emit(OpCodes.Call, type_gettypefromhandle); il.Emit(OpCodes.Ldstr, propName); il.Emit(OpCodes.Call, marshal_offsetof); il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, typeinfo_field); + il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); il.Emit (OpCodes.Callvirt, typeinfo_fieldoffset); il.Emit (OpCodes.Add); @@ -342,7 +329,7 @@ namespace Mono.Cxxi.Abi { } else if (retType.Equals (typeof (CppTypeInfo))) { il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, typeinfo_field); + il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); il.Emit (OpCodes.Ret); } else throw new InvalidProgramException ("Properties in C++ interface can only be of type CppField."); @@ -381,7 +368,7 @@ namespace Mono.Cxxi.Abi { nativeArgs, typeof (CppInstancePtr).Module, true); ReflectionHelper.ApplyMethodParameterAttributes (interfaceMethod, trampolineIn, true); - ILGenerator il = trampolineIn.GetILGenerator (); + var il = trampolineIn.GetILGenerator (); // for static (target) methods: OpCode callInstruction = OpCodes.Call; @@ -430,11 +417,11 @@ namespace Mono.Cxxi.Abi { /** * Defines a new MethodBuilder with the same signature as the passed MethodInfo */ - protected virtual MethodBuilder GetMethodBuilder (MethodInfo interfaceMethod) + protected virtual MethodBuilder GetMethodBuilder (CppTypeInfo typeInfo, MethodInfo interfaceMethod) { - Type [] parameterTypes = ReflectionHelper.GetMethodParameterTypes (interfaceMethod); - MethodBuilder methodBuilder = impl_type.DefineMethod (interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, - interfaceMethod.ReturnType, parameterTypes); + var parameterTypes = ReflectionHelper.GetMethodParameterTypes (interfaceMethod); + var methodBuilder = typeInfo.emit_info.type_builder.DefineMethod (interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, + interfaceMethod.ReturnType, parameterTypes); ReflectionHelper.ApplyMethodParameterAttributes (interfaceMethod, methodBuilder, false); return methodBuilder; } @@ -442,22 +429,22 @@ namespace Mono.Cxxi.Abi { /** * Defines a new MethodBuilder that calls the specified C++ (non-virtual) method using its mangled name */ - protected virtual MethodBuilder GetPInvokeForMethod (PInvokeSignature sig) + protected virtual MethodBuilder GetPInvokeForMethod (CppTypeInfo typeInfo, PInvokeSignature sig) { - string entryPoint = sig.Name; + var entryPoint = sig.Name; if (entryPoint == null) throw new NotSupportedException ("Could not mangle method name."); string lib; - if (IsInline (sig.OrigMethod) && library.InlineMethodPolicy == InlineMethods.SurrogateLib) - lib = library.Name + "-inline"; + if (IsInline (sig.OrigMethod) && typeInfo.Library.InlineMethodPolicy == InlineMethods.SurrogateLib) + lib = typeInfo.Library.Name + "-inline"; else - lib = library.Name; + lib = typeInfo.Library.Name; - 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); + var builder = typeInfo.emit_info.type_builder.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 (sig.OrigMethod, builder, true); return builder; @@ -467,15 +454,15 @@ namespace Mono.Cxxi.Abi { * Emits the IL to load the correct delegate instance and/or retrieve the MethodInfo from the VTable * for a C++ virtual call. */ - protected virtual MethodInfo EmitPrepareVirtualCall (ILGenerator il, CppTypeInfo typeInfo, - LocalBuilder cppInstancePtr, int vtableIndex) + protected virtual MethodInfo EmitPrepareVirtualCall (CppTypeInfo typeInfo, LocalBuilder cppInstancePtr, int vtableIndex) { - Type vtableDelegateType = typeInfo.VTableDelegateTypes [vtableIndex]; - MethodInfo getDelegate = typeinfo_adjvcall.MakeGenericMethod (vtableDelegateType); + var il = typeInfo.emit_info.current_il; + var vtableDelegateType = typeInfo.VTableDelegateTypes [vtableIndex]; + var getDelegate = typeinfo_adjvcall.MakeGenericMethod (vtableDelegateType); // this._typeInfo.GetAdjustedVirtualCall (cppInstancePtr, vtableIndex); il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, typeinfo_field); + il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); il.Emit (OpCodes.Ldloc_S, cppInstancePtr); il.Emit (OpCodes.Ldc_I4, vtableIndex); il.Emit (OpCodes.Callvirt, getDelegate); @@ -487,13 +474,15 @@ namespace Mono.Cxxi.Abi { * 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) + protected virtual void EmitManagedAlloc (CppTypeInfo typeInfo, MethodInfo interfaceMethod) { + var il = typeInfo.emit_info.current_il; + // this._typeInfo.NativeSize il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, typeinfo_field); + il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); - if (wrapper_type != null && interfaceMethod.GetParameters ().Any ()) { + if (typeInfo.WrapperType != null && interfaceMethod.GetParameters ().Any ()) { // load managed wrapper il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Newobj, cppip_fromtype_managed); @@ -503,14 +492,16 @@ namespace Mono.Cxxi.Abi { } } - protected virtual void EmitConstruct (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, + protected virtual void EmitConstruct (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder cppInstancePtr, LocalBuilder nativePtr) { Debug.Assert (psig.Type == MethodType.NativeCtor); - EmitNativeCall (il, nativeMethod, psig, nativePtr); + var il = typeInfo.emit_info.current_il; + + EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); if (cppInstancePtr != null && psig.OrigMethod.ReturnType == typeof (CppInstancePtr)) { - EmitInitVTable (il, cppInstancePtr); + EmitInitVTable (typeInfo, cppInstancePtr); il.Emit (OpCodes.Ldloc_S, cppInstancePtr); } else if (psig.OrigMethod.DeclaringType.GetInterfaces ().Any (i => i.IsGenericType && i.GetGenericTypeDefinition () == typeof (ICppClassOverridable<>))) { @@ -518,10 +509,11 @@ namespace Mono.Cxxi.Abi { } } - protected virtual void EmitDestruct (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, + protected virtual void EmitDestruct (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder cppInstancePtr, LocalBuilder nativePtr) { Debug.Assert (psig.Type == MethodType.NativeDtor); + var il = typeInfo.emit_info.current_il; // we don't do anything if the object wasn't managed alloc if (cppInstancePtr == null) @@ -529,8 +521,8 @@ namespace Mono.Cxxi.Abi { EmitCheckManagedAlloc (il, cppInstancePtr); - EmitResetVTable (il, cppInstancePtr); - EmitNativeCall (il, nativeMethod, psig, nativePtr); + EmitResetVTable (typeInfo, cppInstancePtr); + EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); } /** @@ -538,8 +530,9 @@ namespace Mono.Cxxi.Abi { * GetPInvokeForMethod or the MethodInfo of a vtable method. * To complete method, emit OpCodes.Ret. */ - protected virtual void EmitNativeCall (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) + protected virtual void EmitNativeCall (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { + var il = typeInfo.emit_info.current_il; var interfaceMethod = psig.OrigMethod; var interfaceArgs = interfaceMethod.GetParameters (); @@ -564,9 +557,9 @@ namespace Mono.Cxxi.Abi { } - public virtual PInvokeSignature GetPInvokeSignature (MethodInfo method) + public virtual PInvokeSignature GetPInvokeSignature (CppTypeInfo typeInfo, MethodInfo method) { - var methodType = GetMethodType (method); + var methodType = GetMethodType (typeInfo, method); var parameters = method.GetParameters (); var pinvokeTypes = new List (parameters.Length); @@ -576,7 +569,7 @@ namespace Mono.Cxxi.Abi { return new PInvokeSignature { OrigMethod = method, - Name = GetMangledMethodName (method), + Name = GetMangledMethodName (typeInfo, method), Type = methodType, CallingConvention = GetCallingConvention (method), ParameterTypes = pinvokeTypes, @@ -594,8 +587,7 @@ namespace Mono.Cxxi.Abi { if (IsByVal (icap)) { - var typeInfo = GetTypeInfo (t); - return typeInfo != null? typeInfo.NativeLayout : layout_type; + return GetTypeInfo (t).NativeLayout; } else { // by ref @@ -731,16 +723,12 @@ namespace Mono.Cxxi.Abi { } // 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 ())); @@ -804,11 +792,13 @@ namespace Mono.Cxxi.Abi { /** * Emits IL to load the VTable object onto the stack. */ - protected virtual void EmitLoadVTable (ILGenerator il) + protected virtual void EmitLoadVTable (CppTypeInfo typeInfo) { + var il = typeInfo.emit_info.current_il; + // this._typeInfo.VTable il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Ldfld, typeinfo_field); + il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); il.Emit (OpCodes.Callvirt, typeinfo_vtable); } @@ -816,20 +806,24 @@ namespace Mono.Cxxi.Abi { * 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 cppip) + protected virtual void EmitInitVTable (CppTypeInfo typeInfo, LocalBuilder cppip) { + var il = typeInfo.emit_info.current_il; + // this._typeInfo.VTable.InitInstance (cppInstancePtr); - EmitLoadVTable (il); + EmitLoadVTable (typeInfo); il.Emit (OpCodes.Ldloca_S, cppip); - EmitCallVTableMethod (il, vtable_initinstance, 2, false); + EmitCallVTableMethod (typeInfo, vtable_initinstance, 2, false); } - protected virtual void EmitResetVTable (ILGenerator il, LocalBuilder cppip) + protected virtual void EmitResetVTable (CppTypeInfo typeInfo, LocalBuilder cppip) { + var il = typeInfo.emit_info.current_il; + // this._typeInfo.VTable.ResetInstance (cppInstancePtr); - EmitLoadVTable (il); + EmitLoadVTable (typeInfo); il.Emit (OpCodes.Ldloc_S, cppip); - EmitCallVTableMethod (il, vtable_resetinstance, 2, false); + EmitCallVTableMethod (typeInfo, vtable_resetinstance, 2, false); } /** @@ -840,14 +834,15 @@ namespace Mono.Cxxi.Abi { * 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 EmitCallVTableMethod (ILGenerator il, MethodInfo method, int stackHeight, - bool throwOnNoVTable) + protected virtual void EmitCallVTableMethod (CppTypeInfo typeInfo, MethodInfo method, int stackHeight, bool throwOnNoVTable) { + var il = typeInfo.emit_info.current_il; + // prepare a jump; do not call vtable method if no vtable - Label noVirt = il.DefineLabel (); - Label dontPushOrThrow = il.DefineLabel (); + var noVirt = il.DefineLabel (); + var dontPushOrThrow = il.DefineLabel (); - EmitLoadVTable (il); + EmitLoadVTable (typeInfo); il.Emit (OpCodes.Brfalse_S, noVirt); // if (vtableInfo == null) goto noVirt il.Emit (OpCodes.Callvirt, method); // call method @@ -903,7 +898,8 @@ namespace Mono.Cxxi.Abi { { // make sure we were allocated by managed code // if not, return - Label managedAlloc = il.DefineLabel (); + var managedAlloc = il.DefineLabel (); + il.Emit (OpCodes.Ldloca_S, cppip); il.Emit (OpCodes.Call, cppip_managedalloc); il.Emit (OpCodes.Brtrue_S, managedAlloc); @@ -917,9 +913,11 @@ namespace Mono.Cxxi.Abi { */ protected virtual void EmitCheckDisposed (ILGenerator il, LocalBuilder native, MethodType methodType) { - Label validRef = il.DefineLabel (); + var 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); diff --git a/src/Mono.Cxxi/Abi/EmitInfo.cs b/src/Mono.Cxxi/Abi/EmitInfo.cs new file mode 100644 index 00000000..3e3cda95 --- /dev/null +++ b/src/Mono.Cxxi/Abi/EmitInfo.cs @@ -0,0 +1,12 @@ +using System; +using System.Reflection.Emit; + +namespace Mono.Cxxi.Abi { + + public class EmitInfo { + public TypeBuilder type_builder; + public FieldBuilder typeinfo_field; + public ILGenerator ctor_il, current_il; + } +} + diff --git a/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs b/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs index d88ae71f..6d7ccb12 100644 --- a/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs +++ b/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs @@ -41,26 +41,18 @@ namespace Mono.Cxxi.Abi { 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) + public override CppTypeInfo MakeTypeInfo (CppLibrary lib, string typeName, Type interfaceType, Type layoutType/*?*/, Type/*?*/ wrapperType) { - return new ItaniumTypeInfo (this, GetVirtualMethodSlots (methods), layout_type, wrapper_type); + return new ItaniumTypeInfo (lib, typeName, interfaceType, layoutType, wrapperType); } - private IEnumerable GetVirtualMethodSlots (IEnumerable methods) + public override IEnumerable GetVirtualMethodSlots (CppTypeInfo typeInfo, Type interfaceType) { - foreach (var method in methods) { + foreach (var method in base.GetVirtualMethodSlots (typeInfo, interfaceType)) { if (!IsVirtual (method.OrigMethod)) continue; @@ -72,9 +64,9 @@ namespace Mono.Cxxi.Abi { } } - protected override MethodBuilder DefineMethod (PInvokeSignature sig, CppTypeInfo typeInfo, ref int vtableIndex) + protected override MethodBuilder DefineMethod (CppTypeInfo typeInfo, PInvokeSignature sig, ref int vtableIndex) { - var builder = base.DefineMethod (sig, typeInfo, ref vtableIndex); + var builder = base.DefineMethod (typeInfo, sig, 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) @@ -88,12 +80,13 @@ namespace Mono.Cxxi.Abi { return CallingConvention.Cdecl; } - protected override string GetMangledMethodName (MethodInfo methodInfo) + protected override string GetMangledMethodName (CppTypeInfo typeInfo, MethodInfo methodInfo) { var compressMap = new Dictionary (); + var methodName = methodInfo.Name; + var className = typeInfo.TypeName; - string methodName = methodInfo.Name; - MethodType methodType = GetMethodType (methodInfo); + MethodType methodType = GetMethodType (typeInfo, methodInfo); ParameterInfo [] parameters = methodInfo.GetParameters (); StringBuilder nm = new StringBuilder ("_ZN", 30); @@ -101,8 +94,8 @@ namespace Mono.Cxxi.Abi { if (IsConst (methodInfo)) nm.Append ('K'); - nm.Append (class_name.Length).Append (class_name); - compressMap [class_name] = compressMap.Count; + nm.Append (className.Length).Append (className); + compressMap [className] = compressMap.Count; // FIXME: Implement compression completely @@ -191,22 +184,27 @@ namespace Mono.Cxxi.Abi { // Section 3.1.4: // Classes with non-default copy ctors/destructors are returned using a hidden // argument - bool ReturnByHiddenArgument (MethodInfo method) + bool ReturnByHiddenArgument (CppTypeInfo typeInfo, MethodInfo method) { + var iti = (ItaniumTypeInfo)typeInfo; + if (!IsByVal (method.ReturnTypeCustomAttributes)) return false; - if (hasNonDefaultCopyCtorOrDtor == null) - hasNonDefaultCopyCtorOrDtor = GetMethods ().Any (m => (IsCopyConstructor (m) || GetMethodType (m) == MethodType.NativeDtor) && !IsArtificial (m)); + if (iti.has_non_default_copy_ctor_or_dtor == null) + iti.has_non_default_copy_ctor_or_dtor = GetMethods (typeInfo.InterfaceType) + .Any (m => (IsCopyConstructor (m) || + GetMethodType (typeInfo, m) == MethodType.NativeDtor) && + !IsArtificial (m)); - return hasNonDefaultCopyCtorOrDtor.Value; + return iti.has_non_default_copy_ctor_or_dtor.Value; } - public override PInvokeSignature GetPInvokeSignature (MethodInfo method) + public override PInvokeSignature GetPInvokeSignature (CppTypeInfo/*?*/ typeInfo, MethodInfo method) { - var psig = base.GetPInvokeSignature (method); + var psig = base.GetPInvokeSignature (typeInfo, method); - if (ReturnByHiddenArgument (method)) { + if (ReturnByHiddenArgument (typeInfo, method)) { psig.ParameterTypes.Insert (0, typeof (IntPtr)); psig.ReturnType = typeof (void); } @@ -214,11 +212,13 @@ namespace Mono.Cxxi.Abi { return psig; } - protected override void EmitNativeCall (ILGenerator il, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) + protected override void EmitNativeCall (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { + var il = typeInfo.emit_info.current_il; var method = psig.OrigMethod; + var hiddenReturnByValue = ReturnByHiddenArgument (typeInfo, method); + LocalBuilder returnValue = null; - var hiddenReturnByValue = ReturnByHiddenArgument (method); if (hiddenReturnByValue) { @@ -226,13 +226,14 @@ namespace Mono.Cxxi.Abi { 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); + base.EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); if (hiddenReturnByValue) { EmitCreateCppObjectFromNative (il, method.ReturnType, returnValue); diff --git a/src/Mono.Cxxi/Abi/Impl/ItaniumTypeInfo.cs b/src/Mono.Cxxi/Abi/Impl/ItaniumTypeInfo.cs index 3962d0ab..fcf475b9 100644 --- a/src/Mono.Cxxi/Abi/Impl/ItaniumTypeInfo.cs +++ b/src/Mono.Cxxi/Abi/Impl/ItaniumTypeInfo.cs @@ -36,8 +36,11 @@ using Mono.Cxxi.Util; namespace Mono.Cxxi.Abi { public class ItaniumTypeInfo : CppTypeInfo { - public ItaniumTypeInfo (ItaniumAbi abi, IEnumerable virtualMethods, Type nativeLayout, Type wrapperType) - : base (abi, virtualMethods, nativeLayout, wrapperType) + + protected internal bool? has_non_default_copy_ctor_or_dtor; + + public ItaniumTypeInfo (CppLibrary lib, string typeName, Type interfaceType, Type nativeLayout, Type/*?*/ wrapperType) + : base (lib, typeName, interfaceType, nativeLayout, wrapperType) { } diff --git a/src/Mono.Cxxi/Abi/Impl/MsvcAbi.cs b/src/Mono.Cxxi/Abi/Impl/MsvcAbi.cs index 85883e26..871775ff 100644 --- a/src/Mono.Cxxi/Abi/Impl/MsvcAbi.cs +++ b/src/Mono.Cxxi/Abi/Impl/MsvcAbi.cs @@ -47,11 +47,6 @@ namespace Mono.Cxxi.Abi { { } - protected override CppTypeInfo MakeTypeInfo (IEnumerable methods) - { - return new CppTypeInfo (this, methods.Where (m => IsVirtual (m.OrigMethod)), layout_type, wrapper_type); - } - public override CallingConvention? GetCallingConvention (MethodInfo methodInfo) { // FIXME: Varargs methods ... ? @@ -62,10 +57,10 @@ namespace Mono.Cxxi.Abi { return CallingConvention.ThisCall; } - protected override string GetMangledMethodName (MethodInfo methodInfo) + protected override string GetMangledMethodName (CppTypeInfo typeInfo, MethodInfo methodInfo) { string methodName = methodInfo.Name; - MethodType methodType = GetMethodType (methodInfo); + MethodType methodType = GetMethodType (typeInfo, methodInfo); ParameterInfo [] parameters = methodInfo.GetParameters (); StringBuilder nm = new StringBuilder ("?", 30); @@ -79,7 +74,7 @@ namespace Mono.Cxxi.Abi { // FIXME: This has to include not only the name of the immediate containing class, // but also all names of containing classes and namespaces up the hierarchy. - nm.Append (class_name).Append ("@@"); + nm.Append (typeInfo.TypeName).Append ("@@"); // function modifiers are a matrix of consecutive uppercase letters // depending on access type and virtual (far)/static (far)/far modifiers diff --git a/src/Mono.Cxxi/Abi/Impl/VirtualOnlyAbi.cs b/src/Mono.Cxxi/Abi/Impl/VirtualOnlyAbi.cs index f6cf7d8f..f04aa166 100644 --- a/src/Mono.Cxxi/Abi/Impl/VirtualOnlyAbi.cs +++ b/src/Mono.Cxxi/Abi/Impl/VirtualOnlyAbi.cs @@ -41,15 +41,15 @@ namespace Mono.Cxxi.Abi { } public VirtualOnlyAbi () { } - public override MethodType GetMethodType (MethodInfo imethod) + public override MethodType GetMethodType (CppTypeInfo typeInfo, MethodInfo imethod) { - MethodType defaultType = base.GetMethodType (imethod); + MethodType defaultType = base.GetMethodType (typeInfo, imethod); if (defaultType == MethodType.NativeCtor || defaultType == MethodType.NativeDtor) return MethodType.NoOp; return defaultType; } - protected override string GetMangledMethodName (MethodInfo methodInfo) + protected override string GetMangledMethodName (CppTypeInfo typeInfo, MethodInfo methodInfo) { throw new NotSupportedException ("Name mangling is not supported by this class. All C++ interface methods must be declared virtual."); } diff --git a/src/Mono.Cxxi/CppInstancePtr.cs b/src/Mono.Cxxi/CppInstancePtr.cs index 4bb8a92e..dd3e8403 100644 --- a/src/Mono.Cxxi/CppInstancePtr.cs +++ b/src/Mono.Cxxi/CppInstancePtr.cs @@ -56,9 +56,11 @@ namespace Mono.Cxxi { if (!implCache.TryGetValue (typeof (Iface), out cachedImpl)) { - VirtualOnlyAbi virtualABI = new VirtualOnlyAbi (VTable.BindToSignature); - impl = virtualABI.ImplementClass (typeof (TWrapper), new CppLibrary (string.Empty), string.Empty); - implCache.Add (typeof (Iface), impl); + // FIXME: fix this? or not... + //VirtualOnlyAbi virtualABI = new VirtualOnlyAbi (VTable.BindToSignature); + //impl = virtualABI.ImplementClass (typeof (TWrapper), new CppLibrary (string.Empty), string.Empty); + //implCache.Add (typeof (Iface), impl); + throw new NotImplementedException (); } else impl = (Iface)cachedImpl; diff --git a/src/Mono.Cxxi/CppLibrary.cs b/src/Mono.Cxxi/CppLibrary.cs index 9cdc6e69..ff9db81c 100644 --- a/src/Mono.Cxxi/CppLibrary.cs +++ b/src/Mono.Cxxi/CppLibrary.cs @@ -105,7 +105,8 @@ namespace Mono.Cxxi { public Iface GetClass (string className) where Iface : ICppClass { - return Abi.ImplementClass (null, this, className); + var typeInfo = Abi.MakeTypeInfo (this, className, typeof (Iface), null, null); + return (Iface)Abi.ImplementClass (typeInfo); } // For instantiating or working with a class that may have fields @@ -114,7 +115,8 @@ namespace Mono.Cxxi { where Iface : ICppClassInstantiatable where NativeLayout : struct { - return Abi.ImplementClass (null, this, className); + var typeInfo = Abi.MakeTypeInfo (this, className, typeof (Iface), typeof (NativeLayout), null); + return (Iface)Abi.ImplementClass (typeInfo); } /* The most powerful override. Allows the following from managed code: @@ -127,7 +129,8 @@ namespace Mono.Cxxi { where NativeLayout : struct where Managed : ICppObject { - return Abi.ImplementClass (typeof (Managed), this, className); + var typeInfo = Abi.MakeTypeInfo (this, className, typeof (Iface), typeof (NativeLayout), typeof (Managed)); + return (Iface)Abi.ImplementClass (typeInfo); } } diff --git a/src/Mono.Cxxi/CppTypeInfo.cs b/src/Mono.Cxxi/CppTypeInfo.cs index 64b53c10..85892df6 100644 --- a/src/Mono.Cxxi/CppTypeInfo.cs +++ b/src/Mono.Cxxi/CppTypeInfo.cs @@ -29,6 +29,7 @@ using System; using System.Linq; using System.Reflection; +using System.Reflection.Emit; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -43,52 +44,61 @@ namespace Mono.Cxxi { // TypeComplete indicates when the dust has settled. public class CppTypeInfo { - public CppAbi Abi { get; private set; } + public CppLibrary Library { get; private set; } + + public string TypeName { get; private set; } + public bool IsPrimaryBase { get; protected set; } // < True by default. set to False in cases where it is cloned as a non-primary base + + // returns the number of vtable slots reserved for the + // base class(es) before this class's virtual methods start + public int BaseVTableSlots { get; protected set; } + + public Type InterfaceType { get; private set; } public Type NativeLayout { get; private set; } public Type WrapperType { get; private set; } - public IList VirtualMethods { get; private set; } // read only version - protected List virtual_methods; + // read only versions: + public IList VirtualMethods { get; private set; } + public IList VTableDelegateTypes { get; private set; } + public IList VTableOverrides { get; private set; } + public IList BaseClasses { get; private set; } - public IList VTableDelegateTypes { get; private set; } // read only version + // backing lists: + protected List virtual_methods; protected LazyGeneratedList vt_delegate_types; - - public IList VTableOverrides { get; private set; } // read only version protected LazyGeneratedList vt_overrides; - - public IList BaseClasses { get; private set; } // read only version protected List base_classes; - // returns the number of vtable slots reserved for the - // base class(es) before this class's virtual methods start - public int BaseVTableSlots { get; protected set; } - - public bool TypeComplete { get; private set; } - public bool IsPrimaryBase { get; protected set; } // < True by default. set to False in cases where it is cloned as a non-primary base - protected int native_size_without_padding; // <- this refers to the size of all the fields declared in the nativeLayout struct protected int field_offset_padding_without_vtptr; protected int gchandle_offset_delta; private VTable lazy_vtable; - public CppTypeInfo (CppAbi abi, IEnumerable virtualMethods, Type nativeLayout, Type/*?*/ wrapperType) + internal EmitInfo emit_info; // <- will be null when the type is done being emitted + public bool TypeComplete { get { return emit_info == null; } } + + public CppTypeInfo (CppLibrary lib, string typeName, Type interfaceType, Type nativeLayout, Type/*?*/ wrapperType) : this () { - Abi = abi; + Library = lib; + TypeName = typeName; + + InterfaceType = interfaceType; NativeLayout = nativeLayout; WrapperType = wrapperType; - virtual_methods = new List (virtualMethods); + virtual_methods = new List (Library.Abi.GetVirtualMethodSlots (this, interfaceType)); VirtualMethods = new ReadOnlyCollection (virtual_methods); vt_delegate_types = new LazyGeneratedList (virtual_methods.Count, i => DelegateTypeCache.GetDelegateType (virtual_methods [i])); VTableDelegateTypes = new ReadOnlyCollection (vt_delegate_types); - vt_overrides = new LazyGeneratedList (virtual_methods.Count, i => Abi.GetManagedOverrideTrampoline (this, i)); + vt_overrides = new LazyGeneratedList (virtual_methods.Count, i => Library.Abi.GetManagedOverrideTrampoline (this, i)); VTableOverrides = new ReadOnlyCollection (vt_overrides); - native_size_without_padding = nativeLayout.GetFields ().Any ()? Marshal.SizeOf (nativeLayout) : 0; + if (nativeLayout != null) + native_size_without_padding = nativeLayout.GetFields ().Any ()? Marshal.SizeOf (nativeLayout) : 0; } protected CppTypeInfo () @@ -98,10 +108,11 @@ namespace Mono.Cxxi { field_offset_padding_without_vtptr = 0; gchandle_offset_delta = 0; - TypeComplete = false; IsPrimaryBase = true; BaseVTableSlots = 0; lazy_vtable = null; + + emit_info = new EmitInfo (); } // The contract for Clone is that, if TypeComplete, working with the clone *through the public @@ -197,13 +208,13 @@ namespace Mono.Cxxi { public virtual void CompleteType () { - if (TypeComplete) + if (emit_info == null) return; foreach (var baseClass in base_classes) baseClass.CompleteType (); - TypeComplete = true; + emit_info = null; RemoveVTableDuplicates (); } diff --git a/src/Mono.Cxxi/Makefile.am b/src/Mono.Cxxi/Makefile.am index a82aa596..191bbfd2 100644 --- a/src/Mono.Cxxi/Makefile.am +++ b/src/Mono.Cxxi/Makefile.am @@ -44,6 +44,7 @@ all: $(ASSEMBLY) $(PROGRAMFILES) $(LINUX_PKGCONFIG) FILES = \ Abi/CppAbi.cs \ + Abi/EmitInfo.cs \ Abi/Impl/ItaniumAbi.cs \ Abi/Impl/ItaniumTypeInfo.cs \ Abi/Impl/MsvcAbi.cs \ diff --git a/src/Mono.Cxxi/Mono.Cxxi.csproj b/src/Mono.Cxxi/Mono.Cxxi.csproj index 09d9c9b3..bc1c9725 100644 --- a/src/Mono.Cxxi/Mono.Cxxi.csproj +++ b/src/Mono.Cxxi/Mono.Cxxi.csproj @@ -79,6 +79,7 @@ +