這是有關WebAssembly的系列文章的第四部分,若是您尚未閱讀其餘文章,咱們建議從頭開始。html
WebAssembly是一種在網頁上運行JavaScript之外的編程語言的方法。過去,當您想在瀏覽器中運行代碼以與網頁的不一樣部分進行交互時,惟一的選擇就是JavaScript。前端
所以,當人們談論WebAssembly的速度很快時,Apple與Apple的比較就是JavaScript。但這並不意味着這是兩種狀況,要麼您正在使用WebAssembly,要麼您正在使用JavaScript。webpack
實際上,咱們指望開發人員將在同一應用程序中同時使用WebAssembly和JavaScript。即便您本身不編寫WebAssembly,也能夠利用它。git
WebAssembly模塊定義可從JavaScript使用的功能。所以,就像您今天從npm下載lodash之類的模塊並調用其API中的函數同樣,未來您將可以下載WebAssembly模塊。github
所以,讓咱們看看如何建立WebAssembly模塊,而後如何從JavaScript中使用它們。web
在有關彙編的文章中,我談到了編譯器如何採用高級編程語言並將其轉換爲機器代碼。npm
WebAssembly在哪裏適合這張圖片?編程
您可能會認爲這只是目標彙編語言中的另外一種。這是真的,只是每種語言(x86,ARM)對應於特定的計算機體系結構。後端
當您經過網絡交付要在用戶計算機上執行的代碼時,您不知道代碼將在您的目標體系結構上運行。數組
所以,WebAssembly與其餘類型的程序集有所不一樣。它是概念性機器的機器語言,而不是實際的物理機器。
所以,WebAssembly指令有時稱爲虛擬指令。與JavaScript源代碼相比,它們對機器代碼的映射要直接得多。它們表明了能夠在常見的流行硬件上有效完成的工做的一種交集。可是它們並非直接映射到一種特定硬件的特定機器代碼。
瀏覽器下載WebAssembly。而後,它可使從WebAssembly到目標計算機的彙編代碼的距離很短。
當前對WebAssembly的支持最多的編譯器工具鏈稱爲LLVM。LLVM能夠插入許多不一樣的前端和後端。
注意:大多數WebAssembly模塊開發人員將使用C和Rust等語言進行編碼,而後編譯爲WebAssembly,可是還有其餘方法能夠建立WebAssembly模塊。例如,有一個實驗性工具能夠幫助您使用TypeScript構建WebAssembly模塊,或者您能夠直接在WebAssembly的文本表示形式中進行編碼。
假設咱們想從C到WebAssembly。咱們可使用clang前端從C到LLVM中間表示。一旦進入LLVM的IR,LLVM就會理解它,所以LLVM能夠執行一些優化。
爲了從LLVM的IR(中間表示)過渡到WebAssembly,咱們須要一個後端。LLVM項目中正在進行中的一項。後端是其中的大部分方式,應儘快完成。可是,要使其今天開始工做可能會很棘手。
還有另外一個名爲Emscripten的工具,此工具如今更易於使用。它有本身的後端,能夠經過編譯到另外一個目標(稱爲asm.js)並將其轉換爲WebAssembly來生成WebAssembly。可是,它在後臺使用LLVM,所以您能夠在Emscripten的兩個後端之間切換。
Emscripten包括許多其餘工具和庫,可用於移植整個C / C ++代碼庫,所以,它比編譯器更像是軟件開發人員工具包(SDK)。例如,系統開發人員習慣於擁有能夠讀取和寫入的文件系統,所以Emscripten可使用IndexedDB模擬文件系統。
不管使用哪一種工具鏈,最終結果都是以.wasm結尾的文件。我將在下面解釋有關.wasm文件結構的更多信息。首先,讓咱們看看如何在JS中使用它。
.wasm文件是WebAssembly模塊,能夠將其加載到JavaScript中。到目前爲止,加載過程有點複雜。
function fetchAndInstantiate(url, importObject) { return fetch(url).then(response => response.arrayBuffer() ).then(bytes => WebAssembly.instantiate(bytes, importObject) ).then(results => results.instance ); }
您能夠在咱們的文檔中更深刻地瞭解這一點。
咱們正在努力簡化此過程。咱們但願對工具鏈進行改進,並與現有的模塊捆綁器(如webpack)或加載器(如SystemJS)集成。咱們相信,加載WebAssembly模塊就像加載JavaScript模塊同樣容易。
可是,WebAssembly模塊和JS模塊之間存在主要區別。當前,WebAssembly中的函數只能將數字(整數或浮點數)用做參數或返回值。
對於任何更復雜的數據類型(例如字符串),您必須使用WebAssembly模塊的內存。
若是您主要使用JavaScript,那麼直接訪問內存就不那麼熟悉了。C,C ++和Rust等性能更高的語言每每具備手動內存管理功能。WebAssembly模塊的內存模擬了您會在這些語言中找到的堆。
爲此,它使用JavaScript中的ArrayBuffer。數組緩衝區是字節數組。所以,數組的索引用做內存地址。
若是要在JavaScript和WebAssembly之間傳遞字符串,請將字符轉換爲等效的字符代碼。而後將其寫入內存陣列。因爲索引是整數,所以能夠將索引傳遞給WebAssembly函數。所以,字符串的第一個字符的索引能夠用做指針。
可能正在開發要由Web開發人員使用的WebAssembly模塊的任何人都將圍繞該模塊建立包裝器。這樣,您做爲模塊的使用者就無需瞭解內存管理。
若是您想了解更多信息,請查閱有關使用WebAssembly的內存的文檔。
若是您要使用高級語言編寫代碼,而後將其編譯爲WebAssembly,則無需瞭解WebAssembly模塊的結構。但這有助於瞭解基本知識。
若是您尚未的話,咱們建議您閱讀有關彙編的文章(該系列的第3部分)。
這是一個C函數,咱們將其轉換爲WebAssembly:
int add42(int num) { return num + 42; }
您能夠嘗試使用WASM Explorer來編譯此功能。
若是打開.wasm文件(而且您的編輯器支持顯示它),您將看到相似這樣的內容。
00 61 73 6D 0D 00 00 00 01 86 80 80 80 00 01 60 01 7F 01 7F 03 82 80 80 80 00 01 00 04 84 80 80 80 00 01 70 00 00 05 83 80 80 80 00 01 00 01 06 81 80 80 80 00 00 07 96 80 80 80 00 02 06 6D 65 6D 6F 72 79 02 00 09 5F 5A 35 61 64 64 34 32 69 00 00 0A 8D 80 80 80 00 01 87 80 80 80 00 00 20 00 41 2A 6A 0B
這就是其「二進制」表示形式的模塊。我用雙引號引發來,由於它一般以十六進制表示法顯示,可是能夠很容易地轉換爲二進制表示法或人類可讀的格式。
例如,這是什麼num + 42
樣子。
若是您想知道,如下是這些說明的操做。
您可能已經注意到,該add
操做沒有說明其值應從何而來。這是由於WebAssembly是稱爲堆棧機的示例。這意味着在執行操做以前,操做須要的全部值都在堆棧中排隊。
像這樣的操做項add
知道他們須要多少個值。因爲add
須要兩個值,所以它將從堆棧頂部獲取兩個值。這意味着該add
指令能夠很短(一個字節),由於該指令不須要指定源或目標寄存器。這樣能夠減少.wasm文件的大小,這意味着下載時間更少。
即便WebAssembly是根據堆棧計算機指定的,但這也不是它在物理計算機上的工做方式。當瀏覽器將WebAssembly轉換爲運行瀏覽器的機器的機器代碼時,它將使用寄存器。因爲WebAssembly代碼未指定寄存器,所以它爲瀏覽器提供了更大的靈活性,能夠爲該計算機使用最佳的寄存器分配。
除了add42
函數自己,.wasm文件中還有其餘部分。這些稱爲段。對於任何模塊,某些段都是必需的,而某些則是可選的。
須要:
可選的:
既然您知道如何使用WebAssembly模塊,那麼讓咱們看看爲何WebAssembly很快。
轉自:https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/