Skip to main content

spin_factor_wasi/
wasi_2023_10_18.rs

1use crate::sockets::{SpinSockets, SpinSocketsView};
2use spin_factors::anyhow::Result;
3use std::mem;
4use wasmtime::component::{Linker, Resource, ResourceTable};
5use wasmtime_wasi::TrappableError;
6use wasmtime_wasi::cli::{WasiCli, WasiCliCtxView};
7use wasmtime_wasi::clocks::{WasiClocks, WasiClocksCtxView};
8use wasmtime_wasi::filesystem::{WasiFilesystem, WasiFilesystemCtxView};
9use wasmtime_wasi::p2::DynPollable;
10use wasmtime_wasi::p2::bindings::sockets::udp_create_socket as p2_udp_create;
11use wasmtime_wasi::random::{WasiRandom, WasiRandomCtx};
12
13mod latest {
14    pub use wasmtime_wasi::p2::bindings::*;
15}
16
17mod bindings {
18    pub use super::UdpSocket;
19    use super::latest;
20
21    wasmtime::component::bindgen!({
22        path: "../../wit",
23        world: "wasi:cli/reactor@0.2.0-rc-2023-10-18",
24        imports: {
25            "wasi:io/streams.[drop]output-stream": async | trappable,
26            "wasi:io/streams.[drop]input-stream": async | trappable,
27            "wasi:filesystem/types.[method]descriptor.access-at": async | trappable,
28            "wasi:filesystem/types.[method]descriptor.advise": async | trappable,
29            "wasi:filesystem/types.[method]descriptor.change-directory-permissions-at": async | trappable,
30            "wasi:filesystem/types.[method]descriptor.change-file-permissions-at": async | trappable,
31            "wasi:filesystem/types.[method]descriptor.create-directory-at": async | trappable,
32            "wasi:filesystem/types.[method]descriptor.get-flags": async | trappable,
33            "wasi:filesystem/types.[method]descriptor.get-type": async | trappable,
34            "wasi:filesystem/types.[method]descriptor.is-same-object": async | trappable,
35            "wasi:filesystem/types.[method]descriptor.link-at": async | trappable,
36            "wasi:filesystem/types.[method]descriptor.lock-exclusive": async | trappable,
37            "wasi:filesystem/types.[method]descriptor.lock-shared": async | trappable,
38            "wasi:filesystem/types.[method]descriptor.metadata-hash": async | trappable,
39            "wasi:filesystem/types.[method]descriptor.metadata-hash-at": async | trappable,
40            "wasi:filesystem/types.[method]descriptor.open-at": async | trappable,
41            "wasi:filesystem/types.[method]descriptor.read": async | trappable,
42            "wasi:filesystem/types.[method]descriptor.read-directory": async | trappable,
43            "wasi:filesystem/types.[method]descriptor.readlink-at": async | trappable,
44            "wasi:filesystem/types.[method]descriptor.remove-directory-at": async | trappable,
45            "wasi:filesystem/types.[method]descriptor.rename-at": async | trappable,
46            "wasi:filesystem/types.[method]descriptor.set-size": async | trappable,
47            "wasi:filesystem/types.[method]descriptor.set-times": async | trappable,
48            "wasi:filesystem/types.[method]descriptor.set-times-at": async | trappable,
49            "wasi:filesystem/types.[method]descriptor.stat": async | trappable,
50            "wasi:filesystem/types.[method]descriptor.stat-at": async | trappable,
51            "wasi:filesystem/types.[method]descriptor.symlink-at": async | trappable,
52            "wasi:filesystem/types.[method]descriptor.sync": async | trappable,
53            "wasi:filesystem/types.[method]descriptor.sync-data": async | trappable,
54            "wasi:filesystem/types.[method]descriptor.try-lock-exclusive": async | trappable,
55            "wasi:filesystem/types.[method]descriptor.try-lock-shared": async | trappable,
56            "wasi:filesystem/types.[method]descriptor.unlink-file-at": async | trappable,
57            "wasi:filesystem/types.[method]descriptor.unlock": async | trappable,
58            "wasi:filesystem/types.[method]descriptor.write": async | trappable,
59            "wasi:filesystem/types.[method]directory-entry-stream.read-directory-entry": async | trappable,
60            "wasi:io/streams.[method]input-stream.blocking-read": async | trappable,
61            "wasi:io/streams.[method]input-stream.blocking-skip": async | trappable,
62            "wasi:io/streams.[method]output-stream.forward": async | trappable,
63            "wasi:io/streams.[method]output-stream.blocking-splice": async | trappable,
64            "wasi:io/streams.[method]output-stream.blocking-flush": async | trappable,
65            "wasi:io/streams.[method]output-stream.blocking-write-and-flush": async | trappable,
66            "wasi:io/streams.[method]output-stream.blocking-write-zeroes-and-flush": async | trappable,
67            "wasi:io/poll.poll-list": async | trappable,
68            "wasi:io/poll.poll-one": async | trappable,
69
70            "wasi:sockets/tcp.[method]tcp-socket.start-bind": async | trappable,
71            "wasi:sockets/tcp.[method]tcp-socket.start-connect": async | trappable,
72            "wasi:sockets/udp.[method]udp-socket.finish-connect": async | trappable,
73            "wasi:sockets/udp.[method]udp-socket.receive": async | trappable,
74            "wasi:sockets/udp.[method]udp-socket.send": async | trappable,
75            "wasi:sockets/udp.[method]udp-socket.start-bind": async | trappable,
76            default: trappable,
77        },
78        with: {
79            "wasi:io/poll.pollable": latest::io::poll::Pollable,
80            "wasi:io/streams.input-stream": latest::io::streams::InputStream,
81            "wasi:io/streams.output-stream": latest::io::streams::OutputStream,
82            "wasi:io/streams.error": latest::io::streams::Error,
83            "wasi:filesystem/types.directory-entry-stream": latest::filesystem::types::DirectoryEntryStream,
84            "wasi:filesystem/types.descriptor": latest::filesystem::types::Descriptor,
85            "wasi:cli/terminal-input.terminal-input": latest::cli::terminal_input::TerminalInput,
86            "wasi:cli/terminal-output.terminal-output": latest::cli::terminal_output::TerminalOutput,
87            "wasi:sockets/tcp.tcp-socket": latest::sockets::tcp::TcpSocket,
88            "wasi:sockets/udp.udp-socket": UdpSocket,
89            "wasi:sockets/network.network": latest::sockets::network::Network,
90            "wasi:sockets/ip-name-lookup.resolve-address-stream": latest::sockets::ip_name_lookup::ResolveAddressStream,
91        },
92    });
93}
94
95mod wasi {
96    pub use super::bindings::wasi::{
97        cli0_2_0_rc_2023_10_18 as cli, clocks0_2_0_rc_2023_10_18 as clocks,
98        filesystem0_2_0_rc_2023_10_18 as filesystem, io0_2_0_rc_2023_10_18 as io,
99        random0_2_0_rc_2023_10_18 as random, sockets0_2_0_rc_2023_10_18 as sockets,
100    };
101}
102
103use wasi::cli::terminal_input::TerminalInput;
104use wasi::cli::terminal_output::TerminalOutput;
105use wasi::clocks::monotonic_clock::Instant;
106use wasi::clocks::wall_clock::Datetime;
107use wasi::filesystem::types::{
108    AccessType, Advice, Descriptor, DescriptorFlags, DescriptorStat, DescriptorType,
109    DirectoryEntry, DirectoryEntryStream, Error, ErrorCode as FsErrorCode, Filesize,
110    MetadataHashValue, Modes, NewTimestamp, OpenFlags, PathFlags,
111};
112use wasi::io::streams::{InputStream, OutputStream, StreamError};
113use wasi::sockets::ip_name_lookup::{IpAddress, ResolveAddressStream};
114use wasi::sockets::network::{Ipv4SocketAddress, Ipv6SocketAddress};
115use wasi::sockets::tcp::{
116    ErrorCode as SocketErrorCode, IpAddressFamily, IpSocketAddress, Network, ShutdownType,
117    TcpSocket,
118};
119use wasi::sockets::udp::Datagram;
120
121use crate::HasIo;
122
123pub fn add_to_linker<T>(
124    linker: &mut Linker<T>,
125    io_closure: fn(&mut T) -> &mut ResourceTable,
126    random_closure: fn(&mut T) -> &mut WasiRandomCtx,
127    clocks_closure: fn(&mut T) -> WasiClocksCtxView<'_>,
128    cli_closure: fn(&mut T) -> WasiCliCtxView<'_>,
129    filesystem_closure: fn(&mut T) -> WasiFilesystemCtxView<'_>,
130    sockets_closure: fn(&mut T) -> SpinSocketsView<'_, T>,
131) -> Result<()>
132where
133    T: Send + 'static,
134{
135    wasi::clocks::monotonic_clock::add_to_linker::<_, WasiClocks>(linker, clocks_closure)?;
136    wasi::clocks::wall_clock::add_to_linker::<_, WasiClocks>(linker, clocks_closure)?;
137    wasi::filesystem::types::add_to_linker::<_, WasiFilesystem>(linker, filesystem_closure)?;
138    wasi::filesystem::preopens::add_to_linker::<_, WasiFilesystem>(linker, filesystem_closure)?;
139    wasi::io::poll::add_to_linker::<_, HasIo>(linker, io_closure)?;
140    wasi::io::streams::add_to_linker::<_, HasIo>(linker, io_closure)?;
141    wasi::random::random::add_to_linker::<_, WasiRandom>(linker, random_closure)?;
142    wasi::random::insecure::add_to_linker::<_, WasiRandom>(linker, random_closure)?;
143    wasi::random::insecure_seed::add_to_linker::<_, WasiRandom>(linker, random_closure)?;
144    wasi::cli::exit::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
145    wasi::cli::environment::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
146    wasi::cli::stdin::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
147    wasi::cli::stdout::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
148    wasi::cli::stderr::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
149    wasi::cli::terminal_input::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
150    wasi::cli::terminal_output::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
151    wasi::cli::terminal_stdin::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
152    wasi::cli::terminal_stdout::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
153    wasi::cli::terminal_stderr::add_to_linker::<_, WasiCli>(linker, cli_closure)?;
154    wasi::sockets::tcp::add_to_linker::<_, SpinSockets<T>>(linker, sockets_closure)?;
155    wasi::sockets::tcp_create_socket::add_to_linker::<_, SpinSockets<T>>(linker, sockets_closure)?;
156    wasi::sockets::udp::add_to_linker::<_, SpinSockets<T>>(linker, sockets_closure)?;
157    wasi::sockets::udp_create_socket::add_to_linker::<_, SpinSockets<T>>(linker, sockets_closure)?;
158    wasi::sockets::instance_network::add_to_linker::<_, SpinSockets<T>>(linker, sockets_closure)?;
159    wasi::sockets::network::add_to_linker::<_, SpinSockets<T>>(linker, sockets_closure)?;
160    wasi::sockets::ip_name_lookup::add_to_linker::<_, SpinSockets<T>>(linker, sockets_closure)?;
161    Ok(())
162}
163
164impl wasi::clocks::monotonic_clock::Host for WasiClocksCtxView<'_> {
165    fn now(&mut self) -> wasmtime::Result<Instant> {
166        latest::clocks::monotonic_clock::Host::now(self)
167    }
168
169    fn resolution(&mut self) -> wasmtime::Result<Instant> {
170        latest::clocks::monotonic_clock::Host::resolution(self)
171    }
172
173    fn subscribe(
174        &mut self,
175        when: Instant,
176        absolute: bool,
177    ) -> wasmtime::Result<Resource<DynPollable>> {
178        if absolute {
179            latest::clocks::monotonic_clock::Host::subscribe_instant(self, when)
180        } else {
181            latest::clocks::monotonic_clock::Host::subscribe_duration(self, when)
182        }
183    }
184}
185
186impl wasi::clocks::wall_clock::Host for WasiClocksCtxView<'_> {
187    fn now(&mut self) -> wasmtime::Result<Datetime> {
188        Ok(latest::clocks::wall_clock::Host::now(self)?.into())
189    }
190
191    fn resolution(&mut self) -> wasmtime::Result<Datetime> {
192        Ok(latest::clocks::wall_clock::Host::resolution(self)?.into())
193    }
194}
195
196impl wasi::filesystem::types::Host for WasiFilesystemCtxView<'_> {
197    fn filesystem_error_code(
198        &mut self,
199        err: Resource<wasi::filesystem::types::Error>,
200    ) -> wasmtime::Result<Option<FsErrorCode>> {
201        Ok(latest::filesystem::types::Host::filesystem_error_code(self, err)?.map(|e| e.into()))
202    }
203}
204
205impl wasi::filesystem::types::HostDescriptor for WasiFilesystemCtxView<'_> {
206    fn read_via_stream(
207        &mut self,
208        self_: Resource<Descriptor>,
209        offset: Filesize,
210    ) -> wasmtime::Result<Result<Resource<InputStream>, FsErrorCode>> {
211        convert_result(latest::filesystem::types::HostDescriptor::read_via_stream(
212            self, self_, offset,
213        ))
214    }
215
216    fn write_via_stream(
217        &mut self,
218        self_: Resource<Descriptor>,
219        offset: Filesize,
220    ) -> wasmtime::Result<Result<Resource<OutputStream>, FsErrorCode>> {
221        convert_result(latest::filesystem::types::HostDescriptor::write_via_stream(
222            self, self_, offset,
223        ))
224    }
225
226    fn append_via_stream(
227        &mut self,
228        self_: Resource<Descriptor>,
229    ) -> wasmtime::Result<Result<Resource<OutputStream>, FsErrorCode>> {
230        convert_result(latest::filesystem::types::HostDescriptor::append_via_stream(self, self_))
231    }
232
233    async fn advise(
234        &mut self,
235        self_: Resource<Descriptor>,
236        offset: Filesize,
237        length: Filesize,
238        advice: Advice,
239    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
240        convert_result(
241            latest::filesystem::types::HostDescriptor::advise(
242                self,
243                self_,
244                offset,
245                length,
246                advice.into(),
247            )
248            .await,
249        )
250    }
251
252    async fn sync_data(
253        &mut self,
254        self_: Resource<Descriptor>,
255    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
256        convert_result(latest::filesystem::types::HostDescriptor::sync_data(self, self_).await)
257    }
258
259    async fn get_flags(
260        &mut self,
261        self_: Resource<Descriptor>,
262    ) -> wasmtime::Result<Result<DescriptorFlags, FsErrorCode>> {
263        convert_result(latest::filesystem::types::HostDescriptor::get_flags(self, self_).await)
264    }
265
266    async fn get_type(
267        &mut self,
268        self_: Resource<Descriptor>,
269    ) -> wasmtime::Result<Result<DescriptorType, FsErrorCode>> {
270        convert_result(latest::filesystem::types::HostDescriptor::get_type(self, self_).await)
271    }
272
273    async fn set_size(
274        &mut self,
275        self_: Resource<Descriptor>,
276        size: Filesize,
277    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
278        convert_result(latest::filesystem::types::HostDescriptor::set_size(self, self_, size).await)
279    }
280
281    async fn set_times(
282        &mut self,
283        self_: Resource<Descriptor>,
284        data_access_timestamp: NewTimestamp,
285        data_modification_timestamp: NewTimestamp,
286    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
287        convert_result(
288            latest::filesystem::types::HostDescriptor::set_times(
289                self,
290                self_,
291                data_access_timestamp.into(),
292                data_modification_timestamp.into(),
293            )
294            .await,
295        )
296    }
297
298    async fn read(
299        &mut self,
300        self_: Resource<Descriptor>,
301        length: Filesize,
302        offset: Filesize,
303    ) -> wasmtime::Result<Result<(Vec<u8>, bool), FsErrorCode>> {
304        convert_result(
305            latest::filesystem::types::HostDescriptor::read(self, self_, length, offset).await,
306        )
307    }
308
309    async fn write(
310        &mut self,
311        self_: Resource<Descriptor>,
312        buffer: Vec<u8>,
313        offset: Filesize,
314    ) -> wasmtime::Result<Result<Filesize, FsErrorCode>> {
315        convert_result(
316            latest::filesystem::types::HostDescriptor::write(self, self_, buffer, offset).await,
317        )
318    }
319
320    async fn read_directory(
321        &mut self,
322        self_: Resource<Descriptor>,
323    ) -> wasmtime::Result<Result<Resource<DirectoryEntryStream>, FsErrorCode>> {
324        convert_result(latest::filesystem::types::HostDescriptor::read_directory(self, self_).await)
325    }
326
327    async fn sync(
328        &mut self,
329        self_: Resource<Descriptor>,
330    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
331        convert_result(latest::filesystem::types::HostDescriptor::sync(self, self_).await)
332    }
333
334    async fn create_directory_at(
335        &mut self,
336        self_: Resource<Descriptor>,
337        path: String,
338    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
339        convert_result(
340            latest::filesystem::types::HostDescriptor::create_directory_at(self, self_, path).await,
341        )
342    }
343
344    async fn stat(
345        &mut self,
346        self_: Resource<Descriptor>,
347    ) -> wasmtime::Result<Result<DescriptorStat, FsErrorCode>> {
348        convert_result(latest::filesystem::types::HostDescriptor::stat(self, self_).await)
349    }
350
351    async fn stat_at(
352        &mut self,
353        self_: Resource<Descriptor>,
354        path_flags: PathFlags,
355        path: String,
356    ) -> wasmtime::Result<Result<DescriptorStat, FsErrorCode>> {
357        convert_result(
358            latest::filesystem::types::HostDescriptor::stat_at(
359                self,
360                self_,
361                path_flags.into(),
362                path,
363            )
364            .await,
365        )
366    }
367
368    async fn set_times_at(
369        &mut self,
370        self_: Resource<Descriptor>,
371        path_flags: PathFlags,
372        path: String,
373        data_access_timestamp: NewTimestamp,
374        data_modification_timestamp: NewTimestamp,
375    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
376        convert_result(
377            latest::filesystem::types::HostDescriptor::set_times_at(
378                self,
379                self_,
380                path_flags.into(),
381                path,
382                data_access_timestamp.into(),
383                data_modification_timestamp.into(),
384            )
385            .await,
386        )
387    }
388
389    async fn link_at(
390        &mut self,
391        self_: Resource<Descriptor>,
392        old_path_flags: PathFlags,
393        old_path: String,
394        new_descriptor: Resource<Descriptor>,
395        new_path: String,
396    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
397        convert_result(
398            latest::filesystem::types::HostDescriptor::link_at(
399                self,
400                self_,
401                old_path_flags.into(),
402                old_path,
403                new_descriptor,
404                new_path,
405            )
406            .await,
407        )
408    }
409
410    async fn open_at(
411        &mut self,
412        self_: Resource<Descriptor>,
413        path_flags: PathFlags,
414        path: String,
415        open_flags: OpenFlags,
416        flags: DescriptorFlags,
417        _modes: Modes,
418    ) -> wasmtime::Result<Result<Resource<Descriptor>, FsErrorCode>> {
419        convert_result(
420            latest::filesystem::types::HostDescriptor::open_at(
421                self,
422                self_,
423                path_flags.into(),
424                path,
425                open_flags.into(),
426                flags.into(),
427            )
428            .await,
429        )
430    }
431
432    async fn readlink_at(
433        &mut self,
434        self_: Resource<Descriptor>,
435        path: String,
436    ) -> wasmtime::Result<Result<String, FsErrorCode>> {
437        convert_result(
438            latest::filesystem::types::HostDescriptor::readlink_at(self, self_, path).await,
439        )
440    }
441
442    async fn remove_directory_at(
443        &mut self,
444        self_: Resource<Descriptor>,
445        path: String,
446    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
447        convert_result(
448            latest::filesystem::types::HostDescriptor::remove_directory_at(self, self_, path).await,
449        )
450    }
451
452    async fn rename_at(
453        &mut self,
454        self_: Resource<Descriptor>,
455        old_path: String,
456        new_descriptor: Resource<Descriptor>,
457        new_path: String,
458    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
459        convert_result(
460            latest::filesystem::types::HostDescriptor::rename_at(
461                self,
462                self_,
463                old_path,
464                new_descriptor,
465                new_path,
466            )
467            .await,
468        )
469    }
470
471    async fn symlink_at(
472        &mut self,
473        self_: Resource<Descriptor>,
474        old_path: String,
475        new_path: String,
476    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
477        convert_result(
478            latest::filesystem::types::HostDescriptor::symlink_at(self, self_, old_path, new_path)
479                .await,
480        )
481    }
482
483    async fn access_at(
484        &mut self,
485        _self_: Resource<Descriptor>,
486        _path_flags: PathFlags,
487        _path: String,
488        _type_: AccessType,
489    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
490        wasmtime::bail!("access-at API is no longer supported in the latest snapshot")
491    }
492
493    async fn unlink_file_at(
494        &mut self,
495        self_: Resource<Descriptor>,
496        path: String,
497    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
498        convert_result(
499            latest::filesystem::types::HostDescriptor::unlink_file_at(self, self_, path).await,
500        )
501    }
502
503    async fn change_file_permissions_at(
504        &mut self,
505        _self_: Resource<Descriptor>,
506        _path_flags: PathFlags,
507        _path: String,
508        _modes: Modes,
509    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
510        wasmtime::bail!(
511            "change-file-permissions-at API is no longer supported in the latest snapshot"
512        )
513    }
514
515    async fn change_directory_permissions_at(
516        &mut self,
517        _self_: Resource<Descriptor>,
518        _path_flags: PathFlags,
519        _path: String,
520        _modes: Modes,
521    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
522        wasmtime::bail!(
523            "change-directory-permissions-at API is no longer supported in the latest snapshot"
524        )
525    }
526
527    async fn lock_shared(
528        &mut self,
529        _self_: Resource<Descriptor>,
530    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
531        wasmtime::bail!("lock-shared API is no longer supported in the latest snapshot")
532    }
533
534    async fn lock_exclusive(
535        &mut self,
536        _self_: Resource<Descriptor>,
537    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
538        wasmtime::bail!("lock-exclusive API is no longer supported in the latest snapshot")
539    }
540
541    async fn try_lock_shared(
542        &mut self,
543        _self_: Resource<Descriptor>,
544    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
545        wasmtime::bail!("try-lock-shared API is no longer supported in the latest snapshot")
546    }
547
548    async fn try_lock_exclusive(
549        &mut self,
550        _self_: Resource<Descriptor>,
551    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
552        wasmtime::bail!("try-lock-exclusive API is no longer supported in the latest snapshot")
553    }
554
555    async fn unlock(
556        &mut self,
557        _self_: Resource<Descriptor>,
558    ) -> wasmtime::Result<Result<(), FsErrorCode>> {
559        wasmtime::bail!("unlock API is no longer supported in the latest snapshot")
560    }
561
562    async fn is_same_object(
563        &mut self,
564        self_: Resource<Descriptor>,
565        other: Resource<Descriptor>,
566    ) -> wasmtime::Result<bool> {
567        latest::filesystem::types::HostDescriptor::is_same_object(self, self_, other).await
568    }
569
570    async fn metadata_hash(
571        &mut self,
572        self_: Resource<Descriptor>,
573    ) -> wasmtime::Result<Result<MetadataHashValue, FsErrorCode>> {
574        convert_result(latest::filesystem::types::HostDescriptor::metadata_hash(self, self_).await)
575    }
576
577    async fn metadata_hash_at(
578        &mut self,
579        self_: Resource<Descriptor>,
580        path_flags: PathFlags,
581        path: String,
582    ) -> wasmtime::Result<Result<MetadataHashValue, FsErrorCode>> {
583        convert_result(
584            latest::filesystem::types::HostDescriptor::metadata_hash_at(
585                self,
586                self_,
587                path_flags.into(),
588                path,
589            )
590            .await,
591        )
592    }
593
594    fn drop(&mut self, rep: Resource<Descriptor>) -> wasmtime::Result<()> {
595        latest::filesystem::types::HostDescriptor::drop(self, rep)
596    }
597}
598
599impl wasi::filesystem::types::HostDirectoryEntryStream for WasiFilesystemCtxView<'_> {
600    async fn read_directory_entry(
601        &mut self,
602        self_: Resource<DirectoryEntryStream>,
603    ) -> wasmtime::Result<Result<Option<DirectoryEntry>, FsErrorCode>> {
604        convert_result(
605            latest::filesystem::types::HostDirectoryEntryStream::read_directory_entry(self, self_)
606                .await
607                .map(|e| e.map(DirectoryEntry::from)),
608        )
609    }
610
611    fn drop(&mut self, rep: Resource<DirectoryEntryStream>) -> wasmtime::Result<()> {
612        latest::filesystem::types::HostDirectoryEntryStream::drop(self, rep)
613    }
614}
615
616impl wasi::filesystem::preopens::Host for WasiFilesystemCtxView<'_> {
617    fn get_directories(&mut self) -> wasmtime::Result<Vec<(Resource<Descriptor>, String)>> {
618        latest::filesystem::preopens::Host::get_directories(self)
619    }
620}
621
622impl wasi::io::poll::Host for ResourceTable {
623    async fn poll_list(&mut self, list: Vec<Resource<DynPollable>>) -> wasmtime::Result<Vec<u32>> {
624        latest::io::poll::Host::poll(self, list).await
625    }
626
627    async fn poll_one(&mut self, rep: Resource<DynPollable>) -> wasmtime::Result<()> {
628        latest::io::poll::HostPollable::block(self, rep).await
629    }
630}
631
632impl wasi::io::poll::HostPollable for ResourceTable {
633    fn drop(&mut self, rep: Resource<DynPollable>) -> wasmtime::Result<()> {
634        latest::io::poll::HostPollable::drop(self, rep)
635    }
636}
637
638impl wasi::io::streams::Host for ResourceTable {}
639
640impl wasi::io::streams::HostError for ResourceTable {
641    fn to_debug_string(&mut self, self_: Resource<Error>) -> wasmtime::Result<String> {
642        latest::io::error::HostError::to_debug_string(self, self_)
643    }
644
645    fn drop(&mut self, rep: Resource<Error>) -> wasmtime::Result<()> {
646        latest::io::error::HostError::drop(self, rep)
647    }
648}
649
650impl wasi::io::streams::HostInputStream for ResourceTable {
651    fn read(
652        &mut self,
653        self_: Resource<InputStream>,
654        len: u64,
655    ) -> wasmtime::Result<Result<Vec<u8>, StreamError>> {
656        let result = latest::io::streams::HostInputStream::read(self, self_, len);
657        convert_stream_result(self, result)
658    }
659
660    async fn blocking_read(
661        &mut self,
662        self_: Resource<InputStream>,
663        len: u64,
664    ) -> wasmtime::Result<Result<Vec<u8>, StreamError>> {
665        let result = latest::io::streams::HostInputStream::blocking_read(self, self_, len).await;
666        convert_stream_result(self, result)
667    }
668
669    fn skip(
670        &mut self,
671        self_: Resource<InputStream>,
672        len: u64,
673    ) -> wasmtime::Result<Result<u64, StreamError>> {
674        let result = latest::io::streams::HostInputStream::skip(self, self_, len);
675        convert_stream_result(self, result)
676    }
677
678    async fn blocking_skip(
679        &mut self,
680        self_: Resource<InputStream>,
681        len: u64,
682    ) -> wasmtime::Result<Result<u64, StreamError>> {
683        let result = latest::io::streams::HostInputStream::blocking_skip(self, self_, len).await;
684        convert_stream_result(self, result)
685    }
686
687    fn subscribe(
688        &mut self,
689        self_: Resource<InputStream>,
690    ) -> wasmtime::Result<Resource<DynPollable>> {
691        latest::io::streams::HostInputStream::subscribe(self, self_)
692    }
693
694    async fn drop(&mut self, rep: Resource<InputStream>) -> wasmtime::Result<()> {
695        latest::io::streams::HostInputStream::drop(self, rep).await
696    }
697}
698
699impl wasi::io::streams::HostOutputStream for ResourceTable {
700    fn check_write(
701        &mut self,
702        self_: Resource<OutputStream>,
703    ) -> wasmtime::Result<Result<u64, StreamError>> {
704        let result = latest::io::streams::HostOutputStream::check_write(self, self_);
705        convert_stream_result(self, result)
706    }
707
708    fn write(
709        &mut self,
710        self_: Resource<OutputStream>,
711        contents: Vec<u8>,
712    ) -> wasmtime::Result<Result<(), StreamError>> {
713        let result = latest::io::streams::HostOutputStream::write(self, self_, contents);
714        convert_stream_result(self, result)
715    }
716
717    async fn blocking_write_and_flush(
718        &mut self,
719        self_: Resource<OutputStream>,
720        contents: Vec<u8>,
721    ) -> wasmtime::Result<Result<(), StreamError>> {
722        let result =
723            latest::io::streams::HostOutputStream::blocking_write_and_flush(self, self_, contents)
724                .await;
725        convert_stream_result(self, result)
726    }
727
728    fn flush(
729        &mut self,
730        self_: Resource<OutputStream>,
731    ) -> wasmtime::Result<Result<(), StreamError>> {
732        let result = latest::io::streams::HostOutputStream::flush(self, self_);
733        convert_stream_result(self, result)
734    }
735
736    async fn blocking_flush(
737        &mut self,
738        self_: Resource<OutputStream>,
739    ) -> wasmtime::Result<Result<(), StreamError>> {
740        let result = latest::io::streams::HostOutputStream::blocking_flush(self, self_).await;
741        convert_stream_result(self, result)
742    }
743
744    fn subscribe(
745        &mut self,
746        self_: Resource<OutputStream>,
747    ) -> wasmtime::Result<Resource<DynPollable>> {
748        latest::io::streams::HostOutputStream::subscribe(self, self_)
749    }
750
751    fn write_zeroes(
752        &mut self,
753        self_: Resource<OutputStream>,
754        len: u64,
755    ) -> wasmtime::Result<Result<(), StreamError>> {
756        let result = latest::io::streams::HostOutputStream::write_zeroes(self, self_, len);
757        convert_stream_result(self, result)
758    }
759
760    async fn blocking_write_zeroes_and_flush(
761        &mut self,
762        self_: Resource<OutputStream>,
763        len: u64,
764    ) -> wasmtime::Result<Result<(), StreamError>> {
765        let result = latest::io::streams::HostOutputStream::blocking_write_zeroes_and_flush(
766            self, self_, len,
767        )
768        .await;
769        convert_stream_result(self, result)
770    }
771
772    fn splice(
773        &mut self,
774        self_: Resource<OutputStream>,
775        src: Resource<InputStream>,
776        len: u64,
777    ) -> wasmtime::Result<Result<u64, StreamError>> {
778        let result = latest::io::streams::HostOutputStream::splice(self, self_, src, len);
779        convert_stream_result(self, result)
780    }
781
782    async fn blocking_splice(
783        &mut self,
784        self_: Resource<OutputStream>,
785        src: Resource<InputStream>,
786        len: u64,
787    ) -> wasmtime::Result<Result<u64, StreamError>> {
788        let result =
789            latest::io::streams::HostOutputStream::blocking_splice(self, self_, src, len).await;
790        convert_stream_result(self, result)
791    }
792
793    async fn forward(
794        &mut self,
795        _self_: Resource<OutputStream>,
796        _src: Resource<InputStream>,
797    ) -> wasmtime::Result<Result<u64, StreamError>> {
798        wasmtime::bail!("forward API no longer supported")
799    }
800
801    async fn drop(&mut self, rep: Resource<OutputStream>) -> wasmtime::Result<()> {
802        latest::io::streams::HostOutputStream::drop(self, rep).await
803    }
804}
805
806impl wasi::random::random::Host for WasiRandomCtx {
807    fn get_random_bytes(&mut self, len: u64) -> wasmtime::Result<Vec<u8>> {
808        latest::random::random::Host::get_random_bytes(self, len)
809    }
810
811    fn get_random_u64(&mut self) -> wasmtime::Result<u64> {
812        latest::random::random::Host::get_random_u64(self)
813    }
814}
815
816impl wasi::random::insecure::Host for WasiRandomCtx {
817    fn get_insecure_random_bytes(&mut self, len: u64) -> wasmtime::Result<Vec<u8>> {
818        latest::random::insecure::Host::get_insecure_random_bytes(self, len)
819    }
820
821    fn get_insecure_random_u64(&mut self) -> wasmtime::Result<u64> {
822        latest::random::insecure::Host::get_insecure_random_u64(self)
823    }
824}
825
826impl wasi::random::insecure_seed::Host for WasiRandomCtx {
827    fn insecure_seed(&mut self) -> wasmtime::Result<(u64, u64)> {
828        latest::random::insecure_seed::Host::insecure_seed(self)
829    }
830}
831
832impl wasi::cli::exit::Host for WasiCliCtxView<'_> {
833    fn exit(&mut self, status: Result<(), ()>) -> wasmtime::Result<()> {
834        latest::cli::exit::Host::exit(self, status)
835    }
836}
837
838impl wasi::cli::environment::Host for WasiCliCtxView<'_> {
839    fn get_environment(&mut self) -> wasmtime::Result<Vec<(String, String)>> {
840        latest::cli::environment::Host::get_environment(self)
841    }
842
843    fn get_arguments(&mut self) -> wasmtime::Result<Vec<String>> {
844        latest::cli::environment::Host::get_arguments(self)
845    }
846
847    fn initial_cwd(&mut self) -> wasmtime::Result<Option<String>> {
848        latest::cli::environment::Host::initial_cwd(self)
849    }
850}
851
852impl wasi::cli::stdin::Host for WasiCliCtxView<'_> {
853    fn get_stdin(&mut self) -> wasmtime::Result<Resource<InputStream>> {
854        latest::cli::stdin::Host::get_stdin(self)
855    }
856}
857
858impl wasi::cli::stdout::Host for WasiCliCtxView<'_> {
859    fn get_stdout(&mut self) -> wasmtime::Result<Resource<OutputStream>> {
860        latest::cli::stdout::Host::get_stdout(self)
861    }
862}
863
864impl wasi::cli::stderr::Host for WasiCliCtxView<'_> {
865    fn get_stderr(&mut self) -> wasmtime::Result<Resource<OutputStream>> {
866        latest::cli::stderr::Host::get_stderr(self)
867    }
868}
869
870impl wasi::cli::terminal_stdin::Host for WasiCliCtxView<'_> {
871    fn get_terminal_stdin(&mut self) -> wasmtime::Result<Option<Resource<TerminalInput>>> {
872        latest::cli::terminal_stdin::Host::get_terminal_stdin(self)
873    }
874}
875
876impl wasi::cli::terminal_stdout::Host for WasiCliCtxView<'_> {
877    fn get_terminal_stdout(&mut self) -> wasmtime::Result<Option<Resource<TerminalOutput>>> {
878        latest::cli::terminal_stdout::Host::get_terminal_stdout(self)
879    }
880}
881
882impl wasi::cli::terminal_stderr::Host for WasiCliCtxView<'_> {
883    fn get_terminal_stderr(&mut self) -> wasmtime::Result<Option<Resource<TerminalOutput>>> {
884        latest::cli::terminal_stderr::Host::get_terminal_stderr(self)
885    }
886}
887
888impl wasi::cli::terminal_input::Host for WasiCliCtxView<'_> {}
889
890impl wasi::cli::terminal_input::HostTerminalInput for WasiCliCtxView<'_> {
891    fn drop(&mut self, rep: Resource<TerminalInput>) -> wasmtime::Result<()> {
892        latest::cli::terminal_input::HostTerminalInput::drop(self, rep)
893    }
894}
895
896impl wasi::cli::terminal_output::Host for WasiCliCtxView<'_> {}
897
898impl wasi::cli::terminal_output::HostTerminalOutput for WasiCliCtxView<'_> {
899    fn drop(&mut self, rep: Resource<TerminalOutput>) -> wasmtime::Result<()> {
900        latest::cli::terminal_output::HostTerminalOutput::drop(self, rep)
901    }
902}
903
904impl<T> wasi::sockets::tcp::Host for SpinSocketsView<'_, T> {}
905
906impl<T> wasi::sockets::tcp::HostTcpSocket for SpinSocketsView<'_, T> {
907    async fn start_bind(
908        &mut self,
909        self_: Resource<TcpSocket>,
910        network: Resource<Network>,
911        local_address: IpSocketAddress,
912    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
913        convert_result(
914            latest::sockets::tcp::HostTcpSocket::start_bind(
915                self,
916                self_,
917                network,
918                local_address.into(),
919            )
920            .await,
921        )
922    }
923
924    fn finish_bind(
925        &mut self,
926        self_: Resource<TcpSocket>,
927    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
928        convert_result(latest::sockets::tcp::HostTcpSocket::finish_bind(
929            self, self_,
930        ))
931    }
932
933    async fn start_connect(
934        &mut self,
935        self_: Resource<TcpSocket>,
936        network: Resource<Network>,
937        remote_address: IpSocketAddress,
938    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
939        // Delegate to the P2 SpinSocketsView impl (passing `self`, not `&mut self.inner`).
940        // This snapshot uses the raw P2 TcpSocket type — the resource rep is the same at
941        // start_connect and drop time — so the P2 impl's quota acquire/register/release
942        // logic round-trips correctly without any wrapper-level bookkeeping here.
943        convert_result(
944            latest::sockets::tcp::HostTcpSocket::start_connect(
945                self,
946                self_,
947                network,
948                remote_address.into(),
949            )
950            .await,
951        )
952    }
953
954    fn finish_connect(
955        &mut self,
956        self_: Resource<TcpSocket>,
957    ) -> wasmtime::Result<Result<(Resource<InputStream>, Resource<OutputStream>), SocketErrorCode>>
958    {
959        convert_result(latest::sockets::tcp::HostTcpSocket::finish_connect(
960            self, self_,
961        ))
962    }
963
964    fn start_listen(
965        &mut self,
966        self_: Resource<TcpSocket>,
967    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
968        convert_result(latest::sockets::tcp::HostTcpSocket::start_listen(
969            self, self_,
970        ))
971    }
972
973    fn finish_listen(
974        &mut self,
975        self_: Resource<TcpSocket>,
976    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
977        convert_result(latest::sockets::tcp::HostTcpSocket::finish_listen(
978            self, self_,
979        ))
980    }
981
982    fn accept(
983        &mut self,
984        self_: Resource<TcpSocket>,
985    ) -> wasmtime::Result<
986        Result<
987            (
988                Resource<TcpSocket>,
989                Resource<InputStream>,
990                Resource<OutputStream>,
991            ),
992            SocketErrorCode,
993        >,
994    > {
995        convert_result(latest::sockets::tcp::HostTcpSocket::accept(self, self_))
996    }
997
998    fn local_address(
999        &mut self,
1000        self_: Resource<TcpSocket>,
1001    ) -> wasmtime::Result<Result<IpSocketAddress, SocketErrorCode>> {
1002        convert_result(latest::sockets::tcp::HostTcpSocket::local_address(
1003            self, self_,
1004        ))
1005    }
1006
1007    fn remote_address(
1008        &mut self,
1009        self_: Resource<TcpSocket>,
1010    ) -> wasmtime::Result<Result<IpSocketAddress, SocketErrorCode>> {
1011        convert_result(latest::sockets::tcp::HostTcpSocket::remote_address(
1012            self, self_,
1013        ))
1014    }
1015
1016    fn address_family(&mut self, self_: Resource<TcpSocket>) -> wasmtime::Result<IpAddressFamily> {
1017        latest::sockets::tcp::HostTcpSocket::address_family(self, self_).map(|e| e.into())
1018    }
1019
1020    fn ipv6_only(
1021        &mut self,
1022        _self_: Resource<TcpSocket>,
1023    ) -> wasmtime::Result<Result<bool, SocketErrorCode>> {
1024        wasmtime::bail!("ipv6-only API no longer supported")
1025    }
1026
1027    fn set_ipv6_only(
1028        &mut self,
1029        _self_: Resource<TcpSocket>,
1030        _value: bool,
1031    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1032        wasmtime::bail!("ipv6-only API no longer supported")
1033    }
1034
1035    fn set_listen_backlog_size(
1036        &mut self,
1037        self_: Resource<TcpSocket>,
1038        value: u64,
1039    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1040        convert_result(
1041            latest::sockets::tcp::HostTcpSocket::set_listen_backlog_size(self, self_, value),
1042        )
1043    }
1044
1045    fn keep_alive(
1046        &mut self,
1047        self_: Resource<TcpSocket>,
1048    ) -> wasmtime::Result<Result<bool, SocketErrorCode>> {
1049        convert_result(latest::sockets::tcp::HostTcpSocket::keep_alive_enabled(
1050            self, self_,
1051        ))
1052    }
1053
1054    fn set_keep_alive(
1055        &mut self,
1056        self_: Resource<TcpSocket>,
1057        value: bool,
1058    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1059        convert_result(latest::sockets::tcp::HostTcpSocket::set_keep_alive_enabled(
1060            self, self_, value,
1061        ))
1062    }
1063
1064    fn no_delay(
1065        &mut self,
1066        _self_: Resource<TcpSocket>,
1067    ) -> wasmtime::Result<Result<bool, SocketErrorCode>> {
1068        wasmtime::bail!("no-delay API no longer supported")
1069    }
1070
1071    fn set_no_delay(
1072        &mut self,
1073        _self_: Resource<TcpSocket>,
1074        _value: bool,
1075    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1076        wasmtime::bail!("set-no-delay API no longer supported")
1077    }
1078
1079    fn unicast_hop_limit(
1080        &mut self,
1081        self_: Resource<TcpSocket>,
1082    ) -> wasmtime::Result<Result<u8, SocketErrorCode>> {
1083        convert_result(latest::sockets::tcp::HostTcpSocket::hop_limit(self, self_))
1084    }
1085
1086    fn set_unicast_hop_limit(
1087        &mut self,
1088        self_: Resource<TcpSocket>,
1089        value: u8,
1090    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1091        convert_result(latest::sockets::tcp::HostTcpSocket::set_hop_limit(
1092            self, self_, value,
1093        ))
1094    }
1095
1096    fn receive_buffer_size(
1097        &mut self,
1098        self_: Resource<TcpSocket>,
1099    ) -> wasmtime::Result<Result<u64, SocketErrorCode>> {
1100        convert_result(latest::sockets::tcp::HostTcpSocket::receive_buffer_size(
1101            self, self_,
1102        ))
1103    }
1104
1105    fn set_receive_buffer_size(
1106        &mut self,
1107        self_: Resource<TcpSocket>,
1108        value: u64,
1109    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1110        convert_result(
1111            latest::sockets::tcp::HostTcpSocket::set_receive_buffer_size(self, self_, value),
1112        )
1113    }
1114
1115    fn send_buffer_size(
1116        &mut self,
1117        self_: Resource<TcpSocket>,
1118    ) -> wasmtime::Result<Result<u64, SocketErrorCode>> {
1119        convert_result(latest::sockets::tcp::HostTcpSocket::send_buffer_size(
1120            self, self_,
1121        ))
1122    }
1123
1124    fn set_send_buffer_size(
1125        &mut self,
1126        self_: Resource<TcpSocket>,
1127        value: u64,
1128    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1129        convert_result(latest::sockets::tcp::HostTcpSocket::set_send_buffer_size(
1130            self, self_, value,
1131        ))
1132    }
1133
1134    fn subscribe(&mut self, self_: Resource<TcpSocket>) -> wasmtime::Result<Resource<DynPollable>> {
1135        latest::sockets::tcp::HostTcpSocket::subscribe(self, self_)
1136    }
1137
1138    fn shutdown(
1139        &mut self,
1140        self_: Resource<TcpSocket>,
1141        shutdown_type: ShutdownType,
1142    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1143        convert_result(latest::sockets::tcp::HostTcpSocket::shutdown(
1144            self,
1145            self_,
1146            shutdown_type.into(),
1147        ))
1148    }
1149
1150    fn drop(&mut self, rep: Resource<TcpSocket>) -> wasmtime::Result<()> {
1151        latest::sockets::tcp::HostTcpSocket::drop(self, rep)
1152    }
1153}
1154
1155impl<T> wasi::sockets::tcp_create_socket::Host for SpinSocketsView<'_, T> {
1156    fn create_tcp_socket(
1157        &mut self,
1158        address_family: IpAddressFamily,
1159    ) -> wasmtime::Result<Result<Resource<TcpSocket>, SocketErrorCode>> {
1160        convert_result(latest::sockets::tcp_create_socket::Host::create_tcp_socket(
1161            self,
1162            address_family.into(),
1163        ))
1164    }
1165}
1166
1167impl<T> wasi::sockets::udp::Host for SpinSocketsView<'_, T> {}
1168
1169/// Between the snapshot of WASI that this file is implementing and the current
1170/// implementation of WASI UDP sockets were redesigned slightly to deal with
1171/// a different way of managing incoming and outgoing datagrams. This means
1172/// that this snapshot's `{start,finish}_connect`, `send`, and `receive`
1173/// methods are no longer natively implemented, so they're polyfilled by this
1174/// implementation.
1175pub enum UdpSocket {
1176    Initial(Resource<latest::sockets::udp::UdpSocket>),
1177    Connecting(Resource<latest::sockets::udp::UdpSocket>, IpSocketAddress),
1178    Connected {
1179        socket: Resource<latest::sockets::udp::UdpSocket>,
1180        incoming: Resource<latest::sockets::udp::IncomingDatagramStream>,
1181        outgoing: Resource<latest::sockets::udp::OutgoingDatagramStream>,
1182    },
1183    Dummy,
1184}
1185
1186impl UdpSocket {
1187    async fn finish_connect<T>(
1188        table: &mut SpinSocketsView<'_, T>,
1189        socket: &Resource<UdpSocket>,
1190        explicit: bool,
1191    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1192        let state = table.table.get_mut(socket)?;
1193        let (new_socket, addr) = match mem::replace(state, UdpSocket::Dummy) {
1194            // Implicit finishes will call `stream` for sockets in the initial
1195            // state.
1196            UdpSocket::Initial(socket) if !explicit => (socket, None),
1197            // Implicit finishes won't try to reconnect a socket.
1198            UdpSocket::Connected { .. } if !explicit => return Ok(Ok(())),
1199            // Only explicit finishes can transition from the `Connecting` state.
1200            UdpSocket::Connecting(socket, addr) if explicit => (socket, Some(addr)),
1201            _ => return Ok(Err(SocketErrorCode::ConcurrencyConflict)),
1202        };
1203        let borrow = Resource::new_borrow(new_socket.rep());
1204        let result = convert_result(
1205            latest::sockets::udp::HostUdpSocket::stream(
1206                &mut table.inner,
1207                borrow,
1208                addr.map(|a| a.into()),
1209            )
1210            .await,
1211        )?;
1212        let (incoming, outgoing) = match result {
1213            Ok(pair) => pair,
1214            Err(e) => return Ok(Err(e)),
1215        };
1216        *table.table.get_mut(socket)? = UdpSocket::Connected {
1217            socket: new_socket,
1218            incoming,
1219            outgoing,
1220        };
1221        Ok(Ok(()))
1222    }
1223
1224    fn inner(&self) -> wasmtime::Result<Resource<latest::sockets::udp::UdpSocket>> {
1225        let r = match self {
1226            UdpSocket::Initial(r) => r,
1227            UdpSocket::Connecting(r, _) => r,
1228            UdpSocket::Connected { socket, .. } => socket,
1229            UdpSocket::Dummy => wasmtime::bail!("invalid udp socket state"),
1230        };
1231        Ok(Resource::new_borrow(r.rep()))
1232    }
1233}
1234
1235impl<T> wasi::sockets::udp::HostUdpSocket for SpinSocketsView<'_, T> {
1236    async fn start_bind(
1237        &mut self,
1238        self_: Resource<UdpSocket>,
1239        network: Resource<Network>,
1240        local_address: IpSocketAddress,
1241    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1242        let socket = self.table.get(&self_)?.inner()?;
1243        convert_result(
1244            latest::sockets::udp::HostUdpSocket::start_bind(
1245                &mut self.inner,
1246                socket,
1247                network,
1248                local_address.into(),
1249            )
1250            .await,
1251        )
1252    }
1253
1254    fn finish_bind(
1255        &mut self,
1256        self_: Resource<UdpSocket>,
1257    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1258        let socket = self.table.get(&self_)?.inner()?;
1259        convert_result(latest::sockets::udp::HostUdpSocket::finish_bind(
1260            &mut self.inner,
1261            socket,
1262        ))
1263    }
1264
1265    fn start_connect(
1266        &mut self,
1267        self_: Resource<UdpSocket>,
1268        _network: Resource<Network>,
1269        remote_address: IpSocketAddress,
1270    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1271        let socket = self.table.get_mut(&self_)?;
1272        let (new_state, result) = match mem::replace(socket, UdpSocket::Dummy) {
1273            UdpSocket::Initial(socket) => (UdpSocket::Connecting(socket, remote_address), Ok(())),
1274            other => (other, Err(SocketErrorCode::ConcurrencyConflict)),
1275        };
1276        *socket = new_state;
1277        Ok(result)
1278    }
1279
1280    async fn finish_connect(
1281        &mut self,
1282        self_: Resource<UdpSocket>,
1283    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1284        UdpSocket::finish_connect(self, &self_, true).await
1285    }
1286
1287    async fn receive(
1288        &mut self,
1289        self_: Resource<UdpSocket>,
1290        max_results: u64,
1291    ) -> wasmtime::Result<Result<Vec<Datagram>, SocketErrorCode>> {
1292        // If the socket is in the `initial` state then complete the connect,
1293        // otherwise verify we're connected.
1294        if let Err(e) = UdpSocket::finish_connect(self, &self_, true).await? {
1295            return Ok(Err(e));
1296        }
1297
1298        // Use our connected state to acquire the `incoming-datagram-stream`
1299        // resource, then receive some datagrams.
1300        let incoming = match self.table.get(&self_)? {
1301            UdpSocket::Connected { incoming, .. } => Resource::new_borrow(incoming.rep()),
1302            _ => return Ok(Err(SocketErrorCode::ConcurrencyConflict)),
1303        };
1304        let result: Result<Vec<_>, _> = convert_result(
1305            latest::sockets::udp::HostIncomingDatagramStream::receive(self, incoming, max_results),
1306        )?;
1307        match result {
1308            Ok(datagrams) => Ok(Ok(datagrams
1309                .into_iter()
1310                .map(|datagram| datagram.into())
1311                .collect())),
1312            Err(e) => Ok(Err(e)),
1313        }
1314    }
1315
1316    async fn send(
1317        &mut self,
1318        self_: Resource<UdpSocket>,
1319        mut datagrams: Vec<Datagram>,
1320    ) -> wasmtime::Result<Result<u64, SocketErrorCode>> {
1321        // If the socket is in the `initial` state then complete the connect,
1322        // otherwise verify we're connected.
1323        if let Err(e) = UdpSocket::finish_connect(self, &self_, true).await? {
1324            return Ok(Err(e));
1325        }
1326
1327        // Use our connected state to acquire the `outgoing-datagram-stream`
1328        // resource.
1329        let outgoing = match self.table.get(&self_)? {
1330            UdpSocket::Connected { outgoing, .. } => Resource::new_borrow(outgoing.rep()),
1331            _ => return Ok(Err(SocketErrorCode::ConcurrencyConflict)),
1332        };
1333
1334        // Acquire a sending permit for some datagrams, truncating our list to
1335        // that size if we have one.
1336        let outgoing2 = Resource::new_borrow(outgoing.rep());
1337        match convert_result(
1338            latest::sockets::udp::HostOutgoingDatagramStream::check_send(self, outgoing2),
1339        )? {
1340            Ok(n) => {
1341                if datagrams.len() as u64 > n {
1342                    datagrams.truncate(n as usize);
1343                }
1344            }
1345            Err(e) => return Ok(Err(e)),
1346        }
1347
1348        // Send off the datagrams.
1349        convert_result(
1350            latest::sockets::udp::HostOutgoingDatagramStream::send(
1351                self,
1352                outgoing,
1353                datagrams
1354                    .into_iter()
1355                    .map(|d| latest::sockets::udp::OutgoingDatagram {
1356                        data: d.data,
1357                        remote_address: Some(d.remote_address.into()),
1358                    })
1359                    .collect(),
1360            )
1361            .await,
1362        )
1363    }
1364
1365    fn local_address(
1366        &mut self,
1367        self_: Resource<UdpSocket>,
1368    ) -> wasmtime::Result<Result<IpSocketAddress, SocketErrorCode>> {
1369        let socket = self.table.get(&self_)?.inner()?;
1370        convert_result(latest::sockets::udp::HostUdpSocket::local_address(
1371            &mut self.inner,
1372            socket,
1373        ))
1374    }
1375
1376    fn remote_address(
1377        &mut self,
1378        self_: Resource<UdpSocket>,
1379    ) -> wasmtime::Result<Result<IpSocketAddress, SocketErrorCode>> {
1380        let socket = self.table.get(&self_)?.inner()?;
1381        convert_result(latest::sockets::udp::HostUdpSocket::remote_address(
1382            &mut self.inner,
1383            socket,
1384        ))
1385    }
1386
1387    fn address_family(&mut self, self_: Resource<UdpSocket>) -> wasmtime::Result<IpAddressFamily> {
1388        let socket = self.table.get(&self_)?.inner()?;
1389        latest::sockets::udp::HostUdpSocket::address_family(&mut self.inner, socket)
1390            .map(|e| e.into())
1391    }
1392
1393    fn ipv6_only(
1394        &mut self,
1395        _self_: Resource<UdpSocket>,
1396    ) -> wasmtime::Result<Result<bool, SocketErrorCode>> {
1397        wasmtime::bail!("ipv6-only API no longer supported")
1398    }
1399
1400    fn set_ipv6_only(
1401        &mut self,
1402        _self_: Resource<UdpSocket>,
1403        _value: bool,
1404    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1405        wasmtime::bail!("ipv6-only API no longer supported")
1406    }
1407
1408    fn unicast_hop_limit(
1409        &mut self,
1410        self_: Resource<UdpSocket>,
1411    ) -> wasmtime::Result<Result<u8, SocketErrorCode>> {
1412        let socket = self.table.get(&self_)?.inner()?;
1413        convert_result(latest::sockets::udp::HostUdpSocket::unicast_hop_limit(
1414            &mut self.inner,
1415            socket,
1416        ))
1417    }
1418
1419    fn set_unicast_hop_limit(
1420        &mut self,
1421        self_: Resource<UdpSocket>,
1422        value: u8,
1423    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1424        let socket = self.table.get(&self_)?.inner()?;
1425        convert_result(latest::sockets::udp::HostUdpSocket::set_unicast_hop_limit(
1426            &mut self.inner,
1427            socket,
1428            value,
1429        ))
1430    }
1431
1432    fn receive_buffer_size(
1433        &mut self,
1434        self_: Resource<UdpSocket>,
1435    ) -> wasmtime::Result<Result<u64, SocketErrorCode>> {
1436        let socket = self.table.get(&self_)?.inner()?;
1437        convert_result(latest::sockets::udp::HostUdpSocket::receive_buffer_size(
1438            &mut self.inner,
1439            socket,
1440        ))
1441    }
1442
1443    fn set_receive_buffer_size(
1444        &mut self,
1445        self_: Resource<UdpSocket>,
1446        value: u64,
1447    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1448        let socket = self.table.get(&self_)?.inner()?;
1449        convert_result(
1450            latest::sockets::udp::HostUdpSocket::set_receive_buffer_size(
1451                &mut self.inner,
1452                socket,
1453                value,
1454            ),
1455        )
1456    }
1457
1458    fn send_buffer_size(
1459        &mut self,
1460        self_: Resource<UdpSocket>,
1461    ) -> wasmtime::Result<Result<u64, SocketErrorCode>> {
1462        let socket = self.table.get(&self_)?.inner()?;
1463        convert_result(latest::sockets::udp::HostUdpSocket::send_buffer_size(
1464            &mut self.inner,
1465            socket,
1466        ))
1467    }
1468
1469    fn set_send_buffer_size(
1470        &mut self,
1471        self_: Resource<UdpSocket>,
1472        value: u64,
1473    ) -> wasmtime::Result<Result<(), SocketErrorCode>> {
1474        let socket = self.table.get(&self_)?.inner()?;
1475        convert_result(latest::sockets::udp::HostUdpSocket::set_send_buffer_size(
1476            &mut self.inner,
1477            socket,
1478            value,
1479        ))
1480    }
1481
1482    fn subscribe(&mut self, self_: Resource<UdpSocket>) -> wasmtime::Result<Resource<DynPollable>> {
1483        let socket = self.table.get(&self_)?.inner()?;
1484        latest::sockets::udp::HostUdpSocket::subscribe(&mut self.inner, socket)
1485    }
1486
1487    fn drop(&mut self, rep: Resource<UdpSocket>) -> wasmtime::Result<()> {
1488        let socket_rep = rep.rep();
1489        // Delete before releasing: the only error case that matters is `HasChildren`,
1490        // where the socket still exists and the permit must stay held. `NotPresent`
1491        // (double-drop) is unreachable from a guest, and `release_permit` is idempotent
1492        // anyway since `HashMap::remove` is a no-op for absent keys.
1493        let me = self.table.delete(rep)?;
1494        self.release_permit(socket_rep);
1495        let socket = match me {
1496            UdpSocket::Initial(s) => s,
1497            UdpSocket::Connecting(s, _) => s,
1498            UdpSocket::Connected {
1499                socket,
1500                incoming,
1501                outgoing,
1502            } => {
1503                latest::sockets::udp::HostIncomingDatagramStream::drop(&mut self.inner, incoming)?;
1504                latest::sockets::udp::HostOutgoingDatagramStream::drop(&mut self.inner, outgoing)?;
1505                socket
1506            }
1507            UdpSocket::Dummy => return Ok(()),
1508        };
1509        // Drop the inner P2 socket directly, bypassing quota tracking for rep R.
1510        latest::sockets::udp::HostUdpSocket::drop(&mut self.inner, socket)
1511    }
1512}
1513
1514impl<T> wasi::sockets::udp_create_socket::Host for SpinSocketsView<'_, T> {
1515    fn create_udp_socket(
1516        &mut self,
1517        address_family: IpAddressFamily,
1518    ) -> wasmtime::Result<Result<Resource<UdpSocket>, SocketErrorCode>> {
1519        // Cannot delegate to the P2 SpinSocketsView impl here (unlike TCP). This snapshot
1520        // wraps the P2 UdpSocket in a custom UdpSocket enum stored in a separate resource
1521        // table, so the outer wrapper rep (used at drop time) differs from the inner P2
1522        // socket rep (which the P2 impl would register the permit under). Delegating would
1523        // cause release_permit at drop time to look up the wrong rep and silently leak the
1524        // semaphore slot. Instead, quota is checked explicitly here and the permit is
1525        // registered under the wrapper rep.
1526        let Ok(permit) = self.try_acquire() else {
1527            tracing::warn!("UDP socket creation refused: connection quota exhausted");
1528            return Ok(Err(SocketErrorCode::NewSocketLimit));
1529        };
1530        // Create the inner P2 socket via self.inner to avoid charging quota at the P2 level.
1531        let result = convert_result(p2_udp_create::Host::create_udp_socket(
1532            &mut self.inner,
1533            address_family.into(),
1534        ))?;
1535        let socket = match result {
1536            Ok(socket) => socket,
1537            Err(e) => return Ok(Err(e)),
1538        };
1539        let wrapped = self.table.push(UdpSocket::Initial(socket))?;
1540        self.register_permit(wrapped.rep(), permit);
1541        Ok(Ok(wrapped))
1542    }
1543}
1544
1545impl<T> wasi::sockets::instance_network::Host for SpinSocketsView<'_, T> {
1546    fn instance_network(&mut self) -> wasmtime::Result<Resource<Network>> {
1547        latest::sockets::instance_network::Host::instance_network(&mut self.inner)
1548    }
1549}
1550
1551impl<T> wasi::sockets::network::Host for SpinSocketsView<'_, T> {}
1552
1553impl<T> wasi::sockets::network::HostNetwork for SpinSocketsView<'_, T> {
1554    fn drop(&mut self, rep: Resource<Network>) -> wasmtime::Result<()> {
1555        latest::sockets::network::HostNetwork::drop(&mut self.inner, rep)
1556    }
1557}
1558
1559impl<T> wasi::sockets::ip_name_lookup::Host for SpinSocketsView<'_, T> {
1560    fn resolve_addresses(
1561        &mut self,
1562        network: Resource<Network>,
1563        name: String,
1564        _address_family: Option<IpAddressFamily>,
1565        _include_unavailable: bool,
1566    ) -> wasmtime::Result<Result<Resource<ResolveAddressStream>, SocketErrorCode>> {
1567        convert_result(latest::sockets::ip_name_lookup::Host::resolve_addresses(
1568            &mut self.inner,
1569            network,
1570            name,
1571        ))
1572    }
1573}
1574
1575impl<T> wasi::sockets::ip_name_lookup::HostResolveAddressStream for SpinSocketsView<'_, T> {
1576    fn resolve_next_address(
1577        &mut self,
1578        self_: Resource<ResolveAddressStream>,
1579    ) -> wasmtime::Result<Result<Option<IpAddress>, SocketErrorCode>> {
1580        convert_result(
1581            latest::sockets::ip_name_lookup::HostResolveAddressStream::resolve_next_address(
1582                &mut self.inner,
1583                self_,
1584            )
1585            .map(|e| e.map(|e| e.into())),
1586        )
1587    }
1588
1589    fn subscribe(
1590        &mut self,
1591        self_: Resource<ResolveAddressStream>,
1592    ) -> wasmtime::Result<Resource<DynPollable>> {
1593        latest::sockets::ip_name_lookup::HostResolveAddressStream::subscribe(&mut self.inner, self_)
1594    }
1595
1596    fn drop(&mut self, rep: Resource<ResolveAddressStream>) -> wasmtime::Result<()> {
1597        latest::sockets::ip_name_lookup::HostResolveAddressStream::drop(&mut self.inner, rep)
1598    }
1599}
1600
1601pub fn convert_result<T, T2, E, E2>(
1602    result: Result<T, TrappableError<E>>,
1603) -> wasmtime::Result<Result<T2, E2>>
1604where
1605    T2: From<T>,
1606    E: std::error::Error + Send + Sync + 'static,
1607    E2: From<E>,
1608{
1609    match result {
1610        Ok(e) => Ok(Ok(e.into())),
1611        Err(e) => Ok(Err(e.downcast()?.into())),
1612    }
1613}
1614
1615fn convert_stream_result<T, T2>(
1616    table: &mut ResourceTable,
1617    result: Result<T, wasmtime_wasi::p2::StreamError>,
1618) -> wasmtime::Result<Result<T2, StreamError>>
1619where
1620    T2: From<T>,
1621{
1622    match result {
1623        Ok(e) => Ok(Ok(e.into())),
1624        Err(wasmtime_wasi::p2::StreamError::Closed) => Ok(Err(StreamError::Closed)),
1625        Err(wasmtime_wasi::p2::StreamError::LastOperationFailed(e)) => {
1626            let e = table.push(e)?;
1627            Ok(Err(StreamError::LastOperationFailed(e)))
1628        }
1629        Err(wasmtime_wasi::p2::StreamError::Trap(e)) => Err(e),
1630    }
1631}
1632
1633#[macro_export]
1634macro_rules! convert {
1635    () => {};
1636    ($kind:ident $from:path [<=>] $to:path { $($body:tt)* } $($rest:tt)*) => {
1637        convert!($kind $from => $to { $($body)* });
1638        convert!($kind $to => $from { $($body)* });
1639
1640        convert!($($rest)*);
1641    };
1642    (struct $from:ty => $to:path { $($field:ident,)* } $($rest:tt)*) => {
1643        impl From<$from> for $to {
1644            fn from(e: $from) -> $to {
1645                $to {
1646                    $( $field: e.$field.into(), )*
1647                }
1648            }
1649        }
1650
1651        convert!($($rest)*);
1652    };
1653    (enum $from:path => $to:path { $($variant:ident $(($e:ident))?,)* } $($rest:tt)*) => {
1654        impl From<$from> for $to {
1655            fn from(e: $from) -> $to {
1656                use $from as A;
1657                use $to as B;
1658                match e {
1659                    $(
1660                        A::$variant $(($e))? => B::$variant $(($e.into()))?,
1661                    )*
1662                }
1663            }
1664        }
1665
1666        convert!($($rest)*);
1667    };
1668    (flags $from:path => $to:path { $($flag:ident,)* } $($rest:tt)*) => {
1669        impl From<$from> for $to {
1670            fn from(e: $from) -> $to {
1671                use $from as A;
1672                use $to as B;
1673                let mut out = B::empty();
1674                $(
1675                    if e.contains(A::$flag) {
1676                        out |= B::$flag;
1677                    }
1678                )*
1679                out
1680            }
1681        }
1682
1683        convert!($($rest)*);
1684    };
1685}
1686
1687convert! {
1688    struct latest::clocks::wall_clock::Datetime [<=>] Datetime {
1689        seconds,
1690        nanoseconds,
1691    }
1692
1693    enum latest::filesystem::types::ErrorCode => FsErrorCode {
1694        Access,
1695        WouldBlock,
1696        Already,
1697        BadDescriptor,
1698        Busy,
1699        Deadlock,
1700        Quota,
1701        Exist,
1702        FileTooLarge,
1703        IllegalByteSequence,
1704        InProgress,
1705        Interrupted,
1706        Invalid,
1707        Io,
1708        IsDirectory,
1709        Loop,
1710        TooManyLinks,
1711        MessageSize,
1712        NameTooLong,
1713        NoDevice,
1714        NoEntry,
1715        NoLock,
1716        InsufficientMemory,
1717        InsufficientSpace,
1718        NotDirectory,
1719        NotEmpty,
1720        NotRecoverable,
1721        Unsupported,
1722        NoTty,
1723        NoSuchDevice,
1724        Overflow,
1725        NotPermitted,
1726        Pipe,
1727        ReadOnly,
1728        InvalidSeek,
1729        TextFileBusy,
1730        CrossDevice,
1731    }
1732
1733    enum Advice => latest::filesystem::types::Advice {
1734        Normal,
1735        Sequential,
1736        Random,
1737        WillNeed,
1738        DontNeed,
1739        NoReuse,
1740    }
1741
1742    flags DescriptorFlags [<=>] latest::filesystem::types::DescriptorFlags {
1743        READ,
1744        WRITE,
1745        FILE_INTEGRITY_SYNC,
1746        DATA_INTEGRITY_SYNC,
1747        REQUESTED_WRITE_SYNC,
1748        MUTATE_DIRECTORY,
1749    }
1750
1751    enum DescriptorType [<=>] latest::filesystem::types::DescriptorType {
1752        Unknown,
1753        BlockDevice,
1754        CharacterDevice,
1755        Directory,
1756        Fifo,
1757        SymbolicLink,
1758        RegularFile,
1759        Socket,
1760    }
1761
1762    enum NewTimestamp => latest::filesystem::types::NewTimestamp {
1763        NoChange,
1764        Now,
1765        Timestamp(e),
1766    }
1767
1768    flags PathFlags => latest::filesystem::types::PathFlags {
1769        SYMLINK_FOLLOW,
1770    }
1771
1772    flags OpenFlags => latest::filesystem::types::OpenFlags {
1773        CREATE,
1774        DIRECTORY,
1775        EXCLUSIVE,
1776        TRUNCATE,
1777    }
1778
1779    struct latest::filesystem::types::MetadataHashValue => MetadataHashValue {
1780        lower,
1781        upper,
1782    }
1783
1784    struct latest::filesystem::types::DirectoryEntry => DirectoryEntry {
1785        type_,
1786        name,
1787    }
1788
1789    enum latest::sockets::network::ErrorCode => SocketErrorCode {
1790        Unknown,
1791        AccessDenied,
1792        NotSupported,
1793        InvalidArgument,
1794        OutOfMemory,
1795        Timeout,
1796        ConcurrencyConflict,
1797        NotInProgress,
1798        WouldBlock,
1799        InvalidState,
1800        NewSocketLimit,
1801        AddressNotBindable,
1802        AddressInUse,
1803        RemoteUnreachable,
1804        ConnectionRefused,
1805        ConnectionReset,
1806        ConnectionAborted,
1807        DatagramTooLarge,
1808        NameUnresolvable,
1809        TemporaryResolverFailure,
1810        PermanentResolverFailure,
1811    }
1812
1813    enum latest::sockets::network::IpAddress [<=>] IpAddress {
1814        Ipv4(e),
1815        Ipv6(e),
1816    }
1817
1818    enum latest::sockets::network::IpSocketAddress [<=>] IpSocketAddress {
1819        Ipv4(e),
1820        Ipv6(e),
1821    }
1822
1823    struct latest::sockets::network::Ipv4SocketAddress [<=>] Ipv4SocketAddress {
1824        port,
1825        address,
1826    }
1827
1828    struct latest::sockets::network::Ipv6SocketAddress [<=>] Ipv6SocketAddress {
1829        port,
1830        flow_info,
1831        scope_id,
1832        address,
1833    }
1834
1835    enum latest::sockets::network::IpAddressFamily [<=>] IpAddressFamily {
1836        Ipv4,
1837        Ipv6,
1838    }
1839
1840    enum ShutdownType => latest::sockets::tcp::ShutdownType {
1841        Receive,
1842        Send,
1843        Both,
1844    }
1845
1846    struct latest::sockets::udp::IncomingDatagram => Datagram {
1847        data,
1848        remote_address,
1849    }
1850}
1851
1852impl From<latest::filesystem::types::DescriptorStat> for DescriptorStat {
1853    fn from(e: latest::filesystem::types::DescriptorStat) -> DescriptorStat {
1854        DescriptorStat {
1855            type_: e.type_.into(),
1856            link_count: e.link_count,
1857            size: e.size,
1858            data_access_timestamp: e.data_access_timestamp.map(|e| e.into()),
1859            data_modification_timestamp: e.data_modification_timestamp.map(|e| e.into()),
1860            status_change_timestamp: e.status_change_timestamp.map(|e| e.into()),
1861        }
1862    }
1863}