1use crate::codegen::CodegenCtx;
2use crate::codegen::bounds::{
3 emit_checked_index, emit_checked_index_with_length, stash_array_length, stash_index_operand,
4};
5use crate::codegen::function_emitter::FunctionEmitter;
6use crate::codegen::init_guard;
7use crate::{
8 EnumVariantPayload, ExprId, Ident, MangledName, StmtId, Type, TypedStmtKind, TypedSwitchCase,
9 TypedSwitchValue,
10};
11use wasm_encoder::{BlockType, HeapType, Instruction, RefType, ValType};
12
13use super::cast;
14use super::expr::{
15 emit_expr, emit_method_call, emit_object_property_write, stash_receiver_as_object_shape,
16};
17
18fn branches(kind: &TypedStmtKind) -> bool {
27 matches!(
28 kind,
29 TypedStmtKind::If { .. }
30 | TypedStmtKind::While { .. }
31 | TypedStmtKind::DoWhile { .. }
32 | TypedStmtKind::For { .. }
33 | TypedStmtKind::ForOf { .. }
34 | TypedStmtKind::Switch { .. }
35 | TypedStmtKind::Try { .. }
36 )
37}
38
39pub fn emit_statement(
40 emitter: &mut FunctionEmitter,
41 ctx: &CodegenCtx,
42 id: StmtId,
43) -> Result<(), crate::compiler_error::CompilerFailure> {
44 let stmt = ctx.ta.try_stmt(id).map_err(crate::codegen::arena_failure)?;
45 emitter.record_span(stmt.span);
46 if branches(&stmt.kind) {
50 emitter.clear_all_narrow_shadows();
51 }
52 match &stmt.kind {
53 TypedStmtKind::Block(stmts) => {
54 emitter.push_scope();
57 for &child in stmts {
58 emit_statement(emitter, ctx, child)?;
59 }
60 emitter.pop_scope()?;
61 }
62 TypedStmtKind::Return(value) => {
63 if let Some(expr_id) = value {
64 emit_expr(emitter, ctx, *expr_id)?;
65 cast::emit_coerce_to_return_slot(
66 emitter,
67 ctx,
68 &ctx.ta
69 .try_expr(*expr_id)
70 .map_err(crate::codegen::arena_failure)?
71 .ty,
72 )?;
73 } else if emitter.wasm_result_type(ctx)?.is_some() {
74 super::expr::emit_undefined(emitter, ctx)?;
75 cast::emit_coerce_to_return_slot(emitter, ctx, &Type::Undefined)?;
76 }
77 super::finally::emit_transfer(emitter, super::finally::Transfer::Return)?;
78 }
79 TypedStmtKind::Expr(expr_id) => {
80 emit_expr(emitter, ctx, *expr_id)?;
81 emitter.instruction(Instruction::Drop);
82 }
83 TypedStmtKind::Let {
85 name,
86 ty,
87 value,
88 boxed,
89 ..
90 } => {
91 emit_expr(emitter, ctx, *value)?;
92 let value_ty = ctx
93 .ta
94 .try_expr(*value)
95 .map_err(crate::codegen::arena_failure)?
96 .ty
97 .clone();
98 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, ty)?;
99 if *boxed {
100 let box_idx = ctx.symbols.box_type_idx(ty)?.ok_or_else(|| {
101 crate::codegen::internal_failure("box type registered for every boxed Let")
102 })?;
103 if ctx.ta.parameter_initializations.contains(&id) {
104 let slot = emitter
105 .parameter_binding_cells
106 .get(name)
107 .copied()
108 .ok_or_else(|| {
109 crate::codegen::internal_failure(
110 "parameter pattern cell was not reserved",
111 )
112 })?;
113 let value_slot = emitter.add_anonymous_local(ctx.symbols.value_type(ty)?)?;
114 emitter.instruction(Instruction::LocalSet(value_slot));
115 emitter.instruction(Instruction::LocalGet(slot));
116 emitter.instruction(Instruction::LocalGet(value_slot));
117 emitter.instruction(Instruction::StructSet {
118 struct_type_index: box_idx,
119 field_index: 0,
120 });
121 super::binding_cells::mark_initialized(emitter, slot, box_idx);
122 return Ok(());
123 }
124 emitter.instruction(Instruction::I32Const(1));
125 emitter.instruction(Instruction::StructNew(box_idx));
126 let box_val = ValType::Ref(RefType {
127 nullable: false,
128 heap_type: HeapType::Concrete(box_idx),
129 });
130 let slot = emitter.define_local(name, box_val)?;
131 emitter.instruction(Instruction::LocalSet(slot));
132 } else {
133 let slot = emitter.define_local(name, ctx.symbols.value_type(ty)?)?;
134 emitter.instruction(Instruction::LocalSet(slot));
135 }
136 }
137 TypedStmtKind::ReboxLocal { ident, ty } => {
138 let slot = emitter.write_slot(&ident.name)?;
139 let box_idx = ctx.symbols.box_type_idx(ty)?.ok_or_else(|| {
140 crate::codegen::internal_failure("box type registered for every boxed Let")
141 })?;
142 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
143 emitter.instruction(Instruction::I32Const(1));
144 emitter.instruction(Instruction::StructNew(box_idx));
145 emitter.instruction(Instruction::LocalSet(slot));
146 }
147 TypedStmtKind::Const {
148 name, ty, value, ..
149 } => {
150 emit_expr(emitter, ctx, *value)?;
152 let value_ty = ctx
153 .ta
154 .try_expr(*value)
155 .map_err(crate::codegen::arena_failure)?
156 .ty
157 .clone();
158 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, ty)?;
159 let slot = emitter.define_local(name, ctx.symbols.value_type(ty)?)?;
160 emitter.instruction(Instruction::LocalSet(slot));
161 }
162 TypedStmtKind::AssignLocal {
163 ident,
164 target_ty,
165 value,
166 boxed,
167 narrowed_shadow_ty,
168 } => {
169 let slot = emitter.write_slot(&ident.name)?;
170 if *boxed {
171 let box_idx = ctx.symbols.box_type_idx(target_ty)?.ok_or_else(|| {
176 crate::codegen::internal_failure(
177 "box type registered for every boxed AssignLocal",
178 )
179 })?;
180 let value_ty = ctx
181 .ta
182 .try_expr(*value)
183 .map_err(crate::codegen::arena_failure)?
184 .ty
185 .clone();
186 emitter.instruction(Instruction::LocalGet(slot));
187 emit_expr(emitter, ctx, *value)?;
188 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, target_ty)?;
189 let initialization = ctx.ta.parameter_initializations.contains(&id);
190 if !initialization {
191 super::binding_cells::check_initialized(emitter, ctx, slot, box_idx)?;
192 }
193 emitter.instruction(Instruction::StructSet {
194 struct_type_index: box_idx,
195 field_index: 0,
196 });
197 if initialization {
198 super::binding_cells::mark_initialized(emitter, slot, box_idx);
199 }
200 } else {
201 emit_expr(emitter, ctx, *value)?;
202 let value_ty = ctx
203 .ta
204 .try_expr(*value)
205 .map_err(crate::codegen::arena_failure)?
206 .ty
207 .clone();
208 let stored_ty = match narrowed_shadow_ty {
216 Some(narrowed_ty) => {
217 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, narrowed_ty)?;
218 let narrowed_val = ctx.symbols.value_type(narrowed_ty)?;
219 let shadow_idx = emitter.add_anonymous_local(narrowed_val)?;
220 emitter.instruction(Instruction::LocalTee(shadow_idx));
221 emitter.install_narrow_shadow(&ident.name, shadow_idx, narrowed_val)?;
222 narrowed_ty
223 }
224 None => &value_ty,
225 };
226 cast::emit_coerce_to_slot(emitter, ctx, stored_ty, target_ty)?;
227 emitter.instruction(Instruction::LocalSet(slot));
228 }
229 }
230 TypedStmtKind::AssignGlobal {
231 mangled,
232 target_ty,
233 value,
234 ..
235 } => {
236 emit_assign_global(emitter, ctx, id, mangled, target_ty, *value)?;
237 }
238 TypedStmtKind::AssignField {
239 receiver,
240 name,
241 value,
242 } => {
243 emit_assign_field(emitter, ctx, *receiver, name, *value)?;
244 }
245 TypedStmtKind::AssignIndex {
246 receiver,
247 index,
248 value,
249 elem_ty,
250 } => {
251 emit_assign_index(emitter, ctx, *receiver, *index, *value, elem_ty)?;
252 }
253 TypedStmtKind::If {
254 condition,
255 then_block,
256 else_block,
257 } => {
258 let cond_ty = ctx
259 .ta
260 .try_expr(*condition)
261 .map_err(crate::codegen::arena_failure)?
262 .ty
263 .clone();
264 emit_expr(emitter, ctx, *condition)?;
265 cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
266 emitter.emit_if(BlockType::Empty);
267 emit_statement(emitter, ctx, *then_block)?;
268 if let Some(else_id) = else_block {
269 emitter.emit_else();
270 emit_statement(emitter, ctx, *else_id)?;
271 }
272 emitter.emit_end();
273 }
274 TypedStmtKind::While { condition, body } => {
275 emitter.emit_while_open();
278 let cond_ty = ctx
279 .ta
280 .try_expr(*condition)
281 .map_err(crate::codegen::arena_failure)?
282 .ty
283 .clone();
284 emit_expr(emitter, ctx, *condition)?;
285 cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
286 emitter.instruction(Instruction::I32Eqz);
287 let break_label = emitter.break_label()?;
288 emitter.instruction(Instruction::BrIf(break_label));
289 emit_statement(emitter, ctx, *body)?;
290 let continue_label = emitter.continue_label()?;
291 emitter.instruction(Instruction::Br(continue_label));
292 emitter.emit_while_close();
293 }
294 TypedStmtKind::Break => {
295 let transfer = super::finally::Transfer::Branch {
296 depth: emitter.wasm_block_depth - emitter.break_label()? - 1,
297 finally_floor: emitter.break_finally_floor(),
298 };
299 super::finally::emit_transfer(emitter, transfer)?;
300 }
301 TypedStmtKind::Continue => {
302 let transfer = super::finally::Transfer::Branch {
303 depth: emitter.wasm_block_depth - emitter.continue_label()? - 1,
304 finally_floor: emitter.continue_finally_floor(),
305 };
306 super::finally::emit_transfer(emitter, transfer)?;
307 }
308 TypedStmtKind::For { .. } | TypedStmtKind::ForOf { .. } | TypedStmtKind::DoWhile { .. } => {
309 return Err(crate::codegen::internal_failure(
310 "for / for-of / do-while must be lowered by the desugar pass before codegen",
311 ));
312 }
313 TypedStmtKind::Switch {
314 discriminant,
315 discriminant_ty,
316 cases,
317 default,
318 } => {
319 emit_switch(
320 emitter,
321 ctx,
322 *discriminant,
323 discriminant_ty,
324 cases,
325 default.as_ref().copied(),
326 )?;
327 }
328 TypedStmtKind::NarrowRegion {
329 path,
330 source,
331 binding,
332 cast_info,
333 body,
334 } => {
335 emitter.push_scope();
336 if path.chain.is_empty() {
337 let shadow_val = ctx.symbols.value_type(&cast_info.to_ty)?;
338 let shadow = emitter.add_anonymous_local(shadow_val)?;
339 emit_expr(emitter, ctx, *source)?;
340 cast::emit_narrowing_cast(emitter, ctx, cast_info)?;
341 emitter.instruction(Instruction::LocalSet(shadow));
342 emitter.install_narrow_shadow(&binding.name, shadow, shadow_val)?;
343 } else {
344 emitter.register_narrow_source(&binding.name, *source)?;
348 }
349 emit_statement(emitter, ctx, *body)?;
350 emitter.pop_scope()?;
351 }
352 TypedStmtKind::Throw { value } => {
353 emit_expr(emitter, ctx, *value)?;
355 crate::codegen::throw::emit_error_throw(emitter, ctx);
356 }
357 TypedStmtKind::Try {
358 body,
359 catches,
360 finally,
361 } => {
362 emit_try(emitter, ctx, *body, catches, *finally)?;
363 }
364 }
365 let _: () = if branches(&stmt.kind) {
366 emitter.clear_all_narrow_shadows();
367 };
368 Ok(())
369}
370
371fn emit_operand_once(
376 emitter: &mut FunctionEmitter,
377 ctx: &CodegenCtx,
378 expr: ExprId,
379) -> Result<u32, crate::compiler_error::CompilerFailure> {
380 let slot = emitter.add_anonymous_local(
381 ctx.symbols.value_type(
382 &ctx.ta
383 .try_expr(expr)
384 .map_err(crate::codegen::arena_failure)?
385 .ty,
386 )?,
387 )?;
388 emit_expr(emitter, ctx, expr)?;
389 emitter.instruction(Instruction::LocalSet(slot));
390 emitter.record_single_evaluation(expr, slot)?;
391 Ok(slot)
392}
393
394fn emit_assign_global(
395 emitter: &mut FunctionEmitter,
396 ctx: &CodegenCtx,
397 stmt: StmtId,
398 mangled: &MangledName,
399 target_ty: &Type,
400 value: ExprId,
401) -> Result<(), crate::compiler_error::CompilerFailure> {
402 emit_expr(emitter, ctx, value)?;
403 let value_ty = ctx
404 .ta
405 .try_expr(value)
406 .map_err(crate::codegen::arena_failure)?
407 .ty
408 .clone();
409 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, target_ty)?;
410 let idx = ctx
411 .symbols
412 .global_idx(mangled)
413 .ok_or_else(|| crate::codegen::internal_failure("Inferer guarantees the binding exists"))?;
414 let declares = init_guard::is_declaration(ctx, mangled, stmt);
416 if !declares {
417 init_guard::emit_check(emitter, ctx, mangled);
418 }
419 emitter.instruction(Instruction::GlobalSet(idx));
420 if declares {
421 init_guard::emit_mark_initialized(emitter, ctx, mangled);
422 }
423 Ok(())
424}
425
426fn emit_assign_field(
427 emitter: &mut FunctionEmitter,
428 ctx: &CodegenCtx,
429 receiver: ExprId,
430 name: &Ident,
431 value: ExprId,
432) -> Result<(), crate::compiler_error::CompilerFailure> {
433 let mark = emitter.single_evaluation_mark();
434 let receiver_ty = ctx
435 .ta
436 .source_type(receiver)
437 .map_err(crate::codegen::arena_failure)?
438 .clone();
439 let recv = emit_operand_once(emitter, ctx, receiver)?;
444 emit_operand_once(emitter, ctx, value)?;
445 emitter.instruction(Instruction::LocalGet(recv));
446 if ctx
447 .ta
448 .try_expr(receiver)
449 .map_err(crate::codegen::arena_failure)?
450 .ty
451 != receiver_ty
452 {
453 crate::codegen::cast_check::emit_operation_cast_on_stack(
454 emitter,
455 ctx,
456 &ctx.ta
457 .try_expr(receiver)
458 .map_err(crate::codegen::arena_failure)?
459 .ty,
460 &receiver_ty,
461 )?;
462 }
463 let recv = emitter.add_anonymous_local(ctx.symbols.value_type(&receiver_ty)?)?;
464 emitter.instruction(Instruction::LocalSet(recv));
465 match receiver_ty.peel() {
466 Type::ClassRef { mangled, .. } => {
468 emit_class_field_store(emitter, ctx, receiver, recv, mangled, name, value)?;
469 }
470 _ => emit_object_field_store(emitter, ctx, &receiver_ty, recv, name, value)?,
471 }
472 emitter.end_single_evaluations(mark)?;
473 Ok(())
474}
475
476#[allow(clippy::too_many_arguments)]
477fn emit_class_field_store(
478 emitter: &mut FunctionEmitter,
479 ctx: &CodegenCtx,
480 receiver: ExprId,
481 recv: u32,
482 mangled: &crate::MangledName,
483 name: &Ident,
484 value: ExprId,
485) -> Result<(), crate::compiler_error::CompilerFailure> {
486 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
489 let setter = crate::codegen::classes::accessor_setter_name(&name.name);
490 emit_method_call(
491 emitter,
492 ctx,
493 receiver,
494 mangled,
495 &setter,
496 std::slice::from_ref(&value),
497 None,
498 None,
499 &Type::Void,
500 )?;
501 emitter.instruction(Instruction::Drop);
502 return Ok(());
503 };
504 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
505 crate::codegen::internal_failure("class struct type recorded in classes::emit")
506 })?;
507 let intrinsics = ctx
508 .symbols
509 .intrinsic_type_indices()
510 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
511 let value_ty = ctx
512 .ta
513 .try_expr(value)
514 .map_err(crate::codegen::arena_failure)?
515 .ty
516 .clone();
517 emit_expr(emitter, ctx, value)?;
518 cast::emit_box(emitter, ctx, &value_ty)?;
519 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
520 emitter.instruction(Instruction::LocalSet(stored));
521 emitter.instruction(Instruction::LocalGet(recv));
522 emitter.instruction(Instruction::StructGet {
523 struct_type_index: struct_idx,
524 field_index: 2,
525 });
526 emitter.instruction(Instruction::I32Const(slot as i32));
527 emitter.instruction(Instruction::LocalGet(stored));
528 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
529 let index = emitter.add_anonymous_local(ValType::I32)?;
530 emitter.instruction(Instruction::I32Const(slot as i32));
531 emitter.instruction(Instruction::LocalSet(index));
532 crate::codegen::field_names::emit_mark_present(emitter, ctx, recv, index)?;
533 Ok(())
534}
535
536fn emit_object_field_store(
537 emitter: &mut FunctionEmitter,
538 ctx: &CodegenCtx,
539 receiver_ty: &Type,
540 recv: u32,
541 name: &Ident,
542 value: ExprId,
543) -> Result<(), crate::compiler_error::CompilerFailure> {
544 let object_shape = ctx
545 .symbols
546 .intrinsic_type_indices()
547 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?
548 .object_shape;
549 emitter.instruction(Instruction::LocalGet(recv));
550 let rcv_local = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
551 emit_object_property_write(emitter, ctx, rcv_local, name, value)?;
554 Ok(())
555}
556
557fn emit_assign_index(
558 emitter: &mut FunctionEmitter,
559 ctx: &CodegenCtx,
560 receiver: ExprId,
561 index: ExprId,
562 value: ExprId,
563 elem_ty: &Type,
564) -> Result<(), crate::compiler_error::CompilerFailure> {
565 let mark = emitter.single_evaluation_mark();
566 if ctx
567 .ta
568 .source_type(receiver)
569 .map_err(crate::codegen::arena_failure)?
570 .is_structural_object()
571 {
572 let object = emit_operand_once(emitter, ctx, receiver)?;
573 let key = emit_operand_once(emitter, ctx, index)?;
574 let stored = emit_operand_once(emitter, ctx, value)?;
575 emitter.instruction(Instruction::LocalGet(object));
576 cast::emit_box(
577 emitter,
578 ctx,
579 &ctx.ta
580 .try_expr(receiver)
581 .map_err(crate::codegen::arena_failure)?
582 .ty,
583 )?;
584 emitter.instruction(Instruction::LocalGet(key));
585 crate::codegen::cast_check::emit_operation_cast_on_stack(
586 emitter,
587 ctx,
588 &ctx.ta
589 .try_expr(index)
590 .map_err(crate::codegen::arena_failure)?
591 .ty,
592 &Type::String,
593 )?;
594 emitter.instruction(Instruction::LocalGet(stored));
595 cast::emit_box(
596 emitter,
597 ctx,
598 &ctx.ta
599 .try_expr(value)
600 .map_err(crate::codegen::arena_failure)?
601 .ty,
602 )?;
603 let Some(symbol) = ctx.require(
604 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
605 "dynamic write imported",
606 ) else {
607 return Ok(());
608 };
609 emitter.instruction(Instruction::Call(symbol));
610 } else if matches!(
611 ctx.ta
612 .source_type(receiver)
613 .map_err(crate::codegen::arena_failure)?
614 .peel(),
615 Type::Uint8Array
616 ) {
617 emit_uint8_index_store(emitter, ctx, receiver, index, value)?;
618 } else {
619 emit_array_index_store(emitter, ctx, receiver, index, value, elem_ty)?;
620 }
621 emitter.end_single_evaluations(mark)?;
622 Ok(())
623}
624
625fn emit_uint8_index_store(
627 emitter: &mut FunctionEmitter,
628 ctx: &CodegenCtx,
629 receiver: ExprId,
630 index: ExprId,
631 value: ExprId,
632) -> Result<(), crate::compiler_error::CompilerFailure> {
633 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
634 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
635 })?;
636 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
637 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
638 })?;
639 let recv_local = emit_operand_once(emitter, ctx, receiver)?;
640 let key = emit_operand_once(emitter, ctx, index)?;
641 let original_value = emit_operand_once(emitter, ctx, value)?;
644
645 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
649 nullable: false,
650 heap_type: HeapType::Concrete(raw_uint8_idx),
651 }))?;
652 emitter.instruction(Instruction::LocalGet(recv_local));
653 crate::codegen::cast_check::emit_operation_cast_on_stack(
654 emitter,
655 ctx,
656 &ctx.ta
657 .try_expr(receiver)
658 .map_err(crate::codegen::arena_failure)?
659 .ty,
660 &Type::Uint8Array,
661 )?;
662 emitter.instruction(Instruction::LocalGet(key));
663 super::expr::emit_index_number(
664 emitter,
665 ctx,
666 &ctx.ta
667 .try_expr(index)
668 .map_err(crate::codegen::arena_failure)?
669 .ty,
670 )?;
671 let idx_f64_local = stash_index_operand(emitter)?;
672 let value_local = emitter.add_anonymous_local(ValType::I32)?;
673 emitter.instruction(Instruction::LocalGet(original_value));
674 cast::emit_coerce_to_slot(
675 emitter,
676 ctx,
677 &ctx.ta
678 .try_expr(value)
679 .map_err(crate::codegen::arena_failure)?
680 .ty,
681 &Type::Number,
682 )?;
683 emit_to_uint8(emitter)?;
684 emitter.instruction(Instruction::LocalSet(value_local));
685 emitter.instruction(Instruction::StructGet {
686 struct_type_index: uint8_idx,
687 field_index: 1,
688 });
689 emitter.instruction(Instruction::LocalSet(raw_local));
690 let idx_local = emit_checked_index(emitter, ctx, raw_local, idx_f64_local)?;
691 emitter.instruction(Instruction::LocalGet(raw_local));
692 emitter.instruction(Instruction::LocalGet(idx_local));
693 emitter.instruction(Instruction::LocalGet(value_local));
694 emitter.instruction(Instruction::ArraySet(raw_uint8_idx));
695 Ok(())
696}
697
698fn emit_to_uint8(
702 emitter: &mut FunctionEmitter,
703) -> Result<(), crate::compiler_error::CompilerFailure> {
704 let truncated = emitter.add_anonymous_local(ValType::F64)?;
705 emitter.instruction(Instruction::F64Trunc);
706 emitter.instruction(Instruction::LocalTee(truncated));
707 emitter.instruction(Instruction::LocalGet(truncated));
708 emitter.instruction(Instruction::F64Const(256.0.into()));
709 emitter.instruction(Instruction::F64Div);
710 emitter.instruction(Instruction::F64Floor);
711 emitter.instruction(Instruction::F64Const(256.0.into()));
712 emitter.instruction(Instruction::F64Mul);
713 emitter.instruction(Instruction::F64Sub);
714 emitter.instruction(Instruction::I32TruncSatF64U);
715 Ok(())
716}
717
718fn emit_array_index_store(
720 emitter: &mut FunctionEmitter,
721 ctx: &CodegenCtx,
722 receiver: ExprId,
723 index: ExprId,
724 value: ExprId,
725 _elem_ty: &Type,
726) -> Result<(), crate::compiler_error::CompilerFailure> {
727 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
728 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
729 })?;
730 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
731 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
732 })?;
733 let object_idx = ctx
734 .symbols
735 .intrinsic_type_indices()
736 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
737 .object;
738 let recv_local = emit_operand_once(emitter, ctx, receiver)?;
739 let key = emit_operand_once(emitter, ctx, index)?;
740 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
743 nullable: true,
744 heap_type: HeapType::Concrete(object_idx),
745 }))?;
746 let value_ty = ctx
747 .ta
748 .try_expr(value)
749 .map_err(crate::codegen::arena_failure)?
750 .ty
751 .clone();
752 emit_expr(emitter, ctx, value)?;
753 cast::emit_box(emitter, ctx, &value_ty)?;
755 emitter.instruction(Instruction::LocalSet(value_local));
756
757 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
762 nullable: false,
763 heap_type: HeapType::Concrete(raw_array_idx),
764 }))?;
765 emitter.instruction(Instruction::LocalGet(recv_local));
766 crate::codegen::cast_check::emit_operation_cast_on_stack(
767 emitter,
768 ctx,
769 &ctx.ta
770 .try_expr(receiver)
771 .map_err(crate::codegen::arena_failure)?
772 .ty,
773 &Type::Array(Box::new(Type::Unknown)),
774 )?;
775 emitter.instruction(Instruction::LocalGet(key));
776 super::expr::emit_index_number(
777 emitter,
778 ctx,
779 &ctx.ta
780 .try_expr(index)
781 .map_err(crate::codegen::arena_failure)?
782 .ty,
783 )?;
784 let idx_f64_local = stash_index_operand(emitter)?;
785 let length = stash_array_length(emitter, array_idx)?;
786 emitter.instruction(Instruction::StructGet {
787 struct_type_index: array_idx,
788 field_index: 1,
789 });
790 emitter.instruction(Instruction::LocalSet(raw_arr_local));
791 let idx_local = emit_checked_index_with_length(emitter, ctx, length, idx_f64_local)?;
792 emitter.instruction(Instruction::LocalGet(raw_arr_local));
793 emitter.instruction(Instruction::LocalGet(idx_local));
794 emitter.instruction(Instruction::LocalGet(value_local));
795 emitter.instruction(Instruction::ArraySet(raw_array_idx));
796 Ok(())
797}
798
799pub(super) fn emit_try(
802 emitter: &mut FunctionEmitter,
803 ctx: &CodegenCtx,
804 body: StmtId,
805 catches: &[crate::TypedCatchClause],
806 finally: Option<StmtId>,
807) -> Result<(), crate::compiler_error::CompilerFailure> {
808 if let Some(finally) = finally {
809 super::finally::emit_try_finally(emitter, ctx, body, catches, finally)?;
810 return Ok(());
811 }
812 if catches.is_empty() {
813 emit_statement(emitter, ctx, body)?;
814 return Ok(());
815 }
816 let error_idx = ctx
817 .symbols
818 .intrinsic_type_indices()
819 .ok_or_else(|| crate::codegen::internal_failure("error intrinsic registered"))?
820 .error;
821 let tag_idx = ctx
822 .symbols
823 .error_tag_idx()
824 .ok_or_else(|| crate::codegen::internal_failure("error tag registered"))?;
825 emitter.emit_block(BlockType::Empty);
826 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
827 nullable: false,
828 heap_type: HeapType::Concrete(error_idx),
829 })));
830 emitter.instruction(Instruction::TryTable(
831 BlockType::Empty,
832 std::borrow::Cow::Owned(vec![wasm_encoder::Catch::One {
833 tag: tag_idx,
834 label: 0,
835 }]),
836 ));
837 emitter.bump_block_depth();
838 emit_statement(emitter, ctx, body)?;
839 emitter.emit_end();
840 emitter.instruction(Instruction::Br(1));
841 emitter.emit_end();
842 let err_stash = emitter.add_anonymous_local(ValType::Ref(RefType {
843 nullable: true,
844 heap_type: HeapType::Concrete(error_idx),
845 }))?;
846 emitter.instruction(Instruction::LocalSet(err_stash));
847 emit_catch_dispatch(emitter, ctx, catches, err_stash)?;
848 emitter.emit_end();
849 Ok(())
850}
851
852fn catch_filter_class(
856 ctx: &CodegenCtx,
857 clause_ty: &Type,
858) -> Result<Option<(u32, u32)>, crate::compiler_error::CompilerFailure> {
859 let Type::ClassRef { mangled, .. } = clause_ty.peel() else {
860 return Ok(None);
861 };
862 if *mangled == crate::mangle::prelude("Error") {
863 return Ok(None);
864 }
865 let vtable_global = ctx
866 .symbols
867 .class_vtable_global_idx(mangled)
868 .ok_or_else(|| {
869 crate::codegen::internal_failure("catch-annotation class vtable global recorded")
870 })?;
871 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
872 crate::codegen::internal_failure("catch-annotation class struct type recorded")
873 })?;
874 Ok(Some((vtable_global, struct_idx)))
875}
876
877fn emit_catch_dispatch(
888 emitter: &mut FunctionEmitter,
889 ctx: &CodegenCtx,
890 catches: &[crate::TypedCatchClause],
891 err_stash: u32,
892) -> Result<(), crate::compiler_error::CompilerFailure> {
893 for clause in catches {
894 let Some((vtable_global, struct_idx)) = catch_filter_class(ctx, &clause.ty)? else {
895 emitter.push_scope();
896 emitter.instruction(Instruction::LocalGet(err_stash));
897 emitter.instruction(Instruction::RefAsNonNull);
898 bind_catch_local(emitter, ctx, clause)?;
899 emit_statement(emitter, ctx, clause.body)?;
900 emitter.pop_scope()?;
901 return Ok(());
903 };
904 emitter.instruction(Instruction::LocalGet(err_stash));
905 super::cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
906 emitter.emit_if(BlockType::Empty);
907 emitter.push_scope();
908 emitter.instruction(Instruction::LocalGet(err_stash));
909 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
911 bind_catch_local(emitter, ctx, clause)?;
912 emit_statement(emitter, ctx, clause.body)?;
913 emitter.pop_scope()?;
914 emitter.instruction(Instruction::Br(1));
916 emitter.emit_end();
917 }
918 let tag_idx = ctx
919 .symbols
920 .error_tag_idx()
921 .ok_or_else(|| crate::codegen::internal_failure("error tag registered"))?;
922 emitter.instruction(Instruction::LocalGet(err_stash));
923 emitter.instruction(Instruction::RefAsNonNull);
924 emitter.instruction(Instruction::Throw(tag_idx));
925 Ok(())
926}
927
928fn bind_catch_local(
931 emitter: &mut FunctionEmitter,
932 ctx: &CodegenCtx,
933 clause: &crate::TypedCatchClause,
934) -> Result<(), crate::compiler_error::CompilerFailure> {
935 let slot_ty = if clause.boxed {
936 let box_idx = ctx.symbols.box_type_idx(&clause.ty)?.ok_or_else(|| {
937 crate::codegen::internal_failure("box type registered for every boxed catch binding")
938 })?;
939 emitter.instruction(Instruction::I32Const(1));
941 emitter.instruction(Instruction::StructNew(box_idx));
942 ValType::Ref(RefType {
943 nullable: false,
944 heap_type: HeapType::Concrete(box_idx),
945 })
946 } else {
947 ctx.symbols.value_type(&clause.ty)?
948 };
949 let slot = emitter.define_local(&clause.binding, slot_ty)?;
950 emitter.instruction(Instruction::LocalSet(slot));
951 Ok(())
952}
953
954fn emit_switch(
981 emitter: &mut FunctionEmitter,
982 ctx: &CodegenCtx,
983 discriminant: crate::ExprId,
984 discriminant_ty: &Type,
985 cases: &[TypedSwitchCase],
986 default: Option<StmtId>,
987) -> Result<(), crate::compiler_error::CompilerFailure> {
988 emit_expr(emitter, ctx, discriminant)?;
991 let disc_val = switch_disc_local_type(ctx, discriminant_ty)?;
992 if matches!(discriminant_ty.peel(), Type::NumberEnum { .. }) {
993 let boxed_idx = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
994 crate::codegen::internal_failure("boxed_number type registered with intrinsics")
995 })?;
996 emitter.instruction(Instruction::StructGet {
997 struct_type_index: boxed_idx,
998 field_index: 1,
999 });
1000 }
1001 let disc_local = emitter.add_anonymous_local(disc_val)?;
1002 emitter.instruction(Instruction::LocalSet(disc_local));
1003
1004 emitter.emit_switch_open();
1005 if default.is_some() {
1008 emitter.emit_block(BlockType::Empty);
1009 }
1010 for _ in 0..cases.len() {
1011 emitter.emit_block(BlockType::Empty);
1012 }
1013
1014 for (source_idx, case) in cases.iter().enumerate() {
1016 emitter.record_span(case.span);
1017 for value in &case.values {
1018 emit_case_comparison(emitter, ctx, disc_local, discriminant_ty, value)?;
1019 emitter.instruction(Instruction::BrIf(crate::codegen::wasm_u32(source_idx)?));
1020 }
1021 }
1022 emitter.instruction(Instruction::Br(crate::codegen::wasm_u32(cases.len())?));
1024
1025 for case in cases {
1029 emitter.emit_end();
1030 emitter.push_scope();
1031 emit_statement(emitter, ctx, case.body)?;
1032 emitter.pop_scope()?;
1033 let end_label = emitter.break_label()?;
1034 emitter.instruction(Instruction::Br(end_label));
1035 }
1036
1037 if let Some(default_body) = default {
1038 emitter.emit_end();
1039 emitter.push_scope();
1040 emit_statement(emitter, ctx, default_body)?;
1041 emitter.pop_scope()?;
1042 }
1044
1045 emitter.emit_switch_close();
1046 Ok(())
1047}
1048
1049fn switch_disc_local_type(
1052 ctx: &CodegenCtx,
1053 ty: &Type,
1054) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1055 Ok(if matches!(ty.peel(), Type::NumberEnum { .. }) {
1056 ValType::F64
1057 } else {
1058 ctx.symbols.value_type(ty)?
1059 })
1060}
1061
1062fn emit_case_comparison(
1065 emitter: &mut FunctionEmitter,
1066 ctx: &CodegenCtx,
1067 disc_local: u32,
1068 disc_ty: &Type,
1069 value: &TypedSwitchValue,
1070) -> Result<(), crate::compiler_error::CompilerFailure> {
1071 let disc_val = switch_disc_local_type(ctx, disc_ty)?;
1072 if let Some(case_ty) = switch_case_primitive_type(value)
1073 && matches!(disc_val, ValType::Ref(_))
1074 && disc_val != ctx.symbols.value_type(&case_ty)?
1075 {
1076 emit_boxed_case_comparison(emitter, ctx, disc_local, &case_ty, value)?;
1077 return Ok(());
1078 }
1079 match value {
1080 TypedSwitchValue::Expr { comparison, .. } => {
1081 emit_expr(emitter, ctx, *comparison)?;
1082 }
1083 TypedSwitchValue::Undefined { .. } => {
1084 emitter.instruction(Instruction::LocalGet(disc_local));
1085 super::expr::emit_is_undefined(emitter, ctx)?;
1086 }
1087 TypedSwitchValue::Null { .. } => {
1088 emitter.instruction(Instruction::LocalGet(disc_local));
1090 emitter.instruction(Instruction::RefIsNull);
1091 }
1092 TypedSwitchValue::Number { value, .. } => {
1093 emitter.instruction(Instruction::LocalGet(disc_local));
1094 emitter.instruction(Instruction::F64Const((*value).into()));
1095 emitter.instruction(Instruction::F64Eq);
1096 }
1097 TypedSwitchValue::Boolean { value, .. } => {
1098 emitter.instruction(Instruction::LocalGet(disc_local));
1099 emitter.instruction(Instruction::I32Const(if *value { 1 } else { 0 }));
1100 emitter.instruction(Instruction::I32Eq);
1101 }
1102 TypedSwitchValue::String { value, .. } => {
1103 emit_string_compare(emitter, ctx, disc_local, disc_val, value)?;
1104 }
1105 TypedSwitchValue::Enum { value: payload, .. } => match payload {
1106 EnumVariantPayload::Number(n) => {
1107 emitter.instruction(Instruction::LocalGet(disc_local));
1108 emitter.instruction(Instruction::F64Const((*n).into()));
1109 emitter.instruction(Instruction::F64Eq);
1110 }
1111 EnumVariantPayload::String(s) => {
1112 emit_string_compare(emitter, ctx, disc_local, disc_val, s)?;
1113 }
1114 },
1115 };
1116 Ok(())
1117}
1118
1119fn emit_boxed_case_comparison(
1122 emitter: &mut FunctionEmitter,
1123 ctx: &CodegenCtx,
1124 disc_local: u32,
1125 case_ty: &Type,
1126 value: &TypedSwitchValue,
1127) -> Result<(), crate::compiler_error::CompilerFailure> {
1128 let heap_index = match case_ty {
1129 Type::Number => ctx.symbols.boxed_number_type_idx(),
1130 Type::Boolean => ctx.symbols.boxed_boolean_type_idx(),
1131 Type::String => ctx.symbols.string_type_idx(),
1132 _ => {
1133 return Err(crate::codegen::internal_failure(
1134 "switch case primitive classified above",
1135 ));
1136 }
1137 }
1138 .ok_or_else(|| {
1139 crate::codegen::internal_failure("primitive types registered before switch emission")
1140 })?;
1141 emitter.instruction(Instruction::LocalGet(disc_local));
1142 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(heap_index)));
1143 emitter.emit_if(BlockType::Result(ValType::I32));
1144 emitter.instruction(Instruction::LocalGet(disc_local));
1145 cast::emit_cast_to(emitter, ctx, case_ty)?;
1146 let unboxed = emitter.add_anonymous_local(ctx.symbols.value_type(case_ty)?)?;
1147 emitter.instruction(Instruction::LocalSet(unboxed));
1148 emit_case_comparison(emitter, ctx, unboxed, case_ty, value)?;
1149 emitter.emit_else();
1150 emitter.instruction(Instruction::I32Const(0));
1151 emitter.emit_end();
1152
1153 Ok(())
1154}
1155
1156fn switch_case_primitive_type(value: &TypedSwitchValue) -> Option<Type> {
1157 match value {
1158 TypedSwitchValue::Number { .. }
1159 | TypedSwitchValue::Enum {
1160 value: EnumVariantPayload::Number(_),
1161 ..
1162 } => Some(Type::Number),
1163 TypedSwitchValue::String { .. }
1164 | TypedSwitchValue::Enum {
1165 value: EnumVariantPayload::String(_),
1166 ..
1167 } => Some(Type::String),
1168 TypedSwitchValue::Boolean { .. } => Some(Type::Boolean),
1169 TypedSwitchValue::Null { .. }
1170 | TypedSwitchValue::Undefined { .. }
1171 | TypedSwitchValue::Expr { .. } => None,
1172 }
1173}
1174
1175fn emit_string_compare(
1179 emitter: &mut FunctionEmitter,
1180 ctx: &CodegenCtx,
1181 disc_local: u32,
1182 disc_val: ValType,
1183 literal: &str,
1184) -> Result<(), crate::compiler_error::CompilerFailure> {
1185 let string_eq_idx = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
1186 crate::codegen::internal_failure("string_eq is not imported from the prelude")
1187 })?;
1188
1189 let disc_is_nullable = matches!(
1190 disc_val,
1191 ValType::Ref(RefType {
1192 nullable: true,
1193 heap_type: HeapType::Concrete(_),
1194 })
1195 );
1196 if disc_is_nullable {
1197 emitter.instruction(Instruction::LocalGet(disc_local));
1198 emitter.instruction(Instruction::RefIsNull);
1199 emitter.emit_if(BlockType::Result(ValType::I32));
1200 emitter.instruction(Instruction::I32Const(0));
1201 emitter.emit_else();
1202 emitter.instruction(Instruction::LocalGet(disc_local));
1203 emitter.instruction(Instruction::RefAsNonNull);
1204 super::emit_const_string_by_text(emitter, ctx, literal)?;
1205 emitter.instruction(Instruction::Call(string_eq_idx));
1206 emitter.emit_end();
1207 } else {
1208 emitter.instruction(Instruction::LocalGet(disc_local));
1209 super::emit_const_string_by_text(emitter, ctx, literal)?;
1210 emitter.instruction(Instruction::Call(string_eq_idx));
1211 }
1212 Ok(())
1213}