1use crate::prelude::*;
17use crate::{
18 BinaryReaderError, BlockType, BrTable, CompositeInnerType, ContType, FrameKind, FuncType,
19 Operator, OperatorsReader, RefType, Result, ResumeTable, SubType, TryTable, ValType,
20};
21
22pub trait ModuleArity {
27 fn sub_type_at(&self, type_idx: u32) -> Option<&SubType>;
29
30 fn tag_type_arity(&self, at: u32) -> Option<(u32, u32)>;
32
33 fn type_index_of_function(&self, function_idx: u32) -> Option<u32>;
35
36 fn func_type_of_cont_type(&self, c: &ContType) -> Option<&FuncType>;
38
39 fn sub_type_of_ref_type(&self, rt: &RefType) -> Option<&SubType>;
41
42 fn control_stack_height(&self) -> u32;
44
45 fn label_block(&self, depth: u32) -> Option<(BlockType, FrameKind)>;
47
48 fn sub_type_arity(&self, t: &SubType) -> Option<(u32, u32)> {
50 match &t.composite_type.inner {
51 CompositeInnerType::Func(f) => {
52 Some((f.params().len() as u32, f.results().len() as u32))
53 }
54 CompositeInnerType::Struct(s) => Some((s.fields.len() as u32, s.fields.len() as u32)),
55 CompositeInnerType::Array(_) => None,
56 CompositeInnerType::Cont(c) => {
57 let f = self.func_type_of_cont_type(c)?;
58 Some((f.params().len() as u32, f.results().len() as u32))
59 }
60 }
61 }
62
63 fn block_type_arity(&self, ty: BlockType) -> Option<(u32, u32)> {
65 match ty {
66 BlockType::Empty => Some((0, 0)),
67 BlockType::Type(_) => Some((0, 1)),
68 BlockType::FuncType(t) => self.sub_type_arity(self.sub_type_at(t)?),
69 }
70 }
71}
72
73impl OperatorsReader<'_> {
74 pub fn operator_arity(&self, module: &impl ModuleArity) -> Result<(u32, u32)> {
76 self.clone().read()?.operator_arity(module).ok_or_else(|| {
77 BinaryReaderError::new("operator arity is unknown", self.original_position())
78 })
79 }
80}
81
82impl Operator<'_> {
83 pub fn operator_arity(&self, module: &impl ModuleArity) -> Option<(u32, u32)> {
86 #[cfg_attr(not(feature = "simd"), allow(unused_macro_rules))]
87 macro_rules! operator_arity {
88 ($visit:ident $args:tt arity $a:tt -> $b:tt) => (Some(($a, $b)));
89 ($visit:ident { $($args:ident)* } arity custom) => ($visit(module, $($args),*));
90 }
91 macro_rules! define_arity {
92 ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*) )*) => (
93 match self.clone() {
94 $(
95 Operator::$op $({ $($arg),* })? => {
96 $(
97 $(let _ = $arg;)*
98 )?
99 operator_arity!($visit {$( $($arg)* )?} $($ann)*)
100 }
101 )*
102 }
103 );
104 }
105 crate::for_each_operator!(define_arity)
106 }
107}
108
109fn visit_block(module: &dyn ModuleArity, block: BlockType) -> Option<(u32, u32)> {
110 let (params, _) = module.block_type_arity(block)?;
111 Some((params, params))
112}
113
114fn visit_loop(module: &dyn ModuleArity, block: BlockType) -> Option<(u32, u32)> {
115 visit_block(module, block)
116}
117
118fn visit_if(module: &dyn ModuleArity, block: BlockType) -> Option<(u32, u32)> {
119 let (params, results) = visit_block(module, block)?;
120 Some((params + 1, results))
121}
122
123fn visit_else(module: &dyn ModuleArity) -> Option<(u32, u32)> {
124 let (ty, _kind) = module.label_block(0)?;
125 let (params, results) = module.block_type_arity(ty)?;
126 Some((results, params))
127}
128
129fn visit_end(module: &dyn ModuleArity) -> Option<(u32, u32)> {
130 let (ty, _kind) = module.label_block(0)?;
131 let (_params, results) = module.block_type_arity(ty)?;
132 Some((results, results))
133}
134
135fn visit_br(module: &dyn ModuleArity, depth: u32) -> Option<(u32, u32)> {
136 let (ty, kind) = module.label_block(depth)?;
137 let (params, results) = module.block_type_arity(ty)?;
138 let n = match kind {
139 FrameKind::Loop => params,
140 _ => results,
141 };
142 Some((n, 0))
143}
144
145fn visit_br_if(module: &dyn ModuleArity, depth: u32) -> Option<(u32, u32)> {
146 let (params, _) = visit_br(module, depth)?;
147 Some((params + 1, params))
148}
149
150fn visit_br_table(module: &dyn ModuleArity, table: BrTable<'_>) -> Option<(u32, u32)> {
151 let (params, results) = visit_br(module, table.default())?;
152 Some((params + 1, results))
153}
154
155fn visit_return(module: &dyn ModuleArity) -> Option<(u32, u32)> {
156 let height = module.control_stack_height().checked_sub(1)?;
157 let (ty, _) = module.label_block(height)?;
158 let (_, results) = module.block_type_arity(ty)?;
159 Some((results, 0))
160}
161
162fn visit_call(module: &dyn ModuleArity, func: u32) -> Option<(u32, u32)> {
163 module.sub_type_arity(module.sub_type_at(module.type_index_of_function(func)?)?)
164}
165
166fn visit_call_indirect(module: &dyn ModuleArity, ty: u32, _table: u32) -> Option<(u32, u32)> {
167 let (params, results) = module.sub_type_arity(module.sub_type_at(ty)?)?;
168 Some((params + 1, results))
169}
170
171fn visit_struct_new(module: &dyn ModuleArity, ty: u32) -> Option<(u32, u32)> {
172 let (params, _results) = module.sub_type_arity(module.sub_type_at(ty)?)?;
173 Some((params, 1))
174}
175
176fn visit_array_new_fixed(_module: &dyn ModuleArity, _ty: u32, size: u32) -> Option<(u32, u32)> {
177 Some((size, 1))
178}
179
180fn visit_br_on_cast(
181 module: &dyn ModuleArity,
182 depth: u32,
183 _from: RefType,
184 _to: RefType,
185) -> Option<(u32, u32)> {
186 let (params, _) = visit_br(module, depth)?;
187 Some((params, params))
188}
189
190fn visit_br_on_cast_fail(
191 module: &dyn ModuleArity,
192 depth: u32,
193 _from: RefType,
194 _to: RefType,
195) -> Option<(u32, u32)> {
196 let (params, _) = visit_br(module, depth)?;
197 Some((params, params))
198}
199
200fn visit_typed_select_multi(_module: &dyn ModuleArity, tys: Vec<ValType>) -> Option<(u32, u32)> {
201 let len = u32::try_from(tys.len()).unwrap();
202 Some((1 + 2 * len, len))
203}
204
205fn visit_return_call(module: &dyn ModuleArity, func: u32) -> Option<(u32, u32)> {
206 let (params, _) = visit_call(module, func)?;
207 Some((params, 0))
208}
209
210fn visit_return_call_indirect(module: &dyn ModuleArity, ty: u32, table: u32) -> Option<(u32, u32)> {
211 let (params, _) = visit_call_indirect(module, ty, table)?;
212 Some((params, 0))
213}
214
215fn visit_try_table(module: &dyn ModuleArity, table: TryTable) -> Option<(u32, u32)> {
216 let (params, _) = module.block_type_arity(table.ty)?;
217 Some((params, params))
218}
219
220fn visit_throw(module: &dyn ModuleArity, tag: u32) -> Option<(u32, u32)> {
221 let (params, _) = module.tag_type_arity(tag)?;
222 Some((params, 0))
223}
224
225fn visit_try(module: &dyn ModuleArity, ty: BlockType) -> Option<(u32, u32)> {
226 visit_block(module, ty)
227}
228
229fn visit_catch(module: &dyn ModuleArity, tag: u32) -> Option<(u32, u32)> {
230 let (params, _) = visit_end(module)?;
231 let (tag_params, _) = module.tag_type_arity(tag)?;
232 Some((params, tag_params))
233}
234
235fn visit_delegate(module: &dyn ModuleArity, _depth: u32) -> Option<(u32, u32)> {
236 visit_end(module)
237}
238
239fn visit_catch_all(module: &dyn ModuleArity) -> Option<(u32, u32)> {
240 let (params, _) = visit_end(module)?;
241 Some((params, 0))
242}
243
244fn visit_call_ref(module: &dyn ModuleArity, ty: u32) -> Option<(u32, u32)> {
245 let (params, results) = module.sub_type_arity(module.sub_type_at(ty)?)?;
246 Some((params + 1, results))
247}
248
249fn visit_return_call_ref(module: &dyn ModuleArity, ty: u32) -> Option<(u32, u32)> {
250 let (params, _) = visit_call_ref(module, ty)?;
251 Some((params, 0))
252}
253
254fn visit_br_on_null(module: &dyn ModuleArity, depth: u32) -> Option<(u32, u32)> {
255 let (params, _results) = visit_br(module, depth)?;
256 Some((params + 1, params + 1))
257}
258
259fn visit_br_on_non_null(module: &dyn ModuleArity, depth: u32) -> Option<(u32, u32)> {
260 let (params, _results) = visit_br(module, depth)?;
261 Some((params, params.checked_sub(1)?))
262}
263
264fn visit_cont_bind(module: &dyn ModuleArity, arg: u32, result: u32) -> Option<(u32, u32)> {
265 let (arg_params, _) = module.sub_type_arity(module.sub_type_at(arg)?)?;
266 let (result_params, _) = module.sub_type_arity(module.sub_type_at(result)?)?;
267 Some((arg_params.checked_sub(result_params)? + 1, 1))
268}
269
270fn visit_suspend(module: &dyn ModuleArity, tag: u32) -> Option<(u32, u32)> {
271 module.tag_type_arity(tag)
272}
273
274fn visit_resume(module: &dyn ModuleArity, cont: u32, _table: ResumeTable) -> Option<(u32, u32)> {
275 let (params, results) = module.sub_type_arity(module.sub_type_at(cont)?)?;
276 Some((params + 1, results))
277}
278
279fn visit_resume_throw(
280 module: &dyn ModuleArity,
281 cont: u32,
282 tag: u32,
283 _table: ResumeTable,
284) -> Option<(u32, u32)> {
285 let (params, _) = module.tag_type_arity(tag)?;
286 let (_, results) = module.sub_type_arity(module.sub_type_at(cont)?)?;
287 Some((params + 1, results))
288}
289
290fn visit_switch(module: &dyn ModuleArity, cont: u32, _tag: u32) -> Option<(u32, u32)> {
291 let (params, _) = module.sub_type_arity(module.sub_type_at(cont)?)?;
292 let st = &module.sub_type_at(cont)?.composite_type.inner;
293 let CompositeInnerType::Cont(ct) = &st else {
294 return None;
295 };
296 let last_param = module.func_type_of_cont_type(ct)?.params().last()?;
297 let (cont_params, _) =
298 module.sub_type_arity(module.sub_type_of_ref_type(&last_param.as_reference_type()?)?)?;
299 Some((params, cont_params))
300}