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