/** * Emits IL to call the native method. nativeMethod should be either a method obtained by * GetPInvokeForMethod or the MethodInfo of a vtable method. * To complete method, emit OpCodes.Ret. */ protected virtual void EmitNativeCall(CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { var il = typeInfo.emit_info.current_il; var interfaceMethod = psig.OrigMethod; var interfaceArgs = interfaceMethod.GetParameters(); 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 if (psig.Type != MethodType.NativeCtor) { EmitInboundMarshal(il, psig.ReturnType, interfaceMethod.ReturnType); } }
/** * Defines a new MethodBuilder that calls the specified C++ (non-virtual) method using its mangled name */ protected virtual MethodBuilder GetPInvokeForMethod(CppTypeInfo typeInfo, PInvokeSignature sig) { var entryPoint = sig.Name; if (entryPoint == null) { throw new NotSupportedException("Could not mangle method name."); } string lib; if (IsInline(sig.OrigMethod) && typeInfo.Library.InlineMethodPolicy == InlineMethods.SurrogateLib) { lib = typeInfo.Library.Name + "-inline"; } else { lib = typeInfo.Library.Name; } 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); }
/** * Emits IL to load the VTable object onto the stack. */ 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.emit_info.typeinfo_field); il.Emit(OpCodes.Callvirt, typeinfo_vtable); }
protected virtual void EmitResetVTable(CppTypeInfo typeInfo, LocalBuilder cppip) { var il = typeInfo.emit_info.current_il; // this._typeInfo.VTable.ResetInstance (cppInstancePtr); EmitLoadVTable(typeInfo); il.Emit(OpCodes.Ldloc_S, cppip); EmitCallVTableMethod(typeInfo, vtable_resetinstance, 2, false); }
/** * Defines a new MethodBuilder with the same signature as the passed MethodInfo */ protected virtual MethodBuilder GetMethodBuilder(CppTypeInfo typeInfo, MethodInfo interfaceMethod) { 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); }
// Subclasses should allocate vtPtr and then call WriteOverrides public VTable(CppTypeInfo typeInfo) { this.initialized = false; this.type_info = typeInfo; this.vtPtr = Marshal.AllocHGlobal((EntryCount * EntrySize) + typeInfo.VTableTopPadding + typeInfo.VTableBottomPadding); WriteOverrides(); CppInstancePtr.RegisterManagedVTable(this); }
protected override MethodBuilder DefineMethod (CppTypeInfo typeInfo, PInvokeSignature sig, ref int 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) vtableIndex++; return builder; }
// AFIK, MSVC places only its first base's virtual methods in the derived class's // primary vtable. Subsequent base classes (each?) get another vtable pointer public override void AddBase(CppTypeInfo baseType) { if (TypeComplete) return; if (BaseClasses.Count == 0) base.AddBase (baseType, false); else base.AddBase (baseType, true); }
internal override Delegate GetManagedOverrideTrampoline (CppTypeInfo typeInfo, int vtableIndex) { // FIXME: HACK! we really need to support by val return types for managed override trampolines if (typeInfo.VirtualMethods [vtableIndex] != null && IsByVal (typeInfo.VirtualMethods [vtableIndex].OrigMethod.ReturnTypeCustomAttributes)) return null; return base.GetManagedOverrideTrampoline (typeInfo, vtableIndex); }
// Expects cppip = CppInstancePtr local protected virtual void EmitCreateCppObjectFromNative(ILGenerator il, Type targetType, LocalBuilder cppip) { CppTypeInfo targetTypeInfo = null; if (targetType == typeof(ICppObject)) { targetType = typeof(CppInstancePtr); } // check for a native constructor (i.e. a public ctor in the wrapper that takes CppInstancePtr) if (typeof(ICppObject).IsAssignableFrom(targetType)) { 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)); } // Basically emitting this: // CppInstancePtr.ToManaged<targetType> (native) ?? new targetType (native) // ..but ToManaged is only called if there's a vtable (FIXME: otherwise we rewrap) var hasWrapper = il.DefineLabel(); if (targetTypeInfo == null) { targetTypeInfo = GetTypeInfo(targetType); // FIXME: woof. do we really have to do this? } if (targetTypeInfo != null && targetTypeInfo.HasVTable) { il.Emit(OpCodes.Ldloca, cppip); il.Emit(OpCodes.Call, cppip_native); il.Emit(OpCodes.Call, cppip_tomanaged.MakeGenericMethod(targetType)); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue_S, hasWrapper); il.Emit(OpCodes.Pop); } il.Emit(OpCodes.Ldloc, cppip); il.Emit(OpCodes.Newobj, ctor); il.MarkLabel(hasWrapper); } else if (targetType.IsValueType) { // (targetType)Marshal.PtrToStructure (CppInstancePtr.Native, typeof (targetType)) il.Emit(OpCodes.Ldloca, cppip); il.Emit(OpCodes.Call, cppip_native); il.Emit(OpCodes.Ldtoken, targetType); il.Emit(OpCodes.Call, type_gettypefromhandle); il.Emit(OpCodes.Call, marshal_ptrtostructure); il.Emit(OpCodes.Unbox_Any, targetType); } }
internal override Delegate GetManagedOverrideTrampoline(CppTypeInfo typeInfo, int vtableIndex) { // FIXME: HACK! we really need to support by val return types for managed override trampolines if (typeInfo.VirtualMethods [vtableIndex] != null && IsByVal(typeInfo.VirtualMethods [vtableIndex].OrigMethod.ReturnTypeCustomAttributes)) { return(null); } return(base.GetManagedOverrideTrampoline(typeInfo, vtableIndex)); }
protected override MethodBuilder DefineMethod(CppTypeInfo typeInfo, PInvokeSignature sig, ref int 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) { vtableIndex++; } return(builder); }
public override PInvokeSignature GetPInvokeSignature(CppTypeInfo /*?*/ typeInfo, MethodInfo method) { var psig = base.GetPInvokeSignature(typeInfo, method); if (ReturnByHiddenArgument(typeInfo, method)) { psig.ParameterTypes.Insert(0, typeof(IntPtr)); psig.ReturnType = typeof(void); } return(psig); }
// These methods might be more commonly overridden for a given C++ ABI: public virtual MethodType GetMethodType (CppTypeInfo typeInfo, MethodInfo imethod) { if (IsInline (imethod) && !IsVirtual (imethod) && typeInfo.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; else if (imethod.IsDefined (typeof (DestructorAttribute), false)) return MethodType.NativeDtor; return MethodType.Native; }
public override IEnumerable<PInvokeSignature> GetVirtualMethodSlots(CppTypeInfo typeInfo, Type interfaceType) { foreach (var method in base.GetVirtualMethodSlots (typeInfo, interfaceType)) { if (!IsVirtual (method.OrigMethod)) continue; yield return method; // Itanium has extra slot for virt dtor if (method.Type == MethodType.NativeDtor) yield return null; } }
/** * 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(CppTypeInfo typeInfo, LocalBuilder cppInstancePtr, int vtableIndex) { var il = typeInfo.emit_info.current_il; var vtableDelegateType = typeInfo.VTableDelegateTypes [vtableIndex]; var getDelegate = typeinfo_adjvcall.MakeGenericMethod(vtableDelegateType); // this._typeInfo.GetAdjustedVirtualCall<T> (cppInstancePtr, vtableIndex); il.Emit(OpCodes.Ldarg_0); 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); return(ReflectionHelper.GetMethodInfoForDelegate(vtableDelegateType)); }
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) { return; } EmitCheckManagedAlloc(il, cppInstancePtr); EmitResetVTable(typeInfo, cppInstancePtr); EmitNativeCall(typeInfo, nativeMethod, psig, nativePtr); }
public override IEnumerable <PInvokeSignature> GetVirtualMethodSlots(CppTypeInfo typeInfo, Type interfaceType) { foreach (var method in base.GetVirtualMethodSlots(typeInfo, interfaceType)) { if (!IsVirtual(method.OrigMethod)) { continue; } yield return(method); // Itanium has extra slot for virt dtor if (method.Type == MethodType.NativeDtor) { yield return(null); } } }
protected virtual void EmitConstruct(CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder cppInstancePtr, LocalBuilder nativePtr) { Debug.Assert(psig.Type == MethodType.NativeCtor); var il = typeInfo.emit_info.current_il; EmitNativeCall(typeInfo, nativeMethod, psig, nativePtr); if (cppInstancePtr != null && psig.OrigMethod.ReturnType == typeof(CppInstancePtr)) { EmitInitVTable(typeInfo, cppInstancePtr); il.Emit(OpCodes.Ldloc_S, cppInstancePtr); } else if (psig.OrigMethod.DeclaringType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICppClassOverridable <>))) { throw new InvalidProgramException("In ICppClassOverridable, native constructors must take as first argument and return CppInstancePtr"); } }
// Section 3.1.4: // Classes with non-default copy ctors/destructors are returned using a hidden // argument bool ReturnByHiddenArgument(CppTypeInfo typeInfo, MethodInfo method) { var iti = (ItaniumTypeInfo)typeInfo; if (!IsByVal(method.ReturnTypeCustomAttributes)) { return(false); } 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(iti.has_non_default_copy_ctor_or_dtor.Value); }
public virtual ICppClass ImplementClass(CppTypeInfo typeInfo) { if (typeInfo.WrapperType == null || !wrapper_to_typeinfo.ContainsKey(typeInfo.WrapperType)) { if (typeInfo.WrapperType != null) { wrapper_to_typeinfo.Add(typeInfo.WrapperType, typeInfo); } DefineImplType(typeInfo); 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(typeInfo, property); } typeInfo.emit_info.ctor_il.Emit(OpCodes.Ret); var native_vtable = default(IntPtr); #if INIT_NATIVE_VTABLES var vtable_symbol = GetMangledVTableName(typeInfo); if (vtable_symbol != null) { native_vtable = SymbolResolver.ResolveSymbol(SymbolResolver.LoadImage(ref typeInfo.Library.name), vtable_symbol); } #endif return((ICppClass)Activator.CreateInstance(typeInfo.emit_info.type_builder.CreateType(), typeInfo, native_vtable)); } throw new InvalidOperationException("This type has already been implemented"); }
protected override void EmitNativeCall(CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { var il = typeInfo.emit_info.current_il; var method = psig.OrigMethod; var returnType = method.ReturnType; var hiddenReturnByValue = ReturnByHiddenArgument(typeInfo, method); LocalBuilder returnValue = null; if (hiddenReturnByValue) { returnValue = il.DeclareLocal(typeof(CppInstancePtr)); if (typeof(ICppObject).IsAssignableFrom(returnType)) { il.Emit(OpCodes.Ldc_I4, GetTypeInfo(returnType).NativeSize); } else if (returnType.IsValueType) { il.Emit(OpCodes.Ldc_I4, Marshal.SizeOf(returnType)); } il.Emit(OpCodes.Newobj, cppip_fromsize); il.Emit(OpCodes.Stloc, returnValue); il.Emit(OpCodes.Ldloca, returnValue); il.Emit(OpCodes.Call, cppip_native); } base.EmitNativeCall(typeInfo, nativeMethod, psig, nativePtr); if (hiddenReturnByValue) { EmitCreateCppObjectFromNative(il, returnType, returnValue); if (returnType.IsValueType) { // FIXME: This dispose should prolly be in a Finally block.. il.Emit(OpCodes.Ldloca, returnValue); il.Emit(OpCodes.Call, cppip_dispose); } } }
// These methods might be more commonly overridden for a given C++ ABI: public virtual MethodType GetMethodType(CppTypeInfo typeInfo, MethodInfo imethod) { if (IsInline(imethod) && !IsVirtual(imethod) && typeInfo.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); } else if (imethod.IsDefined(typeof(DestructorAttribute), false)) { return(MethodType.NativeDtor); } return(MethodType.Native); }
public virtual PInvokeSignature GetPInvokeSignature(CppTypeInfo typeInfo, MethodInfo method) { var methodType = GetMethodType(typeInfo, method); var parameters = method.GetParameters(); var pinvokeTypes = new List <Type> (parameters.Length); foreach (var pi in parameters) { pinvokeTypes.Add(ToPInvokeType(pi.ParameterType, pi)); } return(new PInvokeSignature { OrigMethod = method, Name = GetMangledMethodName(typeInfo, method), Type = methodType, CallingConvention = GetCallingConvention(method), ParameterTypes = pinvokeTypes, ReturnType = methodType == MethodType.NativeCtor? typeof(void) : ToPInvokeType(method.ReturnType, method.ReturnTypeCustomAttributes) }); }
/** * A utility function to emit the IL for a vtable-dependant operation. * In other words, classes with no virtual methods will not have vtables, * so this method emits code to check for that and either throw an exception * or do nothing if no vtable exists. To use, push the arguments to the method you * want to call and pass the stackHeight for the call. If no vtable exists, this method * will emit code to pop the arguments off the stack. */ protected virtual void 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 var noVirt = il.DefineLabel(); var dontPushOrThrow = il.DefineLabel(); EmitLoadVTable(typeInfo); il.Emit(OpCodes.Brfalse_S, noVirt); // if (vtableInfo == null) goto noVirt il.Emit(OpCodes.Callvirt, method); // call method il.Emit(OpCodes.Br_S, dontPushOrThrow); // goto dontPushOrThrow il.MarkLabel(noVirt); // noVirt: // since there is no vtable, we did not make the method call. // pop arguments for (int i = 0; i < stackHeight; i++) { il.Emit(OpCodes.Pop); } // if the method was supposed to return a value, we must // still push something onto the stack // FIXME: This is a kludge. What about value types? if (!method.ReturnType.Equals(typeof(void))) { il.Emit(OpCodes.Ldnull); } if (throwOnNoVTable) { il.Emit(OpCodes.Ldstr, "Native class has no VTable."); il.Emit(OpCodes.Newobj, typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) })); il.Emit(OpCodes.Throw); } il.MarkLabel(dontPushOrThrow); }
/** * Emits IL to allocate the memory for a new instance of the C++ class. * To complete method, emit OpCodes.Ret. */ protected virtual void EmitManagedAlloc(CppTypeInfo typeInfo, MethodInfo interfaceMethod) { var il = typeInfo.emit_info.current_il; var cppip = il.DeclareLocal(typeof(CppInstancePtr)); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); if (typeInfo.WrapperType != null && interfaceMethod.GetParameters().Any()) { // load managed wrapper il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Newobj, cppip_fromtype_managed); } else { il.Emit(OpCodes.Callvirt, typeinfo_nativesize); il.Emit(OpCodes.Newobj, cppip_fromsize); } il.Emit(OpCodes.Stloc, cppip); var unknown_native_vtable = il.DefineLabel(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field); il.Emit(OpCodes.Brfalse_S, unknown_native_vtable); il.Emit(OpCodes.Ldloca, cppip); il.Emit(OpCodes.Call, cppip_native); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field); il.Emit(OpCodes.Call, marshal_writeintptr); il.MarkLabel(unknown_native_vtable); il.Emit(OpCodes.Ldloc, cppip); }
protected virtual void DefineImplType(CppTypeInfo typeInfo) { string implTypeName = typeInfo.InterfaceType.Name + "_"; if (typeInfo.NativeLayout != null) { implTypeName += typeInfo.NativeLayout.Name + "_"; } implTypeName += this.GetType().Name + "_Impl"; 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); var native_vtable_field = impl_type.DefineField("_nativeVTable", typeof(IntPtr), FieldAttributes.InitOnly | FieldAttributes.Private); ConstructorBuilder ctor = impl_type.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(CppTypeInfo), typeof(IntPtr) }); 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); // this._nativeVTable = (vtable ptr passed to constructor) ctor_il.Emit(OpCodes.Ldarg_0); ctor_il.Emit(OpCodes.Ldarg_2); ctor_il.Emit(OpCodes.Stfld, native_vtable_field); typeInfo.emit_info.ctor_il = ctor_il; typeInfo.emit_info.typeinfo_field = typeinfo_field; typeInfo.emit_info.native_vtable_field = native_vtable_field; typeInfo.emit_info.type_builder = impl_type; }
protected override string GetMangledVTableName(CppTypeInfo typeInfo) { var compressMap = new Dictionary <string, int> (); var type = typeInfo.GetMangleType(); var nm = new StringBuilder("_ZTV", 30); if (type.Namespaces != null) { nm.Append('N'); foreach (var ns in type.Namespaces) { nm.Append(GetIdentifier(compressMap, ns)); } } nm.Append(GetIdentifier(compressMap, type.ElementTypeName)); if (type.Namespaces != null) { nm.Append('E'); } return(nm.ToString()); }
public virtual PInvokeSignature GetPInvokeSignature (CppTypeInfo typeInfo, MethodInfo method) { var methodType = GetMethodType (typeInfo, method); var parameters = method.GetParameters (); var pinvokeTypes = new List<Type> (parameters.Length); foreach (var pi in parameters) { pinvokeTypes.Add (ToPInvokeType (pi.ParameterType, pi)); } return new PInvokeSignature { OrigMethod = method, Name = GetMangledMethodName (typeInfo, method), Type = methodType, CallingConvention = GetCallingConvention (method), ParameterTypes = pinvokeTypes, ReturnType = methodType == MethodType.NativeCtor? typeof (void) : ToPInvokeType (method.ReturnType, method.ReturnTypeCustomAttributes) }; }
/** * Emits IL to call the native method. nativeMethod should be either a method obtained by * GetPInvokeForMethod or the MethodInfo of a vtable method. * To complete method, emit OpCodes.Ret. */ protected virtual void EmitNativeCall (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { var il = typeInfo.emit_info.current_il; var interfaceMethod = psig.OrigMethod; var interfaceArgs = interfaceMethod.GetParameters (); 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 if (psig.Type != MethodType.NativeCtor) EmitInboundMarshal (il, psig.ReturnType, interfaceMethod.ReturnType); }
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) return; EmitCheckManagedAlloc (il, cppInstancePtr); EmitResetVTable (typeInfo, cppInstancePtr); EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); }
// Takes in an address string which identifies the location of the property value in memory, // along with property type information. // Outputs a string expression which will evaluate to the precise location with the correct C++ type. protected string AdjustPropertyExpressionStringForType(string address_str, string prop_type, string uprop_expr_str, DkmVisualizedExpression context_expr, CppTypeInfo cpp_type_info) { // Special cases first switch (prop_type) { case Prop.Bool: { // Needs special treatment since can be a single bit field as well as just a regular bool // Get a UBoolProperty context var uboolprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Bool); // Read the byte offset and field mask properties string byte_offset_str = GetBoolPropertyByteOffset(uboolprop_em.Expression, context_expr); string field_mask_str = GetBoolPropertyFieldMask(uboolprop_em.Expression, context_expr); // Format an expression which adds the byte offset onto the address, derefs // and then bitwise ANDs with the field mask. return String.Format("(*({0} + {1}) & {2}) != 0", address_str, byte_offset_str, field_mask_str ); } case Prop.Byte: { // Enum properties are a bit awkward, since due to the different ways the enums can be declared, // we can't reliably access them by a cast and dereference. // eg. A regular enum type will be considered 64 bits. // So, we need to use uint8 to get at the memory, and then do the cast, or rather conversion, // *after* dereferencing in order to get the correct type. return String.Format("({0})*({1}*){2}{3}", cpp_type_info.Type, Typ.Byte, address_str, cpp_type_info.Format ); } default: break; } // If we got here, we just need to get the corresponding C++ type, then cast the address // to it and dereference. // [*(type*)address,fmt] return String.Format("*({0}*){1}{2}", cpp_type_info.Type, address_str, cpp_type_info.Format); }
protected override string GetMangledMethodName (CppTypeInfo typeInfo, MethodInfo methodInfo) { var methodName = methodInfo.Name; var type = typeInfo.GetMangleType (); var className = type.ElementTypeName; MethodType methodType = GetMethodType (typeInfo, methodInfo); ParameterInfo [] parameters = methodInfo.GetParameters (); StringBuilder nm = new StringBuilder ("?", 30); if (methodType == MethodType.NativeCtor) nm.Append ("?0"); else if (methodType == MethodType.NativeDtor) nm.Append ("?1"); else nm.Append (methodName).Append ('@'); // 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 (className).Append ("@@"); // function modifiers are a matrix of consecutive uppercase letters // depending on access type and virtual (far)/static (far)/far modifiers // first, access type char funcModifier = 'Q'; // (public) if (IsProtected (methodInfo)) funcModifier = 'I'; else if (IsPrivate (methodInfo)) // (probably don't need this) funcModifier = 'A'; // now, offset based on other modifiers if (IsStatic (methodInfo)) funcModifier += (char)2; else if (IsVirtual (methodInfo)) funcModifier += (char)4; nm.Append (funcModifier); // FIXME: deal with other storage classes for "this" i.e. the "volatile" in -> int foo () volatile; if (!IsStatic (methodInfo)) { if (IsConst (methodInfo)) nm.Append ('B'); else nm.Append ('A'); } switch (GetCallingConvention (methodInfo)) { case CallingConvention.Cdecl: nm.Append ('A'); break; case CallingConvention.ThisCall: nm.Append ('E'); break; case CallingConvention.StdCall: nm.Append ('G'); break; case CallingConvention.FastCall: nm.Append ('I'); break; } // FIXME: handle const, volatile modifiers on return type // FIXME: the manual says this is only omitted for simple types.. are we doing the right thing here? CppType returnType = GetMangleType (methodInfo.ReturnTypeCustomAttributes, methodInfo.ReturnType); if (returnType.ElementType == CppTypes.Class || returnType.ElementType == CppTypes.Struct || returnType.ElementType == CppTypes.Union) nm.Append ("?A"); if (methodType == MethodType.NativeCtor || methodType == MethodType.NativeDtor) nm.Append ('@'); else nm.Append (GetTypeCode (returnType)); int argStart = (IsStatic (methodInfo)? 0 : 1); if (parameters.Length == argStart) { // no args (other than C++ "this" object) nm.Append ("XZ"); return nm.ToString (); } else for (int i = argStart; i < parameters.Length; i++) nm.Append (GetTypeCode (GetMangleType (parameters [i], parameters [i].ParameterType))); nm.Append ("@Z"); return nm.ToString (); }
// Implementing this is recommended.. // otherwise it is not possible to instantiate classes that have vtables and only implicitly defined ctor // Return null if not implemented or if the symbol name of the vtable for the passed typeInfo cannot be mangled (i.e. because there's no vtable) protected virtual string GetMangledVTableName (CppTypeInfo typeInfo) { return null; }
protected virtual void EmitResetVTable (CppTypeInfo typeInfo, LocalBuilder cppip) { var il = typeInfo.emit_info.current_il; // this._typeInfo.VTable.ResetInstance (cppInstancePtr); EmitLoadVTable (typeInfo); il.Emit (OpCodes.Ldloc_S, cppip); EmitCallVTableMethod (typeInfo, vtable_resetinstance, 2, false); }
/** * Defines a new MethodBuilder with the same signature as the passed MethodInfo */ protected virtual MethodBuilder GetMethodBuilder (CppTypeInfo typeInfo, MethodInfo interfaceMethod) { 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; }
/** * Implements the managed trampoline that will be invoked from the vtable by native C++ code when overriding * the specified C++ virtual method with the specified managed one. */ // FIXME: This should be moved into CppTypeInfo class internal virtual Delegate GetManagedOverrideTrampoline (CppTypeInfo typeInfo, int vtableIndex) { if (typeInfo.WrapperType == null) return null; var sig = typeInfo.VirtualMethods [vtableIndex]; if (sig == null) return null; var interfaceMethod = sig.OrigMethod; var targetMethod = FindManagedOverrideTarget (typeInfo.WrapperType, interfaceMethod); if (targetMethod == null) return null; 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. var trampolineIn = new DynamicMethod (typeInfo.WrapperType.Name + "_" + interfaceMethod.Name + "_FromNative", sig.ReturnType, nativeArgs, typeof (CppInstancePtr).Module, true); ReflectionHelper.ApplyMethodParameterAttributes (interfaceMethod, trampolineIn, true); var il = trampolineIn.GetILGenerator (); // for static (target) methods: OpCode callInstruction = OpCodes.Call; int argLoadStart = IsStatic (interfaceMethod)? 0 : 1; // chop off C++ instance ptr if there is one // for instance methods, we need a managed instance to call them on! if (!targetMethod.IsStatic) { callInstruction = OpCodes.Callvirt; argLoadStart = 1; il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldc_I4, typeInfo.GCHandleOffset); var getManagedObj = cppip_tomanaged_size.MakeGenericMethod (typeInfo.WrapperType); il.Emit (OpCodes.Call, getManagedObj); } for (int i = argLoadStart; i < interfaceArgs.Length; i++) { il.Emit (OpCodes.Ldarg, i); EmitInboundMarshal (il, nativeArgs [i], interfaceArgs [i]); } il.Emit (callInstruction, targetMethod); EmitOutboundMarshal (il, targetMethod.ReturnType, sig.ReturnType); il.Emit (OpCodes.Ret); return trampolineIn.CreateDelegate (typeInfo.VTableDelegateTypes [vtableIndex]); }
protected virtual PropertyBuilder DefineProperty (CppTypeInfo typeInfo, PropertyInfo property) { if (property.CanWrite) throw new InvalidProgramException ("Properties in C++ interface must be read-only."); var imethod = property.GetGetMethod (); var methodName = imethod.Name; var propName = property.Name; var retType = imethod.ReturnType; 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 = 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 = 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) 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); // first, get field offset // = ((int)Marshal.OffsetOf (layout_type, propName)) + FieldOffsetPadding; 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.emit_info.typeinfo_field); il.Emit (OpCodes.Callvirt, typeinfo_fieldoffset); il.Emit (OpCodes.Add); // new CppField<T> (<field offset>) 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.emit_info.typeinfo_field); il.Emit (OpCodes.Ret); } else throw new InvalidProgramException ("Properties in C++ interface can only be of type CppField."); fieldProp.SetGetMethod (fieldGetter); return fieldProp; }
protected virtual MethodBuilder DefineMethod (CppTypeInfo typeInfo, PInvokeSignature psig, ref int vtableIndex) { var interfaceMethod = psig.OrigMethod; // 1. Generate managed trampoline to call native method var trampoline = GetMethodBuilder (typeInfo, interfaceMethod); var il = typeInfo.emit_info.current_il = trampoline.GetILGenerator (); 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 if (!interfaceMethod.ReturnType.Equals (typeof (void))) il.Emit (OpCodes.Ldnull); il.Emit (OpCodes.Ret); return trampoline; case MethodType.ManagedAlloc: EmitManagedAlloc (typeInfo, interfaceMethod); il.Emit (OpCodes.Ret); return trampoline; } var isStatic = IsStatic (interfaceMethod); LocalBuilder cppInstancePtr = null; LocalBuilder nativePtr = null; // If we're an instance method, load up the "this" pointer if (!isStatic) { if (psig.ParameterTypes.Count == 0) throw new ArgumentException ("First argument to non-static C++ method must be instance pointer."); // 2. Load the native C++ instance pointer 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, psig.Type); } MethodInfo nativeMethod; if (IsVirtual (interfaceMethod) && psig.Type != MethodType.NativeDtor) { nativeMethod = EmitPrepareVirtualCall (typeInfo, cppInstancePtr, vtableIndex++); } else { if (IsVirtual (interfaceMethod)) vtableIndex++; nativeMethod = GetPInvokeForMethod (typeInfo, psig); } switch (psig.Type) { case MethodType.NativeCtor: EmitConstruct (typeInfo, nativeMethod, psig, cppInstancePtr, nativePtr); break; case MethodType.NativeDtor: EmitDestruct (typeInfo, nativeMethod, psig, cppInstancePtr, nativePtr); break; default: EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); break; } il.Emit (OpCodes.Ret); return trampoline; }
protected override string GetMangledMethodName(CppTypeInfo typeInfo, MethodInfo methodInfo) { var compressMap = new Dictionary<string, int> (); var methodName = methodInfo.Name; var type = typeInfo.GetMangleType (); var className = type.ElementTypeName; MethodType methodType = GetMethodType (typeInfo, methodInfo); ParameterInfo [] parameters = methodInfo.GetParameters (); StringBuilder nm = new StringBuilder ("_ZN", 30); if (IsConst (methodInfo)) nm.Append ('K'); if (type.Namespaces != null) { foreach (var ns in type.Namespaces) nm.Append (GetIdentifier (compressMap, ns)); } nm.Append (GetIdentifier (compressMap, className)); // Insert the class template types here var templates = type.Modifiers.OfType<CppModifiers.TemplateModifier>().FirstOrDefault(); if (templates != null) { nm.Append("I"); foreach (var mangleType in templates.Types) nm.Append (GetTypeCode (mangleType, compressMap)); nm.Append("E"); } // FIXME: Implement compression completely switch (methodType) { case MethodType.NativeCtor: nm.Append ("C1"); break; case MethodType.NativeDtor: nm.Append ("D1"); break; default: nm.Append (methodName.Length).Append (methodName); break; } nm.Append ('E'); int argStart = (IsStatic (methodInfo)? 0 : 1); if (parameters.Length == argStart) // no args (other than C++ "this" object) nm.Append ('v'); else for (int i = argStart; i < parameters.Length; i++) nm.Append (GetTypeCode (GetMangleType (parameters [i], parameters [i].ParameterType), compressMap)); return nm.ToString (); }
public sp_u_fdbg_symbol_s(CppTypeInfo subClass) { __cxxi_LayoutClass(); subClass.AddBase(impl.TypeInfo); }
protected override string GetMangledVTableName(CppTypeInfo typeInfo) { var compressMap = new Dictionary<string, int> (); var type = typeInfo.GetMangleType (); var nm = new StringBuilder ("_ZTV", 30); if (type.Namespaces != null) { nm.Append ('N'); foreach (var ns in type.Namespaces) nm.Append (GetIdentifier (compressMap, ns)); } nm.Append (GetIdentifier (compressMap, type.ElementTypeName)); if (type.Namespaces != null) nm.Append ('E'); return nm.ToString (); }
/** * Emits IL to load the VTable object onto the stack. */ 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.emit_info.typeinfo_field); il.Emit (OpCodes.Callvirt, typeinfo_vtable); }
/** * Defines a new MethodBuilder that calls the specified C++ (non-virtual) method using its mangled name */ protected virtual MethodBuilder GetPInvokeForMethod (CppTypeInfo typeInfo, PInvokeSignature sig) { var entryPoint = sig.Name; if (entryPoint == null) throw new NotSupportedException ("Could not mangle method name."); string lib; if (IsInline (sig.OrigMethod) && typeInfo.Library.InlineMethodPolicy == InlineMethods.SurrogateLib) lib = typeInfo.Library.Name + "-inline"; else lib = typeInfo.Library.Name; 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; }
/** * A utility function to emit the IL for a vtable-dependant operation. * In other words, classes with no virtual methods will not have vtables, * so this method emits code to check for that and either throw an exception * or do nothing if no vtable exists. To use, push the arguments to the method you * want to call and pass the stackHeight for the call. If no vtable exists, this method * will emit code to pop the arguments off the stack. */ protected virtual void 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 var noVirt = il.DefineLabel (); var dontPushOrThrow = il.DefineLabel (); EmitLoadVTable (typeInfo); il.Emit (OpCodes.Brfalse_S, noVirt); // if (vtableInfo == null) goto noVirt il.Emit (OpCodes.Callvirt, method); // call method il.Emit (OpCodes.Br_S, dontPushOrThrow); // goto dontPushOrThrow il.MarkLabel (noVirt); // noVirt: // since there is no vtable, we did not make the method call. // pop arguments for (int i = 0; i < stackHeight; i++) il.Emit (OpCodes.Pop); // if the method was supposed to return a value, we must // still push something onto the stack // FIXME: This is a kludge. What about value types? if (!method.ReturnType.Equals (typeof (void))) il.Emit (OpCodes.Ldnull); if (throwOnNoVTable) { il.Emit (OpCodes.Ldstr, "Native class has no VTable."); il.Emit (OpCodes.Newobj, typeof (InvalidOperationException).GetConstructor(new Type[] {typeof (string)})); il.Emit (OpCodes.Throw); } il.MarkLabel (dontPushOrThrow); }
/** * 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 (CppTypeInfo typeInfo, LocalBuilder cppInstancePtr, int vtableIndex) { var il = typeInfo.emit_info.current_il; var vtableDelegateType = typeInfo.VTableDelegateTypes [vtableIndex]; var getDelegate = typeinfo_adjvcall.MakeGenericMethod (vtableDelegateType); // this._typeInfo.GetAdjustedVirtualCall<T> (cppInstancePtr, vtableIndex); il.Emit (OpCodes.Ldarg_0); 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); return ReflectionHelper.GetMethodInfoForDelegate (vtableDelegateType); }
/** * Emits IL to allocate the memory for a new instance of the C++ class. * To complete method, emit OpCodes.Ret. */ protected virtual void EmitManagedAlloc (CppTypeInfo typeInfo, MethodInfo interfaceMethod) { var il = typeInfo.emit_info.current_il; var cppip = il.DeclareLocal (typeof (CppInstancePtr)); il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); if (typeInfo.WrapperType != null && interfaceMethod.GetParameters ().Any ()) { // load managed wrapper il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Newobj, cppip_fromtype_managed); } else { il.Emit (OpCodes.Callvirt, typeinfo_nativesize); il.Emit (OpCodes.Newobj, cppip_fromsize); } il.Emit (OpCodes.Stloc, cppip); var unknown_native_vtable = il.DefineLabel (); il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field); il.Emit (OpCodes.Brfalse_S, unknown_native_vtable); il.Emit (OpCodes.Ldloca, cppip); il.Emit (OpCodes.Call, cppip_native); il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field); il.Emit (OpCodes.Call, marshal_writeintptr); il.MarkLabel (unknown_native_vtable); il.Emit (OpCodes.Ldloc, cppip); }
protected virtual void EmitConstruct (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder cppInstancePtr, LocalBuilder nativePtr) { Debug.Assert (psig.Type == MethodType.NativeCtor); var il = typeInfo.emit_info.current_il; EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); if (cppInstancePtr != null && psig.OrigMethod.ReturnType == typeof (CppInstancePtr)) { EmitInitVTable (typeInfo, cppInstancePtr); il.Emit (OpCodes.Ldloc_S, cppInstancePtr); } else if (psig.OrigMethod.DeclaringType.GetInterfaces ().Any (i => i.IsGenericType && i.GetGenericTypeDefinition () == typeof (ICppClassOverridable<>))) { throw new InvalidProgramException ("In ICppClassOverridable, native constructors must take as first argument and return CppInstancePtr"); } }
public RTPSource(CppTypeInfo subClass) : base(impl.TypeInfo) { __cxxi_LayoutClass(); subClass.AddBase(impl.TypeInfo); }
public ICompilation(CppTypeInfo subClass) { __cxxi_LayoutClass(); subClass.AddBase(impl.TypeInfo); }
public SMInterface(CppTypeInfo subClass) { __cxxi_LayoutClass(); subClass.AddBase(impl.TypeInfo); }
protected abstract string GetMangledMethodName (CppTypeInfo typeInfo, MethodInfo methodInfo);
public __si_class_type_info_pseudo(CppTypeInfo subClass) { __cxxi_LayoutClass(); subClass.AddBase(impl.TypeInfo); }
public virtual ICppClass ImplementClass (CppTypeInfo typeInfo) { if (typeInfo.WrapperType == null || !wrapper_to_typeinfo.ContainsKey (typeInfo.WrapperType)) { if (typeInfo.WrapperType != null) wrapper_to_typeinfo.Add (typeInfo.WrapperType, typeInfo); DefineImplType (typeInfo); 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 (typeInfo, property); typeInfo.emit_info.ctor_il.Emit (OpCodes.Ret); var native_vtable = default (IntPtr); #if INIT_NATIVE_VTABLES var vtable_symbol = GetMangledVTableName (typeInfo); if (vtable_symbol != null) native_vtable = SymbolResolver.ResolveSymbol (SymbolResolver.LoadImage (ref typeInfo.Library.name), vtable_symbol); #endif return (ICppClass)Activator.CreateInstance (typeInfo.emit_info.type_builder.CreateType (), typeInfo, native_vtable); } throw new InvalidOperationException ("This type has already been implemented"); }
public UsageEnvironment(CppTypeInfo subClass) { __cxxi_LayoutClass(); subClass.AddBase(impl.TypeInfo); }
public virtual IEnumerable<PInvokeSignature> GetVirtualMethodSlots (CppTypeInfo typeInfo, Type interfaceType) { return from m in GetMethods (interfaceType) where IsVirtual (m) select GetPInvokeSignature (typeInfo, m); }
// Takes in a UE4 property type string (eg. IntPropery, ObjectProperty, etc) along with a // expression string which evaluates to a UProperty*, and maps to the corresponding C++ type. protected CppTypeInfo[] GetCppTypeForPropertyType(string prop_type, string uprop_expr_str, DkmVisualizedExpression context_expr) { switch (prop_type) { case Prop.Bool: return new CppTypeInfo[] { new CppTypeInfo("bool") }; case Prop.Struct: { // This is gonna be effort. // Get UStructProperty var ustructprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Struct); // Need to access its UScriptStruct* member 'Struct', and get its object name. var struct_name_expr_str = ustructprop_em.PtrMember(Memb.CppStruct).PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; // Get that name string string struct_name = UE4Utility.GetFNameAsString(struct_name_expr_str, context_expr); // Add the 'F' prefix return new CppTypeInfo[] { new CppTypeInfo("F" + struct_name) }; } case Prop.Byte: { // Could be plain uint8, or a UEnum. // Get UByteProperty var ubyteprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Byte); // Need to access its UEnum* member 'Enum'. var uenum_em = ubyteprop_em.PtrMember(Memb.EnumType); // Evaluate this to see if it is null or not bool is_enum_valid = !UE4Utility.IsPointerNull(uenum_em.Expression, context_expr); if (is_enum_valid) { // This property is an enum, so the type we want is the fully qualified C++ enum type. // @NOTE: Seems that the CppType member should be exactly what we need, but appears to actually be unused. //string cpp_enum_name_expr_str = uenum_em.PtrMember("CppType").Expression; string uenum_fname_expr_str = uenum_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName).Expression; string uenum_name = UE4Utility.GetFNameAsString(uenum_fname_expr_str, context_expr); // We need to know if it's a namespaced enum or not string is_namespaced_enum_expr_str = String.Format( "{0}==UEnum::ECppForm::Namespaced", uenum_em.PtrMember(Memb.EnumCppForm).Expression ); var is_namespaced_res = UE4Utility.EvaluateBooleanExpression(is_namespaced_enum_expr_str, context_expr); // @TODO: on evaluation failure?? CppTypeInfo primary; if (is_namespaced_res.IsValid && is_namespaced_res.Value) { // A namespaced enum type should (hopefully) always be <UEnum::Name>::Type primary = new CppTypeInfo(String.Format("{0}::Type", uenum_name), uenum_name); } else { // For regular or enum class enums, the full type name should be just the name of the UEnum object. primary = new CppTypeInfo(uenum_name); } return new CppTypeInfo[] { primary, // Fallback onto regular byte display, in case enum name symbol not available new CppTypeInfo(Typ.Byte, uenum_name + "?") }; } else { // Must just be a regular uint8 return new CppTypeInfo[] { new CppTypeInfo(Typ.Byte) }; } } case Prop.Array: { // Okay, cast to UArrayProperty and get the inner property type var array_prop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.Array); var inner_prop_em = array_prop_em.PtrMember(Memb.ArrayInner); // @TODO: Need to look into how TArray< bool > is handled. string inner_prop_type = GetPropertyType(inner_prop_em.Expression, context_expr); var inner_cpp_type_info = GetCppTypeForPropertyType(inner_prop_type, inner_prop_em.Expression, context_expr); var result = new CppTypeInfo[inner_cpp_type_info.Length]; for (int i = 0; i < inner_cpp_type_info.Length; ++i) { // Type needed is TArray< %inner_cpp_type%, FDefaultAllocator > string type = String.Format("{0}<{1},{2}>", Typ.Array, inner_cpp_type_info[i].Type, Typ.DefaultAlloc); // Omit allocator from display string, since for UPROPERTY arrays it can't be anything else string display = String.Format("{0}<{1}>", Typ.Array, inner_cpp_type_info[i].Display); result[i] = new CppTypeInfo(type, display); } return result; } case Prop.Object: case Prop.Asset: { if(!Config.ShowExactUObjectTypes) { break; } string obj_cpp_type_name = Typ.UObject; // Need to find out the subtype of the property, which is specified by UObjectPropertyBase::PropertyClass var objprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(CppProp.ObjectBase); var subtype_uclass_em = objprop_em.PtrMember(Memb.ObjectSubtype); var uclass_fname_em = subtype_uclass_em.PtrCast(Typ.UObjectBase).PtrMember(Memb.ObjName); string uclass_fname = UE4Utility.GetFNameAsString(uclass_fname_em.Expression, context_expr); // Is the property class native? var is_native_res = UE4Utility.IsNativeUClassOrUInterface(subtype_uclass_em.Expression, context_expr); // @TODO: currently not really handling failed eval bool is_native = is_native_res.IsValid ? is_native_res.Value : true; string native_uclass_fname; if (is_native) { // Yes native_uclass_fname = uclass_fname; } else { // No, we need to retrieve the name of its native base native_uclass_fname = UE4Utility.GetBlueprintClassNativeBaseName(subtype_uclass_em.Expression, context_expr); } Debug.Assert(native_uclass_fname != null); // Now we have to convert the unprefixed name, to a prefixed C++ type name obj_cpp_type_name = UE4Utility.DetermineNativeUClassCppTypeName(native_uclass_fname, context_expr); string uclass_display_name = UE4Utility.GetBlueprintClassDisplayName(uclass_fname); switch (prop_type) { case Prop.Object: { // if not native, add a suffix to the display type showing the blueprint class of the property // @NOTE: this is nothing to do with what object the value points to and what its type may be. property meta data only. string suffix = is_native ? String.Empty : String.Format(" [{0}]", uclass_display_name); string primary_type = String.Format("{0} *", obj_cpp_type_name); string primary_display = String.Format("{0} *{1}", obj_cpp_type_name, suffix); // fallback, no symbols available for the native base type, so use 'UObject' instead string fallback_type = String.Format("{0} *", Typ.UObject); string fallback_display = String.Format("{0}? *{1}", obj_cpp_type_name, suffix); return new CppTypeInfo[] { new CppTypeInfo(primary_type, primary_display), new CppTypeInfo(fallback_type, fallback_display) }; } case Prop.Asset: { // @NOTE: Don't really see anything to gain by casting to TAssetPtr< xxx >, since it's just another level of encapsulation that isn't // needed for visualization purposes. string suffix = String.Format(" [{0}]", is_native ? obj_cpp_type_name : uclass_display_name); string primary_type = Typ.AssetPtr; //String.Format("TAssetPtr<{0}>", obj_cpp_type_name); string primary_display = String.Format("{0}{1}", Typ.AssetPtr, suffix); // If just using FAssetPtr, no need for a fallback since we don't need to evaluate the specialized template parameter type return new CppTypeInfo[] { new CppTypeInfo(primary_type, primary_display) }; } default: Debug.Assert(false); return null; } } /* @TODO: Not so important. What's below is wrong, but essentially if we implement this, it's just to differentiate between UClass, UBlueprintGeneratedClass, etc case "ClassProperty": case "AssetClassProperty": { if (!Config.ShowExactUObjectTypes) { break; } // Need to find out the subtype of the property, which is specified by UClassProperty::MetaClass/UAssetClassProperty::MetaClass // Cast to whichever property type we are (either UClassProperty or UAssetClassProperty) string propclass_name = String.Format("U{0}", prop_type); var classprop_em = ExpressionManipulator.FromExpression(uprop_expr_str).PtrCast(propclass_name); // Either way, we have a member called 'MetaClass' which specified the base UClass stored in this property var subtype_uclass_em = classprop_em.PtrMember("MetaClass"); var subtype_fname_em = subtype_uclass_em.PtrCast("UObjectBase").PtrMember("Name"); string subtype_fname = UE4Utility.GetFNameAsString(subtype_fname_em.Expression, context_expr); return String.Format("U{0}*", subtype_fname); } */ } // Standard cases, just use cpp type stored in map. // If not found, null string will be returned. string cpp_type = null; if (ue4_proptype_map_.TryGetValue(prop_type, out cpp_type)) { return new CppTypeInfo[] { new CppTypeInfo(cpp_type) }; } else { return null; } }
protected virtual void DefineImplType (CppTypeInfo typeInfo) { string implTypeName = typeInfo.InterfaceType.Name + "_"; if (typeInfo.NativeLayout != null) implTypeName += typeInfo.NativeLayout.Name + "_"; implTypeName += this.GetType ().Name + "_Impl"; 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); var native_vtable_field = impl_type.DefineField ("_nativeVTable", typeof (IntPtr), FieldAttributes.InitOnly | FieldAttributes.Private); ConstructorBuilder ctor = impl_type.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof (CppTypeInfo), typeof (IntPtr) }); 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); // this._nativeVTable = (vtable ptr passed to constructor) ctor_il.Emit (OpCodes.Ldarg_0); ctor_il.Emit (OpCodes.Ldarg_2); ctor_il.Emit (OpCodes.Stfld, native_vtable_field); typeInfo.emit_info.ctor_il = ctor_il; typeInfo.emit_info.typeinfo_field = typeinfo_field; typeInfo.emit_info.native_vtable_field = native_vtable_field; typeInfo.emit_info.type_builder = impl_type; }
public sp_fdbg_ntvtab_s(CppTypeInfo subClass) { __cxxi_LayoutClass(); subClass.AddBase(impl.TypeInfo); }
protected override void EmitNativeCall (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, LocalBuilder nativePtr) { var il = typeInfo.emit_info.current_il; var method = psig.OrigMethod; var returnType = method.ReturnType; var hiddenReturnByValue = ReturnByHiddenArgument (typeInfo, method); LocalBuilder returnValue = null; if (hiddenReturnByValue) { returnValue = il.DeclareLocal (typeof (CppInstancePtr)); if (typeof (ICppObject).IsAssignableFrom (returnType)) il.Emit (OpCodes.Ldc_I4, GetTypeInfo (returnType).NativeSize); else if (returnType.IsValueType) il.Emit (OpCodes.Ldc_I4, Marshal.SizeOf (returnType)); il.Emit (OpCodes.Newobj, cppip_fromsize); il.Emit (OpCodes.Stloc, returnValue); il.Emit (OpCodes.Ldloca, returnValue); il.Emit (OpCodes.Call, cppip_native); } base.EmitNativeCall (typeInfo, nativeMethod, psig, nativePtr); if (hiddenReturnByValue) { EmitCreateCppObjectFromNative (il, returnType, returnValue); if (returnType.IsValueType) { // FIXME: This dispose should prolly be in a Finally block.. il.Emit (OpCodes.Ldloca, returnValue); il.Emit (OpCodes.Call, cppip_dispose); } } }