From 7495fc68ae676309c40532acbc61cf0f0ed4c2c0 Mon Sep 17 00:00:00 2001 From: Syrus Date: Mon, 7 Oct 2019 12:30:48 -0700 Subject: [PATCH 1/3] Added support for WASI --- Cargo.toml | 1 + src/main.rs | 82 ++++++++++++++++++++++++++++++++++++ vm/src/stdlib/time_module.rs | 4 +- 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8192b42a2..3dbb6db5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ num-traits = "0.2.8" flame = { version = "0.2", optional = true } flamescope = { version = "0.1", optional = true } +[target.'cfg(not(target_os = "wasi"))'.dependencies] rustyline = "=5.0.1" diff --git a/src/main.rs b/src/main.rs index 65adc69e4..cd40e8690 100644 --- a/src/main.rs +++ b/src/main.rs @@ -321,8 +321,12 @@ fn write_profile(matches: &ArgMatches) -> Result<(), Box> } fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> { + #[cfg(not(target_os = "wasi"))] import::init_importlib(&vm, true)?; + #[cfg(target_os = "wasi")] + import::init_importlib(&vm, false)?; + if let Some(paths) = option_env!("BUILDTIME_RUSTPYTHONPATH") { let sys_path = vm.get_attribute(vm.sys_module.clone(), "path")?; for (i, path) in std::env::split_paths(paths).enumerate() { @@ -486,6 +490,7 @@ fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> ShellExecResul } } +#[cfg(not(target_os = "wasi"))] fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { use rustyline::{error::ReadlineError, Editor}; @@ -596,3 +601,80 @@ fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { Ok(()) } + + +#[cfg(target_os = "wasi")] +fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { + use std::io::{self, BufRead}; + use std::io::prelude::*; + + println!( + "Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}", + crate_version!() + ); + + // Read a single line: + let mut input = String::new(); + let mut continuing = false; + + loop { + let prompt_name = if continuing { "ps2" } else { "ps1" }; + let prompt = vm + .get_attribute(vm.sys_module.clone(), prompt_name) + .and_then(|prompt| vm.to_str(&prompt)); + let prompt = match prompt { + Ok(ref s) => s.as_str(), + Err(_) => "", + }; + print!("{}", prompt); + io::stdout().flush().ok().expect("Could not flush stdout"); + + let stdin = io::stdin(); + + let result = match stdin.lock().lines().next().unwrap() { + Ok(line) => { + debug!("You entered {:?}", line); + let stop_continuing = line.is_empty(); + + if input.is_empty() { + input = line; + } else { + input.push_str(&line); + } + input.push_str("\n"); + + if continuing { + if stop_continuing { + continuing = false; + } else { + continue; + } + } + + match shell_exec(vm, &input, scope.clone()) { + ShellExecResult::Ok => { + input.clear(); + Ok(()) + } + ShellExecResult::Continue => { + continuing = true; + Ok(()) + } + ShellExecResult::PyErr(err) => { + input.clear(); + Err(err) + } + } + } + Err(err) => { + eprintln!("Readline error: {:?}", err); + break; + } + }; + + if let Err(exc) = result { + print_exception(vm, &exc); + } + } + Ok(()) +} diff --git a/vm/src/stdlib/time_module.rs b/vm/src/stdlib/time_module.rs index 71063abc2..6522e85b8 100644 --- a/vm/src/stdlib/time_module.rs +++ b/vm/src/stdlib/time_module.rs @@ -56,7 +56,7 @@ fn duration_to_f64(d: Duration) -> f64 { (d.as_secs() as f64) + (f64::from(d.subsec_nanos()) / 1e9) } -#[cfg(not(target_arch = "wasm32"))] +#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))] fn time_time(_vm: &VirtualMachine) -> f64 { match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(v) => duration_to_f64(v), @@ -64,7 +64,7 @@ fn time_time(_vm: &VirtualMachine) -> f64 { } } -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] fn time_time(_vm: &VirtualMachine) -> f64 { use wasm_bindgen::prelude::*; #[wasm_bindgen] From f1ad06d4707fed78dd53c9fb013b87b23d57a402 Mon Sep 17 00:00:00 2001 From: Syrus Date: Mon, 7 Oct 2019 12:46:18 -0700 Subject: [PATCH 2/3] =?UTF-8?q?Added=20wapm=20integration=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ README.md | 21 +++++++++++++++++++++ wapm.toml | 20 ++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 wapm.toml diff --git a/.gitignore b/.gitignore index 8347c5ec5..05270f95d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ tests/snippets/resources flame-graph.html flame.txt flamescope.json +/wapm.lock +/wapm_packages diff --git a/README.md b/README.md index b28ba7133..f0ffa2adb 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,27 @@ Or use the interactive shell: >>>>> 2+2 4 +### WASI + +You can compile RustPython to a standalone WebAssembly WASI module so it can run anywhere. + +```shell +$ wapm install rustpython +$ wapm run rustpython +>>>>> 2+2 +4 +``` + +#### Building the WASI file + +You can build the WebAssembly WASI file with: + +``` +cargo build --release --target wasm32-wasi --features="freeze-stdlib" +``` + +> Note: we use the `freeze-stdlib` to include the standard libarary inside the binary. + ## Disclaimer RustPython is in a development phase and should not be used in production or a diff --git a/wapm.toml b/wapm.toml new file mode 100644 index 000000000..e45248dcd --- /dev/null +++ b/wapm.toml @@ -0,0 +1,20 @@ +[package] +name="rustpython" +version="0.0.1" +description="A Python-3 (CPython >= 3.5.0) Interpreter written in Rust 🐍 😱 🤘" +license-file="LICENSE" +readme = "README.md" +repository = "https://github.com/RustPython/RustPython" + +[[module]] +name="rustpython" +source="target/wasm32-wasi/release/rustpython.wasm" +abi = "wasi" +interfaces = {wasi= "0.0.0-unstable"} + +[[command]] +name="rustpython" +module="rustpython" + +# [fs] +# "Lib"="Lib" From 32123b56d4aec454af795f8660ef20b002625c8e Mon Sep 17 00:00:00 2001 From: Syrus Date: Mon, 7 Oct 2019 23:43:51 -0700 Subject: [PATCH 3/3] Improved PR based on feedback --- src/main.rs | 10 +++------- wapm.toml | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index cd40e8690..78f90f365 100644 --- a/src/main.rs +++ b/src/main.rs @@ -321,11 +321,8 @@ fn write_profile(matches: &ArgMatches) -> Result<(), Box> } fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> { - #[cfg(not(target_os = "wasi"))] - import::init_importlib(&vm, true)?; - - #[cfg(target_os = "wasi")] - import::init_importlib(&vm, false)?; + // We only include the standard library bytecode in WASI + import::init_importlib(&vm, cfg!(not(target_os = "wasi")))?; if let Some(paths) = option_env!("BUILDTIME_RUSTPYTHONPATH") { let sys_path = vm.get_attribute(vm.sys_module.clone(), "path")?; @@ -602,11 +599,10 @@ fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { Ok(()) } - #[cfg(target_os = "wasi")] fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> { - use std::io::{self, BufRead}; use std::io::prelude::*; + use std::io::{self, BufRead}; println!( "Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}", diff --git a/wapm.toml b/wapm.toml index e45248dcd..6c0575ee8 100644 --- a/wapm.toml +++ b/wapm.toml @@ -1,6 +1,6 @@ [package] name="rustpython" -version="0.0.1" +version="0.0.4" description="A Python-3 (CPython >= 3.5.0) Interpreter written in Rust 🐍 😱 🤘" license-file="LICENSE" readme = "README.md"