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