soroban_env_common/vmcaller_env.rs
1#![allow(clippy::needless_lifetimes)]
2#[cfg(feature = "wasmi")]
3use crate::xdr::{ScErrorCode, ScErrorType};
4
5use super::{
6 AddressObject, Bool, BytesObject, DurationObject, Error, I128Object, I256Object, I256Val,
7 I64Object, MapObject, MuxedAddressObject, StorageType, StringObject, SymbolObject,
8 TimepointObject, U128Object, U256Object, U256Val, U32Val, U64Object, U64Val, Val, VecObject,
9 Void,
10};
11use crate::call_macro_with_all_host_functions;
12use crate::{CheckedEnvArg, EnvBase, Symbol};
13#[cfg(not(feature = "wasmi"))]
14use core::marker::PhantomData;
15
16/// The VmCallerEnv trait is similar to the Env trait -- it
17/// provides all the same-named methods -- but they have a form that takes an
18/// initial [`VmCaller`] argument by `&mut` that may or may-not wrap a
19/// `wasmi::Caller` structure, depending on whether it was invoked from a wasmi
20/// host-function wrapper.
21///
22/// There is a blanket `impl<T:VmCallerEnv> Env for T` so that any
23/// type (eg. `Host`) that implements `VmCallerEnv` automatically also
24/// implements `Env`, just by calling the corresponding
25/// `VmCallerEnv` method with the [`VmCaller::none()`] argument. This
26/// allows code to import and use `Env` directly (such as the native
27/// contract) to call host methods without having to write `VmCaller::none()`
28/// everywhere.
29#[cfg(feature = "wasmi")]
30pub struct VmCaller<'a, T>(pub Option<wasmi::Caller<'a, T>>);
31#[cfg(feature = "wasmi")]
32impl<'a, T> VmCaller<'a, T> {
33 pub fn none() -> Self {
34 VmCaller(None)
35 }
36 pub fn try_ref(&self) -> Result<&wasmi::Caller<'a, T>, Error> {
37 match &self.0 {
38 Some(caller) => Ok(caller),
39 None => Err(Error::from_type_and_code(
40 ScErrorType::Context,
41 ScErrorCode::InternalError,
42 )),
43 }
44 }
45 pub fn try_mut(&mut self) -> Result<&mut wasmi::Caller<'a, T>, Error> {
46 match &mut self.0 {
47 Some(caller) => Ok(caller),
48 None => Err(Error::from_type_and_code(
49 ScErrorType::Context,
50 ScErrorCode::InternalError,
51 )),
52 }
53 }
54}
55
56#[cfg(not(feature = "wasmi"))]
57pub struct VmCaller<'a, T> {
58 _nothing: PhantomData<&'a T>,
59}
60#[cfg(not(feature = "wasmi"))]
61impl<'a, T> VmCaller<'a, T> {
62 pub fn none() -> Self {
63 VmCaller {
64 _nothing: PhantomData,
65 }
66 }
67}
68
69///////////////////////////////////////////////////////////////////////////////
70/// X-macro use: defining trait VmCallerEnv
71///////////////////////////////////////////////////////////////////////////////
72//
73// This is a helper macro used only by generate_vmcaller_checked_env_trait
74// below. It consumes a token-tree of the form:
75//
76// {fn $fn_id:ident $args:tt -> $ret:ty}
77//
78// and produces the the corresponding method declaration to be used in the Env
79// trait.
80macro_rules! host_function_helper {
81 {
82 $($min_proto:literal)?, $($max_proto:literal)?,
83 $(#[$attr:meta])*
84 fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty
85 }
86 =>
87 {
88 $(#[$attr])*
89 fn $fn_id(&self, vmcaller: &mut VmCaller<Self::VmUserState>, $($arg:$type),*) -> Result<$ret, Self::Error>;
90 };
91}
92
93// This is a callback macro that pattern-matches the token-tree passed by the
94// x-macro (call_macro_with_all_host_functions) and produces a suite of method
95// declarations, which it places in the body of the declaration of the
96// VmCallerEnv trait.
97macro_rules! generate_vmcaller_checked_env_trait {
98 {
99 $(
100 // This outer pattern matches a single 'mod' block of the token-tree
101 // passed from the x-macro to this macro. It is embedded in a `$()*`
102 // pattern-repetition matcher so that it will match all provided
103 // 'mod' blocks provided.
104 $(#[$mod_attr:meta])*
105 mod $mod_id:ident $mod_str:literal
106 {
107 $(
108 // This inner pattern matches a single function description
109 // inside a 'mod' block in the token-tree passed from the
110 // x-macro to this macro. It is embedded in a `$()*`
111 // pattern-repetition matcher so that it will match all such
112 // descriptions.
113 $(#[$fn_attr:meta])*
114 { $fn_str:literal, $($min_proto:literal)?, $($max_proto:literal)?, fn $fn_id:ident $args:tt -> $ret:ty }
115 )*
116 }
117 )*
118 }
119
120 => // The part of the macro above this line is a matcher; below is its expansion.
121
122 {
123 // This macro expands to a single item: the VmCallerEnv trait
124
125 /// This trait is a variant of the [Env](crate::Env) trait used to
126 /// define the interface implemented by Host. The wasmi VM dispatch
127 /// functions (in soroban_env_host::dispatch) call methods on
128 /// `VmCallerEnv`, passing a [`VmCaller`] that wraps the wasmi Caller
129 /// context, and then convert any `Result::Err(...)` return value into a
130 /// VM trap, halting VM execution.
131 ///
132 /// There is also a blanket `impl<T:VmCallerEnv> Env for T` that
133 /// implements the `Env` for any `VmCallerEnv` by passing
134 /// [`VmCaller::none()`] for the first argument, allowing user code such
135 /// as the native contract to avoid writing `VmCaller::none()`
136 /// everywhere.
137 pub trait VmCallerEnv: EnvBase
138 {
139 type VmUserState;
140 $(
141 $(
142 // This invokes the host_function_helper! macro above
143 // passing only the relevant parts of the declaration
144 // matched by the inner pattern above. It is embedded in two
145 // nested `$()*` pattern-repetition expanders that
146 // correspond to the pattern-repetition matchers in the
147 // match section, but we ignore the structure of the 'mod'
148 // block repetition-level from the outer pattern in the
149 // expansion, flattening all functions from all 'mod' blocks
150 // into the VmCallerEnv trait.
151 host_function_helper!{$($min_proto)?, $($max_proto)?, $(#[$fn_attr])* fn $fn_id $args -> $ret}
152 )*
153 )*
154 }
155 };
156}
157
158// Here we invoke the x-macro passing generate_env_trait as its callback macro.
159call_macro_with_all_host_functions! { generate_vmcaller_checked_env_trait }
160
161///////////////////////////////////////////////////////////////////////////////
162/// X-macro use: impl<E> Env for VmCallerEnv<E>
163///////////////////////////////////////////////////////////////////////////////
164//
165// This is a helper macro used only by
166// generate_impl_checked_env_for_vmcaller_checked_env below. It consumes a
167// token-tree of the form:
168//
169// {fn $fn_id:ident $args:tt -> $ret:ty}
170//
171// and produces the the corresponding method declaration to be used in the Env
172// trait.
173macro_rules! vmcaller_none_function_helper {
174 {$($min_proto:literal)?, $($max_proto:literal)?, fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty}
175 =>
176 {
177 // We call `augment_err_result` here to give the Env a chance to attach
178 // context (eg. a backtrace) to any error that was generated by code
179 // that didn't have an Env on hand when creating the error. This will at
180 // least localize the error to a given Env call.
181 fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error> {
182 // Check the ledger protocol version against the function-specified
183 // boundaries, this prevents calling a host function using the host
184 // directly as `Env` (i.e. native mode) when the protocol version is
185 // out of bound.
186 $( self.check_protocol_version_lower_bound($min_proto)?; )?
187 $( self.check_protocol_version_upper_bound($max_proto)?; )?
188
189 #[cfg(all(not(target_family = "wasm"), feature = "tracy"))]
190 let _span = tracy_span!(core::stringify!($fn_id));
191 #[cfg(feature = "std")]
192 if self.tracing_enabled()
193 {
194 self.trace_env_call(&core::stringify!($fn_id), &[$(&$arg),*])?;
195 }
196 let res: Result<_, _> = self.augment_err_result(<Self as VmCallerEnv>::$fn_id(self, &mut VmCaller::none(), $($arg.check_env_arg(self)?),*));
197 let res = match res {
198 Ok(ok) => Ok(ok.check_env_arg(self)?),
199 Err(err) => Err(err)
200 };
201 #[cfg(feature = "std")]
202 if self.tracing_enabled()
203 {
204 let dyn_res: Result<&dyn core::fmt::Debug,&Self::Error> = match &res {
205 Ok(ref ok) => Ok(ok),
206 Err(err) => Err(err)
207 };
208 self.trace_env_ret(&core::stringify!($fn_id), &dyn_res)?;
209 }
210 res
211 }
212 };
213}
214
215// This is a callback macro that pattern-matches the token-tree passed by the
216// x-macro (call_macro_with_all_host_functions) and produces a suite of method
217// declarations, which it places in the body of the blanket impl of Env for
218// T:Env
219macro_rules! impl_env_for_vmcaller_env {
220 {
221 $(
222 // This outer pattern matches a single 'mod' block of the token-tree
223 // passed from the x-macro to this macro. It is embedded in a `$()*`
224 // pattern-repetition matcher so that it will match all provided
225 // 'mod' blocks provided.
226 $(#[$mod_attr:meta])*
227 mod $mod_id:ident $mod_str:literal
228 {
229 $(
230 // This inner pattern matches a single function description
231 // inside a 'mod' block in the token-tree passed from the
232 // x-macro to this macro. It is embedded in a `$()*`
233 // pattern-repetition matcher so that it will match all such
234 // descriptions.
235 $(#[$fn_attr:meta])*
236 { $fn_str:literal, $($min_proto:literal)?, $($max_proto:literal)?, fn $fn_id:ident $args:tt -> $ret:ty }
237 )*
238 }
239 )*
240 }
241
242 => // The part of the macro above this line is a matcher; below is its expansion.
243
244 {
245 // This macro expands to a single item: a blanket impl that makes all
246 // `VmCallerEnv` types automatically `Env` types, just
247 // passing [`VmCaller::none()`] as their first argument.
248 impl<T:VmCallerEnv> $crate::Env for T
249 {
250 $(
251 $(
252 // This invokes the vmcaller_none_function_helper! macro above
253 // passing only the relevant parts of the declaration
254 // matched by the inner pattern above. It is embedded in two
255 // nested `$()*` pattern-repetition expanders that
256 // correspond to the pattern-repetition matchers in the
257 // match section, but we ignore the structure of the 'mod'
258 // block repetition-level from the outer pattern in the
259 // expansion, flattening all functions from all 'mod' blocks
260 // into the impl.
261 vmcaller_none_function_helper!{$($min_proto)?, $($max_proto)?, fn $fn_id $args -> $ret}
262 )*
263 )*
264 }
265 };
266}
267
268// Here we invoke the x-macro passing
269// generate_checked_env_for_vmcaller_checked_env as its callback macro.
270call_macro_with_all_host_functions! { impl_env_for_vmcaller_env }