title: [A crash course in WebAssembly] 創做並使用 WebAssembly 模塊javascript
date: 2018-3-22 16:42:00前端
categories: 翻譯java
tags: WebAssemblyweb
source: 原文地址npm
auther: Lin Clark編程
這是WebAssembly系列文章的第四部分。若是您尚未閱讀其餘文章,咱們建議您從頭開始。後端
WebAssembly是一種在網頁上運行JavaScript之外的編程語言的方法。在過去,當您想在瀏覽器中運行代碼以與網頁的不一樣部分進行交互時,您惟一的選擇就是JavaScript。api
因此當人們談論WebAssembly變得愈來愈快時,比較的對象一般是在說JavaScript。可是,這並不意味着它是一種或者兩種狀況————您使用的是WebAssembly,或者使用JavaScript。數組
事實上,咱們預計開發人員將在同一個應用程序中同時使用WebAssembly和JavaScript。即便你本身不寫WebAssembly,你也能夠利用它。瀏覽器
WebAssembly定義的模塊函數可讓JavaScript調用,將來有一天你能夠像從npm上下載loadsh之類的各類模塊同樣下載WebAssembly模塊並調用它們的api。
so,讓咱們來看看如何建立一個WebAssembly模塊而後在JS中使用。
在上一篇講assembly的文章中,咱們談到了編譯器接受講高級編程語言並將其翻譯爲機器碼
那麼WebAssembly因該出如今這張圖的什麼位置呢?
你可能會以爲只是增長了一個目標彙編語言(與x86/ARM相似)。除了不針對某個架構外,基本上是對的。
當您經過Web在用戶計算機上傳遞代碼時,您不知道代碼將在哪一個目標架構上運行。
因此WebAssembly與其餘類型的assembly有點不一樣。它是概念機器的機器語言,而不是實際的物理機器。
所以,WebAssembly指令有時被稱爲虛擬指令。它們比JavaScript代碼更直接地映射到機器碼。它們表明了能夠在普通流行硬件中高效完成的一種交集。但它們不是直接映射到特定硬件的特定機器碼。
瀏覽器下載WebAssembly。而後,它可以快速將WebAssembly翻譯爲目標機器的彙編代碼。
目前對WebAssembly支持最多的編譯器工具鏈稱爲LLVM。有許多不一樣的前端和後端能夠插入到LLVM中。
note:大多數WebAssembly模塊開發人員將使用C和Rust等語言進行編碼,而後編譯爲WebAssembly,但還有其餘方法能夠建立WebAssembly模塊。例如,有一個實驗工具能夠幫助您使用TypeScript構建WebAssembly模塊,或者您能夠直接編寫WebAssembly的文本表示形式。
假設咱們想從C到WebAssembly。咱們可使用clang
前端從C到LLVM中間表示。一旦它在LLVM的IR中,LLVM就會理解它,因此LLVM能夠執行一些優化。
要從LLVM的IR(中間表示)轉到WebAssembly,咱們須要一個後端。目前LLVM項目正在進行中。這個後端重要的部分,會盡快完善。目前來講使用WebAssembly可能會很是棘手。
還有另外一個名爲Emscripten的工具,目前使用起來更容易一些。它有本身的後端,能夠經過編譯到另外一個目標(稱爲asm.js),而後將其轉換爲WebAssembly來生成WebAssembly。不過,它使用LLVM,所以你能夠在Emscripten的兩個後端之間切換。
!()[https://hacks.mozilla.org/files/2017/02/04-03-toolchain07-768x631.png]
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模塊的內存模擬了您在這些語言的堆(heap)。
爲此,它使用JavaScript中稱爲ArrayBuffer的東西。數組緩衝區是一個字節數組。數組的索引則做爲內存地址。
若是你想在JavaScript和WebAssembly之間傳遞一個字符串,你能夠將這些字符轉換爲它們的字符代碼。而後你就能夠將它寫入數組緩衝區(memory array)。因爲索引是整數,因此能夠將索引傳遞給WebAssembly函數。所以,字符串的第一個字符的索引能夠用做指針。
任何開發WebAssembly模塊以供Web開發人員使用的人均可能會爲該模塊建立一個包裝。做爲模塊的使用者,您就可以不須要了解內存管理。
若是您想了解更多信息,請查看關於使用WebAssembly內存的文檔。
若是您使用更高級別的語言編寫代碼,而後將其編譯爲WebAssembly,則無需知道WebAssembly模塊的結構。但它能夠幫助你理解基礎。
若是你尚未準備好,咱們建議閱讀關於裝配的文章(本系列的第3部分)。
這是一個咱們將翻譯爲WebAssembly的C函數:
int add42(int num) {
return num + 42;
}
複製代碼
您能夠嘗試使用WASM資源管理器來編譯此功能。
若是你打開.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
複製代碼
這是「二進制(binary)」表示的模塊。我將引號放在二進制文件中,由於它一般以十六進制表示法顯示,但能夠很容易地轉換爲二進制符號或可讀格式。
例如,這裏是num + 42
的樣子:
以防萬一您想知道,那麼這些會說明它們作了什麼
您可能已經注意到,add
操做沒有說明其值應來自哪裏。這是由於WebAssembly就像一個堆棧機器。這意味着在執行操做以前,操做所需的全部值都在堆棧中排隊。
像add
操做知道他們須要多少個值。因爲add須要兩個,所以它會從堆棧的頂部取兩個值。這意味着add指令能夠很短(一個字節),由於指令不須要指定源寄存器或目標寄存器。這會減少.wasm文件的大小,從而減小它的下載時間。
儘管WebAssembly是根據堆棧計算機指定的,但這並非它在物理機器上的工做方式。當瀏覽器將WebAssembly轉換爲正在運行瀏覽器的機器的機器碼時,它將使用寄存器。因爲WebAssembly代碼不指定寄存器,所以可使瀏覽器更靈活地爲該機器分配寄存器,達到最佳的分配方案。
除了add42
函數自己,.wasm文件中還有其餘部分。這些被稱爲sections。某些sections對於任何模塊都是必需的,有些是可選的。
必須的:
類型Type 包含此模塊中定義的函數的函數簽名和任何導入的函數。
函數Function 爲此模塊中定義的每一個函數提供索引。
代碼Code 此模塊中每一個函數的實際函數體。
可選的:
Export 使函數,內存,表和全局變量對其餘WebAssembly模塊和JavaScript可用。這容許單獨編譯的模塊動態連接在一塊兒。這是Web組件版的.dll。
Import 指定要從其餘WebAssembly模塊或JavaScript導入的函數,內存,表(tables)和全局變量。
Start 加載WebAssembly模塊時會自動運行的函數(基本上相似於main函數)。
Global 爲模塊聲明全局變量。
Memory 定義此模塊將使用的內存。
Table 使映射到WebAssembly模塊以外的值成爲可能,例如JavaScript對象。 這對容許間接函數調用特別有用。
Data 初始化導入的或本地的內存。
Element 初始化導入的或本地的表。
有關章節的更多信息,請參閱sections的工做原理。
如今您已經知道如何使用WebAssembly模塊,讓咱們來看看爲何WebAssembly速度很快。