25.ILR解释器

25.原理相关-解释器


25.1 知识点

ILRuntime中的解释器用来干什么?

当ILRuntime通过Mono.Cecil库将DLL文件中的 IL 中间语言读取出来后,会利用已经写好的相关代码将IL代码解释翻译,之后用于执行。它主要做了以下几个步骤:

  1. 遍历方法体中的每一条指令。
  2. 使用内部巨大的 switch case 用于处理IL中的每一条指令,判断不同的指令类型来处理对应逻辑。

在执行代码时,使用ILRuntime内部自己实现的运行栈来管理内存。ILRuntime中使用非托管内存,内存不会被GC管理,而是ILRuntime内部自己管理,通过指针直接对内存进行操作。其中使用自定义类 StackObject 来表达基础类型值。

ILRuntime中的内存布局

在ILRuntime实现的非托管运行栈中存储的对象主要是StackObject对象。在该运行栈中,StackObject对象是依次排列的,我们只需要移动当前的栈指针就可以获取到栈中存储的各数据。

                     **StackObject** (栈指针 - 1)
                        ObjectType
                          Value
                         ValueLow
 栈指针   ————>      **StackObject** (栈指针)
                        ObjectType
                          Value
                         ValueLow
                     **StackObject** (栈指针 + 1)
                        ObjectType
                          Value
                         ValueLow

ILRuntime中方法指令的调用

  1. 按顺序将参数依次压栈。
  2. 利用栈指针的移动获取到各参数进行处理。
  3. 清理栈(将栈指针移动到顶部,下方内容就认为会被释放了)。
  参数1                  返回值
  参数2                  栈指针
  局部变量1    ——————>
  局部变量2
  栈指针

关于CLR重定向

我们可以在ILIntepreter脚本中搜索callvirt指令(对对象调用后期绑定方法,并且将返回值推送到计算堆栈上)。
在该指令处理中,如果直接是IL方法直接执行,不是的话,再判断,如果发现是CLR重定向,会调用对应绑定的委托函数,不是的话调用反射。关键参数是esp 代表当前栈指针位置。

我们可以在ILIntepreter脚本中搜索.Add,把栈指针前移,得到参数a和b,栈指针最终指向栈顶,计算返回值。

 b = esp - 1;
 a = esp - 1 - 1;
 esp = a;

//  参数a              
//  参数b               
//  栈指针

根据a的类型计算返回值后让栈指针++。

switch (a->ObjectType)
{
    case ObjectTypes.Long:
        *((long*)&esp->Value) = *((long*)&a->Value) + *((long*)&b->Value);
        break;
    case ObjectTypes.Integer:
        esp->Value = a->Value + b->Value;
        break;
    case ObjectTypes.Float:
        *((float*)&esp->Value) = *((float*)&a->Value) + *((float*)&b->Value);
        break;
    case ObjectTypes.Double:
        *((double*)&esp->Value) = *((double*)&a->Value) + *((double*)&b->Value);
        break;
    default:
        throw new NotImplementedException();
}
esp++;

// 返回值设置
// 栈指针++

总结

ILRuntime的解释器利用已经写好的相关代码将IL代码解释翻译后进行执行。对于我们来说,需要大致了解它的内存布局,以及方法指令是如何调用的。这样我们才能够看懂或书写CLR重定向相关的逻辑。


25.2 练习题

ILRuntime中栈指针的加减对于我们的意义是什么?

  1. 可以利用栈指针的加减获取到之前压入栈中的各参数
  2. 可以改变栈指针的位置,来清理栈


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏