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 core::cell::{Cell, RefCell};
30 use core::fmt::Write as _;
31 use core::future::Future;
32 use core::pin::Pin;
33 use core::task::{Context, Poll, Waker};
34 use wasmtime::component::{Component, Linker, Resource, ResourceTable};
35 use wasmtime::{Engine, Result, Store, bail};
36 use 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)]
48 pub 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 
75 fn 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 
123 fn 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.
146 wasmtime::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
166 pub 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.
175 impl IoView for ExampleCtx {
176     fn table(&mut self) -> &mut ResourceTable {
177         &mut self.table
178     }
179 }
180 
181 impl 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.
209 pub 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::filesystem::preopens::add_to_linker::<_, Data>(linker, |t| t)?;
221     wasi::filesystem::types::add_to_linker::<_, Data>(linker, |t| t)?;
222     Ok(())
223 }
224 
225 // WasiCtx and the Executor need to share a single clock, so make it reference
226 // counted.
227 #[derive(Clone)]
228 struct Clock(Rc<Cell<u64>>);
229 impl Clock {
230     fn new() -> Self {
231         Clock(Rc::new(Cell::new(0)))
232     }
233     fn get(&self) -> u64 {
234         self.0.get()
235     }
236     fn set(&self, to: u64) {
237         self.0.set(to)
238     }
239     fn timer(&self, due: u64) -> Deadline {
240         Deadline {
241             clock: self.clone(),
242             due,
243         }
244     }
245 }
246 // SAFETY: only will consume this crate in single-threaded environment
247 unsafe impl Send for Clock {}
248 unsafe impl Sync for Clock {}
249 
250 // A Deadline is used to implement the monotonic clock's pollable. It is a
251 // future which is ready when the clock reaches the due time.
252 #[derive(Clone)]
253 struct Deadline {
254     clock: Clock,
255     due: u64,
256 }
257 impl Future for Deadline {
258     type Output = ();
259     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
260         let now = self.clock.get();
261         if now < self.due {
262             Executor::current().push_deadline(self.due, cx.waker().clone());
263             Poll::Pending
264         } else {
265             Poll::Ready(())
266         }
267     }
268 }
269 #[wasmtime_wasi_io::async_trait]
270 impl Pollable for Deadline {
271     async fn ready(&mut self) {
272         self.clone().await
273     }
274 }
275 
276 // An input-stream which is never ready for reading is used to implement
277 // stdin.
278 struct NeverReadable;
279 #[wasmtime_wasi_io::async_trait]
280 impl Pollable for NeverReadable {
281     async fn ready(&mut self) {
282         struct Pending;
283         impl Future for Pending {
284             type Output = ();
285             fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
286                 Poll::Pending
287             }
288         }
289         Pending.await
290     }
291 }
292 impl InputStream for NeverReadable {
293     fn read(&mut self, _: usize) -> wasmtime_wasi_io::streams::StreamResult<Bytes> {
294         unreachable!("never ready for reading")
295     }
296 }
297 
298 // WriteLog is used implement stdout and stderr. Cloneable because wasi:cli
299 // requires, when calling get_stdout/get_stderr multiple times, to provide
300 // distinct resources that point to the same underlying stream. RefCell
301 // provides mutation, and VecDeque provides O(1) push_back operation.
302 #[derive(Clone)]
303 struct WriteLog {
304     log: Rc<RefCell<VecDeque<Bytes>>>,
305 }
306 impl WriteLog {
307     fn new() -> Self {
308         Self {
309             log: Rc::new(RefCell::new(VecDeque::new())),
310         }
311     }
312 }
313 // SAFETY: only will consume this crate in single-threaded environment
314 unsafe impl Send for WriteLog {}
315 unsafe impl Sync for WriteLog {}
316 
317 impl OutputStream for WriteLog {
318     fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> {
319         Ok(usize::MAX)
320     }
321     fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> {
322         self.log.borrow_mut().push_back(contents);
323         Ok(())
324     }
325     fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> {
326         Ok(())
327     }
328 }
329 #[wasmtime_wasi_io::async_trait]
330 impl Pollable for WriteLog {
331     async fn ready(&mut self) {
332         // always ready - return immediately.
333     }
334 }
335 
336 // Global symbol (no thread local storage on this target) provides ability for
337 // Future impls to tell the Executor what they are waiting on.
338 static EXECUTOR: ExecutorGlobal = ExecutorGlobal::new();
339 
340 // RefCell for mutation, Option so the Executor can be present only for life
341 // of the block_on call.
342 struct ExecutorGlobal(RefCell<Option<Executor>>);
343 impl ExecutorGlobal {
344     const fn new() -> Self {
345         ExecutorGlobal(RefCell::new(None))
346     }
347 }
348 // SAFETY: only will consume this crate in single-threaded environment
349 unsafe impl Send for ExecutorGlobal {}
350 unsafe impl Sync for ExecutorGlobal {}
351 
352 // Rc because executor and global both need to hold a reference, and makes it
353 // convenient to implement current(). RefCell for mutation.
354 struct Executor(Rc<RefCell<ExecutorInner>>);
355 
356 impl Executor {
357     pub fn new() -> Self {
358         Executor(Rc::new(RefCell::new(ExecutorInner {
359             schedule: Vec::new(),
360         })))
361     }
362     pub fn current() -> Self {
363         Executor(
364             EXECUTOR
365                 .0
366                 .borrow_mut()
367                 .as_ref()
368                 .expect("Executor::current must be called within block_on")
369                 .0
370                 .clone(),
371         )
372     }
373     pub fn push_deadline(&mut self, due: u64, waker: Waker) {
374         self.0.borrow_mut().schedule.push((due, waker))
375     }
376 }
377 
378 // Schedule, as provided by the Deadline future impls. Map of due times to
379 // wakers.
380 struct ExecutorInner {
381     schedule: Vec<(u64, Waker)>,
382 }
383 
384 impl ExecutorInner {
385     // Get the earliest deadline currently waiting. None if there are no
386     // deadlines.
387     fn earliest_deadline(&self) -> Option<u64> {
388         self.schedule.iter().map(|(due, _)| due).min().copied()
389     }
390     // Return all wakers associated with deadlines before or equal to the
391     // current clock time. Removes the wakers and their deadline from the
392     // schedule.
393     fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> {
394         let mut i = 0;
395         let mut wakers = Vec::new();
396         // This is basically https://doc.rust-lang.org/std/vec/struct.Vec.html#method.extract_if,
397         // which is unstable
398         while i < self.schedule.len() {
399             if let Some((due, _)) = self.schedule.get(i) {
400                 if *due <= now {
401                     let (_, waker) = self.schedule.remove(i);
402                     wakers.push(waker);
403                 } else {
404                     i += 1;
405                 }
406             } else {
407                 break;
408             }
409         }
410         wakers
411     }
412 }
413 
414 fn block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R> {
415     // Guard against nested invocations
416     if EXECUTOR.0.borrow_mut().is_some() {
417         panic!("cannot block_on while executor is running!")
418     }
419     let executor = Executor::new();
420     *EXECUTOR.0.borrow_mut() = Some(Executor(executor.0.clone()));
421 
422     // No special waker needed for this executor.
423     let mut cx = Context::from_waker(Waker::noop());
424     let mut f = core::pin::pin!(f);
425 
426     // Drive the Future to completion in the following loop
427     let r = 'outer: loop {
428         // Arbitrary. Could be as little as 1. There's no fuel-based async
429         // yielding in this example so repeated polls is probably not making
430         // progress without "providing input" from the outside environment,
431         // below.
432         const POLLS_PER_CLOCK: usize = 200;
433         for _ in 0..POLLS_PER_CLOCK {
434             match f.as_mut().poll(&mut cx) {
435                 Poll::Pending => {}
436                 Poll::Ready(r) => break 'outer r,
437             }
438         }
439 
440         // This is where a non-example executor would wait for input from the
441         // "outside world". This example checks if the schedule indicates the
442         // guest is waiting on some future deadline and fast-forwards time
443         // until then, because no other input is possible in this example.
444         if let Some(sleep_until) = executor.0.borrow().earliest_deadline() {
445             clock.set(sleep_until);
446         } else {
447             clock.set(clock.get() + 1);
448         }
449 
450         // Any wakers which are ready can be waked now.
451         for waker in executor.0.borrow_mut().ready_deadlines(clock.get()) {
452             waker.wake()
453         }
454     };
455 
456     // Clean up guard for nested invocations
457     let _ = EXECUTOR
458         .0
459         .borrow_mut()
460         .take()
461         .expect("executor vacated global while running");
462     r
463 }
464 
465 // -------------- impls for the bindgen! Host traits ------------------
466 // These impls are written directly for WasiCtx, which is fine because this
467 // example isn't trying to create reusable library code.
468 
469 impl wasi::clocks::monotonic_clock::Host for ExampleCtx {
470     fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> {
471         Ok(self.clock.get())
472     }
473     fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> {
474         Ok(1)
475     }
476     fn subscribe_duration(
477         &mut self,
478         duration: wasi::clocks::monotonic_clock::Duration,
479     ) -> Result<Resource<DynPollable>> {
480         self.subscribe_instant(self.clock.get() + duration)
481     }
482     fn subscribe_instant(
483         &mut self,
484         deadline: wasi::clocks::monotonic_clock::Instant,
485     ) -> Result<Resource<DynPollable>> {
486         let timer = self.clock.timer(deadline);
487         let deadline = self.table().push(timer)?;
488         Ok(subscribe(self.table(), deadline)?)
489     }
490 }
491 
492 impl wasi::clocks::wall_clock::Host for ExampleCtx {
493     fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
494         // A bogus time. This datetime is relative to the unix epoch. Just
495         // reuse the monotonic time for the sake of the example.
496         let now = self.clock.get();
497         let seconds = now / 1_000_000_000;
498         let nanoseconds = (now - (seconds * 1_000_000_000)) as u32;
499         Ok(wasi::clocks::wall_clock::Datetime {
500             seconds,
501             nanoseconds,
502         })
503     }
504     fn resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
505         Ok(wasi::clocks::wall_clock::Datetime {
506             seconds: 0,
507             nanoseconds: 1,
508         })
509     }
510 }
511 
512 // No arguments, environment variables, or cwd are provided.
513 impl wasi::cli::environment::Host for ExampleCtx {
514     fn get_arguments(&mut self) -> Result<Vec<String>> {
515         Ok(Vec::new())
516     }
517     fn get_environment(&mut self) -> Result<Vec<(String, String)>> {
518         Ok(Vec::new())
519     }
520     fn initial_cwd(&mut self) -> Result<Option<String>> {
521         Ok(None)
522     }
523 }
524 
525 // Ideally this would follow the example in wasmtime-wasi: make a struct, impl
526 // Error on it, and try downcasting to it at the call_run site to see if the
527 // wasi:cli/exit was used to exit with success without unwinding - valid but
528 // uncommon behavior that should be treated the same as returning ok from the
529 // wasi:cli/run.run function. Our example program doesn't exit that way.
530 impl wasi::cli::exit::Host for ExampleCtx {
531     fn exit(&mut self, code: Result<(), ()>) -> Result<()> {
532         if code.is_ok() {
533             bail!("wasi exit success")
534         } else {
535             bail!("wasi exit error")
536         }
537     }
538     // This is feature-flagged (unstable) in the wits. Per the LinkOptions
539     // passed to the wasi::cli::exit::add_to_linker, it won't be found in
540     // any guest code.
541     fn exit_with_code(&mut self, _: u8) -> Result<()> {
542         unreachable!("this unstable func is not added to the linker");
543     }
544 }
545 
546 impl wasi::cli::stdin::Host for ExampleCtx {
547     fn get_stdin(&mut self) -> Result<Resource<DynInputStream>> {
548         let stdin: DynInputStream = Box::new(NeverReadable);
549         Ok(self.table().push(stdin)?)
550     }
551 }
552 
553 impl wasi::cli::stdout::Host for ExampleCtx {
554     fn get_stdout(&mut self) -> Result<Resource<DynOutputStream>> {
555         let stdout: DynOutputStream = Box::new(self.stdout.clone());
556         Ok(self.table().push(stdout)?)
557     }
558 }
559 
560 impl wasi::cli::stderr::Host for ExampleCtx {
561     fn get_stderr(&mut self) -> Result<Resource<DynOutputStream>> {
562         let stderr: DynOutputStream = Box::new(self.stderr.clone());
563         Ok(self.table().push(stderr)?)
564     }
565 }
566 
567 // This is obviously bogus and breaks the guarantees given by this interface.
568 // In a real embedding, provide a high quality source of randomness here.
569 impl wasi::random::random::Host for ExampleCtx {
570     fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> {
571         let mut vec = Vec::new();
572         vec.resize(len as usize, 0u8);
573         Ok(vec)
574     }
575     fn get_random_u64(&mut self) -> Result<u64> {
576         Ok(0)
577     }
578 }
579 
580 // The preopens are the only place the filesystem is provided a Descriptor,
581 // from which to try open_at to get more Descriptors. If we don't provide
582 // anything here, none of the methods on Descriptor will ever be reachable,
583 // because Resources are unforgable (the runtime will trap bogus indexes).
584 impl wasi::filesystem::preopens::Host for ExampleCtx {
585     fn get_directories(
586         &mut self,
587     ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> {
588         // Never construct a Descriptor, so all of the bails in the rest of Filesystem should be
589         // unreachable.
590         Ok(Vec::new())
591     }
592 }
593 
594 // This impl is completely empty!
595 impl wasi::filesystem::types::HostDescriptor for ExampleCtx {
596     fn read_via_stream(
597         &mut self,
598         _: Resource<wasi::filesystem::types::Descriptor>,
599         _: u64,
600     ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> {
601         unreachable!("no filesystem")
602     }
603     fn write_via_stream(
604         &mut self,
605         _: Resource<wasi::filesystem::types::Descriptor>,
606         _: u64,
607     ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
608         unreachable!("no filesystem")
609     }
610     fn append_via_stream(
611         &mut self,
612         _: Resource<wasi::filesystem::types::Descriptor>,
613     ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
614         unreachable!("no filesystem")
615     }
616     fn advise(
617         &mut self,
618         _: Resource<wasi::filesystem::types::Descriptor>,
619         _: u64,
620         _: u64,
621         _: wasi::filesystem::types::Advice,
622     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
623         unreachable!("no filesystem")
624     }
625     fn sync_data(
626         &mut self,
627         _: Resource<wasi::filesystem::types::Descriptor>,
628     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
629         unreachable!("no filesystem")
630     }
631     fn get_flags(
632         &mut self,
633         _: Resource<wasi::filesystem::types::Descriptor>,
634     ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>>
635     {
636         unreachable!("no filesystem")
637     }
638     fn get_type(
639         &mut self,
640         _: Resource<wasi::filesystem::types::Descriptor>,
641     ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>>
642     {
643         unreachable!("no filesystem")
644     }
645     fn set_size(
646         &mut self,
647         _: Resource<wasi::filesystem::types::Descriptor>,
648         _: u64,
649     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
650         unreachable!("no filesystem")
651     }
652     fn set_times(
653         &mut self,
654         _: Resource<wasi::filesystem::types::Descriptor>,
655         _: wasi::filesystem::types::NewTimestamp,
656         _: wasi::filesystem::types::NewTimestamp,
657     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
658         unreachable!("no filesystem")
659     }
660     fn read(
661         &mut self,
662         _: Resource<wasi::filesystem::types::Descriptor>,
663         _: u64,
664         _: u64,
665     ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> {
666         unreachable!("no filesystem")
667     }
668     fn write(
669         &mut self,
670         _: Resource<wasi::filesystem::types::Descriptor>,
671         _: Vec<u8>,
672         _: u64,
673     ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> {
674         unreachable!("no filesystem")
675     }
676 
677     fn read_directory(
678         &mut self,
679         _: Resource<wasi::filesystem::types::Descriptor>,
680     ) -> Result<
681         Result<
682             Resource<wasi::filesystem::types::DirectoryEntryStream>,
683             wasi::filesystem::types::ErrorCode,
684         >,
685     > {
686         unreachable!("no filesystem")
687     }
688     fn sync(
689         &mut self,
690         _: Resource<wasi::filesystem::types::Descriptor>,
691     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
692         unreachable!("no filesystem")
693     }
694     fn create_directory_at(
695         &mut self,
696         _: Resource<wasi::filesystem::types::Descriptor>,
697         _: String,
698     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
699         unreachable!("no filesystem")
700     }
701     fn stat(
702         &mut self,
703         _: Resource<wasi::filesystem::types::Descriptor>,
704     ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
705     {
706         unreachable!("no filesystem")
707     }
708     fn stat_at(
709         &mut self,
710         _: Resource<wasi::filesystem::types::Descriptor>,
711         _: wasi::filesystem::types::PathFlags,
712         _: String,
713     ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
714     {
715         unreachable!("no filesystem")
716     }
717     fn set_times_at(
718         &mut self,
719         _: Resource<wasi::filesystem::types::Descriptor>,
720         _: wasi::filesystem::types::PathFlags,
721         _: String,
722         _: wasi::filesystem::types::NewTimestamp,
723         _: wasi::filesystem::types::NewTimestamp,
724     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
725         unreachable!("no filesystem")
726     }
727     fn link_at(
728         &mut self,
729         _: Resource<wasi::filesystem::types::Descriptor>,
730         _: wasi::filesystem::types::PathFlags,
731         _: String,
732         _: Resource<wasi::filesystem::types::Descriptor>,
733         _: String,
734     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
735         unreachable!("no filesystem")
736     }
737     fn open_at(
738         &mut self,
739         _: Resource<wasi::filesystem::types::Descriptor>,
740         _: wasi::filesystem::types::PathFlags,
741         _: String,
742         _: wasi::filesystem::types::OpenFlags,
743         _: wasi::filesystem::types::DescriptorFlags,
744     ) -> Result<
745         Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>,
746     > {
747         unreachable!("no filesystem")
748     }
749     fn readlink_at(
750         &mut self,
751         _: Resource<wasi::filesystem::types::Descriptor>,
752         _: String,
753     ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> {
754         unreachable!("no filesystem")
755     }
756     fn remove_directory_at(
757         &mut self,
758         _: Resource<wasi::filesystem::types::Descriptor>,
759         _: String,
760     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
761         unreachable!("no filesystem")
762     }
763     fn rename_at(
764         &mut self,
765         _: Resource<wasi::filesystem::types::Descriptor>,
766         _: String,
767         _: Resource<wasi::filesystem::types::Descriptor>,
768         _: String,
769     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
770         unreachable!("no filesystem")
771     }
772     fn symlink_at(
773         &mut self,
774         _: Resource<wasi::filesystem::types::Descriptor>,
775         _: String,
776         _: String,
777     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
778         unreachable!("no filesystem")
779     }
780     fn unlink_file_at(
781         &mut self,
782         _: Resource<wasi::filesystem::types::Descriptor>,
783         _: String,
784     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
785         unreachable!("no filesystem")
786     }
787     fn is_same_object(
788         &mut self,
789         _: Resource<wasi::filesystem::types::Descriptor>,
790         _: Resource<wasi::filesystem::types::Descriptor>,
791     ) -> Result<bool> {
792         unreachable!("no filesystem")
793     }
794     fn metadata_hash(
795         &mut self,
796         _: Resource<wasi::filesystem::types::Descriptor>,
797     ) -> Result<
798         Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
799     > {
800         unreachable!("no filesystem")
801     }
802     fn metadata_hash_at(
803         &mut self,
804         _: Resource<wasi::filesystem::types::Descriptor>,
805         _: wasi::filesystem::types::PathFlags,
806         _: String,
807     ) -> Result<
808         Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
809     > {
810         unreachable!("no filesystem")
811     }
812 
813     fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> {
814         unreachable!("no filesystem")
815     }
816 }
817 // Only place this resource can be created is with Descriptor::read_directory,
818 // so this will never be constructed either.
819 impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx {
820     fn read_directory_entry(
821         &mut self,
822         _: Resource<wasi::filesystem::types::DirectoryEntryStream>,
823     ) -> Result<
824         Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>,
825     > {
826         unreachable!("no filesystem")
827     }
828     fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> {
829         unreachable!("no filesystem")
830     }
831 }
832 
833 // No stream is ever constructed from a Descriptor, there will never be a
834 // valid downcast of a stream error into a filesystem error-code.
835 impl wasi::filesystem::types::Host for ExampleCtx {
836     fn filesystem_error_code(
837         &mut self,
838         _: Resource<wasmtime_wasi_io::streams::Error>,
839     ) -> Result<Option<wasi::filesystem::types::ErrorCode>> {
840         Ok(None)
841     }
842 }
843