2009-06-21 17 views
1

我一直在使用PascalScript腳本引擎上的Issue 14,其中使用Goto命令跳出Case區塊會產生編譯器錯誤,即使這是完全有效的(如果難看的話)Object Pascal代碼。跳出某個塊時,是否有安全的方式來清除基於堆棧的代碼?

發現編譯器中的ProcessCase例程調用HasInvalidJumps,它掃描在Case區塊外面導致的任何Gotos,並在找到一個時發出編譯器錯誤。如果我發表評論,它編譯得很好,但最終會在運行時崩潰。反彙編字節碼顯示了原因。我已用原始腳本代碼對其進行註釋:

[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 

「goto L1;」在103處的語句跳過113和114處的清除彈出,這使得堆棧處於無效狀態。

德爾福沒有任何問題,因爲它不使用計算堆棧。然而,PascalScript並不如此幸運。我需要一些方法來完成這項工作,因爲這種模式在一些比較簡單的系統的傳統腳本中非常常見,而且很少有我已經轉換爲PascalScript並需要支持的控制結構。

任何人有任何想法如何打補丁Codegen所以它會正確地清理堆棧?

回答

1

的簡單的解決辦法是:

當生成用於GOTO goto語句,前綴GOTO與RET之前出現相同的清理代碼。

+0

雖然展開堆棧可能在這裏工作,但我不確定它適用於所有情況。 – skamradt 2009-06-22 16:55:44

+0

明天你會得到一個跳出兩個嵌套case語句的goto,但是在標籤後面有一些代碼,你可以從 – 2009-06-26 21:20:04

3

IIRC經典帕斯卡轉到規則是:

  • 跳躍只允許一個塊外(督察從高到較低嵌套級別上樹的「相同」分支)
  • 從當地程序到他們的父母。

後來是afaik從來沒有得到Borland派生帕斯卡支持,但第一個仍然成立。

所以你需要像馬丁說的那樣生成現有的代碼,但可能它是用於多個塊級別的,所以你不能爲每個goto代碼生成代碼,但必須生成代碼(以退出需要的精確數量塊)。

一個典型的測試模式是使用goto退出多個嵌套ifs(可能在一個循環中),因爲這是一個經典的微優化,其速度至少高達D7。

請記住,如果評估和其分支的begin..end塊可能產生了需要清理的臨時數據。

----------後

加入我覺得codegenerator需要一種方式來走GOTO和其端點之間的範圍,產生了沿途塊相關的退出代碼。這樣一個修補程序適用於一般情況,而不僅僅是這個例子。 既然你只能跳出範圍,而不是跳入範圍,那可能不那麼難。

IOW生成的東西,它等效於(對於一個假想的雙殼體塊)

Lgoto1gluecode: //退出代碼第一框 彈出X 彈出ý //退出代碼第一框 彈出甲 彈出B 轉到real_goto_destination

可以進行額外的分析。例如。如果只有一個範圍,並且它已經有一個清理出口標籤,則可以直接跳轉。如果您確定上述彈出窗口只是丟棄值(而不是保存寄存器),您可以一次添加$ 16,%esp(4 * 4字節值)等值。

1

它看起來像我計算跳遠有多遠是個問題。我將不得不花費一些時間來查看解析器的實現以進一步提供幫助,但我的猜測是,使用goto時必須執行額外的處理,並且堆棧中有值,並且goto會放在這些值之後從堆棧中移除。當然,要確定這一點,您需要將正在解析的當前位置(goto)和正向解析保存到目標位置,以監視堆棧更改,如果是,則要麼向後調整goto位置,要麼將代碼注入Martin建議。