Skip to main content

wasmtime_wasi/p2/
mod.rs

1//! # Wasmtime's WASIp2 Implementation
2//!
3//!
4//! This module provides a Wasmtime host implementation of WASI 0.2 (aka WASIp2
5//! aka Preview 2) and WASI 0.1 (aka WASIp1 aka Preview 1). WASI is implemented
6//! with the Rust crates [`tokio`] and [`cap-std`] primarily, meaning that
7//! operations are implemented in terms of their native platform equivalents by
8//! default.
9//!
10//! # WASIp2 interfaces
11//!
12//! This module contains implementations of the following interfaces:
13//!
14//! * [`wasi:cli/environment`]
15//! * [`wasi:cli/exit`]
16//! * [`wasi:cli/stderr`]
17//! * [`wasi:cli/stdin`]
18//! * [`wasi:cli/stdout`]
19//! * [`wasi:cli/terminal-input`]
20//! * [`wasi:cli/terminal-output`]
21//! * [`wasi:cli/terminal-stderr`]
22//! * [`wasi:cli/terminal-stdin`]
23//! * [`wasi:cli/terminal-stdout`]
24//! * [`wasi:clocks/monotonic-clock`]
25//! * [`wasi:clocks/wall-clock`]
26//! * [`wasi:filesystem/preopens`]
27//! * [`wasi:filesystem/types`]
28//! * [`wasi:random/insecure-seed`]
29//! * [`wasi:random/insecure`]
30//! * [`wasi:random/random`]
31//! * [`wasi:sockets/instance-network`]
32//! * [`wasi:sockets/ip-name-lookup`]
33//! * [`wasi:sockets/network`]
34//! * [`wasi:sockets/tcp-create-socket`]
35//! * [`wasi:sockets/tcp`]
36//! * [`wasi:sockets/udp-create-socket`]
37//! * [`wasi:sockets/udp`]
38//!
39//! Most traits are implemented for [`WasiCtxView`] trait which provides
40//! access to [`WasiCtx`] and [`ResourceTable`], which defines the configuration
41//! for WASI and handle state. The [`WasiView`] trait is used to acquire and
42//! construct a [`WasiCtxView`].
43//!
44//! The [`wasmtime-wasi-io`] crate contains implementations of the
45//! following interfaces, and this module reuses those implementations:
46//!
47//! * [`wasi:io/error`]
48//! * [`wasi:io/poll`]
49//! * [`wasi:io/streams`]
50//!
51//! These traits are implemented directly for [`ResourceTable`]. All aspects of
52//! `wasmtime-wasi-io` that are used by this module are re-exported. Unless you
53//! are implementing other host functionality that needs to interact with the
54//! WASI scheduler and don't want to use other functionality provided by
55//! `wasmtime-wasi`, you don't need to take a direct dependency on
56//! `wasmtime-wasi-io`.
57//!
58//! # Generated Bindings
59//!
60//! This module uses [`wasmtime::component::bindgen!`] to generate bindings for
61//! all WASI interfaces. Raw bindings are available in the [`bindings`] submodule
62//! of this module. Downstream users can either implement these traits themselves
63//! or you can use the built-in implementations in this module for
64//! `WasiImpl<T: WasiView>`.
65//!
66//! # The `WasiView` trait
67//!
68//! This module's implementation of WASI is done in terms of an implementation of
69//! [`WasiView`]. This trait provides a "view" into WASI-related state that is
70//! contained within a [`Store<T>`](wasmtime::Store).
71//!
72//! For all of the generated bindings in this module (Host traits),
73//! implementations are provided looking like:
74//!
75//! ```
76//! # use wasmtime_wasi::WasiCtxView;
77//! # trait WasiView {}
78//! # mod bindings { pub mod wasi { pub trait Host {} } }
79//! impl bindings::wasi::Host for WasiCtxView<'_> {
80//!     // ...
81//! }
82//! ```
83//!
84//! where the [`WasiCtxView`] type comes from [`WasiView::ctx`] for the type
85//! contained within the `Store<T>`. The [`add_to_linker_sync`] and
86//! [`add_to_linker_async`] function then require that `T: WasiView` with
87//! [`Linker<T>`](wasmtime::component::Linker).
88//!
89//! To implement the [`WasiView`] trait you will first select a
90//! `T` to put in `Store<T>` (typically, by defining your own struct).
91//! Somewhere within `T` you'll store:
92//!
93//! * [`ResourceTable`] - created through default constructors.
94//! * [`WasiCtx`] - created through [`WasiCtxBuilder`].
95//!
96//! You'll then write an implementation of the [`WasiView`]
97//! trait to access those items in your `T`. For example:
98//! ```
99//! use wasmtime::component::ResourceTable;
100//! use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
101//!
102//! struct MyCtx {
103//!     table: ResourceTable,
104//!     wasi: WasiCtx,
105//! }
106//!
107//! impl WasiView for MyCtx {
108//!     fn ctx(&mut self) -> WasiCtxView<'_> {
109//!         WasiCtxView { ctx: &mut self.wasi, table: &mut self.table }
110//!     }
111//! }
112//! ```
113//!
114//! # Async and Sync
115//!
116//! All WASIp2 functions are blocking from WebAssembly's point of view: a
117//! WebAssembly call into these functions returns only when they are complete.
118//!
119//! This module provides an implementation of those functions in the host, where
120//! for some functions, it is appropriate to implement them using async Rust and
121//! the Tokio executor. The host implementation still blocks WebAssembly, but it
122//! does not block the host's thread. Synchronous wrappers are also provided for
123//! all async implementations, which create a private Tokio executor.
124//!
125//! Users can choose between these modes of implementation using variants
126//! of the add_to_linker functions:
127//!
128//! * For non-async users, use [`add_to_linker_sync`].
129//! * For async users, use [`add_to_linker_async`].
130//!
131//! Note that bindings are generated once for async and once for sync. Most
132//! interfaces do not change, however, so only interfaces with blocking
133//! functions have bindings generated twice. Bindings are organized as:
134//!
135//! * [`bindings`] - default location of all bindings, blocking functions are
136//!   `async`
137//! * [`bindings::sync`] - blocking interfaces have synchronous versions here.
138//!
139//! # Module-specific traits
140//!
141//! This module's default implementation of WASI bindings to native primitives
142//! for the platform that it is compiled for. For example opening a TCP socket
143//! uses the native platform to open a TCP socket (so long as [`WasiCtxBuilder`]
144//! allows it). There are a few important traits, however, that are specific to
145//! this module.
146//!
147//! * [`InputStream`] and [`OutputStream`] - these are the host traits
148//!   behind the WASI `input-stream` and `output-stream` types in the
149//!   `wasi:io/streams` interface. These enable embedders to build their own
150//!   custom stream and insert them into a [`ResourceTable`] (as a boxed trait
151//!   object, see [`DynInputStream`] and [`DynOutputStream`]) to be used from
152//!   wasm.
153//!
154//! * [`Pollable`] - this trait enables building arbitrary logic to get hooked
155//!   into a `pollable` resource from `wasi:io/poll`. A pollable resource is
156//!   created through the [`subscribe`] function.
157//!
158//! * [`HostWallClock`](crate::HostWallClock) and [`HostMonotonicClock`](crate::HostMonotonicClock) are used in conjunction with
159//!   [`WasiCtxBuilder::wall_clock`] and [`WasiCtxBuilder::monotonic_clock`] if
160//!   the defaults host's clock should not be used.
161//!
162//! * [`StdinStream`] and [`StdoutStream`] are used to provide custom
163//!   stdin/stdout streams if they're not inherited (or null, which is the
164//!   default).
165//!
166//! These traits enable embedders to customize small portions of WASI interfaces
167//! provided while still providing all other interfaces.
168//!
169//! # Examples
170//!
171//! Usage of this module is done through a few steps to get everything hooked up:
172//!
173//! 1. First implement [`WasiView`] for your type which is the
174//!    `T` in `Store<T>`.
175//! 2. Add WASI interfaces to a `wasmtime::component::Linker<T>`. This is either
176//!    done through top-level functions like [`add_to_linker_sync`] or through
177//!    individual `add_to_linker` functions in generated bindings throughout
178//!    this module.
179//! 3. Create a [`WasiCtx`] for each `Store<T>` through [`WasiCtxBuilder`]. Each
180//!    WASI context is "null" or "empty" by default, so items must be explicitly
181//!    added to get accessed by wasm (such as env vars or program arguments).
182//! 4. Use the previous `Linker<T>` to instantiate a `Component` within a
183//!    `Store<T>`.
184//!
185//! For examples see each of [`WasiView`], [`WasiCtx`], [`WasiCtxBuilder`],
186//! [`add_to_linker_sync`], and [`bindings::Command`].
187//!
188//! [`wasmtime::component::bindgen!`]: https://docs.rs/wasmtime/latest/wasmtime/component/macro.bindgen.html
189//! [`tokio`]: https://crates.io/crates/tokio
190//! [`cap-std`]: https://crates.io/crates/cap-std
191//! [`wasmtime-wasi-io`]: https://crates.io/crates/wasmtime-wasi-io
192//! [`wasi:cli/environment`]: bindings::cli::environment::Host
193//! [`wasi:cli/exit`]: bindings::cli::exit::Host
194//! [`wasi:cli/stderr`]: bindings::cli::stderr::Host
195//! [`wasi:cli/stdin`]: bindings::cli::stdin::Host
196//! [`wasi:cli/stdout`]: bindings::cli::stdout::Host
197//! [`wasi:cli/terminal-input`]: bindings::cli::terminal_input::Host
198//! [`wasi:cli/terminal-output`]: bindings::cli::terminal_output::Host
199//! [`wasi:cli/terminal-stdin`]: bindings::cli::terminal_stdin::Host
200//! [`wasi:cli/terminal-stdout`]: bindings::cli::terminal_stdout::Host
201//! [`wasi:cli/terminal-stderr`]: bindings::cli::terminal_stderr::Host
202//! [`wasi:clocks/monotonic-clock`]: bindings::clocks::monotonic_clock::Host
203//! [`wasi:clocks/wall-clock`]: bindings::clocks::wall_clock::Host
204//! [`wasi:filesystem/preopens`]: bindings::filesystem::preopens::Host
205//! [`wasi:filesystem/types`]: bindings::filesystem::types::Host
206//! [`wasi:io/error`]: wasmtime_wasi_io::bindings::wasi::io::error::Host
207//! [`wasi:io/poll`]: wasmtime_wasi_io::bindings::wasi::io::poll::Host
208//! [`wasi:io/streams`]: wasmtime_wasi_io::bindings::wasi::io::streams::Host
209//! [`wasi:random/insecure-seed`]: bindings::random::insecure_seed::Host
210//! [`wasi:random/insecure`]: bindings::random::insecure::Host
211//! [`wasi:random/random`]: bindings::random::random::Host
212//! [`wasi:sockets/instance-network`]: bindings::sockets::instance_network::Host
213//! [`wasi:sockets/ip-name-lookup`]: bindings::sockets::ip_name_lookup::Host
214//! [`wasi:sockets/network`]: bindings::sockets::network::Host
215//! [`wasi:sockets/tcp-create-socket`]: bindings::sockets::tcp_create_socket::Host
216//! [`wasi:sockets/tcp`]: bindings::sockets::tcp::Host
217//! [`wasi:sockets/udp-create-socket`]: bindings::sockets::udp_create_socket::Host
218//! [`wasi:sockets/udp`]: bindings::sockets::udp::Host
219//! [`ResourceTable`]: wasmtime::component::ResourceTable
220//! [`WasiCtx`]: crate::WasiCtx
221//! [`WasiCtxView`]: crate::WasiCtxView
222//! [`WasiCtxBuilder`]: crate::WasiCtxBuilder
223//! [`WasiCtxBuilder::wall_clock`]: crate::WasiCtxBuilder::wall_clock
224//! [`WasiCtxBuilder::monotonic_clock`]: crate::WasiCtxBuilder::monotonic_clock
225//! [`StdinStream`]: crate::cli::StdinStream
226//! [`StdoutStream`]: crate::cli::StdoutStream
227
228use crate::WasiView;
229use crate::cli::{WasiCli, WasiCliView as _};
230use crate::clocks::{WasiClocks, WasiClocksView as _};
231use crate::filesystem::{WasiFilesystem, WasiFilesystemView as _};
232use crate::random::WasiRandom;
233use crate::sockets::{WasiSockets, WasiSocketsView as _};
234use wasmtime::component::{HasData, Linker, ResourceTable};
235
236pub mod bindings;
237pub(crate) mod filesystem;
238mod host;
239mod ip_name_lookup;
240mod network;
241pub mod pipe;
242mod poll;
243mod stdio;
244mod tcp;
245mod udp;
246mod write_stream;
247
248pub use self::filesystem::{FsError, FsResult, ReaddirIterator};
249pub use self::network::{Network, SocketError, SocketResult};
250pub use self::stdio::IsATTY;
251pub(crate) use tcp::P2TcpStreamingState;
252// These contents of wasmtime-wasi-io are re-exported by this module for compatibility:
253// they were originally defined in this module before being factored out, and many
254// users of this module depend on them at these names.
255pub use wasmtime_wasi_io::poll::{DynFuture, DynPollable, MakeFuture, Pollable, subscribe};
256pub use wasmtime_wasi_io::streams::{
257    DynInputStream, DynOutputStream, Error as IoError, InputStream, OutputStream, StreamError,
258    StreamResult,
259};
260
261/// Add all WASI interfaces from this crate into the `linker` provided.
262///
263/// This function will add the `async` variant of all interfaces into the
264/// [`Linker`] provided. For embeddings with async support disabled see
265/// [`add_to_linker_sync`] instead.
266///
267/// This function will add all interfaces implemented by this crate to the
268/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
269/// this crate.
270///
271/// # Example
272///
273/// ```
274/// use wasmtime::{Engine, Result, Store, Config};
275/// use wasmtime::component::{ResourceTable, Linker};
276/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
277///
278/// fn main() -> Result<()> {
279///     let engine = Engine::default();
280///
281///     let mut linker = Linker::<MyState>::new(&engine);
282///     wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
283///     // ... add any further functionality to `linker` if desired ...
284///
285///     let mut builder = WasiCtx::builder();
286///
287///     // ... configure `builder` more to add env vars, args, etc ...
288///
289///     let mut store = Store::new(
290///         &engine,
291///         MyState {
292///             ctx: builder.build(),
293///             table: ResourceTable::new(),
294///         },
295///     );
296///
297///     // ... use `linker` to instantiate within `store` ...
298///
299///     Ok(())
300/// }
301///
302/// struct MyState {
303///     ctx: WasiCtx,
304///     table: ResourceTable,
305/// }
306///
307/// impl WasiView for MyState {
308///     fn ctx(&mut self) -> WasiCtxView<'_> {
309///         WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
310///     }
311/// }
312/// ```
313pub fn add_to_linker_async<T: WasiView>(linker: &mut Linker<T>) -> wasmtime::Result<()> {
314    let options = bindings::LinkOptions::default();
315    add_to_linker_with_options_async(linker, &options)
316}
317
318/// Similar to [`add_to_linker_async`], but with the ability to enable unstable features.
319pub fn add_to_linker_with_options_async<T: WasiView>(
320    linker: &mut Linker<T>,
321    options: &bindings::LinkOptions,
322) -> wasmtime::Result<()> {
323    add_async_io_to_linker(linker)?;
324    add_nonblocking_to_linker(linker, options)?;
325
326    let l = linker;
327    bindings::filesystem::types::add_to_linker::<T, WasiFilesystem>(l, T::filesystem)?;
328    bindings::sockets::tcp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
329    bindings::sockets::udp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
330    Ok(())
331}
332
333/// Shared functionality for [`add_to_linker_async`] and [`add_to_linker_sync`].
334fn add_nonblocking_to_linker<'a, T: WasiView, O>(
335    linker: &mut Linker<T>,
336    options: &'a O,
337) -> wasmtime::Result<()>
338where
339    bindings::sockets::network::LinkOptions: From<&'a O>,
340    bindings::cli::exit::LinkOptions: From<&'a O>,
341{
342    use crate::p2::bindings::{cli, clocks, filesystem, random, sockets};
343
344    let l = linker;
345    clocks::wall_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
346    clocks::monotonic_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
347    filesystem::preopens::add_to_linker::<T, WasiFilesystem>(l, T::filesystem)?;
348    random::random::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
349    random::insecure::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
350    random::insecure_seed::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
351    cli::exit::add_to_linker::<T, WasiCli>(l, &options.into(), T::cli)?;
352    cli::environment::add_to_linker::<T, WasiCli>(l, T::cli)?;
353    cli::stdin::add_to_linker::<T, WasiCli>(l, T::cli)?;
354    cli::stdout::add_to_linker::<T, WasiCli>(l, T::cli)?;
355    cli::stderr::add_to_linker::<T, WasiCli>(l, T::cli)?;
356    cli::terminal_input::add_to_linker::<T, WasiCli>(l, T::cli)?;
357    cli::terminal_output::add_to_linker::<T, WasiCli>(l, T::cli)?;
358    cli::terminal_stdin::add_to_linker::<T, WasiCli>(l, T::cli)?;
359    cli::terminal_stdout::add_to_linker::<T, WasiCli>(l, T::cli)?;
360    cli::terminal_stderr::add_to_linker::<T, WasiCli>(l, T::cli)?;
361    sockets::tcp_create_socket::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
362    sockets::udp_create_socket::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
363    sockets::instance_network::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
364    sockets::network::add_to_linker::<T, WasiSockets>(l, &options.into(), T::sockets)?;
365    sockets::ip_name_lookup::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
366    Ok(())
367}
368
369/// Same as [`add_to_linker_async`] except that this only adds interfaces
370/// present in the `wasi:http/proxy` world.
371pub fn add_to_linker_proxy_interfaces_async<T: WasiView>(
372    linker: &mut Linker<T>,
373) -> wasmtime::Result<()> {
374    add_async_io_to_linker(linker)?;
375    add_proxy_interfaces_nonblocking(linker)
376}
377
378/// Same as [`add_to_linker_sync`] except that this only adds interfaces
379/// present in the `wasi:http/proxy` world.
380#[doc(hidden)]
381pub fn add_to_linker_proxy_interfaces_sync<T: WasiView>(
382    linker: &mut Linker<T>,
383) -> wasmtime::Result<()> {
384    add_sync_wasi_io(linker)?;
385    add_proxy_interfaces_nonblocking(linker)
386}
387
388fn add_proxy_interfaces_nonblocking<T: WasiView>(linker: &mut Linker<T>) -> wasmtime::Result<()> {
389    use crate::p2::bindings::{cli, clocks, random};
390
391    let l = linker;
392    clocks::wall_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
393    clocks::monotonic_clock::add_to_linker::<T, WasiClocks>(l, T::clocks)?;
394    random::random::add_to_linker::<T, WasiRandom>(l, |t| &mut t.ctx().ctx.random)?;
395    cli::stdin::add_to_linker::<T, WasiCli>(l, T::cli)?;
396    cli::stdout::add_to_linker::<T, WasiCli>(l, T::cli)?;
397    cli::stderr::add_to_linker::<T, WasiCli>(l, T::cli)?;
398    Ok(())
399}
400
401/// Add all WASI interfaces from this crate into the `linker` provided.
402///
403/// This function will add the synchronous variant of all interfaces into the
404/// [`Linker`] provided. For embeddings with async support enabled see
405/// [`add_to_linker_async`] instead.
406///
407/// This function will add all interfaces implemented by this crate to the
408/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
409/// this crate.
410///
411/// # Example
412///
413/// ```
414/// use wasmtime::{Engine, Result, Store, Config};
415/// use wasmtime::component::{ResourceTable, Linker};
416/// use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
417///
418/// fn main() -> Result<()> {
419///     let engine = Engine::default();
420///
421///     let mut linker = Linker::<MyState>::new(&engine);
422///     wasmtime_wasi::p2::add_to_linker_sync(&mut linker)?;
423///     // ... add any further functionality to `linker` if desired ...
424///
425///     let mut builder = WasiCtx::builder();
426///
427///     // ... configure `builder` more to add env vars, args, etc ...
428///
429///     let mut store = Store::new(
430///         &engine,
431///         MyState {
432///             ctx: builder.build(),
433///             table: ResourceTable::new(),
434///         },
435///     );
436///
437///     // ... use `linker` to instantiate within `store` ...
438///
439///     Ok(())
440/// }
441///
442/// struct MyState {
443///     ctx: WasiCtx,
444///     table: ResourceTable,
445/// }
446/// impl WasiView for MyState {
447///     fn ctx(&mut self) -> WasiCtxView<'_> {
448///         WasiCtxView { ctx: &mut self.ctx, table: &mut self.table }
449///     }
450/// }
451/// ```
452pub fn add_to_linker_sync<T: WasiView>(
453    linker: &mut wasmtime::component::Linker<T>,
454) -> wasmtime::Result<()> {
455    let options = bindings::sync::LinkOptions::default();
456    add_to_linker_with_options_sync(linker, &options)
457}
458
459/// Similar to [`add_to_linker_sync`], but with the ability to enable unstable features.
460pub fn add_to_linker_with_options_sync<T: WasiView>(
461    linker: &mut wasmtime::component::Linker<T>,
462    options: &bindings::sync::LinkOptions,
463) -> wasmtime::Result<()> {
464    add_nonblocking_to_linker(linker, options)?;
465    add_sync_wasi_io(linker)?;
466
467    let l = linker;
468    bindings::sync::filesystem::types::add_to_linker::<T, WasiFilesystem>(l, T::filesystem)?;
469    bindings::sync::sockets::tcp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
470    bindings::sync::sockets::udp::add_to_linker::<T, WasiSockets>(l, T::sockets)?;
471    Ok(())
472}
473
474/// Shared functionality of [`add_to_linker_sync`]` and
475/// [`add_to_linker_proxy_interfaces_sync`].
476fn add_sync_wasi_io<T: WasiView>(
477    linker: &mut wasmtime::component::Linker<T>,
478) -> wasmtime::Result<()> {
479    let l = linker;
480    wasmtime_wasi_io::bindings::wasi::io::error::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
481    bindings::sync::io::poll::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
482    bindings::sync::io::streams::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
483    Ok(())
484}
485
486struct HasIo;
487
488impl HasData for HasIo {
489    type Data<'a> = &'a mut ResourceTable;
490}
491
492// FIXME: it's a bit unfortunate that this can't use
493// `wasmtime_wasi_io::add_to_linker` and that's because `T: WasiView`, here,
494// not `T: IoView`. Ideally we'd have `impl<T: WasiView> IoView for T` but
495// that's not possible with these two traits in separate crates. For now this
496// is some small duplication but if this gets worse over time then we'll want
497// to massage this.
498fn add_async_io_to_linker<T: WasiView>(l: &mut Linker<T>) -> wasmtime::Result<()> {
499    wasmtime_wasi_io::bindings::wasi::io::error::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
500    wasmtime_wasi_io::bindings::wasi::io::poll::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
501    wasmtime_wasi_io::bindings::wasi::io::streams::add_to_linker::<T, HasIo>(l, |t| t.ctx().table)?;
502    Ok(())
503}