1 //! This example demonstrates how wasmtime-wasi-io can be used in a #![no_std] 2 //! target as the basis for a WASI implementation. 3 //! 4 //! This example can execute a wasi:cli/command component on a custom async 5 //! executor with no dependencies on the environment: execution is 6 //! deterministic, and no sources of input are provided to the component. The 7 //! WASI implementation is deliberately limited and incomplete, and many WASI 8 //! components will not even instantiate, or execute correctly, because this 9 //! is not a fully fleshed-out example. 10 //! 11 //! The wasmtime-wasi implementation of WASI depends on the tokio executor, 12 //! cap-std family of crates, and others to provide a complete implementation 13 //! of WASI p2 on top of Unix-based and Windows operating systems. It would be 14 //! difficult and/or inappropriate to port to other settings. This example 15 //! might be a good starting point for how to go about rolling your own WASI 16 //! implementation that is particular to your own execution environment. 17 //! 18 //! The wasmtime-wasi-io crate, which is a key part of this example, provides 19 //! an implementation of the wasi:io package, which is the foundation of 20 //! WASIp2. wasmtime-wasi-io provides the Pollable, InputStream, and 21 //! OutputStream traits, and this example shows implementations of those 22 //! traits for this particular embedding. 23 24 use alloc::boxed::Box; 25 use alloc::collections::VecDeque; 26 use alloc::rc::Rc; 27 use alloc::string::{String, ToString}; 28 use alloc::vec::Vec; 29 use anyhow::{Result, bail}; 30 use core::cell::{Cell, RefCell}; 31 use core::fmt::Write as _; 32 use core::future::Future; 33 use core::pin::Pin; 34 use core::task::{Context, Poll, Waker}; 35 use wasmtime::component::{Component, Linker, Resource, ResourceTable}; 36 use wasmtime::{Config, Engine, Store}; 37 use wasmtime_wasi_io::{ 38 IoView, 39 bytes::Bytes, 40 poll::{DynPollable, Pollable, subscribe}, 41 streams::{DynInputStream, DynOutputStream, InputStream, OutputStream}, 42 }; 43 44 /// Unlike super::run, its nice to provide some sort of output showing what the 45 /// wasi program did while it executed, so this function reports in out_buf 46 /// what stdout/stderr prints occured on success (returns 0), or the error 47 /// message on failure (returns != 0). 48 #[unsafe(no_mangle)] 49 pub unsafe extern "C" fn run_wasi( 50 out_buf: *mut u8, 51 out_size: *mut usize, 52 wasi_component: *const u8, 53 wasi_component_size: usize, 54 ) -> usize { 55 unsafe { 56 let buf = core::slice::from_raw_parts_mut(out_buf, *out_size); 57 let wasi_component = core::slice::from_raw_parts(wasi_component, wasi_component_size); 58 match run(wasi_component) { 59 Ok(output) => { 60 let len = buf.len().min(output.len()); 61 buf[..len].copy_from_slice(&output.as_bytes()[..len]); 62 *out_size = len; 63 return 0; 64 } 65 Err(e) => { 66 let msg = format!("{e:?}"); 67 let len = buf.len().min(msg.len()); 68 buf[..len].copy_from_slice(&msg.as_bytes()[..len]); 69 *out_size = len; 70 return 1; 71 } 72 } 73 } 74 } 75 76 fn run(wasi_component: &[u8]) -> Result<String> { 77 // wasmtime-wasi-io requires an async store, because the wasi:io/poll 78 // interface will poll as Pending while execution is suspended and it is 79 // waiting for a Pollable to become Ready. This example provides a very 80 // small async executor which is entered below with `block_on`. 81 let mut config = Config::default(); 82 config.async_support(true); 83 // For future: we could consider turning on fuel in the Config to meter 84 // how long a wasm guest could execute for. 85 let engine = Engine::new(&config)?; 86 87 // Like with modules, we deserialize components into native code: 88 let component = match deserialize(&engine, wasi_component)? { 89 Some(c) => c, 90 None => return Ok("cannot load native code - requires virtual memory".to_string()), 91 }; 92 93 // Linker provides wasmtime-wasi-io's implementation of wasi:io package, 94 // and a number of other wasi interfaces implemented below as part of this 95 // example. 96 let mut linker = Linker::new(&engine); 97 wasmtime_wasi_io::add_to_linker_async(&mut linker)?; 98 add_to_linker_async(&mut linker)?; 99 100 // Ensure all imports of the component are satisfied by the linker: 101 let instance_pre = linker.instantiate_pre(&component)?; 102 // Ensure the exports of the component provide the Command world: 103 let command_pre = CommandPre::new(instance_pre)?; 104 105 // Executor and WasiCtx share the same clock: 106 let clock = Clock::new(); 107 108 // Use our custom executor to run some async code here: 109 block_on(clock.clone(), async move { 110 let ctx = ExampleCtx { 111 table: ResourceTable::new(), 112 clock, 113 stdout: WriteLog::new(), 114 stderr: WriteLog::new(), 115 }; 116 let mut store = Store::new(&engine, ctx); 117 // instantiate runs the wasm `start` section of 118 let instance = command_pre.instantiate_async(&mut store).await?; 119 instance 120 .wasi_cli_run() 121 .call_run(&mut store) 122 .await? 123 .map_err(|()| anyhow::anyhow!("wasi cli run returned error"))?; 124 125 store.into_data().output() 126 }) 127 } 128 129 fn deserialize(engine: &Engine, component: &[u8]) -> Result<Option<Component>> { 130 match unsafe { Component::deserialize(engine, component) } { 131 Ok(component) => Ok(Some(component)), 132 Err(e) => { 133 // Currently if custom signals/virtual memory are disabled then this 134 // example is expected to fail to load since loading native code 135 // requires virtual memory. In the future this will go away as when 136 // signals-based-traps is disabled then that means that the 137 // interpreter should be used which should work here. 138 if !cfg!(feature = "custom") 139 && e.to_string() 140 .contains("requires virtual memory to be enabled") 141 { 142 Ok(None) 143 } else { 144 Err(e) 145 } 146 } 147 } 148 } 149 150 // Generate bindings for the entire wasi:cli command world. We won't impl and 151 // link with all of these generated bindings for the sake of this example. 152 wasmtime::component::bindgen!({ 153 path: "../../../crates/wasi/src/p2/wit", 154 world: "wasi:cli/command", 155 async: { only_imports: [] }, 156 trappable_imports: true, 157 // Important: tell bindgen that anywhere it encounters the wasi:io 158 // package, refer to the bindings generated in the wasmtime_wasi_io crate. 159 // This way, all uses of the streams and pollable in the bindings in this 160 // file match with the resource types (DynInputStream, DynOutputStream, 161 // DynPollable) we use from the wasmtime_wasi_io crate. 162 with: { 163 "wasi:io": wasmtime_wasi_io::bindings::wasi::io, 164 } 165 }); 166 167 /// A Ctx struct particular to this example. In library code designed to be 168 /// reused and extended, this might be called a WasiCtx and not include a 169 /// ResourceTable as a member, but for the sake of this example, we put 170 /// everything that the bind 171 pub struct ExampleCtx { 172 table: ResourceTable, 173 clock: Clock, 174 stdout: WriteLog, 175 stderr: WriteLog, 176 } 177 178 // Provide an IoView impl in order to satisfy 179 // wasmtime_wasi_io::add_to_linker_async. 180 impl IoView for ExampleCtx { 181 fn table(&mut self) -> &mut ResourceTable { 182 &mut self.table 183 } 184 } 185 186 impl ExampleCtx { 187 // Collect all of the output written to stdout and stderr into a simple 188 // human-readable string, to be written to out_buf from run_wasi on 189 // success. Lossy utf8 conversion because this is an example. 190 fn output(&self) -> Result<String> { 191 let mut out = String::new(); 192 let stdout = self.stdout.log.borrow(); 193 if !stdout.is_empty() { 194 write!(&mut out, "stdout:\n")?; 195 for chunk in stdout.iter() { 196 write!(&mut out, "{}", String::from_utf8_lossy(chunk))?; 197 } 198 } 199 let stderr = self.stderr.log.borrow(); 200 if !stderr.is_empty() { 201 write!(&mut out, "stderr:\n")?; 202 for chunk in stderr.iter() { 203 write!(&mut out, "{}", String::from_utf8_lossy(chunk))?; 204 } 205 } 206 Ok(out) 207 } 208 } 209 210 // Add the minimum number of wasi interfaces to the Linker to instantiate the 211 // example application. This does not provide support for the entire 212 // wasi:cli/command world. Many of these impls are bare-bones and some are 213 // intentionally broken, see notes below. 214 pub fn add_to_linker_async(linker: &mut Linker<ExampleCtx>) -> Result<()> { 215 type Data = wasmtime::component::HasSelf<ExampleCtx>; 216 217 wasi::clocks::monotonic_clock::add_to_linker::<_, Data>(linker, |t| t)?; 218 wasi::clocks::wall_clock::add_to_linker::<_, Data>(linker, |t| t)?; 219 wasi::cli::environment::add_to_linker::<_, Data>(linker, |t| t)?; 220 wasi::cli::exit::add_to_linker::<_, Data>(linker, &Default::default(), |t| t)?; 221 wasi::cli::stdin::add_to_linker::<_, Data>(linker, |t| t)?; 222 wasi::cli::stdout::add_to_linker::<_, Data>(linker, |t| t)?; 223 wasi::cli::stderr::add_to_linker::<_, Data>(linker, |t| t)?; 224 wasi::random::random::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. Clonable 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 // This is obviously bogus and breaks the guarantees given by this interface. 573 // In a real embedding, provide a high quality source of randomness here. 574 impl wasi::random::random::Host for ExampleCtx { 575 fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> { 576 let mut vec = Vec::new(); 577 vec.resize(len as usize, 0u8); 578 Ok(vec) 579 } 580 fn get_random_u64(&mut self) -> Result<u64> { 581 Ok(0) 582 } 583 } 584 585 // The preopens are the only place the filesystem is provided a Descriptor, 586 // from which to try open_at to get more Descriptors. If we don't provide 587 // anything here, none of the methods on Descriptor will ever be reachable, 588 // because Resources are unforgable (the runtime will trap bogus indexes). 589 impl wasi::filesystem::preopens::Host for ExampleCtx { 590 fn get_directories( 591 &mut self, 592 ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> { 593 // Never construct a Descriptor, so all of the bails in the rest of Filesystem should be 594 // unreachable. 595 Ok(Vec::new()) 596 } 597 } 598 599 // This impl is completely empty! 600 impl wasi::filesystem::types::HostDescriptor for ExampleCtx { 601 fn read_via_stream( 602 &mut self, 603 _: Resource<wasi::filesystem::types::Descriptor>, 604 _: u64, 605 ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> { 606 unreachable!("no filesystem") 607 } 608 fn write_via_stream( 609 &mut self, 610 _: Resource<wasi::filesystem::types::Descriptor>, 611 _: u64, 612 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> { 613 unreachable!("no filesystem") 614 } 615 fn append_via_stream( 616 &mut self, 617 _: Resource<wasi::filesystem::types::Descriptor>, 618 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> { 619 unreachable!("no filesystem") 620 } 621 fn advise( 622 &mut self, 623 _: Resource<wasi::filesystem::types::Descriptor>, 624 _: u64, 625 _: u64, 626 _: wasi::filesystem::types::Advice, 627 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 628 unreachable!("no filesystem") 629 } 630 fn sync_data( 631 &mut self, 632 _: Resource<wasi::filesystem::types::Descriptor>, 633 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 634 unreachable!("no filesystem") 635 } 636 fn get_flags( 637 &mut self, 638 _: Resource<wasi::filesystem::types::Descriptor>, 639 ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>> 640 { 641 unreachable!("no filesystem") 642 } 643 fn get_type( 644 &mut self, 645 _: Resource<wasi::filesystem::types::Descriptor>, 646 ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>> 647 { 648 unreachable!("no filesystem") 649 } 650 fn set_size( 651 &mut self, 652 _: Resource<wasi::filesystem::types::Descriptor>, 653 _: u64, 654 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 655 unreachable!("no filesystem") 656 } 657 fn set_times( 658 &mut self, 659 _: Resource<wasi::filesystem::types::Descriptor>, 660 _: wasi::filesystem::types::NewTimestamp, 661 _: wasi::filesystem::types::NewTimestamp, 662 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 663 unreachable!("no filesystem") 664 } 665 fn read( 666 &mut self, 667 _: Resource<wasi::filesystem::types::Descriptor>, 668 _: u64, 669 _: u64, 670 ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> { 671 unreachable!("no filesystem") 672 } 673 fn write( 674 &mut self, 675 _: Resource<wasi::filesystem::types::Descriptor>, 676 _: Vec<u8>, 677 _: u64, 678 ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> { 679 unreachable!("no filesystem") 680 } 681 682 fn read_directory( 683 &mut self, 684 _: Resource<wasi::filesystem::types::Descriptor>, 685 ) -> Result< 686 Result< 687 Resource<wasi::filesystem::types::DirectoryEntryStream>, 688 wasi::filesystem::types::ErrorCode, 689 >, 690 > { 691 unreachable!("no filesystem") 692 } 693 fn sync( 694 &mut self, 695 _: Resource<wasi::filesystem::types::Descriptor>, 696 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 697 unreachable!("no filesystem") 698 } 699 fn create_directory_at( 700 &mut self, 701 _: Resource<wasi::filesystem::types::Descriptor>, 702 _: String, 703 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 704 unreachable!("no filesystem") 705 } 706 fn stat( 707 &mut self, 708 _: Resource<wasi::filesystem::types::Descriptor>, 709 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>> 710 { 711 unreachable!("no filesystem") 712 } 713 fn stat_at( 714 &mut self, 715 _: Resource<wasi::filesystem::types::Descriptor>, 716 _: wasi::filesystem::types::PathFlags, 717 _: String, 718 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>> 719 { 720 unreachable!("no filesystem") 721 } 722 fn set_times_at( 723 &mut self, 724 _: Resource<wasi::filesystem::types::Descriptor>, 725 _: wasi::filesystem::types::PathFlags, 726 _: String, 727 _: wasi::filesystem::types::NewTimestamp, 728 _: wasi::filesystem::types::NewTimestamp, 729 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 730 unreachable!("no filesystem") 731 } 732 fn link_at( 733 &mut self, 734 _: Resource<wasi::filesystem::types::Descriptor>, 735 _: wasi::filesystem::types::PathFlags, 736 _: String, 737 _: Resource<wasi::filesystem::types::Descriptor>, 738 _: String, 739 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 740 unreachable!("no filesystem") 741 } 742 fn open_at( 743 &mut self, 744 _: Resource<wasi::filesystem::types::Descriptor>, 745 _: wasi::filesystem::types::PathFlags, 746 _: String, 747 _: wasi::filesystem::types::OpenFlags, 748 _: wasi::filesystem::types::DescriptorFlags, 749 ) -> Result< 750 Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>, 751 > { 752 unreachable!("no filesystem") 753 } 754 fn readlink_at( 755 &mut self, 756 _: Resource<wasi::filesystem::types::Descriptor>, 757 _: String, 758 ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> { 759 unreachable!("no filesystem") 760 } 761 fn remove_directory_at( 762 &mut self, 763 _: Resource<wasi::filesystem::types::Descriptor>, 764 _: String, 765 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 766 unreachable!("no filesystem") 767 } 768 fn rename_at( 769 &mut self, 770 _: Resource<wasi::filesystem::types::Descriptor>, 771 _: String, 772 _: Resource<wasi::filesystem::types::Descriptor>, 773 _: String, 774 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 775 unreachable!("no filesystem") 776 } 777 fn symlink_at( 778 &mut self, 779 _: Resource<wasi::filesystem::types::Descriptor>, 780 _: String, 781 _: String, 782 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 783 unreachable!("no filesystem") 784 } 785 fn unlink_file_at( 786 &mut self, 787 _: Resource<wasi::filesystem::types::Descriptor>, 788 _: String, 789 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> { 790 unreachable!("no filesystem") 791 } 792 fn is_same_object( 793 &mut self, 794 _: Resource<wasi::filesystem::types::Descriptor>, 795 _: Resource<wasi::filesystem::types::Descriptor>, 796 ) -> Result<bool> { 797 unreachable!("no filesystem") 798 } 799 fn metadata_hash( 800 &mut self, 801 _: Resource<wasi::filesystem::types::Descriptor>, 802 ) -> Result< 803 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>, 804 > { 805 unreachable!("no filesystem") 806 } 807 fn metadata_hash_at( 808 &mut self, 809 _: Resource<wasi::filesystem::types::Descriptor>, 810 _: wasi::filesystem::types::PathFlags, 811 _: String, 812 ) -> Result< 813 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>, 814 > { 815 unreachable!("no filesystem") 816 } 817 818 fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> { 819 unreachable!("no filesystem") 820 } 821 } 822 // Only place this resource can be created is with Descriptor::read_directory, 823 // so this will never be constructed either. 824 impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx { 825 fn read_directory_entry( 826 &mut self, 827 _: Resource<wasi::filesystem::types::DirectoryEntryStream>, 828 ) -> Result< 829 Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>, 830 > { 831 unreachable!("no filesystem") 832 } 833 fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> { 834 unreachable!("no filesystem") 835 } 836 } 837 838 // No stream is ever constructed from a Descriptor, there will never be a 839 // valid downcast of a stream error into a filesystem error-code. 840 impl wasi::filesystem::types::Host for ExampleCtx { 841 fn filesystem_error_code( 842 &mut self, 843 _: Resource<wasmtime_wasi_io::streams::Error>, 844 ) -> Result<Option<wasi::filesystem::types::ErrorCode>> { 845 Ok(None) 846 } 847 } 848