Basic groundwork for viperid, a fantasy console
This commit is contained in:
commit
13b61a6def
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
129
Cargo.lock
generated
Normal file
129
Cargo.lock
generated
Normal file
@ -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",
|
||||
]
|
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
@ -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"
|
||||
]
|
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
To launch the editor:
|
||||
|
||||
```
|
||||
$ cargo run --manifest-path crates\editor\Cargo.toml
|
||||
```
|
||||
|
1
crates/editor/.gitignore
vendored
Normal file
1
crates/editor/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/
|
24
crates/editor/Cargo.lock
generated
Normal file
24
crates/editor/Cargo.lock
generated
Normal file
@ -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",
|
||||
]
|
11
crates/editor/Cargo.toml
Normal file
11
crates/editor/Cargo.toml
Normal file
@ -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 = "../.." }
|
80
crates/editor/src/go_builder.rs
Normal file
80
crates/editor/src/go_builder.rs
Normal file
@ -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<GoBuilder> {
|
||||
// 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<Vec<u8>> {
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
}
|
14
crates/editor/src/main.rs
Normal file
14
crates/editor/src/main.rs
Normal file
@ -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(())
|
||||
}
|
8
crates/minifb_host/Cargo.toml
Normal file
8
crates/minifb_host/Cargo.toml
Normal file
@ -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]
|
0
crates/minifb_host/src/lib.rs
Normal file
0
crates/minifb_host/src/lib.rs
Normal file
11
crates/player/Cargo.toml
Normal file
11
crates/player/Cargo.toml
Normal file
@ -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"
|
37
crates/player/src/executor.rs
Normal file
37
crates/player/src/executor.rs
Normal file
@ -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 = <Linker<ExecutorState>>::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(())
|
||||
}
|
4
crates/player/src/lib.rs
Normal file
4
crates/player/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod executor;
|
||||
mod wasi;
|
||||
|
||||
pub use executor::run_entry_point;
|
346
crates/player/src/wasi/implementation.rs
Normal file
346
crates/player/src/wasi/implementation.rs
Normal file
@ -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<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn args_sizes_get(&mut self, memory: &mut [u8], argc: i32, argv_buf_size: i32) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn environ_sizes_get(&mut self, memory: &mut [u8], environc: i32, environ_buf_size: i32) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
write_u32(memory, outptr, 1000);
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn clock_time_get(&mut self, memory: &mut [u8], id: i32, precision: i64, outptr: i32) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_allocate(&mut self, _memory: &mut [u8], _fd: i32, _offset: i64, _len: i64) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_close(&mut self, _memory: &mut [u8], _fd: i32) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_datasync(&mut self, _memory: &mut [u8], _fd: i32) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_fdstat_get(&mut self, _memory: &mut [u8], _fd: i32, _offset0: i32) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_flags(&mut self, _memory: &mut [u8], _fd: i32, _flags: i32) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_filestat_get(&mut self, _memory: &mut [u8], _fd: i32, _offset0: i32) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_filestat_set_size(&mut self, _memory: &mut [u8], _fd: i32, _size: i64) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_prestat_get(&mut self, _memory: &mut [u8], fd: i32, _offset0: i32) -> super::MPResult<i32> {
|
||||
// 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<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_renumber(&mut self, _memory: &mut [u8], _fd: i32, _to: i32) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_seek(&mut self, _memory: &mut [u8], _fd: i32, _offset: i64, _whence: i32, _offset0: i32) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_sync(&mut self, _memory: &mut [u8], _fd: i32) -> super::MPResult<i32> {
|
||||
Ok(FUNCTION_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
fn fd_tell(&mut self, _memory: &mut [u8], _fd: i32, _offset0: i32) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn path_remove_directory(
|
||||
&mut self,
|
||||
_memory: &mut [u8],
|
||||
_fd: i32, _offset: i32, _length: i32
|
||||
) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn path_unlink_file(&mut self, _memory: &mut [u8], _fd: i32, _offset: i32, _length: i32) -> super::MPResult<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn poll_oneoff(&mut self, _memory: &mut [u8], _in_: i32, _out: i32, _nsubscriptions: i32, _offset0: i32) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
// TODO: Actually implement
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn sched_yield(&mut self, _memory: &mut [u8]) -> super::MPResult<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn random_get(&mut self, _memory: &mut [u8], _buf: i32, _buf_len: i32) -> super::MPResult<i32> {
|
||||
// TODO: Actually implement
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn sock_accept(&mut self, _memory: &mut [u8], _fd: i32, _flags: i32, _offset0: i32) -> super::MPResult<i32> {
|
||||
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<i32> {
|
||||
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<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn sock_shutdown(
|
||||
&mut self,
|
||||
_memory: &mut [u8],
|
||||
_fd: i32, _how: i32) -> super::MPResult<i32> {
|
||||
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());
|
||||
}
|
699
crates/player/src/wasi/integration.rs
Normal file
699
crates/player/src/wasi/integration.rs
Normal file
@ -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<T, U>(
|
||||
linker: &mut Linker<T>,
|
||||
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;
|
||||
);
|
127
crates/player/src/wasi/interface.rs
Normal file
127
crates/player/src/wasi/interface.rs
Normal file
@ -0,0 +1,127 @@
|
||||
pub type MPResult<T> = Result<T, wasmi::core::Trap>;
|
||||
|
||||
pub trait MinimalPreview1 {
|
||||
fn args_get(&mut self, memory: &mut [u8], argv: i32, argv_buf: i32) -> MPResult<i32>;
|
||||
fn args_sizes_get(&mut self, memory: &mut [u8], offset0: i32, offset1: i32) -> MPResult<i32>;
|
||||
fn environ_get(&mut self, memory: &mut [u8], environ: i32, environ_buf: i32) -> MPResult<i32>;
|
||||
fn environ_sizes_get(&mut self, memory: &mut [u8], offset0: i32, offset1: i32) -> MPResult<i32>;
|
||||
fn clock_res_get(&mut self, memory: &mut [u8], id: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn clock_time_get(&mut self, memory: &mut [u8], id: i32, precision: i64, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_advise(&mut self, memory: &mut [u8], fd: i32, offset: i64, len: i64, advice: i32) -> MPResult<i32>;
|
||||
fn fd_allocate(&mut self, memory: &mut [u8], fd: i32, offset: i64, len: i64) -> MPResult<i32>;
|
||||
fn fd_close(&mut self, memory: &mut [u8], fd: i32) -> MPResult<i32>;
|
||||
fn fd_datasync(&mut self, memory: &mut [u8], fd: i32) -> MPResult<i32>;
|
||||
fn fd_fdstat_get(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_fdstat_set_flags(&mut self, memory: &mut [u8], fd: i32, flags: i32) -> MPResult<i32>;
|
||||
fn fd_fdstat_set_rights(&mut self, memory: &mut [u8], fd: i32, fs_rights_base: i64, fs_rights_inheriting: i64) -> MPResult<i32>;
|
||||
fn fd_filestat_get(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_filestat_set_size(&mut self, memory: &mut [u8], fd: i32, size: i64) -> MPResult<i32>;
|
||||
fn fd_filestat_set_times(&mut self, memory: &mut [u8], fd: i32, atim: i64, mtim: i64, fst_flags: i32) -> MPResult<i32>;
|
||||
fn fd_pread(&mut self, memory: &mut [u8], fd: i32, iov_buf: i32, iov_buf_len: i32, offset: i64, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_prestat_get(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_prestat_dir_name(&mut self, memory: &mut [u8], fd: i32, path: i32, path_len: i32) -> MPResult<i32>;
|
||||
fn fd_pwrite(&mut self, memory: &mut [u8], fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset: i64, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_read(&mut self, memory: &mut [u8], fd: i32, iov_buf: i32, iov_buf_len: i32, offset1: i32) -> MPResult<i32>;
|
||||
fn fd_readdir(&mut self, memory: &mut [u8], fd: i32, buf: i32, buf_len: i32, cookie: i64, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_renumber(&mut self, memory: &mut [u8], fd: i32, to: i32) -> MPResult<i32>;
|
||||
fn fd_seek(&mut self, memory: &mut [u8], fd: i32, offset: i64, whence: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_sync(&mut self, memory: &mut [u8], fd: i32) -> MPResult<i32>;
|
||||
fn fd_tell(&mut self, memory: &mut [u8], fd: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn fd_write(&mut self, memory: &mut [u8], fd: i32, ciov_buf: i32, ciov_buf_len: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn path_create_directory(&mut self, memory: &mut [u8], fd: i32, offset: i32, length: i32) -> MPResult<i32>;
|
||||
fn path_filestat_get(&mut self, memory: &mut [u8], fd: i32, flags: i32, offset: i32, length: i32, offset0: i32) -> MPResult<i32>;
|
||||
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<i32>;
|
||||
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<i32>;
|
||||
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<i32>;
|
||||
fn path_readlink(
|
||||
&mut self,
|
||||
memory: &mut [u8],
|
||||
fd: i32,
|
||||
offset: i32,
|
||||
length: i32,
|
||||
buf: i32,
|
||||
buf_len: i32,
|
||||
offset0: i32,
|
||||
) -> MPResult<i32>;
|
||||
fn path_remove_directory(
|
||||
&mut self,
|
||||
memory: &mut [u8],
|
||||
fd: i32, offset: i32, length: i32
|
||||
) -> MPResult<i32>;
|
||||
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<i32>;
|
||||
fn path_symlink(
|
||||
&mut self,
|
||||
memory: &mut [u8],
|
||||
old_offset: i32,
|
||||
old_length: i32,
|
||||
fd: i32,
|
||||
new_offset: i32,
|
||||
new_length: i32,
|
||||
) -> MPResult<i32>;
|
||||
fn path_unlink_file(&mut self, memory: &mut [u8], fd: i32, offset: i32, length: i32) -> MPResult<i32>;
|
||||
fn poll_oneoff(&mut self, memory: &mut [u8], in_: i32, out: i32, nsubscriptions: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn proc_exit(&mut self, memory: &mut [u8], rval: i32) -> MPResult<()>;
|
||||
fn proc_raise(&mut self, memory: &mut [u8], sig: i32) -> MPResult<i32>;
|
||||
fn sched_yield(&mut self, memory: &mut [u8]) -> MPResult<i32>;
|
||||
fn random_get(&mut self, memory: &mut [u8], buf: i32, buf_len: i32) -> MPResult<i32>;
|
||||
fn sock_accept(&mut self, memory: &mut [u8], fd: i32, flags: i32, offset0: i32) -> MPResult<i32>;
|
||||
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<i32>;
|
||||
fn sock_send(
|
||||
&mut self,
|
||||
memory: &mut [u8],
|
||||
fd: i32, ciov_buf: i32, ciov_buf_len: i32, si_flags: i32, offset0: i32) -> MPResult<i32>;
|
||||
fn sock_shutdown(
|
||||
&mut self,
|
||||
memory: &mut [u8],
|
||||
fd: i32, how: i32) -> MPResult<i32>;
|
||||
}
|
7
crates/player/src/wasi/mod.rs
Normal file
7
crates/player/src/wasi/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
mod interface;
|
||||
mod integration;
|
||||
mod implementation;
|
||||
|
||||
pub use interface::{MPResult, MinimalPreview1};
|
||||
pub use integration::integrate;
|
||||
pub use implementation::StockWasi;
|
0
example_project/.gitignore
vendored
Normal file
0
example_project/.gitignore
vendored
Normal file
BIN
example_project/build/game.wasm
Normal file
BIN
example_project/build/game.wasm
Normal file
Binary file not shown.
3
example_project/go.mod
Normal file
3
example_project/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module example_project
|
||||
|
||||
go 1.21.5
|
16
example_project/main.go
Normal file
16
example_project/main.go
Normal file
@ -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!")
|
||||
}
|
||||
}
|
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
pub type VResult<T> = Result<T, anyhow::Error>;
|
Loading…
Reference in New Issue
Block a user