internal List <CodeInstruction> FinalizeILCodes(Emitter emitter, List <MethodInfo> transpilers, List <Label> endLabels, out bool hasReturnCode) { hasReturnCode = false; if (generator == null) { return(null); } // pass1 - define labels and add them to instructions that are target of a jump // foreach (var ilInstruction in ilInstructions) { switch (ilInstruction.opcode.OperandType) { case OperandType.InlineSwitch: { var targets = ilInstruction.operand as ILInstruction[]; if (targets != null) { var labels = new List <Label>(); foreach (var target in targets) { var label = generator.DefineLabel(); target.labels.Add(label); labels.Add(label); } ilInstruction.argument = labels.ToArray(); } break; } case OperandType.ShortInlineBrTarget: case OperandType.InlineBrTarget: { var target = ilInstruction.operand as ILInstruction; if (target != null) { var label = generator.DefineLabel(); target.labels.Add(label); ilInstruction.argument = label; } break; } } } // pass2 - filter through all processors // var codeTranspiler = new CodeTranspiler(ilInstructions, argumentShift); transpilers.Do(transpiler => codeTranspiler.Add(transpiler)); var codeInstructions = codeTranspiler.GetResult(generator, method); if (emitter == null) { return(codeInstructions); } emitter.LogComment("start original"); // pass3 - log out all new local variables // var savedLog = FileLog.GetBuffer(true); emitter.LogAllLocalVariables(); FileLog.LogBuffered(savedLog); // pass4 - check for any RET // hasReturnCode = codeInstructions.Any(code => code.opcode == OpCodes.Ret); // pass5 - remove RET if it appears at the end // while (true) { var lastInstruction = codeInstructions.LastOrDefault(); if (lastInstruction == null || lastInstruction.opcode != OpCodes.Ret) { break; } // remember any existing labels endLabels.AddRange(lastInstruction.labels); codeInstructions.RemoveAt(codeInstructions.Count - 1); } // pass6 - mark labels and exceptions and emit codes // codeInstructions.Do(codeInstruction => { // mark all labels codeInstruction.labels.Do(label => emitter.MarkLabel(label)); // start all exception blocks // TODO: we ignore the resulting label because we have no way to use it // codeInstruction.blocks.Do(block => { emitter.MarkBlockBefore(block, out var label); }); var code = codeInstruction.opcode; var operand = codeInstruction.operand; // replace RET with a jump to the end (outside this code) if (code == OpCodes.Ret) { var endLabel = generator.DefineLabel(); code = OpCodes.Br; operand = endLabel; endLabels.Add(endLabel); } // replace short jumps with long ones (can be optimized but requires byte counting, not instruction counting) if (shortJumps.TryGetValue(code, out var longJump)) { code = longJump; } switch (code.OperandType) { case OperandType.InlineNone: emitter.Emit(code); break; case OperandType.InlineSig: var cecilGenerator = generator.GetProxiedShim <CecilILGenerator>(); if (cecilGenerator == null) { // Right now InlineSignatures can only be emitted using MonoMod.Common and its CecilILGenerator. // That is because DynamicMethod's original ILGenerator is very restrictive about the calli opcode. throw new NotSupportedException(); } if (operand == null) { throw new Exception($"Wrong null argument: {codeInstruction}"); } if ((operand is ICallSiteGenerator) == false) { throw new Exception($"Wrong Emit argument type {operand.GetType()} in {codeInstruction}"); } emitter.AddInstruction(code, operand); emitter.LogIL(code, operand); cecilGenerator.Emit(code, (ICallSiteGenerator)operand); break; default: if (operand == null) { throw new Exception($"Wrong null argument: {codeInstruction}"); } emitter.AddInstruction(code, operand); emitter.LogIL(code, operand); _ = generator.DynEmit(code, operand); break; } codeInstruction.blocks.Do(block => emitter.MarkBlockAfter(block)); }); emitter.LogComment("end original"); return(codeInstructions); }