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::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 { 235 fn new() -> Self { 236 Clock(Rc::new(Cell::new(0))) 237 } 238 fn get(&self) -> u64 { 239 self.0.get() 240 } 241 fn set(&self, to: u64) { 242 self.0.set(to) 243 } 244 fn timer(&self, due: u64) -> Deadline { 245 Deadline { 246 clock: self.clone(), 247 due, 248 } 249 } 250 } 251 // SAFETY: only will consume this crate in single-threaded environment 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 = (); 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 { 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 { 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 { 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 { 312 fn new() -> Self { 313 Self { 314 log: Rc::new(RefCell::new(VecDeque::new())), 315 } 316 } 317 } 318 // SAFETY: only will consume this crate in single-threaded environment 319 unsafe impl Send for WriteLog {} 320 unsafe impl Sync for WriteLog {} 321 322 impl OutputStream for WriteLog { 323 fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> { 324 Ok(usize::MAX) 325 } 326 fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> { 327 self.log.borrow_mut().push_back(contents); 328 Ok(()) 329 } 330 fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> { 331 Ok(()) 332 } 333 } 334 #[wasmtime_wasi_io::async_trait] 335 impl Pollable for WriteLog { 336 async fn ready(&mut self) { 337 // always ready - return immediately. 338 } 339 } 340 341 // Global symbol (no thread local storage on this target) provides ability for 342 // Future impls to tell the Executor what they are waiting on. 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 { 349 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 { 362 pub fn new() -> Self { 363 Executor(Rc::new(RefCell::new(ExecutorInner { 364 schedule: Vec::new(), 365 }))) 366 } 367 pub fn current() -> Self { 368 Executor( 369 EXECUTOR 370 .0 371 .borrow_mut() 372 .as_ref() 373 .expect("Executor::current must be called within block_on") 374 .0 375 .clone(), 376 ) 377 } 378 pub fn push_deadline(&mut self, due: u64, waker: Waker) { 379 self.0.borrow_mut().schedule.push((due, waker)) 380 } 381 } 382 383 // Schedule, as provided by the Deadline future impls. Map of due times to 384 // wakers. 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. 392 fn earliest_deadline(&self) -> Option<u64> { 393 self.schedule.iter().map(|(due, _)| due).min().copied() 394 } 395 // Return all wakers associated with deadlines before or equal to the 396 // current clock time. Removes the wakers and their deadline from the 397 // schedule. 398 fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> { 399 let mut i = 0; 400 let mut wakers = Vec::new(); 401 // This is basically https://doc.rust-lang.org/std/vec/struct.Vec.html#method.extract_if, 402 // which is unstable 403 while i < self.schedule.len() { 404 if let Some((due, _)) = self.schedule.get(i) { 405 if *due <= now { 406 let (_, waker) = self.schedule.remove(i); 407 wakers.push(waker); 408 } else { 409 i += 1; 410 } 411 } else { 412 break; 413 } 414 } 415 wakers 416 } 417 } 418 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 { 475 fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> { 476 Ok(self.clock.get()) 477 } 478 fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> { 479 Ok(1) 480 } 481 fn subscribe_duration( 482 &mut self, 483 duration: wasi::clocks::monotonic_clock::Duration, 484 ) -> Result<Resource<DynPollable>> { 485 self.subscribe_instant(self.clock.get() + duration) 486 } 487 fn subscribe_instant( 488 &mut self, 489 deadline: wasi::clocks::monotonic_clock::Instant, 490 ) -> Result<Resource<DynPollable>> { 491 let timer = self.clock.timer(deadline); 492 let deadline = self.table().push(timer)?; 493 Ok(subscribe(self.table(), deadline)?) 494 } 495 } 496 497 impl wasi::clocks::wall_clock::Host for ExampleCtx { 498 fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> { 499 // A bogus time. This datetime is relative to the unix epoch. Just 500 // reuse the monotonic time for the sake of the example. 501 let now = self.clock.get(); 502 let seconds = now / 1_000_000_000; 503 let nanoseconds = (now - (seconds * 1_000_000_000)) as u32; 504 Ok(wasi::clocks::wall_clock::Datetime { 505 seconds, 506 nanoseconds, 507 }) 508 } 509 fn resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> { 510 Ok(wasi::clocks::wall_clock::Datetime { 511 seconds: 0, 512 nanoseconds: 1, 513 }) 514 } 515 } 516 517 // No arguments, environment variables, or cwd are provided. 518 impl wasi::cli::environment::Host for ExampleCtx { 519 fn get_arguments(&mut self) -> Result<Vec<String>> { 520 Ok(Vec::new()) 521 } 522 fn get_environment(&mut self) -> Result<Vec<(String, String)>> { 523 Ok(Vec::new()) 524 } 525 fn initial_cwd(&mut self) -> Result<Option<String>> { 526 Ok(None) 527 } 528 } 529 530 // Ideally this would follow the example in wasmtime-wasi: make a struct, impl 531 // Error on it, and try downcasting to it at the call_run site to see if the 532 // wasi:cli/exit was used to exit with success without unwinding - valid but 533 // uncommon behavior that should be treated the same as returning ok from the 534 // wasi:cli/run.run function. Our example program doesn't exit that way. 535 impl wasi::cli::exit::Host for ExampleCtx { 536 fn exit(&mut self, code: Result<(), ()>) -> Result<()> { 537 if code.is_ok() { 538 bail!("wasi exit success") 539 } else { 540 bail!("wasi exit error") 541 } 542 } 543 // This is feature-flagged (unstable) in the wits. Per the LinkOptions 544 // passed to the wasi::cli::exit::add_to_linker, it won't be found in 545 // any guest code. 546 fn exit_with_code(&mut self, _: u8) -> Result<()> { 547 unreachable!("this unstable func is not added to the linker"); 548 } 549 } 550 551 impl wasi::cli::stdin::Host for ExampleCtx { 552 fn get_stdin(&mut self) -> Result<Resource<DynInputStream>> { 553 let stdin: DynInputStream = Box::new(NeverReadable); 554 Ok(self.table().push(stdin)?) 555 } 556 } 557 558 impl wasi::cli::stdout::Host for ExampleCtx { 559 fn get_stdout(&mut self) -> Result<Resource<DynOutputStream>> { 560 let stdout: DynOutputStream = Box::new(self.stdout.clone()); 561 Ok(self.table().push(stdout)?) 562 } 563 } 564 565 impl wasi::cli::stderr::Host for ExampleCtx { 566 fn get_stderr(&mut self) -> Result<Resource<DynOutputStream>> { 567 let stderr: DynOutputStream = Box::new(self.stderr.clone()); 568 Ok(self.table().push(stderr)?) 569 } 570 } 571 572 // Terminal resources: nothing is a terminal in this minimal embedding. 573 impl wasi::cli::terminal_input::Host for ExampleCtx {} 574 impl wasi::cli::terminal_input::HostTerminalInput for ExampleCtx { 575 fn drop(&mut self, r: Resource<wasi::cli::terminal_input::TerminalInput>) -> Result<()> { 576 self.table.delete(r)?; 577 Ok(()) 578 } 579 } 580 impl wasi::cli::terminal_output::Host for ExampleCtx {} 581 impl wasi::cli::terminal_output::HostTerminalOutput for ExampleCtx { 582 fn drop(&mut self, r: Resource<wasi::cli::terminal_output::TerminalOutput>) -> Result<()> { 583 self.table.delete(r)?; 584 Ok(()) 585 } 586 } 587 impl wasi::cli::terminal_stdin::Host for ExampleCtx { 588 fn get_terminal_stdin( 589 &mut self, 590 ) -> Result<Option<Resource<wasi::cli::terminal_input::TerminalInput>>> { 591 Ok(None) 592 } 593 } 594 impl wasi::cli::terminal_stdout::Host for ExampleCtx { 595 fn get_terminal_stdout( 596 &mut self, 597 ) -> Result<Option<Resource<wasi::cli::terminal_output::TerminalOutput>>> { 598 Ok(None) 599 } 600 } 601 impl wasi::cli::terminal_stderr::Host for ExampleCtx { 602 fn get_terminal_stderr( 603 &mut self, 604 ) -> Result<Option<Resource<wasi::cli::terminal_output::TerminalOutput>>> { 605 Ok(None) 606 } 607 } 608 609 // This is obviously bogus and breaks the guarantees given by this interface. 610 // In a real embedding, provide a high quality source of randomness here. 611 impl wasi::random::random::Host for ExampleCtx { 612 fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> { 613 let mut vec = Vec::new(); 614 vec.resize(len as usize, 0u8); 615 Ok(vec) 616 } 617 fn get_random_u64(&mut self) -> Result<u64> { 618 Ok(0) 619 } 620 } 621 622 // The preopens are the only place the filesystem is provided a Descriptor, 623 // from which to try open_at to get more Descriptors. If we don't provide 624 // anything here, none of the methods on Descriptor will ever be reachable, 625 // because Resources are unforgable (the runtime will trap bogus indexes). 626 impl wasi::filesystem::preopens::Host for ExampleCtx { 627 fn get_directories( 628 &mut self, 629 ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> { 630 // Never construct a Descriptor, so all of the bails in the rest of Filesystem should be 631 // unreachable. 632 Ok(Vec::new()) 633 } 634 } 635 636 // This impl is completely empty! 637 impl wasi::filesystem::types::HostDescriptor for ExampleCtx { 638 fn read_via_stream( 639 &mut self, 640 _: Resource<wasi::filesystem::types::Descriptor>, 641 _: u64, 642 ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> { 643 unreachable!("no filesystem") 644 } 645 fn write_via_stream( 646 &mut self, 647 _: Resource<wasi::filesystem::types::Descriptor>, 648 _: u64, 649 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> { 650 unreachable!("no filesystem") 651 } 652 fn append_via_stream( 653 &mut self, 654 _: Resource<wasi::filesystem::types::Descriptor>, 655 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> { 656 unreachable!("no filesystem") 657 } 658 fn advise( 659 &mut self, 660 _: Resource<wasi::filesystem::types::Descriptor>, 661 _: u64, 662 _: u64, 663 _: wasi::filesystem::types::Advice, 664 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 665 unreachable!("no filesystem") 666 } 667 fn sync_data( 668 &mut self, 669 _: Resource<wasi::filesystem::types::Descriptor>, 670 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 671 unreachable!("no filesystem") 672 } 673 fn get_flags( 674 &mut self, 675 _: Resource<wasi::filesystem::types::Descriptor>, 676 ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>> 677 { 678 unreachable!("no filesystem") 679 } 680 fn get_type( 681 &mut self, 682 _: Resource<wasi::filesystem::types::Descriptor>, 683 ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>> 684 { 685 unreachable!("no filesystem") 686 } 687 fn set_size( 688 &mut self, 689 _: Resource<wasi::filesystem::types::Descriptor>, 690 _: u64, 691 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 692 unreachable!("no filesystem") 693 } 694 fn set_times( 695 &mut self, 696 _: Resource<wasi::filesystem::types::Descriptor>, 697 _: wasi::filesystem::types::NewTimestamp, 698 _: wasi::filesystem::types::NewTimestamp, 699 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 700 unreachable!("no filesystem") 701 } 702 fn read( 703 &mut self, 704 _: Resource<wasi::filesystem::types::Descriptor>, 705 _: u64, 706 _: u64, 707 ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> { 708 unreachable!("no filesystem") 709 } 710 fn write( 711 &mut self, 712 _: Resource<wasi::filesystem::types::Descriptor>, 713 _: Vec<u8>, 714 _: u64, 715 ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> { 716 unreachable!("no filesystem") 717 } 718 719 fn read_directory( 720 &mut self, 721 _: Resource<wasi::filesystem::types::Descriptor>, 722 ) -> Result< 723 Result< 724 Resource<wasi::filesystem::types::DirectoryEntryStream>, 725 wasi::filesystem::types::ErrorCode, 726 >, 727 > { 728 unreachable!("no filesystem") 729 } 730 fn sync( 731 &mut self, 732 _: Resource<wasi::filesystem::types::Descriptor>, 733 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 734 unreachable!("no filesystem") 735 } 736 fn create_directory_at( 737 &mut self, 738 _: Resource<wasi::filesystem::types::Descriptor>, 739 _: String, 740 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 741 unreachable!("no filesystem") 742 } 743 fn stat( 744 &mut self, 745 _: Resource<wasi::filesystem::types::Descriptor>, 746 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>> 747 { 748 unreachable!("no filesystem") 749 } 750 fn stat_at( 751 &mut self, 752 _: Resource<wasi::filesystem::types::Descriptor>, 753 _: wasi::filesystem::types::PathFlags, 754 _: String, 755 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>> 756 { 757 unreachable!("no filesystem") 758 } 759 fn set_times_at( 760 &mut self, 761 _: Resource<wasi::filesystem::types::Descriptor>, 762 _: wasi::filesystem::types::PathFlags, 763 _: String, 764 _: wasi::filesystem::types::NewTimestamp, 765 _: wasi::filesystem::types::NewTimestamp, 766 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 767 unreachable!("no filesystem") 768 } 769 fn link_at( 770 &mut self, 771 _: Resource<wasi::filesystem::types::Descriptor>, 772 _: wasi::filesystem::types::PathFlags, 773 _: String, 774 _: Resource<wasi::filesystem::types::Descriptor>, 775 _: String, 776 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 777 unreachable!("no filesystem") 778 } 779 fn open_at( 780 &mut self, 781 _: Resource<wasi::filesystem::types::Descriptor>, 782 _: wasi::filesystem::types::PathFlags, 783 _: String, 784 _: wasi::filesystem::types::OpenFlags, 785 _: wasi::filesystem::types::DescriptorFlags, 786 ) -> Result< 787 Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>, 788 > { 789 unreachable!("no filesystem") 790 } 791 fn readlink_at( 792 &mut self, 793 _: Resource<wasi::filesystem::types::Descriptor>, 794 _: String, 795 ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> { 796 unreachable!("no filesystem") 797 } 798 fn remove_directory_at( 799 &mut self, 800 _: Resource<wasi::filesystem::types::Descriptor>, 801 _: String, 802 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 803 unreachable!("no filesystem") 804 } 805 fn rename_at( 806 &mut self, 807 _: Resource<wasi::filesystem::types::Descriptor>, 808 _: String, 809 _: Resource<wasi::filesystem::types::Descriptor>, 810 _: String, 811 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 812 unreachable!("no filesystem") 813 } 814 fn symlink_at( 815 &mut self, 816 _: Resource<wasi::filesystem::types::Descriptor>, 817 _: String, 818 _: String, 819 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 820 unreachable!("no filesystem") 821 } 822 fn unlink_file_at( 823 &mut self, 824 _: Resource<wasi::filesystem::types::Descriptor>, 825 _: String, 826 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 827 unreachable!("no filesystem") 828 } 829 fn is_same_object( 830 &mut self, 831 _: Resource<wasi::filesystem::types::Descriptor>, 832 _: Resource<wasi::filesystem::types::Descriptor>, 833 ) -> Result<bool> { 834 unreachable!("no filesystem") 835 } 836 fn metadata_hash( 837 &mut self, 838 _: Resource<wasi::filesystem::types::Descriptor>, 839 ) -> Result< 840 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>, 841 > { 842 unreachable!("no filesystem") 843 } 844 fn metadata_hash_at( 845 &mut self, 846 _: Resource<wasi::filesystem::types::Descriptor>, 847 _: wasi::filesystem::types::PathFlags, 848 _: String, 849 ) -> Result< 850 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>, 851 > { 852 unreachable!("no filesystem") 853 } 854 855 fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> { 856 unreachable!("no filesystem") 857 } 858 } 859 // Only place this resource can be created is with Descriptor::read_directory, 860 // so this will never be constructed either. 861 impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx { 862 fn read_directory_entry( 863 &mut self, 864 _: Resource<wasi::filesystem::types::DirectoryEntryStream>, 865 ) -> Result< 866 Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>, 867 > { 868 unreachable!("no filesystem") 869 } 870 fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> { 871 unreachable!("no filesystem") 872 } 873 } 874 875 // No stream is ever constructed from a Descriptor, there will never be a 876 // valid downcast of a stream error into a filesystem error-code. 877 impl wasi::filesystem::types::Host for ExampleCtx { 878 fn filesystem_error_code( 879 &mut self, 880 _: Resource<wasmtime_wasi_io::streams::Error>, 881 ) -> Result<Option<wasi::filesystem::types::ErrorCode>> { 882 Ok(None) 883 } 884 } 885