Ejemplo n.º 1
0
        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);
        }