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