.Net RuntimeExplorer开发日志(三) IL to C# - 解析switch语句(1)

  在switch语句的解析上大概花费了四个公休日和无数的零散时间,写了删,删了写,来来回回费劲,直到思路慢慢理清,总算完成代码。

  在IL的switch语句中,operand是一个数组,执行的动作是从栈上pop出一个判断值,并以此数值为下标跳转到数组中的offset。原则上这可以跳转到代码的任何地方,未混淆的代码就会那样,但我们的目的是尽量分析,回复到能够读懂的代码,大体的思路如下。

  在switch的operand数组中的值会是以下这几类:

  (一)有效判断值,有代码,跳转到offset

  (二)有效判断值,无代码,跳转到out offset

  (三)有效判断值,无实际代码,只有continue,goto关键字,则直接跳到关键字的目标offset

  (四)无效判断值,有default,跳转到default offset

  (五)无效判断,无default,跳转到out offset

  switch语句后面通常会有一个br语句,而在switch和br之间可能会有不定量条件选择语句或default块,它们是为了弥补判断值不是int或是较大的int值。也就是说在这里把无法用数组下标判断的非整数或是大整数变成了一串 if () {} ,这些独立的if在判断成功后也全都会跳转到后面的代码中,成为独立的case块存在。

  有实际代码的case块都会按代码的书写顺序排列在switch的br语句下面,每个有代码的case块的上一句IL语句一定会是一个结束句,br,goto,ret这三种,否则的话就会让运行流程穿多个case块,这在编译时是过不去的。再说详细一点,第一个case块上方的br语句跳转的目标有两个可能,default offset或out offset,而其它case块上方,只要是br语句只能跳转到out offset,而且在正常编译中全部的case块的上方br语句中一定会有一个跳转到out offset。由于out offset一定是这些值中最大的,我们很容易就找到整个switch的出口点。对于default块存在的位置有两种情况,在switch语句下面,那么switch下方的第一个br语句则一定是跳转到out offset;在case块的最下面,那么switch下方的br语句则会指向它。

  实际上就算理清了这些条条,还是又重写了一遍,代码数量直接翻倍,因为忘记了一个特殊的情况——default中就只有一句continue。

.Net RuntimeExplorer开发日志(三) IL to C# - 解析switch语句(1)

上一篇:[WPF]是时候将WPF控件库从.Net Framework升级到.NET Core 3.1


下一篇:C# 中的数据类型整理