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)]
run_wasi( out_buf: *mut u8, out_size: *mut usize, wasi_component: *const u8, wasi_component_size: usize, ) -> usize48 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 
run(wasi_component: &[u8]) -> Result<String>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 
deserialize(engine: &Engine, component: &[u8]) -> Result<Option<Component>>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 {
table(&mut self) -> &mut ResourceTable176     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.
output(&self) -> Result<String>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.
add_to_linker_async(linker: &mut Linker<ExampleCtx>) -> Result<()>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::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)]
233 struct Clock(Rc<Cell<u64>>);
234 impl Clock {
new() -> Self235     fn new() -> Self {
236         Clock(Rc::new(Cell::new(0)))
237     }
get(&self) -> u64238     fn get(&self) -> u64 {
239         self.0.get()
240     }
set(&self, to: u64)241     fn set(&self, to: u64) {
242         self.0.set(to)
243     }
timer(&self, due: u64) -> Deadline244     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
252 unsafe impl Send for Clock {}
253 unsafe 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)]
258 struct Deadline {
259     clock: Clock,
260     due: u64,
261 }
262 impl Future for Deadline {
263     type Output = ();
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::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]
275 impl Pollable for Deadline {
ready(&mut self)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.
283 struct NeverReadable;
284 #[wasmtime_wasi_io::async_trait]
285 impl Pollable for NeverReadable {
ready(&mut self)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 }
297 impl InputStream for NeverReadable {
read(&mut self, _: usize) -> wasmtime_wasi_io::streams::StreamResult<Bytes>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)]
308 struct WriteLog {
309     log: Rc<RefCell<VecDeque<Bytes>>>,
310 }
311 impl WriteLog {
new() -> Self312     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
319 unsafe impl Send for WriteLog {}
320 unsafe impl Sync for WriteLog {}
321 
322 impl OutputStream for WriteLog {
check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize>323     fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> {
324         Ok(usize::MAX)
325     }
write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()>326     fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> {
327         self.log.borrow_mut().push_back(contents);
328         Ok(())
329     }
flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()>330     fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> {
331         Ok(())
332     }
333 }
334 #[wasmtime_wasi_io::async_trait]
335 impl Pollable for WriteLog {
ready(&mut self)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.
343 static 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.
347 struct ExecutorGlobal(RefCell<Option<Executor>>);
348 impl ExecutorGlobal {
new() -> Self349     const fn new() -> Self {
350         ExecutorGlobal(RefCell::new(None))
351     }
352 }
353 // SAFETY: only will consume this crate in single-threaded environment
354 unsafe impl Send for ExecutorGlobal {}
355 unsafe 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.
359 struct Executor(Rc<RefCell<ExecutorInner>>);
360 
361 impl Executor {
new() -> Self362     pub fn new() -> Self {
363         Executor(Rc::new(RefCell::new(ExecutorInner {
364             schedule: Vec::new(),
365         })))
366     }
current() -> Self367     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     }
push_deadline(&mut self, due: u64, waker: Waker)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.
385 struct ExecutorInner {
386     schedule: Vec<(u64, Waker)>,
387 }
388 
389 impl ExecutorInner {
390     // Get the earliest deadline currently waiting. None if there are no
391     // deadlines.
earliest_deadline(&self) -> Option<u64>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.
ready_deadlines(&mut self, now: u64) -> Vec<Waker>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 
block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R>419 fn 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 
474 impl wasi::clocks::monotonic_clock::Host for ExampleCtx {
now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant>475     fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> {
476         Ok(self.clock.get())
477     }
resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration>478     fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> {
479         Ok(1)
480     }
subscribe_duration( &mut self, duration: wasi::clocks::monotonic_clock::Duration, ) -> Result<Resource<DynPollable>>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     }
subscribe_instant( &mut self, deadline: wasi::clocks::monotonic_clock::Instant, ) -> Result<Resource<DynPollable>>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 
497 impl wasi::clocks::wall_clock::Host for ExampleCtx {
now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime>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     }
resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime>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.
518 impl wasi::cli::environment::Host for ExampleCtx {
get_arguments(&mut self) -> Result<Vec<String>>519     fn get_arguments(&mut self) -> Result<Vec<String>> {
520         Ok(Vec::new())
521     }
get_environment(&mut self) -> Result<Vec<(String, String)>>522     fn get_environment(&mut self) -> Result<Vec<(String, String)>> {
523         Ok(Vec::new())
524     }
initial_cwd(&mut self) -> Result<Option<String>>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.
535 impl wasi::cli::exit::Host for ExampleCtx {
exit(&mut self, code: Result<(), ()>) -> Result<()>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.
exit_with_code(&mut self, _: u8) -> Result<()>546     fn exit_with_code(&mut self, _: u8) -> Result<()> {
547         unreachable!("this unstable func is not added to the linker");
548     }
549 }
550 
551 impl wasi::cli::stdin::Host for ExampleCtx {
get_stdin(&mut self) -> Result<Resource<DynInputStream>>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 
558 impl wasi::cli::stdout::Host for ExampleCtx {
get_stdout(&mut self) -> Result<Resource<DynOutputStream>>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 
565 impl wasi::cli::stderr::Host for ExampleCtx {
get_stderr(&mut self) -> Result<Resource<DynOutputStream>>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.
573 impl wasi::cli::terminal_input::Host for ExampleCtx {}
574 impl wasi::cli::terminal_input::HostTerminalInput for ExampleCtx {
drop(&mut self, r: Resource<wasi::cli::terminal_input::TerminalInput>) -> Result<()>575     fn drop(&mut self, r: Resource<wasi::cli::terminal_input::TerminalInput>) -> Result<()> {
576         self.table.delete(r)?;
577         Ok(())
578     }
579 }
580 impl wasi::cli::terminal_output::Host for ExampleCtx {}
581 impl wasi::cli::terminal_output::HostTerminalOutput for ExampleCtx {
drop(&mut self, r: Resource<wasi::cli::terminal_output::TerminalOutput>) -> Result<()>582     fn drop(&mut self, r: Resource<wasi::cli::terminal_output::TerminalOutput>) -> Result<()> {
583         self.table.delete(r)?;
584         Ok(())
585     }
586 }
587 impl wasi::cli::terminal_stdin::Host for ExampleCtx {
get_terminal_stdin( &mut self, ) -> Result<Option<Resource<wasi::cli::terminal_input::TerminalInput>>>588     fn get_terminal_stdin(
589         &mut self,
590     ) -> Result<Option<Resource<wasi::cli::terminal_input::TerminalInput>>> {
591         Ok(None)
592     }
593 }
594 impl wasi::cli::terminal_stdout::Host for ExampleCtx {
get_terminal_stdout( &mut self, ) -> Result<Option<Resource<wasi::cli::terminal_output::TerminalOutput>>>595     fn get_terminal_stdout(
596         &mut self,
597     ) -> Result<Option<Resource<wasi::cli::terminal_output::TerminalOutput>>> {
598         Ok(None)
599     }
600 }
601 impl wasi::cli::terminal_stderr::Host for ExampleCtx {
get_terminal_stderr( &mut self, ) -> Result<Option<Resource<wasi::cli::terminal_output::TerminalOutput>>>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.
611 impl wasi::random::random::Host for ExampleCtx {
get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>>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     }
get_random_u64(&mut self) -> Result<u64>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).
626 impl wasi::filesystem::preopens::Host for ExampleCtx {
get_directories( &mut self, ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>>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!
637 impl wasi::filesystem::types::HostDescriptor for ExampleCtx {
read_via_stream( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: u64, ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>>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     }
write_via_stream( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: u64, ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>>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     }
append_via_stream( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>>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     }
advise( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: u64, _: u64, _: wasi::filesystem::types::Advice, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
sync_data( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
get_flags( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>>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     }
get_type( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>>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     }
set_size( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: u64, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
set_times( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: wasi::filesystem::types::NewTimestamp, _: wasi::filesystem::types::NewTimestamp, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
read( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: u64, _: u64, ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>>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     }
write( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: Vec<u8>, _: u64, ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>>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 
read_directory( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result< Result< Resource<wasi::filesystem::types::DirectoryEntryStream>, wasi::filesystem::types::ErrorCode, >, >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     }
sync( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>730     fn sync(
731         &mut self,
732         _: Resource<wasi::filesystem::types::Descriptor>,
733     ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
734         unreachable!("no filesystem")
735     }
create_directory_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: String, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
stat( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>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     }
stat_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: wasi::filesystem::types::PathFlags, _: String, ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>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     }
set_times_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: wasi::filesystem::types::PathFlags, _: String, _: wasi::filesystem::types::NewTimestamp, _: wasi::filesystem::types::NewTimestamp, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
link_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: wasi::filesystem::types::PathFlags, _: String, _: Resource<wasi::filesystem::types::Descriptor>, _: String, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
open_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: wasi::filesystem::types::PathFlags, _: String, _: wasi::filesystem::types::OpenFlags, _: wasi::filesystem::types::DescriptorFlags, ) -> Result< Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>, >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     }
readlink_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: String, ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>>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     }
remove_directory_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: String, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
rename_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: String, _: Resource<wasi::filesystem::types::Descriptor>, _: String, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
symlink_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: String, _: String, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
unlink_file_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: String, ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>>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     }
is_same_object( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result<bool>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     }
metadata_hash( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, ) -> Result< Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>, >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     }
metadata_hash_at( &mut self, _: Resource<wasi::filesystem::types::Descriptor>, _: wasi::filesystem::types::PathFlags, _: String, ) -> Result< Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>, >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 
drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()>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.
861 impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx {
read_directory_entry( &mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>, ) -> Result< Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>, >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     }
drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()>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.
877 impl wasi::filesystem::types::Host for ExampleCtx {
filesystem_error_code( &mut self, _: Resource<wasmtime_wasi_io::streams::Error>, ) -> Result<Option<wasi::filesystem::types::ErrorCode>>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 }
885