【问题标题】:How do I get an IL bytearray from a DynamicMethod?如何从 DynamicMethod 获取 IL 字节数组?
【发布时间】:2010-11-10 16:55:15
【问题描述】:

作为一个新奇事物,我试图看看 IL 与运行时生成的轻量级代码与 VS 编译器生成的代码有何不同,因为我注意到 VS 代码往往以不同的性能配置文件运行对于演员表之类的东西。

于是我写了如下代码::

Func<object,string> vs = x=>(string)x;
Expression<Func<object,string>> exp = x=>(string)x;
var compiled = exp.Compile(); 
Array.ForEach(vs.Method.GetMethodBody().GetILAsByteArray(),Console.WriteLine);
Array.ForEach(compiled.Method.GetMethodBody().GetILAsByteArray(),Console.WriteLine);

不幸的是,这会引发异常,因为 GetMethodBody 显然是对表达式树生成的代码的非法操作。我如何以库的方式(即不使用外部工具,除非该工具具有 API)查看使用轻量级代码生成的代码生成的代码?

编辑:错误发生在第 5 行,compiled.Method.GetMethodBody() 抛出异常。

编辑2: 有谁知道如何恢复方法中声明的局部变量?还是没有办法GetVariables?

【问题讨论】:

  • 哪一行抛出异常?你能注释掉第一个 Array.ForEach 并看看它是否有效吗?我怀疑对 GetMethodBody() 的第一次调用失败仅仅是因为该表达式尚未编译为 IL。我看不出第二次调用失败的原因。
  • 有趣的问题。我在 GetMethodBody 调用中收到 InvalidOperationException("Operation in not valid due to the current state of the object")。我不确定从 CachedAnonymousDelegate 与 Expression 开始生活会如何影响您作为 Func 的行为。我会继续做这个。
  • 应切换所选答案,因为它不涵盖所有情况并且不必要地复杂。请参阅this answer
  • 对于局部变量,我相信你必须访问 m_localSignature 并根据 ECMA 335 解析它

标签: c# dynamicmethod


【解决方案1】:

是的,不行,方法是Reflection.Emit生成的。 IL 存储在 MethodBuilder 的 ILGenerator 中。你可以把它挖出来,但你必须非常绝望。需要反思才能到达内部和私人成员。这适用于 .NET 3.5SP1:

using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
...

        var mtype = compiled.Method.GetType();
        var fiOwner = mtype.GetField("m_owner", BindingFlags.Instance | BindingFlags.NonPublic);
        var dynMethod = fiOwner.GetValue(compiled.Method) as DynamicMethod;
        var ilgen = dynMethod.GetILGenerator();
        var fiBytes = ilgen.GetType().GetField("m_ILStream", BindingFlags.Instance | BindingFlags.NonPublic);
        var fiLength = ilgen.GetType().GetField("m_length", BindingFlags.Instance | BindingFlags.NonPublic);
        byte[] il = fiBytes.GetValue(ilgen) as byte[];
        int cnt = (int)fiLength.GetValue(ilgen);
        // Dump <cnt> bytes from <il>
        //...

在 .NET 4.0 上,您必须使用 ilgen.GetType().BaseType.GetField(...),因为 IL 生成器已更改,DynamicILGenerator 派生自 ILGenerator。

【讨论】:

  • 哦,该死的太丑了。干得好......很遗憾代码不能更漂亮。 :(
  • 刚试过,这在.NET 4中似乎不起作用。它告诉我fiBytes为空:(
  • @Michael - 是的,这就是反射的风险。 IL 生成器已更改。你必须使用 ilgen.GetType().BaseType.GetField(...)
  • 当心,这不适用于所有DynamicMethods。 See this answer 获取当前解决方案。
  • 嗯,另一个绝望的程序员,我想我必须在接下来的 50 年里听他们的。哦,快乐。
【解决方案2】:

这里的 ILReader 应该可以工作。

ILVisualizer 2010 Solution

【讨论】:

    【解决方案3】:

    根据 Hans Passant 的工作,我能够更深入地挖掘,似乎有一个您应该调用的方法,称为 BakeByteArray,因此以下工作::

    var dynMethod = fiOwner.GetValue(compiled.Method) as DynamicMethod;
    var ilgen =dynamicMethod.GetILGenerator();
    byte[] il = ilgen.GetType().GetMethod("BakeByteArray", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(ilgen, null) as byte[];
    

    这当然有帮助,但我仍然无法解决 VariableInfo's 的问题,这对我的工作会有帮助。

    【讨论】:

    • 你烤了两次。不确定它是否会被烧毁,可能不会。
    • 我不确定,但是这段代码完全没用,因为我无法分解我使用这种方法实际制作的代码,因为我无法将 metadataToken 解析为任何有用的模块被列在上面的东西似乎一文不值。
    • 来自 AbdelRaheim 的 answer 的 ILVisualizer 也使用带有反射的 BakeByteArray
    【解决方案4】:

    这里的当前解决方案不能很好地解决 .NET 4 中的当前情况。您可以使用DynamicILInfoILGenerator 创建动态方法,但此处列出的解决方案不适用于DynamicILInfo 动态方法根本

    无论您使用DynamicILInfo 方法生成IL 还是ILGenerator 方法,IL 字节码都以DynamicMethod.m_resolver.m_code 结尾。您不必检查这两种方法,这是一个不太复杂的解决方案。

    这是您应该使用的版本:

    public static byte[] GetILBytes(DynamicMethod dynamicMethod)
    {
        var resolver = typeof(DynamicMethod).GetField("m_resolver", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(dynamicMethod);
        if (resolver == null) throw new ArgumentException("The dynamic method's IL has not been finalized.");
        return (byte[])resolver.GetType().GetField("m_code", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(resolver);
    }
    

    See this answer 获取更多帮助方法和 DynamicMethod 令牌解析问题的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-06
      • 1970-01-01
      • 2021-05-02
      • 2019-07-23
      相关资源
      最近更新 更多