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