1use std::collections::HashSet;
4
5use wasm_encoder::{
6 CompositeInnerType, CompositeType, FieldType, StorageType, StructType, SubType, TypeSection,
7 ValType,
8};
9
10use crate::codegen::symbol_table::SymbolTable;
11use crate::{
12 ClosureBody, ExprId, StmtId, Type, TypedAst, TypedExprKind, TypedParam, TypedStmtKind,
13};
14
15pub fn collect(
16 ta: &TypedAst,
17 symbols: &SymbolTable,
18) -> Result<Vec<ValType>, crate::compiler_error::CompilerFailure> {
19 let mut state = Collector {
20 ta,
21 symbols,
22 seen: HashSet::new(),
23 order: Vec::new(),
24 };
25 let function_bodies: Vec<(Vec<TypedParam>, StmtId)> = ta
26 .functions
27 .iter()
28 .map(|f| (f.params.clone(), f.body))
29 .collect();
30 for (params, body) in function_bodies {
31 for p in ¶ms {
32 if p.boxed {
33 state.note(&p.ty)?;
34 }
35 }
36 state.walk_stmt(body)?;
37 }
38 let stmt_ids: Vec<StmtId> = ta.top_level_statements.clone();
39 for sid in stmt_ids {
40 state.walk_stmt(sid)?;
41 }
42 for member in class_member_params(ta) {
46 for p in member {
47 if p.boxed {
48 state.note(&p.ty)?;
49 }
50 }
51 }
52 for body in ta.class_body_roots() {
53 state.walk_stmt(body)?;
54 }
55 for init in ta.class_field_initializers() {
56 state.walk_expr(init)?;
57 }
58 Ok(state.order)
59}
60
61fn class_member_params(ta: &TypedAst) -> Vec<Vec<TypedParam>> {
64 let mut out = Vec::new();
65 for ty_decl in &ta.types {
66 let crate::TypedTypeDecl::Class(c) = ty_decl else {
67 continue;
68 };
69 if let Some(ctor) = &c.constructor {
70 out.push(ctor.params.clone());
71 }
72 out.extend(c.methods.iter().map(|m| m.params.clone()));
73 for a in &c.accessors {
74 if let crate::TypedClassAccessor::Setter { param, .. } = a {
75 out.push(vec![param.clone()]);
76 }
77 }
78 }
79 out
80}
81
82struct Collector<'a> {
83 ta: &'a TypedAst,
84 symbols: &'a SymbolTable,
85 seen: HashSet<ValType>,
86 order: Vec<ValType>,
87}
88
89impl Collector<'_> {
90 fn note(&mut self, ty: &Type) -> Result<(), crate::compiler_error::CompilerFailure> {
92 let valtype = self.symbols.slot_value_type(ty)?;
93 if self.seen.insert(valtype) {
94 self.order.push(valtype);
95 };
96 Ok(())
97 }
98
99 fn walk_stmt(&mut self, id: StmtId) -> Result<(), crate::compiler_error::CompilerFailure> {
100 let _: () = match &self
101 .ta
102 .try_stmt(id)
103 .map_err(crate::codegen::arena_failure)?
104 .kind
105 {
106 TypedStmtKind::Block(stmts) => {
107 for &s in stmts {
108 self.walk_stmt(s)?;
109 }
110 }
111 TypedStmtKind::Let {
112 ty, value, boxed, ..
113 } => {
114 if *boxed {
115 self.note(ty)?;
116 }
117 self.walk_expr(*value)?;
118 }
119 TypedStmtKind::Const { value, .. } => self.walk_expr(*value)?,
120 TypedStmtKind::If {
121 condition,
122 then_block,
123 else_block,
124 } => {
125 self.walk_expr(*condition)?;
126 self.walk_stmt(*then_block)?;
127 if let Some(eb) = else_block {
128 self.walk_stmt(*eb)?;
129 }
130 }
131 TypedStmtKind::While { condition, body } => {
132 self.walk_expr(*condition)?;
133 self.walk_stmt(*body)?;
134 }
135 TypedStmtKind::For {
136 init,
137 condition,
138 update,
139 body,
140 } => {
141 if let Some(i) = init {
142 self.walk_stmt(*i)?;
143 }
144 if let Some(c) = condition {
145 self.walk_expr(*c)?;
146 }
147 if let Some(u) = update {
148 self.walk_stmt(*u)?;
149 }
150 self.walk_stmt(*body)?;
151 }
152 TypedStmtKind::ForOf { iter, body, .. } => {
153 self.walk_expr(*iter)?;
154 self.walk_stmt(*body)?;
155 }
156 TypedStmtKind::DoWhile { body, condition } => {
157 self.walk_stmt(*body)?;
158 self.walk_expr(*condition)?;
159 }
160 TypedStmtKind::Switch {
161 discriminant,
162 cases,
163 default,
164 ..
165 } => {
166 self.walk_expr(*discriminant)?;
167 for comparison in cases
168 .iter()
169 .flat_map(crate::TypedSwitchCase::label_comparisons)
170 {
171 self.walk_expr(comparison)?;
172 }
173 for case in cases {
174 self.walk_stmt(case.body)?;
175 }
176 if let Some(d) = default {
177 self.walk_stmt(*d)?;
178 }
179 }
180 TypedStmtKind::Break | TypedStmtKind::Continue | TypedStmtKind::ReboxLocal { .. } => {}
181 TypedStmtKind::Return(value) => {
182 if let Some(v) = value {
183 self.walk_expr(*v)?;
184 }
185 }
186 TypedStmtKind::Expr(e) => self.walk_expr(*e)?,
187 TypedStmtKind::AssignLocal { value, .. }
188 | TypedStmtKind::AssignGlobal { value, .. } => self.walk_expr(*value)?,
189 TypedStmtKind::AssignField {
190 receiver, value, ..
191 } => {
192 self.walk_expr(*receiver)?;
193 self.walk_expr(*value)?;
194 }
195 TypedStmtKind::AssignIndex {
196 receiver,
197 index,
198 value,
199 ..
200 } => {
201 self.walk_expr(*receiver)?;
202 self.walk_expr(*index)?;
203 self.walk_expr(*value)?;
204 }
205 TypedStmtKind::NarrowRegion { source, body, .. } => {
206 self.walk_expr(*source)?;
207 self.walk_stmt(*body)?;
208 }
209 TypedStmtKind::Throw { value } => self.walk_expr(*value)?,
210 TypedStmtKind::Try {
211 body,
212 catches,
213 finally,
214 } => {
215 self.walk_stmt(*body)?;
216 for c in catches {
217 if c.boxed {
218 self.note(&c.ty)?;
219 }
220 self.walk_stmt(c.body)?;
221 }
222 if let Some(f) = finally {
223 self.walk_stmt(*f)?;
224 }
225 }
226 };
227 Ok(())
228 }
229
230 fn walk_expr(&mut self, id: ExprId) -> Result<(), crate::compiler_error::CompilerFailure> {
231 let _: () = match &self
232 .ta
233 .try_expr(id)
234 .map_err(crate::codegen::arena_failure)?
235 .kind
236 {
237 TypedExprKind::Closure {
238 params,
239 captured,
240 body,
241 ..
242 } => {
243 for p in params {
244 if p.boxed {
245 self.note(&p.ty)?;
246 }
247 }
248 for c in captured {
249 if c.boxed {
250 self.note(&c.ty)?;
251 }
252 }
253 match *body {
254 ClosureBody::Expr(e) => self.walk_expr(e)?,
255 ClosureBody::Block(b) => self.walk_stmt(b)?,
256 }
257 }
258 TypedExprKind::Binary { lhs, rhs, .. } => {
259 self.walk_expr(*lhs)?;
260 self.walk_expr(*rhs)?;
261 }
262 TypedExprKind::EffectThen { effect, result } => {
263 self.walk_expr(*effect)?;
264 self.walk_expr(*result)?;
265 }
266 TypedExprKind::Sequence { stmts, result } => {
267 for &stmt in stmts {
268 self.walk_stmt(stmt)?;
269 }
270 self.walk_expr(*result)?;
271 }
272 TypedExprKind::Unary { operand, .. } => self.walk_expr(*operand)?,
273 TypedExprKind::TypeofTag { value, .. } | TypedExprKind::InstanceOf { value, .. } => {
274 self.walk_expr(*value)?;
275 }
276 TypedExprKind::Call { args, .. }
277 | TypedExprKind::McpCall { args, .. }
278 | TypedExprKind::SuperCtorCall { args, .. }
279 | TypedExprKind::SuperMethodCall { args, .. } => {
280 for &a in args {
281 self.walk_expr(a)?;
282 }
283 }
284 TypedExprKind::CallClosure { callee, args } => {
285 self.walk_expr(*callee)?;
286 for &a in args {
287 self.walk_expr(a)?;
288 }
289 }
290 TypedExprKind::GenericCall { args, .. } => {
291 for a in args {
292 self.walk_expr(a.expr)?;
293 }
294 }
295 TypedExprKind::MethodCall { receiver, args, .. } => {
296 self.walk_expr(*receiver)?;
297 for &a in args {
298 self.walk_expr(a)?;
299 }
300 }
301 TypedExprKind::GenericMethodCall { receiver, args, .. } => {
302 self.walk_expr(*receiver)?;
303 for a in args {
304 self.walk_expr(a.expr)?;
305 }
306 }
307 TypedExprKind::IntrinsicCall { args, .. } => {
308 for &a in args {
309 self.walk_expr(a)?;
310 }
311 }
312 TypedExprKind::ObjectLiteral { members, .. } => {
313 for member in members {
314 for expression in member.expressions() {
315 self.walk_expr(expression)?;
316 }
317 }
318 }
319 TypedExprKind::ArrayLiteral { elements, .. } => {
320 for e in elements {
321 self.walk_expr(e.expr_id())?;
322 }
323 }
324 TypedExprKind::TupleLiteral { elements, .. } => {
325 for &e in elements {
326 self.walk_expr(e)?;
327 }
328 }
329 TypedExprKind::FieldAccess { receiver, .. }
330 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => {
331 self.walk_expr(*receiver)?;
332 }
333 TypedExprKind::IndexAccess { receiver, index } => {
334 self.walk_expr(*receiver)?;
335 self.walk_expr(*index)?;
336 }
337 TypedExprKind::Narrowed { source, inner, .. } => {
338 self.walk_expr(*source)?;
339 self.walk_expr(*inner)?;
340 }
341 TypedExprKind::Ternary { cond, then_, else_ } => {
342 self.walk_expr(*cond)?;
343 self.walk_expr(*then_)?;
344 self.walk_expr(*else_)?;
345 }
346 TypedExprKind::NullishCoalesce { lhs, rhs } => {
347 self.walk_expr(*lhs)?;
348 self.walk_expr(*rhs)?;
349 }
350 TypedExprKind::OptionalChain { base, parts } => {
351 self.walk_expr(*base)?;
352 for part in parts {
353 match part {
354 crate::TypedChainPart::Index { idx, .. } => self.walk_expr(*idx)?,
355 crate::TypedChainPart::Call { args, .. }
356 | crate::TypedChainPart::MethodCall { args, .. } => {
357 for a in args {
358 self.walk_expr(*a)?;
359 }
360 }
361 crate::TypedChainPart::Field { .. }
362 | crate::TypedChainPart::InterfaceProperty { .. }
363 | crate::TypedChainPart::NonNull { .. } => {}
364 }
365 }
366 }
367 TypedExprKind::PostfixUnary { target, .. } => match target {
368 crate::PostfixTarget::Local {
369 boxed, target_ty, ..
370 } => {
371 if *boxed {
372 self.note(target_ty)?;
373 }
374 }
375 crate::PostfixTarget::Global { .. } => {}
376 crate::PostfixTarget::Field { receiver, .. } => self.walk_expr(*receiver)?,
377 crate::PostfixTarget::Index {
378 receiver, index, ..
379 } => {
380 self.walk_expr(*receiver)?;
381 self.walk_expr(*index)?;
382 }
383 },
384 TypedExprKind::NonNullAssert { value } | TypedExprKind::Cast { value, .. } => {
385 self.walk_expr(*value)?;
386 }
387 TypedExprKind::Number(_)
388 | TypedExprKind::BigInt(_)
389 | TypedExprKind::String(_)
390 | TypedExprKind::Boolean(_)
391 | TypedExprKind::Null
392 | TypedExprKind::Undefined
393 | TypedExprKind::This
394 | TypedExprKind::Regex { .. }
395 | TypedExprKind::LocalRef { .. }
396 | TypedExprKind::LocalNarrowRef { .. }
397 | TypedExprKind::GlobalRef { .. }
398 | TypedExprKind::FunctionRef { .. }
399 | TypedExprKind::NumberEnumMember { .. }
400 | TypedExprKind::StringEnumMember { .. } => {}
401 };
402 Ok(())
403 }
404}
405
406pub fn emit(
408 value_types_to_emit: &[ValType],
409 types: &mut TypeSection,
410 symbols: &mut SymbolTable,
411 next_type_idx: &mut u32,
412) {
413 for val in value_types_to_emit {
414 let field = FieldType {
415 element_type: StorageType::Val(match *val {
416 ValType::Ref(mut reference) => {
417 reference.nullable = true;
418 ValType::Ref(reference)
419 }
420 value => value,
421 }),
422 mutable: true,
423 };
424 types.ty().subtype(&SubType {
425 is_final: false,
426 supertype_idx: None,
427 composite_type: CompositeType {
428 inner: CompositeInnerType::Struct(StructType {
429 fields: vec![
430 field,
431 FieldType {
432 element_type: StorageType::Val(ValType::I32),
433 mutable: true,
434 },
435 ]
436 .into_boxed_slice(),
437 }),
438 shared: false,
439 descriptor: None,
440 describes: None,
441 },
442 });
443 symbols.record_box_type(*val, *next_type_idx);
444 *next_type_idx += 1;
445 }
446}