WebAssembly 基礎以及結合其他程式語言

SRIGT發表於2024-10-13

0x00 WebAssembly 基礎

詳情參考《WebAssembly | MDN》

(1)概述

  • WebAssembly 簡稱 WASM 或 WA,是一種新的編碼方式,可以在現代的 Web 瀏覽器中執行
  • 可以透過編譯器,把多種程式語言(如 C/C++、C#、Go、Python、Rust、TypeScript 等)編寫的程式碼轉化為 WA,並在瀏覽器中使用
  • 特點:
    • 靈活度高:是一種低階的類組合語言
    • 體積較小:具有緊湊的二進位制格式
    • 效能提升:接近原生的效能執行
  • WA 可以與 JavaScript 共存,允許兩者一起工作
  • WA 關鍵概念:
    • 模組:表示一個已經被瀏覽器編譯為可執行機器碼的 WA 二進位制程式碼
    • 記憶體:一個可變長的 ArrayBuffer
    • 表格:一個可變長的型別化陣列
    • 例項:一個模組及其在執行時使用的所有狀態(包括記憶體、表格和一系列匯入值)
  • 使用 WA 編寫的相關應用:Figma

(2)載入與執行

  1. 通常,編譯器將其他語言的程式碼編譯成 .wasm 檔案,以便在 WA 環境中使用

  2. 在瀏覽器環境中,可以透過 AJAX 匯入外部檔案,如匯入 .wasm 檔案

    fetch("main.wasm");
    
  3. JavaScript 中的 WebAssembly 物件是所有 WA 相關功能的名稱空間,其中 WebAssembly.compile / WebAssembly.instantiateWebAssembly.compileStreaming / WebAssembly.instantiateStreaming 方法組合可以用於載入和執行 WA 程式碼

    fetch("main.wasm")
      .then((response) => response.arrayBuffer())
      .then((bytes) => WebAssembly.compile(bytes))
      .then((module) => {
        const instance = new WebAssembly.Instance(module);
        console.log(instance.exports);
      });
    

    WebAssembly.instantiateStreaming(fetch("main.wasm")).then(
      (results) => {
        const instance = results.instance;
        console.log(instance.exports);
      },
    );
    

(3)相關 JavaScript API

  • WebAssembly:所有 WA 相關功能的名稱空間

a. 物件

  • WebAssembly.Module:包含已經由瀏覽器編譯的無狀態 WebAssembly 程式碼
  • WebAssembly.Global:一個全域性變數例項,可以被 JavaScript 和 importable/exportable 訪問
  • WebAssembly.Instance:有狀態,是 WebAssembly.Module 的一個可執行例項
  • WebAssembly.Table:代表 WA 表格概念的 JavaScript 包裝物件,具有類陣列結構,儲存了多個函式引用
  • WebAssembly.Tag:定義了一種 WA 異常的型別,該異常可以從 WA 程式碼丟擲或丟擲
  • WebAssembly.Exception:表示從 WA 丟擲到 JavaScript 的執行時異常,或者從 JavaScript 丟擲到 WA 異常處理程式的執行時異常
  • WebAssembly.LinkError:表示在模組例項化期間發生錯誤

b. 方法

  • WebAssembly.Memory():用於建立一個新的 Memory 記憶體物件
  • WebAssembly.CompileError():建立一個新的 WA 編譯錯誤物件
  • WebAssembly.RuntimeError():建立一個新的 WA 執行時錯誤物件

0x01 結合 C/C++

  1. 使用 C 或 C++ 編寫一段程式碼(以 C 為例)

    // filename: main.c
    #include<stdio.h>
    
    int main(){
      printf("Hello, WebAssembly!");
      return 0;
    }
    

    執行測試無誤後繼續

  2. 下載並安裝用於編譯 C/C++ 到 WA 的 Emscripten

    詳細操作參考官方下載與安裝文件:https://emscripten.org/docs/getting_started/downloads.html

  3. 使用命令 emcc main.c -s WASM=1 -o main.html 編譯

    • emcc:Emscripten 提供的工具
    • main.c:基於 C 語言的程式碼
    • -s WASM=1:指定輸出 WA
    • -o main.html:輸出 main.wasm、main.js 和 main.html 檔案,按需使用

0x02 結合 C#

  1. 使用 C# 編寫一段程式碼

    // filename: main.cs
    public class Example
    {
      public static void Main()
      {
        System.Console.WriteLine("Hello, WebAssembly!");
      }
    }
    
  2. 安裝 .NET Core SDK、mono

  3. 使用命令 mcs --out:main.dll -t:library main.cs 將 C# 程式碼編譯為 DLL

  4. 使用命令 mono --runtime=mono --aot=llvm main.dll 將 DLL 編譯為 WA

0x03 結合 Go

  1. 使用 Go 編寫一段程式碼:

    // filename: main.go
    package main
    
    import "fmt"
    
    func main() {
      fmt.Println("Hello, WebAssembly!")
    }
    
  2. 使用命令 GOOS=js GOARCH=wasm go build -o main.wasm main.go 透過 GOCC 將 main.go 編譯為 main.wasm

0x04 結合 Python

  • 可以透過 py2wasm 工具將 Python 編譯為 WA,或使用 pyodide 直接在 JavaScript 中執行 Python

a. py2wasm

  1. 使用 Python 編寫一段程式碼:

    # filename: main.py
    if __name__ == '__main__':
        print("Hello, WebAssembly!")
    
  2. 使用命令 pip install py2wasm 安裝 py2wasm 工具

  3. 使用命令 py2wasm main.py -o main.wasm 將 main.py 編譯為 main.wasm

b. pyodide

在 HTML 頭中匯入 pyodide.js 並編寫 Python 程式碼

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/pyodide/v0.26.0/full/pyodide.js"></script>
  </head>

  <body>
    <script>
      async function main() {
        let pyodide = await loadPyodide();
        await pyodide.loadPackage("numpy"); // 載入一個 Python 庫
        let result = await pyodide.runPythonAsync(`
                import numpy as np
                np.sum([1, 2, 3, 4])
            `);
        console.log(result);
      }
      main();
    </script>
  </body>
</html>

如果在 NodeJS 環境中,可以使用命令 npm install pyodide 匯入

0x05 結合 Rust

參考自《將 Rust 程式碼編譯為 WASM | 部落格園-_zhiqiu》

  1. 使用命令 cargo add wasm-bindgen 新增依賴項

  2. 使用命令 rustup target add wasm32-unknown-unknown 安裝目標

  3. 使用 Rust 編寫一段程式碼:

    // filename: main.rs
    use wasm_bindgen::prelude::*;
    
    // 使用 #[wasm_bindgen] 宏來匯出函式到 JavaScript
    #[wasm_bindgen]
    pub fn greet(name: &str) -> String {
      format!("Hello, {}!", name)
    }
    
  4. 使用命令 cargo build --target wasm32-unknown-unknown --release 將 main.rs 編譯為 main.wasm 等檔案

  5. 使用命令 wasm-bindgen --out-dir ./out --target web target/wasm32-unknown-unknown/release/lib_wasm.wasm 生成 JavaScript 繫結檔案,並設定輸出目錄為 ./out

0x06 結合 TypeScript

  • AssemblyScript 簡稱 AS,可以將 TypeScript 的嚴格變體編譯為 WA
  • 具體操作方法參考 AS 官方文件

-End-

相關文章