Linux中國

如何在 WebAssembly 中寫 「Hello World」?

WebAssembly 是一種位元組碼格式,幾乎所有的瀏覽器 都可以將它編譯成其宿主操作系統的機器代碼。除了 JavaScript 和 WebGL 之外,WebAssembly 還滿足了將應用移植到瀏覽器中以實現平台獨立的需求。作為 C++ 和 Rust 的編譯目標,WebAssembly 使 Web 瀏覽器能夠以接近原生的速度執行代碼。

當談論 WebAssembly 應用時,你必須區分三種狀態:

  1. 源碼(如 C++ 或 Rust): 你有一個用兼容語言編寫的應用,你想把它在瀏覽器中執行。
  2. WebAssembly 位元組碼: 你選擇 WebAssembly 位元組碼作為編譯目標。最後,你得到一個 .wasm 文件。
  3. 機器碼(opcode): 瀏覽器載入 .wasm 文件,並將其編譯成主機系統的相應機器碼。

WebAssembly 還有一種文本格式,用人類可讀的文本表示二進位格式。為了簡單起見,我將其稱為 WASM-text。WASM-text 可以比作高級彙編語言。當然,你不會基於 WASM-text 來編寫一個完整的應用,但了解它的底層工作原理是很好的(特別是對於調試和性能優化)。

本文將指導你在 WASM-text 中創建經典的 「Hello World」 程序。

創建 .wat 文件

WASM-text 文件通常以 .wat 結尾。第一步創建一個名為 helloworld.wat 的空文本文件,用你最喜歡的文本編輯器打開它,然後粘貼進去:

(module
    ;; 從 JavaScript 命名空間導入
    (import  "console"  "log" (func  $log (param  i32  i32))) ;; 導入 log 函數
    (import  "js"  "mem" (memory  1)) ;; 導入 1 頁 內存(64kb)

    ;; 我們的模塊的數據段
    (data (i32.const 0) "Hello World from WebAssembly!")

    ;; 函數聲明:導出 helloWorld(),無參數
    (func (export  "helloWorld")
        i32.const 0  ;; 傳遞偏移 0 到 log
        i32.const 29  ;; 傳遞長度 29 到 log(示例文本的字元串長度)
        call  $log
        )
)

WASM-text 格式是基於 S 表達式的。為了實現交互,JavaScript 函數用 import 語句導入,WebAssembly 函數用 export 語句導出。在這個例子中,從 console 模塊中導入 log 函數,它需要兩個類型為 i32 的參數作為輸入,以及一頁內存(64KB)來存儲字元串。

字元串將被寫入偏移量 為 0 的數據段。數據段是你的內存的 疊加投影 overlay ,內存是在 JavaScript 部分分配的。

函數用關鍵字 func 標記。當進入函數時,棧是空的。在調用另一個函數之前,函數參數會被壓入棧中(這裡是偏移量和長度)(見 call $log)。當一個函數返回一個 f32 類型時(例如),當離開函數時,一個 f32 變數必須保留在棧中(但在本例中不是這樣)。

創建 .wasm 文件

WASM-text 和 WebAssembly 位元組碼是 1:1 對應的,這意味著你可以將 WASM-text 轉換成位元組碼(反之亦然)。你已經有了 WASM-text,現在將創建位元組碼。

轉換可以通過 WebAssembly Binary Toolkit(WABT)來完成。從該鏈接克隆倉庫,並按照安裝說明進行安裝。

建立工具鏈後,打開控制台並輸入以下內容,將 WASM-text 轉換為位元組碼:

wat2wasm helloworld.wat -o helloworld.wasm

你也可以用以下方法將位元組碼轉換為 WASM-text:

wasm2wat helloworld.wasm -o helloworld_reverse.wat

一個從 .wasm 文件創建的 .wat 文件不包括任何函數或參數名稱。默認情況下,WebAssembly 用它們的索引來識別函數和參數。

編譯 .wasm 文件

目前,WebAssembly 只與 JavaScript 共存,所以你必須編寫一個簡短的腳本來載入和編譯 .wasm 文件並進行函數調用。你還需要在 WebAssembly 模塊中定義你要導入的函數。

創建一個空的文本文件,並將其命名為 helloworld.html,然後打開你喜歡的文本編輯器並粘貼進去:

<!DOCTYPE  html>
<html>
  <head>
    <meta  charset="utf-8">
    <title>Simple template</title>
  </head>
  <body>
    <script>

      var memory = new  WebAssembly.Memory({initial:1});

      function  consoleLogString(offset, length) {
        var  bytes = new  Uint8Array(memory.buffer, offset, length);
        var  string = new  TextDecoder(&apos;utf8&apos;).decode(bytes);
        console.log(string);
      };

      var  importObject = {
        console: {
          log:  consoleLogString
        },
        js : {
          mem:  memory
        }
      };

      WebAssembly.instantiateStreaming(fetch(&apos;helloworld.wasm&apos;), importObject)
      .then(obj  => {
        obj.instance.exports.helloWorld();
      });

    </script>
  </body>
</html>

WebAssembly.Memory(...) 方法返回一個大小為 64KB 的內存頁。函數 consoleLogString 根據長度和偏移量從該內存頁讀取一個字元串。這兩個對象作為 importObject 的一部分傳遞給你的 WebAssembly 模塊。

在你運行這個例子之前,你可能必須允許 Firefox 從這個目錄中訪問文件,在地址欄輸入 about:config,並將 privacy.file_unique_origin 設置為 true

![Firefox setting](/data/attachment/album/202103/30/095912pamnmaayg1y1nzla.png "Firefox setting")

注意: 這樣做會使你容易受到 CVE-2019-11730 安全問題的影響。

現在,在 Firefox 中打開 helloworld.html,按下 Ctrl+K 打開開發者控制台。

![Debugger output](/data/attachment/album/202103/30/095912ji88ttpuu4grsd4t.png "Debugger output")

了解更多

這個 Hello World 的例子只是 MDN 的 了解 WebAssembly 文本格式 文檔中的教程之一。如果你想了解更多關於 WebAssembly 的知識以及它的工作原理,可以看看這些文檔。

via: https://opensource.com/article/21/3/hello-world-webassembly

作者:Stephan Avenwedde 選題:lujun9972 譯者:geekpi 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國