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