1use crate::{Error, Result};
3use smallvec::SmallVec;
4use wasmparser::BlockType;
5
6#[repr(u8)]
8#[derive(Clone, Copy, PartialEq, Eq, Debug)]
9pub enum ControlStackFrameType {
10 If(bool),
14 Else,
16 Loop,
18 Block,
20}
21
22#[derive(Clone)]
28pub struct ControlStackFrame {
29 pub ty: ControlStackFrameType,
33 pub original_pc_offset: u16,
35 result: BlockType,
39
40 pub original_sp: u16,
42
43 pub might_return_early: bool,
45}
46
47impl ControlStackFrame {
48 pub fn new(
50 ty: ControlStackFrameType,
51 original_pc_offset: u16,
52 original_sp: u16,
53 result: BlockType,
54 ) -> Self {
55 Self {
56 ty,
57 original_pc_offset,
58 original_sp,
59 result,
60 might_return_early: false,
61 }
62 }
63
64 pub fn pc_offset(&self) -> u16 {
66 self.original_pc_offset
67 }
68
69 pub fn result(&self) -> BlockType {
71 self.result
72 }
73
74 pub fn set_might_return_early(&mut self, value: bool) {
76 self.might_return_early = value;
77 }
78
79 pub fn is_function_boundary(&self) -> bool {
81 matches!(self.ty, ControlStackFrameType::Block)
83 }
84}
85
86#[derive(Default)]
88pub struct ControlStack {
89 pub stack: SmallVec<[ControlStackFrame; 32]>,
93}
94
95impl ControlStack {
96 pub fn depth(&self) -> usize {
98 self.stack.len()
99 }
100
101 pub fn mark_else(&mut self) -> Result<ControlStackFrame> {
103 let last = self
104 .stack
105 .last_mut()
106 .ok_or_else(|| Error::ControlStackUnderflow)?;
107
108 if last.ty != ControlStackFrameType::If(false) {
109 return Err(Error::InvalidElseBlock(last.original_pc_offset));
110 }
111
112 last.ty = ControlStackFrameType::If(true);
113 Ok(last.clone())
114 }
115
116 pub fn push(&mut self, frame: ControlStackFrame) {
118 self.stack.push(frame);
119 }
120
121 pub fn pop(&mut self) -> Result<ControlStackFrame> {
123 self.stack.pop().ok_or_else(|| Error::ControlStackUnderflow)
124 }
125
126 pub fn label_from_depth(&self, mut depth: u32) -> Result<u16> {
128 for frame in self.stack.iter().rev() {
129 if frame.ty == ControlStackFrameType::Else {
130 continue;
131 }
132
133 if depth == 0 {
134 return Ok(frame.pc_offset());
135 }
136
137 depth -= 1;
138 }
139
140 Err(Error::InvalidDepth(depth as usize))
141 }
142
143 pub fn frame_from_depth(&self, mut depth: u32) -> Result<&ControlStackFrame> {
145 for (i, frame) in self.stack.iter().rev().enumerate() {
146 if frame.ty == ControlStackFrameType::Else {
147 continue;
148 }
149
150 if depth == 0 {
151 return Ok(&self.stack[self.stack.len() - 1 - i]);
152 }
153
154 depth -= 1;
155 }
156
157 Err(Error::InvalidDepth(depth as usize))
158 }
159
160 pub fn is_exit_branch(&self, depth: u32) -> bool {
162 if depth as usize >= self.depth() {
164 return true;
165 }
166
167 if let Ok(frame) = self.frame_from_depth(depth) {
169 if matches!(frame.ty, ControlStackFrameType::Block) {
171 for (i, f) in self.stack.iter().enumerate() {
172 if f.original_pc_offset == frame.original_pc_offset {
173 return i == 0; }
175 }
176 }
177 }
178
179 false
180 }
181
182 pub fn mark_frames_with_early_return(&mut self, depth: u32) {
184 let target_idx = if depth as usize >= self.depth() {
185 0 } else {
188 let mut target_idx = self.depth();
190 let mut current_depth = 0;
191
192 for (i, frame) in self.stack.iter().rev().enumerate() {
193 if frame.ty == ControlStackFrameType::Else {
194 continue;
195 }
196
197 if current_depth == depth {
198 target_idx = self.depth() - 1 - i;
199 break;
200 }
201
202 current_depth += 1;
203 }
204
205 target_idx
206 };
207
208 for i in target_idx..self.depth() {
210 self.stack[i].set_might_return_early(true);
211 }
212 }
213
214 pub fn ret_ty(&self, depth: usize) -> Result<BlockType> {
216 if depth == 0 {
217 return Err(Error::InvalidDepth(depth));
218 }
219
220 self.stack
221 .get(self.depth() - depth)
222 .map(|f| f.result)
223 .ok_or_else(|| Error::InvalidDepth(depth))
224 }
225
226 pub fn ty(&self, depth: usize) -> Result<ControlStackFrameType> {
228 if depth == 0 {
229 return Err(Error::InvalidDepth(depth));
230 }
231
232 self.stack
233 .get(self.depth() - depth)
234 .map(|f| f.ty)
235 .ok_or_else(|| Error::InvalidDepth(depth))
236 }
237
238 pub fn len(&self) -> usize {
240 self.stack.len()
241 }
242
243 pub fn is_empty(&self) -> bool {
245 self.stack.is_empty()
246 }
247}