commit 13b61a6def2fa7dad58129c35dd44868878fb049 Author: Nyeogmi Date: Sun Apr 21 22:10:48 2024 -0700 Basic groundwork for viperid, a fantasy console diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c37e26f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,129 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "editor" +version = "0.1.0" +dependencies = [ + "anyhow", + "player", + "viperid", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "minifb_host" +version = "0.1.0" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "player" +version = "0.1.0" +dependencies = [ + "anyhow", + "viperid", + "wasmi", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "viperid" +version = "0.1.0" +dependencies = [ + "anyhow", +] + +[[package]] +name = "wasmi" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8281d1d660cdf54c76a3efa9ddd0c270cada1383a995db3ccb43d166456c7" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0bec7f9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "viperid" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.82" + +[workspace] +members = [ + "crates/editor", + "crates/minifb_host", + "crates/player" +] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba937f2 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +To launch the editor: + +``` +$ cargo run --manifest-path crates\editor\Cargo.toml +``` + diff --git a/crates/editor/.gitignore b/crates/editor/.gitignore new file mode 100644 index 0000000..9f97022 --- /dev/null +++ b/crates/editor/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/crates/editor/Cargo.lock b/crates/editor/Cargo.lock new file mode 100644 index 0000000..26ccf39 --- /dev/null +++ b/crates/editor/Cargo.lock @@ -0,0 +1,24 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "editor" +version = "0.1.0" +dependencies = [ + "anyhow", + "shared", +] + +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "anyhow", +] diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml new file mode 100644 index 0000000..c10c3ac --- /dev/null +++ b/crates/editor/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "editor" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.82" +player = { path = "../player" } +viperid = { path = "../.." } diff --git a/crates/editor/src/go_builder.rs b/crates/editor/src/go_builder.rs new file mode 100644 index 0000000..da392ae --- /dev/null +++ b/crates/editor/src/go_builder.rs @@ -0,0 +1,80 @@ +use std::{fs::File, io::Read, path::{Path, PathBuf}, process::Command}; + +use viperid::VResult; + +pub struct GoBuilder { + project_directory: PathBuf, + entry_point: PathBuf +} + +impl GoBuilder { + pub fn new(project_directory: &Path) -> VResult { + // make sure Go is a supported version + let output = Command::new("go").arg("version").output()?; + + if !output.status.success() { + anyhow::bail!("Go is not installed or did not start: {:?}", output.status) + } + + // TODO: Support more Go versions + let text = output.stdout; + if !text.starts_with(b"go version go1.21") { + anyhow::bail!("unsupported Go version (must be 1.21): {}", String::from_utf8_lossy(&text).trim()) + } + + // make sure the project is really here + if !project_directory.exists() { + anyhow::bail!("project directory must exist: {}", project_directory.to_string_lossy()) + } + + let entry_point = project_directory.join("main.go"); + if !entry_point.exists() { + anyhow::bail!("entry point must exist: {}", entry_point.to_string_lossy()) + } + + // OK, we should be able to do things with Go without any errors + Ok(GoBuilder { + project_directory: PathBuf::from(project_directory), + entry_point + }) + } + + pub fn build(&self) -> VResult> { + // NOTE: We've checked that the project directory exists + if !self.project_directory.exists() { + anyhow::bail!( + "project directory disappeared: {}", + self.project_directory.to_string_lossy() + ); + } + if !self.entry_point.exists() { + anyhow::bail!( + "entry point disappeared: {}", + self.entry_point.to_string_lossy() + ); + } + + let project_directory = &self.project_directory; + let entry_point = &self.entry_point; + let build_directory = project_directory.join("build"); + let wasm_name = build_directory.join("game.wasm"); + + let mut child = Command::new("go") + .args(["build", "-o"]).arg(&wasm_name) + .env("GOOS", "wasip1") + .env("GOARCH", "wasm") + .arg("-trimpath") + .arg(entry_point) + .spawn()?; + let status = child.wait()?; + if !status.success() { + anyhow::bail!("go failed"); + } + + let mut buf = vec![]; + File::open(wasm_name)?.read_to_end(&mut buf)?; + Ok(buf) + } + + +} \ No newline at end of file diff --git a/crates/editor/src/main.rs b/crates/editor/src/main.rs new file mode 100644 index 0000000..84cbba2 --- /dev/null +++ b/crates/editor/src/main.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; + +use viperid::VResult; + +use crate::go_builder::GoBuilder; + +mod go_builder; + +fn main() -> VResult<()> { + let builder = GoBuilder::new(&PathBuf::from("example_project"))?; + let wasm = builder.build()?; + player::run_entry_point(&wasm)?; + Ok(()) +} diff --git a/crates/minifb_host/Cargo.toml b/crates/minifb_host/Cargo.toml new file mode 100644 index 0000000..b32a67f --- /dev/null +++ b/crates/minifb_host/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "minifb_host" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/crates/minifb_host/src/lib.rs b/crates/minifb_host/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/crates/player/Cargo.toml b/crates/player/Cargo.toml new file mode 100644 index 0000000..9a124cc --- /dev/null +++ b/crates/player/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "player" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.82" +viperid = { path = "../.." } +wasmi = "0.31.2" diff --git a/crates/player/src/executor.rs b/crates/player/src/executor.rs new file mode 100644 index 0000000..051bd3a --- /dev/null +++ b/crates/player/src/executor.rs @@ -0,0 +1,37 @@ +use viperid::VResult; +use wasmi::{Engine, Linker, Module, Store}; + +use crate::wasi::{self, StockWasi}; + +struct ExecutorState { + wasi: wasi::StockWasi +} + +impl ExecutorState { + fn new() -> Self { + ExecutorState { + wasi: StockWasi::new() + } + + } +} + +pub fn run_entry_point(wasm: &[u8]) -> VResult<()> { + let engine = Engine::default(); + + let module = Module::new(&engine, wasm)?; + + let mut store = Store::new(&engine, ExecutorState::new()); + + let mut linker = >::new(&engine); + wasi::integrate(&mut linker, |hs| &mut hs.wasi)?; + + let instance = linker + .instantiate(&mut store, &module)? + .start(&mut store)?; + + let main = instance.get_typed_func::<(), ()>(&store, "_start")?; + main.call(&mut store, ())?; + + Ok(()) +} \ No newline at end of file diff --git a/crates/player/src/lib.rs b/crates/player/src/lib.rs new file mode 100644 index 0000000..2b1f410 --- /dev/null +++ b/crates/player/src/lib.rs @@ -0,0 +1,4 @@ +mod executor; +mod wasi; + +pub use executor::run_entry_point; \ No newline at end of file diff --git a/crates/player/src/wasi/implementation.rs b/crates/player/src/wasi/implementation.rs new file mode 100644 index 0000000..dd0c3cc --- /dev/null +++ b/crates/player/src/wasi/implementation.rs @@ -0,0 +1,346 @@ +use std::time::SystemTime; + +use wasmi::core::Trap; + +use super::MinimalPreview1; + +pub struct StockWasi { + +} +impl StockWasi { + pub fn new() -> Self { + Self { } + } +} + +// https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/wasi/api.h#L118 +const BAD_DESCRIPTOR: i32 = 8; +const FUNCTION_NOT_IMPLEMENTED: i32 = 52; + +// Implemented based on docs here +// https://wasix.org/docs/api-reference/wasi/fd_allocate +// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/wasi_snapshot_preview1.witx +impl MinimalPreview1 for StockWasi { + fn args_get(&mut self, _memory: &mut [u8], _argv: i32, _argv_buf: i32) -> super::MPResult { + Ok(0) + } + + fn args_sizes_get(&mut self, memory: &mut [u8], argc: i32, argv_buf_size: i32) -> super::MPResult { + write_u32(memory, argc, 0); + write_u32(memory, argv_buf_size, 0); + Ok(0) + } + + fn environ_get(&mut self, _memory: &mut [u8], _environ: i32, _environ_buf: i32) -> super::MPResult { + Ok(0) + } + + fn environ_sizes_get(&mut self, memory: &mut [u8], environc: i32, environ_buf_size: i32) -> super::MPResult { + write_u32(memory, environc, 0); + write_u32(memory, environ_buf_size, 0); + Ok(0) + } + + fn clock_res_get(&mut self, memory: &mut [u8], _id: i32, outptr: i32) -> super::MPResult { + write_u32(memory, outptr, 1000); + Ok(0) + } + + fn clock_time_get(&mut self, memory: &mut [u8], id: i32, precision: i64, outptr: i32) -> super::MPResult { + println!("clock_time_get: {} {} {}", id, precision, outptr); + // TODO: Actually fetch time + let duration = + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) + .expect("time must be post-epoch"); + + write_u64(memory, outptr, duration.as_nanos() as u64); + Ok(0) + } + + fn fd_advise(&mut self, _memory: &mut [u8], _fd: i32, _offset: i64, _len: i64, _advice: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_allocate(&mut self, _memory: &mut [u8], _fd: i32, _offset: i64, _len: i64) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_close(&mut self, _memory: &mut [u8], _fd: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_datasync(&mut self, _memory: &mut [u8], _fd: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_fdstat_get(&mut self, _memory: &mut [u8], _fd: i32, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_fdstat_set_flags(&mut self, _memory: &mut [u8], _fd: i32, _flags: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_fdstat_set_rights(&mut self, _memory: &mut [u8], _fd: i32, _fs_rights_base: i64, _fs_rights_inheriting: i64) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_filestat_get(&mut self, _memory: &mut [u8], _fd: i32, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_filestat_set_size(&mut self, _memory: &mut [u8], _fd: i32, _size: i64) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_filestat_set_times(&mut self, _memory: &mut [u8], _fd: i32, _atim: i64, _mtim: i64, _fst_flags: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_pread(&mut self, _memory: &mut [u8], _fd: i32, _iov_buf: i32, _iov_buf_len: i32, _offset: i64, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_prestat_get(&mut self, _memory: &mut [u8], fd: i32, _offset0: i32) -> super::MPResult { + // for some reason TinyGo asks about fd 3 immediately + if fd == 3 { + return Ok(BAD_DESCRIPTOR); + } + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_prestat_dir_name(&mut self, _memory: &mut [u8], _fd: i32, _path: i32, _path_len: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_pwrite(&mut self, _memory: &mut [u8], _fd: i32, _ciov_buf: i32, _ciov_buf_len: i32, _offset: i64, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_read(&mut self, _memory: &mut [u8], _fd: i32, _iov_buf: i32, _iov_buf_len: i32, _offset1: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_readdir(&mut self, _memory: &mut [u8], _fd: i32, _buf: i32, _buf_len: i32, _cookie: i64, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_renumber(&mut self, _memory: &mut [u8], _fd: i32, _to: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_seek(&mut self, _memory: &mut [u8], _fd: i32, _offset: i64, _whence: i32, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_sync(&mut self, _memory: &mut [u8], _fd: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_tell(&mut self, _memory: &mut [u8], _fd: i32, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn fd_write(&mut self, memory: &mut [u8], fd: i32, ciov_buf: i32, ciov_buf_len: i32, n_written_ptr: i32) -> super::MPResult { + let mut out = vec![]; + let mut n_written = 0; + for i in 0..ciov_buf_len { + let iov_ptr = ciov_buf + i * 8; + let ptr = read_u32(memory, iov_ptr); + let len = read_u32(memory, iov_ptr + 4); + n_written += len; + + for b in 0..len { + out.push(memory[ptr as usize + b as usize]); + } + } + + write_u32(memory, n_written_ptr, n_written); + + if fd == 1 { + // stdout + println!("Written to stdout: {}", String::from_utf8_lossy(&out)); + } + else if fd == 2 { + // stderr + println!("Written to stderr: {}", String::from_utf8_lossy(&out)); + } else { + println!("Written to invalid FD {}: {}", fd, String::from_utf8_lossy(&out)); + } + + Ok(0) + } + + fn path_create_directory(&mut self, _memory: &mut [u8], _fd: i32, _offset: i32, _length: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn path_filestat_get(&mut self, _memory: &mut [u8], _fd: i32, _flags: i32, _offset: i32, _length: i32, _offset0: i32) -> super::MPResult { + Ok(FUNCTION_NOT_IMPLEMENTED) + } + + fn path_filestat_set_times( + &mut self, + _memory: &mut [u8], + _fd: i32, + _flags: i32, + _offset: i32, + _length: i32, + _atim: i64, + _mtim: i64, + _fst_flags: i32, + ) -> super::MPResult { + Ok(0) + } + + fn path_link( + &mut self, + _memory: &mut [u8], + _old_fd: i32, + _old_flags: i32, + _old_offset: i32, + _old_length: i32, + _new_fd: i32, + _new_offset: i32, + _new_length: i32, + ) -> super::MPResult { + Ok(0) + } + + fn path_open( + &mut self, + _memory: &mut [u8], + _fd: i32, + _dirflags: i32, + _offset: i32, + _length: i32, + _oflags: i32, + _fs_rights_base: i64, + _fdflags: i64, + _fs_rights_inheriting: i32, + _offset0: i32, + ) -> super::MPResult { + Ok(0) + } + + fn path_readlink( + &mut self, + _memory: &mut [u8], + _fd: i32, + _offset: i32, + _length: i32, + _buf: i32, + _buf_len: i32, + _offset0: i32, + ) -> super::MPResult { + Ok(0) + } + + fn path_remove_directory( + &mut self, + _memory: &mut [u8], + _fd: i32, _offset: i32, _length: i32 + ) -> super::MPResult { + Ok(0) + } + + fn path_rename( + &mut self, + _memory: &mut [u8], + _fd: i32, + _old_offset: i32, + _old_length: i32, + _new_fd: i32, + _new_offset: i32, + _new_length: i32, + ) -> super::MPResult { + Ok(0) + } + + fn path_symlink( + &mut self, + _memory: &mut [u8], + _old_offset: i32, + _old_length: i32, + _fd: i32, + _new_offset: i32, + _new_length: i32, + ) -> super::MPResult { + Ok(0) + } + + fn path_unlink_file(&mut self, _memory: &mut [u8], _fd: i32, _offset: i32, _length: i32) -> super::MPResult { + Ok(0) + } + + fn poll_oneoff(&mut self, _memory: &mut [u8], _in_: i32, _out: i32, _nsubscriptions: i32, _offset0: i32) -> super::MPResult { + Ok(0) + } + + fn proc_exit(&mut self, _memory: &mut [u8], rval: i32) -> super::MPResult<()> { + Err(Trap::i32_exit(rval)) + } + + fn proc_raise(&mut self, _memory: &mut [u8], _sig: i32) -> super::MPResult { + // TODO: Actually implement + Ok(0) + } + + fn sched_yield(&mut self, _memory: &mut [u8]) -> super::MPResult { + Ok(0) + } + + fn random_get(&mut self, _memory: &mut [u8], _buf: i32, _buf_len: i32) -> super::MPResult { + // TODO: Actually implement + Ok(0) + } + + fn sock_accept(&mut self, _memory: &mut [u8], _fd: i32, _flags: i32, _offset0: i32) -> super::MPResult { + Ok(0) + } + + fn sock_recv( + &mut self, + _memory: &mut [u8], + _fd: i32, + _iov_buf: i32, + _iov_buf_len: i32, + _ri_flags: i32, + _offset0: i32, + _offset1: i32, + ) -> super::MPResult { + Ok(0) + } + + fn sock_send( + &mut self, + _memory: &mut [u8], + _fd: i32, _ciov_buf: i32, _ciov_buf_len: i32, _si_flags: i32, _offset0: i32) -> super::MPResult { + Ok(0) + } + + fn sock_shutdown( + &mut self, + _memory: &mut [u8], + _fd: i32, _how: i32) -> super::MPResult { + Ok(0) + } +} + +fn read_u32(memory: &[u8], ptr: i32) -> u32 { + let ptr = ptr as usize; + let slice = &memory[ptr..ptr + 4]; + let mut ary = [0; 4]; + ary.copy_from_slice(slice); + return u32::from_le_bytes(ary) +} + +fn write_u32(memory: &mut [u8], ptr: i32, value: u32) { + let ptr = ptr as usize; + (&mut memory[ptr..ptr + 4]).copy_from_slice(&value.to_le_bytes()); +} + +fn write_u64(memory: &mut [u8], ptr: i32, value: u64) { + let ptr = ptr as usize; + (&mut memory[ptr..ptr + 8]).copy_from_slice(&value.to_le_bytes()); +} \ No newline at end of file diff --git a/crates/player/src/wasi/integration.rs b/crates/player/src/wasi/integration.rs new file mode 100644 index 0000000..1e1ab97 --- /dev/null +++ b/crates/player/src/wasi/integration.rs @@ -0,0 +1,699 @@ +// Based closely on +// https://github.com/wasmi-labs/wasmi/blob/master/crates/wasi/src/sync/snapshots/preview_1.rs + +use wasmi::{Caller, Extern, Linker}; + + +// Creates the function item `add_wasi_snapshot_preview1_to_wasmi_linker` which when called adds all +// `wasi preview_1` functions to the linker +macro_rules! impl_add_to_linker_for_funcs { + ( + $( + $( #[$docs:meta] )* + fn $fname:ident ($( $arg:ident : $typ:ty ),* $(,)? ) -> $ret:tt + );+ $(;)? + ) => { + /// Adds the entire `WASI API` to the [`Linker`]. + /// + /// Once linked, users can make use of all the low-level functionalities that `WASI` provides. + /// + /// You could call them `syscall`s and you'd be correct, because they mirror + /// what a non-os-dependent set of syscalls would look like. + /// You now have access to resources such as files, directories, random number generators, + /// and certain parts of the networking stack. + /// + /// # Note + /// + /// `WASI` is versioned in snapshots. It's still a WIP. Currently, this crate supports `preview_1` + /// Look [here](https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md) for more details. + pub fn integrate( + linker: &mut Linker, + wasi_ctx: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static, + ) -> Result<(), wasmi::Error> + where U: crate::wasi::MinimalPreview1 + { + $( + // $(#[$docs])* // TODO: find place for docs + linker.func_wrap( + "wasi_snapshot_preview1", + stringify!($fname), + move |mut caller: Caller<'_, T>, $($arg : $typ,)*| -> Result<$ret, wasmi::core::Trap> { + let memory = match caller.get_export("memory") { + Some(Extern::Memory(m)) => m, + _ => return Err(wasmi::core::Trap::new(String::from("missing required WASI memory export"))), + }; + let(memory, ctx) = memory.data_and_store_mut(&mut caller); + let ctx = wasi_ctx(ctx); + println!("syscall: {}", stringify!($fname)); + let result = crate::wasi::MinimalPreview1::$fname(ctx, memory, $($arg,)*)?; + Ok(<$ret>::from(result)) + } + )?; + )* + Ok(()) + } + } +} + +impl_add_to_linker_for_funcs!( + /// Read command-line argument data. + /// + /// # Note + /// + /// The size of the array should match that returned by `args_sizes_get`. + /// Each argument is expected to be \0 terminated. + fn args_get(argv: i32, argv_buf: i32) -> i32; + + /// Return command-line argument data sizes. + /// + /// # Note + /// + /// Returns the number of arguments and the size of the argument string data, or an error. + /// Note that `offset0` and `offset1` are offsets into memory where the two results are stored + fn args_sizes_get(offset0: i32, offset1: i32) -> i32; + + /// Read environment variable data. + /// + /// # Note + /// + /// The sizes of the buffers should match that returned by `environ_sizes_get`. + /// Key/value pairs are expected to be joined with =s, and terminated with \0s. + fn environ_get(environ: i32, environ_buf: i32) -> i32; + + /// Returns the number of environment variables. + /// + /// # Note + /// + /// Returns the number of environment variable arguments and the size of the environment variable data. + /// Note that `offset0` and `offset1` are offsets into memory where the two results are stored. + fn environ_sizes_get(offset0: i32, offset1: i32) -> i32; + + /// Return the resolution of a clock. + /// + /// Implementations are required to provide a non-zero value for supported clocks. + /// For unsupported clocks, return `errno::inval`. + /// + /// # Note + /// + /// This is similar to `clock_getres` in POSIX. + /// The `id` is the `ClockID` and `offset0` is the offset into memory where the result is written. + fn clock_res_get(id: i32, offset0: i32) -> i32; + + /// Return the time value of a clock. + /// + /// # Note + /// + /// This is similar to `clock_gettime` in POSIX. The result is stored in `offset0`. + fn clock_time_get(id: i32, precision: i64, offset0: i32) -> i32; + + /// Provide file advisory information on a file descriptor. + /// + /// # Note + /// + /// This is similar to `posix_fadvise` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset`: The offset within the file to which the advisory applies. + /// - `len`: The length of the region to which the advisory applies. + /// - `advice`: The advice. + fn fd_advise(fd: i32, offset: i64, len: i64, advice: i32) -> i32; + + /// Force the allocation of space in a file. + /// + /// # Note + /// + /// This is similar to `posix_fallocate` in `POSIX`. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset`: The offset at which to start the allocation. + /// - `len`: The length of the area that is allocated. + fn fd_allocate(fd: i32, offset: i64, len: i64) -> i32; + + /// Close a file descriptor. + /// + /// # Note + /// + /// This is similar to `close` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor that shall be closed. + fn fd_close(fd: i32) -> i32; + + /// Synchronize the data of a file to disk. + /// + /// # Note + /// + /// This is similar to `fdatasync` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor of the file to be synchronized to disk. + fn fd_datasync(fd: i32) -> i32; + + /// Get the attributes of a file descriptor. + /// + /// # Note + /// + /// This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset0`: The offset into memory where the result is written to. + fn fd_fdstat_get(fd: i32, offset0: i32) -> i32; + + /// Adjust the flags associated with a file descriptor. + /// + /// # Note + /// + /// This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `flags`: The desired values of the file descriptor flags. + fn fd_fdstat_set_flags(fd: i32, flags: i32) -> i32; + + /// Adjust the rights associated with a file descriptor. + /// + /// # Note + /// + /// This can only be used to remove rights, and returns `errno::notcapable` + /// if called in a way that would attempt to add rights. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `fs_rights_base`: The desired rights of the file descriptor. + /// - `fs_rights_inheriting`: The inherited rights. + fn fd_fdstat_set_rights(fd: i32, fs_rights_base: i64, fs_rights_inheriting: i64) -> i32; + + /// Returns the attributes of an open file. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset0`: The offset into memory where the buffer of the file's attributes is written. + fn fd_filestat_get(fd: i32, offset0: i32) -> i32; + + /// Adjust the size of an open file. + /// + /// # Note + /// + /// - If this increases the file's size, the extra bytes are filled with zeros. + /// - This is similar to `ftruncate` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `size`: The desired file size. + fn fd_filestat_set_size(fd: i32, size: i64) -> i32; + + /// Adjust the timestamps of an open file or directory. + /// + /// # Note + /// + /// This is similar to `futimens` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `atim`: The desired values of the data access timestamp. + /// - `mtim`: The desired values of the data modification timestamp. + /// - `fst_flags`: A bitmask indicating which timestamps to adjust. + fn fd_filestat_set_times(fd: i32, atim: i64, mtim: i64, fst_flags: i32) -> i32; + + /// Read from a file descriptor, without using and updating the file descriptor's offset. + /// + /// # Note + /// + /// This is similar to `preadv` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `iov_buf`, `iov_buf_len`: Used to create `iovec`, + /// which is the list of scatter/gather vectors in which to store data. + /// - `offset`: The offset within the file at which to read. + /// - `offsset0`: The size of bytes read is written here. + fn fd_pread(fd: i32, iov_buf: i32, iov_buf_len: i32, offset: i64, offset0: i32) -> i32; + + /// Return a description of the given preopened file descriptor. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset0`: The location in the memory where the buffer that stores the description is written. + fn fd_prestat_get(fd: i32, offset0: i32) -> i32; + + /// Return a description of the given preopened file descriptor. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `path`: A buffer into which to write the preopened directory name. + /// - `path_len`: The length of the `path` buffer. + fn fd_prestat_dir_name(fd: i32, path: i32, path_len: i32) -> i32; + + /// Write to a file descriptor, without using and updating the file descriptor's offset. + /// + /// # Note + /// + /// This is similar to `pwritev` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: file descriptor + /// - `ciov_buf`, `ciov_buf_len`: Used to create `ciovec`, + /// which is the list of scatter/gather vectors from which to retrieve data. + /// - `offset`: The offset within the file at which to write. + /// - `offsset0`: The size of bytes written is written here. + fn fd_pwrite(fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset: i64, offset0: i32) -> i32; + + /// Read from a file descriptor. Note: This is similar to readv in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `iov_buf`, `iov_buf_len`: used to create iovec, which is the list of scatter/gather vectors in which to store data. + /// - `offset`: The offset within the file at which to read. + /// - `offsset0`: size of bytes read is written here + fn fd_read(fd: i32, iov_buf: i32, iov_buf_len: i32, offset1: i32) -> i32; + + /// Read directory entries from a directory. + /// + /// # Note + /// + /// - When successful, the contents of the output buffer consist of a sequence of directory entries. + /// - Each directory entry consists of a `dirent` object, + /// followed by `dirent::d_namlen` bytes holding the name of the directory entry. + /// - This function fills the output buffer as much as possible, + /// potentially truncating the last directory entry. + /// - This allows the caller to grow its read buffer size in case it's too small + /// to fit a single large directory entry, or skip the oversized directory entry. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `buf`: The buffer where directory entries are stored. + /// - `buf_len`: The length of the `buf` buffer. + /// - `cookie`: The location within the directory to start reading. + /// - `offset0`: The result, i.e. the number of bytes stored in the read buffer, is stored at this offset in memory + /// if less than the size of the read buffer, the end of the directory has been reached. + fn fd_readdir(fd: i32, buf: i32, buf_len: i32, cookie: i64, offset0: i32) -> i32; + + /// Atomically replace a file descriptor by renumbering another file descriptor. + /// + /// # Note + /// + /// - Due to the strong focus on thread safety, this environment does not provide a mechanism + /// to duplicate or renumber a file descriptor to an arbitrary number, like `dup2()`. + /// This would be prone to race conditions, as an actual file descriptor with the same number + /// could be allocated by a different thread at the same time. + /// - This function provides a way to atomically renumber file descriptors, + /// which would disappear if `dup2()` were to be removed entirely. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `to`: The file descriptor to overwrite. + fn fd_renumber(fd: i32, to: i32) -> i32; + + /// Move the offset of a file descriptor. + /// + /// # Note + /// + /// This is similar to `lseek` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset`: The number of bytes to move. + /// - `whence`: The base from which the offset is relative + /// - `offset0`: The memory location to which the new offset of the file descriptor, + /// relative to the start of the file is stored. + fn fd_seek(fd: i32, offset: i64, whence: i32, offset0: i32) -> i32; + + /// Synchronize the data and metadata of a file to disk. + /// + /// # Note + /// + /// This is similar to `fsync` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + fn fd_sync(fd: i32) -> i32; + + /// Return the current offset of a file descriptor. + /// + /// # Note + /// + /// This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset0`: Offset into the memory where result is stored upon success. + /// - `result`: The current offset of the file descriptor, relative to the start of the file. + fn fd_tell(fd: i32, offset0: i32) -> i32; + + /// Write to a file descriptor. + /// + /// # Note + /// + /// This is similar to `writev` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `ciov_buf`, `ciov_buf_len`: used to create ciovec, which is the list of + /// scatter/gather vectors from which to retrieve data. + /// - `offset0`: The offset into the memory where result (size written) is stored + fn fd_write(fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset0: i32) -> i32; + + /// Create a directory. + /// + /// # Note + /// + /// This is similar to `mkdirat` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset`, `length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path string at which to create the directory. + fn path_create_directory(fd: i32, offset: i32, length: i32) -> i32; + + /// Return the attributes of a file or directory. + /// + /// # Note + /// + /// This is similar to `stat` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `flags`: Flags determining the method of how the path is resolved. + /// - `offset`, `length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path string of the file or directory to inspect. + /// - `offset0`: The buffer where the file's attributes are stored. + fn path_filestat_get(fd: i32, flags: i32, offset: i32, length: i32, offset0: i32) -> i32; + + /// Adjust the timestamps of a file or directory. + /// + /// # Note + /// + /// This is similar to `utimensat` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `flags`: Flags determining the method of how the path is resolved. + /// - `offset`, `length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path string of the file or directory to operate on. + /// - `atim`: The desired values of the data access timestamp. + /// - `mtim`: The desired values of the data modification timestamp. + /// - `fst_flags`: A bitmask indicating which timestamps to adjust. + fn path_filestat_set_times( + fd: i32, + flags: i32, + offset: i32, + length: i32, + atim: i64, + mtim: i64, + fst_flags: i32, + ) -> i32; + + /// Create a hard link. + /// + /// # Note + /// + /// This is similar to `linkat` in POSIX. + /// + /// # Parameters + /// + /// - `old_fd`: file descriptor + /// - `old_flags`: Flags determining the method of how the path is resolved. + /// - `old_offset`, `old_length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path string source path from which to link. + /// - `new_fd`: The working directory at which the resolution of the new path starts. + /// - `new_offset`, `new_length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path string, i.e. ehe destination path at + /// which to create the hard link. + fn path_link( + old_fd: i32, + old_flags: i32, + old_offset: i32, + old_length: i32, + new_fd: i32, + new_offset: i32, + new_length: i32, + ) -> i32; + + /// Open a file or directory. + /// + /// # Note + /// + /// - The returned file descriptor is not guaranteed to be the lowest-numbered file descriptor not currently open; + /// it is randomized to prevent applications from depending on making assumptions about indexes, + /// since this is error-prone in multi-threaded contexts. + /// - The returned file descriptor is guaranteed to be less than 2^31. + /// - This is similar to `openat` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `dirflags`: Flags determining the method of how the path is resolved. + /// - `offset`, `length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the relative path of the file or directory to open, + /// relative to the `path_open::fd` directory. + /// - `oflags`: The method by which to open the file. + /// - `fs_rights_base`: The initial rights of the newly created file descriptor + /// - `fs_rights_inheriting`: The rights to inherit. + /// - `fdflags`: The file descriptor flags. + /// - `offset0`: The offset into memory where result is stored. + /// The result is the file descriptor of the file that has been opened. + fn path_open( + fd: i32, + dirflags: i32, + offset: i32, + length: i32, + oflags: i32, + fs_rights_base: i64, + fdflags: i64, + fs_rights_inheriting: i32, + offset0: i32, + ) -> i32; + + /// Read the contents of a symbolic link. + /// + /// # Note + /// + /// This is similar to `readlinkat` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset`, `length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path of the symbolic link from which to read. + /// - `buf`: The buffer to which to write the contents of the symbolic link. + /// - `buf_len`: The length of the `buf` buffer. + /// - `offset0`: The offset into memory where result is stored. + /// The result is the number of bytes placed in the buffer. + fn path_readlink( + fd: i32, + offset: i32, + length: i32, + buf: i32, + buf_len: i32, + offset0: i32, + ) -> i32; + + /// Remove a directory. + /// + /// # Note + /// + /// - Returns `errno::notempty` if the directory is not empty. + /// - This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset`, `length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path to the directory to remove. + fn path_remove_directory(fd: i32, offset: i32, length: i32) -> i32; + + /// Rename a file or directory. + /// + /// # Note + /// + /// - This is similar to `renameat` in POSIX. + /// - This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `old_offset`, `old_length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the source path of the file or directory to rename. + /// - `new_fd`: The working directory at which the resolution of the new path starts. + /// - `new_offset`, `new_length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the destination path to which to rename the file or directory. + fn path_rename( + fd: i32, + old_offset: i32, + old_length: i32, + new_fd: i32, + new_offset: i32, + new_length: i32, + ) -> i32; + + /// Create a symbolic link. + /// + /// # Note + /// + /// This is similar to `symlinkat` in POSIX. + /// + /// # Parameters + /// + /// - `old_offset`, `old_length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path to the contents of the symbolic link. + /// - `fd`: The file descriptor. + /// - `new_offset`, `new_length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the destination path at which to create the symbolic link. + fn path_symlink( + old_offset: i32, + old_length: i32, + fd: i32, + new_offset: i32, + new_length: i32, + ) -> i32; + + /// Unlink a file. + /// + /// # Note + /// + /// - Returns `errno::isdir` if the path refers to a directory. + /// - This is similar to `unlinkat(fd, path, 0)` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `offset`, `length`: The offset/length pair used to create a guest pointer into host memory. + /// This pointer references the path to a file to unlink. + fn path_unlink_file(fd: i32, offset: i32, length: i32) -> i32; + + /// Concurrently poll for the occurrence of a set of events. + /// + /// # Parameters + /// + /// - `in_`: The events to which to subscribe. + /// - `out`: The events that have occurred. + /// - `nsubscriptions`: Both the number of subscriptions and events. + /// - `offset0`: The offset into memory where the number of events is stored. + fn poll_oneoff(in_: i32, out: i32, nsubscriptions: i32, offset0: i32) -> i32; + + /// Terminate the process normally. + /// + /// # Note + /// + /// An exit code of 0 indicates successful termination of the program. + /// The meanings of other values is dependent on the environment. + /// + /// # Parameters + /// + /// - `rval`: The exit code returned by the process. + fn proc_exit(rval: i32) -> (); + + /// Send a signal to the process of the calling thread. + /// Note: This is similar to `raise` in POSIX. + /// # Parameters + /// + /// sig: The signal condition to trigger. + fn proc_raise(sig: i32) -> i32; + + /// Temporarily yield execution of the calling thread. + /// + /// # Note + /// + /// This is similar to sched_yield in POSIX. + fn sched_yield() -> i32; + + /// Write high-quality random data into a buffer. + /// + /// # Parameters + /// + /// - `buf`: The buffer to fill with random data. + /// - `buf_len`: The length of the `buf` buffer. + fn random_get(buf: i32, buf_len: i32) -> i32; + + /// Accept a new incoming connection. + /// + /// # Note + /// + /// This is similar to `accept` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The listening socket. + /// - `flags`: The desired values of the file descriptor flags. + /// - `offset0`: The offset into memory where the new socket connection `fd` is stored. + fn sock_accept(fd: i32, flags: i32, offset0: i32) -> i32; + + /// Receive a message from a socket. + /// + /// # Note + /// + /// This is similar to `recv` in POSIX, though it also supports reading + /// the data into multiple buffers in the manner of `readv`. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `iov_buf`, `iov_buf_len`: Used to create `iovec`, which is the list of scatter/gather + /// vectors in which to store data. + /// - `ri_flags`: The message flags. + /// - `offset0`, `offset1`: The offset into memory where the number of + /// bytes in `ri_data` and message flags are stored. + fn sock_recv( + fd: i32, + iov_buf: i32, + iov_buf_len: i32, + ri_flags: i32, + offset0: i32, + offset1: i32, + ) -> i32; + + /// Send a message on a socket. + /// + /// # Note + /// + /// This is similar to `send` in POSIX, though it also supports writing + /// the data from multiple buffers in the manner of `writev`. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `ciov_buf`, `ciov_buf_len`: Used to create ciovec, which is the list of + /// scatter/gather vectors from which to retrieve data. + /// - `si_flags`: The message flags. + /// - `offset0`: The offset into memory where number of bytes transmitted is stored. + fn sock_send(fd: i32, ciov_buf: i32, ciov_buf_len: i32, si_flags: i32, offset0: i32) -> i32; + + /// Shut down socket send and receive channels. + /// + /// # Note + /// + /// This is similar to `shutdown` in POSIX. + /// + /// # Parameters + /// + /// - `fd`: The file descriptor. + /// - `how`: Which channels on the socket to shut down. + fn sock_shutdown(fd: i32, how: i32) -> i32; +); \ No newline at end of file diff --git a/crates/player/src/wasi/interface.rs b/crates/player/src/wasi/interface.rs new file mode 100644 index 0000000..7a9a361 --- /dev/null +++ b/crates/player/src/wasi/interface.rs @@ -0,0 +1,127 @@ +pub type MPResult = Result; + +pub trait MinimalPreview1 { + fn args_get(&mut self, memory: &mut [u8], argv: i32, argv_buf: i32) -> MPResult; + fn args_sizes_get(&mut self, memory: &mut [u8], offset0: i32, offset1: i32) -> MPResult; + fn environ_get(&mut self, memory: &mut [u8], environ: i32, environ_buf: i32) -> MPResult; + fn environ_sizes_get(&mut self, memory: &mut [u8], offset0: i32, offset1: i32) -> MPResult; + fn clock_res_get(&mut self, memory: &mut [u8], id: i32, offset0: i32) -> MPResult; + fn clock_time_get(&mut self, memory: &mut [u8], id: i32, precision: i64, offset0: i32) -> MPResult; + fn fd_advise(&mut self, memory: &mut [u8], fd: i32, offset: i64, len: i64, advice: i32) -> MPResult; + fn fd_allocate(&mut self, memory: &mut [u8], fd: i32, offset: i64, len: i64) -> MPResult; + fn fd_close(&mut self, memory: &mut [u8], fd: i32) -> MPResult; + fn fd_datasync(&mut self, memory: &mut [u8], fd: i32) -> MPResult; + fn fd_fdstat_get(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult; + fn fd_fdstat_set_flags(&mut self, memory: &mut [u8], fd: i32, flags: i32) -> MPResult; + fn fd_fdstat_set_rights(&mut self, memory: &mut [u8], fd: i32, fs_rights_base: i64, fs_rights_inheriting: i64) -> MPResult; + fn fd_filestat_get(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult; + fn fd_filestat_set_size(&mut self, memory: &mut [u8], fd: i32, size: i64) -> MPResult; + fn fd_filestat_set_times(&mut self, memory: &mut [u8], fd: i32, atim: i64, mtim: i64, fst_flags: i32) -> MPResult; + fn fd_pread(&mut self, memory: &mut [u8], fd: i32, iov_buf: i32, iov_buf_len: i32, offset: i64, offset0: i32) -> MPResult; + fn fd_prestat_get(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult; + fn fd_prestat_dir_name(&mut self, memory: &mut [u8], fd: i32, path: i32, path_len: i32) -> MPResult; + fn fd_pwrite(&mut self, memory: &mut [u8], fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset: i64, offset0: i32) -> MPResult; + fn fd_read(&mut self, memory: &mut [u8], fd: i32, iov_buf: i32, iov_buf_len: i32, offset1: i32) -> MPResult; + fn fd_readdir(&mut self, memory: &mut [u8], fd: i32, buf: i32, buf_len: i32, cookie: i64, offset0: i32) -> MPResult; + fn fd_renumber(&mut self, memory: &mut [u8], fd: i32, to: i32) -> MPResult; + fn fd_seek(&mut self, memory: &mut [u8], fd: i32, offset: i64, whence: i32, offset0: i32) -> MPResult; + fn fd_sync(&mut self, memory: &mut [u8], fd: i32) -> MPResult; + fn fd_tell(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult; + fn fd_write(&mut self, memory: &mut [u8], fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset0: i32) -> MPResult; + fn path_create_directory(&mut self, memory: &mut [u8], fd: i32, offset: i32, length: i32) -> MPResult; + fn path_filestat_get(&mut self, memory: &mut [u8], fd: i32, flags: i32, offset: i32, length: i32, offset0: i32) -> MPResult; + fn path_filestat_set_times( + &mut self, + memory: &mut [u8], + fd: i32, + flags: i32, + offset: i32, + length: i32, + atim: i64, + mtim: i64, + fst_flags: i32, + ) -> MPResult; + fn path_link( + &mut self, + memory: &mut [u8], + old_fd: i32, + old_flags: i32, + old_offset: i32, + old_length: i32, + new_fd: i32, + new_offset: i32, + new_length: i32, + ) -> MPResult; + fn path_open( + &mut self, + memory: &mut [u8], + fd: i32, + dirflags: i32, + offset: i32, + length: i32, + oflags: i32, + fs_rights_base: i64, + fdflags: i64, + fs_rights_inheriting: i32, + offset0: i32, + ) -> MPResult; + fn path_readlink( + &mut self, + memory: &mut [u8], + fd: i32, + offset: i32, + length: i32, + buf: i32, + buf_len: i32, + offset0: i32, + ) -> MPResult; + fn path_remove_directory( + &mut self, + memory: &mut [u8], + fd: i32, offset: i32, length: i32 + ) -> MPResult; + fn path_rename( + &mut self, + memory: &mut [u8], + fd: i32, + old_offset: i32, + old_length: i32, + new_fd: i32, + new_offset: i32, + new_length: i32, + ) -> MPResult; + fn path_symlink( + &mut self, + memory: &mut [u8], + old_offset: i32, + old_length: i32, + fd: i32, + new_offset: i32, + new_length: i32, + ) -> MPResult; + fn path_unlink_file(&mut self, memory: &mut [u8], fd: i32, offset: i32, length: i32) -> MPResult; + fn poll_oneoff(&mut self, memory: &mut [u8], in_: i32, out: i32, nsubscriptions: i32, offset0: i32) -> MPResult; + fn proc_exit(&mut self, memory: &mut [u8], rval: i32) -> MPResult<()>; + fn proc_raise(&mut self, memory: &mut [u8], sig: i32) -> MPResult; + fn sched_yield(&mut self, memory: &mut [u8]) -> MPResult; + fn random_get(&mut self, memory: &mut [u8], buf: i32, buf_len: i32) -> MPResult; + fn sock_accept(&mut self, memory: &mut [u8], fd: i32, flags: i32, offset0: i32) -> MPResult; + fn sock_recv( + &mut self, + memory: &mut [u8], + fd: i32, + iov_buf: i32, + iov_buf_len: i32, + ri_flags: i32, + offset0: i32, + offset1: i32, + ) -> MPResult; + fn sock_send( + &mut self, + memory: &mut [u8], + fd: i32, ciov_buf: i32, ciov_buf_len: i32, si_flags: i32, offset0: i32) -> MPResult; + fn sock_shutdown( + &mut self, + memory: &mut [u8], + fd: i32, how: i32) -> MPResult; +} \ No newline at end of file diff --git a/crates/player/src/wasi/mod.rs b/crates/player/src/wasi/mod.rs new file mode 100644 index 0000000..f88d7d8 --- /dev/null +++ b/crates/player/src/wasi/mod.rs @@ -0,0 +1,7 @@ +mod interface; +mod integration; +mod implementation; + +pub use interface::{MPResult, MinimalPreview1}; +pub use integration::integrate; +pub use implementation::StockWasi; \ No newline at end of file diff --git a/example_project/.gitignore b/example_project/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/example_project/build/game.wasm b/example_project/build/game.wasm new file mode 100644 index 0000000..e6f815a Binary files /dev/null and b/example_project/build/game.wasm differ diff --git a/example_project/go.mod b/example_project/go.mod new file mode 100644 index 0000000..1a8e856 --- /dev/null +++ b/example_project/go.mod @@ -0,0 +1,3 @@ +module example_project + +go 1.21.5 \ No newline at end of file diff --git a/example_project/main.go b/example_project/main.go new file mode 100644 index 0000000..e2a5282 --- /dev/null +++ b/example_project/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "runtime/debug" +) + +func main() { + count := 0 + for { + fmt.Printf("Hello! %d", count) + count++ + debug.PrintStack() + panic("pizza!") + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3c18475 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub type VResult = Result;