【问题标题】:Mono.Cecil: injecting try/finally?Mono.Cecil:注入try/finally?
【发布时间】:2012-09-27 23:51:13
【问题描述】:

很好地概述了在 Mono.Cecil was answered here 中实现 try/catch,但他在没有完整的 try/catch/finally 时就停了下来。那么如何使用 Mono.Cecil 实现 try/finally?

【问题讨论】:

    标签: .net mono.cecil


    【解决方案1】:

    这里是如何注入 finally。

    首先,您需要修正您的退货声明。你只想要一个。

    Instruction FixReturns()
    {
        if (Method.ReturnType == TypeSystem.Void)
        {
            var instructions = body.Instructions;
            var lastRet = Instruction.Create(OpCodes.Ret);
            instructions.Add(lastRet);
    
            for (var index = 0; index < instructions.Count - 1; index++)
            {
                var instruction = instructions[index];
                if (instruction.OpCode == OpCodes.Ret)
                {
                    instructions[index] = Instruction.Create(OpCodes.Leave, lastRet);
                }
            }
            return lastRet;
        }
        else
        {
            var instructions = body.Instructions;
            var returnVariable = new VariableDefinition("methodTimerReturn", Method.ReturnType);
            body.Variables.Add(returnVariable);
            var lastLd = Instruction.Create(OpCodes.Ldloc, returnVariable);
            instructions.Add(lastLd);
            instructions.Add(Instruction.Create(OpCodes.Ret));
    
            for (var index = 0; index < instructions.Count - 2; index++)
            {
                var instruction = instructions[index];
                if (instruction.OpCode == OpCodes.Ret)
                {
                    instructions[index] = Instruction.Create(OpCodes.Leave, lastLd);
                    instructions.Insert(index, Instruction.Create(OpCodes.Stloc, returnVariable));
                    index++;
                }
            }
            return lastLd;
        }
    }
    

    然后找到第一条指令。如果是实例构造函数,则需要跳过 2。

    Instruction FirstInstructionSkipCtor()
    {
        if (Method.IsConstructor && !Method.IsStatic)
        {
            return body.Instructions.Skip(2).First();
        }
        return body.Instructions.First();
    }
    

    然后缝合起来

    void InnerProcess()
    {
        body = Method.Body;
        body.SimplifyMacros();
        ilProcessor = body.GetILProcessor();
    
        var returnInstruction = FixReturns();
    
        var firstInstruction = FirstInstructionSkipCtor();
    
        var beforeReturn = Instruction.Create(OpCodes.Nop);
        ilProcessor.InsertBefore(returnInstruction, beforeReturn);
    
        InjectIlForFinaly(returnInstruction);
    
        var handler = new ExceptionHandler(ExceptionHandlerType.Finally)
            {
                TryStart = firstInstruction,
                TryEnd = beforeReturn,
                HandlerStart = beforeReturn,
                HandlerEnd = returnInstruction,
            };
    
        body.ExceptionHandlers.Add(handler);
        body.InitLocals = true;
        body.OptimizeMacros();
    }
    

    【讨论】:

    • 所以它与那篇文章中描述的过程基本相同,只是将 ExceptionHandlerType.Finally 传递给 new ExceptionHandler()。如果你想要一个 try/catch/finally,你将创建两个“ExceptionHandler”实例并将它们添加到方法中,一个用于 catch,一个用于 finally。如果是这种情况,那么这些名称肯定会令人困惑,因为您并没有真正创建新的异常处理程序,至少对于“finally”而言。谢谢指点。
    【解决方案2】:

    发现检查的示例非常有用且非常有用。但是,在更复杂的条件下确实遇到了问题。在 FixReturns() 中,无论是 void 还是非 void 返回范围,通过创建新指令来更改 ret -> leave 的方法都会孤立原件。这可以将其他指令与孤儿作为操作数(例如,分支到原始 ret)。结果代码最终无效。

    我们只是更新了现有的 ret 指令的操作码/操作数对,而不是创建新的,一切看起来都很好。

    干杯。

    【讨论】:

      猜你喜欢
      • 2012-01-24
      • 1970-01-01
      • 1970-01-01
      • 2014-11-27
      • 2011-08-31
      • 1970-01-01
      • 2017-04-12
      • 2018-03-19
      • 1970-01-01
      相关资源
      最近更新 更多