• / 24
  • 下載費用:30 金幣  

擴展開發環境.pdf

摘要
申請專利號:

CN201380068712.0

申請日:

2013.12.27

公開號:

CN105164641A

公開日:

2015.12.16

當前法律狀態:

授權

有效性:

有權

法律詳情: 授權|||實質審查的生效IPC(主分類):G06F 9/45申請日:20131227|||公開
IPC分類號: G06F9/45 主分類號: G06F9/45
申請人: 微軟技術許可有限責任公司
發明人: F·A·瑪每瑞; M·C·范寧
地址: 美國華盛頓州
優先權: 13/732,073 2012.12.31 US
專利代理機構: 上海專利商標事務所有限公司31100 代理人: 顧嘉運
PDF完整版下載: PDF下載
法律狀態
申請(專利)號:

CN201380068712.0

授權公告號:

||||||

法律狀態公告日:

2019.03.08|||2016.01.13|||2015.12.16

法律狀態類型:

授權|||實質審查的生效|||公開

摘要

本文描述了用于擴展軟件開發工具的行為的概念和技術。軟件開發工具可訪問并消費擴展以將該軟件開發工具配置成以擴展模式執行操作。在一個示例中,擴展可基于輸入源代碼來擴展編譯器。在一個配置中,編譯器擴展可以向編譯器提供特定編程語言的各種源代碼元素的一個或多個運行時語義。編譯器可訪問擴展列表以確定編譯器是否將要以擴展模式對特定源代碼元素或邏輯單元執行編譯操作。

權利要求書

權利要求書
1.  一種用于執行源代碼開發操作的方法,所述方法包括:
接收包括包含至少一個邏輯單元的源代碼的輸入文件;
接收對所述至少一個邏輯單元執行開發操作的指令;
響應于接收到對所述至少一個邏輯單元執行所述開發操作的指令,確定擴展是否將被消費以用于所述開發操作;以及
如果所述擴展將被消費以用于所述開發操作,則
檢索所述擴展;
通過消費所述擴展來變為擴展模式;以及
以所述擴展模式對所述至少一個邏輯單元執行所述開發操作。

2.  如權利要求1所述的方法,其特征在于,所述開發操作是預處理、詞法分析、句法分析、語義分析、指令調度、代碼生成、鏈接、匯編、解釋和JIT編譯中的一個或多個。

3.  如權利要求1所述的方法,其特征在于,確定所述擴展是否將被消費以用于所述開發操作包括接收指定將在所述擴展模式中對其進行操作的一個或多個邏輯單元的擴展輸入。

4.  如權利要求1所述的方法,其特征在于,確定所述擴展是否將被消費以用于所述開發操作包括以下動作中的一個或多個:
在擴展文件中檢查所述擴展;
通過基于與輸入相關聯的名稱或標識符計算擴展文件名來查找所述擴展;以及
檢查與編譯單元內聯的元數據。

5.  一種計算機,包括:
處理器;以及
與所述處理器通信的計算機可讀存儲介質,所述計算機可讀存儲介質包括 其上存儲的計算機可執行指令,所述計算機可執行指令在由所述處理器執行時使所述處理器:
接收包括將被編譯的至少一個邏輯單元的輸入文件;
接收對所述至少一個邏輯單元執行編譯操作的指令;
響應于接收到執行所述編譯操作的指令,確定是否將在所述至少一個邏輯單元將被編譯時為所述至少一個邏輯單元執行擴展;以及
如果將為所述邏輯單元執行所述擴展,則
檢索針對所述至少一個邏輯單元的擴展;以及
在對所述至少一個邏輯單元執行所述編譯操作之前執行所述擴展;以及
使用所述擴展來對所述至少一個邏輯單元執行所述編譯操作。

6.  如權利要求5所述的計算機,其特征在于,所述邏輯單元包括以下各項中的一個或多個:對函數的定義或使用、類、方法、變量、宏、作用域、模板、過程、閉包、類型、指示、源文件本身或庫。

7.  如權利要求5所述的計算機,其特征在于,所述至少一個邏輯單元是針對庫或系統的傳統版本的源代碼,而輸出是能對所述庫或所述系統的新版本進行操作的計算機程序。

8.  如權利要求5所述的計算機,其特征在于,所述用于確定是否將在所述至少一個邏輯單元將被編譯時為所述至少一個邏輯單元執行所述擴展的計算機可執行指令包括在由所述處理器執行時使所述處理器執行以下操作的計算機可執行指令:接收指定將被擴展的一個或多個可擴展邏輯單元的擴展輸入或者在擴展文件中檢查針對所述至少一個邏輯單元的擴展。

9.  如權利要求5所述的計算機,其特征在于,針對所述至少一個邏輯單元的所述擴展包括迫使對邏輯單元的引用引用功能等同體。

10.  一種包括其上存儲的計算機可執行指令的計算機可讀存儲介質,所 述計算機可執行指令在由計算機執行時使所述計算機:
接收將從第二編程語言編譯成第一編程語言的輸入文件,所述輸入文件包括用所述第二編程語言的多個邏輯單元;
接收對所述輸入文件中的所述多個邏輯單元執行代碼生成的指令;
響應于執行代碼生成的所述指令,分析所述多個邏輯單元中的第一邏輯單元;
確定擴展是否與所述多個邏輯單元中的所述第一邏輯單元相關聯;
如果擴展與所述多個邏輯單元中的所述第一邏輯單元相關聯,則
執行與所述第一邏輯單元相關聯的所述擴展;
如果擴展不與所述多個邏輯單元中的所述第一邏輯單元相關聯,則
訪問庫,
在不執行所述擴展的情況下將所述第一邏輯單元編譯成經編譯的第一邏輯單元,以及
將所述經編譯的第一邏輯單元輸出到用所述第一編程語言的輸出文件。

說明書

說明書擴展開發環境
背景
為了開發用于執行計算任務的軟件應用,軟件開發者通常為軟件應用編寫概括該軟件應用的功能的源代碼。存在對開發者可用的各種類型的語言,這取決于軟件應用的特定用途、其中將執行軟件應用的計算環境和/或軟件開發者的個人偏好。例如,語言可以是其中通常在編譯時確定變量類型的靜態類型化編程語言。在另一示例中,語言可以是在運行時確定變量類型的動態類型化編程語言。
一旦軟件開發者編寫源代碼,如果其中將要執行源代碼的環境需要可執行代碼,則使用編譯器來將源代碼編譯成可執行代碼(或者取決于系統環境而能在執行時被解釋)。編譯器用于將源代碼轉換成可以直接在計算機系統上執行的機器代碼。計算環境執行可執行代碼以實現源代碼的功能。連同將源代碼編譯成可執行代碼,常規編譯器還可通過輸出調試信息來幫助軟件開發者。軟件開發者可使用調試信息來修復源代碼中的潛在問題,當在運行時期間執行可執行代碼時,這些潛在問題可能導致故障。
本文所做出的本公開正是關于這些和其他考慮事項而提出的。
概述
本文描述了用于擴展開發環境的概念和技術。開發工具可消費擴展以修改開發工具針對所描繪的源代碼元素的行為。在一些實施例中,開發工具可消費擴展以便在各種過程中使用擴展行為,這些過程包括但不限于預處理、詞法分析、句法分析、靜態分析、指令調度、代碼生成、鏈接、匯編、解釋和JIT編譯。例如,在擴展編譯器的行為時,擴展可由編譯器在包括但不限于句法分析、語義分析、代碼生成和代碼優化的編譯過程的各個階段期間消費。在一個配置中,當在編譯器開發環境內使用時,編譯器擴展可以向編譯器提供特定編程語言的各種源代碼元素的一個或多個運行時語義。在一些配置中,可以在諸如但 不限于靜態分析器、解釋器、編譯器、鏈接器、匯編器和模擬器的各種編程操作中使用擴展。
應當理解,上述主題可被實現為計算機控制的裝置、計算機進程、計算系統或諸如計算機可讀存儲介質等制品。通過閱讀下面的詳細描述并審閱相關聯的附圖,這些及各種其他特征將變得顯而易見。
提供本概述是為了以精簡的形式介紹將在以下詳細描述中進一步描述的一些概念。本概述并不旨在標識所要求保護的主題的關鍵特征或必要特征,也不旨在將本概述用來限制所要求保護的主題的范圍。此外,所要求保護的主題不限于解決在本公開的任一部分中所提及的任何或所有缺點的實現。
附圖簡述
圖1是可被用來實現本文公開的各實施例的說明性操作環境的系統圖。
圖2是執行可被用來實現本文公開的各實施例的編譯器的說明性編譯環境的系統圖。
圖3是示出根據一些實施例的其中根據擴展來處理源代碼的邏輯單元的編譯環境的功能框圖。
圖4是示出根據一些實施例的將傳統源代碼變換成用于經更新的操作環境的可執行代碼的功能框圖。
圖5是示出根據一些實施例的用于擴展軟件開發工具的示例性方法的框圖。
圖6是示出能夠實現本文中所呈現的實施例的各方面的計算系統的說明性計算機硬件和軟件體系結構的計算機體系結構圖。
詳細描述
以下詳細描述涉及擴展軟件開發工具的行為。在各配置中,向軟件開發工具提供擴展。軟件開發工具消費擴展。擴展將軟件開發工具針對所描繪的操作的行為從默認模式改為擴展模式。在一些示例中,軟件開發工具可出于各種原因在擴展模式中使用,這些原因的示例在下文中更詳細地描述。
盡管在結合計算機系統上的操作系統和應用程序的執行而執行的程序模 塊的一般上下文中提出了本文描述的主題,但是本領域技術人員將認識到,其他實現可以結合其他類型的程序模塊來執行。一般而言,程序模塊包括執行特定任務或實現特定抽象數據類型的例程、程序、組件、數據結構和其他類型的結構。此外,本領域技術人員將明白,可以利用其他計算機系統配置來實施本文描述的主題,這些計算機系統配置包括手持式設備、多處理器系統、基于微處理器的或可編程消費電子產品、小型計算機、大型計算機等等。
在以下詳細描述中,參考了構成詳細描述的一部分并作為說明示出了各具體實施方式或示例的附圖。現在參考附圖(全部若干附圖中相同的標號表示相同的元素),將提出用于擴展源代碼開發環境的計算系統、計算機可讀存儲介質和計算機實現的方法以及其他方面。
現在參考圖1,將描述用于文本呈現的各實施例的源代碼開發環境100的各方面。圖1所示的源代碼開發環境100包括計算設備102。在一些實施例中,計算設備102可包括臺式計算機、膝上型計算機、筆記本計算機、超便攜計算機、上網本計算機或其它類型的計算設備。操作系統104在計算設備102上執行。操作系統104是用于控制計算設備102處的各種功能的可執行程序。
計算設備102可執行軟件開發工具106。軟件開發工具106可包括程序或工具,諸如但不限于靜態分析器、解釋器、編譯器、鏈接器、匯編器和模擬器。軟件開發工具106可接收包括編程語言中所表達的源代碼的輸入文件108。軟件開發工具106可接收對輸入文件108中的源代碼執行各種開發操作的命令或指令。
應當明白,本文描述的概念和技術不限于任何特定類型的編程語言。例如,輸入文件108中的源代碼編程文件類型可使用靜態類型化編程語言、動態類型化編程語言或其它類型來表達。靜態類型化編程語言的示例包括但不限于C、Fortran、Java和Pascal。動態類型化編程語言的示例包括但不限于JavaScript、Perl、Lisp和VBScript。其他類型包括強和弱類型、安全和不安全類型、相交類型、聯合類型、鴨子類型和依賴類型。應當明白,本文描述的概念和技術不限于任何特定類型。還應明白,本文描述的概念和技術不限于作為一種特定類型的編程語言,因為一些編程語言可用作或可被認為是各種類型。
在一些配置中,開發操作可以是不產生輸出的對輸入文件108中的源代碼 的操作。在其他配置中,開發操作可生成在圖1中被示為開發工具輸出110的輸出。例如,如果軟件開發工具106是編譯器且操作是代碼生成,則開發工具輸出110可包括可執行代碼中所表達的一個或多個輸出邏輯單元。如此處所使用的,“邏輯單元”包括一起形成邏輯結合整體的一組代碼塊或語句。邏輯單元可以是源代碼元素或者可包括形成邏輯結合整體的一個或多個源代碼元素。開發工具輸出110還可包括但不限于可查看消息、查詢、輸入文件108的一個或多個分量的重寫,等等。本文描述的概念和技術不限于任何特定輸出。
在某些情況下,軟件開發工具106可能不被配置成或不具有以下能力:理解軟件開發工具106正對其執行開發操作的輸入文件108中的源代碼元素的行為。如此處所使用的,“源代碼元素”包括但不限于對函數的定義或使用、類、方法、變量、宏、模板、過程、閉包、類型、源代碼文件本身或所引用的庫。可以存在軟件開發工具106無法識別或理解源代碼元素的行為的各種原因。
例如,如果源代碼元素的行為將在運行時期間被決定,則軟件開發工具106無法理解該元素的行為。邏輯單元可包括軟件開發工具106未被配置來識別的函數。在其他情況下,軟件開發工具106可被配置成對源代碼元素執行不安全、過期或由于某種原因而不合需要的操作。在其他情況下,源代碼元素或邏輯單元可包括妨礙軟件開發工具106識別的誤拼寫。在編譯器環境中的另一示例中,編譯器無法在編譯時理解各種函數的實際運行時語義。應明白,這些和其他示例僅僅是示例性的。本文描述的概念和技術不限于軟件開發工具106的任何特定限制、故障或不合需要的行為。
為了修改軟件開發工具106的行為,軟件開發工具106可被配置成訪問和消費擴展112,擴展112在執行各種開發操作時擴展(或修改)軟件開發工具106的行為。在擴展模式中操作的軟件開發工具106在圖1中被示為軟件開發工具(經擴展的)106A。如此處所使用的,“擴展”意指軟件開發工具106消費擴展112以擴大、增加和/或更改源開發工具106的能力。通過擴展軟件開發工具106的能力,軟件開發工具106可被配置成(作為示例而非限制)執行它先前無法執行的操作。在其他配置中,軟件開發工具106可被擴展為軟件開發工具(經擴展的)106A,以便對源代碼元素執行經修改的操作,而不是以非擴展模式執行默認操作。在一些示例中,軟件開發工具106A可被擴展為軟件 開發工具(經擴展的)106A,以便在以默認或非擴展模式執行操作之外還以擴展模式執行操作。例如,擴展模式可包括不在默認模式中執行的對源代碼元素的附加操作。
圖2是示出其中軟件開發工具106是編譯器206的編譯環境200的功能框圖。如上所述,本文描述的概念和技術不限于任何特定開發環境。本文所包含的各種說明和描述僅僅出于描述目的而使用編譯器環境,且不表示將當前所公開的主題僅僅限于編譯器環境的意圖。為了執行編譯操作,編譯器206可以在其編程內具有執行編譯操作所必需的組件。
在一些配置中,編譯器206可包括內部庫214,該內部庫214在一些配置中可以是可由編譯器206消費以執行各種編譯操作的程序或可執行代碼的集合。在一些配置中,編譯器206可訪問第三方庫,諸如外部庫216。在一些配置中,外部庫216可以是未被包括在內部庫214中的可由編譯器206消費以執行各種編譯操作的程序或可執行代碼的集合(或編譯器206的源代碼)。如此處所使用的,“庫”包括可由諸如編譯器206等軟件開發工具消費的二進制形式的代碼。內部庫214和外部庫216在圖1中僅僅出于說明目的而被示為單個庫,并且不反映將本公開限于單個庫的意圖。內部庫214或外部庫216可包括可以從源程序消費的程序代碼的函數、宏、類模板(取決于特定編譯器和可執行代碼)和其他單元。
如上所述,為了處置特定編譯時操作,編譯器206可在其代碼內具有編程,可訪問內部庫214和/或可訪問外部庫216。例如,編譯器206可接收對程序段(I)執行語義分析的指令:
inti;
printf(“%s\n”,i);(I)。
在一些配置中,編譯器206可使用編譯器206的各個組件來對程序段(I)執行語義分析。在其他配置中,編譯器206可以在編譯程序段(I)時訪問內部庫214或外部庫216(或兩者)。
盡管常規編譯器可能能夠對程序段(I)執行語義分析(或其他編譯時操作),但在運行時期間可能由于各種編譯時狀況而出現錯誤。如果源代碼元素的行為是在運行時期間用動態類型化編程語言來決定的,則在源代碼的特性在 編譯時期間未被靜態描述的情況下可能出現錯誤。作為示例,以下JavaScript代碼包括可由包括內部庫214和/或外部庫216在內的各種庫提供的被稱為“DefineNamespace”的函數:
DefineNamespace(‘Utilities.File’,{open:function(name){/*代碼*/}});
varfile=Utilities.File.open(‘a.txt’);
名稱“Utilities.File”通常直到運行時才被指定,此時執行DefineNamespace。上述對代碼執行語義分析的編譯器可能不知道該代碼在運行時是否正確地執行。為了降低常規上通過編譯時分析要求,但在運行時期間導致程序不正確地運行的函數引入錯誤的概率,編譯器206可訪問可以向該編譯器206提供各種邏輯單元的運行時語義的擴展112A-112N,以使得編譯器206能夠在編譯時期間理解邏輯單元(擴展112A-112N之后被統稱為和/或通稱為“擴展112”)。在各種配置中,向編譯器206提供運行時語義可幫助編譯器206確認與邏輯單元相關聯的參數。
擴展112可由各種源創建和/或從各種源提供給編譯器206。例如,擴展112可以是被提供給開發者的用于特定操作環境的軟件開發套件的一部分。在另一示例中,擴展112可由一個或多個開發者生成以供其他開發者使用。在另一示例中,開發者可以在內部庫214、外部庫216或其他第三方庫上創作擴展112。在另一示例中,擴展112可以是內部庫214和/或外部庫216的一部分。
編譯器206可以在編譯時期間消費擴展112以便對源代碼元素和/或邏輯單元執行編譯操作。在上述第一示例中,編譯器206可被配置成檢測“printf”函數是將根據擴展112來處理的函數。在該配置中,編譯器206可消費擴展112以便以擴展模式執行編譯操作,而不是消費內部庫214中的可執行代碼以便以默認或非擴展模式執行編譯操作。在上述第二示例中,通過消費擴展112,編譯器206可被配置成在運行時期間理解“DefineNamespace”函數行為并且在一個示例中通過創建合適對象來在編譯時期間模仿(或模擬)該行為。
擴展112還可以向編譯器206提供識別源代碼中的特定錯誤的能力,這些特定錯誤可能在運行時期間導致錯誤或者可能妨礙編譯操作完成。例如,如果開發者編寫函數“DefineNamespace”的源代碼但以錯誤方式編寫該函數,則編譯器206可消費擴展112以創建各種對象并確定該函數將在被執行時生成錯 誤。例如,開發者可編寫其中單詞“open”被誤拼寫為“oopen”的代碼段:
DefineNamespace(‘Utilities.File’,{open:function(name){/*代碼*/}});
varfile=Utilities.File.oopen(‘a.txt’).
編譯器206可消費擴展112以便對上述代碼段執行成功的語義分析。例如,編譯器206可消費擴展112以便通過用靜態地處理上述代碼段的一個或多個結果填充符號表來以擴展模式執行操作。在創建靜態處理的一個或多個結果后,編譯器206可檢測到第二行可由于“open”的誤拼寫而導致運行時錯誤。在該示例中,如果編譯操作是代碼生成操作,則編譯器206可以在消費擴展112后以擴展模式行動并生成可操作的可執行代碼。在不以擴展模式操作的情況下,編譯器206可以在非擴展模式中使用默認行為來生成不可操作代碼。一些其他示例在下文中提供。
傳統上,靜態分析已經與靜態語言相關聯。例如,考慮以下C代碼:
longaNumber;
voidSomeFunction(inti){/*函數體*/}
voidSomeOtherFunction(){
XomeFunction();
aNumber="hello";
}
在以上示例中,編譯器206可標記2個語義問題:名稱“XomeFunction”不存在;以及aNumber被賦值“string”(在技術上是指向字符數組的指針)。常規C編譯器能這樣做的原因是因為C是靜態語言。在靜態語言中,所有語言構造的名稱和類型在編譯時期間都是已知的。甚至對于靜態語言而言,特定檢查通常直到運行時才能被執行-即使所有元素的名稱和類型在編譯時期間都是已知的。例如,考慮以下C函數:
date_tcreate_date(unsignedcharday,unsignedcharmonth,shortintyear){
/*實現*/
}
該函數被認為是靜態的。該函數本身、所有其參數及其類型以及其返回類型在編譯時期間全都是已知的。然而,特定種類的錯誤在運行時期間仍然可能 發生。考慮以上代碼的消費者的以下示例:
date_tdate1=create_date(29,02,2012);/*有效日期,2012是閏年*/
date_tdate2=create_date(29,02,2010);/*無效日期,2010不是閏年*/
以上對create_date函數的兩個調用被認為是有效的;該函數存在,自變量的數量是正確的,自變量的類型是正確的,所提供的值在針對類型的期望范圍內,且消費者的期望返回類型匹配該函數所聲明的一個返回類型。然而,這些日期中只有一個是有效的。構造date1將會工作,但date2不會。create_date函數針對date2的行為取決于如何實現該函數。例如,可能拋出運行時異常,可能返回表示錯誤的特殊實例date_t,和/或可能創建包含無效數據的有效實例date_t。
如上所示,甚至對靜態語言而言,也存在傳統上在運行時期間執行的特定種類的檢查。測試軟件可以是易出錯且昂貴的。理想地,應在編譯時或構建時期間捕捉到盡可能多的錯誤。如果create_date函數被構建在編譯器中或者是該語言的標準庫的一部分,則該編譯器可執行檢查以提出關于以上date2的錯誤。標準庫是隨編譯器提供的預構建庫的集合。因此,編譯器可被編程為知道該標準庫。
然而,如果以上create_date函數不是標準庫的一部分,則編譯器無法執行該函數專用的任何檢查。如果開發團隊想要執行該函數專用的檢查,則必須出于這樣的目的而編寫或消費附加工具。這些工具可以對:源代碼;目標代碼;調試符號表;或者上述各項的組合進行操作。
這些工具可以是:獨立庫;為語義分析器(諸如FxCop、Lint等工具)編寫的規則;其他;或上述各項的組合。但是,這些工具可具有若干缺陷。例如,它們可能需要被加入(即,開發者將需要為其代碼庫中的每一個編譯位置打開工具/規則/等等)。由于這些工具不是編譯器的一部分,因此它們丟失與該編譯器的同步(例如,這些工具可能在新版本的編譯器改變例如目標文件的布局的情況下中斷)。在其他情況下,這些工具經常需要重復編譯器中的功能(例如,解析、語義分析等)以提供有意義的分析。
為了擴展編譯器206的行為和/或功能,可實現插件模型。在以上示例中,考慮create_date函數的導入聲明被改變為看似以下:
extension("date.dll")
date_tcreate_date(unsignedcharday,unsignedcharmonth,shortintyear){
/*實現*/
}
在以上示例中,date.dll是包含對編譯器的一個或多個插件(即,編譯器擴展)的庫。具體而言,它可包括將擴展編譯器的語義分析的插件,如下:

通過運行與對create_date的調用的語義分析相關聯的擴展112(即,插件),編譯器206可執行原本在編譯時期間是不可能的檢查。
在一些配置中,對于語義分析階段,這可具有勝于上述外部工具的以下優點。檢查可以相對“廉價”-工具鏈接其他程序、構造模型等的成本被節省。檢查可由編譯器206執行-無需加入,它就在那兒,只要使用編譯器206。不存在與編譯器206重復的功能。并且,擴展112未丟失與編譯器206的同步。
在以上示例中,C語言句法已經用可用于指定擴展112的關鍵字來擴充。替代實現包括但不限于:包含擴展的文件需要使用語言句法來顯式聲明;將源元素映射到擴展的外部文件;源代碼中的格式化良好的評論(源代碼注釋);源屬性;以及其他。其他示例包括但不限于:編譯器可查找具有與包含引用的文件相同的文件前綴的DLL(例如,對于date.h、date.c或date.obj,編譯器將查找date.dll);編譯器可查找具有相同的基名的腳本(例如,date.lua或date.js);系統中注冊的類型庫(例如,COM),其中類型庫被指定;系統中注冊的類型庫(例如,COM),其中類型庫是隱式的;以及擴展可位于與引用(例如,date.h、date.c或date.obj)相同的單元中。應理解,本文描述的概念和技術不限于上述實現。
繼續以上示例,到DLL的完整路徑尚未被指定。替代實現包括但不限于:擴展112路徑可以是顯式的;擴展112路徑可以是隱式的,其中編譯器可搜索當前工作目錄、源文件目錄、預定義目錄、預定義系統文件夾;擴展112路徑可以是隱式的,且搜索路徑由用戶相對于命令行選項顯式提供,命令行選項具有將按序搜索的目錄的列表或者要搜索(例如,通過使用#pragma指示)的目錄的源中列表;或者擴展112路徑可以是系統中的全局設置,諸如系統注冊表或環境變量。另一實現可以經由web服務跨網絡動態地獲取擴展112。這些概念和技術不限于在此明確描述的實現,因為可使用其他實現并且其他實現被認為在本公開的范圍內。
在以上示例中,已經為擴展112提供具有二進制碼的單獨DLL。替代實現包括但不限于:可使用腳本語言(諸如JavaScript、Lua等);擴展112可以是編寫為源程序的一部分的函數本身;或者擴展112可以是源程序中的評論。這些概念和技術不限于在此明確描述的實現,因為可使用其他實現并且其他實現被認為在本公開的范圍內。
在以上示例中,擴展DLL中的入口點的名稱尚未被提供。替代實現包括 但不限于:具有與引用構造相同的名稱的函數,其中使用過載來消除針相同構造的不同擴展的歧義;具有與引用構造相同的名稱的函數,其中使用相同的詞綴來消除對相同構造的不同擴展的歧義;需要提供入口點的顯式名稱;擴展函數的v表中的偏移;或者導出函數的導出索引。這些概念和技術不限于在此明確描述的實現,因為可使用其他實現并且其他實現被認為在本公開的范圍內。
在以上示例中,隱式名稱匹配是區分大小寫的。替代實現包括但不限于:大小寫敏感;大小寫不敏感;或者特定大寫規則(例如,關于詞綴等)。這些概念和技術不限于在此明確描述的實現,因為可使用其他實現并且其他實現被認為在本公開的范圍內。
在以上示例中,已擴展編譯器206行為以用于語義分析期間的函數調用。可以在語義分析期間擴展的替代源構造包括但不限于:對過載操作符的調用;函數引用;類型實例化;變量引用;變量賦值;或者指針解引用。這些概念和技術不限于在此明確描述的實現,因為可使用其他實現并且其他實現被認為在本公開的范圍內。
以上示例提出關于被傳遞給函數的特定值的錯誤。對擴展112可用的其他動作包括但不限于:添加、修改或刪除符號表中的符號;對輸入文檔的句法樹進行操作;提出錯誤和警告;改變錯誤和警告的抑制狀態;創建、修改和刪除源元素,諸如類型、枚舉、常量值、宏或命名空間;或者執行文件操作。這些概念和技術不限于在此明確描述的實現,因為可使用其他實現并且其他實現被認為在本公開的范圍內。
以上示例示出了與庫一起提供的擴展。這不是創作和分發擴展的唯一方式。創作和分發擴展的其他方式包括但不限于:與庫本身一起分發的對庫的擴展,諸如與編譯器一起分發的標準庫擴展;由與對標準庫的擴展不同的廠商開發的擴展;或者由庫的消費者開發的用于強制實施由團隊架構師定義的特定用途或幫助遷移至庫的新版本的擴展。
以上示例未示出對擴展的許可。一些許可可以包括但不限于:與庫相同的許可;以使用擴展112的許可為條件的使用庫的許可;開源許可;閉源但免費使用的許可;或者專用的付費許可。以上示例也未示出在擴展112未被找到或具有錯誤的情況下的編譯器206行為。一些方法可以包括但不限于:提出錯誤; 提出警告;記錄故障日志并繼續;記錄故障日志并中止;不做任何事并忽略故障;提供用于指定做什么的源中指示;或者提供用于指定做什么以及擴展聲明的機制。
以上示例示出了靜態語言的編譯器可如何使用擴展。擴展也可用于動態語言。考慮以下JavaScript示例:
WinJS.Namespace.define("Utilities.File",{
open:function(name){
/*實現*/
}
});
varf=Utilities.File.open("foo.txt");
在以上示例中,對庫函數(WinJS.Namespace.define)的調用將創建名稱Utilities.File.open。在動態類型語言中,名稱創建直到運行時才發生。靜態地,以上示例不包含名稱Utilities.File.open。Utilities.File僅僅是被傳遞至函數的字符串。傳統上,由于用動態語言(諸如JavaScript)編寫的程序能夠在運行時期間創建和刪除名稱(如以上示例中),因此用于此類語言的靜態分析器無法提出關于不存在的名稱的問題。擴展可用于提高對動態程序的語義分析的質量。在以上示例中,擴展可被提供給WinJS.Namespace.define函數,該擴展執行以下步驟:檢查第一參數;如果它不是字符串,則退出擴展;解析字符串參數;和/或在不存在必需符號的情況下創建所有必需符號。
通過對WinJS.Namespace.define的調用運行上述擴展112,靜態分析器可具有將在運行時期間創建的名稱的知識。上述語義分析器然后將能夠標記違例。例如,考慮與先前程序類似但具有打字錯誤的以下程序:
WinJS.Namespace.define("Utilities.File",{
open:function(name){
/*實現*/
}
});
varf=Utilities.File.oopen("foo.txt");
語義分析器可被配置成標記打字錯誤,因為所請求的名稱(“oopen”)無法在符號表中找到。編譯器206可被觸發以便在使用各種機制來編譯輸入文件108中的源代碼時訪問擴展112。例如,編譯器206可被配置成在對輸入文件108中的源代碼的每一個邏輯單元執行編譯操作時檢查擴展112。在另一配置中,編譯器206可被配置成基于包括指定要執行的邏輯單元的擴展輸入的編譯時指令集來訪問擴展112。例如,編譯器206可以在編譯時被指示只用擴展112來處理所指定或預定的邏輯單元。將根據擴展112來處理的預定的邏輯單元或源代碼元素可被存儲編譯器206可訪問的列表中。應理解,其他邏輯單元或源代碼元素可使用擴展112來處理。本文公開的概念和技術不限于任何特定元素或邏輯單元。
用于檢索和消費擴展112的其他觸發事件可包括但不限于:將擴展112與諸如‘foo’等任何標識符的出現相關聯。例如,在常規JS運行時中,先前是合法的一些關鍵字可能不再合法(因為它們已被保留以供在該語言中使用)。本文描述的概念和技術可實現用于一致地重寫這些標識符以使其與所保留的關鍵字一致/不再沖突的算法。另一觸發事件可以是擴展112已經與對特定函數或例程的調用點相關聯。在遇到致力于特定/唯一標識的符號(函數、屬性、特定變量)的調用點時,調用擴展。擴展可能更改/重寫或甚至移除調用點。或者,擴展可能僅僅檢查調用點以創建自身可用于附加診斷/代碼發出等的一些存儲器內信息。命名空間構造示例落在該場景中。對于此,對在運行時產生存儲器內類的API的調用點可能無法以任何方式修改。相反,可以為將在運行時產生的類型/成員創建符號,以使得對這些類型/成員的后續引用可以在代碼中驗證。
另一觸發事件可以是將擴展112與特定類的構造相關聯。例如,當出現任何‘newFoo()’時,調用擴展112。該實現還可用于順便訪問(dropin)替代類或者由于依賴于被禁止的類型的嘗試而停止編譯。其他觸發事件可以是將擴展112與對特定庫/二進制碼/外部引用的引用相關聯。例如,如果程序鏈接到不安全的庫的已作廢版本v1,則擴展112的各種配置可更改編譯以迫使該程序鏈接到經更新/更安全的版本。在一個示例中,用于邏輯單元的擴展112可迫使對邏輯單元的引用引用功能等同體。又一觸發事件可以是將擴展112與抽象句法樹節點種類相關聯。附加配置可以是將擴展112與詞法令牌種類相關聯。
編譯器206還可被配置成檢索和消費針對未被顯式指定而是被隱式指定的源代碼元素或邏輯單元。例如,編譯器206可被配置成消費針對具有與源代碼中的其他邏輯單元或源代碼元素相同的名稱的邏輯單元或源代碼元素的擴展112。在另一示例中,編譯器206可被配置成消費針對具有相同拼寫但不管任何大寫差異的邏輯單元或源代碼元素的擴展112。在另一示例中,可提供針對具有常見的誤拼寫名稱的邏輯單元或源代碼元素的擴展112。在該示例中,編譯器206可被配置成消費對應于誤拼寫的邏輯單元或源代碼元素的擴展112。擴展112可被配置成糾正誤拼寫。在另一配置中,擴展112可以與實現特定接口的每一類型相關聯。在另一配置中,擴展112可以與擴展那特定基類型的所有類型相關聯。
圖3是示出其中根據擴展112來處理源代碼的邏輯單元的編譯環境300的框圖。在圖3中,編譯器206可訪問內部庫214、外部庫216和擴展112。輸入文件108A包括具有邏輯單元302A-302C的源代碼。輸入文件108B包括具有邏輯單元302D-302N(之后統稱為和/或通稱為“邏輯單元302”)的源代碼。應理解,本文描述的概念和技術不限于任何輸入文件108配置,因為各種配置可包括一個輸入,而其他配置可包括多個輸入文件,諸如輸入文件108A和輸入文件108B。
編譯器206可被配置成確定編譯器206是否將在擴展模式中對邏輯單元302中的特定邏輯單元進行操作。應理解,編譯器206可被配置成使用諸如本文描述的那些觸發機制之類的各種觸發機制來檢查擴展。例如,編譯器206可通過以下操作來確定擴展112是否將被消費以用于開發操作:在擴展文件中檢查擴展,通過基于與輸入相關聯的名稱或標識符計算擴展文件名來查找擴展,檢查與編譯單元內聯的元數據,查找具有與包含引用的文件相同的文件前綴的DLL,以及查找具有相同的基名的腳本。編譯器206可接收對輸入文件108A和輸入文件108B執行編譯操作的指令。響應于接收到該指令,編譯器206可以對邏輯單元302執行編譯操作。
在一個配置中,編譯器206可以對邏輯單元302執行編譯操作以便在開發工具輸出110中創建可執行代碼(代碼生成),這些可執行代碼在圖3中被示為邏輯單元304A-304N(之后被統稱為和/或通稱為“邏輯單元304”)。應理 解,開發工具輸出110中的邏輯單元304可包括不直接以1:1關系對應于輸入文件中的邏輯單元302的邏輯單元。例如,在代碼生成編譯操作期間,邏輯單元304可以從一個或多個邏輯單元302中生成或者可由編譯器206生成為與邏輯單元302中的任何特定邏輯單元都無關的邏輯單元。
編譯器206可被配置成檢測到將通過消費擴展112來編譯邏輯單元302D。如上所述,擴展112可以在諸如但不限于句法分析、語義分析、優化和代碼生成等各種編譯操作期間消費。在圖3所示的配置中,編譯器206可開始對邏輯單元302D的代碼生成操作。編譯器206消費擴展112中的擴展(代碼生成)112A以便以擴展模式對邏輯單元302D執行代碼生成操作。編譯器206可生成被示為開發工具輸出110的邏輯單元304中的邏輯單元(經擴展的)204D的輸出邏輯單元。應理解,為邏輯單元(經擴展的)304B指定“擴展”意味著該邏輯單元已經在編譯器206消費擴展(代碼生成)112A之后使用擴展編譯器模式來生成。
如果執行另一編譯器操作,則編譯器206可被配置成基于該操作來使用其他擴展。例如,擴展(語義)112B可由編譯器206在對邏輯單元302D的語義分析操作期間消費。在另一示例中,擴展(優化)112C可由編譯器206在對邏輯單元302D的優化操作期間消費。在另一示例中,擴展(句法)112D可由編譯器206在對邏輯單元302D的句法分析操作期間消費。
應理解,本文描述的概念和技術不限于編譯器206的任何特定操作。還應理解,不一定消費針對源代碼文件中的所有邏輯單元或源代碼元素的擴展。例如,擴展可能由編譯器206針對諸如邏輯單元302D等源代碼元素消費以用于特定編譯操作,且不在其他編譯操作中消費。此外,如上所述,應理解,編譯器206可被配置成消費針對除了函數之外的源代碼元素或邏輯單元的擴展。作為示例而非限制,編譯器206可被配置成消費針對函數、類、方法或變量或其他元素的擴展。本文公開的概念和技術不限于任何特定邏輯單元或源代碼元素的擴展的執行。
本文描述的概念和技術的各實施例可給予開發者關于編寫源代碼的一定程度的靈活性。一些示例在上文中描述。本文描述的概念和技術的實現的附加示例是將傳統編程語言(或傳統版本)變換成編程語言的經更新版本、不同的 編程語言或已消費庫的經更新版本。如此處所使用的,“傳統”意指已經被另一源代碼或編程語言替代的源代碼或編程語言。例如,傳統源代碼可以是早于源代碼的當前使用版本的源代碼版本。在另一示例中,傳統源代碼可以是用早于當前使用的編程語言的編程語言的源代碼。在另一示例中,輸入可以是針對庫或系統的傳統版本的源代碼,而輸出可以是可以對庫或系統的新版本進行操作的計算機程序。
圖4是示出使用本文描述的各種概念和技術來將傳統代碼變換成供在經更新系統中使用的代碼的功能框圖。傳統代碼輸入文件108包括傳統代碼輸入文件108中所包括的邏輯單元402A-402N(之后統稱為和/或通稱為“邏輯單元402”)。邏輯單元402形成傳統程序的源代碼的至少一部分。如果開發者希望在諸如新環境之類的經更新系統中使用邏輯單元402,則開發者可能被要求修改傳統單元402的各部分以使得傳統單元402可以在新環境中執行。
在圖4所示的配置中,如果編譯器206在代碼生成操作期間使用外部庫216,則在該代碼生成操作完成時生成的邏輯單元在新環境中可能不正確地或不按所需要的那樣運行。這可能出于各種原因而發生。例如,邏輯單元402可包括不在執行開發工具輸出110中的可包括傳統代碼的可執行代碼的環境中使用的邏輯單元。在另一示例中,邏輯單元402可包括具有在執行開發工具輸出110的環境中不再使用的變量的函數。
為了向開發者提供在編譯器206編譯邏輯單元402時更新邏輯單元402的能力,擴展112可包括擴展(傳統)112E。擴展(傳統)112E可由編譯器206消費以擴展編譯器206的行為。在一個配置中,擴展(傳統)112E可由編譯器206在生成用編程語言的第一版本編寫的代碼的代碼生成操作期間消費以兼容該編程語言的第二版本。例如,傳統擴展112E可由編譯器206消費以為邏輯單元404A-404N(之后統稱為和/或通稱為“邏輯單元404”)中的邏輯單元(經擴展的邏輯單元404B)生成可包括與新環境兼容的源代碼邏輯單元的可執行代碼。應理解,對于擴展(傳統)112E相關聯的術語“傳統”的使用在該配置中意味著該擴展時是與庫、源代碼元素、環境等的傳統版本相關聯的活動(或當前使用的)擴展。
現在轉向圖5,提供了根據說明性實施例的擴展軟件開發工具106的各方 面。應該理解,不一定按任何特定次序來呈現此處公開的方法的操作,并且用替換次序來執行部分或全部操作是可能的且可構想的。為了易于描述和說明,按所示次序來呈現各操作。可以添加、省略和/或同時執行操作,而不脫離所附權利要求書的范圍。
還應當理解,所示方法可在任何時間結束且不必完整地執行。所述方法的部分或全部操作和/或基本上等價的操作可以通過執行計算機存儲介質上所包括的計算機可讀指令來執行,如本文所定義的。如在說明書和權利要求書中使用的術語“計算機可讀指令”及其變型,在本文是用來廣泛地包括例程、應用、應用模塊、程序模塊、程序、組件、數據結構、算法等等。計算機可讀指令可以在各種系統配置上實現,包括單處理器或多處理器系統、小型計算機、大型計算機、個人計算機、手持式計算設備、基于微處理器的可編程消費電子產品、其組合等等。
因此,應該理解,本文所述的邏輯操作被實現為:(1)一系列計算機實現的動作或運行于計算系統上的程序模塊;和/或(2)計算系統內的互連的機器邏輯電路或電路模塊。該實現是取決于計算系統的性能及其他要求的選擇問題。因此,此處描述的邏輯操作被不同地稱為狀態、操作、結構設備、動作或模塊。這些操作、結構設備、動作和模塊可以用軟件、固件、專用數字邏輯及其任何組合來實現。各方法的操作在下文中被描述為至少部分地由軟件開發工具106、編譯器206、內部庫214、外部庫216、擴展112或其組合來實現。
轉向圖5,方法500開始于操作502并繼續至操作504,其中軟件開發工具106接收執行開發操作的指令。如上所述,開發操作可包括但不限于預處理、詞法分析、句法分析、靜態分析、指令調度、代碼生成、鏈接、匯編、解釋、模擬和JIT編譯。
從操作504,方法500繼續至操作506,其中軟件開發工具106確定擴展112是否對該操作可用。如上所述,該操作可以是對各種源代碼元素或邏輯單元的各種操作。例如,軟件開發工具106可接收為特定邏輯單元生成代碼的指令。軟件開發工具106可確定擴展是否可用于為該特定邏輯單元生成代碼。如果沒有擴展對該操作可用,則方法500繼續至操作508,其中軟件開發工具106以默認(或非擴展)模式執行該操作。方法500然后在操作510處結束。
如果擴展對該操作可用,則方法500繼續至操作512,其中軟件開發工具106訪問擴展112。軟件開發工具106可以從各種源訪問擴展112。例如,擴展112可以是包括由第三方開發者提供的可執行代碼的單獨文件。在另一示例中,擴展112可被加載到軟件開發工具106的內部庫214中。
從操作512,方法500繼續至操作514,其中軟件開發工具106消費擴展112。通過消費擴展,軟件開發工具106被配置成以擴展模式而非默認或任何其他模式執行操作。應理解,擴展112不限于對源代碼元素或邏輯單元執行的操作。例如,擴展112可將軟件開發工具106配置成對外部庫216、內部庫214或軟件開發工具106執行操作。本文描述的概念和技術不限于對輸入文件的操作。
從操作514,方法500繼續至操作516,其中軟件開發工具106以擴展模式執行操作。軟件開發工具(經擴展的)106A的擴展模式可取決于軟件開發工具106的特定配置來啟動或撤消。例如,開發者可能想要軟件開發工具106在一個時間段期間以默認模式操作,而在其他時間段中以擴展模式(106A)操作。例如,系統可能在程序調用特定應用編程接口時經歷安全問題。擴展112可以變得可供軟件開發工具106消費以使得軟件開發工具106生成導致該程序調用安全API的代碼。當安全問題結束時,軟件開發工具106可被重新編程為生成必要的代碼。由此,在該示例中,擴展112是臨時的。方法500然后在操作510處結束。
圖6示出了能夠執行本文描述的軟件組件以提供本文描述的概念和技術的設備的說明性計算機體系結構600。由此,圖6所示的計算機體系結構600示出服務器計算機、移動電話、PDA、智能電話、臺式計算機、上網本計算機、平板計算機、和/或膝上型計算機的體系結構。計算機體系結構600可用于執行本文所呈現的軟件組件的任何方面。
圖6所示的計算機體系結構600包括中央處理單元602(“CPU”)、包括隨機存取存儲器606(“RAM”)和只讀存儲器(“ROM”)608在內的系統存儲器604、以及將存儲器604耦合至CPU602的系統總線610。基本輸入/輸出系統被存儲在ROM608中,該系統包含幫助諸如在啟動期間在計算機體系結構600中的元件之間傳輸信息的基本例程。計算機體系結構600還包括用于存儲 來自圖1的操作系統104以及一個或多個應用程序或文件的大容量存儲設備612,這些應用程序或文件包括但不限于軟件開發工具106、內部庫214和擴展112。
大容量存儲設備612通過連接至總線610的大容量存儲控制器(未示出)連接至CPU602。大容量存儲設備612及其相關聯的計算機可讀介質為計算機體系結構600提供非易失性存儲。雖然對此處包含的計算機可讀介質的描述引用了諸如硬盤或CD-ROM驅動器之類的大容量存儲設備,但是本領域的技術人員應該明白,計算機可讀介質可以是可由計算機體系結構600訪問的任何可用計算機存儲介質或通信介質。
通信介質包括諸如載波或其它傳輸機制等已調制數據信號中的計算機可讀指令、數據結構、程序模塊或其它數據,且包含任何傳遞介質。術語“已調制數據信號”指的是其一個或多個特征以在信號中編碼信息的方式被更改或設定的信號。作為示例而非限制,通信介質包括諸如有線網絡或直接線連接之類的有線介質,以及諸如聲學、RF、紅外及其他無線介質之類的無線介質。上述的任意組合也應包括在計算機可讀介質的范圍之內。
作為示例而非限制,計算機存儲介質可包括以用于存儲諸如計算機可讀指令、數據結構、程序模塊或其它數據等信息的任何方法或技術實現的易失性和非易失性、可移動和不可移動介質。例如,計算機介質包括但不限于,RAM、ROM、EPROM、EEPROM、閃存或其他固態存儲器技術、CD-ROM、數字多功能盤(“DVD”)、HD-DVD、藍光(BLU-RAY)或其他光學存儲、磁帶盒、磁帶、磁盤存儲或其他磁性存儲設備、或能用于存儲所需信息且可以由計算機體系結構600訪問的任何其他介質。為了聲明的目的,短語“計算機存儲介質”及其變型不包括波或信號本身和/或通信介質。
根據各實施例,計算機體系結構600可以使用通過諸如網絡620之類的網絡到遠程計算機的邏輯連接來在聯網環境中操作。計算機體系結構600可以通過連接至總線610的網絡接口單元616來連接到網絡620。應當理解,網絡接口單元616還可以被用來連接到其他類型的網絡和遠程計算機系統。計算機體系結構600也可包括輸入/輸出控制器618,該輸入/輸出控制器618用于接收和處理來自多個其他設備(包括鍵盤、鼠標或電子指示筆)的輸入。類似地, 輸入/輸出控制器618可以向顯示屏、打印機或其他類型的輸出設備提供輸出。
應當理解,本文所描述的軟件組件在被加載到CPU602中并被執行時可以將CPU602和總體計算機體系結構600從通用計算系統變換成為被定制為促進本文提出的功能的專用計算系統。CPU602可以用任意數量的晶體管或其他分立的電路元件(它們可以分別地或共同地呈現任意數量的狀態)構建。更具體而言,CPU602可以響應于包含在本文所公開的軟件模塊中的可執行指令而作為有限狀態機來操作。這些計算機可執行指令可以通過指定CPU602如何在各狀態之間轉換來變換CPU602,由此變換了構成CPU602的晶體管或其它分立硬件元件。
對本文所提出的軟件模塊的編碼也可變換本文所提出的計算機可讀介質的物理結構。在本說明書的不同實現中,物理結構的具體變換可取決于各種因素。這樣的因素的示例可以包括,但不僅限于:用于實現計算機可讀介質的技術、計算機可讀介質被表征為主存儲器還是輔存儲器等等。例如,如果計算機可讀介質被實現為基于半導體的存儲器,則本文所公開的軟件可以通過變換半導體存儲器的物理狀態而在計算機可讀介質上編碼。例如,軟件可以變換構成半導體存儲器的晶體管、電容器或其它分立電路元件的狀態。軟件還可變換這些組件的物理狀態以在其上存儲數據。
作為另一示例,本文所公開的計算機可讀介質可以使用磁或光技術來實現。在這些實現中,本文所提出的軟件可以在磁或光介質中編碼了軟件時變換所述磁或光介質的物理狀態。這些變換可以包括改變給定磁性介質內的特定位置的磁性。這些變換還可以包括改變給定光學介質內的特定位置的物理特征或特性,以改變這些位置的光學特性。在沒有偏離本說明書的范圍和精神的情況下,物理介質的其他變換也是可以的,前面提供的示例只是為了便于此描述。
鑒于以上內容,應當理解,在計算機體系結構600中發生許多類型的物理變換以便存儲并執行本文所提出的軟件組件。還應當理解,計算機體系結構600可以包括其它類型的計算設備,包括:手持式計算機、嵌入式計算機系統、個人數字助理、以及本領域技術人員已知的其它類型的計算設備。還可以構想的是,計算機體系結構600可以不包括圖6所示的全部組件,可以包括未在圖6中明確示出的其它組件,或者可利用以某種方式不同于圖6所示的體系結構。
基于上述內容,應當意識到,本文已經公開了用于擴展軟件開發工具的行為的概念和技術。雖然用計算機結構特征、方法和變換動作、特定計算機器、以及計算機可讀介質專用的語言描述了本文中所描述的主題,但是應當理解,所附權利要求書中所定義的本發明不必限于本文中所描述的具體特征、動作、或介質。相反,這些具體特征、動作以及介質是作為實現權利要求的示例形式而公開的。
以上所述的主題僅作為說明提供,并且不應被解釋為限制。可對本文中所描述的主題作出各種修改和改變,而不必遵循示出和描述的示例實施例和應用且不背離所附權利要求書中所闡述的本發明的真正精神和范圍。

關 鍵 詞:
擴展 開發 環境
  專利查詢網所有資源均是用戶自行上傳分享,僅供網友學習交流,未經上傳用戶書面授權,請勿作他用。
關于本文
本文標題:擴展開發環境.pdf
鏈接地址:http://www.rgyfuv.icu/p-6409614.html
關于我們 - 網站聲明 - 網站地圖 - 資源地圖 - 友情鏈接 - 網站客服客服 - 聯系我們

[email protected] 2017-2018 zhuanlichaxun.net網站版權所有
經營許可證編號:粵ICP備17046363號-1 
 


收起
展開
山东11选5中奖结果走势图