專利名稱:用于多個異常處理模型的中間表示的制作方法
技術領域:
該技術領域涉及編譯器計算機程序的組件,尤其涉及用于編譯程序的異常處理構造的中間表示。
背景一般而言,轉換器是接收以一種計算機編程語言編寫的程序作為輸入,并產生另一種編程語言的程序作為輸出的計算機程序。接收高級源語言(例如,C++、JAVA等)作為輸入,并生成諸如匯編語言或機器語言等低級語言作為輸出的轉換器有時候被更特別地稱為編譯器。編譯器程序內的轉換過程一般由多個階段構成。
圖1示出了一個流程圖,它示出了編譯器的多個階段的一個這樣的分解。在110接收源代碼的源程序表示。然后在120,詞法分析器將源代碼的各字符分離成被稱為標記的邏輯組。標記可以是源語言的句法中諸如IF或WHILE等關鍵詞、諸如+和-等操作符、標識符以及標點符號。在130,句法分析器將標記組合在一起成為諸如表達式或語句等句法結構。在140,生成源代碼的中間表示(IR),包括異常處理構造,以便于編譯器的后端操作,諸如150處的代碼優(yōu)化以及隨后160處的代碼生成。在編譯器過程中可以有多個中間表示。在代碼優(yōu)化階段150,可指示各種技術改進在140處生成的中間表示,使得最終的目標代碼能更快地運行且使用更少的存儲器。在160處的結束階段,代碼生成器產生要由處理器執(zhí)行的目標程序(目標代碼)170。
當檢測到源程序中的缺陷時,調用異常處理。在現(xiàn)有的編譯器框架中,源程序內的異常處理構造是與中間表示的主控制流分開處理的。傳統(tǒng)上,異常處理構造不在中間表示的控制流中顯式地表示。在一種公知的技術中,源代碼中檢測到異常處理構造的區(qū)域與主控制流劃界,且因此不經受與主控制流相同的代碼優(yōu)化技術。在又一方法中,異常處理構造是在主控制流之外的表中捕捉的,且編譯器后端分開處理它們。由此,需要一種用于異常處理構造的中間表示,它允許這類構造能夠在主控制流中顯式地表示,以利用與源代碼的其余部分相同的代碼優(yōu)化和代碼生成技術(即,編譯器后端)。
同樣,傳統(tǒng)上,中間表示是對源語言專用的。由此,編譯器必須知道與每一表示相關聯(lián)的源語言的專用異常處理模型。為此,這些異常處理模型通常由四個特征來表征。第一個特征確定異常是同步還是異步的。同步異常與拋出且處理該異常的控制線程的動作相關聯(lián)。在這一情況下,異??偸桥c線程的指令相關聯(lián)。換言之,異常處理動作是在某些條件失敗時由指令調用的。然而,異步異常被注入到拋出并處理該異常的線程之外的其它控制線程中。在Microsoft CLR中這可由通過系統(tǒng)API中止線程引起。這些異常不與特定的指令相關聯(lián)。其效應是在線程中稱為同步點的某一合適的點出引發(fā)異常。
其次,異??山K止或恢復導致異常的指令。在終止異常的情況下,終止指令,且啟動過濾器、處理程序或結束動作。然而在恢復模型的情況下,有錯誤的指令可在執(zhí)行了某一異常處理動作之后自動恢復。C/C++中的結構化異常處理(SEH)構造落入這一類別。這通常要求保護包括導致異常的指令的整個區(qū)域,好像所有存儲器訪問都如易失性存儲那樣運作。由此,禁止存儲器訪問的任何優(yōu)化。
第三,異常處理模型可以是精確或不精確的。在精確異常處理模型中,兩個指令的相對順序需要保留存儲器狀態(tài)的可觀察行為。這意味著如果處理程序或另一代碼片段看到不同的變量值,則不能執(zhí)行對指令的重排。諸如C#、Microsoft CLR和C++等語言要求精確的機制。在這些模型中,編譯器可能需要相對于彼此重排異常指令,以及其效應全局可見的任何其它指令。在不精確模型中,未定義異常效應上指令的相對順序,且編譯器可以自由地重排這些指令。在任一模型中,總是定義異常指令及其處理程序之間的順序,且該順序基于控制依賴性。如Ada等某些語言具有不精確異常模型。
異常處理模型的第四種特征是如何在各種異常處理模型中執(zhí)行處理程序關聯(lián)。在包括C++、C#和Microsoft CLR的大多數(shù)語言中,處理程序關聯(lián)是詞法上的且是靜態(tài)執(zhí)行的。這意味著標識處理程序代碼的起始是靜態(tài)可能的,且是唯一的。如以下所解釋的,靜態(tài)地標識處理程序主體的這一屬性可用于生成異常處理指令的中間表示。由此,需要一種單獨的統(tǒng)一框架用于中間地表示異常處理構造,該框架跨表示異常處理的多個模型是統(tǒng)一的,且能夠考慮到上述這些模型的各種屬性。
概要如此處所描述的,可使用異常處理構造的統(tǒng)一的中間表示來表達各種語言的異常處理模型。在一個方面,此處描述了涉及中間表示的單個指令集,用于表達多個不同的異常處理機制。例如,可使用一公共相關指令集來描述從try區(qū)域到finally區(qū)域然后到finally區(qū)域外部的控制流。在又一方面,可使用公共相關指令集來表達從try區(qū)域到catch區(qū)域的控制流。此外,也可表達保護處理程序或catch區(qū)域的過濾器。也可表達從try區(qū)域到“except”區(qū)域以在某些條件下將控制傳遞回導致異常的區(qū)域的控制流。也可使用該異常處理構造的統(tǒng)一中間表示來表達涉及對象析構函數(shù)的異常處理控制流。
在再一方面,此處描述了用于生成表達異常處理構造的控制流的統(tǒng)一中間表示的方法和系統(tǒng)。在一方面,中間表示可通過轉換源代碼文件的中間語言表示來生成。可使用多個不同的中間語言來生成異常處理構造的中間表示。在又一方面,異常處理構造的中間表示可由軟件開發(fā)工具用于諸如代碼生成、代碼優(yōu)化、分析等任務。
當參考附圖繼續(xù)閱讀以下所示的實施例的詳細描述時,可以清楚其它特征和優(yōu)點。
附圖簡述圖1是示出典型的編譯器及其組件的各種處理階段的流程圖。
圖2是示出用于使用能夠表示多個語言專用異常處理模型的統(tǒng)一異常處理框架來生成異常處理指令的中間表示的系統(tǒng)的框圖。
圖3A是示出用于使用能夠表示多個語言專用異常處理模型的統(tǒng)一異常處理框架來生成異常處理指令的中間表示的方法的流程圖。
圖3B是示出用于讀取軟件的中間表示并從其中生成可執(zhí)行版本的方法的流程圖。
圖4是示出用于CIL和MSIL語言的多個IL讀取器的圖2的系統(tǒng)的一個實施例的框圖。
圖5是用于中間表示的指令的數(shù)據(jù)結構的一個實施例的圖示。
圖6是未保護的導致異常的指令的偽代碼表示的清單。
圖7是圖6的代碼的中間表示的清單。
圖8是具有由finally塊保護的非導致異常的指令的try代碼段的偽代碼表示的清單。
圖9是圖8的代碼的中間表示的清單。
圖10是具有由finally塊保護的導致異常的指令的try代碼段的偽代碼表示的清單。
圖11是圖10的代碼以及適當?shù)漠惓L幚沓绦驑撕灥闹虚g表示的清單。
圖12是具有由兩個過濾器和兩個catch塊保護的導致異常的指令的try代碼段的偽代碼表示的清單。
圖13是圖12的代碼以及涉及catch塊的適當處理程序標簽和過濾器的中間表示的清單。
圖14是具有由兩個過濾器、兩個catch塊和結束代碼塊保護的導致異常的指令的try代碼段的偽代碼表示的清單。
圖15是圖14的代碼以及涉及catch和結束塊的適當處理程序標簽和過濾器的中間表示的清單。
圖16是由catch塊保護的嵌套try代碼段的偽代碼表示的清單。
圖17是圖16的代碼以及涉及嵌套與外部的catch和結束塊的適當處理程序標簽和過濾器的中間表示的清單。
圖18是示出用于將異常處理構造從中間語言轉換成另一中間表示的一種方法的框圖。
圖19A是異常處理數(shù)據(jù)表的數(shù)據(jù)結構的圖示。
圖19B是用于將偏移量映射到其標簽的標簽映射的一個圖示。
圖19C是在將受保護的塊映射到其各自的偏移量之后的標簽映射的另一圖示。
圖20是用于使用異常處理數(shù)據(jù)表以及受保護的塊及其處理程序和用于生成中間表示的目標塊之間的包含信息的一種方法的流程圖。
圖21是示出用于確定受保護的塊及其處理程序和目標塊之間的包含關系的范圍樹圖的一個示例的圖示。
圖22是示出局部對象的構造和析構的C++程序的清單。
圖23是用于在對象的構造和析構期間表達可能的異常處理路徑的偽代碼表示的清單。
圖24是圖22和23的代碼的中間表示的清單。
圖25是示出表達式臨時對象的條件構造的C++程序的清單。
圖26是用于表達表達式臨時對象的條件構造和析構的可能異常路徑的偽代碼表示的清單。
圖27是圖26的代碼的中間表示的清單。
圖28是傳值返回對象的C++程序的清單。
圖29A是圖28所示的傳值對象析構的可能異常路徑的中間表示的清單。
圖29B是圖29A的清單的延續(xù)。
圖30是傳值拋出對象的C++程序的清單。
圖31是表達圖30所示的拋出值類型對象的可能異常路徑的偽代碼表示的清單。
圖32是圖31的中間表示的清單。
圖33是由異常代碼塊保護的try代碼段的偽代碼表示的清單。
圖34是圖33的代碼的中間表示的清單。
圖35是用于將以后綴表示法形式表達的中間語言轉換成另一中間表示的示例性方法的流程圖。
圖36是通過讀取以后綴表示法形式表達的代碼中構建中間表示的數(shù)據(jù)結構的一種實現(xiàn)的圖示。
圖37是示出用于使用圖36的數(shù)據(jù)結構來通過讀取以后綴表示法形式表達的代碼構建中間表示的示例性方法的流程圖。
圖38A是使用后綴表示法實現(xiàn)的圖22的示例性代碼段的清單。
圖38B是圖38A的延續(xù)。
圖38C是圖38A和B的進一步延續(xù)。
圖39是示出在將圖38的代碼轉換成中間表示的過程中圖36的數(shù)據(jù)結構的狀態(tài)的框圖。
詳細描述異常處理構造的語言無關中間表示圖2示出了用于由編譯器后端240為代碼優(yōu)化而實現(xiàn)的用于多個源語言(205-208)的統(tǒng)一異常處理中間表示230的系統(tǒng)200。如圖2所示,系統(tǒng)200包括用于多個源代碼表示205-208的每一個的中間語言(IL)表示210-213,它由將多個IL表示210-213轉換成單個中間表示230的IL讀取器220分析或讀取。IL表示是比中間表示230更高級的中間表示,并可以用任意數(shù)量的公知中間語言,如MSIL(Microsoft CLR)(用于C#、Visual Basic、JScript、C和FORTRAN)以及CIL(用于C++)來表達。即使用于生成用于多個語言的統(tǒng)一異常處理框架的系統(tǒng)200被示出為具有用于多個源語言的單個IL讀取器處理,也可能實現(xiàn)多個這樣的讀取器,其每一個都對應于IL表示210-213中的一個或多個。
圖3A示出了用于使用IL讀取器220來生成用于以多種不同的源語言表達的異常處理構造的統(tǒng)一中間表示集的一般總體方法。在310,由讀取器220接收軟件的中間語言表示(例如,源代碼文件的中間語言表示),且在315,讀取或分析該文件以標識IL代碼流中的異常處理構造(320)。然后在330,讀取器220(也可以被認為是虛擬機)生成先前在320標識的異常處理構造的單個統(tǒng)一的中間表示。這一異常處理框架然后可用于簡化諸如代碼優(yōu)化和代碼生成等編譯器后端處理。
具有異常處理構造的軟件的統(tǒng)一中間表示可顯式地表達軟件的異常處理控制。圖3B示出了用于從軟件的統(tǒng)一中間表示生成可執(zhí)行代碼的方法350。這一方法可由例如編譯器或其它軟件開發(fā)工具在為軟件生成可執(zhí)行版本(例如,機器專用代碼或其它目標代碼)時使用。
在360,讀取該統(tǒng)一中間表示(例如,由編譯器或其它軟件開發(fā)工具)。例如,可使用由圖3A的方法生成的統(tǒng)一中間表示??稍谛枰獣r執(zhí)行對該統(tǒng)一中間表示的其他變換、轉換或優(yōu)化。
在370,生成軟件的計算機可執(zhí)行版本(例如,由編譯器或其它軟件開發(fā)工具)。軟件的計算機可執(zhí)行版本基于該統(tǒng)一中間表示執(zhí)行軟件的異常處理控制流。
圖4示出了用于生成以多個IL表示的形式表達的多個源語言內的異常處理構造的簡單且統(tǒng)一的中間表示的系統(tǒng)的另一實施例。如圖4所示,微軟的.NET框架(例如,C#、C、Microsoft Visual Basic、Jscript和FORTRAN)中支持的源語言組410首先被轉換成MSIL表示440。然而,由于其與其它源語言的差異,C++是以被稱為CIL 430的另一中間語言來表達的。CIL和MSIL內的控制流和異常處理模型以完全不同的方式來表達,且因此可能需要為CIL和MSIL表示提供單獨的IL讀取器(435和445)。
讀取器435和445都可使用在其各自的讀取器中實現(xiàn)的適當?shù)乃惴▉矸治龌蜃x取其各自的中間語言代碼流,以使用提供給后端460的統(tǒng)一異常處理指令框架450來表達中間語言代碼流中的異常處理構造或指令或表達式。以下本文的剩余部分描述了這一語言無關的異常處理指令集的各種成分。此外,中間語言中的異常處理構造的示例被示出為被轉換成其各自的語言無關的中間表示。本文也描述了用于分析中間語言和生成異常處理構造的中間語言表示的算法和方法。
在中間表示的主控制流內顯式表達的導致異常的指令導致異常的指令由其處理程序或finally區(qū)域來保護。當指令導致異常時,控制流可傳遞到處理程序,且有時候可基于過濾器指令的處理來有條件地選擇處理程序??刂瓶苫诋惓;蛑苯恿髦链a的finally區(qū)域,不論以何種方式,它都被處理并用于實現(xiàn)清理代碼。最終,總是在控制退出對應的try區(qū)域之前執(zhí)行這些區(qū)域。該機制可用于實現(xiàn)清理代碼,諸如關閉文件句柄、套接字、鎖定等。圖8和12示出了表示各種異常處理相關指令的偽代碼。例如,圖12示出了由兩個catch塊保護的try區(qū)域。要選擇這兩個catch塊(圖12)中的哪一個來處理取決于過濾器塊的處理結果。作為另一示例,圖8示出了由finally塊保護的try區(qū)域。
如參考圖3所描述的,可分析具有用于表達異常處理的各種模型的中間語言表示以確定導致異常的指令及其各自的處理程序和延續(xù)之間的控制流,然后可在同一控制流內將這些處理程序和延續(xù)顯式地表達為不會導致異常的指令的剩余部分。實現(xiàn)這一目的的一種方法是使用具有適度的存儲器分配成本(例如,每一指令一個字)的指令來構建控制流表示。處理程序可由使用可由導致異常的指令定義的異常變量的指令來表示。處理程序或過濾器指令然后可測試該異常變量,并基于異常對象的值或類型分支到處理程序主體或另一處理程序。類似地,C++或C#中由finally子句保護的指令具有指向為finally區(qū)域之外的控制轉移的目標而捕捉延續(xù)的指令的控制流邊緣或指針。在本情況下finally區(qū)域的末尾可由將控制轉移到finally區(qū)域的起始處的所捕捉的延續(xù)的指令來建模。中間語言的這些特征將在下文中參考示例進一步描述。
指令的格式如上所述,中間語言表示的異常處理構造的中間表示可以在指令級表達。圖5示出了用于指令或節(jié)點(IR節(jié)點)的數(shù)據(jù)結構的一個這樣的通用實現(xiàn),該數(shù)據(jù)結構允許在代碼的剩余部分的中間表示的控制流內表達異常處理構造。異常處理指令及其功能的特定中間表示在本文的稍后部分中描述。一般而言,IR指令可以在編譯器組件分層結構的各種級別處執(zhí)行。它們具有操作符(op代碼)字段和一組源(或輸入)操作數(shù)、一組目標(或輸出)操作數(shù)。這些操作數(shù)通常是對符號節(jié)點的引用。另外,源和目標操作數(shù)的每一個可以是類型化的,且操作符和操作數(shù)的類型可用于解決任何二義性。在圖5所示的示例指令中,504處的操作符具有兩個源操作數(shù)506和507,以及兩個目標操作數(shù)508和509。
異常處理語義可以通過向每一指令505提供指向標簽指令520的處理程序字段510來表示,該標簽指令是用于指令505的處理程序530的起始。如果指令無法拋出異常,則指令的處理程序字段510被設為NULL。如果指令能夠拋出異常但沒有處理程序,則編譯器可構建一特殊的處理程序以將控制傳播到當前方法之外。
用于描述圖5的IR指令505的文本表示法如下CC,DST=OPER1 SRC1,SRC2;$HANDLER1如果存在處理程序標簽,則它出現(xiàn)在分號之后。當指令不拋出異常時,處理程序字段被設為NULL。這可以通過指令的語義來指定,或被認為是作為優(yōu)化或程序分析的結果的情況。在該情況下,指令可以在文本上表示如下CC,DST2=OPER1 SRC1,SRC2;在對于指令沒有目標操作數(shù)或結果的情況下,省略指令描述中的目標和“=”符號。例如,條件分支指令沒有任何顯式的目標操作數(shù),因此它可以在文本上表示如下CBRANCH SRC1,SRC1-LABEL,SRC2-LABEL;異常處理指令以下段落通過描述其操作、輸入和輸出,描述了中間表示的各種異常處理相關指令。示例將示出該指令集如何可用于生成與不涉及異常處理的指令在同一控制流中的各種模型的異常處理構造的中間表示。
展開
表1UNWIND指令用于在不存在用于異常的匹配處理程序時表示當前方法之外的控制流。展開指令之前有一標簽,且之后是方法的退出。UNWIND操作的源操作數(shù)(x)表示拋出的異常對象。這使得數(shù)據(jù)流是顯式的。在一個方法中可以有一個或多個展開指令。然而,每一方法僅有一個UNWIND允許對每一方法節(jié)省中間表示空間。UNWIND指令的處理程序字段通常也被設為NULL。
圖6和7示出了在中間表示中對UNWIND指令的使用。圖6示出了用于可導致異常的未受保護區(qū)域的偽代碼。在轉換成中間表示的過程中,IL 220讀取器將分析中間語言(210-213)表示中的代碼,而非偽代碼。然而,在這些示例中使用偽代碼以簡化控制流的說明。例如,在圖6中,如果嘗試除以零的操作,則表達式x=a div b可導致異常。即使原始的源代碼或其中間語言表示(例如,MSIL或CIL)無法為該區(qū)域指定處理程序,該中間語言表示也可提供一默認處理程序,它通常是UNWIND指令。由此,圖6的代碼的中間表示可以如圖7所示。在該中間表示中,示出導致異常的指令的處理程序字段710被填寫,且指向具有標簽$HANDLER的處理程序,該標簽標記了UNWIND指令的起始?,F(xiàn)在如果導致了異常,則UNWIND指令將控制流移至方法之外。
結束控制流進入或離開finally區(qū)域可以在中間表示中由一組相關的指令來表示,如FINAL、FINALLY和ENDFINALLY。FINAL指令一般處理控制向finally區(qū)域的轉移,而FINALLY指令可接受來自FINAL指令的轉移或通過具有處理程序的導致異常的指令。ENDFINALLY指令表示離開finally區(qū)域的控制流。
表2FINAL指令表示控制向finally指令的起始的顯式轉移。該指令的第一個源操作數(shù)是相關聯(lián)的finally指令的起始標簽,而第二個操作數(shù)是其中控制在執(zhí)行了finally區(qū)域之后被轉移的延續(xù)標簽。FINAL指令的處理程序字段通常被設為NULL。
表3FINALLY指令具有兩個目標操作數(shù)。第一個操作數(shù)是異常變量。這對異常對象的數(shù)據(jù)流進行建模。第二個操作數(shù)是所捕捉的延續(xù)的標簽或代碼操作數(shù)。當執(zhí)行FINALLY指令作為所捕捉的異常的結果時,延續(xù)是詞法上封閉的處理程序的標簽、FINALLY標簽或UNWIND指令。該延續(xù)標簽被反映為匹配的ENDFINALLY(見下文)的處理程序字段。FINALLY指令的處理程序字段通常被設為NULL。
表4ENDFINALLY指令具有兩個或多個操作數(shù)。第一個操作數(shù)是異常變量。第二個操作數(shù)是其類型為標簽或代碼操作數(shù)的類型的延續(xù)變量。它也具有情況列表,該列表用于表示程序中顯式結束調用的可能的控制轉移。ENFINALLY指令必須將其處理程序字段設為詞法上封閉的外部finally或處理程序(即,F(xiàn)ILTER或UNWIND指令)的標簽。如果對匹配的finally指令沒有異常的控制流,則處理程序字段可為NULL。此外,F(xiàn)INALLY指令的目標操作數(shù)E和R與ENDFINALLY指令的源操作數(shù)E和R相同。這確保了可由后端組件在代碼優(yōu)化期間使用的兩個指令之間的數(shù)據(jù)依賴性。
圖8和9示出了使用FINAL以及FINALLY和ENDFINALLY指令實現(xiàn)從IL表示到中間表示的finally塊的一個示例。圖8示出了try塊的偽代碼。在源代碼或其中間語言表示中沒有指定處理程序。然而,與前一示例不同,不需要指定默認處理程序,因為表達式810不是導致異常的指令。由此,控制僅顯式地流至finally區(qū)域。圖8的代碼的中間表示可以如圖9所示地表達。表達式910沒有指定處理程序。FINAL指令915可以顯式地將控制轉移到由指向finally塊920的標簽$FINALIZE指示的finally區(qū)域。一旦執(zhí)行了finally塊,控制就轉移到FINAL指令915所指示的延續(xù)標簽(在該情況下為“$END”)。
圖10和11示出了使用FINAL、FINALLY和ENDFINALLY指令又一try-finally塊向中間表示的轉換。然而,在該表示中,向異常處理指令添加了處理程序。圖10示出了可導致由finally塊1015保護的異常的指令1010。圖11示出了try finally塊的中間表示以及異常處理程序。1110處導致異常的指令被分配定向到1115處的finally指令的起始的處理程序標簽$FINALIZE。在本示例中,對通過finally區(qū)域的兩種類型的控制流建模。首先,通過1110處導致異常的操作執(zhí)行FINALLY和ENDFINALLY指令。在該情況下,在執(zhí)行了ENDFINALLY指令1120之后,控制被傳遞到由$PROPAGATE標簽標記的區(qū)域。這實際上捕捉了異常路徑上的延續(xù)。然而,到finally區(qū)域的控制流也可通過FINAL指令1112顯式地轉移。在該情況下,在ENDFINALLY指令1120的末端,延續(xù)到達由$END區(qū)域標記的區(qū)域,該區(qū)域不實現(xiàn)UNWIND指令。
用于表示結束控制流的又一組異常處理中間表示可被稱為FAULT和ENDFAULT指令。它們類似于FINALLY和ENDFINALLY指令,然而,與FINALLY指令不同,控制流不能顯式地從FINAL指令傳遞到FAULT指令。到FAULT指令的控制僅通過導致異常的指令分支。
表5ENDFAULT指令終止相關的FAULT處理程序,且向指定的處理程序拋出異常。如果移除了到對應的FAULT指令的所有異??刂屏鳎瑒t處理程序字段可為NULL。在該情況下,錯誤處理程序是不可達的,且可被刪除。
表6基于過濾器的處理程序某些中間語言(例如,MSIL)實現(xiàn)基于過濾器的處理程序,由此不同的處理程序基于導致異常的事件的特性被分配給導致異常的事件。這一控制流可以使用捕捉和過濾異常然后向異常指定處理程序的指令(例如,F(xiàn)ILTER、ENDFILTER以及TYPEFILTER)以中間表示來表示。如下所述,TYPEFILTER指令可以是FILTER和ENDFILTER指令的簡短形式。
表7該指令可用于實現(xiàn)MSIL中通用的基于過濾器的處理程序。這匹配任何異常,并僅在指令的目標操作數(shù)中返回異常對象。過濾器指令被加標簽,并且其后跟隨可以使用或不使用異常變量的任意指令序列。過濾器指令最終必須到達ENDFILTER,而不會干涉FILTER指令。FILTER指令的處理程序字段通常為NULL。
表8ENDFILTER指令測試布爾操作數(shù)(X),且如果它為1,則分支到處理程序標簽,否則嘗試另一過濾器或展開。
表9TYPEFILTER指令測試異常對象的類型是否為目標操作數(shù)(靜態(tài)已知)的類型的子類型。如果是,則控制轉移到第一標簽(處理程序標簽)。否則,嘗試另一過濾器或展開指令標簽。當類型過濾器匹配時,目標操作數(shù)被設為異常對象。TYPEFILTER指令的處理程序字段通常為NULL。
注意,TYPEFILTER指令是一種簡短形式,且實際上可被表示為FILTER和ENDFILTER操作的組合,如下t.obj32=FILTER;e.Type=CHKTYPE t.obj32;x.cc=CMP(NEQ)e.Type,0.null;ENDFILTER x.cc,$LABEL1,$LABEL2;
FILTER指令返回其類型被驗證為e.Type的異常對象,且如果它是e.Type,則x.cc被設為TRUE,否則設為FALSE。然后,在ENDFILTER處,根據(jù)x.cc的值,延續(xù)被確定為$LABEL1或$LABEL2。同一表達式可被表示為如下的TYPEFILTER指令。
e.Type=TYPEFILTER$LABEL1,$LABEL2;圖12和13示出了使用基于過濾器的異常處理指令的try-catch塊的實現(xiàn)。圖12描述了由兩個不同的處理程序區(qū)域1220和1230保護的導致異常的try區(qū)域1210。1215和1225處的過濾器基于所返回的異常對象的類型確定要實現(xiàn)哪一處理程序。圖13示出了使用中間表示的TYPEFILTER指令的try-catch對的中間表示。兩個導致異常的指令1310的處理程序字段都被設為指向第一個過濾器1315的$HANDLER1。如果異常對象是DivideByZeroException(除以零異常)類型,則控制流至標簽為$CATCH1的catch塊。如果不是,則控制流至由標簽$HANDLER2引用的下一過濾器1325?;诋惓ο蟮念愋褪欠駷镋xcpetion(異常)類型,控制要么流至由標簽$CATCH2標識的第二個catch塊1330,要么流至由標簽$PROPAGATE標識的UNWIND指令1335。
MATCHANYFILTER指令是基于過濾器的異常處理指令的一種形式。
表10該過濾器總是無條件地匹配任何異常,并將控制轉移到有效標簽。這等效于FILTER-ENDFILTER對,其中ENDFILTER的第一個操作數(shù)總是1,且第二個標簽未指定。MATCHANYFILTER指令的處理程序標簽必須為NULL。MATCHANYFILTER指令也是一種簡短形式,并可使用FILTER和ENDFILTER指令來表示,如下所示。
e.Type=FILTER;ENDFILTER 1.cc,$LABEL1,$LABEL2;用于上述FILTER和ENDFILTER組合的等效MATCHANYFILTER指令如下。
e.Type=MATCHANYFILTER$LABEL1;
表示由基于過濾器的處理程序保護的try塊和結束又一異常處理模型基于所導致的異常的類型將控制從try塊流至一個或多個處理程序區(qū)域,然后流至一個或多個finally區(qū)域。圖14示出了用于由一對處理程序1420和1430保護的try塊1410以及finally塊1440的中間語言表示(例如,MSIL)的偽代碼。過濾器1415和1425基于所返回的異常對象的類型確定要處理兩個處理程序塊中的哪一個。不論遍歷的catch塊是什么,finally塊都必須在退出方法之前接觸到。
圖15示出了使用基于過濾器的TYPEFILTER指令和FINAL、FINALLY和ENDFINALLY結束指令的圖14所示的控制流的中間表示。該示例展示了未出現(xiàn)在前一示例中的若干有意思的要點。首先,每一處理程序1520和1530通過分別位于1521和1531處的finally指令的顯式調用來終止。這反映了如圖14所示的原始程序的語義,其中控制從處理程序1420和1430流至finally塊1440。其次,控制最后一個處理程序1530的過濾器1525為finally指令而非展開指令1550指定了標簽。這確保如果最后一個過濾器不匹配,則它將控制轉移到正確的finally塊。該要點將也在異常處理的嵌套情況的示例中進一步示出。
圖16和17示出了嵌套的異常處理。圖16示出了用于嵌套在try-catch塊的try部分中的try-catch-finally塊的中間語言表示的偽代碼。圖17示出了使用上述過濾器、處理程序和finally指令的這一異??刂屏鞯闹虚g表示。如圖16所示,外部try塊1610由1620和1630處具有過濾器的兩個處理程序塊來保護。嵌套的try塊1615由具有過濾器的catch塊1625和1635處的finally塊來保護。此處基于若干因素存在若干可能的異常路徑,這些因素包括在源代碼中發(fā)生異常的情況。所有這些各種異常路徑都可使用如圖17所示的中間表示異常指令來表達。導致異常的指令1705是由標簽為$HANDLER1 1710的外部基于過濾器的處理程序塊來保護的,該標簽可將控制傳遞到標簽為$HANDLER2 1715的又一基于過濾器的處理程序塊。內部try塊1615中導致異常的指令1706不僅由標簽為$HANDLER3 1720的內部基于過濾器的塊來保護,且其異常路徑也可通過標簽為1710處的$HANDLER1和/或1715處的$HANDLER2的塊。例如,如果在1706處的內部try塊中的表達式處導致了DivideByZero異常,則異常路徑通過將1726處ENDFINALLY塊的處理程序字段設為$HANDLER1標簽,經由1725處的結束塊到達1710處的適當處理程序塊。該流程表示了從try塊1615到finally塊1635然后到處理程序1620的流程。
用于將異常處理構造從中間語言代碼轉換成較低級中間表示的方法如圖2所示,使用上述指令的異常處理構造的中間表示可由IL讀取器220生成,該讀取器處理中間語言的代碼以生成這一表示。IL讀取器220可使用任意數(shù)量的不同過程或算法,并且算法的選擇或設計可以部分地依賴于中間語言本身,尤其是其用于表示異常處理構造的模型。例如,圖4示出了適用于讀取MSIL中間語言的源代碼表示并使用上述異常處理指令生成中間表示的IL讀取器445。
圖18示出了用于生成異常處理指令的中間表示的一種可能的方法。在某些中間語言(例如,MSIL)中,異常數(shù)據(jù)可在與主代碼流分離的數(shù)據(jù)結構中捕捉。用于這些語言的IL讀取器(轉換器)可接收異常處理數(shù)據(jù)表1810以及中間語言形式的主代碼流1820作為輸入。這一數(shù)據(jù)然后可由1830處的IL讀取器讀取,以確定導致異常的指令和與這些指令相關聯(lián)的任何catch、finally或過濾器塊之間的關系,以生成1840處的中間表示。
例如,圖19A示出了包含異常處理數(shù)據(jù)的數(shù)據(jù)表,該異常處理數(shù)據(jù)可作為源代碼的中間語言表示的一部分可用。圖19A所示的數(shù)據(jù)對應于圖12和13所示的代碼片段。如上所述,為簡明起見,圖12僅示出了中間語言表示的偽代碼。然而,IL讀取器實際上將分析或讀取諸如MSIL等中間語言代碼。到各種指令的偏移量1240可以如圖12所示的注明。IL讀取器知道封閉可區(qū)分的代碼塊的偏移量對。例如,圖19A的異常處理數(shù)據(jù)注明了1910處try塊1210的偏移量入口、1915處的塊類型(例如,try-catch、try-finally、try-catch-finally等)、以及1920處的其處理程序塊的偏移量入口。類似地,指令的異常路徑上的finally塊、過濾器塊、延續(xù)塊和其它塊的偏移量入口及其與彼此的關系可以以如圖19A所示的偏移量入口的形式注明。
然而,如上所述,中間表示的異常指令使用標簽來標記或標識各種粘合的代碼塊,且這些標簽用于構建中間表示以及控制流。由此,IL讀取器使用諸如圖19A所示的異常處理數(shù)據(jù)表來生成用于構建中間表示的標簽。圖20是用于處理中間語言代碼形式的輸入的方法的一個示例,該輸入包括用于生成中間表示的所捕捉的異常處理數(shù)據(jù)。在2010,如果在代碼內沒有要分析或讀取的方法,則轉換過程在2015停止。如果不是,則在2020讀取當前方法及其相關聯(lián)的異常處理數(shù)據(jù)表。在2030,使用包括對每一代碼塊的偏移量范圍的異常處理數(shù)據(jù)來建立可被保護的代碼塊與形成其處理程序、過濾器、結束、延續(xù)塊等的代碼塊之間的包含關系。
例如,圖21示出了構建映射圖12所示的各種代碼塊之間的包含關系的樹形數(shù)據(jù)結構。首先,將屬于整個方法2110的偏移量分配給樹中的節(jié)點。然后,當讀取異常處理數(shù)據(jù)中的每一偏移量范圍(圖19A)時,它們被分配給其它關系,諸如基于由異常處理數(shù)據(jù)提供的信息讓哪些范圍由哪些處理程序來保護。例如,try-catch偏移量范圍2120被示出為包含由兩個處理程序,即2140處的第一catch和2150處的第二catch保護的try范圍2130。該示例示出了方法內的try-catch塊的單個情況,但是當多個代碼塊彼此嵌套時樹形結構可以變得更大,且樹形結構提供了一種表示這一多個包含關系的合適方式。
現(xiàn)在參考圖20,當建立各種代碼塊之間的包含關系(2030)時,在2040,可僅由其偏移量范圍標識的每一處理程序和目標塊(例如,finally或延續(xù)等)現(xiàn)在可被分配不同的標簽。例如,圖19B示出了標簽$HANDLER1和$HANDLER2被分配給圖12的代碼中所標識的兩個處理程序塊。再次返回到圖20,在2050,使用各種代碼塊的包含關系,受保護的塊如圖19C所示的向其處理程序和目標塊分配標簽。這允許以上述中間表示表達式的標簽形式來表達受保護的塊及其相關聯(lián)的處理程序和目標塊之間的關系。一旦將受保護的塊映射到其適當?shù)奶幚沓绦蚝推渌繕藟K,在2060,通過再次分析代碼構建該方法的中間表示。在這一輪分析中,讀取每一指令,且如果它是導致異常的指令,則使用先前所構建的范圍樹數(shù)據(jù)結構來標識其處理程序。該過程可以重復,直到程序內的所有方法都被轉換成其中間表示。
對象構造和析構作為try-finally塊的中間表示諸如C++等某些語言允許程序員聲明在聲明它們的塊或表達式內具有局部生命期的對象(類)變量。該語言語義要求當退出該塊作用域或表達式作用域時,調用對應的對象析構函數(shù)。這些操作可在中間表示中表示為一組或多組try-finally塊。例如,圖22示出了使用局部對象的程序。語句S1和S3分別生成對class1和class2的構造函數(shù)的調用。這些類的構造函數(shù)以及調用這些對象的語句S2和S4可能拋出異常。在該情況下,可能需要適當?shù)那宄?。如果語句S1拋出異常,則傳播該異常,因為尚未成功地創(chuàng)建任何對象。如果S2或S3拋出異常,則需要調用class1的析構函數(shù)。然而,如果S4拋出異常,則需要調用class2的析構函數(shù),然后調用class1的析構函數(shù)。由于該示例沒有處理程序,因此異常被傳播到方法的調用者。這些操作可以在概念上由圖23所示的嵌套try-finally構造來表達。obj1的構造函數(shù)2310在任何try塊的外部。由此,如果在構造函數(shù)2310期間拋出異常,則在退出方法之前無需析構任何對象。然而,如果在2310處成功地構造了obj1,則它必須經由通過2320處的finally塊來析構。然而,如果控制到達2330處的內部try塊,則obj1和obj2都必須經由通過finally塊2320和2340來析構。對這些操作使用FINAL、FINALLY和ENDFINALLY指令的中間表示可如圖24所示。兩個FINAL指令2410和2420提供了到分別在2430和2440處表示的兩組FINALLY和ENDFINALLY指令的顯式入口。控制流也可在第二個對象的析構期間導致異常的情況下通過異常到達這些指令。2435處第二個對象的析構函數(shù)指令具有指向包含2445處的第一個對象的析構函數(shù)的FINALLY和ENDFINALLY指令2440的處理程序標簽($DTOR1)。這是因為如果控制流到達第二個對象的析構函數(shù)2430,則第一個對象必須被構造,且因此需要在退出方法之前被銷毀。即使在析構第二個對象時沒有拋出任何異常,在退出方法之前仍在2440處($DTOR1)析構第一個對象。
表達式臨時對象的中間表示諸如C++等某些語言允許創(chuàng)建表達式臨時對象。這些對象是在表達式求值期間創(chuàng)建的,且在表達式求值之后,通常在包含表達式的語句求值之后被銷毀。如果表達式是條件表達式,則所創(chuàng)建的對象必須被有條件地析構。例如,圖25示出了表達式臨時對象obj1(x)和obj2(x+1)。在調用了foo()之后必須調用這些對象的析構函數(shù)。然而,注意這些對象的創(chuàng)建是根據(jù)變量“x”的值有條件地發(fā)生的。由此,這些對象的析構函數(shù)指令也必須由同一條件來保護。這可以被表達為如圖26的偽代碼中所示的一組嵌套的try-finally塊。取決于“x”的值,分別在2610和2620創(chuàng)建obj1和obj2。由此,基于同一條件,obj1或obj2必須分別在2630和2640處析構。與前一示例不同,此處在任何給定時刻僅創(chuàng)建一個對象。圖27示出了使用多組上述FINAL、FINALLY和ENDFINALLY指令的這一構造的一個中間表示。取決于“x”的值,分支指令2710指向2720處obj1的構造函數(shù)的代碼,或指向2730處obj2的構造函數(shù)的代碼。同樣要注意,如果在創(chuàng)建2720或2730處的對象的期間拋出了異常,則將處理程序標簽設為$PROPAGATE(2721和2731),它標記了將控制傳遞到方法之外的UNWIND指令。然后再一次取決于所創(chuàng)建的對象,在2740出析構obj1,或者在2750處析構obj2,這兩個析構函數(shù)都包含在一對FINALLY和ENDFINALLY指令中。以此方式,可以使用中間表示指令來表示表達式臨時對象的條件創(chuàng)建和析構。
傳值返回對象的中間表示諸如C++等某些語言允許傳值返回對象。在返回對象之前,在局部創(chuàng)建的對象上調用析構函數(shù)。例如,在圖28中,在2810局部創(chuàng)建對象a和b。如果這些局部對象的任一個上的析構函數(shù)拋出異常,則在退出方法之前必須銷毀2820處的返回對象r1以及2830處的對象r2。圖28中的代碼的異常處理控制流可以用如圖29A和29B所示的中間表示來表達。在返回2910處的對象r1之前,該方法必須調用分別位于2920和2930處的局部創(chuàng)建的對象a和b的析構函數(shù)。然而,如果這些析構函數(shù)2920或2930的任一個拋出異常,則必須調用返回對象的析構函數(shù)2940。這一控制流可以如圖29所示使用適當?shù)腇INAL、FINALLY和ENDFINALLY指令組來表示。例如,2930處的對象b的析構函數(shù)的處理程序標簽($final a1)指向對象a的析構函數(shù)的標簽2920,該對象的處理程序標簽($final r1)進而指向2940處的返回對象r1的析構函數(shù)。這確保如果在析構對象a或b或兩者的期間導致異常,則在退出該方法之前也析構返回對象r1。注意,在2940,如果標志$F1等于“1”,則調用r1的析構函數(shù),該標志在2950處被設為“1”,且如果對象a或對象b的任一個不能被成功銷毀,則保持被設為“1”。以此方式,如果對象a或對象b的析構不成功,則析構返回對象r1。返回對象r2的條件析構以同樣的方式處理。
傳值拋出對象的中間表示諸如C++等某些語言允許傳值(即在棧上分配的值)拋出和捕捉對象。如類型“int”等簡單的原語值不會產生任何問題。然而,拋出在棧上分配的結構和類可能要求調用構造函數(shù)和析構函數(shù)。圖30示出了用于傳值拋出對象的源代碼。該操作在概念上可用圖31所示的偽代碼來表示。在本示例中,做出局部值的副本,并且在該副本上調用副本構造函數(shù)3110。當在3120拋出值時,必須傳遞局部值的新副本的析構函數(shù),使得接收該值的方法稍后可以調用該析構函數(shù)。finally塊3130保護所有的異常,且負責調用析構函數(shù)。
try-finally塊可以用中間表示被表示為一組FINAL、FINALLY和ENDFINALLY指令,且以下指令可用于表示拋出值類型,該值類型具有在復制指令中為其定義的副本構造函數(shù)和析構函數(shù)。
表11這是用于拋出具有為其定義的副本構造函數(shù)和析構函數(shù)的值類型的拋出的特殊形式。它具有兩個操作數(shù)。第一個操作數(shù)是指向具有被拋出的值的位置的指針,而第二個操作數(shù)是執(zhí)行析構的函數(shù)指針。其語義是當找到處理程序時析構拋出的對象。這本質上保持局部值類型位置在運行時是活的。這可用于對值類型的C++異常語義進行建模。THROW指令的處理程序字段通常不被設為NULL。圖32示出了圖30和31的代碼的中間表示。THROWVAL指令3210用于表示值拋出,且在該示例中,它被示出為接收指向被拋出的值的位置的指針3220以及指向稍后由接收該拋出的對象的方法使用的其析構函數(shù)的指針3230。
try-except構造的中間表示對諸如C和C++等語言的結構化異常處理(SEH)擴展提供了一種被表達為try-except塊的異常處理構造。圖33示出了一個try-except塊。與帶有過濾器的catch塊一樣,except塊基于所導致的異常的類型指定了指向導致異常的指令的處理程序的指針。然而,except塊也考慮到了恢復導致異常的指令的執(zhí)行的可能性。以下兩個中間表示表達式可用于表示這一異??刂屏?。
表12SENENTER指令標記了對try-except區(qū)域的入口。其處理程序指定了對處理程序的控制依賴性以及受保護區(qū)域的主體。
表13ENDRESUMEFILTER類似于ENDFILTER,不同之處在于它可引起執(zhí)行導致異常的指令以在源操作數(shù)具有-1的值時恢復。圖34示出了使用上述SEHENTER、FILTER和ENDRESUMEFILTER表達式用于圖33的try-except構造的中間表示。在3410,使用SHENTER指令對嘗試3420的主體的調用加前綴。同樣,為確保try-except區(qū)域中的操作之間的正確的控制依賴性,SEHENTER表達式的處理程序被設為FILTER指令3430,如同對3420處的導致異常的指令foo()的調用一樣。ENDRESUME指令用于表示基于由過濾器函數(shù)3450返回的值的異常的延續(xù)。如果值“t”為1,則控制傳遞到處理程序主體($HANDLERBODY)。如果該值為“0”,則退出該方法。然而,如果返回的t的值為“-1”,則控制返回到$LABEL以恢復在最先引起異常的操作的執(zhí)行。同樣,圖34的表示提供了一種異常路徑,它具有直接從SENENTER指令3410的退出。這確保了僅完成安全的代碼移動。然而,在同一表示中,對3450處的過濾器函數(shù)的調用沒有處理程序,但是可如果filter()函數(shù)可能導致異常,則可設置諸如展開指令(未示出)等處理程序。
用于將異常處理構造的中間語言表示從中間語言代碼轉換成較低級中間表示的替換方法如圖4所示,可能需要單獨的IL讀取器來從不同的中間語言(例如,CIL、MSIL等)生成中間表示,以符合對該特定語言專用的異常處理模型。以下章節(jié)描述了用于從如CIL那樣以后綴表示法形式表達操作的語言生成異常處理構造的中間表示的方法。
一般而言,在后綴表示法表達式中,操作的操作數(shù)在操作符之前表達,例如,在用于諸如T=5+3等ADD操作的代碼中,讀取器將在遇到操作符“+”的代碼(即,ADD)之前遇到操作數(shù)5和3的代碼。使用后綴表示法形式的這一代碼的轉換器,尤其是能夠在一遍中轉換這一代碼的轉換器可以首先轉換操作數(shù)的代碼,然后基于其操作數(shù)或其子節(jié)點(也稱為表達式的子表達式)的代碼為整個操作構建經轉換的代碼。
圖35示出了用于從諸如CIL等使用后綴表示法的中間語言生成異常處理構造的中間表示的一個總體方法。在3510,逐個節(jié)點(即,逐個表達式)讀取輸入代碼。然后,在3520,由于后綴表示法形式的遞歸特性,確定代碼流的剩余部分內的當前節(jié)點的上下文。該上下文稍后可在3530處用于基于其子節(jié)點的代碼將父節(jié)點的代碼放在一起。
后綴表示法語言可以用使用選中操作符的操作的形式來表達異常信息,該選中操作符可以由IL讀取器以圖35的方式來處理,以生成指令的統(tǒng)一框架(例如,F(xiàn)INAL、FINALLY以及ENDFINALLY)形式的中間表示。圖36示出了一種實現(xiàn)圖34的方法的方式。圖36示出了擔當構建塊的若干數(shù)據(jù)結構(例如,3610、3620、3630),用于包含可稍后可被組合以共同完成諸如方法等代碼段的中間表示形式的經轉換的中間表示代碼。數(shù)據(jù)結構3610和3630可以被實現(xiàn)為其自己的節(jié)點具有其自己的數(shù)據(jù)結構的概念棧。當讀取并轉換中間語言(例如,CIL)的代碼時,與每一子操作、子表達式、子節(jié)點等有關的中間表示代碼可被儲存在數(shù)據(jù)結構(例如,3610、3620和3630)中。然后,當為每一操作建立上下文或包含關系或在其它適當?shù)臅r刻,所有經轉換的中間代碼被加在一起以生成完整的轉換。
在一個這樣的方法中,從中間語言輸入中讀取的節(jié)點被壓入求值棧3610中,在該棧中可對它們進行求值。從輸入中讀取的節(jié)點的求值可能要求將某些節(jié)點從求值?;駿H棧中彈出,并將新節(jié)點壓入求值?;駿H棧。一般而言,求值棧3610可包含與大多數(shù)主代碼流有關的中間代碼。來自求值棧的某些節(jié)點然后可被出棧,以當建立父和子節(jié)點的上下文或包含關系時為其它節(jié)點構建代碼。例如,回到簡單的加法表達式T=5+3,當建立5和3為“+”操作的操作數(shù)的表達式時,求值棧3610中與常數(shù)5和3有關的節(jié)點被出棧,且用于加法操作的代碼可通過組成其子節(jié)點,即表示5和3的節(jié)點的代碼來合成。轉換算法使用并維護了求值棧上的節(jié)點已計算了所有的屬性的不變性。
DTOR代碼數(shù)據(jù)結構3620可以被認為是用于為出現(xiàn)在方法主體中的所有的對象析構函數(shù)、catch塊以及finally塊封裝經轉換的中間表示代碼序列的結構。由此,不使用異常處理(EH)棧3630來包含代碼序列,但是EH??杀徽J為是用于通過構建標簽來建立各種代碼序列之間的關系的延續(xù)棧。EH棧建立try、catch和finally區(qū)域之間的嵌套關系。每一區(qū)域可由與該區(qū)域相關聯(lián)的標簽來標識。EH棧中的每一節(jié)點具有唯一的ID,稱為狀態(tài)。狀態(tài)的概念用于計算在表達式求值中所分配的對象的數(shù)目。該信息稍后可用于諸如確定需要被添加到經轉換的代碼及其與代碼的剩余部分的關系的析構函數(shù)的數(shù)目。
求值棧3610中的每一節(jié)點的數(shù)據(jù)結構可示出如下。
表14僅“Opcode”和“IL Type”字段可以由編譯器的前端提供,剩余字段是在生成中間表示代碼時在轉換期間填充的。FirstInstr和LastInstr字段允許代碼的串接和前置。整個DTOR數(shù)據(jù)結構3620可以類似于求值棧3610的一個節(jié)點的數(shù)據(jù)結構來實現(xiàn)。
EH棧節(jié)點可具有以下用于表示異常路徑上的延續(xù)的數(shù)據(jù)結構。
表15Label字段指向可在Finally或TypeFilter指令之前的標簽指令。Flags字段可用于標識所執(zhí)行的異常處理操作的特征。例如,它可用于標識要轉換的析構函數(shù)代碼是否用于諸如表達式臨時對象等臨時對象,或者它是否用于可在構造它的表達式之外析構的對象。
在用于求值棧3610、DTOR代碼3620和EH棧3630的節(jié)點的數(shù)據(jù)結構的上下文中,可如圖37所示的更詳細地描述用于生成如圖35所示的中間表示的方法。圖37的方法是為轉換的中間語言流中的每一方法實現(xiàn)的。在3710,圖36所示的轉換數(shù)據(jù)結構(例如,3610、3620和3630)被初始化。例如,求值棧3610和EH棧3620被初始化為空。同樣,可將UNWIND節(jié)點壓入EH棧中,因為所有的方法至少都具有一個到該方法外部的異常路徑延續(xù),且也可初始化EH棧的當前狀態(tài)。稍后在3720,從中間語言代碼中讀取節(jié)點,并將其EH狀態(tài)初始化為CurrentEHState(當前EH狀態(tài))。再一次,使用狀態(tài)來維護遞歸地讀取的節(jié)點的上下文。稍后在3740,在求值棧和EH棧的現(xiàn)有上下文中對節(jié)點求值。求值的結果是定義節(jié)點的Opnd、FirstInstr和LastInstr字段,以及上下文中的改變(即,節(jié)點可被壓入或彈出求值?;駿H棧,且CurrentEHState值可被修改)。求值棧上的節(jié)點表示已求值的節(jié)點,它們已完整地填充了其字段。然后,在3750,如果求值的操作不是退出,則讀取下一節(jié)點。然而,如果是方法的退出,則在3760,包含在求值棧3610中的所有已求值節(jié)點中的經轉換的代碼被前置,且在DTOR代碼數(shù)據(jù)結構3620中的代碼被串接成這一結果以產生完整的中間表示代碼。
后綴表示法的中間語言代碼向較低級中間表示的示例轉換求值方法3740可以在讀取中間語言代碼的過程中遇到它們時對不同的操作是不同的。以下示例示出了用于對中間語言代碼中某些這樣的操作求值的方法。圖22和23示出了用于諸如C++等語言的對象構造函數(shù)和析構函數(shù)的偽代碼。圖24示出了使用諸如FINAL、FINALLY、ENDFINALLY等指令被轉換成中間表示的圖22和23的代碼。由IL讀取器(即,轉換器)處理的后綴表示法中間語言代碼可以是圖38A、38B和38C中所示的形式。這一代碼可以被讀取并使用上文參考圖36和37所描述的方法來轉換成中間表示(圖24)。
圖39示出了使用轉換數(shù)據(jù)結構(例如,3905、3915和3925)來轉換圖38A、38B、38C中包含對象構造函數(shù)和析構函數(shù)的中間語言代碼過程。首先,所有數(shù)據(jù)結構被初始化為空。然后,當讀取圖38A、38B和38C中的代碼時,對每一操作求值,并且將節(jié)點壓入求值棧3905中,或從該棧中彈出以生成DTOR代碼數(shù)據(jù)結構3925中的代碼并操縱EH棧3915。在本示例中,對象1的構造函數(shù)首先在3810處標識,這作為節(jié)點被壓入3910處的求值棧中。稍后在3820,遇到用于對象1的析構函數(shù)的代碼,它也被臨時壓入求值棧中。當讀取器在3830處遇到Oppushstate操作符時,知道析構函數(shù)3820和構造函數(shù)3810是Oppushstate操作符3830的操作數(shù)。也知道析構函數(shù)代碼是處理程序,因此需要被放置在DTOR代碼數(shù)據(jù)結構3925中。由此,求值棧3905上與對象1的析構函數(shù)有關的頂部節(jié)點被出棧,并最終連同標識該塊的標簽一起被追加到DTOR代碼數(shù)據(jù)結構3925。DTOR代碼數(shù)據(jù)結構3925也用具有如圖39所示的適當?shù)难永m(xù)的FINALLY和ENDFINALLY指令來追加。EH棧3915將被初始化為具有帶UNWIND指令前的標簽的節(jié)點3920,但是現(xiàn)在新節(jié)點3940被添加到EH棧,且其標簽被設為被添加到包含F(xiàn)INALLY和ENDFINALY指令的代碼塊的標簽。在這一點上,確定了與異常路徑和延續(xù)有關的中間代碼。對象2的代碼以類似的方式來求值。稍后在3840,當遇到Opdtoraction操作符時,如2410和2420所示地構建與進入finally區(qū)域的顯式入口有關的中間代碼(即,F(xiàn)INAL指令)。圖39示出了在圖38的中間語言代碼到達其中已對OPpushstate指令3830進行求值的點之后,求值棧3905、DTOR代碼數(shù)據(jù)結構3925以及EH棧3915的狀態(tài)。由此,將用于中間表示的代碼縫合在一起所需的所有代碼被包含在數(shù)據(jù)結構3905和3925及其各節(jié)點內,它們可被添加以形成完整的經轉換的代碼。雖然,用于對各種操作符的代碼求值并將它們縫合在一起的方法可以不同,上述各種數(shù)據(jù)結構可以在每一個別的操作符及其函數(shù)和形式的上下文中使用,以生成期望的中間表示。
替換方案參考所示的實施例描述并示出了本發(fā)明的原理之后,可以認識到,所示的實施例可以在排列和細節(jié)上進行修改而不脫離這些原理。盡管此處所描述的技術是通過使用編譯器的示例來示出的,但這些技術中的任一種可使用其它軟件開發(fā)工具(例如,調試器、優(yōu)化器、模擬器和軟件分析工具)。同樣,應當理解,此處所描述的程序、過程或方法不涉及或限于任何特定類型的計算機裝置。各種類型的通用或專用計算機裝置可用于或執(zhí)行依照此處所描述的教導的操作。此處所描述的動作可以由包括用于執(zhí)行這些動作的計算機可執(zhí)行指令的計算機可讀介質來實現(xiàn)。所示實施例中以軟件示出的元素可以用硬件來實現(xiàn),反之亦然。鑒于可向其應用本發(fā)明的原理的許多可能的實施例,應當認識到,詳細描述的實施例僅是說明性的,且不應當被理解為局限本發(fā)明的范圍。相反,要求保護落入所附權利要求書及其等效技術方案的范圍和精神之內的所有這樣的實施例作為本發(fā)明。
權利要求
1.一種處理包括異常處理構造的軟件的統(tǒng)一中間表示的方法,所述方法包括讀取包括異常處理構造的軟件的統(tǒng)一中間表示;其中,所述統(tǒng)一中間表示顯式地表達了所述軟件的異常處理控制流;以及基于所述統(tǒng)一中間表示生成實現(xiàn)所述異常處理控制流的軟件的計算機可讀版本。
2.如權利要求1所述的方法,其特征在于,所述統(tǒng)一中間表示包括用于表達向結束代碼塊的控制轉移的第一指令;用于表達向所述結束代碼塊的控制轉移的接受的第二指令;以及用于表達離開所述結束代碼塊的控制轉移的第三指令。
3.如權利要求2所述的方法,其特征在于,所述結束代碼塊包括涉及對象的析構函數(shù)的代碼。
4.如權利要求2所述的方法,其特征在于,所述結束塊包括涉及表達式臨時對象的析構函數(shù)的代碼。
5.如權利要求2所述的方法,其特征在于,所述第二指令的目標操作數(shù)與所述第三指令的源操作數(shù)相同。
6.如權利要求2所述的方法,其特征在于,用于表達向所述結束代碼塊的顯式控制轉移的第一指令還包括指示要用于表達向所述結束塊的控制轉移的結束代碼塊的起始的標簽;以及指示在退出所述結束代碼塊之后控制轉移的延續(xù)的標簽。
7.如權利要求2所述的方法,其特征在于,用于表達向所述結束代碼塊的控制轉移的接受的第二指令之前有指示所述結束代碼塊的起始的標簽,且向所述結束塊的控制轉移是由對所述標簽的使用來指示的。
8.如權利要求2所述的方法,其特征在于,用于表達離開所述結束代碼塊的控制轉移的第三指令包括用于基于進入所述結束代碼塊是顯式的還是由異常引起的,來指示離開所述結束代碼塊的控制轉移的不同延續(xù)的字段。
9.如權利要求1所述的方法,其特征在于,所述統(tǒng)一中間表示包括用于捕捉異常并返回涉及所述異常的異常對象的第一指令;以及用于基于所述異常對象的類型值指定所述異常的處理程序的第二指令。
10.如權利要求9所述的方法,其特征在于,用于指定所述處理程序的第二指令包括用于指示所述異常對象的類型值的至少一個布爾源操作數(shù);如果所述布爾源操作數(shù)為真,指示在涉及向其傳遞控制流的處理程序的代碼塊之前的標簽的至少一個源操作數(shù);以及如果所述布爾源操作數(shù)為假,指示在涉及向其傳遞控制流的延續(xù)的代碼塊之前的標簽的至少一個源操作數(shù)。
11.如權利要求1所述的方法,其特征在于,所述統(tǒng)一中間表示包括用于基于涉及所述異常的異常對象的類型值來指定異常的處理程序的指令,其中,所述指令的目標操作數(shù)包括預定的異常對象,所述指令的第一源操作數(shù)包括指示涉及所述處理程序的代碼塊的標簽,且第二源操作數(shù)包括指示涉及延續(xù)的代碼塊的標簽。
12.如權利要求11所述的方法,其特征在于,所述指令可用于將所述異常對象的類型值與所述預定異常對象的類型值進行比較,且如果存在匹配,則將控制流傳遞到涉及所述處理程序標簽的代碼塊,且如果不存在匹配,則將控制流傳遞到涉及所述延續(xù)標簽的代碼塊。
13.如權利要求1所述的方法,其特征在于,所述統(tǒng)一中間表示包括用于指示進入try-except區(qū)域的第一指令;以及用于基于涉及所述異常的類型值選擇多個控制流路徑中的一個用于異常處理的第二指令,其中,可用于選擇的所述多個控制流路徑包括涉及引起所述異常的指令的執(zhí)行的恢復的路徑。
14.如權利要求13所述的方法,其特征在于,用于選擇所述多個控制流中的一個用于異常處理的第二指令包括指示所述異常的類型值的操作數(shù);指示處理程序代碼塊的標簽操作數(shù);指示延續(xù)代碼塊的標簽操作數(shù);以及指示導致異常的指令的標簽操作數(shù)。
15.一種用于實現(xiàn)用于多種源代碼語言的統(tǒng)一異常處理中間表示的系統(tǒng),所述系統(tǒng)包括用于獲取源代碼文件的中間語言表示并基于所述中間語言表示生成所述源代碼的異常處理構造的統(tǒng)一中間表示的中間語言讀取器;其中,所述統(tǒng)一中間表示顯式表達了所述源代碼的異常處理控制流。
16.如權利要求15所述的系統(tǒng),其特征在于,還包括用于基于所述統(tǒng)一中間表示生成目標代碼的編譯器。
17.如權利要求15所述的系統(tǒng),其特征在于,所述異常處理構造的統(tǒng)一中間表示包括用于表達向結束代碼塊的顯式控制轉移的第一指令、用于表達向所述結束代碼塊的控制轉移的接受的第二指令、以及用于表達離開所述結束代碼塊的控制轉移的第三指令。
18.如權利要求17所述的系統(tǒng),其特征在于,所述第二指令的目標操作數(shù)與所述第三指令的源操作數(shù)相同。
19.如權利要求17所述的系統(tǒng),其特征在于,所述結束代碼塊包括涉及對象的析構函數(shù)的代碼。
20.如權利要求17所述的系統(tǒng),其特征在于,所述結束塊包括涉及表達式臨時對象的析構函數(shù)的代碼。
21.如權利要求17所述的系統(tǒng),其特征在于,用于表達向所述結束代碼塊的顯式控制轉移的第一指令還包括指示所述結束代碼塊的起始的標簽、以及指示在退出所述結束代碼塊之后控制轉移的延續(xù)的標簽。
22.如權利要求17所述的系統(tǒng),其特征在于,用于表達向所述結束代碼塊的控制轉移的接受的第二指令之前有指示所述結束代碼塊的起始的標簽,且向所述結束塊的控制轉移是由對所述標簽的使用來指示的。
23.如權利要求17所述的系統(tǒng),其特征在于,用于表達離開所述結束代碼塊的控制轉移的第三指令包括用于基于進入所述結束代碼塊是顯式的還是由異常引起的,來指示離開所述結束代碼塊的控制轉移的不同延續(xù)的字段。
24.如權利要求23所述的系統(tǒng),其特征在于,在顯式進入之后離開所述結束代碼塊的控制轉移的延續(xù)匹配由所述第一指令指定的延續(xù)。
25.如權利要求15所述的系統(tǒng),其特征在于,所述異常處理構造的統(tǒng)一中間表示包括用于捕捉異常并返回涉及所述異常的異常對象的第一指令、以及用于基于所述異常對象的類型值指定所述異常的處理程序的第二指令。
26.如權利要求25所述的系統(tǒng),其特征在于,用于指定所述處理程序的第二指令包括用于指示所述異常對象的類型值的至少一個布爾源操作數(shù);如果所述布爾源操作數(shù)為真,指示在涉及向其傳遞控制流的處理程序的代碼塊之前的標簽的至少一個源操作數(shù);以及如果所述布爾源操作數(shù)為假,指示在涉及向其傳遞控制流的延續(xù)的代碼塊之前的標簽的至少一個源操作數(shù)。
27.如權利要求15所述的系統(tǒng),其特征在于,所述異常處理構造的統(tǒng)一中間表示包括至少一個用于基于涉及所述異常的異常對象的類型值來指定異常的處理程序的指令,其中,所述指令的目標操作數(shù)包括預定的異常對象,所述指令的第一源操作數(shù)包括指示涉及所述處理程序的代碼塊的標簽,且第二源操作數(shù)包括指示涉及延續(xù)的代碼塊的標簽。
28.如權利要求27所述的系統(tǒng),其特征在于,所述指令可用于將所述異常對象的類型值與所述預定異常對象的類型值進行比較,且如果存在匹配,則將控制流傳遞到涉及所述處理程序標簽的代碼塊,且如果不存在匹配,則將控制流傳遞到涉及所述延續(xù)標簽的代碼塊。
29.如權利要求15所述的系統(tǒng),其特征在于,所述異常處理構造的統(tǒng)一中間表示包括用于指示進入try-except區(qū)域的第一指令、以及用于基于涉及所述異常的類型值選擇多個控制流路徑中的一個用于異常處理的第二指令,其中,可用于選擇的所述多個控制流路徑包括涉及引起所述異常的指令的執(zhí)行的恢復的路徑。
30.如權利要求29所述的系統(tǒng),其特征在于,用于選擇所述控制流路徑用于異常處理的第二指令包括指示所述異常的類型值的操作數(shù);指示處理程序代碼塊的標簽操作數(shù);指示延續(xù)代碼塊的標簽操作數(shù);以及指示導致異常的指令的標簽操作數(shù)。
31.如權利要求30所述的系統(tǒng),其特征在于,用于指示進入try-except區(qū)域的第一指令的處理程序與所述導致異常的指令的處理程序相同。
32.一種其上儲存源代碼的異常處理構造的中間表示的計算機可讀存儲介質,所述異常處理構造的中間表示包括用于表達向結束代碼塊的顯式控制轉移的第一指令;用于表達向所述結束代碼塊的控制轉移的接受的第二指令;以及用于表達離開所述結束代碼塊的控制轉移的第三指令。
33.如權利要求32所述的計算機可讀存儲介質,其特征在于,用于表達向所述結束代碼塊的顯式控制轉移的第一指令還包括指示所述結束代碼塊的起始的標簽、以及指示在退出所述結束代碼塊之后控制轉移的延續(xù)的標簽。
34.如權利要求32所述的計算機可讀存儲介質,其特征在于,用于表達向所述結束代碼塊的控制轉移的接受的第二指令之前有指示所述結束代碼塊的起始的標簽,且向所述結束塊的控制轉移是由對所述標簽的使用來指示的。
35.如權利要求34所述的計算機可讀存儲介質,其特征在于,所述控制轉移是顯式的。
36.如權利要求34所述的計算機可讀存儲介質,其特征在于,所述控制轉移是由異常引起的。
37.如權利要求32所述的計算機可讀存儲介質,其特征在于,用于表達離開所述結束代碼塊的控制轉移的第三指令包括用于基于進入所述結束代碼塊是顯式的還是由異常引起的,來指示離開所述結束代碼塊的控制轉移的不同延續(xù)的字段。
38.如權利要求37所述的計算機可讀存儲介質,其特征在于,在顯式進入之后離開所述結束代碼塊的控制轉移的延續(xù)匹配由所述第一指令指定的延續(xù)。
39.如權利要求32所述的計算機可讀存儲介質,其特征在于,所述第二指令的目標操作數(shù)與所述第三指令的源操作數(shù)相同。
40.如權利要求32所述的計算機可讀存儲介質,其特征在于,到所述結束塊的控制流是由一組相關的FINAL、FINALLY和ENDFINALLY指令來表達的。
41.如權利要求32所述的計算機可讀存儲介質,其特征在于,所述結束代碼塊包括涉及對象的析構函數(shù)的代碼。
42.如權利要求32所述的計算機可讀存儲介質,其特征在于,所述結束代碼塊包括涉及表達式臨時對象的析構函數(shù)的代碼。
43.如權利要求42所述的計算機可讀存儲介質,其特征在于,所述表達式臨時對象是在條件為真時創(chuàng)建的,且在同一條件為真時,所述控制被轉移到所述結束代碼塊。
44.一種其上儲存源代碼的異常處理構造的中間表示的計算機可讀存儲介質,所述異常處理構造的中間表示包括用于捕捉異常并返回涉及所述異常的異常對象的第一指令;以及用于基于所述異常對象的類型值指定所述異常的處理程序的第二指令。
45.如權利要求44所述的計算機可讀介質,其特征在于,用于指定所述處理程序的第二指令包括用于指示所述異常對象的類型值的至少一個布爾源操作數(shù);如果所述布爾源操作數(shù)為真,指示在涉及向其傳遞控制流的處理程序的代碼塊之前的標簽的至少一個源操作數(shù);以及如果所述布爾源操作數(shù)為假,指示在涉及向其傳遞控制流的延續(xù)的代碼塊之前的標簽的至少一個源操作數(shù)。
46.如權利要求45所述的計算機可讀介質,其特征在于,所述延續(xù)代碼塊涉及另一過濾器。
47.如權利要求45所述的計算機可讀介質,其特征在于,所述延續(xù)相關代碼塊包括展開指令。
48.一種其上儲存源代碼的異常處理構造的中間表示的計算機可讀存儲介質,所述異常處理構造的中間表示包括用于基于涉及所述異常的異常對象的類型值來指定異常的處理程序的指令,其中,所述指令的目標操作數(shù)包括預定的異常對象,所述指令的第一源操作數(shù)包括指示涉及所述處理程序的代碼塊的標簽,且第二源操作數(shù)包括指示涉及延續(xù)的代碼塊的標簽。
49.如權利要求48所述的計算機可讀存儲介質,其特征在于,所述指令可用于將所述異常對象的類型值與所述預定異常對象的類型值進行比較,且如果存在匹配,則將控制流傳遞到涉及所述處理程序標簽的代碼塊,且如果不存在匹配,則將控制流傳遞到涉及所述延續(xù)標簽的代碼塊。
50.一種其上儲存源代碼的異常處理構造的中間表示的計算機可讀存儲介質,所述異常處理構造的中間表示包括用于指示進入try-except區(qū)域的第一指令;以及用于基于涉及所述異常的類型值選擇多個控制流路徑中的一個用于異常處理的第二指令,其中,可用于選擇的所述多個控制流路徑包括涉及引起所述異常的指令的執(zhí)行的恢復的路徑。
51.如權利要求50所述的計算機可讀存儲介質,其特征在于,用于選擇所述控制流路徑用于異常處理的第二指令包括指示所述異常的類型值的操作數(shù);指示處理程序代碼塊的標簽操作數(shù);指示延續(xù)代碼塊的標簽操作數(shù);以及指示導致異常的指令的標簽操作數(shù)。
52.如權利要求50所述的計算機可讀存儲介質,其特征在于,用于指示進入try-except區(qū)域的第一指令的處理程序與所述導致異常的指令的處理程序相同。
53.一種用于實現(xiàn)用于多種源代碼語言的統(tǒng)一異常處理中間表示的系統(tǒng),所述系統(tǒng)包括用于讀取源代碼文件的中間語言表示并基于所述中間語言表示生成所述源代碼的異常處理構造的統(tǒng)一中間表示的裝置;其中,所述統(tǒng)一中間表示顯式表達了所述源代碼的異常處理控制流。
全文摘要
如本發(fā)明所描述的,源代碼文件的中間表示可用于在生成源代碼的目標代碼之前顯式地表達異常處理控制流。如本發(fā)明中進一步描述的,可使用中間表示的單獨的一組統(tǒng)一的指令來表達與多種不同的編程語言有關的不同的異常處理機制。異常(10)處理指令的中間形式可通過轉換源代碼文件的中間語言表示來生成。多種不同的中間語言中的源代碼的表示可被轉換成中間表示的一組單獨的統(tǒng)一指令。異常處理指令的中間形式然后可由軟件開發(fā)工具用于諸如代碼生成、代碼優(yōu)化、代碼分析等任務。由此,可對多種不同語言的中間表示使用用于代碼優(yōu)化、分析和生成的同一方法。
文檔編號G06F11/00GK1842767SQ200480022496
公開日2006年10月4日 申請日期2004年5月21日 優(yōu)先權日2003年6月26日
發(fā)明者V·K·格羅弗, A·V·S·薩斯特瑞 申請人:微軟公司