diff --git a/compiler/wasm-pack/Cargo.toml b/compiler/wasm-pack/Cargo.toml new file mode 100644 index 000000000..8b27f0c79 --- /dev/null +++ b/compiler/wasm-pack/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "hello_world" +version = "0.1.0" +authors = ["The wasm-bindgen Developers"] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = "^0.2" +web-sys = "^0.3" +js-sys = "^0.3" +wasm-bindgen-futures = "^0.4" +yew = "0.17" +seed = "0.8.0" + +[package.metadata.wasm-pack.profile.release] +wasm-opt = false \ No newline at end of file diff --git a/compiler/wasm-pack/Dockerfile b/compiler/wasm-pack/Dockerfile new file mode 100644 index 000000000..ae2dd8a55 --- /dev/null +++ b/compiler/wasm-pack/Dockerfile @@ -0,0 +1,17 @@ +# syntax = docker/dockerfile:experimental + +# fetch dependencies to local +FROM shepmaster/rust-nightly as sources +RUN cargo install wasm-pack +ADD --chown=playground src/lib.rs /playground/src/lib.rs +# TODO support top 100 crates +ADD --chown=playground Cargo.toml /playground/Cargo.toml +RUN cargo fetch + +# build dependencies +FROM sources +RUN wasm-pack build --target web --out-name package --dev +RUN rm src/*.rs + +ADD --chown=playground cargo-pack /playground/.cargo/bin/ +ENTRYPOINT ["/playground/tools/entrypoint.sh"] diff --git a/compiler/wasm-pack/cargo-pack b/compiler/wasm-pack/cargo-pack new file mode 100755 index 000000000..64f14c25e --- /dev/null +++ b/compiler/wasm-pack/cargo-pack @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -eu + +# Rewrite our arguments to be `cargo build` instead of `cargo wasm`; +# this assumes that the command will always be `cargo wasm ...`. We +# capture the output directory in order to place the result file. +shift # Ignore "wasm" +args=() +while (( "$#" )); do + if [[ "$1" == "--" ]] ; then + : # Ignore + elif [[ "$1" == "-o" ]] ; then + shift + output="$1" + else + args+="$1" + fi + + shift +done +# Greatly inspired from https://gitlab.com/strwrite/seed-playground +# --dev flag disables the wasm-opt for optimization downloaded from networks +wasm-pack build --target web --out-name package --dev + +cat pkg/package_bg.wasm | base64 > "${output}.wasm" +cat pkg/package.js | base64 > "${output}.js" + diff --git a/compiler/wasm-pack/src/lib.rs b/compiler/wasm-pack/src/lib.rs new file mode 100644 index 000000000..8f6dcb2f2 --- /dev/null +++ b/compiler/wasm-pack/src/lib.rs @@ -0,0 +1,19 @@ +use wasm_bindgen::prelude::*; + +// Called by our JS entry point to run the example +#[wasm_bindgen(start)] +pub fn run() -> Result<(), JsValue> { + // Use `web_sys`'s global `window` function to get a handle on the global + // window object. + let window = web_sys::window().expect("no global `window` exists"); + let document = window.document().expect("should have a document on window"); + let body = document.body().expect("document should have a body"); + + // Manufacture the element we're gonna append + let val = document.create_element("p")?; + val.set_text_content(Some("Hello from Rust!")); + + body.append_child(&val)?; + + Ok(()) +} \ No newline at end of file diff --git a/ui/Cargo.lock b/ui/Cargo.lock index 4b8225a0e..c5c75ff2b 100644 --- a/ui/Cargo.lock +++ b/ui/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "aho-corasick" version = "0.7.15" diff --git a/ui/src/main.rs b/ui/src/main.rs index 2421bb8f2..a337087b0 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -85,6 +85,7 @@ fn main() { mount.mount("/meta/version/miri", meta_version_miri); mount.mount("/meta/gist", gist_router); mount.mount("/evaluate.json", evaluate); + mount.mount("/wasm-pack", wasm_pack); let mut chain = Chain::new(mount); let file_logger = FileLogger::new(logfile).expect("Unable to create file logger"); @@ -281,6 +282,16 @@ fn evaluate(req: &mut Request<'_, '_>) -> IronResult { }) } +fn wasm_pack(req: &mut Request<'_, '_>) -> IronResult{ + with_sandbox(req, |sandbox, req: WasmPackRequest| { + let req = req.try_into()?; + sandbox + .wasm_pack(&req) + .map(WasmPackResponse::from) + .context(WasmPack) + }) +} + fn with_sandbox(req: &mut Request<'_, '_>, f: F) -> IronResult where F: FnOnce(Sandbox, Req) -> Result, @@ -488,6 +499,8 @@ pub enum Error { Execution { source: sandbox::Error }, #[snafu(display("Evaluation operation failed: {}", source))] Evaluation { source: sandbox::Error }, + #[snafu(display("wasm-pack operation failed: {}", source))] + WasmPack { source: sandbox::Error }, #[snafu(display("Linting operation failed: {}", source))] Linting { source: sandbox::Error }, #[snafu(display("Expansion operation failed: {}", source))] @@ -691,6 +704,20 @@ struct EvaluateResponse { error: Option, } +#[derive(Debug, Clone, Deserialize)] +struct WasmPackRequest { + code: String +} + +#[derive(Debug, Clone, Serialize)] +struct WasmPackResponse { + success: bool, + wasm_js: String, + wasm_bg: String, + stdout: String, + stderr: String, +} + impl TryFrom for sandbox::CompileRequest { type Error = Error; @@ -927,6 +954,29 @@ impl From for EvaluateResponse { } } +impl TryFrom for sandbox::WasmPackRequest { + type Error = Error; + + fn try_from(me: WasmPackRequest) -> Result { + Ok(sandbox::WasmPackRequest { + code: me.code, + ..sandbox::WasmPackRequest::default() + }) + } +} + +impl From for WasmPackResponse { + fn from(me: sandbox::WasmPackResponse) -> Self { + WasmPackResponse { + success: me.success, + wasm_bg: me.wasm_bg, + wasm_js: me.wasm_js, + stdout: me.stdout, + stderr: me.stderr, + } + } +} + fn parse_target(s: &str) -> Result { Ok(match s { "asm" => sandbox::CompileTarget::Assembly(sandbox::AssemblyFlavor::Att, diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs index 2b529d088..fac6624ae 100644 --- a/ui/src/sandbox.rs +++ b/ui/src/sandbox.rs @@ -194,6 +194,43 @@ impl Sandbox { }) } + // Greatly inspired from https://gitlab.com/strwrite/seed-playground + pub fn wasm_pack(&self, req: &WasmPackRequest) -> Result { + use CompileTarget::*; + use CrateType::*; + + let compile_req = CompileRequest{ + backtrace: false, + channel: Channel::WasmPack, + code: req.code.clone(), + crate_type: Library(LibraryType::Cdylib), + edition: Some(Edition::Rust2018), + mode: Mode::Debug, + target: WasmPack, + tests: false, + }; + let res = self.compile(&compile_req)?; + let js_file = + fs::read_dir(&self.output_dir) + .context(UnableToReadOutput)? + .flat_map(|entry| entry) + .map(|entry| entry.path()) + .find(|path| path.extension() == Some(OsStr::new("js"))); + + let js_code = match js_file { + Some(file) => read(&file)?.unwrap_or_else(String::new), + None => String::new() // TODO: return proper error? + }; + + Ok(WasmPackResponse { + success: res.success, + stdout: res.stdout, + stderr: res.stderr, + wasm_bg: res.code, + wasm_js: js_code + }) + } + pub fn format(&self, req: &FormatRequest) -> Result { self.write_source_code(&req.code)?; let command = self.format_command(req); @@ -419,10 +456,14 @@ impl Sandbox { mount_output_dir.push(":"); mount_output_dir.push("/playground-result"); + // let mut mount_output_wasm = self.scratch.as_path().join("pkg").as_os_str().to_os_string(); + // mount_output_wasm.push(":"); + // mount_output_wasm.push("/playground/pkg"); let mut cmd = basic_secure_docker_command(); cmd .arg("--volume").arg(&mount_input_file) + // .arg("--volume").arg(&mount_output_wasm) .arg("--volume").arg(&mount_output_dir); cmd @@ -469,6 +510,7 @@ fn build_execution_command(target: Option, channel: Channel, mode let mut cmd = vec!["cargo"]; match (target, req.crate_type(), tests) { + (Some(WasmPack), _, _) => cmd.push("pack"), (Some(Wasm), _, _) => cmd.push("wasm"), (Some(_), _, _) => cmd.push("rustc"), (_, _, true) => cmd.push("test"), @@ -511,12 +553,14 @@ fn build_execution_command(target: Option, channel: Channel, mode Mir => cmd.push("--emit=mir"), Hir => cmd.push("-Zunpretty=hir"), Wasm => { /* handled by cargo-wasm wrapper */ }, + WasmPack => { /* handled by cargo-wasmpack wrapper */ }, } - } - + } + log::debug!("{:?}", &cmd); cmd } + fn set_execution_environment(cmd: &mut Command, target: Option, req: impl CrateTypeRequest + EditionRequest + BacktraceRequest) { use self::CompileTarget::*; @@ -623,6 +667,7 @@ pub enum CompileTarget { Mir, Hir, Wasm, + WasmPack, } impl CompileTarget { @@ -633,6 +678,7 @@ impl CompileTarget { CompileTarget::Mir => "mir", CompileTarget::Hir => "hir", CompileTarget::Wasm => "wat", + CompileTarget::WasmPack => "wasm", }; OsStr::new(ext) } @@ -648,6 +694,7 @@ impl fmt::Display for CompileTarget { Mir => "Rust MIR".fmt(f), Hir => "Rust HIR".fmt(f), Wasm => "WebAssembly".fmt(f), + WasmPack => "WasmPack".fmt(f), } } } @@ -657,6 +704,7 @@ pub enum Channel { Stable, Beta, Nightly, + WasmPack, } impl Channel { @@ -667,6 +715,7 @@ impl Channel { Stable => "rust-stable", Beta => "rust-beta", Nightly => "rust-nightly", + WasmPack => "rust-wasm-pack", } } } @@ -924,6 +973,35 @@ pub struct MacroExpansionResponse { pub stderr: String, } +#[derive(Debug, Clone)] +pub struct WasmPackRequest { + pub code: String, + pub crate_type: CrateType, + pub output_name: String, +} + +impl Default for WasmPackRequest { + fn default() -> Self { + WasmPackRequest { + code: String::from(""), + crate_type: CrateType::Library(LibraryType::Rlib), + output_name: "wasm".to_string(), + } + } +} + +impl CrateTypeRequest for WasmPackRequest { + fn crate_type(&self) -> CrateType { self.crate_type } +} + +#[derive(Debug, Clone)] +pub struct WasmPackResponse { + pub wasm_js: String, + pub wasm_bg: String, + pub success: bool, + pub stdout: String, + pub stderr: String, +} #[cfg(test)] mod test { use super::*;