gwasmi/engine/stack/
mod.rs1mod frames;
2mod values;
3
4pub use self::{
5 frames::{CallStack, FuncFrame},
6 values::{ValueStack, ValueStackPtr},
7};
8use crate::{
9 core::UntypedValue,
10 engine::{code_map::CodeMap, func_types::FuncTypeRegistry, FuncParams},
11 func::{HostFuncEntity, WasmFuncEntity},
12 AsContext,
13 Instance,
14 StoreContextMut,
15};
16use core::{
17 fmt::{self, Display},
18 mem::size_of,
19};
20use wasmi_core::{Trap, TrapCode};
21
22const DEFAULT_MIN_VALUE_STACK_HEIGHT: usize = 1024;
24
25const DEFAULT_MAX_VALUE_STACK_HEIGHT: usize = 1024 * DEFAULT_MIN_VALUE_STACK_HEIGHT;
27
28const DEFAULT_MAX_RECURSION_DEPTH: usize = 1024;
30
31#[cold]
33fn err_stack_overflow() -> TrapCode {
34 TrapCode::StackOverflow
35}
36
37#[derive(Debug, Copy, Clone)]
39pub struct StackLimits {
40 initial_value_stack_height: usize,
42 maximum_value_stack_height: usize,
44 maximum_recursion_depth: usize,
46}
47
48#[derive(Debug)]
50pub enum LimitsError {
51 InitialValueStackExceedsMaximum,
53}
54
55impl Display for LimitsError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 LimitsError::InitialValueStackExceedsMaximum => {
59 write!(f, "initial value stack height exceeds maximum stack height")
60 }
61 }
62 }
63}
64
65impl StackLimits {
66 pub fn new(
72 initial_value_stack_height: usize,
73 maximum_value_stack_height: usize,
74 maximum_recursion_depth: usize,
75 ) -> Result<Self, LimitsError> {
76 if initial_value_stack_height > maximum_value_stack_height {
77 return Err(LimitsError::InitialValueStackExceedsMaximum);
78 }
79 Ok(Self {
80 initial_value_stack_height,
81 maximum_value_stack_height,
82 maximum_recursion_depth,
83 })
84 }
85}
86
87impl Default for StackLimits {
88 fn default() -> Self {
89 let register_len = size_of::<UntypedValue>();
90 let initial_value_stack_height = DEFAULT_MIN_VALUE_STACK_HEIGHT / register_len;
91 let maximum_value_stack_height = DEFAULT_MAX_VALUE_STACK_HEIGHT / register_len;
92 Self {
93 initial_value_stack_height,
94 maximum_value_stack_height,
95 maximum_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
96 }
97 }
98}
99
100#[derive(Debug, Default)]
102pub struct Stack {
103 pub values: ValueStack,
105 pub frames: CallStack,
107}
108
109impl Stack {
110 pub fn new(limits: StackLimits) -> Self {
114 let frames = CallStack::new(limits.maximum_recursion_depth);
115 let values = ValueStack::new(
116 limits.initial_value_stack_height,
117 limits.maximum_value_stack_height,
118 );
119 Self { values, frames }
120 }
121
122 pub fn empty() -> Self {
128 Self {
129 values: ValueStack::empty(),
130 frames: CallStack::default(),
131 }
132 }
133
134 pub fn is_empty(&self) -> bool {
140 self.values.is_empty()
141 }
142
143 pub fn prepare_wasm_call(
145 &mut self,
146 wasm_func: &WasmFuncEntity,
147 code_map: &CodeMap,
148 ) -> Result<(), TrapCode> {
149 let header = code_map.header(wasm_func.func_body());
150 self.values.prepare_wasm_call(header)?;
151 let ip = code_map.instr_ptr(header.iref());
152 let instance = wasm_func.instance();
153 self.frames.init(ip, instance);
154 Ok(())
155 }
156
157 pub fn call_host_as_root<T>(
159 &mut self,
160 ctx: StoreContextMut<T>,
161 host_func: HostFuncEntity,
162 func_types: &FuncTypeRegistry,
163 ) -> Result<(), Trap> {
164 self.call_host_impl(ctx, host_func, None, func_types)
165 }
166
167 #[inline(always)]
174 pub fn call_host_impl<T>(
175 &mut self,
176 ctx: StoreContextMut<T>,
177 host_func: HostFuncEntity,
178 instance: Option<&Instance>,
179 func_types: &FuncTypeRegistry,
180 ) -> Result<(), Trap> {
181 let (input_types, output_types) = func_types
184 .resolve_func_type(host_func.ty_dedup())
185 .params_results();
186 let len_inputs = input_types.len();
189 let len_outputs = output_types.len();
190 let max_inout = len_inputs.max(len_outputs);
191 self.values.reserve(max_inout)?;
192 let delta = if len_outputs > len_inputs {
193 let delta = len_outputs - len_inputs;
197 self.values.extend_zeros(delta);
198 delta
199 } else {
200 0
201 };
202 let params_results = FuncParams::new(
203 self.values.peek_as_slice_mut(max_inout),
204 len_inputs,
205 len_outputs,
206 );
207 let trampoline = ctx
211 .as_context()
212 .store
213 .resolve_trampoline(host_func.trampoline())
214 .clone();
215 trampoline
216 .call(ctx, instance, params_results)
217 .map_err(|error| {
218 self.values.drop(delta);
224 error
225 })?;
226 if len_outputs < len_inputs {
229 let delta = len_inputs - len_outputs;
230 self.values.drop(delta);
231 }
232 Ok(())
236 }
237
238 pub fn reset(&mut self) {
240 self.values.reset();
241 self.frames.reset();
242 }
243}