Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/examples/min-platform/embedding/src/wasi.rs
2450 views
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::{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 occurred 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 = super::config();
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
imports: { default: trappable },
156
exports: { default: async },
157
require_store_data_send: true,
158
// Important: tell bindgen that anywhere it encounters the wasi:io
159
// package, refer to the bindings generated in the wasmtime_wasi_io crate.
160
// This way, all uses of the streams and pollable in the bindings in this
161
// file match with the resource types (DynInputStream, DynOutputStream,
162
// DynPollable) we use from the wasmtime_wasi_io crate.
163
with: {
164
"wasi:io": wasmtime_wasi_io::bindings::wasi::io,
165
}
166
});
167
168
/// A Ctx struct particular to this example. In library code designed to be
169
/// reused and extended, this might be called a WasiCtx and not include a
170
/// ResourceTable as a member, but for the sake of this example, we put
171
/// everything that the bind
172
pub struct ExampleCtx {
173
table: ResourceTable,
174
clock: Clock,
175
stdout: WriteLog,
176
stderr: WriteLog,
177
}
178
179
// Provide an IoView impl in order to satisfy
180
// wasmtime_wasi_io::add_to_linker_async.
181
impl IoView for ExampleCtx {
182
fn table(&mut self) -> &mut ResourceTable {
183
&mut self.table
184
}
185
}
186
187
impl ExampleCtx {
188
// Collect all of the output written to stdout and stderr into a simple
189
// human-readable string, to be written to out_buf from run_wasi on
190
// success. Lossy utf8 conversion because this is an example.
191
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
211
// Add the minimum number of wasi interfaces to the Linker to instantiate the
212
// example application. This does not provide support for the entire
213
// wasi:cli/command world. Many of these impls are bare-bones and some are
214
// intentionally broken, see notes below.
215
pub 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
// WasiCtx and the Executor need to share a single clock, so make it reference
232
// counted.
233
#[derive(Clone)]
234
struct Clock(Rc<Cell<u64>>);
235
impl 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
}
252
// SAFETY: only will consume this crate in single-threaded environment
253
unsafe impl Send for Clock {}
254
unsafe impl Sync for Clock {}
255
256
// A Deadline is used to implement the monotonic clock's pollable. It is a
257
// future which is ready when the clock reaches the due time.
258
#[derive(Clone)]
259
struct Deadline {
260
clock: Clock,
261
due: u64,
262
}
263
impl 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]
276
impl Pollable for Deadline {
277
async fn ready(&mut self) {
278
self.clone().await
279
}
280
}
281
282
// An input-stream which is never ready for reading is used to implement
283
// stdin.
284
struct NeverReadable;
285
#[wasmtime_wasi_io::async_trait]
286
impl 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
}
298
impl 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
// WriteLog is used implement stdout and stderr. Cloneable because wasi:cli
305
// requires, when calling get_stdout/get_stderr multiple times, to provide
306
// distinct resources that point to the same underlying stream. RefCell
307
// provides mutation, and VecDeque provides O(1) push_back operation.
308
#[derive(Clone)]
309
struct WriteLog {
310
log: Rc<RefCell<VecDeque<Bytes>>>,
311
}
312
impl WriteLog {
313
fn new() -> Self {
314
Self {
315
log: Rc::new(RefCell::new(VecDeque::new())),
316
}
317
}
318
}
319
// SAFETY: only will consume this crate in single-threaded environment
320
unsafe impl Send for WriteLog {}
321
unsafe impl Sync for WriteLog {}
322
323
impl 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]
336
impl Pollable for WriteLog {
337
async fn ready(&mut self) {
338
// always ready - return immediately.
339
}
340
}
341
342
// Global symbol (no thread local storage on this target) provides ability for
343
// Future impls to tell the Executor what they are waiting on.
344
static EXECUTOR: ExecutorGlobal = ExecutorGlobal::new();
345
346
// RefCell for mutation, Option so the Executor can be present only for life
347
// of the block_on call.
348
struct ExecutorGlobal(RefCell<Option<Executor>>);
349
impl ExecutorGlobal {
350
const fn new() -> Self {
351
ExecutorGlobal(RefCell::new(None))
352
}
353
}
354
// SAFETY: only will consume this crate in single-threaded environment
355
unsafe impl Send for ExecutorGlobal {}
356
unsafe impl Sync for ExecutorGlobal {}
357
358
// Rc because executor and global both need to hold a reference, and makes it
359
// convenient to implement current(). RefCell for mutation.
360
struct Executor(Rc<RefCell<ExecutorInner>>);
361
362
impl 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
384
// Schedule, as provided by the Deadline future impls. Map of due times to
385
// wakers.
386
struct ExecutorInner {
387
schedule: Vec<(u64, Waker)>,
388
}
389
390
impl ExecutorInner {
391
// Get the earliest deadline currently waiting. None if there are no
392
// deadlines.
393
fn earliest_deadline(&self) -> Option<u64> {
394
self.schedule.iter().map(|(due, _)| due).min().copied()
395
}
396
// Return all wakers associated with deadlines before or equal to the
397
// current clock time. Removes the wakers and their deadline from the
398
// schedule.
399
fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> {
400
let mut i = 0;
401
let mut wakers = Vec::new();
402
// This is basically https://doc.rust-lang.org/std/vec/struct.Vec.html#method.extract_if,
403
// which is unstable
404
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
420
fn block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R> {
421
// Guard against nested invocations
422
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
// No special waker needed for this executor.
429
let mut cx = Context::from_waker(Waker::noop());
430
let mut f = core::pin::pin!(f);
431
432
// Drive the Future to completion in the following loop
433
let r = 'outer: loop {
434
// Arbitrary. Could be as little as 1. There's no fuel-based async
435
// yielding in this example so repeated polls is probably not making
436
// progress without "providing input" from the outside environment,
437
// below.
438
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
// This is where a non-example executor would wait for input from the
447
// "outside world". This example checks if the schedule indicates the
448
// guest is waiting on some future deadline and fast-forwards time
449
// until then, because no other input is possible in this example.
450
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
// Any wakers which are ready can be waked now.
457
for waker in executor.0.borrow_mut().ready_deadlines(clock.get()) {
458
waker.wake()
459
}
460
};
461
462
// Clean up guard for nested invocations
463
let _ = EXECUTOR
464
.0
465
.borrow_mut()
466
.take()
467
.expect("executor vacated global while running");
468
r
469
}
470
471
// -------------- impls for the bindgen! Host traits ------------------
472
// These impls are written directly for WasiCtx, which is fine because this
473
// example isn't trying to create reusable library code.
474
475
impl 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
498
impl wasi::clocks::wall_clock::Host for ExampleCtx {
499
fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
500
// A bogus time. This datetime is relative to the unix epoch. Just
501
// reuse the monotonic time for the sake of the example.
502
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
518
// No arguments, environment variables, or cwd are provided.
519
impl 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
531
// Ideally this would follow the example in wasmtime-wasi: make a struct, impl
532
// Error on it, and try downcasting to it at the call_run site to see if the
533
// wasi:cli/exit was used to exit with success without unwinding - valid but
534
// uncommon behavior that should be treated the same as returning ok from the
535
// wasi:cli/run.run function. Our example program doesn't exit that way.
536
impl 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
// This is feature-flagged (unstable) in the wits. Per the LinkOptions
545
// passed to the wasi::cli::exit::add_to_linker, it won't be found in
546
// any guest code.
547
fn exit_with_code(&mut self, _: u8) -> Result<()> {
548
unreachable!("this unstable func is not added to the linker");
549
}
550
}
551
552
impl 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
559
impl 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
566
impl 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
573
// This is obviously bogus and breaks the guarantees given by this interface.
574
// In a real embedding, provide a high quality source of randomness here.
575
impl 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
586
// The preopens are the only place the filesystem is provided a Descriptor,
587
// from which to try open_at to get more Descriptors. If we don't provide
588
// anything here, none of the methods on Descriptor will ever be reachable,
589
// because Resources are unforgable (the runtime will trap bogus indexes).
590
impl wasi::filesystem::preopens::Host for ExampleCtx {
591
fn get_directories(
592
&mut self,
593
) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> {
594
// Never construct a Descriptor, so all of the bails in the rest of Filesystem should be
595
// unreachable.
596
Ok(Vec::new())
597
}
598
}
599
600
// This impl is completely empty!
601
impl 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
}
823
// Only place this resource can be created is with Descriptor::read_directory,
824
// so this will never be constructed either.
825
impl 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
839
// No stream is ever constructed from a Descriptor, there will never be a
840
// valid downcast of a stream error into a filesystem error-code.
841
impl 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
}
849
850