Skip to main content

embedding/
wasi.rs

1//! This example demonstrates how wasmtime-wasi-io can be used in a #![no_std]
2//! target as the basis for a WASI implementation.
3//!
4//! This example can execute a wasi:cli/command component on a custom async
5//! executor with no dependencies on the environment: execution is
6//! deterministic, and no sources of input are provided to the component. The
7//! WASI implementation is deliberately limited and incomplete, and many WASI
8//! components will not even instantiate, or execute correctly, because this
9//! is not a fully fleshed-out example.
10//!
11//! The wasmtime-wasi implementation of WASI depends on the tokio executor,
12//! cap-std family of crates, and others to provide a complete implementation
13//! of WASI p2 on top of Unix-based and Windows operating systems. It would be
14//! difficult and/or inappropriate to port to other settings. This example
15//! might be a good starting point for how to go about rolling your own WASI
16//! implementation that is particular to your own execution environment.
17//!
18//! The wasmtime-wasi-io crate, which is a key part of this example, provides
19//! an implementation of the wasi:io package, which is the foundation of
20//! WASIp2. wasmtime-wasi-io provides the Pollable, InputStream, and
21//! OutputStream traits, and this example shows implementations of those
22//! traits for this particular embedding.
23
24use alloc::boxed::Box;
25use alloc::collections::VecDeque;
26use alloc::rc::Rc;
27use alloc::string::{String, ToString};
28use alloc::vec::Vec;
29use core::cell::{Cell, RefCell};
30use core::fmt::Write as _;
31use core::future::Future;
32use core::pin::Pin;
33use core::task::{Context, Poll, Waker};
34use wasmtime::component::{Component, Linker, Resource, ResourceTable};
35use wasmtime::{Engine, Result, Store, bail};
36use wasmtime_wasi_io::{
37    IoView,
38    bytes::Bytes,
39    poll::{DynPollable, Pollable, subscribe},
40    streams::{DynInputStream, DynOutputStream, InputStream, OutputStream},
41};
42
43/// Unlike super::run, its nice to provide some sort of output showing what the
44/// wasi program did while it executed, so this function reports in out_buf
45/// what stdout/stderr prints occurred on success (returns 0), or the error
46/// message on failure (returns != 0).
47#[unsafe(no_mangle)]
48pub unsafe extern "C" fn run_wasi(
49    out_buf: *mut u8,
50    out_size: *mut usize,
51    wasi_component: *const u8,
52    wasi_component_size: usize,
53) -> usize {
54    unsafe {
55        let buf = core::slice::from_raw_parts_mut(out_buf, *out_size);
56        let wasi_component = core::slice::from_raw_parts(wasi_component, wasi_component_size);
57        match run(wasi_component) {
58            Ok(output) => {
59                let len = buf.len().min(output.len());
60                buf[..len].copy_from_slice(&output.as_bytes()[..len]);
61                *out_size = len;
62                return 0;
63            }
64            Err(e) => {
65                let msg = format!("{e:?}");
66                let len = buf.len().min(msg.len());
67                buf[..len].copy_from_slice(&msg.as_bytes()[..len]);
68                *out_size = len;
69                return 1;
70            }
71        }
72    }
73}
74
75fn run(wasi_component: &[u8]) -> Result<String> {
76    let config = super::config();
77    // For future: we could consider turning on fuel in the Config to meter
78    // how long a wasm guest could execute for.
79    let engine = Engine::new(&config)?;
80
81    // Like with modules, we deserialize components into native code:
82    let component = match deserialize(&engine, wasi_component)? {
83        Some(c) => c,
84        None => return Ok("cannot load native code - requires virtual memory".to_string()),
85    };
86
87    // Linker provides wasmtime-wasi-io's implementation of wasi:io package,
88    // and a number of other wasi interfaces implemented below as part of this
89    // example.
90    let mut linker = Linker::new(&engine);
91    wasmtime_wasi_io::add_to_linker_async(&mut linker)?;
92    add_to_linker_async(&mut linker)?;
93
94    // Ensure all imports of the component are satisfied by the linker:
95    let instance_pre = linker.instantiate_pre(&component)?;
96    // Ensure the exports of the component provide the Command world:
97    let command_pre = CommandPre::new(instance_pre)?;
98
99    // Executor and WasiCtx share the same clock:
100    let clock = Clock::new();
101
102    // Use our custom executor to run some async code here:
103    block_on(clock.clone(), async move {
104        let ctx = ExampleCtx {
105            table: ResourceTable::new(),
106            clock,
107            stdout: WriteLog::new(),
108            stderr: WriteLog::new(),
109        };
110        let mut store = Store::new(&engine, ctx);
111        // instantiate runs the wasm `start` section of
112        let instance = command_pre.instantiate_async(&mut store).await?;
113        instance
114            .wasi_cli_run()
115            .call_run(&mut store)
116            .await?
117            .map_err(|()| wasmtime::format_err!("wasi cli run returned error"))?;
118
119        store.into_data().output()
120    })
121}
122
123fn deserialize(engine: &Engine, component: &[u8]) -> Result<Option<Component>> {
124    match unsafe { Component::deserialize(engine, component) } {
125        Ok(component) => Ok(Some(component)),
126        Err(e) => {
127            // Currently if custom signals/virtual memory are disabled then this
128            // example is expected to fail to load since loading native code
129            // requires virtual memory. In the future this will go away as when
130            // signals-based-traps is disabled then that means that the
131            // interpreter should be used which should work here.
132            if !cfg!(feature = "custom")
133                && e.to_string()
134                    .contains("requires virtual memory to be enabled")
135            {
136                Ok(None)
137            } else {
138                Err(e)
139            }
140        }
141    }
142}
143
144// Generate bindings for the entire wasi:cli command world. We won't impl and
145// link with all of these generated bindings for the sake of this example.
146wasmtime::component::bindgen!({
147    path: "../../../crates/wasi/src/p2/wit",
148    world: "wasi:cli/command",
149    imports: { default: trappable },
150    exports: { default: async },
151    require_store_data_send: true,
152    // Important: tell bindgen that anywhere it encounters the wasi:io
153    // package, refer to the bindings generated in the wasmtime_wasi_io crate.
154    // This way, all uses of the streams and pollable in the bindings in this
155    // file match with the resource types (DynInputStream, DynOutputStream,
156    // DynPollable) we use from the wasmtime_wasi_io crate.
157    with: {
158        "wasi:io": wasmtime_wasi_io::bindings::wasi::io,
159    }
160});
161
162/// A Ctx struct particular to this example. In library code designed to be
163/// reused and extended, this might be called a WasiCtx and not include a
164/// ResourceTable as a member, but for the sake of this example, we put
165/// everything that the bind
166pub struct ExampleCtx {
167    table: ResourceTable,
168    clock: Clock,
169    stdout: WriteLog,
170    stderr: WriteLog,
171}
172
173// Provide an IoView impl in order to satisfy
174// wasmtime_wasi_io::add_to_linker_async.
175impl IoView for ExampleCtx {
176    fn table(&mut self) -> &mut ResourceTable {
177        &mut self.table
178    }
179}
180
181impl ExampleCtx {
182    // Collect all of the output written to stdout and stderr into a simple
183    // human-readable string, to be written to out_buf from run_wasi on
184    // success. Lossy utf8 conversion because this is an example.
185    fn output(&self) -> Result<String> {
186        let mut out = String::new();
187        let stdout = self.stdout.log.borrow();
188        if !stdout.is_empty() {
189            write!(&mut out, "stdout:\n")?;
190            for chunk in stdout.iter() {
191                write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
192            }
193        }
194        let stderr = self.stderr.log.borrow();
195        if !stderr.is_empty() {
196            write!(&mut out, "stderr:\n")?;
197            for chunk in stderr.iter() {
198                write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
199            }
200        }
201        Ok(out)
202    }
203}
204
205// Add the minimum number of wasi interfaces to the Linker to instantiate the
206// example application. This does not provide support for the entire
207// wasi:cli/command world. Many of these impls are bare-bones and some are
208// intentionally broken, see notes below.
209pub fn add_to_linker_async(linker: &mut Linker<ExampleCtx>) -> Result<()> {
210    type Data = wasmtime::component::HasSelf<ExampleCtx>;
211
212    wasi::clocks::monotonic_clock::add_to_linker::<_, Data>(linker, |t| t)?;
213    wasi::clocks::wall_clock::add_to_linker::<_, Data>(linker, |t| t)?;
214    wasi::cli::environment::add_to_linker::<_, Data>(linker, |t| t)?;
215    wasi::cli::exit::add_to_linker::<_, Data>(linker, &Default::default(), |t| t)?;
216    wasi::cli::stdin::add_to_linker::<_, Data>(linker, |t| t)?;
217    wasi::cli::stdout::add_to_linker::<_, Data>(linker, |t| t)?;
218    wasi::cli::stderr::add_to_linker::<_, Data>(linker, |t| t)?;
219    wasi::random::random::add_to_linker::<_, Data>(linker, |t| t)?;
220    wasi::cli::terminal_input::add_to_linker::<_, Data>(linker, |t| t)?;
221    wasi::cli::terminal_output::add_to_linker::<_, Data>(linker, |t| t)?;
222    wasi::cli::terminal_stdin::add_to_linker::<_, Data>(linker, |t| t)?;
223    wasi::cli::terminal_stdout::add_to_linker::<_, Data>(linker, |t| t)?;
224    wasi::cli::terminal_stderr::add_to_linker::<_, Data>(linker, |t| t)?;
225    wasi::filesystem::preopens::add_to_linker::<_, Data>(linker, |t| t)?;
226    wasi::filesystem::types::add_to_linker::<_, Data>(linker, |t| t)?;
227    Ok(())
228}
229
230// WasiCtx and the Executor need to share a single clock, so make it reference
231// counted.
232#[derive(Clone)]
233struct Clock(Rc<Cell<u64>>);
234impl Clock {
235    fn new() -> Self {
236        Clock(Rc::new(Cell::new(0)))
237    }
238    fn get(&self) -> u64 {
239        self.0.get()
240    }
241    fn set(&self, to: u64) {
242        self.0.set(to)
243    }
244    fn timer(&self, due: u64) -> Deadline {
245        Deadline {
246            clock: self.clone(),
247            due,
248        }
249    }
250}
251// SAFETY: only will consume this crate in single-threaded environment
252unsafe impl Send for Clock {}
253unsafe impl Sync for Clock {}
254
255// A Deadline is used to implement the monotonic clock's pollable. It is a
256// future which is ready when the clock reaches the due time.
257#[derive(Clone)]
258struct Deadline {
259    clock: Clock,
260    due: u64,
261}
262impl Future for Deadline {
263    type Output = ();
264    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
265        let now = self.clock.get();
266        if now < self.due {
267            Executor::current().push_deadline(self.due, cx.waker().clone());
268            Poll::Pending
269        } else {
270            Poll::Ready(())
271        }
272    }
273}
274#[wasmtime_wasi_io::async_trait]
275impl Pollable for Deadline {
276    async fn ready(&mut self) {
277        self.clone().await
278    }
279}
280
281// An input-stream which is never ready for reading is used to implement
282// stdin.
283struct NeverReadable;
284#[wasmtime_wasi_io::async_trait]
285impl Pollable for NeverReadable {
286    async fn ready(&mut self) {
287        struct Pending;
288        impl Future for Pending {
289            type Output = ();
290            fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
291                Poll::Pending
292            }
293        }
294        Pending.await
295    }
296}
297impl InputStream for NeverReadable {
298    fn read(&mut self, _: usize) -> wasmtime_wasi_io::streams::StreamResult<Bytes> {
299        unreachable!("never ready for reading")
300    }
301}
302
303// WriteLog is used implement stdout and stderr. Cloneable because wasi:cli
304// requires, when calling get_stdout/get_stderr multiple times, to provide
305// distinct resources that point to the same underlying stream. RefCell
306// provides mutation, and VecDeque provides O(1) push_back operation.
307#[derive(Clone)]
308struct WriteLog {
309    log: Rc<RefCell<VecDeque<Bytes>>>,
310}
311impl WriteLog {
312    fn new() -> Self {
313        Self {
314            log: Rc::new(RefCell::new(VecDeque::new())),
315        }
316    }
317}
318// SAFETY: only will consume this crate in single-threaded environment
319unsafe impl Send for WriteLog {}
320unsafe impl Sync for WriteLog {}
321
322impl OutputStream for WriteLog {
323    fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> {
324        Ok(usize::MAX)
325    }
326    fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> {
327        self.log.borrow_mut().push_back(contents);
328        Ok(())
329    }
330    fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> {
331        Ok(())
332    }
333}
334#[wasmtime_wasi_io::async_trait]
335impl Pollable for WriteLog {
336    async fn ready(&mut self) {
337        // always ready - return immediately.
338    }
339}
340
341// Global symbol (no thread local storage on this target) provides ability for
342// Future impls to tell the Executor what they are waiting on.
343static EXECUTOR: ExecutorGlobal = ExecutorGlobal::new();
344
345// RefCell for mutation, Option so the Executor can be present only for life
346// of the block_on call.
347struct ExecutorGlobal(RefCell<Option<Executor>>);
348impl ExecutorGlobal {
349    const fn new() -> Self {
350        ExecutorGlobal(RefCell::new(None))
351    }
352}
353// SAFETY: only will consume this crate in single-threaded environment
354unsafe impl Send for ExecutorGlobal {}
355unsafe impl Sync for ExecutorGlobal {}
356
357// Rc because executor and global both need to hold a reference, and makes it
358// convenient to implement current(). RefCell for mutation.
359struct Executor(Rc<RefCell<ExecutorInner>>);
360
361impl Executor {
362    pub fn new() -> Self {
363        Executor(Rc::new(RefCell::new(ExecutorInner {
364            schedule: Vec::new(),
365        })))
366    }
367    pub fn current() -> Self {
368        Executor(
369            EXECUTOR
370                .0
371                .borrow_mut()
372                .as_ref()
373                .expect("Executor::current must be called within block_on")
374                .0
375                .clone(),
376        )
377    }
378    pub fn push_deadline(&mut self, due: u64, waker: Waker) {
379        self.0.borrow_mut().schedule.push((due, waker))
380    }
381}
382
383// Schedule, as provided by the Deadline future impls. Map of due times to
384// wakers.
385struct ExecutorInner {
386    schedule: Vec<(u64, Waker)>,
387}
388
389impl ExecutorInner {
390    // Get the earliest deadline currently waiting. None if there are no
391    // deadlines.
392    fn earliest_deadline(&self) -> Option<u64> {
393        self.schedule.iter().map(|(due, _)| due).min().copied()
394    }
395    // Return all wakers associated with deadlines before or equal to the
396    // current clock time. Removes the wakers and their deadline from the
397    // schedule.
398    fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> {
399        let mut i = 0;
400        let mut wakers = Vec::new();
401        // This is basically https://doc.rust-lang.org/std/vec/struct.Vec.html#method.extract_if,
402        // which is unstable
403        while i < self.schedule.len() {
404            if let Some((due, _)) = self.schedule.get(i) {
405                if *due <= now {
406                    let (_, waker) = self.schedule.remove(i);
407                    wakers.push(waker);
408                } else {
409                    i += 1;
410                }
411            } else {
412                break;
413            }
414        }
415        wakers
416    }
417}
418
419fn block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R> {
420    // Guard against nested invocations
421    if EXECUTOR.0.borrow_mut().is_some() {
422        panic!("cannot block_on while executor is running!")
423    }
424    let executor = Executor::new();
425    *EXECUTOR.0.borrow_mut() = Some(Executor(executor.0.clone()));
426
427    // No special waker needed for this executor.
428    let mut cx = Context::from_waker(Waker::noop());
429    let mut f = core::pin::pin!(f);
430
431    // Drive the Future to completion in the following loop
432    let r = 'outer: loop {
433        // Arbitrary. Could be as little as 1. There's no fuel-based async
434        // yielding in this example so repeated polls is probably not making
435        // progress without "providing input" from the outside environment,
436        // below.
437        const POLLS_PER_CLOCK: usize = 200;
438        for _ in 0..POLLS_PER_CLOCK {
439            match f.as_mut().poll(&mut cx) {
440                Poll::Pending => {}
441                Poll::Ready(r) => break 'outer r,
442            }
443        }
444
445        // This is where a non-example executor would wait for input from the
446        // "outside world". This example checks if the schedule indicates the
447        // guest is waiting on some future deadline and fast-forwards time
448        // until then, because no other input is possible in this example.
449        if let Some(sleep_until) = executor.0.borrow().earliest_deadline() {
450            clock.set(sleep_until);
451        } else {
452            clock.set(clock.get() + 1);
453        }
454
455        // Any wakers which are ready can be waked now.
456        for waker in executor.0.borrow_mut().ready_deadlines(clock.get()) {
457            waker.wake()
458        }
459    };
460
461    // Clean up guard for nested invocations
462    let _ = EXECUTOR
463        .0
464        .borrow_mut()
465        .take()
466        .expect("executor vacated global while running");
467    r
468}
469
470// -------------- impls for the bindgen! Host traits ------------------
471// These impls are written directly for WasiCtx, which is fine because this
472// example isn't trying to create reusable library code.
473
474impl wasi::clocks::monotonic_clock::Host for ExampleCtx {
475    fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> {
476        Ok(self.clock.get())
477    }
478    fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> {
479        Ok(1)
480    }
481    fn subscribe_duration(
482        &mut self,
483        duration: wasi::clocks::monotonic_clock::Duration,
484    ) -> Result<Resource<DynPollable>> {
485        self.subscribe_instant(self.clock.get() + duration)
486    }
487    fn subscribe_instant(
488        &mut self,
489        deadline: wasi::clocks::monotonic_clock::Instant,
490    ) -> Result<Resource<DynPollable>> {
491        let timer = self.clock.timer(deadline);
492        let deadline = self.table().push(timer)?;
493        Ok(subscribe(self.table(), deadline)?)
494    }
495}
496
497impl wasi::clocks::wall_clock::Host for ExampleCtx {
498    fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
499        // A bogus time. This datetime is relative to the unix epoch. Just
500        // reuse the monotonic time for the sake of the example.
501        let now = self.clock.get();
502        let seconds = now / 1_000_000_000;
503        let nanoseconds = (now - (seconds * 1_000_000_000)) as u32;
504        Ok(wasi::clocks::wall_clock::Datetime {
505            seconds,
506            nanoseconds,
507        })
508    }
509    fn resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
510        Ok(wasi::clocks::wall_clock::Datetime {
511            seconds: 0,
512            nanoseconds: 1,
513        })
514    }
515}
516
517// No arguments, environment variables, or cwd are provided.
518impl wasi::cli::environment::Host for ExampleCtx {
519    fn get_arguments(&mut self) -> Result<Vec<String>> {
520        Ok(Vec::new())
521    }
522    fn get_environment(&mut self) -> Result<Vec<(String, String)>> {
523        Ok(Vec::new())
524    }
525    fn initial_cwd(&mut self) -> Result<Option<String>> {
526        Ok(None)
527    }
528}
529
530// Ideally this would follow the example in wasmtime-wasi: make a struct, impl
531// Error on it, and try downcasting to it at the call_run site to see if the
532// wasi:cli/exit was used to exit with success without unwinding - valid but
533// uncommon behavior that should be treated the same as returning ok from the
534// wasi:cli/run.run function. Our example program doesn't exit that way.
535impl wasi::cli::exit::Host for ExampleCtx {
536    fn exit(&mut self, code: Result<(), ()>) -> Result<()> {
537        if code.is_ok() {
538            bail!("wasi exit success")
539        } else {
540            bail!("wasi exit error")
541        }
542    }
543    // This is feature-flagged (unstable) in the wits. Per the LinkOptions
544    // passed to the wasi::cli::exit::add_to_linker, it won't be found in
545    // any guest code.
546    fn exit_with_code(&mut self, _: u8) -> Result<()> {
547        unreachable!("this unstable func is not added to the linker");
548    }
549}
550
551impl wasi::cli::stdin::Host for ExampleCtx {
552    fn get_stdin(&mut self) -> Result<Resource<DynInputStream>> {
553        let stdin: DynInputStream = Box::new(NeverReadable);
554        Ok(self.table().push(stdin)?)
555    }
556}
557
558impl wasi::cli::stdout::Host for ExampleCtx {
559    fn get_stdout(&mut self) -> Result<Resource<DynOutputStream>> {
560        let stdout: DynOutputStream = Box::new(self.stdout.clone());
561        Ok(self.table().push(stdout)?)
562    }
563}
564
565impl wasi::cli::stderr::Host for ExampleCtx {
566    fn get_stderr(&mut self) -> Result<Resource<DynOutputStream>> {
567        let stderr: DynOutputStream = Box::new(self.stderr.clone());
568        Ok(self.table().push(stderr)?)
569    }
570}
571
572// Terminal resources: nothing is a terminal in this minimal embedding.
573impl wasi::cli::terminal_input::Host for ExampleCtx {}
574impl wasi::cli::terminal_input::HostTerminalInput for ExampleCtx {
575    fn drop(&mut self, r: Resource<wasi::cli::terminal_input::TerminalInput>) -> Result<()> {
576        self.table.delete(r)?;
577        Ok(())
578    }
579}
580impl wasi::cli::terminal_output::Host for ExampleCtx {}
581impl wasi::cli::terminal_output::HostTerminalOutput for ExampleCtx {
582    fn drop(&mut self, r: Resource<wasi::cli::terminal_output::TerminalOutput>) -> Result<()> {
583        self.table.delete(r)?;
584        Ok(())
585    }
586}
587impl wasi::cli::terminal_stdin::Host for ExampleCtx {
588    fn get_terminal_stdin(
589        &mut self,
590    ) -> Result<Option<Resource<wasi::cli::terminal_input::TerminalInput>>> {
591        Ok(None)
592    }
593}
594impl wasi::cli::terminal_stdout::Host for ExampleCtx {
595    fn get_terminal_stdout(
596        &mut self,
597    ) -> Result<Option<Resource<wasi::cli::terminal_output::TerminalOutput>>> {
598        Ok(None)
599    }
600}
601impl wasi::cli::terminal_stderr::Host for ExampleCtx {
602    fn get_terminal_stderr(
603        &mut self,
604    ) -> Result<Option<Resource<wasi::cli::terminal_output::TerminalOutput>>> {
605        Ok(None)
606    }
607}
608
609// This is obviously bogus and breaks the guarantees given by this interface.
610// In a real embedding, provide a high quality source of randomness here.
611impl wasi::random::random::Host for ExampleCtx {
612    fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> {
613        let mut vec = Vec::new();
614        vec.resize(len as usize, 0u8);
615        Ok(vec)
616    }
617    fn get_random_u64(&mut self) -> Result<u64> {
618        Ok(0)
619    }
620}
621
622// The preopens are the only place the filesystem is provided a Descriptor,
623// from which to try open_at to get more Descriptors. If we don't provide
624// anything here, none of the methods on Descriptor will ever be reachable,
625// because Resources are unforgable (the runtime will trap bogus indexes).
626impl wasi::filesystem::preopens::Host for ExampleCtx {
627    fn get_directories(
628        &mut self,
629    ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> {
630        // Never construct a Descriptor, so all of the bails in the rest of Filesystem should be
631        // unreachable.
632        Ok(Vec::new())
633    }
634}
635
636// This impl is completely empty!
637impl wasi::filesystem::types::HostDescriptor for ExampleCtx {
638    fn read_via_stream(
639        &mut self,
640        _: Resource<wasi::filesystem::types::Descriptor>,
641        _: u64,
642    ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> {
643        unreachable!("no filesystem")
644    }
645    fn write_via_stream(
646        &mut self,
647        _: Resource<wasi::filesystem::types::Descriptor>,
648        _: u64,
649    ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
650        unreachable!("no filesystem")
651    }
652    fn append_via_stream(
653        &mut self,
654        _: Resource<wasi::filesystem::types::Descriptor>,
655    ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
656        unreachable!("no filesystem")
657    }
658    fn advise(
659        &mut self,
660        _: Resource<wasi::filesystem::types::Descriptor>,
661        _: u64,
662        _: u64,
663        _: wasi::filesystem::types::Advice,
664    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
665        unreachable!("no filesystem")
666    }
667    fn sync_data(
668        &mut self,
669        _: Resource<wasi::filesystem::types::Descriptor>,
670    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
671        unreachable!("no filesystem")
672    }
673    fn get_flags(
674        &mut self,
675        _: Resource<wasi::filesystem::types::Descriptor>,
676    ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>>
677    {
678        unreachable!("no filesystem")
679    }
680    fn get_type(
681        &mut self,
682        _: Resource<wasi::filesystem::types::Descriptor>,
683    ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>>
684    {
685        unreachable!("no filesystem")
686    }
687    fn set_size(
688        &mut self,
689        _: Resource<wasi::filesystem::types::Descriptor>,
690        _: u64,
691    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
692        unreachable!("no filesystem")
693    }
694    fn set_times(
695        &mut self,
696        _: Resource<wasi::filesystem::types::Descriptor>,
697        _: wasi::filesystem::types::NewTimestamp,
698        _: wasi::filesystem::types::NewTimestamp,
699    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
700        unreachable!("no filesystem")
701    }
702    fn read(
703        &mut self,
704        _: Resource<wasi::filesystem::types::Descriptor>,
705        _: u64,
706        _: u64,
707    ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> {
708        unreachable!("no filesystem")
709    }
710    fn write(
711        &mut self,
712        _: Resource<wasi::filesystem::types::Descriptor>,
713        _: Vec<u8>,
714        _: u64,
715    ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> {
716        unreachable!("no filesystem")
717    }
718
719    fn read_directory(
720        &mut self,
721        _: Resource<wasi::filesystem::types::Descriptor>,
722    ) -> Result<
723        Result<
724            Resource<wasi::filesystem::types::DirectoryEntryStream>,
725            wasi::filesystem::types::ErrorCode,
726        >,
727    > {
728        unreachable!("no filesystem")
729    }
730    fn sync(
731        &mut self,
732        _: Resource<wasi::filesystem::types::Descriptor>,
733    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
734        unreachable!("no filesystem")
735    }
736    fn create_directory_at(
737        &mut self,
738        _: Resource<wasi::filesystem::types::Descriptor>,
739        _: String,
740    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
741        unreachable!("no filesystem")
742    }
743    fn stat(
744        &mut self,
745        _: Resource<wasi::filesystem::types::Descriptor>,
746    ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
747    {
748        unreachable!("no filesystem")
749    }
750    fn stat_at(
751        &mut self,
752        _: Resource<wasi::filesystem::types::Descriptor>,
753        _: wasi::filesystem::types::PathFlags,
754        _: String,
755    ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
756    {
757        unreachable!("no filesystem")
758    }
759    fn set_times_at(
760        &mut self,
761        _: Resource<wasi::filesystem::types::Descriptor>,
762        _: wasi::filesystem::types::PathFlags,
763        _: String,
764        _: wasi::filesystem::types::NewTimestamp,
765        _: wasi::filesystem::types::NewTimestamp,
766    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
767        unreachable!("no filesystem")
768    }
769    fn link_at(
770        &mut self,
771        _: Resource<wasi::filesystem::types::Descriptor>,
772        _: wasi::filesystem::types::PathFlags,
773        _: String,
774        _: Resource<wasi::filesystem::types::Descriptor>,
775        _: String,
776    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
777        unreachable!("no filesystem")
778    }
779    fn open_at(
780        &mut self,
781        _: Resource<wasi::filesystem::types::Descriptor>,
782        _: wasi::filesystem::types::PathFlags,
783        _: String,
784        _: wasi::filesystem::types::OpenFlags,
785        _: wasi::filesystem::types::DescriptorFlags,
786    ) -> Result<
787        Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>,
788    > {
789        unreachable!("no filesystem")
790    }
791    fn readlink_at(
792        &mut self,
793        _: Resource<wasi::filesystem::types::Descriptor>,
794        _: String,
795    ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> {
796        unreachable!("no filesystem")
797    }
798    fn remove_directory_at(
799        &mut self,
800        _: Resource<wasi::filesystem::types::Descriptor>,
801        _: String,
802    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
803        unreachable!("no filesystem")
804    }
805    fn rename_at(
806        &mut self,
807        _: Resource<wasi::filesystem::types::Descriptor>,
808        _: String,
809        _: Resource<wasi::filesystem::types::Descriptor>,
810        _: String,
811    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
812        unreachable!("no filesystem")
813    }
814    fn symlink_at(
815        &mut self,
816        _: Resource<wasi::filesystem::types::Descriptor>,
817        _: String,
818        _: String,
819    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
820        unreachable!("no filesystem")
821    }
822    fn unlink_file_at(
823        &mut self,
824        _: Resource<wasi::filesystem::types::Descriptor>,
825        _: String,
826    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
827        unreachable!("no filesystem")
828    }
829    fn is_same_object(
830        &mut self,
831        _: Resource<wasi::filesystem::types::Descriptor>,
832        _: Resource<wasi::filesystem::types::Descriptor>,
833    ) -> Result<bool> {
834        unreachable!("no filesystem")
835    }
836    fn metadata_hash(
837        &mut self,
838        _: Resource<wasi::filesystem::types::Descriptor>,
839    ) -> Result<
840        Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
841    > {
842        unreachable!("no filesystem")
843    }
844    fn metadata_hash_at(
845        &mut self,
846        _: Resource<wasi::filesystem::types::Descriptor>,
847        _: wasi::filesystem::types::PathFlags,
848        _: String,
849    ) -> Result<
850        Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
851    > {
852        unreachable!("no filesystem")
853    }
854
855    fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> {
856        unreachable!("no filesystem")
857    }
858}
859// Only place this resource can be created is with Descriptor::read_directory,
860// so this will never be constructed either.
861impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx {
862    fn read_directory_entry(
863        &mut self,
864        _: Resource<wasi::filesystem::types::DirectoryEntryStream>,
865    ) -> Result<
866        Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>,
867    > {
868        unreachable!("no filesystem")
869    }
870    fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> {
871        unreachable!("no filesystem")
872    }
873}
874
875// No stream is ever constructed from a Descriptor, there will never be a
876// valid downcast of a stream error into a filesystem error-code.
877impl wasi::filesystem::types::Host for ExampleCtx {
878    fn filesystem_error_code(
879        &mut self,
880        _: Resource<wasmtime_wasi_io::streams::Error>,
881    ) -> Result<Option<wasi::filesystem::types::ErrorCode>> {
882        Ok(None)
883    }
884}