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 if let Some(setter) = host_property_setter(ctx, &receiver_ty, name) {
466 emitter.instruction(Instruction::LocalGet(recv));
468 emitter.instruction(Instruction::RefAsNonNull);
469 emit_expr(emitter, ctx, value)?;
470 emitter.instruction(Instruction::Call(setter));
471 emitter.end_single_evaluations(mark)?;
472 return Ok(());
473 }
474 match receiver_ty.lowered() {
475 Type::ClassRef { mangled, .. } => {
477 emit_class_field_store(emitter, ctx, receiver, recv, mangled, name, value)?;
478 }
479 _ => emit_object_field_store(emitter, ctx, &receiver_ty, recv, name, value)?,
480 }
481 emitter.end_single_evaluations(mark)?;
482 Ok(())
483}
484
485fn host_property_setter(ctx: &CodegenCtx, receiver_ty: &Type, name: &Ident) -> Option<u32> {
488 let iface = crate::codegen::host_property_iface(receiver_ty)?;
489 let key = crate::mangle::extend(&iface, &crate::mangle::property_setter(&name.name));
490 ctx.symbols.func_idx(&key)
491}
492
493#[allow(clippy::too_many_arguments)]
494fn emit_class_field_store(
495 emitter: &mut FunctionEmitter,
496 ctx: &CodegenCtx,
497 receiver: ExprId,
498 recv: u32,
499 mangled: &crate::MangledName,
500 name: &Ident,
501 value: ExprId,
502) -> Result<(), crate::compiler_error::CompilerFailure> {
503 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
506 let setter = crate::codegen::classes::accessor_setter_name(&name.name);
507 emit_method_call(
508 emitter,
509 ctx,
510 receiver,
511 mangled,
512 &setter,
513 std::slice::from_ref(&value),
514 None,
515 None,
516 &Type::Void,
517 )?;
518 emitter.instruction(Instruction::Drop);
519 return Ok(());
520 };
521 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
522 crate::codegen::internal_failure("class struct type recorded in classes::emit")
523 })?;
524 let intrinsics = ctx
525 .symbols
526 .intrinsic_type_indices()
527 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
528 let value_ty = ctx
529 .ta
530 .try_expr(value)
531 .map_err(crate::codegen::arena_failure)?
532 .ty
533 .clone();
534 emit_expr(emitter, ctx, value)?;
535 cast::emit_box(emitter, ctx, &value_ty)?;
536 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
537 emitter.instruction(Instruction::LocalSet(stored));
538 emitter.instruction(Instruction::LocalGet(recv));
539 emitter.instruction(Instruction::StructGet {
540 struct_type_index: struct_idx,
541 field_index: 2,
542 });
543 emitter.instruction(Instruction::I32Const(slot as i32));
544 emitter.instruction(Instruction::LocalGet(stored));
545 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
546 let index = emitter.add_anonymous_local(ValType::I32)?;
547 emitter.instruction(Instruction::I32Const(slot as i32));
548 emitter.instruction(Instruction::LocalSet(index));
549 crate::codegen::field_names::emit_mark_present(emitter, ctx, recv, index)?;
550 Ok(())
551}
552
553fn emit_object_field_store(
554 emitter: &mut FunctionEmitter,
555 ctx: &CodegenCtx,
556 receiver_ty: &Type,
557 recv: u32,
558 name: &Ident,
559 value: ExprId,
560) -> Result<(), crate::compiler_error::CompilerFailure> {
561 let object_shape = ctx
562 .symbols
563 .intrinsic_type_indices()
564 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?
565 .object_shape;
566 let builtin = crate::codegen::may_hold_builtin_value(receiver_ty);
567 if builtin {
568 emitter.instruction(Instruction::LocalGet(recv));
569 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
570 object_shape,
571 )));
572 emitter.emit_if(BlockType::Empty);
573 }
574 emitter.instruction(Instruction::LocalGet(recv));
575 let rcv_local = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
576 emit_object_property_write(emitter, ctx, rcv_local, name, value)?;
579 if builtin {
580 emitter.emit_else();
581 emit_builtin_field_store(emitter, ctx, recv, name, value)?;
582 emitter.emit_end();
583 }
584 Ok(())
585}
586
587fn emit_builtin_field_store(
592 emitter: &mut FunctionEmitter,
593 ctx: &CodegenCtx,
594 recv: u32,
595 name: &Ident,
596 value: ExprId,
597) -> Result<(), crate::compiler_error::CompilerFailure> {
598 let key = ctx
599 .symbols
600 .field_name_string_global_idx(&name.name)
601 .ok_or_else(|| crate::codegen::internal_failure("written field name interned"))?;
602 let set_field = ctx
603 .symbols
604 .prelude_func_idx("ObjectConstructor##setField")
605 .ok_or_else(|| crate::codegen::internal_failure("dynamic write imported"))?;
606 emitter.instruction(Instruction::LocalGet(recv));
607 emitter.instruction(Instruction::GlobalGet(key));
608 emit_expr(emitter, ctx, value)?;
609 cast::emit_box(
610 emitter,
611 ctx,
612 &ctx.ta
613 .try_expr(value)
614 .map_err(crate::codegen::arena_failure)?
615 .ty,
616 )?;
617 emitter.instruction(Instruction::Call(set_field));
618 Ok(())
619}
620
621fn emit_assign_index(
622 emitter: &mut FunctionEmitter,
623 ctx: &CodegenCtx,
624 receiver: ExprId,
625 index: ExprId,
626 value: ExprId,
627 elem_ty: &Type,
628) -> Result<(), crate::compiler_error::CompilerFailure> {
629 let mark = emitter.single_evaluation_mark();
630 if ctx
631 .ta
632 .source_type(receiver)
633 .map_err(crate::codegen::arena_failure)?
634 .is_structural_object()
635 {
636 let object = emit_operand_once(emitter, ctx, receiver)?;
637 let key = emit_operand_once(emitter, ctx, index)?;
638 let stored = emit_operand_once(emitter, ctx, value)?;
639 emitter.instruction(Instruction::LocalGet(object));
640 cast::emit_box(
641 emitter,
642 ctx,
643 &ctx.ta
644 .try_expr(receiver)
645 .map_err(crate::codegen::arena_failure)?
646 .ty,
647 )?;
648 emitter.instruction(Instruction::LocalGet(key));
649 crate::codegen::cast_check::emit_operation_cast_on_stack(
650 emitter,
651 ctx,
652 &ctx.ta
653 .try_expr(index)
654 .map_err(crate::codegen::arena_failure)?
655 .ty,
656 &Type::String,
657 )?;
658 emitter.instruction(Instruction::LocalGet(stored));
659 cast::emit_box(
660 emitter,
661 ctx,
662 &ctx.ta
663 .try_expr(value)
664 .map_err(crate::codegen::arena_failure)?
665 .ty,
666 )?;
667 let Some(symbol) = ctx.require(
668 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
669 "dynamic write imported",
670 ) else {
671 return Ok(());
672 };
673 emitter.instruction(Instruction::Call(symbol));
674 } else if matches!(
675 ctx.ta
676 .source_type(receiver)
677 .map_err(crate::codegen::arena_failure)?
678 .lowered(),
679 Type::Uint8Array
680 ) {
681 emit_uint8_index_store(emitter, ctx, receiver, index, value)?;
682 } else {
683 emit_array_index_store(emitter, ctx, receiver, index, value, elem_ty)?;
684 }
685 emitter.end_single_evaluations(mark)?;
686 Ok(())
687}
688
689fn emit_uint8_index_store(
691 emitter: &mut FunctionEmitter,
692 ctx: &CodegenCtx,
693 receiver: ExprId,
694 index: ExprId,
695 value: ExprId,
696) -> Result<(), crate::compiler_error::CompilerFailure> {
697 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
698 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
699 })?;
700 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
701 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
702 })?;
703 let recv_local = emit_operand_once(emitter, ctx, receiver)?;
704 let key = emit_operand_once(emitter, ctx, index)?;
705 let original_value = emit_operand_once(emitter, ctx, value)?;
708
709 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
713 nullable: false,
714 heap_type: HeapType::Concrete(raw_uint8_idx),
715 }))?;
716 emitter.instruction(Instruction::LocalGet(recv_local));
717 crate::codegen::cast_check::emit_operation_cast_on_stack(
718 emitter,
719 ctx,
720 &ctx.ta
721 .try_expr(receiver)
722 .map_err(crate::codegen::arena_failure)?
723 .ty,
724 &Type::Uint8Array,
725 )?;
726 emitter.instruction(Instruction::LocalGet(key));
727 super::expr::emit_index_number(
728 emitter,
729 ctx,
730 &ctx.ta
731 .try_expr(index)
732 .map_err(crate::codegen::arena_failure)?
733 .ty,
734 )?;
735 let idx_f64_local = stash_index_operand(emitter)?;
736 let value_local = emitter.add_anonymous_local(ValType::I32)?;
737 emitter.instruction(Instruction::LocalGet(original_value));
738 cast::emit_coerce_to_slot(
739 emitter,
740 ctx,
741 &ctx.ta
742 .try_expr(value)
743 .map_err(crate::codegen::arena_failure)?
744 .ty,
745 &Type::Number,
746 )?;
747 emit_to_uint8(emitter)?;
748 emitter.instruction(Instruction::LocalSet(value_local));
749 emitter.instruction(Instruction::StructGet {
750 struct_type_index: uint8_idx,
751 field_index: 1,
752 });
753 emitter.instruction(Instruction::LocalSet(raw_local));
754 let idx_local = emit_checked_index(emitter, ctx, raw_local, idx_f64_local)?;
755 emitter.instruction(Instruction::LocalGet(raw_local));
756 emitter.instruction(Instruction::LocalGet(idx_local));
757 emitter.instruction(Instruction::LocalGet(value_local));
758 emitter.instruction(Instruction::ArraySet(raw_uint8_idx));
759 Ok(())
760}
761
762fn emit_to_uint8(
766 emitter: &mut FunctionEmitter,
767) -> Result<(), crate::compiler_error::CompilerFailure> {
768 let truncated = emitter.add_anonymous_local(ValType::F64)?;
769 emitter.instruction(Instruction::F64Trunc);
770 emitter.instruction(Instruction::LocalTee(truncated));
771 emitter.instruction(Instruction::LocalGet(truncated));
772 emitter.instruction(Instruction::F64Const(256.0.into()));
773 emitter.instruction(Instruction::F64Div);
774 emitter.instruction(Instruction::F64Floor);
775 emitter.instruction(Instruction::F64Const(256.0.into()));
776 emitter.instruction(Instruction::F64Mul);
777 emitter.instruction(Instruction::F64Sub);
778 emitter.instruction(Instruction::I32TruncSatF64U);
779 Ok(())
780}
781
782fn emit_array_index_store(
784 emitter: &mut FunctionEmitter,
785 ctx: &CodegenCtx,
786 receiver: ExprId,
787 index: ExprId,
788 value: ExprId,
789 _elem_ty: &Type,
790) -> Result<(), crate::compiler_error::CompilerFailure> {
791 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
792 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
793 })?;
794 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
795 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
796 })?;
797 let object_idx = ctx
798 .symbols
799 .intrinsic_type_indices()
800 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
801 .object;
802 let recv_local = emit_operand_once(emitter, ctx, receiver)?;
803 let key = emit_operand_once(emitter, ctx, index)?;
804 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
807 nullable: true,
808 heap_type: HeapType::Concrete(object_idx),
809 }))?;
810 let value_ty = ctx
811 .ta
812 .try_expr(value)
813 .map_err(crate::codegen::arena_failure)?
814 .ty
815 .clone();
816 emit_expr(emitter, ctx, value)?;
817 cast::emit_box(emitter, ctx, &value_ty)?;
819 emitter.instruction(Instruction::LocalSet(value_local));
820
821 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
826 nullable: false,
827 heap_type: HeapType::Concrete(raw_array_idx),
828 }))?;
829 emitter.instruction(Instruction::LocalGet(recv_local));
830 crate::codegen::cast_check::emit_operation_cast_on_stack(
831 emitter,
832 ctx,
833 &ctx.ta
834 .try_expr(receiver)
835 .map_err(crate::codegen::arena_failure)?
836 .ty,
837 &Type::Array(Box::new(Type::Unknown)),
838 )?;
839 emitter.instruction(Instruction::LocalGet(key));
840 super::expr::emit_index_number(
841 emitter,
842 ctx,
843 &ctx.ta
844 .try_expr(index)
845 .map_err(crate::codegen::arena_failure)?
846 .ty,
847 )?;
848 let idx_f64_local = stash_index_operand(emitter)?;
849 let length = stash_array_length(emitter, array_idx)?;
850 emitter.instruction(Instruction::StructGet {
851 struct_type_index: array_idx,
852 field_index: 1,
853 });
854 emitter.instruction(Instruction::LocalSet(raw_arr_local));
855 let idx_local = emit_checked_index_with_length(emitter, ctx, length, idx_f64_local)?;
856 emitter.instruction(Instruction::LocalGet(raw_arr_local));
857 emitter.instruction(Instruction::LocalGet(idx_local));
858 emitter.instruction(Instruction::LocalGet(value_local));
859 emitter.instruction(Instruction::ArraySet(raw_array_idx));
860 Ok(())
861}
862
863pub(super) fn emit_try(
866 emitter: &mut FunctionEmitter,
867 ctx: &CodegenCtx,
868 body: StmtId,
869 catches: &[crate::TypedCatchClause],
870 finally: Option<StmtId>,
871) -> Result<(), crate::compiler_error::CompilerFailure> {
872 if let Some(finally) = finally {
873 super::finally::emit_try_finally(emitter, ctx, body, catches, finally)?;
874 return Ok(());
875 }
876 if catches.is_empty() {
877 emit_statement(emitter, ctx, body)?;
878 return Ok(());
879 }
880 let error_idx = ctx
881 .symbols
882 .intrinsic_type_indices()
883 .ok_or_else(|| crate::codegen::internal_failure("error intrinsic registered"))?
884 .error;
885 let tag_idx = ctx
886 .symbols
887 .error_tag_idx()
888 .ok_or_else(|| crate::codegen::internal_failure("error tag registered"))?;
889 emitter.emit_block(BlockType::Empty);
890 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
891 nullable: false,
892 heap_type: HeapType::Concrete(error_idx),
893 })));
894 emitter.instruction(Instruction::TryTable(
895 BlockType::Empty,
896 std::borrow::Cow::Owned(vec![wasm_encoder::Catch::One {
897 tag: tag_idx,
898 label: 0,
899 }]),
900 ));
901 emitter.bump_block_depth();
902 emit_statement(emitter, ctx, body)?;
903 emitter.emit_end();
904 emitter.instruction(Instruction::Br(1));
905 emitter.emit_end();
906 let err_stash = emitter.add_anonymous_local(ValType::Ref(RefType {
907 nullable: true,
908 heap_type: HeapType::Concrete(error_idx),
909 }))?;
910 emitter.instruction(Instruction::LocalSet(err_stash));
911 emit_catch_dispatch(emitter, ctx, catches, err_stash)?;
912 emitter.emit_end();
913 Ok(())
914}
915
916fn catch_filter_class(
920 ctx: &CodegenCtx,
921 clause_ty: &Type,
922) -> Result<Option<(u32, u32)>, crate::compiler_error::CompilerFailure> {
923 let Type::ClassRef { mangled, .. } = clause_ty.lowered() else {
924 return Ok(None);
925 };
926 if *mangled == crate::mangle::prelude("Error") {
927 return Ok(None);
928 }
929 let vtable_global = ctx
930 .symbols
931 .class_vtable_global_idx(mangled)
932 .ok_or_else(|| {
933 crate::codegen::internal_failure("catch-annotation class vtable global recorded")
934 })?;
935 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
936 crate::codegen::internal_failure("catch-annotation class struct type recorded")
937 })?;
938 Ok(Some((vtable_global, struct_idx)))
939}
940
941fn emit_catch_dispatch(
952 emitter: &mut FunctionEmitter,
953 ctx: &CodegenCtx,
954 catches: &[crate::TypedCatchClause],
955 err_stash: u32,
956) -> Result<(), crate::compiler_error::CompilerFailure> {
957 for clause in catches {
958 let Some((vtable_global, struct_idx)) = catch_filter_class(ctx, &clause.ty)? else {
959 emitter.push_scope();
960 emitter.instruction(Instruction::LocalGet(err_stash));
961 emitter.instruction(Instruction::RefAsNonNull);
962 bind_catch_local(emitter, ctx, clause)?;
963 emit_statement(emitter, ctx, clause.body)?;
964 emitter.pop_scope()?;
965 return Ok(());
967 };
968 emitter.instruction(Instruction::LocalGet(err_stash));
969 super::cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
970 emitter.emit_if(BlockType::Empty);
971 emitter.push_scope();
972 emitter.instruction(Instruction::LocalGet(err_stash));
973 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
975 bind_catch_local(emitter, ctx, clause)?;
976 emit_statement(emitter, ctx, clause.body)?;
977 emitter.pop_scope()?;
978 emitter.instruction(Instruction::Br(1));
980 emitter.emit_end();
981 }
982 let tag_idx = ctx
983 .symbols
984 .error_tag_idx()
985 .ok_or_else(|| crate::codegen::internal_failure("error tag registered"))?;
986 emitter.instruction(Instruction::LocalGet(err_stash));
987 emitter.instruction(Instruction::RefAsNonNull);
988 emitter.instruction(Instruction::Throw(tag_idx));
989 Ok(())
990}
991
992fn bind_catch_local(
995 emitter: &mut FunctionEmitter,
996 ctx: &CodegenCtx,
997 clause: &crate::TypedCatchClause,
998) -> Result<(), crate::compiler_error::CompilerFailure> {
999 let slot_ty = if clause.boxed {
1000 let box_idx = ctx.symbols.box_type_idx(&clause.ty)?.ok_or_else(|| {
1001 crate::codegen::internal_failure("box type registered for every boxed catch binding")
1002 })?;
1003 emitter.instruction(Instruction::I32Const(1));
1005 emitter.instruction(Instruction::StructNew(box_idx));
1006 ValType::Ref(RefType {
1007 nullable: false,
1008 heap_type: HeapType::Concrete(box_idx),
1009 })
1010 } else {
1011 ctx.symbols.value_type(&clause.ty)?
1012 };
1013 let slot = emitter.define_local(&clause.binding, slot_ty)?;
1014 emitter.instruction(Instruction::LocalSet(slot));
1015 Ok(())
1016}
1017
1018fn emit_switch(
1045 emitter: &mut FunctionEmitter,
1046 ctx: &CodegenCtx,
1047 discriminant: crate::ExprId,
1048 discriminant_ty: &Type,
1049 cases: &[TypedSwitchCase],
1050 default: Option<StmtId>,
1051) -> Result<(), crate::compiler_error::CompilerFailure> {
1052 emit_expr(emitter, ctx, discriminant)?;
1055 let disc_val = switch_disc_local_type(ctx, discriminant_ty)?;
1056 if matches!(discriminant_ty.lowered(), Type::NumberEnum { .. }) {
1057 let boxed_idx = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
1058 crate::codegen::internal_failure("boxed_number type registered with intrinsics")
1059 })?;
1060 emitter.instruction(Instruction::StructGet {
1061 struct_type_index: boxed_idx,
1062 field_index: 1,
1063 });
1064 }
1065 let disc_local = emitter.add_anonymous_local(disc_val)?;
1066 emitter.instruction(Instruction::LocalSet(disc_local));
1067
1068 emitter.emit_switch_open();
1069 if default.is_some() {
1072 emitter.emit_block(BlockType::Empty);
1073 }
1074 for _ in 0..cases.len() {
1075 emitter.emit_block(BlockType::Empty);
1076 }
1077
1078 for (source_idx, case) in cases.iter().enumerate() {
1080 emitter.record_span(case.span);
1081 for value in &case.values {
1082 emit_case_comparison(emitter, ctx, disc_local, discriminant_ty, value)?;
1083 emitter.instruction(Instruction::BrIf(crate::codegen::wasm_u32(source_idx)?));
1084 }
1085 }
1086 emitter.instruction(Instruction::Br(crate::codegen::wasm_u32(cases.len())?));
1088
1089 for case in cases {
1093 emitter.emit_end();
1094 emitter.push_scope();
1095 emit_statement(emitter, ctx, case.body)?;
1096 emitter.pop_scope()?;
1097 let end_label = emitter.break_label()?;
1098 emitter.instruction(Instruction::Br(end_label));
1099 }
1100
1101 if let Some(default_body) = default {
1102 emitter.emit_end();
1103 emitter.push_scope();
1104 emit_statement(emitter, ctx, default_body)?;
1105 emitter.pop_scope()?;
1106 }
1108
1109 emitter.emit_switch_close();
1110 Ok(())
1111}
1112
1113fn switch_disc_local_type(
1116 ctx: &CodegenCtx,
1117 ty: &Type,
1118) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1119 Ok(if matches!(ty.lowered(), Type::NumberEnum { .. }) {
1120 ValType::F64
1121 } else {
1122 ctx.symbols.value_type(ty)?
1123 })
1124}
1125
1126fn emit_case_comparison(
1129 emitter: &mut FunctionEmitter,
1130 ctx: &CodegenCtx,
1131 disc_local: u32,
1132 disc_ty: &Type,
1133 value: &TypedSwitchValue,
1134) -> Result<(), crate::compiler_error::CompilerFailure> {
1135 let disc_val = switch_disc_local_type(ctx, disc_ty)?;
1136 if let Some(case_ty) = switch_case_primitive_type(value)
1137 && matches!(disc_val, ValType::Ref(_))
1138 && disc_val != ctx.symbols.value_type(&case_ty)?
1139 {
1140 emit_boxed_case_comparison(emitter, ctx, disc_local, &case_ty, value)?;
1141 return Ok(());
1142 }
1143 match value {
1144 TypedSwitchValue::Expr { comparison, .. } => {
1145 emit_expr(emitter, ctx, *comparison)?;
1146 }
1147 TypedSwitchValue::Undefined { .. } => {
1148 emitter.instruction(Instruction::LocalGet(disc_local));
1149 super::expr::emit_is_undefined(emitter, ctx)?;
1150 }
1151 TypedSwitchValue::Null { .. } => {
1152 emitter.instruction(Instruction::LocalGet(disc_local));
1154 emitter.instruction(Instruction::RefIsNull);
1155 }
1156 TypedSwitchValue::Number { value, .. } => {
1157 emitter.instruction(Instruction::LocalGet(disc_local));
1158 emitter.instruction(Instruction::F64Const((*value).into()));
1159 emitter.instruction(Instruction::F64Eq);
1160 }
1161 TypedSwitchValue::Boolean { value, .. } => {
1162 emitter.instruction(Instruction::LocalGet(disc_local));
1163 emitter.instruction(Instruction::I32Const(if *value { 1 } else { 0 }));
1164 emitter.instruction(Instruction::I32Eq);
1165 }
1166 TypedSwitchValue::String { value, .. } => {
1167 emit_string_compare(emitter, ctx, disc_local, disc_val, value)?;
1168 }
1169 TypedSwitchValue::Enum { value: payload, .. } => match payload {
1170 EnumVariantPayload::Number(n) => {
1171 emitter.instruction(Instruction::LocalGet(disc_local));
1172 emitter.instruction(Instruction::F64Const((*n).into()));
1173 emitter.instruction(Instruction::F64Eq);
1174 }
1175 EnumVariantPayload::String(s) => {
1176 emit_string_compare(emitter, ctx, disc_local, disc_val, s)?;
1177 }
1178 },
1179 };
1180 Ok(())
1181}
1182
1183fn emit_boxed_case_comparison(
1186 emitter: &mut FunctionEmitter,
1187 ctx: &CodegenCtx,
1188 disc_local: u32,
1189 case_ty: &Type,
1190 value: &TypedSwitchValue,
1191) -> Result<(), crate::compiler_error::CompilerFailure> {
1192 let heap_index = match case_ty {
1193 Type::Number => ctx.symbols.boxed_number_type_idx(),
1194 Type::Boolean => ctx.symbols.boxed_boolean_type_idx(),
1195 Type::String => ctx.symbols.string_type_idx(),
1196 _ => {
1197 return Err(crate::codegen::internal_failure(
1198 "switch case primitive classified above",
1199 ));
1200 }
1201 }
1202 .ok_or_else(|| {
1203 crate::codegen::internal_failure("primitive types registered before switch emission")
1204 })?;
1205 emitter.instruction(Instruction::LocalGet(disc_local));
1206 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(heap_index)));
1207 emitter.emit_if(BlockType::Result(ValType::I32));
1208 emitter.instruction(Instruction::LocalGet(disc_local));
1209 cast::emit_cast_to(emitter, ctx, case_ty)?;
1210 let unboxed = emitter.add_anonymous_local(ctx.symbols.value_type(case_ty)?)?;
1211 emitter.instruction(Instruction::LocalSet(unboxed));
1212 emit_case_comparison(emitter, ctx, unboxed, case_ty, value)?;
1213 emitter.emit_else();
1214 emitter.instruction(Instruction::I32Const(0));
1215 emitter.emit_end();
1216
1217 Ok(())
1218}
1219
1220fn switch_case_primitive_type(value: &TypedSwitchValue) -> Option<Type> {
1221 match value {
1222 TypedSwitchValue::Number { .. }
1223 | TypedSwitchValue::Enum {
1224 value: EnumVariantPayload::Number(_),
1225 ..
1226 } => Some(Type::Number),
1227 TypedSwitchValue::String { .. }
1228 | TypedSwitchValue::Enum {
1229 value: EnumVariantPayload::String(_),
1230 ..
1231 } => Some(Type::String),
1232 TypedSwitchValue::Boolean { .. } => Some(Type::Boolean),
1233 TypedSwitchValue::Null { .. }
1234 | TypedSwitchValue::Undefined { .. }
1235 | TypedSwitchValue::Expr { .. } => None,
1236 }
1237}
1238
1239fn emit_string_compare(
1243 emitter: &mut FunctionEmitter,
1244 ctx: &CodegenCtx,
1245 disc_local: u32,
1246 disc_val: ValType,
1247 literal: &str,
1248) -> Result<(), crate::compiler_error::CompilerFailure> {
1249 let string_eq_idx = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
1250 crate::codegen::internal_failure("string_eq is not imported from the prelude")
1251 })?;
1252
1253 let disc_is_nullable = matches!(
1254 disc_val,
1255 ValType::Ref(RefType {
1256 nullable: true,
1257 heap_type: HeapType::Concrete(_),
1258 })
1259 );
1260 if disc_is_nullable {
1261 emitter.instruction(Instruction::LocalGet(disc_local));
1262 emitter.instruction(Instruction::RefIsNull);
1263 emitter.emit_if(BlockType::Result(ValType::I32));
1264 emitter.instruction(Instruction::I32Const(0));
1265 emitter.emit_else();
1266 emitter.instruction(Instruction::LocalGet(disc_local));
1267 emitter.instruction(Instruction::RefAsNonNull);
1268 super::emit_const_string_by_text(emitter, ctx, literal)?;
1269 emitter.instruction(Instruction::Call(string_eq_idx));
1270 emitter.emit_end();
1271 } else {
1272 emitter.instruction(Instruction::LocalGet(disc_local));
1273 super::emit_const_string_by_text(emitter, ctx, literal)?;
1274 emitter.instruction(Instruction::Call(string_eq_idx));
1275 }
1276 Ok(())
1277}