Luigit
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());
    }
}