repositories / smith
smith
There are many coding harnesses - but this one is fast
owned by admin
smith-harness/src/wasm/sealed_wasi.rs
Raw//! Sealed WASI: Smith's own synchronous implementations of the WASI imports
//! that guest std runtimes pull in (`SMH-SPEC-WASM0001`, no ambient WASI).
//!
//! Sealed by default: empty environment and arguments, empty stdin, stdout
//! and stderr writes discarded, no terminals, `exit` traps, the monotonic
//! clock reads a host-injected value, and every pollable is ready.
//!
//! No host state backs a stream or pollable, so resources are bare handles
//! with no table entry; dropping one is a no-op.
use super::PluginHostState;
use super::bindings::wasi::cli::{
environment, exit, stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr,
terminal_stdin, terminal_stdout,
};
use super::bindings::wasi::clocks::monotonic_clock;
use super::bindings::wasi::io::error::{self, Error};
use super::bindings::wasi::io::poll::{self, Pollable};
use super::bindings::wasi::io::streams::{self, InputStream, OutputStream, StreamError};
use wasmtime::component::Resource;
/// The only handle value ever minted; no handle carries host state.
const SEALED: u32 = 0;
/// Bytes a sealed output stream accepts per write; writes are discarded.
const WRITE_BUDGET: u64 = 4096;
fn sealed<T: 'static>() -> Resource<T> {
Resource::new_own(SEALED)
}
impl poll::HostPollable for PluginHostState {
fn ready(&mut self, _pollable: Resource<Pollable>) -> bool {
true
}
fn block(&mut self, _pollable: Resource<Pollable>) {}
fn drop(&mut self, _pollable: Resource<Pollable>) -> wasmtime::Result<()> {
Ok(())
}
}
impl poll::Host for PluginHostState {
fn poll(&mut self, pollables: Vec<Resource<Pollable>>) -> Vec<u32> {
(0..u32::try_from(pollables.len()).unwrap_or(u32::MAX)).collect()
}
}
impl error::HostError for PluginHostState {
fn to_debug_string(&mut self, _error: Resource<Error>) -> String {
String::new()
}
fn drop(&mut self, _error: Resource<Error>) -> wasmtime::Result<()> {
Ok(())
}
}
impl error::Host for PluginHostState {}
impl streams::HostInputStream for PluginHostState {
fn read(&mut self, _stream: Resource<InputStream>, _len: u64) -> Result<Vec<u8>, StreamError> {
Err(StreamError::Closed)
}
fn blocking_read(
&mut self,
_stream: Resource<InputStream>,
_len: u64,
) -> Result<Vec<u8>, StreamError> {
Err(StreamError::Closed)
}
fn skip(&mut self, _stream: Resource<InputStream>, _len: u64) -> Result<u64, StreamError> {
Err(StreamError::Closed)
}
fn blocking_skip(
&mut self,
_stream: Resource<InputStream>,
_len: u64,
) -> Result<u64, StreamError> {
Err(StreamError::Closed)
}
fn subscribe(&mut self, _stream: Resource<InputStream>) -> Resource<Pollable> {
sealed()
}
fn drop(&mut self, _stream: Resource<InputStream>) -> wasmtime::Result<()> {
Ok(())
}
}
impl streams::HostOutputStream for PluginHostState {
fn check_write(&mut self, _stream: Resource<OutputStream>) -> Result<u64, StreamError> {
Ok(WRITE_BUDGET)
}
fn write(
&mut self,
_stream: Resource<OutputStream>,
_contents: Vec<u8>,
) -> Result<(), StreamError> {
Ok(())
}
fn blocking_write_and_flush(
&mut self,
_stream: Resource<OutputStream>,
_contents: Vec<u8>,
) -> Result<(), StreamError> {
Ok(())
}
fn flush(&mut self, _stream: Resource<OutputStream>) -> Result<(), StreamError> {
Ok(())
}
fn blocking_flush(&mut self, _stream: Resource<OutputStream>) -> Result<(), StreamError> {
Ok(())
}
fn subscribe(&mut self, _stream: Resource<OutputStream>) -> Resource<Pollable> {
sealed()
}
fn write_zeroes(
&mut self,
_stream: Resource<OutputStream>,
_len: u64,
) -> Result<(), StreamError> {
Ok(())
}
fn blocking_write_zeroes_and_flush(
&mut self,
_stream: Resource<OutputStream>,
_len: u64,
) -> Result<(), StreamError> {
Ok(())
}
fn splice(
&mut self,
_stream: Resource<OutputStream>,
_source: Resource<InputStream>,
_len: u64,
) -> Result<u64, StreamError> {
Err(StreamError::Closed)
}
fn blocking_splice(
&mut self,
_stream: Resource<OutputStream>,
_source: Resource<InputStream>,
_len: u64,
) -> Result<u64, StreamError> {
Err(StreamError::Closed)
}
fn drop(&mut self, _stream: Resource<OutputStream>) -> wasmtime::Result<()> {
Ok(())
}
}
impl streams::Host for PluginHostState {}
impl monotonic_clock::Host for PluginHostState {
fn now(&mut self) -> monotonic_clock::Instant {
self.monotonic_now
}
fn resolution(&mut self) -> monotonic_clock::Duration {
1
}
fn subscribe_instant(&mut self, _when: monotonic_clock::Instant) -> Resource<Pollable> {
sealed()
}
fn subscribe_duration(&mut self, _when: monotonic_clock::Duration) -> Resource<Pollable> {
sealed()
}
}
impl environment::Host for PluginHostState {
fn get_environment(&mut self) -> Vec<(String, String)> {
Vec::new()
}
fn get_arguments(&mut self) -> Vec<String> {
Vec::new()
}
fn initial_cwd(&mut self) -> Option<String> {
None
}
}
impl exit::Host for PluginHostState {
fn exit(&mut self, status: Result<(), ()>) -> wasmtime::Result<()> {
Err(wasmtime::format_err!("guest exited: {status:?}"))
}
fn exit_with_code(&mut self, status_code: u8) -> wasmtime::Result<()> {
Err(wasmtime::format_err!(
"guest exited with code {status_code}"
))
}
}
impl stdin::Host for PluginHostState {
fn get_stdin(&mut self) -> Resource<InputStream> {
sealed()
}
}
impl stdout::Host for PluginHostState {
fn get_stdout(&mut self) -> Resource<OutputStream> {
sealed()
}
}
impl stderr::Host for PluginHostState {
fn get_stderr(&mut self) -> Resource<OutputStream> {
sealed()
}
}
impl terminal_input::HostTerminalInput for PluginHostState {
fn drop(&mut self, _terminal: Resource<terminal_input::TerminalInput>) -> wasmtime::Result<()> {
Ok(())
}
}
impl terminal_input::Host for PluginHostState {}
impl terminal_output::HostTerminalOutput for PluginHostState {
fn drop(
&mut self,
_terminal: Resource<terminal_output::TerminalOutput>,
) -> wasmtime::Result<()> {
Ok(())
}
}
impl terminal_output::Host for PluginHostState {}
impl terminal_stdin::Host for PluginHostState {
fn get_terminal_stdin(&mut self) -> Option<Resource<terminal_input::TerminalInput>> {
None
}
}
impl terminal_stdout::Host for PluginHostState {
fn get_terminal_stdout(&mut self) -> Option<Resource<terminal_output::TerminalOutput>> {
None
}
}
impl terminal_stderr::Host for PluginHostState {
fn get_terminal_stderr(&mut self) -> Option<Resource<terminal_output::TerminalOutput>> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wasm::PluginCeilings;
use poll::Host as _;
use streams::{HostInputStream as _, HostOutputStream as _};
fn state() -> PluginHostState {
PluginHostState {
monotonic_now: 42,
ceilings: PluginCeilings::default(),
}
}
#[test]
fn poll_reports_every_pollable_ready() {
let mut host = state();
assert_eq!(host.poll(vec![sealed(), sealed(), sealed()]), [0, 1, 2]);
assert!(host.poll(Vec::new()).is_empty());
}
#[test]
fn stdin_is_closed_and_stdout_discards() {
let mut host = state();
assert!(matches!(host.read(sealed(), 16), Err(StreamError::Closed)));
assert_eq!(host.check_write(sealed()).unwrap(), WRITE_BUDGET);
assert!(host.write(sealed(), b"dropped".to_vec()).is_ok());
}
#[test]
fn clock_is_host_injected_and_exit_traps() {
let mut host = state();
assert_eq!(monotonic_clock::Host::now(&mut host), 42);
assert!(exit::Host::exit(&mut host, Ok(())).is_err());
assert!(exit::Host::exit_with_code(&mut host, 0).is_err());
}
#[test]
fn environment_is_empty() {
let mut host = state();
assert!(environment::Host::get_environment(&mut host).is_empty());
assert!(environment::Host::get_arguments(&mut host).is_empty());
assert!(environment::Host::initial_cwd(&mut host).is_none());
}
}