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