Path: blob/main/examples/min-platform/embedding/src/wasi.rs
2450 views
//! This example demonstrates how wasmtime-wasi-io can be used in a #![no_std]1//! target as the basis for a WASI implementation.2//!3//! This example can execute a wasi:cli/command component on a custom async4//! executor with no dependencies on the environment: execution is5//! deterministic, and no sources of input are provided to the component. The6//! WASI implementation is deliberately limited and incomplete, and many WASI7//! components will not even instantiate, or execute correctly, because this8//! is not a fully fleshed-out example.9//!10//! The wasmtime-wasi implementation of WASI depends on the tokio executor,11//! cap-std family of crates, and others to provide a complete implementation12//! of WASI p2 on top of Unix-based and Windows operating systems. It would be13//! difficult and/or inappropriate to port to other settings. This example14//! might be a good starting point for how to go about rolling your own WASI15//! implementation that is particular to your own execution environment.16//!17//! The wasmtime-wasi-io crate, which is a key part of this example, provides18//! an implementation of the wasi:io package, which is the foundation of19//! WASIp2. wasmtime-wasi-io provides the Pollable, InputStream, and20//! OutputStream traits, and this example shows implementations of those21//! traits for this particular embedding.2223use alloc::boxed::Box;24use alloc::collections::VecDeque;25use alloc::rc::Rc;26use alloc::string::{String, ToString};27use alloc::vec::Vec;28use anyhow::{Result, bail};29use core::cell::{Cell, RefCell};30use core::fmt::Write as _;31use core::future::Future;32use core::pin::Pin;33use core::task::{Context, Poll, Waker};34use wasmtime::component::{Component, Linker, Resource, ResourceTable};35use wasmtime::{Engine, Store};36use wasmtime_wasi_io::{37IoView,38bytes::Bytes,39poll::{DynPollable, Pollable, subscribe},40streams::{DynInputStream, DynOutputStream, InputStream, OutputStream},41};4243/// Unlike super::run, its nice to provide some sort of output showing what the44/// wasi program did while it executed, so this function reports in out_buf45/// what stdout/stderr prints occurred on success (returns 0), or the error46/// message on failure (returns != 0).47#[unsafe(no_mangle)]48pub unsafe extern "C" fn run_wasi(49out_buf: *mut u8,50out_size: *mut usize,51wasi_component: *const u8,52wasi_component_size: usize,53) -> usize {54unsafe {55let buf = core::slice::from_raw_parts_mut(out_buf, *out_size);56let wasi_component = core::slice::from_raw_parts(wasi_component, wasi_component_size);57match run(wasi_component) {58Ok(output) => {59let len = buf.len().min(output.len());60buf[..len].copy_from_slice(&output.as_bytes()[..len]);61*out_size = len;62return 0;63}64Err(e) => {65let msg = format!("{e:?}");66let len = buf.len().min(msg.len());67buf[..len].copy_from_slice(&msg.as_bytes()[..len]);68*out_size = len;69return 1;70}71}72}73}7475fn run(wasi_component: &[u8]) -> Result<String> {76// wasmtime-wasi-io requires an async store, because the wasi:io/poll77// interface will poll as Pending while execution is suspended and it is78// waiting for a Pollable to become Ready. This example provides a very79// small async executor which is entered below with `block_on`.80let mut config = super::config();81config.async_support(true);82// For future: we could consider turning on fuel in the Config to meter83// how long a wasm guest could execute for.84let engine = Engine::new(&config)?;8586// Like with modules, we deserialize components into native code:87let component = match deserialize(&engine, wasi_component)? {88Some(c) => c,89None => return Ok("cannot load native code - requires virtual memory".to_string()),90};9192// Linker provides wasmtime-wasi-io's implementation of wasi:io package,93// and a number of other wasi interfaces implemented below as part of this94// example.95let mut linker = Linker::new(&engine);96wasmtime_wasi_io::add_to_linker_async(&mut linker)?;97add_to_linker_async(&mut linker)?;9899// Ensure all imports of the component are satisfied by the linker:100let instance_pre = linker.instantiate_pre(&component)?;101// Ensure the exports of the component provide the Command world:102let command_pre = CommandPre::new(instance_pre)?;103104// Executor and WasiCtx share the same clock:105let clock = Clock::new();106107// Use our custom executor to run some async code here:108block_on(clock.clone(), async move {109let ctx = ExampleCtx {110table: ResourceTable::new(),111clock,112stdout: WriteLog::new(),113stderr: WriteLog::new(),114};115let mut store = Store::new(&engine, ctx);116// instantiate runs the wasm `start` section of117let instance = command_pre.instantiate_async(&mut store).await?;118instance119.wasi_cli_run()120.call_run(&mut store)121.await?122.map_err(|()| anyhow::anyhow!("wasi cli run returned error"))?;123124store.into_data().output()125})126}127128fn deserialize(engine: &Engine, component: &[u8]) -> Result<Option<Component>> {129match unsafe { Component::deserialize(engine, component) } {130Ok(component) => Ok(Some(component)),131Err(e) => {132// Currently if custom signals/virtual memory are disabled then this133// example is expected to fail to load since loading native code134// requires virtual memory. In the future this will go away as when135// signals-based-traps is disabled then that means that the136// interpreter should be used which should work here.137if !cfg!(feature = "custom")138&& e.to_string()139.contains("requires virtual memory to be enabled")140{141Ok(None)142} else {143Err(e)144}145}146}147}148149// Generate bindings for the entire wasi:cli command world. We won't impl and150// link with all of these generated bindings for the sake of this example.151wasmtime::component::bindgen!({152path: "../../../crates/wasi/src/p2/wit",153world: "wasi:cli/command",154imports: { default: trappable },155exports: { default: async },156require_store_data_send: true,157// Important: tell bindgen that anywhere it encounters the wasi:io158// 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 this160// file match with the resource types (DynInputStream, DynOutputStream,161// DynPollable) we use from the wasmtime_wasi_io crate.162with: {163"wasi:io": wasmtime_wasi_io::bindings::wasi::io,164}165});166167/// A Ctx struct particular to this example. In library code designed to be168/// reused and extended, this might be called a WasiCtx and not include a169/// ResourceTable as a member, but for the sake of this example, we put170/// everything that the bind171pub struct ExampleCtx {172table: ResourceTable,173clock: Clock,174stdout: WriteLog,175stderr: WriteLog,176}177178// Provide an IoView impl in order to satisfy179// wasmtime_wasi_io::add_to_linker_async.180impl IoView for ExampleCtx {181fn table(&mut self) -> &mut ResourceTable {182&mut self.table183}184}185186impl ExampleCtx {187// Collect all of the output written to stdout and stderr into a simple188// human-readable string, to be written to out_buf from run_wasi on189// success. Lossy utf8 conversion because this is an example.190fn output(&self) -> Result<String> {191let mut out = String::new();192let stdout = self.stdout.log.borrow();193if !stdout.is_empty() {194write!(&mut out, "stdout:\n")?;195for chunk in stdout.iter() {196write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;197}198}199let stderr = self.stderr.log.borrow();200if !stderr.is_empty() {201write!(&mut out, "stderr:\n")?;202for chunk in stderr.iter() {203write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;204}205}206Ok(out)207}208}209210// Add the minimum number of wasi interfaces to the Linker to instantiate the211// example application. This does not provide support for the entire212// wasi:cli/command world. Many of these impls are bare-bones and some are213// intentionally broken, see notes below.214pub fn add_to_linker_async(linker: &mut Linker<ExampleCtx>) -> Result<()> {215type Data = wasmtime::component::HasSelf<ExampleCtx>;216217wasi::clocks::monotonic_clock::add_to_linker::<_, Data>(linker, |t| t)?;218wasi::clocks::wall_clock::add_to_linker::<_, Data>(linker, |t| t)?;219wasi::cli::environment::add_to_linker::<_, Data>(linker, |t| t)?;220wasi::cli::exit::add_to_linker::<_, Data>(linker, &Default::default(), |t| t)?;221wasi::cli::stdin::add_to_linker::<_, Data>(linker, |t| t)?;222wasi::cli::stdout::add_to_linker::<_, Data>(linker, |t| t)?;223wasi::cli::stderr::add_to_linker::<_, Data>(linker, |t| t)?;224wasi::random::random::add_to_linker::<_, Data>(linker, |t| t)?;225wasi::filesystem::preopens::add_to_linker::<_, Data>(linker, |t| t)?;226wasi::filesystem::types::add_to_linker::<_, Data>(linker, |t| t)?;227Ok(())228}229230// WasiCtx and the Executor need to share a single clock, so make it reference231// counted.232#[derive(Clone)]233struct Clock(Rc<Cell<u64>>);234impl Clock {235fn new() -> Self {236Clock(Rc::new(Cell::new(0)))237}238fn get(&self) -> u64 {239self.0.get()240}241fn set(&self, to: u64) {242self.0.set(to)243}244fn timer(&self, due: u64) -> Deadline {245Deadline {246clock: self.clone(),247due,248}249}250}251// SAFETY: only will consume this crate in single-threaded environment252unsafe impl Send for Clock {}253unsafe impl Sync for Clock {}254255// A Deadline is used to implement the monotonic clock's pollable. It is a256// future which is ready when the clock reaches the due time.257#[derive(Clone)]258struct Deadline {259clock: Clock,260due: u64,261}262impl Future for Deadline {263type Output = ();264fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {265let now = self.clock.get();266if now < self.due {267Executor::current().push_deadline(self.due, cx.waker().clone());268Poll::Pending269} else {270Poll::Ready(())271}272}273}274#[wasmtime_wasi_io::async_trait]275impl Pollable for Deadline {276async fn ready(&mut self) {277self.clone().await278}279}280281// An input-stream which is never ready for reading is used to implement282// stdin.283struct NeverReadable;284#[wasmtime_wasi_io::async_trait]285impl Pollable for NeverReadable {286async fn ready(&mut self) {287struct Pending;288impl Future for Pending {289type Output = ();290fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {291Poll::Pending292}293}294Pending.await295}296}297impl InputStream for NeverReadable {298fn read(&mut self, _: usize) -> wasmtime_wasi_io::streams::StreamResult<Bytes> {299unreachable!("never ready for reading")300}301}302303// WriteLog is used implement stdout and stderr. Cloneable because wasi:cli304// requires, when calling get_stdout/get_stderr multiple times, to provide305// distinct resources that point to the same underlying stream. RefCell306// provides mutation, and VecDeque provides O(1) push_back operation.307#[derive(Clone)]308struct WriteLog {309log: Rc<RefCell<VecDeque<Bytes>>>,310}311impl WriteLog {312fn new() -> Self {313Self {314log: Rc::new(RefCell::new(VecDeque::new())),315}316}317}318// SAFETY: only will consume this crate in single-threaded environment319unsafe impl Send for WriteLog {}320unsafe impl Sync for WriteLog {}321322impl OutputStream for WriteLog {323fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> {324Ok(usize::MAX)325}326fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> {327self.log.borrow_mut().push_back(contents);328Ok(())329}330fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> {331Ok(())332}333}334#[wasmtime_wasi_io::async_trait]335impl Pollable for WriteLog {336async fn ready(&mut self) {337// always ready - return immediately.338}339}340341// Global symbol (no thread local storage on this target) provides ability for342// Future impls to tell the Executor what they are waiting on.343static EXECUTOR: ExecutorGlobal = ExecutorGlobal::new();344345// RefCell for mutation, Option so the Executor can be present only for life346// of the block_on call.347struct ExecutorGlobal(RefCell<Option<Executor>>);348impl ExecutorGlobal {349const fn new() -> Self {350ExecutorGlobal(RefCell::new(None))351}352}353// SAFETY: only will consume this crate in single-threaded environment354unsafe impl Send for ExecutorGlobal {}355unsafe impl Sync for ExecutorGlobal {}356357// Rc because executor and global both need to hold a reference, and makes it358// convenient to implement current(). RefCell for mutation.359struct Executor(Rc<RefCell<ExecutorInner>>);360361impl Executor {362pub fn new() -> Self {363Executor(Rc::new(RefCell::new(ExecutorInner {364schedule: Vec::new(),365})))366}367pub fn current() -> Self {368Executor(369EXECUTOR370.0371.borrow_mut()372.as_ref()373.expect("Executor::current must be called within block_on")374.0375.clone(),376)377}378pub fn push_deadline(&mut self, due: u64, waker: Waker) {379self.0.borrow_mut().schedule.push((due, waker))380}381}382383// Schedule, as provided by the Deadline future impls. Map of due times to384// wakers.385struct ExecutorInner {386schedule: Vec<(u64, Waker)>,387}388389impl ExecutorInner {390// Get the earliest deadline currently waiting. None if there are no391// deadlines.392fn earliest_deadline(&self) -> Option<u64> {393self.schedule.iter().map(|(due, _)| due).min().copied()394}395// Return all wakers associated with deadlines before or equal to the396// current clock time. Removes the wakers and their deadline from the397// schedule.398fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> {399let mut i = 0;400let mut wakers = Vec::new();401// This is basically https://doc.rust-lang.org/std/vec/struct.Vec.html#method.extract_if,402// which is unstable403while i < self.schedule.len() {404if let Some((due, _)) = self.schedule.get(i) {405if *due <= now {406let (_, waker) = self.schedule.remove(i);407wakers.push(waker);408} else {409i += 1;410}411} else {412break;413}414}415wakers416}417}418419fn block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R> {420// Guard against nested invocations421if EXECUTOR.0.borrow_mut().is_some() {422panic!("cannot block_on while executor is running!")423}424let executor = Executor::new();425*EXECUTOR.0.borrow_mut() = Some(Executor(executor.0.clone()));426427// No special waker needed for this executor.428let mut cx = Context::from_waker(Waker::noop());429let mut f = core::pin::pin!(f);430431// Drive the Future to completion in the following loop432let r = 'outer: loop {433// Arbitrary. Could be as little as 1. There's no fuel-based async434// yielding in this example so repeated polls is probably not making435// progress without "providing input" from the outside environment,436// below.437const POLLS_PER_CLOCK: usize = 200;438for _ in 0..POLLS_PER_CLOCK {439match f.as_mut().poll(&mut cx) {440Poll::Pending => {}441Poll::Ready(r) => break 'outer r,442}443}444445// This is where a non-example executor would wait for input from the446// "outside world". This example checks if the schedule indicates the447// guest is waiting on some future deadline and fast-forwards time448// until then, because no other input is possible in this example.449if let Some(sleep_until) = executor.0.borrow().earliest_deadline() {450clock.set(sleep_until);451} else {452clock.set(clock.get() + 1);453}454455// Any wakers which are ready can be waked now.456for waker in executor.0.borrow_mut().ready_deadlines(clock.get()) {457waker.wake()458}459};460461// Clean up guard for nested invocations462let _ = EXECUTOR463.0464.borrow_mut()465.take()466.expect("executor vacated global while running");467r468}469470// -------------- impls for the bindgen! Host traits ------------------471// These impls are written directly for WasiCtx, which is fine because this472// example isn't trying to create reusable library code.473474impl wasi::clocks::monotonic_clock::Host for ExampleCtx {475fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> {476Ok(self.clock.get())477}478fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> {479Ok(1)480}481fn subscribe_duration(482&mut self,483duration: wasi::clocks::monotonic_clock::Duration,484) -> Result<Resource<DynPollable>> {485self.subscribe_instant(self.clock.get() + duration)486}487fn subscribe_instant(488&mut self,489deadline: wasi::clocks::monotonic_clock::Instant,490) -> Result<Resource<DynPollable>> {491let timer = self.clock.timer(deadline);492let deadline = self.table().push(timer)?;493Ok(subscribe(self.table(), deadline)?)494}495}496497impl wasi::clocks::wall_clock::Host for ExampleCtx {498fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {499// A bogus time. This datetime is relative to the unix epoch. Just500// reuse the monotonic time for the sake of the example.501let now = self.clock.get();502let seconds = now / 1_000_000_000;503let nanoseconds = (now - (seconds * 1_000_000_000)) as u32;504Ok(wasi::clocks::wall_clock::Datetime {505seconds,506nanoseconds,507})508}509fn resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {510Ok(wasi::clocks::wall_clock::Datetime {511seconds: 0,512nanoseconds: 1,513})514}515}516517// No arguments, environment variables, or cwd are provided.518impl wasi::cli::environment::Host for ExampleCtx {519fn get_arguments(&mut self) -> Result<Vec<String>> {520Ok(Vec::new())521}522fn get_environment(&mut self) -> Result<Vec<(String, String)>> {523Ok(Vec::new())524}525fn initial_cwd(&mut self) -> Result<Option<String>> {526Ok(None)527}528}529530// Ideally this would follow the example in wasmtime-wasi: make a struct, impl531// Error on it, and try downcasting to it at the call_run site to see if the532// wasi:cli/exit was used to exit with success without unwinding - valid but533// uncommon behavior that should be treated the same as returning ok from the534// wasi:cli/run.run function. Our example program doesn't exit that way.535impl wasi::cli::exit::Host for ExampleCtx {536fn exit(&mut self, code: Result<(), ()>) -> Result<()> {537if code.is_ok() {538bail!("wasi exit success")539} else {540bail!("wasi exit error")541}542}543// This is feature-flagged (unstable) in the wits. Per the LinkOptions544// passed to the wasi::cli::exit::add_to_linker, it won't be found in545// any guest code.546fn exit_with_code(&mut self, _: u8) -> Result<()> {547unreachable!("this unstable func is not added to the linker");548}549}550551impl wasi::cli::stdin::Host for ExampleCtx {552fn get_stdin(&mut self) -> Result<Resource<DynInputStream>> {553let stdin: DynInputStream = Box::new(NeverReadable);554Ok(self.table().push(stdin)?)555}556}557558impl wasi::cli::stdout::Host for ExampleCtx {559fn get_stdout(&mut self) -> Result<Resource<DynOutputStream>> {560let stdout: DynOutputStream = Box::new(self.stdout.clone());561Ok(self.table().push(stdout)?)562}563}564565impl wasi::cli::stderr::Host for ExampleCtx {566fn get_stderr(&mut self) -> Result<Resource<DynOutputStream>> {567let stderr: DynOutputStream = Box::new(self.stderr.clone());568Ok(self.table().push(stderr)?)569}570}571572// 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.574impl wasi::random::random::Host for ExampleCtx {575fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> {576let mut vec = Vec::new();577vec.resize(len as usize, 0u8);578Ok(vec)579}580fn get_random_u64(&mut self) -> Result<u64> {581Ok(0)582}583}584585// 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 provide587// anything here, none of the methods on Descriptor will ever be reachable,588// because Resources are unforgable (the runtime will trap bogus indexes).589impl wasi::filesystem::preopens::Host for ExampleCtx {590fn 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 be594// unreachable.595Ok(Vec::new())596}597}598599// This impl is completely empty!600impl wasi::filesystem::types::HostDescriptor for ExampleCtx {601fn read_via_stream(602&mut self,603_: Resource<wasi::filesystem::types::Descriptor>,604_: u64,605) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> {606unreachable!("no filesystem")607}608fn write_via_stream(609&mut self,610_: Resource<wasi::filesystem::types::Descriptor>,611_: u64,612) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {613unreachable!("no filesystem")614}615fn append_via_stream(616&mut self,617_: Resource<wasi::filesystem::types::Descriptor>,618) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {619unreachable!("no filesystem")620}621fn 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>> {628unreachable!("no filesystem")629}630fn sync_data(631&mut self,632_: Resource<wasi::filesystem::types::Descriptor>,633) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {634unreachable!("no filesystem")635}636fn get_flags(637&mut self,638_: Resource<wasi::filesystem::types::Descriptor>,639) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>>640{641unreachable!("no filesystem")642}643fn get_type(644&mut self,645_: Resource<wasi::filesystem::types::Descriptor>,646) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>>647{648unreachable!("no filesystem")649}650fn set_size(651&mut self,652_: Resource<wasi::filesystem::types::Descriptor>,653_: u64,654) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {655unreachable!("no filesystem")656}657fn 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>> {663unreachable!("no filesystem")664}665fn read(666&mut self,667_: Resource<wasi::filesystem::types::Descriptor>,668_: u64,669_: u64,670) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> {671unreachable!("no filesystem")672}673fn write(674&mut self,675_: Resource<wasi::filesystem::types::Descriptor>,676_: Vec<u8>,677_: u64,678) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> {679unreachable!("no filesystem")680}681682fn read_directory(683&mut self,684_: Resource<wasi::filesystem::types::Descriptor>,685) -> Result<686Result<687Resource<wasi::filesystem::types::DirectoryEntryStream>,688wasi::filesystem::types::ErrorCode,689>,690> {691unreachable!("no filesystem")692}693fn sync(694&mut self,695_: Resource<wasi::filesystem::types::Descriptor>,696) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {697unreachable!("no filesystem")698}699fn create_directory_at(700&mut self,701_: Resource<wasi::filesystem::types::Descriptor>,702_: String,703) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {704unreachable!("no filesystem")705}706fn stat(707&mut self,708_: Resource<wasi::filesystem::types::Descriptor>,709) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>710{711unreachable!("no filesystem")712}713fn 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{720unreachable!("no filesystem")721}722fn 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>> {730unreachable!("no filesystem")731}732fn 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>> {740unreachable!("no filesystem")741}742fn 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<750Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>,751> {752unreachable!("no filesystem")753}754fn readlink_at(755&mut self,756_: Resource<wasi::filesystem::types::Descriptor>,757_: String,758) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> {759unreachable!("no filesystem")760}761fn remove_directory_at(762&mut self,763_: Resource<wasi::filesystem::types::Descriptor>,764_: String,765) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {766unreachable!("no filesystem")767}768fn 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>> {775unreachable!("no filesystem")776}777fn symlink_at(778&mut self,779_: Resource<wasi::filesystem::types::Descriptor>,780_: String,781_: String,782) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {783unreachable!("no filesystem")784}785fn unlink_file_at(786&mut self,787_: Resource<wasi::filesystem::types::Descriptor>,788_: String,789) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {790unreachable!("no filesystem")791}792fn is_same_object(793&mut self,794_: Resource<wasi::filesystem::types::Descriptor>,795_: Resource<wasi::filesystem::types::Descriptor>,796) -> Result<bool> {797unreachable!("no filesystem")798}799fn metadata_hash(800&mut self,801_: Resource<wasi::filesystem::types::Descriptor>,802) -> Result<803Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,804> {805unreachable!("no filesystem")806}807fn metadata_hash_at(808&mut self,809_: Resource<wasi::filesystem::types::Descriptor>,810_: wasi::filesystem::types::PathFlags,811_: String,812) -> Result<813Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,814> {815unreachable!("no filesystem")816}817818fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> {819unreachable!("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.824impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx {825fn read_directory_entry(826&mut self,827_: Resource<wasi::filesystem::types::DirectoryEntryStream>,828) -> Result<829Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>,830> {831unreachable!("no filesystem")832}833fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> {834unreachable!("no filesystem")835}836}837838// No stream is ever constructed from a Descriptor, there will never be a839// valid downcast of a stream error into a filesystem error-code.840impl wasi::filesystem::types::Host for ExampleCtx {841fn filesystem_error_code(842&mut self,843_: Resource<wasmtime_wasi_io::streams::Error>,844) -> Result<Option<wasi::filesystem::types::ErrorCode>> {845Ok(None)846}847}848849850