walrus/module/functions/local_function/
context.rs1use crate::error::{ErrorKind, Result};
4use crate::ir::{BlockKind, Instr, InstrLocId, InstrSeq, InstrSeqId, InstrSeqType};
5use crate::module::functions::{FunctionId, LocalFunction};
6use crate::module::Module;
7use crate::parse::IndicesToIds;
8use crate::ty::ValType;
9use crate::{ModuleTypes, TypeId};
10use anyhow::Context;
11
12#[derive(Debug)]
13pub(crate) struct ControlFrame {
14 pub start_types: Box<[ValType]>,
16
17 pub end_types: Box<[ValType]>,
19
20 pub unreachable: bool,
23
24 pub block: InstrSeqId,
26
27 pub kind: BlockKind,
29}
30
31pub(crate) type ControlStack = Vec<ControlFrame>;
33
34#[derive(Debug)]
35pub(crate) struct ValidationContext<'a> {
36 pub module: &'a Module,
38
39 pub indices: &'a IndicesToIds,
41
42 pub func_id: FunctionId,
44
45 pub func: &'a mut LocalFunction,
47
48 pub controls: &'a mut ControlStack,
50
51 pub if_else: Vec<IfElseState>,
53}
54
55#[derive(Debug)]
56pub struct IfElseState {
57 pub start: InstrLocId,
58 pub consequent: InstrSeqId,
59 pub alternative: Option<InstrSeqId>,
60}
61
62impl<'a> ValidationContext<'a> {
63 pub fn new(
65 module: &'a Module,
66 indices: &'a IndicesToIds,
67 func_id: FunctionId,
68 func: &'a mut LocalFunction,
69 controls: &'a mut ControlStack,
70 ) -> ValidationContext<'a> {
71 ValidationContext {
72 module,
73 indices,
74 func_id,
75 func,
76 controls,
77 if_else: Vec::new(),
78 }
79 }
80
81 pub fn push_control(
82 &mut self,
83 kind: BlockKind,
84 start_types: Box<[ValType]>,
85 end_types: Box<[ValType]>,
86 ) -> Result<InstrSeqId> {
87 impl_push_control(
88 &self.module.types,
89 kind,
90 self.func,
91 self.controls,
92 start_types,
93 end_types,
94 )
95 }
96
97 pub fn push_control_with_ty(&mut self, kind: BlockKind, ty: TypeId) -> InstrSeqId {
98 let (start_types, end_types) = self.module.types.params_results(ty);
99 let start_types: Box<[_]> = start_types.into();
100 let end_types: Box<[_]> = end_types.into();
101 impl_push_control_with_ty(
102 &self.module.types,
103 kind,
104 self.func,
105 self.controls,
106 ty.into(),
107 start_types,
108 end_types,
109 )
110 }
111
112 pub fn pop_control(&mut self) -> Result<(ControlFrame, InstrSeqId)> {
113 let frame = impl_pop_control(self.controls)?;
114 let block = frame.block;
115 Ok((frame, block))
116 }
117
118 pub fn unreachable(&mut self) {
119 let frame = self.controls.last_mut().unwrap();
120 frame.unreachable = true;
121 }
122
123 pub fn control(&self, n: usize) -> Result<&ControlFrame> {
124 if n >= self.controls.len() {
125 anyhow::bail!("jump to nonexistent control block");
126 }
127 let idx = self.controls.len() - n - 1;
128 Ok(&self.controls[idx])
129 }
130
131 pub fn alloc_instr_in_block(
132 &mut self,
133 block: InstrSeqId,
134 instr: impl Into<Instr>,
135 loc: InstrLocId,
136 ) {
137 self.func.block_mut(block).instrs.push((instr.into(), loc));
138 }
139
140 pub fn alloc_instr_in_control(
141 &mut self,
142 control: usize,
143 instr: impl Into<Instr>,
144 loc: InstrLocId,
145 ) -> Result<()> {
146 let frame = self.control(control)?;
147 if frame.unreachable {
148 return Ok(());
149 }
150 let block = frame.block;
151 self.alloc_instr_in_block(block, instr, loc);
152 Ok(())
153 }
154
155 pub fn alloc_instr(&mut self, instr: impl Into<Instr>, loc: InstrLocId) {
156 self.alloc_instr_in_control(0, instr, loc).unwrap();
157 }
158
159 pub fn add_legacy_catch(&mut self, catch: crate::ir::LegacyCatch) -> Result<()> {
160 let frame = self.control(1)?; let block = frame.block;
163 let seq = self.func.block_mut(block);
164
165 if let Some((Instr::Try(ref mut try_instr), _)) = seq.instrs.last_mut() {
167 try_instr.catches.push(catch);
168 return Ok(());
169 }
170
171 anyhow::bail!("No Try instruction found to add catch clause to");
172 }
173}
174
175fn impl_push_control(
176 types: &ModuleTypes,
177 kind: BlockKind,
178 func: &mut LocalFunction,
179 controls: &mut ControlStack,
180 start_types: Box<[ValType]>,
181 end_types: Box<[ValType]>,
182) -> Result<InstrSeqId> {
183 let ty = InstrSeqType::existing(types, &start_types, &end_types).ok_or_else(|| {
184 anyhow::anyhow!(
185 "attempted to push a control frame for an instruction \
186 sequence with a type that does not exist"
187 )
188 .context(format!("type: {:?} -> {:?}", &start_types, &end_types))
189 })?;
190
191 Ok(impl_push_control_with_ty(
192 types,
193 kind,
194 func,
195 controls,
196 ty,
197 start_types,
198 end_types,
199 ))
200}
201
202fn impl_push_control_with_ty(
203 types: &ModuleTypes,
204 kind: BlockKind,
205 func: &mut LocalFunction,
206 controls: &mut ControlStack,
207 ty: InstrSeqType,
208 start_types: Box<[ValType]>,
209 end_types: Box<[ValType]>,
210) -> InstrSeqId {
211 if let InstrSeqType::MultiValue(ty) = ty {
212 debug_assert_eq!(types.params(ty), &start_types[..]);
213 debug_assert_eq!(types.results(ty), &end_types[..]);
214 }
215
216 let block = func.add_block(|id| InstrSeq::new(id, ty));
217
218 controls.push(ControlFrame {
219 start_types,
220 end_types,
221 unreachable: false,
222 block,
223 kind,
224 });
225
226 block
227}
228
229fn impl_pop_control(controls: &mut ControlStack) -> Result<ControlFrame> {
230 controls
231 .last()
232 .ok_or(ErrorKind::InvalidWasm)
233 .context("attempted to pop a frame from an empty control stack")?;
234 let frame = controls.pop().unwrap();
235 Ok(frame)
236}