首頁 >後端開發 >Python教學 >為 WebAssembly 構建

為 WebAssembly 構建

Mary-Kate Olsen
Mary-Kate Olsen原創
2024-12-02 01:26:09313瀏覽

Building for WebAssembly

我目前正在為 Memphis(我的 Rust 中的 Python 解釋器)探索兩個有趣的主題:建立 WebAssembly 和嵌入 CPython。由於本週沒有重大里程碑要報告,我想分享一些正在進行的想法。對我來說,孟菲斯是一個透過實際實驗來擴展我的概念理解的項目 - 希望這篇文章可以為您做同樣的事情,因為我們正在探索我正在探索的一些設計決策。

瀏覽器中的 Python

將 Memphis 編譯為 WebAssembly 目標已經在我腦海中浮現了一段時間,兩個星期六前,我終於嘗試了一下。在杯墊上放了一杯溫熱的滴濾咖啡,我掰響指關節開始了。

WebAssembly 是現代 Web 瀏覽器內的沙盒執行環境,它補充了傳統的 JavaScript 環境。 Wasm 環境更接近本機程式碼,可用於受益於更高效能的 CPU 上下文的任務;想想數字運算或愚蠢的繁忙迴圈。我對它的興趣不是從性能的角度來看,而是因為它完全是可能的。 Rust 的賣點之一(實際上有無數賣點)是它可以針對 Wasm。有人可能會問,怎麼辦?這是可能的,因為 Rust 使用 LLVM 作為其編譯器後端。 Rust 編譯器前端會產生 LLVM 中間表示 (IR) 程式碼,LLVM 可以將其編譯為數十個目標的本機程式碼。

這是一個相當巨大的好處,我很好奇它是否只適用於孟菲斯。我之前對在瀏覽器中運行 Python 的想法幾乎為零,所以這似乎是測試 Wasm 學習曲線的絕佳機會。

設定 wasm-pack 並建置 WebAssembly

我啟動了人工智慧助理並詢問啟動順序。發出嘟嘟嘟嘟嘟的聲音。以下是我一路上學到的註解的步驟。

# wasm-pack helps compile our Rust code to WebAssembly and bundle it
# with JavaScript bindings we can call from our HTML/JavaScript page.
cargo install wasm-pack

# wasm-pack also downloads the wasm32-unknown-unknown target via
# rustup for us. If for whatever reason it does not, you can use this: 
# rustup target add wasm32-unknown-unknown
# We must specify a feature flag because our wasm_bindgen interface is
# behind the wasm feature flag.
wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm

我第一次嘗試就建造成功了!然而,因為我們沒有將 Rust 二進位檔案中的任何函數標記為可從 WebAssembly 調用,所以它沒有做太多事情。

我們可以安裝 wasm-bindgen 箱來執行此操作,我將其放在功能標誌後面。我將其添加到我的 Cargo.toml 中。

[dependencies]
wasm-bindgen = { version = "0.2", optional = true }

[features]
wasm = ["wasm-bindgen"]

這是我加入 src/lib.rs 檔案中的一小段程式碼,位於 wasm 功能標誌後面。 greet 函數以 #[wasm_bindgen] 修飾,使該符號在 JavaScript 中可用。

# wasm-pack helps compile our Rust code to WebAssembly and bundle it
# with JavaScript bindings we can call from our HTML/JavaScript page.
cargo install wasm-pack

# wasm-pack also downloads the wasm32-unknown-unknown target via
# rustup for us. If for whatever reason it does not, you can use this: 
# rustup target add wasm32-unknown-unknown
# We must specify a feature flag because our wasm_bindgen interface is
# behind the wasm feature flag.
wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm

建立 JavaScript 介面

我還向我的人工智慧助理詢問了我可以用來測試我的 Wasm 介面的盡可能小的 JavaScript 片段。當我們呼叫 init() 時,瀏覽器會載入 .wasm 文件,執行 JIT 編譯步驟以將可移植的 WebAssembly 二進位檔案轉換為本機程式碼,並初始化 WebAssembly 執行時期的記憶體。

[dependencies]
wasm-bindgen = { version = "0.2", optional = true }

[features]
wasm = ["wasm-bindgen"]

就像奇蹟中的奇蹟一樣,它確實有效。誠然,我沒有在瀏覽器中運行任何 Python 程式碼,但與我的二進位檔案互動是一個巨大的步驟,年輕人不能低估它的價值。

下一步是給它一個用 JavaScript 定義的 Python 表達式,並讓 Wasm 二進位處理數字。正如我在 REPL 文章中提到的,軟體專案中的每個入口點都是改進我的抽象的機會,這裡肯定會再次出現這種情況。當我翻閱我的 Memphis 儲存庫時,我意識到哇,我真的應該有一個更好的介面來傳遞字串並將其作為 Python 進行評估。 就像我說的,我喜歡新的入口點。

目前,我會使用我的交叉檢查適配器。 Crosscheck 是我正在進行的測試框架,用於驗證 Treewalk 解釋器和字節碼 VM 對於給定的 Python 輸入產生相同的行為。它以空服員所做的事情命名。

這是我更新的 Rust 程式碼。

#[cfg(feature = "wasm")]
mod wasm {
    use wasm_bindgen::prelude::wasm_bindgen;

    // Export a function to JavaScript
    #[wasm_bindgen]
    pub fn greet() -> String {
        "Hello from WebAssembly!".to_string()
    }
}

這是我更新的 JavaScript 程式碼,它呼叫新的 Rust 評估函數。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Wasm Test</title>
</head>
<body>
    <script type="module">
        import init, { greet } from './pkg/memphis.js';

        async function run() {
            await init();
            console.log(greet());
        }

        run();
    </script>
</body>
</html>

偵錯 WebAssembly 錯誤

現在,當我運行它時,我得到了…控制台錯誤。它因未實現的錯誤而崩潰。

我查了一下,並不清楚是什麼原因造成的。您可以單擊原始程式碼,但對於 Wasm 建置來說,它只是一個彙編區塊,沒有引用原始 Rust 函數。

我進行了一些人工智慧聊天/谷歌搜索,發現了兩種有用的方法。一種是用於 Wasm 建置的 console_log,它在瀏覽器控制台中顯示來自 Rust 程式碼的日誌語句。這對一些人有幫助,但我真正想要的是堆疊追蹤。輸入console_error_panic_hook。它立即給了我 Rust 堆疊跟踪,即 CLUTCH。如果您正在建立自己的 Wasm,請立即停止閱讀並添加此箱子。我甚至不介意你永遠讀不完這篇文章。費里斯希望你使用這個箱子?以下是我將其添加到我的 Wasm 介面的方法。

#[cfg(feature = "wasm")]
mod wasm {
    use wasm_bindgen::prelude::wasm_bindgen;

    use crosscheck::{InterpreterTest, TreewalkAdapter};

    // Export a function to JavaScript
    #[wasm_bindgen]
    pub fn greet() -> String {
        "Hello from WebAssembly!".to_string()
    }

    #[wasm_bindgen]
    pub fn evaluate(code: String) -> String {
        let result = TreewalkAdapter.execute(&code);
        format!("{}", result)
    }
}

我的堆疊追蹤指出了罪魁禍首:我使用 std::env 來請求一些作業系統資源,這在 Wasm 運行時(即沙盒部分)中是不允許的。我將這些呼叫放在功能標誌後面(它們與我如何隨意確定 Python 標準庫在主機上的位置有關)並再次啟動我的建置。在與正確顯示我的返回類型相關的一些小失敗之後......

它成功了。這是我現在在瀏覽器控制台中看到的內容。

# wasm-pack helps compile our Rust code to WebAssembly and bundle it
# with JavaScript bindings we can call from our HTML/JavaScript page.
cargo install wasm-pack

# wasm-pack also downloads the wasm32-unknown-unknown target via
# rustup for us. If for whatever reason it does not, you can use this: 
# rustup target add wasm32-unknown-unknown
# We must specify a feature flag because our wasm_bindgen interface is
# behind the wasm feature flag.
wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm

tldr 我可以在瀏覽器中執行Python。 (值得讚揚的是,RustPython 也這樣做了:https://rustpython.github.io/demo/。我沒有深入研究他們的項目,但它似乎很全面。)Python 列表理解是在JavaScript 中以字符串形式定義的,回應清單由編譯為Wasm 的Rust 程式碼評估,並轉換回可由JavaScript 顯示的字串。

此設定目前僅支援表達式。為了評估語句(並稍後讀回其結果),我需要在 Rust 端保留狀態。我也夢想建立一個 JavaScript REPL。這聽起來像是未來的我的一個問題(而且是一個無聊的夢)。

結局

我已經聊得夠久了,所以我打算推遲到下週一再討論嵌入式 Python。

對誘餌和開關表示歉意。內容日曆不等人。

需要明確的是,透過嵌入式 Python,我的意思是在孟菲斯內部嵌入 CPython 解釋器,而不是在「嵌入式系統」環境中運行 Python。那會毫無理由地困難。與孟菲斯不同,孟菲斯很難好玩。


如果您想將更多類似的貼文直接發送到您的收件匣,您可以在這裡訂閱!

別處

除了指導軟體工程師之外,我還寫了我作為成人診斷自閉症患者的經歷。更少的程式碼和相同數量的笑話。

  • 為什麼我渴望得到認同? - 來自 Scratch 點組織

以上是為 WebAssembly 構建的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn