我一直在研究PascalScript 脚本引擎的第 14 期,其中使用 Goto 命令跳出 Case 块会产生编译器错误,即使这是完全有效(如果丑陋)的 Object Pascal 代码。
结果编译器中的 ProcessCase 例程调用 HasInvalidJumps,它会扫描任何在 Case 块之外的 Goto,如果找到,则会给出编译器错误。如果我评论检查,它编译得很好,但最终在运行时崩溃。字节码的反汇编说明了原因。我已经用原始脚本代码对其进行了注释:
[TYPES]
<SNIPPED>
[VARS]
Var [0]: 27 Class TFORM
Var [1]: 28 Class TAPPLICATION
Var [2]: 11 S32 //i: integer
[PROCS]
Proc [0] Export: !MAIN -1
{begin}
[0] ASSIGN GlobalVar[2], [1]
{ i := 1;}
[15] PUSHTYPE 11(S32) // 1
[20] ASSIGN Base[1], GlobalVar[2]
{ case i of}
[31] PUSHTYPE 25(U8) // 2
{ 0:}
[36] COMPARE into Base[2]: [0] = Base[1]
[57] COND_NOT_GOTO currpos + 5 Base[2] [72]
{ end;}
[67] GOTO currpos + 41 [113]
{ 1:}
[72] COMPARE into Base[2]: [1] = Base[1]
[93] COND_NOT_GOTO currpos + 10 Base[2] [113]
{ goto L1;}
[103] GOTO currpos + 8 [116]
{ end;}
[108] GOTO currpos + 0 [113]
{ end; //<-- case}
[113] POP // 1
[114] POP // 0
{ Exit;}
[115] RET
{L1:
Writeln('Label L1');}
[116] PUSHTYPE 17(WideString) // 1
[121] ASSIGN Base[1], ['????????']
[144] CALL 1
{end.}
[149] POP // 0
[150] RET
Proc [1]: External Decl: \00\00 WRITELN
“转到 L1;” 103 处的语句跳过了 113 和 114 处的清理弹出,这使堆栈处于无效状态。
Delphi 对此没有任何问题,因为它不使用计算堆栈。然而,PascalScript 就没有那么幸运了。我需要一些方法来完成这项工作,因为这种模式在一些来自更简单系统的遗留脚本中非常常见,而我已经翻译成 PascalScript 并且需要能够支持的控制结构的方式很少。
任何人都知道如何修补 codegen 以便正确清理堆栈?