/// <summary> /// Get the managed size of a given type. This matches an IL-level sizeof(t), even if it cannot be determined normally in C#. /// Note that sizeof(t) != Marshal.SizeOf(t), f.e. when t is char. /// </summary> /// <param name="t">The type to get the size from.</param> /// <returns>The managed type size.</returns> public static int GetManagedSize(this Type t) { if (_GetManagedSizeCache.TryGetValue(t, out int size)) return size; if (_GetManagedSizeHelper == null) { Assembly asm; const string @namespace = "MonoMod.Utils"; const string @name = "GetManagedSizeHelper"; const string @fullName = @namespace + "." + @name; #if !CECIL0_9 using ( #endif ModuleDefinition module = ModuleDefinition.CreateModule( @fullName, new ModuleParameters() { Kind = ModuleKind.Dll, #if !CECIL0_9 && MONOMOD_UTILS ReflectionImporterProvider = MMReflectionImporter.Provider #endif } ) #if CECIL0_9 ; #else ) #endif { TypeDefinition type = new TypeDefinition( @namespace, @name, MC.TypeAttributes.Public | MC.TypeAttributes.Abstract | MC.TypeAttributes.Sealed ) { BaseType = module.TypeSystem.Object }; module.Types.Add(type); MethodDefinition method = new MethodDefinition(@name, MC.MethodAttributes.Public | MC.MethodAttributes.Static | MC.MethodAttributes.HideBySig, module.TypeSystem.Int32 ); GenericParameter genParam = new GenericParameter("T", method); method.GenericParameters.Add(genParam); type.Methods.Add(method); ILProcessor il = method.Body.GetILProcessor(); il.Emit(OpCodes.Sizeof, genParam); il.Emit(OpCodes.Ret); asm = ReflectionHelper.Load(module); } _GetManagedSizeHelper = asm.GetType(@fullName).GetMethod(@name); } size = (_GetManagedSizeHelper.MakeGenericMethod(t).CreateDelegate<Func<int>>() as Func<int>)(); lock (_GetManagedSizeCache) { return _GetManagedSizeCache[t] = size; } }
protected override MethodInfo _Generate(DynamicMethodDefinition dmd, object context) { MethodDefinition def = dmd.Definition; TypeDefinition typeDef = context as TypeDefinition; bool moduleIsTemporary = false; ModuleDefinition module = typeDef?.Module; HashSet <string> accessChecksIgnored = null; if (typeDef == null) { moduleIsTemporary = true; accessChecksIgnored = new HashSet <string>(); string name = dmd.GetDumpName("Cecil"); module = ModuleDefinition.CreateModule(name, new ModuleParameters() { Kind = ModuleKind.Dll, #if !CECIL0_9 ReflectionImporterProvider = MMReflectionImporter.ProviderNoDefault #endif }); module.Assembly.CustomAttributes.Add(new CustomAttribute(module.ImportReference(DynamicMethodDefinition.c_UnverifiableCodeAttribute))); if (dmd.Debug) { CustomAttribute caDebug = new CustomAttribute(module.ImportReference(DynamicMethodDefinition.c_DebuggableAttribute)); caDebug.ConstructorArguments.Add(new CustomAttributeArgument( module.ImportReference(typeof(DebuggableAttribute.DebuggingModes)), DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.Default )); module.Assembly.CustomAttributes.Add(caDebug); } typeDef = new TypeDefinition( "", $"DMD<{dmd.OriginalMethod?.Name?.Replace('.', '_')}>?{GetHashCode()}", Mono.Cecil.TypeAttributes.Public | Mono.Cecil.TypeAttributes.Abstract | Mono.Cecil.TypeAttributes.Sealed | Mono.Cecil.TypeAttributes.Class ) { BaseType = module.TypeSystem.Object }; module.Types.Add(typeDef); } try { #pragma warning disable IDE0039 // Use local function Relinker relinker = (mtp, ctx) => { return(module.ImportReference(mtp)); }; #pragma warning restore IDE0039 // Use local function MethodDefinition clone = new MethodDefinition("_" + def.Name.Replace('.', '_'), def.Attributes, module.TypeSystem.Void) { MethodReturnType = def.MethodReturnType, Attributes = Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.Static, ImplAttributes = Mono.Cecil.MethodImplAttributes.IL | Mono.Cecil.MethodImplAttributes.Managed, DeclaringType = typeDef, NoInlining = true }; foreach (ParameterDefinition param in def.Parameters) { clone.Parameters.Add(param.Clone().Relink(relinker, clone)); } clone.ReturnType = def.ReturnType.Relink(relinker, clone); typeDef.Methods.Add(clone); clone.HasThis = def.HasThis; Mono.Cecil.Cil.MethodBody body = clone.Body = def.Body.Clone(clone); foreach (VariableDefinition var in clone.Body.Variables) { var.VariableType = var.VariableType.Relink(relinker, clone); } foreach (ExceptionHandler handler in clone.Body.ExceptionHandlers) { if (handler.CatchType != null) { handler.CatchType = handler.CatchType.Relink(relinker, clone); } } for (int instri = 0; instri < body.Instructions.Count; instri++) { Instruction instr = body.Instructions[instri]; object operand = instr.Operand; // Import references. if (operand is ParameterDefinition param) { operand = clone.Parameters[param.Index]; } else if (operand is IMetadataTokenProvider mtp) { operand = mtp.Relink(relinker, clone); } if (operand is DynamicMethodReference dmref) { // TODO: Fix up DynamicMethod inline refs. } if (accessChecksIgnored != null && operand is MemberReference mref) { IMetadataScope asmRef = (mref as TypeReference)?.Scope ?? mref.DeclaringType.Scope; if (!accessChecksIgnored.Contains(asmRef.Name)) { CustomAttribute caAccess = new CustomAttribute(module.ImportReference(DynamicMethodDefinition.c_IgnoresAccessChecksToAttribute)); caAccess.ConstructorArguments.Add(new CustomAttributeArgument( module.ImportReference(typeof(DebuggableAttribute.DebuggingModes)), asmRef.Name )); module.Assembly.CustomAttributes.Add(caAccess); accessChecksIgnored.Add(asmRef.Name); } } instr.Operand = operand; } clone.HasThis = false; if (def.HasThis) { TypeReference type = def.DeclaringType; if (type.IsValueType) { type = new ByReferenceType(type); } clone.Parameters.Insert(0, new ParameterDefinition("<>_this", Mono.Cecil.ParameterAttributes.None, type.Relink(relinker, clone))); } if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MONOMOD_DMD_DUMP"))) { string dir = Path.GetFullPath(Environment.GetEnvironmentVariable("MONOMOD_DMD_DUMP")); string name = module.Name + ".dll"; string path = Path.Combine(dir, name); dir = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } if (File.Exists(path)) { File.Delete(path); } using (Stream fileStream = File.OpenWrite(path)) module.Write(fileStream); } Assembly asm = ReflectionHelper.Load(module); return(asm.GetType(typeDef.FullName.Replace("+", "\\+"), false, false) .GetMethod(clone.Name, BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)); } finally { #if !CECIL0_9 if (moduleIsTemporary) { module.Dispose(); } #endif } }