我目前正在為 Memphis(我的 Rust 中的 Python 解釋器)探索兩個有趣的主題:建立 WebAssembly 和嵌入 CPython。由於本週沒有重大里程碑要報告,我想分享一些正在進行的想法。對我來說,孟菲斯是一個透過實際實驗來擴展我的概念理解的項目 - 希望這篇文章可以為您做同樣的事情,因為我們正在探索我正在探索的一些設計決策。
將 Memphis 編譯為 WebAssembly 目標已經在我腦海中浮現了一段時間,兩個星期六前,我終於嘗試了一下。在杯墊上放了一杯溫熱的滴濾咖啡,我掰響指關節開始了。
WebAssembly 是現代 Web 瀏覽器內的沙盒執行環境,它補充了傳統的 JavaScript 環境。 Wasm 環境更接近本機程式碼,可用於受益於更高效能的 CPU 上下文的任務;想想數字運算或愚蠢的繁忙迴圈。我對它的興趣不是從性能的角度來看,而是因為它完全是可能的。 Rust 的賣點之一(實際上有無數賣點)是它可以針對 Wasm。有人可能會問,怎麼辦?這是可能的,因為 Rust 使用 LLVM 作為其編譯器後端。 Rust 編譯器前端會產生 LLVM 中間表示 (IR) 程式碼,LLVM 可以將其編譯為數十個目標的本機程式碼。
這是一個相當巨大的好處,我很好奇它是否只適用於孟菲斯。我之前對在瀏覽器中運行 Python 的想法幾乎為零,所以這似乎是測試 Wasm 學習曲線的絕佳機會。
我啟動了人工智慧助理並詢問啟動順序。發出嘟嘟嘟嘟嘟的聲音。以下是我一路上學到的註解的步驟。
# 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
我還向我的人工智慧助理詢問了我可以用來測試我的 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>
現在,當我運行它時,我得到了…控制台錯誤。它因未實現的錯誤而崩潰。
我查了一下,並不清楚是什麼原因造成的。您可以單擊原始程式碼,但對於 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。那會毫無理由地困難。與孟菲斯不同,孟菲斯很難好玩。
如果您想將更多類似的貼文直接發送到您的收件匣,您可以在這裡訂閱!
除了指導軟體工程師之外,我還寫了我作為成人診斷自閉症患者的經歷。更少的程式碼和相同數量的笑話。
以上是為 WebAssembly 構建的詳細內容。更多資訊請關注PHP中文網其他相關文章!