diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bec7eafa32..2d2ec40d4b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -216,13 +216,6 @@ jobs: - name: Check compilation for freebsd run: cargo check --target x86_64-unknown-freebsd - - uses: dtolnay/rust-toolchain@stable - with: - target: wasm32-unknown-unknown - - - name: Check compilation for wasm32 - run: cargo check --target wasm32-unknown-unknown --no-default-features - - uses: dtolnay/rust-toolchain@stable with: target: x86_64-unknown-freebsd @@ -380,6 +373,14 @@ jobs: env: NODE_OPTIONS: "--openssl-legacy-provider" working-directory: ./wasm/demo + - uses: mwilliamson/setup-wabt-action@v1 + with: { wabt-version: "1.0.30" } + - name: check wasm32-unknown without js + run: | + cargo build --release -p wasm-unknown-test --target wasm32-unknown-unknown --verbose + if wasm-objdump -xj Import target/wasm32-unknown-unknown/release/wasm_unknown_test.wasm; then + echo "ERROR: wasm32-unknown module expects imports from the host environment" >2 + fi - name: build notebook demo if: github.ref == 'refs/heads/release' run: | diff --git a/Cargo.lock b/Cargo.lock index 5fcb773d84..b5d99cb2a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3154,6 +3154,14 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-unknown-test" +version = "0.1.0" +dependencies = [ + "getrandom", + "rustpython-vm", +] + [[package]] name = "web-sys" version = "0.3.61" diff --git a/Cargo.toml b/Cargo.toml index eec5d84a8f..741aab8856 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,8 @@ x86_64-pc-windows-msvc = { triplet = "x64-windows-static-md", dev-dependencies = resolver = "2" members = [ "compiler", "compiler/core", "compiler/codegen", - ".", "common", "derive", "jit", "vm", "vm/sre_engine", "pylib", "stdlib", "wasm/lib", "derive-impl", + ".", "common", "derive", "jit", "vm", "vm/sre_engine", "pylib", "stdlib", "derive-impl", + "wasm/lib", "wasm/wasm-unknown-test", ] [workspace.package] diff --git a/stdlib/Cargo.toml b/stdlib/Cargo.toml index 485855d646..1e327c4e9d 100644 --- a/stdlib/Cargo.toml +++ b/stdlib/Cargo.toml @@ -22,7 +22,7 @@ ssl-vendor = ["ssl", "openssl/vendored", "openssl-probe"] [dependencies] # rustpython crates rustpython-derive = { workspace = true } -rustpython-vm = { workspace = true } +rustpython-vm = { workspace = true, default-features = false } rustpython-common = { workspace = true } ahash = { workspace = true } diff --git a/vm/Cargo.toml b/vm/Cargo.toml index cfe177f67d..8088e732e3 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -10,7 +10,7 @@ repository.workspace = true license.workspace = true [features] -default = ["compiler"] +default = ["compiler", "wasmbind"] importlib = [] encodings = ["importlib"] vm-tracing-logging = [] @@ -23,8 +23,7 @@ ast = ["rustpython-ast"] codegen = ["rustpython-codegen", "ast"] parser = ["rustpython-parser", "ast"] serde = ["dep:serde"] -wasmbind = ["chrono/wasmbind", "getrandom/js"] - +wasmbind = ["chrono/wasmbind", "getrandom/js", "wasm-bindgen"] [dependencies] rustpython-compiler = { workspace = true, optional = true } @@ -142,8 +141,8 @@ features = [ "Win32_UI_WindowsAndMessaging", ] -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = "0.2.92" +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] +wasm-bindgen = { version = "0.2.92", optional = true } [build-dependencies] glob = { workspace = true } diff --git a/vm/src/stdlib/time.rs b/vm/src/stdlib/time.rs index 4a9e66066d..8ef67bacec 100644 --- a/vm/src/stdlib/time.rs +++ b/vm/src/stdlib/time.rs @@ -99,8 +99,7 @@ mod decl { #[cfg(not(all( target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) + not(any(target_os = "emscripten", target_os = "wasi")), )))] fn _time(vm: &VirtualMachine) -> PyResult { Ok(duration_since_system_now(vm)?.as_secs_f64()) @@ -123,6 +122,15 @@ mod decl { Ok(Date::now() / 1000.0) } + #[cfg(all( + target_arch = "wasm32", + not(feature = "wasmbind"), + not(any(target_os = "emscripten", target_os = "wasi")) + ))] + fn _time(vm: &VirtualMachine) -> PyResult { + Err(vm.new_not_implemented_error("time.time".to_owned())) + } + #[pyfunction] fn monotonic(vm: &VirtualMachine) -> PyResult { Ok(get_monotonic_time(vm)?.as_secs_f64()) diff --git a/vm/src/vm/vm_object.rs b/vm/src/vm/vm_object.rs index 5b5ef0526f..466db6f3a8 100644 --- a/vm/src/vm/vm_object.rs +++ b/vm/src/vm/vm_object.rs @@ -13,12 +13,20 @@ impl VirtualMachine { #[track_caller] #[cold] fn _py_panic_failed(&self, exc: PyBaseExceptionRef, msg: &str) -> ! { - #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] + #[cfg(not(all( + target_arch = "wasm32", + not(any(target_os = "emscripten", target_os = "wasi")), + )))] { self.print_exception(exc); self.flush_std(); panic!("{msg}") } + #[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")), + ))] #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] { use wasm_bindgen::prelude::*; @@ -32,6 +40,15 @@ impl VirtualMachine { error(&s); panic!("{}; exception backtrace above", msg) } + #[cfg(all( + target_arch = "wasm32", + not(feature = "wasmbind"), + not(any(target_os = "emscripten", target_os = "wasi")), + ))] + { + let _ = exc; + panic!("{}; python exception not available", msg) + } } pub(crate) fn flush_std(&self) { diff --git a/wasm/wasm-unknown-test/Cargo.toml b/wasm/wasm-unknown-test/Cargo.toml new file mode 100644 index 0000000000..b559016923 --- /dev/null +++ b/wasm/wasm-unknown-test/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wasm-unknown-test" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +getrandom = { version = "0.2.7", features = ["custom"] } +rustpython-vm = { path = "../../vm", default-features = false, features = ["compiler"] } diff --git a/wasm/wasm-unknown-test/README.md b/wasm/wasm-unknown-test/README.md new file mode 100644 index 0000000000..3c4abd59cf --- /dev/null +++ b/wasm/wasm-unknown-test/README.md @@ -0,0 +1 @@ +A test crate to ensure that `rustpython-vm` compiles on `wasm32-unknown-unknown` without a JS host. diff --git a/wasm/wasm-unknown-test/src/lib.rs b/wasm/wasm-unknown-test/src/lib.rs new file mode 100644 index 0000000000..fd043aea3a --- /dev/null +++ b/wasm/wasm-unknown-test/src/lib.rs @@ -0,0 +1,16 @@ +use rustpython_vm::{eval, Interpreter}; + +pub unsafe extern "C" fn eval(s: *const u8, l: usize) -> u32 { + let src = std::slice::from_raw_parts(s, l); + let src = std::str::from_utf8(src).unwrap(); + Interpreter::without_stdlib(Default::default()).enter(|vm| { + let res = eval::eval(vm, src, vm.new_scope_with_builtins(), "").unwrap(); + res.try_into_value(vm).unwrap() + }) +} + +fn getrandom_always_fail(_buf: &mut [u8]) -> Result<(), getrandom::Error> { + Err(getrandom::Error::UNSUPPORTED) +} + +getrandom::register_custom_getrandom!(getrandom_always_fail);