Skip to content

Make rustpython-vm compatible with non-js wasm32-unknown & add tests #4211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 --manifest-path wasm/wasm-unknown-test/Cargo.toml --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: |
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

[workspace.package]
Expand Down
4 changes: 2 additions & 2 deletions stdlib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ license.workspace = true

[features]
default = ["compiler"]
compiler = ["rustpython-vm/compiler"]
threading = ["rustpython-common/threading", "rustpython-vm/threading"]
zlib = ["libz-sys", "flate2/zlib"]
bz2 = ["bzip2"]
ssl = ["openssl", "openssl-sys", "foreign-types-shared"]
ssl-vendor = ["ssl", "openssl/vendored", "openssl-probe"]
compiler = ["rustpython-vm/compiler"]

[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 }
Expand Down
25 changes: 15 additions & 10 deletions stdlib/src/dis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@ mod decl {
let co = if let Ok(co) = obj.get_attr("__code__", vm) {
// Method or function:
PyRef::try_from_object(vm, co)?
} else if let Ok(_co_str) = PyStrRef::try_from_object(vm, obj.clone()) {
} else if let Ok(co_str) = PyStrRef::try_from_object(vm, obj.clone()) {
#[cfg(not(feature = "compiler"))]
return Err(vm.new_runtime_error(
"dis.dis() with str argument requires `compiler` feature".to_owned(),
));
{
let _ = co_str;
return Err(vm.new_runtime_error(
"dis.dis() with str argument requires `compiler` feature".to_owned(),
));
}
#[cfg(feature = "compiler")]
vm.compile(
_co_str.as_str(),
crate::vm::compiler::Mode::Exec,
"<dis>".to_owned(),
)
.map_err(|err| vm.new_syntax_error(&err, Some(_co_str.as_str())))?
{
vm.compile(
co_str.as_str(),
crate::vm::compiler::Mode::Exec,
"<dis>".to_owned(),
)
.map_err(|err| vm.new_syntax_error(&err, Some(co_str.as_str())))?
}
} else {
PyRef::try_from_object(vm, obj)?
};
Expand Down
10 changes: 6 additions & 4 deletions vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repository.workspace = true
license.workspace = true

[features]
default = ["compiler"]
default = ["compiler", "js"]
importlib = []
encodings = ["importlib"]
vm-tracing-logging = []
Expand All @@ -24,6 +24,8 @@ codegen = ["rustpython-codegen", "ast"]
parser = ["rustpython-parser", "ast"]
serde = ["dep:serde"]

js = ["getrandom/js", "chrono/wasmbind", "wasm-bindgen"]

[dependencies]
rustpython-compiler = { workspace = true, optional = true }
rustpython-codegen = { workspace = true, optional = true }
Expand Down Expand Up @@ -71,7 +73,7 @@ thread_local = { workspace = true }
memchr = { workspace = true }

caseless = "0.2.1"
getrandom = { version = "0.2.12", features = ["js"] }
getrandom = { version = "0.2.12" }
flamer = { version = "0.4", optional = true }
half = "1.8.2"
memoffset = "0.9.1"
Expand Down Expand Up @@ -140,8 +142,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 }
Expand Down
7 changes: 6 additions & 1 deletion vm/src/stdlib/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ mod decl {
Ok(duration_since_system_now(vm)?.as_secs_f64())
}

#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
#[cfg(all(target_arch = "wasm32", feature = "js", not(target_os = "wasi")))]
fn _time(_vm: &VirtualMachine) -> PyResult<f64> {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
Expand All @@ -115,6 +115,11 @@ mod decl {
Ok(Date::now() / 1000.0)
}

#[cfg(all(target_arch = "wasm32", not(feature = "js"), not(target_os = "wasi")))]
fn _time(vm: &VirtualMachine) -> PyResult<f64> {
Err(vm.new_not_implemented_error("time.time".to_owned()))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will need a way to implement time, I have the APIs in my environment, but I will need a way to override this

}

#[pyfunction]
fn monotonic(vm: &VirtualMachine) -> PyResult<f64> {
Ok(get_monotonic_time(vm)?.as_secs_f64())
Expand Down
7 changes: 6 additions & 1 deletion vm/src/vm/vm_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl VirtualMachine {
self.flush_std();
panic!("{msg}")
}
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
#[cfg(all(target_arch = "wasm32", feature = "js", not(target_os = "wasi")))]
{
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
Expand All @@ -32,6 +32,11 @@ impl VirtualMachine {
error(&s);
panic!("{}; exception backtrace above", msg)
}
#[cfg(all(target_arch = "wasm32", not(feature = "js"), not(target_os = "wasi")))]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be useful to still see information from the exception? Can't you do something like:

let err_string: String = exc.to_pyobject(vm).repr(vm).unwrap().to_string();

Or does the msg have everything we need?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be useful

{
let _ = exc;
panic!("{}; python exception not available", msg)
}
}

pub(crate) fn flush_std(&self) {
Expand Down
2 changes: 1 addition & 1 deletion wasm/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ rustpython-common = { workspace = true }
rustpython-pylib = { workspace = true, optional = true }
rustpython-stdlib = { workspace = true, default-features = false, optional = true }
# make sure no threading! otherwise wasm build will fail
rustpython-vm = { workspace = true, features = ["compiler", "encodings", "serde"] }
rustpython-vm = { workspace = true, features = ["compiler", "encodings", "serde", "js"] }

rustpython-parser = { workspace = true }

Expand Down
13 changes: 13 additions & 0 deletions wasm/wasm-unknown-test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "wasm-unknown-test"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
getrandom = { version = "0.2.12", features = ["custom"] }
rustpython-vm = { path = "../../vm", default-features = false, features = ["compiler"] }

[workspace]
1 change: 1 addition & 0 deletions wasm/wasm-unknown-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A test crate to ensure that `rustpython-vm` compiles on `wasm32-unknown-unknown` without a JS host.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

16 changes: 16 additions & 0 deletions wasm/wasm-unknown-test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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(), "<string>").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);
Loading