public static void MarkBlockAfter(this CecilILGenerator il, ExceptionBlock block) { if (block.blockType == ExceptionBlockType.EndExceptionBlock) { il.EndExceptionBlock(); } }
public static LocalBuilder GetLocal(this CecilILGenerator il, VariableDefinition varDef) { var vars = (Dictionary <LocalBuilder, VariableDefinition>)AccessTools .Field(typeof(CecilILGenerator), "_Variables") .GetValue(il); var loc = vars.FirstOrDefault(kv => kv.Value == varDef).Key; if (loc != null) { return(loc); } // TODO: Remove once MonoMod allows to specify this manually var type = varDef.VariableType.ResolveReflection(); var pinned = varDef.VariableType.IsPinned; var index = varDef.Index; loc = (LocalBuilder)( c_LocalBuilder_params == 4 ? c_LocalBuilder.Invoke(new object[] { index, type, null, pinned }) : c_LocalBuilder_params == 3 ? c_LocalBuilder.Invoke(new object[] { index, type, null }) : c_LocalBuilder_params == 2 ? c_LocalBuilder.Invoke(new object[] { type, null }) : c_LocalBuilder_params == 0 ? c_LocalBuilder.Invoke(new object[] { }) : throw new NotSupportedException() ); f_LocalBuilder_position?.SetValue(loc, (ushort)index); f_LocalBuilder_is_pinned?.SetValue(loc, pinned); vars[loc] = varDef; return(loc); }
public static void MarkBlockBefore(this CecilILGenerator il, ExceptionBlock block) { switch (block.blockType) { case ExceptionBlockType.BeginExceptionBlock: il.BeginExceptionBlock(); return; case ExceptionBlockType.BeginCatchBlock: il.BeginCatchBlock(block.catchType); return; case ExceptionBlockType.BeginExceptFilterBlock: il.BeginExceptFilterBlock(); return; case ExceptionBlockType.BeginFaultBlock: il.BeginFaultBlock(); return; case ExceptionBlockType.BeginFinallyBlock: il.BeginFinallyBlock(); return; case ExceptionBlockType.EndExceptionBlock: return; default: throw new ArgumentOutOfRangeException(); } }
public static Dictionary <LocalBuilder, VariableDefinition> GetGenDictionary(this ILGenerator gen) { CecilILGenerator CecilGen = gen.GetProxiedShim <CecilILGenerator>(); return((Dictionary <LocalBuilder, VariableDefinition>)AccessTools .Field(typeof(CecilILGenerator), "_Variables") .GetValue(CecilGen)); }
/// <summary>Returns the methods unmodified list of CodeInstructions</summary> /// <param name="original">The original method</param> /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param> /// <returns>A list containing all the original CodeInstructions</returns> public static List<CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator) { // Create a copy var dmd = new DynamicMethodDefinition(original); // Create a manipulator to obtain the instructions var manipulator = new ILManipulator(dmd.Definition.Body); generator = new CecilILGenerator(dmd.GetILProcessor()).GetProxy(); return manipulator.GetInstructions(generator); }
public static LocalBuilder GetLocal(this CecilILGenerator il, VariableDefinition varDef) { var vars = (Dictionary <LocalBuilder, VariableDefinition>)AccessTools .Field(typeof(CecilILGenerator), "_Variables") .GetValue(il); var loc = vars.FirstOrDefault(kv => kv.Value == varDef).Key; if (loc != null) { return(loc); } loc = il.DeclareLocal(varDef.VariableType.ResolveReflection()); il.IL.Body.Variables.Remove(vars[loc]); vars[loc] = varDef; return(loc); }
public static void Emit(this CecilILGenerator il, OpCode opcode, object operand) { emitCodeDelegate(il, opcode, operand); }
internal Emitter(ILGenerator il, bool debug) { this.il = il.GetProxiedShim <CecilILGenerator>(); this.debug = debug; }
/// <summary> /// Processes and writes IL to the provided method body. /// Note that this cleans the existing method body (removes insturctions and exception handlers). /// </summary> /// <param name="body">Method body to write to.</param> /// <param name="original">Original method that transpiler can optionally call into</param> /// <exception cref="NotSupportedException"> /// One of IL opcodes contains a CallSide (e.g. calli), which is currently not /// fully supported. /// </exception> /// <exception cref="ArgumentNullException">One of IL opcodes with an operand contains a null operand.</exception> public void WriteTo(MethodBody body, MethodBase original = null) { // Clean up the body of the target method body.Instructions.Clear(); body.ExceptionHandlers.Clear(); var il = new CecilILGenerator(body.GetILProcessor()); var cil = il.GetProxy(); // Define an "empty" label // In Harmony, the first label can point to the end of the method // Apparently, some transpilers naively call new Label() to define a label and thus end up // using the first label without knowing it // By defining the first label we'll ensure label count is correct il.DefineLabel(); // Step 1: Prepare labels for instructions Prepare(vDef => il.GetLocal(vDef), il.DefineLabel); // Step 2: Run the code instruction through transpilers var newInstructions = ApplyTranspilers(cil, original); // We don't remove trailing `ret`s because we need to do so only if prefixes/postfixes are present // Step 3: Emit code foreach (var ins in newInstructions) { ins.labels.ForEach(l => il.MarkLabel(l)); ins.blocks.ForEach(b => il.MarkBlockBefore(b)); // We don't replace `ret`s yet because we might not need to // We do that only if we add prefixes/postfixes // We also don't need to care for long/short forms thanks to Cecil/MonoMod // Temporary fix: CecilILGenerator doesn't properly handle ldarg switch (ins.opcode.OperandType) { case SRE.OperandType.InlineNone: il.Emit(ins.opcode); break; case SRE.OperandType.InlineSig: throw new NotSupportedException( "Emitting opcodes with CallSites is currently not fully implemented"); default: if (ins.operand == null) { throw new ArgumentNullException(nameof(ins.operand), $"Invalid argument for {ins}"); } il.Emit(ins.opcode, ins.operand); break; } ins.blocks.ForEach(b => il.MarkBlockAfter(b)); } // Note: We lose all unassigned labels here along with any way to log them // On the contrary, we gain better logging anyway down the line by using Cecil // Step 4: Run the code through raw IL manipulators (if any) // TODO: IL Manipulators }