由于没想到合适的 Evaluation Stack 对应的中文,索性就不给它中文名了。
Evaluation Stack 是基于 MSIL 应用程序(C#、F#、VB.NET 语言应用)的关键结构,它是应用程序 和 内存之间的桥梁。
它跟普通的栈有一些关键性的区别。
你的应用程序 可以通过 使用 Evaluation Stack ,去访问 函数参数、局部变量、临时对象等。
通常,函数参数和局部变量 是存在内存【栈】上的,但是你的函数 却不能直接访问这些信息。想访问这些数据,必须使用 load 命令,把它们从内存 移动到 Evaluation Stack 的 Slot(槽,4字节或8字节的单元)上;反过来,可以用(store命令)更新Evaluation Stack上的 局部变量或参数。
Evaluation Stack 是个栈,因此,也遵循 先进后出(LIFO)的原则。 当函数开始的时候,Evaluation Stack是空的。 随着函数的执行,会向Evaluation Stack里面增删元素。在函数退出前,除有返回值的情况外,Evaluation Stack必须是空。Jmp、Tail命令是这个规则的例外。如果函数退出时Evaluation Stack不为空,CLR会抛出InvalidProgramException 异常。
.maxstack 是用来限制 Evaluation Stack 大小的命令,是可选的。如果没有写,默认Evaluation Stack 提供8个Slot。.maxstack 用来确保应用如预期的执行。如果执行时,Evaluation Stack的长度 超过了.maxstack 指定的长度,则可能代表着 程序收到了潜在的逻辑问题 或者安全风险。不管如何,这种情况都值得提醒用户。
简言之,Evaluation Stack就是 函数 和 内存间的桥梁。
----------------------------------
以下不是翻译:
以一个简单的函数为例:
public int Add(int fact1, int fact2) { return fact1 + fact2; }
编译后的IL,可以看出函数的一般“套路”:对参数、返回值、局部变量的读写,就是在操作 Evaluation Stack。
// 方法 .method public hidebysig instance int32 Add ( int32 fact1, int32 fact2 ) cil managed { // 方法起始 RVA 地址 0x30a4 // 方法起始地址(相对于文件绝对值:0x12a4) // 代码长度 9 (0x9) .maxstack 2 //CC note:限定了 这个函数的 evaluation stack 大小是 2。函数执行过程中,,evaluation stack长度不能超过2。否则会有安全告警 .locals init ( [0] int32 ) // 0x12B0: 00 IL_0000: nop // 0x12B1: 03 IL_0001: ldarg.1 //CCNote:把第1个参数 加载到 evaluation stack // 0x12B2: 04 IL_0002: ldarg.2 //CCNote:把第2个参数 加载到 evaluation stack // 0x12B3: 58 IL_0003: add //CCNote:从 evaluation stack 上 pop 出来2个元素,进行加法运算。再把结果 push 到 evaluation stack // 0x12B4: 0A IL_0004: stloc.0 //CCNote:从 evaluation stack 上 pop 出来1个元素,并把它存到 局部变量列表的 第0号 元素中。 // 0x12B5: 2B 00 IL_0005: br.s IL_0007 // 0x12B7: 06 IL_0007: ldloc.0 //CCNote:把 局部变量列表的 第0号元素 加载到 evaluation stack // 0x12B8: 2A IL_0008: ret //CCNote:return 命令, 从 被调函数的 evaluation stack中 Pop出来 顶上元素,然后 push到 调用方的 evaluation stack 中。 } // 方法 HelloWorld::Add 结束