1use alloc::boxed::Box;
25use alloc::collections::VecDeque;
26use alloc::rc::Rc;
27use alloc::string::{String, ToString};
28use alloc::vec::Vec;
29use anyhow::{Result, bail};
30use core::cell::{Cell, RefCell};
31use core::fmt::Write as _;
32use core::future::Future;
33use core::pin::Pin;
34use core::task::{Context, Poll, Waker};
35use wasmtime::component::{Component, Linker, Resource, ResourceTable};
36use wasmtime::{Engine, Store};
37use wasmtime_wasi_io::{
38 IoView,
39 bytes::Bytes,
40 poll::{DynPollable, Pollable, subscribe},
41 streams::{DynInputStream, DynOutputStream, InputStream, OutputStream},
42};
43
44#[unsafe(no_mangle)]
49pub 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
76fn run(wasi_component: &[u8]) -> Result<String> {
77 let mut config = super::config();
82 config.async_support(true);
83 let engine = Engine::new(&config)?;
86
87 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 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 let instance_pre = linker.instantiate_pre(&component)?;
102 let command_pre = CommandPre::new(instance_pre)?;
104
105 let clock = Clock::new();
107
108 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 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
129fn 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 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
150wasmtime::component::bindgen!({
153 path: "../../../crates/wasi/src/p2/wit",
154 world: "wasi:cli/command",
155 imports: { default: trappable },
156 exports: { default: async },
157 require_store_data_send: true,
158 with: {
164 "wasi:io": wasmtime_wasi_io::bindings::wasi::io,
165 }
166});
167
168pub struct ExampleCtx {
173 table: ResourceTable,
174 clock: Clock,
175 stdout: WriteLog,
176 stderr: WriteLog,
177}
178
179impl IoView for ExampleCtx {
182 fn table(&mut self) -> &mut ResourceTable {
183 &mut self.table
184 }
185}
186
187impl ExampleCtx {
188 fn output(&self) -> Result<String> {
192 let mut out = String::new();
193 let stdout = self.stdout.log.borrow();
194 if !stdout.is_empty() {
195 write!(&mut out, "stdout:\n")?;
196 for chunk in stdout.iter() {
197 write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
198 }
199 }
200 let stderr = self.stderr.log.borrow();
201 if !stderr.is_empty() {
202 write!(&mut out, "stderr:\n")?;
203 for chunk in stderr.iter() {
204 write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
205 }
206 }
207 Ok(out)
208 }
209}
210
211pub fn add_to_linker_async(linker: &mut Linker<ExampleCtx>) -> Result<()> {
216 type Data = wasmtime::component::HasSelf<ExampleCtx>;
217
218 wasi::clocks::monotonic_clock::add_to_linker::<_, Data>(linker, |t| t)?;
219 wasi::clocks::wall_clock::add_to_linker::<_, Data>(linker, |t| t)?;
220 wasi::cli::environment::add_to_linker::<_, Data>(linker, |t| t)?;
221 wasi::cli::exit::add_to_linker::<_, Data>(linker, &Default::default(), |t| t)?;
222 wasi::cli::stdin::add_to_linker::<_, Data>(linker, |t| t)?;
223 wasi::cli::stdout::add_to_linker::<_, Data>(linker, |t| t)?;
224 wasi::cli::stderr::add_to_linker::<_, Data>(linker, |t| t)?;
225 wasi::random::random::add_to_linker::<_, Data>(linker, |t| t)?;
226 wasi::filesystem::preopens::add_to_linker::<_, Data>(linker, |t| t)?;
227 wasi::filesystem::types::add_to_linker::<_, Data>(linker, |t| t)?;
228 Ok(())
229}
230
231#[derive(Clone)]
234struct Clock(Rc<Cell<u64>>);
235impl Clock {
236 fn new() -> Self {
237 Clock(Rc::new(Cell::new(0)))
238 }
239 fn get(&self) -> u64 {
240 self.0.get()
241 }
242 fn set(&self, to: u64) {
243 self.0.set(to)
244 }
245 fn timer(&self, due: u64) -> Deadline {
246 Deadline {
247 clock: self.clone(),
248 due,
249 }
250 }
251}
252unsafe impl Send for Clock {}
254unsafe impl Sync for Clock {}
255
256#[derive(Clone)]
259struct Deadline {
260 clock: Clock,
261 due: u64,
262}
263impl Future for Deadline {
264 type Output = ();
265 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
266 let now = self.clock.get();
267 if now < self.due {
268 Executor::current().push_deadline(self.due, cx.waker().clone());
269 Poll::Pending
270 } else {
271 Poll::Ready(())
272 }
273 }
274}
275#[wasmtime_wasi_io::async_trait]
276impl Pollable for Deadline {
277 async fn ready(&mut self) {
278 self.clone().await
279 }
280}
281
282struct NeverReadable;
285#[wasmtime_wasi_io::async_trait]
286impl Pollable for NeverReadable {
287 async fn ready(&mut self) {
288 struct Pending;
289 impl Future for Pending {
290 type Output = ();
291 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
292 Poll::Pending
293 }
294 }
295 Pending.await
296 }
297}
298impl InputStream for NeverReadable {
299 fn read(&mut self, _: usize) -> wasmtime_wasi_io::streams::StreamResult<Bytes> {
300 unreachable!("never ready for reading")
301 }
302}
303
304#[derive(Clone)]
309struct WriteLog {
310 log: Rc<RefCell<VecDeque<Bytes>>>,
311}
312impl WriteLog {
313 fn new() -> Self {
314 Self {
315 log: Rc::new(RefCell::new(VecDeque::new())),
316 }
317 }
318}
319unsafe impl Send for WriteLog {}
321unsafe impl Sync for WriteLog {}
322
323impl OutputStream for WriteLog {
324 fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> {
325 Ok(usize::MAX)
326 }
327 fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> {
328 self.log.borrow_mut().push_back(contents);
329 Ok(())
330 }
331 fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> {
332 Ok(())
333 }
334}
335#[wasmtime_wasi_io::async_trait]
336impl Pollable for WriteLog {
337 async fn ready(&mut self) {
338 }
340}
341
342static EXECUTOR: ExecutorGlobal = ExecutorGlobal::new();
345
346struct ExecutorGlobal(RefCell<Option<Executor>>);
349impl ExecutorGlobal {
350 const fn new() -> Self {
351 ExecutorGlobal(RefCell::new(None))
352 }
353}
354unsafe impl Send for ExecutorGlobal {}
356unsafe impl Sync for ExecutorGlobal {}
357
358struct Executor(Rc<RefCell<ExecutorInner>>);
361
362impl Executor {
363 pub fn new() -> Self {
364 Executor(Rc::new(RefCell::new(ExecutorInner {
365 schedule: Vec::new(),
366 })))
367 }
368 pub fn current() -> Self {
369 Executor(
370 EXECUTOR
371 .0
372 .borrow_mut()
373 .as_ref()
374 .expect("Executor::current must be called within block_on")
375 .0
376 .clone(),
377 )
378 }
379 pub fn push_deadline(&mut self, due: u64, waker: Waker) {
380 self.0.borrow_mut().schedule.push((due, waker))
381 }
382}
383
384struct ExecutorInner {
387 schedule: Vec<(u64, Waker)>,
388}
389
390impl ExecutorInner {
391 fn earliest_deadline(&self) -> Option<u64> {
394 self.schedule.iter().map(|(due, _)| due).min().copied()
395 }
396 fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> {
400 let mut i = 0;
401 let mut wakers = Vec::new();
402 while i < self.schedule.len() {
405 if let Some((due, _)) = self.schedule.get(i) {
406 if *due <= now {
407 let (_, waker) = self.schedule.remove(i);
408 wakers.push(waker);
409 } else {
410 i += 1;
411 }
412 } else {
413 break;
414 }
415 }
416 wakers
417 }
418}
419
420fn block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R> {
421 if EXECUTOR.0.borrow_mut().is_some() {
423 panic!("cannot block_on while executor is running!")
424 }
425 let executor = Executor::new();
426 *EXECUTOR.0.borrow_mut() = Some(Executor(executor.0.clone()));
427
428 let mut cx = Context::from_waker(Waker::noop());
430 let mut f = core::pin::pin!(f);
431
432 let r = 'outer: loop {
434 const POLLS_PER_CLOCK: usize = 200;
439 for _ in 0..POLLS_PER_CLOCK {
440 match f.as_mut().poll(&mut cx) {
441 Poll::Pending => {}
442 Poll::Ready(r) => break 'outer r,
443 }
444 }
445
446 if let Some(sleep_until) = executor.0.borrow().earliest_deadline() {
451 clock.set(sleep_until);
452 } else {
453 clock.set(clock.get() + 1);
454 }
455
456 for waker in executor.0.borrow_mut().ready_deadlines(clock.get()) {
458 waker.wake()
459 }
460 };
461
462 let _ = EXECUTOR
464 .0
465 .borrow_mut()
466 .take()
467 .expect("executor vacated global while running");
468 r
469}
470
471impl wasi::clocks::monotonic_clock::Host for ExampleCtx {
476 fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> {
477 Ok(self.clock.get())
478 }
479 fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> {
480 Ok(1)
481 }
482 fn subscribe_duration(
483 &mut self,
484 duration: wasi::clocks::monotonic_clock::Duration,
485 ) -> Result<Resource<DynPollable>> {
486 self.subscribe_instant(self.clock.get() + duration)
487 }
488 fn subscribe_instant(
489 &mut self,
490 deadline: wasi::clocks::monotonic_clock::Instant,
491 ) -> Result<Resource<DynPollable>> {
492 let timer = self.clock.timer(deadline);
493 let deadline = self.table().push(timer)?;
494 Ok(subscribe(self.table(), deadline)?)
495 }
496}
497
498impl wasi::clocks::wall_clock::Host for ExampleCtx {
499 fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
500 let now = self.clock.get();
503 let seconds = now / 1_000_000_000;
504 let nanoseconds = (now - (seconds * 1_000_000_000)) as u32;
505 Ok(wasi::clocks::wall_clock::Datetime {
506 seconds,
507 nanoseconds,
508 })
509 }
510 fn resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
511 Ok(wasi::clocks::wall_clock::Datetime {
512 seconds: 0,
513 nanoseconds: 1,
514 })
515 }
516}
517
518impl wasi::cli::environment::Host for ExampleCtx {
520 fn get_arguments(&mut self) -> Result<Vec<String>> {
521 Ok(Vec::new())
522 }
523 fn get_environment(&mut self) -> Result<Vec<(String, String)>> {
524 Ok(Vec::new())
525 }
526 fn initial_cwd(&mut self) -> Result<Option<String>> {
527 Ok(None)
528 }
529}
530
531impl wasi::cli::exit::Host for ExampleCtx {
537 fn exit(&mut self, code: Result<(), ()>) -> Result<()> {
538 if code.is_ok() {
539 bail!("wasi exit success")
540 } else {
541 bail!("wasi exit error")
542 }
543 }
544 fn exit_with_code(&mut self, _: u8) -> Result<()> {
548 unreachable!("this unstable func is not added to the linker");
549 }
550}
551
552impl wasi::cli::stdin::Host for ExampleCtx {
553 fn get_stdin(&mut self) -> Result<Resource<DynInputStream>> {
554 let stdin: DynInputStream = Box::new(NeverReadable);
555 Ok(self.table().push(stdin)?)
556 }
557}
558
559impl wasi::cli::stdout::Host for ExampleCtx {
560 fn get_stdout(&mut self) -> Result<Resource<DynOutputStream>> {
561 let stdout: DynOutputStream = Box::new(self.stdout.clone());
562 Ok(self.table().push(stdout)?)
563 }
564}
565
566impl wasi::cli::stderr::Host for ExampleCtx {
567 fn get_stderr(&mut self) -> Result<Resource<DynOutputStream>> {
568 let stderr: DynOutputStream = Box::new(self.stderr.clone());
569 Ok(self.table().push(stderr)?)
570 }
571}
572
573impl wasi::random::random::Host for ExampleCtx {
576 fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> {
577 let mut vec = Vec::new();
578 vec.resize(len as usize, 0u8);
579 Ok(vec)
580 }
581 fn get_random_u64(&mut self) -> Result<u64> {
582 Ok(0)
583 }
584}
585
586impl wasi::filesystem::preopens::Host for ExampleCtx {
591 fn get_directories(
592 &mut self,
593 ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> {
594 Ok(Vec::new())
597 }
598}
599
600impl wasi::filesystem::types::HostDescriptor for ExampleCtx {
602 fn read_via_stream(
603 &mut self,
604 _: Resource<wasi::filesystem::types::Descriptor>,
605 _: u64,
606 ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> {
607 unreachable!("no filesystem")
608 }
609 fn write_via_stream(
610 &mut self,
611 _: Resource<wasi::filesystem::types::Descriptor>,
612 _: u64,
613 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
614 unreachable!("no filesystem")
615 }
616 fn append_via_stream(
617 &mut self,
618 _: Resource<wasi::filesystem::types::Descriptor>,
619 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
620 unreachable!("no filesystem")
621 }
622 fn advise(
623 &mut self,
624 _: Resource<wasi::filesystem::types::Descriptor>,
625 _: u64,
626 _: u64,
627 _: wasi::filesystem::types::Advice,
628 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
629 unreachable!("no filesystem")
630 }
631 fn sync_data(
632 &mut self,
633 _: Resource<wasi::filesystem::types::Descriptor>,
634 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
635 unreachable!("no filesystem")
636 }
637 fn get_flags(
638 &mut self,
639 _: Resource<wasi::filesystem::types::Descriptor>,
640 ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>>
641 {
642 unreachable!("no filesystem")
643 }
644 fn get_type(
645 &mut self,
646 _: Resource<wasi::filesystem::types::Descriptor>,
647 ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>>
648 {
649 unreachable!("no filesystem")
650 }
651 fn set_size(
652 &mut self,
653 _: Resource<wasi::filesystem::types::Descriptor>,
654 _: u64,
655 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
656 unreachable!("no filesystem")
657 }
658 fn set_times(
659 &mut self,
660 _: Resource<wasi::filesystem::types::Descriptor>,
661 _: wasi::filesystem::types::NewTimestamp,
662 _: wasi::filesystem::types::NewTimestamp,
663 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
664 unreachable!("no filesystem")
665 }
666 fn read(
667 &mut self,
668 _: Resource<wasi::filesystem::types::Descriptor>,
669 _: u64,
670 _: u64,
671 ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> {
672 unreachable!("no filesystem")
673 }
674 fn write(
675 &mut self,
676 _: Resource<wasi::filesystem::types::Descriptor>,
677 _: Vec<u8>,
678 _: u64,
679 ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> {
680 unreachable!("no filesystem")
681 }
682
683 fn read_directory(
684 &mut self,
685 _: Resource<wasi::filesystem::types::Descriptor>,
686 ) -> Result<
687 Result<
688 Resource<wasi::filesystem::types::DirectoryEntryStream>,
689 wasi::filesystem::types::ErrorCode,
690 >,
691 > {
692 unreachable!("no filesystem")
693 }
694 fn sync(
695 &mut self,
696 _: Resource<wasi::filesystem::types::Descriptor>,
697 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
698 unreachable!("no filesystem")
699 }
700 fn create_directory_at(
701 &mut self,
702 _: Resource<wasi::filesystem::types::Descriptor>,
703 _: String,
704 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
705 unreachable!("no filesystem")
706 }
707 fn stat(
708 &mut self,
709 _: Resource<wasi::filesystem::types::Descriptor>,
710 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
711 {
712 unreachable!("no filesystem")
713 }
714 fn stat_at(
715 &mut self,
716 _: Resource<wasi::filesystem::types::Descriptor>,
717 _: wasi::filesystem::types::PathFlags,
718 _: String,
719 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
720 {
721 unreachable!("no filesystem")
722 }
723 fn set_times_at(
724 &mut self,
725 _: Resource<wasi::filesystem::types::Descriptor>,
726 _: wasi::filesystem::types::PathFlags,
727 _: String,
728 _: wasi::filesystem::types::NewTimestamp,
729 _: wasi::filesystem::types::NewTimestamp,
730 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
731 unreachable!("no filesystem")
732 }
733 fn link_at(
734 &mut self,
735 _: Resource<wasi::filesystem::types::Descriptor>,
736 _: wasi::filesystem::types::PathFlags,
737 _: String,
738 _: Resource<wasi::filesystem::types::Descriptor>,
739 _: String,
740 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
741 unreachable!("no filesystem")
742 }
743 fn open_at(
744 &mut self,
745 _: Resource<wasi::filesystem::types::Descriptor>,
746 _: wasi::filesystem::types::PathFlags,
747 _: String,
748 _: wasi::filesystem::types::OpenFlags,
749 _: wasi::filesystem::types::DescriptorFlags,
750 ) -> Result<
751 Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>,
752 > {
753 unreachable!("no filesystem")
754 }
755 fn readlink_at(
756 &mut self,
757 _: Resource<wasi::filesystem::types::Descriptor>,
758 _: String,
759 ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> {
760 unreachable!("no filesystem")
761 }
762 fn remove_directory_at(
763 &mut self,
764 _: Resource<wasi::filesystem::types::Descriptor>,
765 _: String,
766 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
767 unreachable!("no filesystem")
768 }
769 fn rename_at(
770 &mut self,
771 _: Resource<wasi::filesystem::types::Descriptor>,
772 _: String,
773 _: Resource<wasi::filesystem::types::Descriptor>,
774 _: String,
775 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
776 unreachable!("no filesystem")
777 }
778 fn symlink_at(
779 &mut self,
780 _: Resource<wasi::filesystem::types::Descriptor>,
781 _: String,
782 _: String,
783 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
784 unreachable!("no filesystem")
785 }
786 fn unlink_file_at(
787 &mut self,
788 _: Resource<wasi::filesystem::types::Descriptor>,
789 _: String,
790 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
791 unreachable!("no filesystem")
792 }
793 fn is_same_object(
794 &mut self,
795 _: Resource<wasi::filesystem::types::Descriptor>,
796 _: Resource<wasi::filesystem::types::Descriptor>,
797 ) -> Result<bool> {
798 unreachable!("no filesystem")
799 }
800 fn metadata_hash(
801 &mut self,
802 _: Resource<wasi::filesystem::types::Descriptor>,
803 ) -> Result<
804 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
805 > {
806 unreachable!("no filesystem")
807 }
808 fn metadata_hash_at(
809 &mut self,
810 _: Resource<wasi::filesystem::types::Descriptor>,
811 _: wasi::filesystem::types::PathFlags,
812 _: String,
813 ) -> Result<
814 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
815 > {
816 unreachable!("no filesystem")
817 }
818
819 fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> {
820 unreachable!("no filesystem")
821 }
822}
823impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx {
826 fn read_directory_entry(
827 &mut self,
828 _: Resource<wasi::filesystem::types::DirectoryEntryStream>,
829 ) -> Result<
830 Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>,
831 > {
832 unreachable!("no filesystem")
833 }
834 fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> {
835 unreachable!("no filesystem")
836 }
837}
838
839impl wasi::filesystem::types::Host for ExampleCtx {
842 fn filesystem_error_code(
843 &mut self,
844 _: Resource<wasmtime_wasi_io::streams::Error>,
845 ) -> Result<Option<wasi::filesystem::types::ErrorCode>> {
846 Ok(None)
847 }
848}