1use crate::codegen::CodegenCtx;
2use crate::codegen::bounds::receiver_name;
3use crate::codegen::bounds::{
4 emit_checked_index, emit_checked_index_with_length, stash_array_length, stash_index_operand,
5};
6use crate::codegen::function_emitter::FunctionEmitter;
7use crate::codegen::init_guard;
8use crate::runtime::prelude::index::IndexAccess;
9use crate::{
10 EnumVariantPayload, ExprId, Ident, MangledName, StmtId, Type, TypedStmtKind, TypedSwitchCase,
11 TypedSwitchValue,
12};
13use wasm_encoder::{BlockType, HeapType, Instruction, RefType, ValType};
14
15use super::cast;
16use super::expr::{
17 emit_expr, emit_method_call, emit_object_property_write, stash_receiver_as_object_shape,
18};
19
20fn branches(kind: &TypedStmtKind) -> bool {
29 matches!(
30 kind,
31 TypedStmtKind::If { .. }
32 | TypedStmtKind::While { .. }
33 | TypedStmtKind::DoWhile { .. }
34 | TypedStmtKind::For { .. }
35 | TypedStmtKind::ForOf { .. }
36 | TypedStmtKind::Switch { .. }
37 | TypedStmtKind::Try { .. }
38 )
39}
40
41pub fn emit_statement(
42 emitter: &mut FunctionEmitter,
43 ctx: &CodegenCtx,
44 id: StmtId,
45) -> Result<(), crate::compiler_error::CompilerFailure> {
46 let stmt = ctx.ta.try_stmt(id).map_err(crate::codegen::arena_failure)?;
47 emitter.record_span(stmt.span);
48 if branches(&stmt.kind) {
52 emitter.clear_all_narrow_shadows();
53 }
54 match &stmt.kind {
55 TypedStmtKind::Block(stmts) => {
56 emitter.push_scope();
59 for &child in stmts {
60 emit_statement(emitter, ctx, child)?;
61 }
62 emitter.pop_scope()?;
63 }
64 TypedStmtKind::Return(value) => {
65 if let Some(expr_id) = value {
66 emit_expr(emitter, ctx, *expr_id)?;
67 cast::emit_coerce_to_return_slot(
68 emitter,
69 ctx,
70 &ctx.ta
71 .try_expr(*expr_id)
72 .map_err(crate::codegen::arena_failure)?
73 .ty,
74 )?;
75 } else if emitter.wasm_result_type(ctx)?.is_some() {
76 super::expr::emit_undefined(emitter, ctx)?;
77 cast::emit_coerce_to_return_slot(emitter, ctx, &Type::Undefined)?;
78 }
79 super::finally::emit_transfer(emitter, super::finally::Transfer::Return)?;
80 }
81 TypedStmtKind::Expr(expr_id) => {
82 emit_expr(emitter, ctx, *expr_id)?;
83 emitter.instruction(Instruction::Drop);
84 }
85 TypedStmtKind::Let {
87 name,
88 ty,
89 value,
90 boxed,
91 ..
92 } => {
93 let present_flag = present_flag_for(emitter, ctx, name)?;
94 super::expr::emit_value_with_present_flag(emitter, ctx, *value, present_flag)?;
95 let value_ty = ctx
96 .ta
97 .try_expr(*value)
98 .map_err(crate::codegen::arena_failure)?
99 .ty
100 .clone();
101 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, ty)?;
102 if *boxed {
103 let box_idx = ctx.symbols.box_type_idx(ty)?.ok_or_else(|| {
104 crate::codegen::internal_failure("box type registered for every boxed Let")
105 })?;
106 if ctx.ta.parameter_initializations.contains(&id) {
107 let slot = emitter
108 .parameter_binding_cells
109 .get(name)
110 .copied()
111 .ok_or_else(|| {
112 crate::codegen::internal_failure(
113 "parameter pattern cell was not reserved",
114 )
115 })?;
116 let value_slot = emitter.add_anonymous_local(ctx.symbols.value_type(ty)?)?;
117 emitter.instruction(Instruction::LocalSet(value_slot));
118 emitter.instruction(Instruction::LocalGet(slot));
119 emitter.instruction(Instruction::LocalGet(value_slot));
120 emitter.instruction(Instruction::StructSet {
121 struct_type_index: box_idx,
122 field_index: 0,
123 });
124 super::binding_cells::mark_initialized(emitter, slot, box_idx);
125 note_maybe_missing_slot(emitter, ctx, name, slot, ty, present_flag);
126 return Ok(());
127 }
128 emitter.instruction(Instruction::I32Const(1));
129 emitter.instruction(Instruction::StructNew(box_idx));
130 let box_val = ValType::Ref(RefType {
131 nullable: false,
132 heap_type: HeapType::Concrete(box_idx),
133 });
134 let slot = emitter.define_local(name, box_val)?;
135 emitter.instruction(Instruction::LocalSet(slot));
136 note_maybe_missing_slot(emitter, ctx, name, slot, ty, present_flag);
137 } else {
138 let slot = emitter.define_local(name, ctx.symbols.value_type(ty)?)?;
139 emitter.instruction(Instruction::LocalSet(slot));
140 note_maybe_missing_slot(emitter, ctx, name, slot, ty, present_flag);
141 }
142 }
143 TypedStmtKind::ReboxLocal { ident, ty } => {
144 let slot = emitter.write_slot(&ident.name)?;
145 let box_idx = ctx.symbols.box_type_idx(ty)?.ok_or_else(|| {
146 crate::codegen::internal_failure("box type registered for every boxed Let")
147 })?;
148 super::binding_cells::read(emitter, ctx, slot, box_idx)?;
149 emitter.instruction(Instruction::I32Const(1));
150 emitter.instruction(Instruction::StructNew(box_idx));
151 emitter.instruction(Instruction::LocalSet(slot));
152 }
153 TypedStmtKind::Const {
154 name, ty, value, ..
155 } => {
156 let present_flag = present_flag_for(emitter, ctx, name)?;
158 super::expr::emit_value_with_present_flag(emitter, ctx, *value, present_flag)?;
159 let value_ty = ctx
160 .ta
161 .try_expr(*value)
162 .map_err(crate::codegen::arena_failure)?
163 .ty
164 .clone();
165 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, ty)?;
166 let slot = emitter.define_local(name, ctx.symbols.value_type(ty)?)?;
167 emitter.instruction(Instruction::LocalSet(slot));
168 note_maybe_missing_slot(emitter, ctx, name, slot, ty, present_flag);
169 }
170 TypedStmtKind::AssignLocal {
171 ident,
172 target_ty,
173 value,
174 boxed,
175 narrowed_shadow_ty,
176 } => {
177 let slot = emitter.write_slot(&ident.name)?;
178 if *boxed {
179 let box_idx = ctx.symbols.box_type_idx(target_ty)?.ok_or_else(|| {
184 crate::codegen::internal_failure(
185 "box type registered for every boxed AssignLocal",
186 )
187 })?;
188 let value_ty = ctx
189 .ta
190 .try_expr(*value)
191 .map_err(crate::codegen::arena_failure)?
192 .ty
193 .clone();
194 emitter.instruction(Instruction::LocalGet(slot));
195 let present_flag = emitter.present_flag(slot);
196 super::expr::emit_value_with_present_flag(emitter, ctx, *value, present_flag)?;
197 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, target_ty)?;
198 let initialization = ctx.ta.parameter_initializations.contains(&id);
199 if !initialization {
200 super::binding_cells::check_initialized(emitter, ctx, slot, box_idx)?;
201 }
202 emitter.instruction(Instruction::StructSet {
203 struct_type_index: box_idx,
204 field_index: 0,
205 });
206 if initialization {
207 super::binding_cells::mark_initialized(emitter, slot, box_idx);
208 }
209 } else {
210 let present_flag = emitter.present_flag(slot);
211 super::expr::emit_value_with_present_flag(emitter, ctx, *value, present_flag)?;
212 let value_ty = ctx
213 .ta
214 .try_expr(*value)
215 .map_err(crate::codegen::arena_failure)?
216 .ty
217 .clone();
218 let stored_ty = match narrowed_shadow_ty {
226 Some(narrowed_ty) => {
227 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, narrowed_ty)?;
228 let narrowed_val = ctx.symbols.value_type(narrowed_ty)?;
229 let shadow_idx = emitter.add_anonymous_local(narrowed_val)?;
230 emitter.instruction(Instruction::LocalTee(shadow_idx));
231 emitter.install_narrow_shadow(&ident.name, shadow_idx, narrowed_val)?;
232 narrowed_ty
233 }
234 None => &value_ty,
235 };
236 cast::emit_coerce_to_slot(emitter, ctx, stored_ty, target_ty)?;
237 emitter.instruction(Instruction::LocalSet(slot));
238 }
239 }
240 TypedStmtKind::AssignGlobal {
241 mangled,
242 target_ty,
243 value,
244 ..
245 } => {
246 emit_assign_global(emitter, ctx, id, mangled, target_ty, *value)?;
247 }
248 TypedStmtKind::AssignField {
249 receiver,
250 name,
251 value,
252 } => {
253 emit_assign_field(emitter, ctx, *receiver, name, *value)?;
254 }
255 TypedStmtKind::AssignIndex {
256 receiver,
257 index,
258 value,
259 elem_ty,
260 } => {
261 emit_assign_index(emitter, ctx, *receiver, *index, *value, elem_ty)?;
262 }
263 TypedStmtKind::If {
264 condition,
265 then_block,
266 else_block,
267 } => {
268 let cond_ty = ctx
269 .ta
270 .try_expr(*condition)
271 .map_err(crate::codegen::arena_failure)?
272 .ty
273 .clone();
274 emit_expr(emitter, ctx, *condition)?;
275 cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
276 emitter.emit_if(BlockType::Empty);
277 emit_statement(emitter, ctx, *then_block)?;
278 if let Some(else_id) = else_block {
279 emitter.emit_else();
280 emit_statement(emitter, ctx, *else_id)?;
281 }
282 emitter.emit_end();
283 }
284 TypedStmtKind::While { condition, body } => {
285 emitter.emit_while_open();
288 let cond_ty = ctx
289 .ta
290 .try_expr(*condition)
291 .map_err(crate::codegen::arena_failure)?
292 .ty
293 .clone();
294 emit_expr(emitter, ctx, *condition)?;
295 cast::emit_condition_to_i32(emitter, ctx, &cond_ty)?;
296 emitter.instruction(Instruction::I32Eqz);
297 let break_label = emitter.break_label()?;
298 emitter.instruction(Instruction::BrIf(break_label));
299 emit_statement(emitter, ctx, *body)?;
300 let continue_label = emitter.continue_label()?;
301 emitter.instruction(Instruction::Br(continue_label));
302 emitter.emit_while_close();
303 }
304 TypedStmtKind::Break => {
305 let transfer = super::finally::Transfer::Branch {
306 depth: emitter.wasm_block_depth - emitter.break_label()? - 1,
307 finally_floor: emitter.break_finally_floor(),
308 };
309 super::finally::emit_transfer(emitter, transfer)?;
310 }
311 TypedStmtKind::Continue => {
312 let transfer = super::finally::Transfer::Branch {
313 depth: emitter.wasm_block_depth - emitter.continue_label()? - 1,
314 finally_floor: emitter.continue_finally_floor(),
315 };
316 super::finally::emit_transfer(emitter, transfer)?;
317 }
318 TypedStmtKind::For { .. } | TypedStmtKind::ForOf { .. } | TypedStmtKind::DoWhile { .. } => {
319 return Err(crate::codegen::internal_failure(
320 "for / for-of / do-while must be lowered by the desugar pass before codegen",
321 ));
322 }
323 TypedStmtKind::Switch {
324 discriminant,
325 discriminant_ty,
326 cases,
327 default,
328 } => {
329 emit_switch(
330 emitter,
331 ctx,
332 *discriminant,
333 discriminant_ty,
334 cases,
335 default.as_ref().copied(),
336 )?;
337 }
338 TypedStmtKind::NarrowRegion {
339 path,
340 source,
341 binding,
342 cast_info,
343 body,
344 } => {
345 emitter.push_scope();
346 if path.chain.is_empty() {
347 if !super::expr::reads_maybe_missing_local(emitter, ctx, *source)? {
349 let shadow_val = ctx.symbols.value_type(&cast_info.to_ty)?;
350 let shadow = emitter.add_anonymous_local(shadow_val)?;
351 emit_expr(emitter, ctx, *source)?;
352 cast::emit_narrowing_cast(emitter, ctx, cast_info)?;
353 emitter.instruction(Instruction::LocalSet(shadow));
354 emitter.install_narrow_shadow(&binding.name, shadow, shadow_val)?;
355 }
356 } else {
357 emitter.register_narrow_source(&binding.name, *source)?;
361 }
362 emit_statement(emitter, ctx, *body)?;
363 emitter.pop_scope()?;
364 }
365 TypedStmtKind::Throw { value } => {
366 emit_expr(emitter, ctx, *value)?;
368 crate::codegen::throw::emit_error_throw(emitter, ctx);
369 }
370 TypedStmtKind::Try {
371 body,
372 catches,
373 finally,
374 } => {
375 emit_try(emitter, ctx, *body, catches, *finally)?;
376 }
377 }
378 let _: () = if branches(&stmt.kind) {
379 emitter.clear_all_narrow_shadows();
380 };
381 Ok(())
382}
383
384fn emit_operand_once(
389 emitter: &mut FunctionEmitter,
390 ctx: &CodegenCtx,
391 expr: ExprId,
392) -> Result<u32, crate::compiler_error::CompilerFailure> {
393 let slot = emitter.add_anonymous_local(
394 ctx.symbols.value_type(
395 &ctx.ta
396 .try_expr(expr)
397 .map_err(crate::codegen::arena_failure)?
398 .ty,
399 )?,
400 )?;
401 emit_expr(emitter, ctx, expr)?;
402 emitter.instruction(Instruction::LocalSet(slot));
403 emitter.record_single_evaluation(expr, slot)?;
404 Ok(slot)
405}
406
407fn emit_assign_global(
408 emitter: &mut FunctionEmitter,
409 ctx: &CodegenCtx,
410 stmt: StmtId,
411 mangled: &MangledName,
412 target_ty: &Type,
413 value: ExprId,
414) -> Result<(), crate::compiler_error::CompilerFailure> {
415 emit_expr(emitter, ctx, value)?;
416 let value_ty = ctx
417 .ta
418 .try_expr(value)
419 .map_err(crate::codegen::arena_failure)?
420 .ty
421 .clone();
422 cast::emit_coerce_to_slot(emitter, ctx, &value_ty, target_ty)?;
423 let idx = ctx
424 .symbols
425 .global_idx(mangled)
426 .ok_or_else(|| crate::codegen::internal_failure("Inferer guarantees the binding exists"))?;
427 let declares = init_guard::is_declaration(ctx, mangled, stmt);
429 if !declares {
430 init_guard::emit_check(emitter, ctx, mangled);
431 }
432 emitter.instruction(Instruction::GlobalSet(idx));
433 if declares {
434 init_guard::emit_mark_initialized(emitter, ctx, mangled);
435 }
436 Ok(())
437}
438
439fn emit_assign_field(
440 emitter: &mut FunctionEmitter,
441 ctx: &CodegenCtx,
442 receiver: ExprId,
443 name: &Ident,
444 value: ExprId,
445) -> Result<(), crate::compiler_error::CompilerFailure> {
446 let mark = emitter.single_evaluation_mark();
447 let receiver_ty = ctx
448 .ta
449 .source_type(receiver)
450 .map_err(crate::codegen::arena_failure)?
451 .clone();
452 let recv = emit_operand_once(emitter, ctx, receiver)?;
457 emit_operand_once(emitter, ctx, value)?;
458 emitter.instruction(Instruction::LocalGet(recv));
459 if ctx
460 .ta
461 .try_expr(receiver)
462 .map_err(crate::codegen::arena_failure)?
463 .ty
464 != receiver_ty
465 {
466 crate::codegen::cast_check::emit_operation_cast_on_stack(
467 emitter,
468 ctx,
469 &ctx.ta
470 .try_expr(receiver)
471 .map_err(crate::codegen::arena_failure)?
472 .ty,
473 &receiver_ty,
474 )?;
475 }
476 let recv = emitter.add_anonymous_local(ctx.symbols.value_type(&receiver_ty)?)?;
477 emitter.instruction(Instruction::LocalSet(recv));
478 if let Some(setter) = host_property_setter(ctx, &receiver_ty, name) {
479 emitter.instruction(Instruction::LocalGet(recv));
481 emitter.instruction(Instruction::RefAsNonNull);
482 emit_expr(emitter, ctx, value)?;
483 emitter.instruction(Instruction::Call(setter));
484 emitter.end_single_evaluations(mark)?;
485 return Ok(());
486 }
487 match receiver_ty.lowered() {
488 Type::ClassRef { mangled, .. } => {
490 emit_class_field_store(emitter, ctx, receiver, recv, mangled, name, value)?;
491 }
492 _ => emit_object_field_store(emitter, ctx, &receiver_ty, recv, name, value)?,
493 }
494 emitter.end_single_evaluations(mark)?;
495 Ok(())
496}
497
498fn host_property_setter(ctx: &CodegenCtx, receiver_ty: &Type, name: &Ident) -> Option<u32> {
501 let iface = crate::codegen::host_property_iface(receiver_ty)?;
502 let key = crate::mangle::extend(&iface, &crate::mangle::property_setter(&name.name));
503 ctx.symbols.func_idx(&key)
504}
505
506#[allow(clippy::too_many_arguments)]
507fn emit_class_field_store(
508 emitter: &mut FunctionEmitter,
509 ctx: &CodegenCtx,
510 receiver: ExprId,
511 recv: u32,
512 mangled: &crate::MangledName,
513 name: &Ident,
514 value: ExprId,
515) -> Result<(), crate::compiler_error::CompilerFailure> {
516 let Some(slot) = ctx.symbols.class_field_slot(mangled, &name.name) else {
519 let setter = crate::codegen::classes::accessor_setter_name(&name.name);
520 emit_method_call(
521 emitter,
522 ctx,
523 receiver,
524 mangled,
525 &setter,
526 std::slice::from_ref(&value),
527 None,
528 &[],
529 None,
530 &Type::Void,
531 )?;
532 emitter.instruction(Instruction::Drop);
533 return Ok(());
534 };
535 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
536 crate::codegen::internal_failure("class struct type recorded in classes::emit")
537 })?;
538 let intrinsics = ctx
539 .symbols
540 .intrinsic_type_indices()
541 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
542 let value_ty = ctx
543 .ta
544 .try_expr(value)
545 .map_err(crate::codegen::arena_failure)?
546 .ty
547 .clone();
548 emit_expr(emitter, ctx, value)?;
549 cast::emit_box(emitter, ctx, &value_ty)?;
550 let stored = emitter.add_anonymous_local(ctx.symbols.value_type(&Type::Unknown)?)?;
551 emitter.instruction(Instruction::LocalSet(stored));
552 emitter.instruction(Instruction::LocalGet(recv));
553 emitter.instruction(Instruction::StructGet {
554 struct_type_index: struct_idx,
555 field_index: 2,
556 });
557 emitter.instruction(Instruction::I32Const(slot as i32));
558 emitter.instruction(Instruction::LocalGet(stored));
559 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
560 let index = emitter.add_anonymous_local(ValType::I32)?;
561 emitter.instruction(Instruction::I32Const(slot as i32));
562 emitter.instruction(Instruction::LocalSet(index));
563 crate::codegen::field_names::emit_mark_present(emitter, ctx, recv, index)?;
564 Ok(())
565}
566
567fn emit_object_field_store(
568 emitter: &mut FunctionEmitter,
569 ctx: &CodegenCtx,
570 receiver_ty: &Type,
571 recv: u32,
572 name: &Ident,
573 value: ExprId,
574) -> Result<(), crate::compiler_error::CompilerFailure> {
575 let object_shape = ctx
576 .symbols
577 .intrinsic_type_indices()
578 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?
579 .object_shape;
580 let builtin = crate::codegen::may_hold_builtin_value(receiver_ty);
581 if builtin {
582 emitter.instruction(Instruction::LocalGet(recv));
583 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(
584 object_shape,
585 )));
586 emitter.emit_if(BlockType::Empty);
587 }
588 emitter.instruction(Instruction::LocalGet(recv));
589 let rcv_local = stash_receiver_as_object_shape(emitter, receiver_ty, object_shape)?;
590 emit_object_property_write(emitter, ctx, rcv_local, name, value)?;
593 if builtin {
594 emitter.emit_else();
595 emit_builtin_field_store(emitter, ctx, recv, name, value)?;
596 emitter.emit_end();
597 }
598 Ok(())
599}
600
601fn emit_builtin_field_store(
606 emitter: &mut FunctionEmitter,
607 ctx: &CodegenCtx,
608 recv: u32,
609 name: &Ident,
610 value: ExprId,
611) -> Result<(), crate::compiler_error::CompilerFailure> {
612 let key = ctx
613 .symbols
614 .field_name_string_global_idx(&name.name)
615 .ok_or_else(|| crate::codegen::internal_failure("written field name interned"))?;
616 let set_field = ctx
617 .symbols
618 .prelude_func_idx("ObjectConstructor##setField")
619 .ok_or_else(|| crate::codegen::internal_failure("dynamic write imported"))?;
620 emitter.instruction(Instruction::LocalGet(recv));
621 emitter.instruction(Instruction::GlobalGet(key));
622 emit_expr(emitter, ctx, value)?;
623 cast::emit_box(
624 emitter,
625 ctx,
626 &ctx.ta
627 .try_expr(value)
628 .map_err(crate::codegen::arena_failure)?
629 .ty,
630 )?;
631 emitter.instruction(Instruction::Call(set_field));
632 Ok(())
633}
634
635fn emit_assign_index(
636 emitter: &mut FunctionEmitter,
637 ctx: &CodegenCtx,
638 receiver: ExprId,
639 index: ExprId,
640 value: ExprId,
641 elem_ty: &Type,
642) -> Result<(), crate::compiler_error::CompilerFailure> {
643 let mark = emitter.single_evaluation_mark();
644 if ctx
645 .ta
646 .source_type(receiver)
647 .map_err(crate::codegen::arena_failure)?
648 .indexes_by_name()
649 {
650 let object = emit_operand_once(emitter, ctx, receiver)?;
651 let key = emit_operand_once(emitter, ctx, index)?;
652 let stored = emit_operand_once(emitter, ctx, value)?;
653 emitter.instruction(Instruction::LocalGet(object));
654 cast::emit_box(
655 emitter,
656 ctx,
657 &ctx.ta
658 .try_expr(receiver)
659 .map_err(crate::codegen::arena_failure)?
660 .ty,
661 )?;
662 emitter.instruction(Instruction::LocalGet(key));
663 crate::codegen::cast_check::emit_operation_cast_on_stack(
664 emitter,
665 ctx,
666 &ctx.ta
667 .try_expr(index)
668 .map_err(crate::codegen::arena_failure)?
669 .ty,
670 &Type::String,
671 )?;
672 emitter.instruction(Instruction::LocalGet(stored));
673 cast::emit_box(
674 emitter,
675 ctx,
676 &ctx.ta
677 .try_expr(value)
678 .map_err(crate::codegen::arena_failure)?
679 .ty,
680 )?;
681 let Some(symbol) = ctx.require(
682 ctx.symbols.prelude_func_idx("ObjectConstructor##setField"),
683 "dynamic write imported",
684 ) else {
685 return Ok(());
686 };
687 emitter.instruction(Instruction::Call(symbol));
688 } else if matches!(
689 ctx.ta
690 .source_type(receiver)
691 .map_err(crate::codegen::arena_failure)?
692 .lowered(),
693 Type::Uint8Array
694 ) {
695 emit_uint8_index_store(emitter, ctx, receiver, index, value)?;
696 } else {
697 emit_array_index_store(emitter, ctx, receiver, index, value, elem_ty)?;
698 }
699 emitter.end_single_evaluations(mark)?;
700 Ok(())
701}
702
703fn emit_uint8_index_store(
705 emitter: &mut FunctionEmitter,
706 ctx: &CodegenCtx,
707 receiver: ExprId,
708 index: ExprId,
709 value: ExprId,
710) -> Result<(), crate::compiler_error::CompilerFailure> {
711 let raw_uint8_idx = ctx.symbols.raw_uint8_array_type_idx().ok_or_else(|| {
712 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
713 })?;
714 let uint8_idx = ctx.symbols.uint8_array_type_idx().ok_or_else(|| {
715 crate::codegen::internal_failure("Type::Uint8Array requires intrinsic types declared")
716 })?;
717 let recv_local = emit_operand_once(emitter, ctx, receiver)?;
718 let key = emit_operand_once(emitter, ctx, index)?;
719 let original_value = emit_operand_once(emitter, ctx, value)?;
722
723 let raw_local = emitter.add_anonymous_local(ValType::Ref(RefType {
727 nullable: false,
728 heap_type: HeapType::Concrete(raw_uint8_idx),
729 }))?;
730 emitter.instruction(Instruction::LocalGet(recv_local));
731 crate::codegen::cast_check::emit_operation_cast_on_stack(
732 emitter,
733 ctx,
734 &ctx.ta
735 .try_expr(receiver)
736 .map_err(crate::codegen::arena_failure)?
737 .ty,
738 &Type::Uint8Array,
739 )?;
740 emitter.instruction(Instruction::LocalGet(key));
741 super::expr::emit_index_number(
742 emitter,
743 ctx,
744 &ctx.ta
745 .try_expr(index)
746 .map_err(crate::codegen::arena_failure)?
747 .ty,
748 )?;
749 let idx_f64_local = stash_index_operand(emitter)?;
750 let value_local = emitter.add_anonymous_local(ValType::I32)?;
751 emitter.instruction(Instruction::LocalGet(original_value));
752 cast::emit_coerce_to_slot(
753 emitter,
754 ctx,
755 &ctx.ta
756 .try_expr(value)
757 .map_err(crate::codegen::arena_failure)?
758 .ty,
759 &Type::Number,
760 )?;
761 emit_to_uint8(emitter)?;
762 emitter.instruction(Instruction::LocalSet(value_local));
763 emitter.instruction(Instruction::StructGet {
764 struct_type_index: uint8_idx,
765 field_index: 1,
766 });
767 emitter.instruction(Instruction::LocalSet(raw_local));
768 let idx_local = emit_checked_index(
769 emitter,
770 ctx,
771 raw_local,
772 idx_f64_local,
773 &receiver_name(ctx.ta, receiver)?,
774 IndexAccess::FixedWrite,
775 )?;
776 emitter.instruction(Instruction::LocalGet(raw_local));
777 emitter.instruction(Instruction::LocalGet(idx_local));
778 emitter.instruction(Instruction::LocalGet(value_local));
779 emitter.instruction(Instruction::ArraySet(raw_uint8_idx));
780 Ok(())
781}
782
783fn emit_to_uint8(
787 emitter: &mut FunctionEmitter,
788) -> Result<(), crate::compiler_error::CompilerFailure> {
789 let truncated = emitter.add_anonymous_local(ValType::F64)?;
790 emitter.instruction(Instruction::F64Trunc);
791 emitter.instruction(Instruction::LocalTee(truncated));
792 emitter.instruction(Instruction::LocalGet(truncated));
793 emitter.instruction(Instruction::F64Const(256.0.into()));
794 emitter.instruction(Instruction::F64Div);
795 emitter.instruction(Instruction::F64Floor);
796 emitter.instruction(Instruction::F64Const(256.0.into()));
797 emitter.instruction(Instruction::F64Mul);
798 emitter.instruction(Instruction::F64Sub);
799 emitter.instruction(Instruction::I32TruncSatF64U);
800 Ok(())
801}
802
803fn emit_array_index_store(
805 emitter: &mut FunctionEmitter,
806 ctx: &CodegenCtx,
807 receiver: ExprId,
808 index: ExprId,
809 value: ExprId,
810 _elem_ty: &Type,
811) -> Result<(), crate::compiler_error::CompilerFailure> {
812 let raw_array_idx = ctx.symbols.raw_array_type_idx().ok_or_else(|| {
813 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
814 })?;
815 let array_idx = ctx.symbols.array_type_idx().ok_or_else(|| {
816 crate::codegen::internal_failure("Type::Array requires intrinsic types declared")
817 })?;
818 let object_idx = ctx
819 .symbols
820 .intrinsic_type_indices()
821 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
822 .object;
823 let recv_local = emit_operand_once(emitter, ctx, receiver)?;
824 let key = emit_operand_once(emitter, ctx, index)?;
825 let value_local = emitter.add_anonymous_local(ValType::Ref(RefType {
828 nullable: true,
829 heap_type: HeapType::Concrete(object_idx),
830 }))?;
831 let value_ty = ctx
832 .ta
833 .try_expr(value)
834 .map_err(crate::codegen::arena_failure)?
835 .ty
836 .clone();
837 emit_expr(emitter, ctx, value)?;
838 cast::emit_box(emitter, ctx, &value_ty)?;
840 emitter.instruction(Instruction::LocalSet(value_local));
841
842 let raw_arr_local = emitter.add_anonymous_local(ValType::Ref(RefType {
847 nullable: false,
848 heap_type: HeapType::Concrete(raw_array_idx),
849 }))?;
850 emitter.instruction(Instruction::LocalGet(recv_local));
851 crate::codegen::cast_check::emit_operation_cast_on_stack(
852 emitter,
853 ctx,
854 &ctx.ta
855 .try_expr(receiver)
856 .map_err(crate::codegen::arena_failure)?
857 .ty,
858 &Type::Array(Box::new(Type::Unknown)),
859 )?;
860 emitter.instruction(Instruction::LocalGet(key));
861 super::expr::emit_index_number(
862 emitter,
863 ctx,
864 &ctx.ta
865 .try_expr(index)
866 .map_err(crate::codegen::arena_failure)?
867 .ty,
868 )?;
869 let idx_f64_local = stash_index_operand(emitter)?;
870 let length = stash_array_length(emitter, array_idx)?;
871 emitter.instruction(Instruction::StructGet {
872 struct_type_index: array_idx,
873 field_index: 1,
874 });
875 emitter.instruction(Instruction::LocalSet(raw_arr_local));
876 let idx_local = emit_checked_index_with_length(
877 emitter,
878 ctx,
879 length,
880 idx_f64_local,
881 &receiver_name(ctx.ta, receiver)?,
882 IndexAccess::Write,
883 )?;
884 emitter.instruction(Instruction::LocalGet(raw_arr_local));
885 emitter.instruction(Instruction::LocalGet(idx_local));
886 emitter.instruction(Instruction::LocalGet(value_local));
887 emitter.instruction(Instruction::ArraySet(raw_array_idx));
888 Ok(())
889}
890
891pub(super) fn emit_try(
894 emitter: &mut FunctionEmitter,
895 ctx: &CodegenCtx,
896 body: StmtId,
897 catches: &[crate::TypedCatchClause],
898 finally: Option<StmtId>,
899) -> Result<(), crate::compiler_error::CompilerFailure> {
900 if let Some(finally) = finally {
901 super::finally::emit_try_finally(emitter, ctx, body, catches, finally)?;
902 return Ok(());
903 }
904 if catches.is_empty() {
905 emit_statement(emitter, ctx, body)?;
906 return Ok(());
907 }
908 let error_idx = ctx
909 .symbols
910 .intrinsic_type_indices()
911 .ok_or_else(|| crate::codegen::internal_failure("error intrinsic registered"))?
912 .error;
913 let tag_idx = ctx
914 .symbols
915 .error_tag_idx()
916 .ok_or_else(|| crate::codegen::internal_failure("error tag registered"))?;
917 emitter.emit_block(BlockType::Empty);
918 emitter.emit_block(BlockType::Result(ValType::Ref(RefType {
919 nullable: false,
920 heap_type: HeapType::Concrete(error_idx),
921 })));
922 emitter.instruction(Instruction::TryTable(
923 BlockType::Empty,
924 std::borrow::Cow::Owned(vec![wasm_encoder::Catch::One {
925 tag: tag_idx,
926 label: 0,
927 }]),
928 ));
929 emitter.bump_block_depth();
930 emit_statement(emitter, ctx, body)?;
931 emitter.emit_end();
932 emitter.instruction(Instruction::Br(1));
933 emitter.emit_end();
934 let err_stash = emitter.add_anonymous_local(ValType::Ref(RefType {
935 nullable: true,
936 heap_type: HeapType::Concrete(error_idx),
937 }))?;
938 emitter.instruction(Instruction::LocalSet(err_stash));
939 emit_catch_dispatch(emitter, ctx, catches, err_stash)?;
940 emitter.emit_end();
941 Ok(())
942}
943
944fn catch_filter_class(
948 ctx: &CodegenCtx,
949 clause_ty: &Type,
950) -> Result<Option<(u32, u32)>, crate::compiler_error::CompilerFailure> {
951 let Type::ClassRef { mangled, .. } = clause_ty.lowered() else {
952 return Ok(None);
953 };
954 if *mangled == crate::mangle::prelude("Error") {
955 return Ok(None);
956 }
957 let vtable_global = ctx
958 .symbols
959 .class_vtable_global_idx(mangled)
960 .ok_or_else(|| {
961 crate::codegen::internal_failure("catch-annotation class vtable global recorded")
962 })?;
963 let struct_idx = ctx.symbols.class_struct_type_idx(mangled).ok_or_else(|| {
964 crate::codegen::internal_failure("catch-annotation class struct type recorded")
965 })?;
966 Ok(Some((vtable_global, struct_idx)))
967}
968
969fn emit_catch_dispatch(
980 emitter: &mut FunctionEmitter,
981 ctx: &CodegenCtx,
982 catches: &[crate::TypedCatchClause],
983 err_stash: u32,
984) -> Result<(), crate::compiler_error::CompilerFailure> {
985 for clause in catches {
986 let Some((vtable_global, struct_idx)) = catch_filter_class(ctx, &clause.ty)? else {
987 emitter.push_scope();
988 emitter.instruction(Instruction::LocalGet(err_stash));
989 emitter.instruction(Instruction::RefAsNonNull);
990 bind_catch_local(emitter, ctx, clause)?;
991 emit_statement(emitter, ctx, clause.body)?;
992 emitter.pop_scope()?;
993 return Ok(());
995 };
996 emitter.instruction(Instruction::LocalGet(err_stash));
997 super::cast::emit_nominal_instance_test(emitter, ctx, vtable_global)?;
998 emitter.emit_if(BlockType::Empty);
999 emitter.push_scope();
1000 emitter.instruction(Instruction::LocalGet(err_stash));
1001 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(struct_idx)));
1003 bind_catch_local(emitter, ctx, clause)?;
1004 emit_statement(emitter, ctx, clause.body)?;
1005 emitter.pop_scope()?;
1006 emitter.instruction(Instruction::Br(1));
1008 emitter.emit_end();
1009 }
1010 let tag_idx = ctx
1011 .symbols
1012 .error_tag_idx()
1013 .ok_or_else(|| crate::codegen::internal_failure("error tag registered"))?;
1014 emitter.instruction(Instruction::LocalGet(err_stash));
1015 emitter.instruction(Instruction::RefAsNonNull);
1016 emitter.instruction(Instruction::Throw(tag_idx));
1017 Ok(())
1018}
1019
1020fn bind_catch_local(
1023 emitter: &mut FunctionEmitter,
1024 ctx: &CodegenCtx,
1025 clause: &crate::TypedCatchClause,
1026) -> Result<(), crate::compiler_error::CompilerFailure> {
1027 let slot_ty = if clause.boxed {
1028 let box_idx = ctx.symbols.box_type_idx(&clause.ty)?.ok_or_else(|| {
1029 crate::codegen::internal_failure("box type registered for every boxed catch binding")
1030 })?;
1031 emitter.instruction(Instruction::I32Const(1));
1033 emitter.instruction(Instruction::StructNew(box_idx));
1034 ValType::Ref(RefType {
1035 nullable: false,
1036 heap_type: HeapType::Concrete(box_idx),
1037 })
1038 } else {
1039 ctx.symbols.value_type(&clause.ty)?
1040 };
1041 let slot = emitter.define_local(&clause.binding, slot_ty)?;
1042 emitter.instruction(Instruction::LocalSet(slot));
1043 Ok(())
1044}
1045
1046fn emit_switch(
1073 emitter: &mut FunctionEmitter,
1074 ctx: &CodegenCtx,
1075 discriminant: crate::ExprId,
1076 discriminant_ty: &Type,
1077 cases: &[TypedSwitchCase],
1078 default: Option<StmtId>,
1079) -> Result<(), crate::compiler_error::CompilerFailure> {
1080 emit_expr(emitter, ctx, discriminant)?;
1083 let disc_val = switch_disc_local_type(ctx, discriminant_ty)?;
1084 if matches!(discriminant_ty.lowered(), Type::NumberEnum { .. }) {
1085 let boxed_idx = ctx.symbols.boxed_number_type_idx().ok_or_else(|| {
1086 crate::codegen::internal_failure("boxed_number type registered with intrinsics")
1087 })?;
1088 emitter.instruction(Instruction::StructGet {
1089 struct_type_index: boxed_idx,
1090 field_index: 1,
1091 });
1092 }
1093 let disc_local = emitter.add_anonymous_local(disc_val)?;
1094 emitter.instruction(Instruction::LocalSet(disc_local));
1095
1096 emitter.emit_switch_open();
1097 if default.is_some() {
1100 emitter.emit_block(BlockType::Empty);
1101 }
1102 for _ in 0..cases.len() {
1103 emitter.emit_block(BlockType::Empty);
1104 }
1105
1106 for (source_idx, case) in cases.iter().enumerate() {
1108 emitter.record_span(case.span);
1109 for value in &case.values {
1110 emit_case_comparison(emitter, ctx, disc_local, discriminant_ty, value)?;
1111 emitter.instruction(Instruction::BrIf(crate::codegen::wasm_u32(source_idx)?));
1112 }
1113 }
1114 emitter.instruction(Instruction::Br(crate::codegen::wasm_u32(cases.len())?));
1116
1117 for case in cases {
1121 emitter.emit_end();
1122 emitter.push_scope();
1123 emit_statement(emitter, ctx, case.body)?;
1124 emitter.pop_scope()?;
1125 let end_label = emitter.break_label()?;
1126 emitter.instruction(Instruction::Br(end_label));
1127 }
1128
1129 if let Some(default_body) = default {
1130 emitter.emit_end();
1131 emitter.push_scope();
1132 emit_statement(emitter, ctx, default_body)?;
1133 emitter.pop_scope()?;
1134 }
1136
1137 emitter.emit_switch_close();
1138 Ok(())
1139}
1140
1141fn switch_disc_local_type(
1144 ctx: &CodegenCtx,
1145 ty: &Type,
1146) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1147 Ok(if matches!(ty.lowered(), Type::NumberEnum { .. }) {
1148 ValType::F64
1149 } else {
1150 ctx.symbols.value_type(ty)?
1151 })
1152}
1153
1154fn emit_case_comparison(
1157 emitter: &mut FunctionEmitter,
1158 ctx: &CodegenCtx,
1159 disc_local: u32,
1160 disc_ty: &Type,
1161 value: &TypedSwitchValue,
1162) -> Result<(), crate::compiler_error::CompilerFailure> {
1163 let disc_val = switch_disc_local_type(ctx, disc_ty)?;
1164 if let Some(case_ty) = switch_case_primitive_type(value)
1165 && matches!(disc_val, ValType::Ref(_))
1166 && disc_val != ctx.symbols.value_type(&case_ty)?
1167 {
1168 emit_boxed_case_comparison(emitter, ctx, disc_local, &case_ty, value)?;
1169 return Ok(());
1170 }
1171 match value {
1172 TypedSwitchValue::Expr { comparison, .. } => {
1173 emit_expr(emitter, ctx, *comparison)?;
1174 }
1175 TypedSwitchValue::Undefined { .. } => {
1176 emitter.instruction(Instruction::LocalGet(disc_local));
1177 super::expr::emit_is_undefined(emitter, ctx)?;
1178 }
1179 TypedSwitchValue::Null { .. } => {
1180 emitter.instruction(Instruction::LocalGet(disc_local));
1182 emitter.instruction(Instruction::RefIsNull);
1183 }
1184 TypedSwitchValue::Number { value, .. } => {
1185 emitter.instruction(Instruction::LocalGet(disc_local));
1186 emitter.instruction(Instruction::F64Const((*value).into()));
1187 emitter.instruction(Instruction::F64Eq);
1188 }
1189 TypedSwitchValue::Boolean { value, .. } => {
1190 emitter.instruction(Instruction::LocalGet(disc_local));
1191 emitter.instruction(Instruction::I32Const(if *value { 1 } else { 0 }));
1192 emitter.instruction(Instruction::I32Eq);
1193 }
1194 TypedSwitchValue::String { value, .. } => {
1195 emit_string_compare(emitter, ctx, disc_local, disc_val, value)?;
1196 }
1197 TypedSwitchValue::Enum { value: payload, .. } => match payload {
1198 EnumVariantPayload::Number(n) => {
1199 emitter.instruction(Instruction::LocalGet(disc_local));
1200 emitter.instruction(Instruction::F64Const((*n).into()));
1201 emitter.instruction(Instruction::F64Eq);
1202 }
1203 EnumVariantPayload::String(s) => {
1204 emit_string_compare(emitter, ctx, disc_local, disc_val, s)?;
1205 }
1206 },
1207 };
1208 Ok(())
1209}
1210
1211fn emit_boxed_case_comparison(
1214 emitter: &mut FunctionEmitter,
1215 ctx: &CodegenCtx,
1216 disc_local: u32,
1217 case_ty: &Type,
1218 value: &TypedSwitchValue,
1219) -> Result<(), crate::compiler_error::CompilerFailure> {
1220 let heap_index = match case_ty {
1221 Type::Number => ctx.symbols.boxed_number_type_idx(),
1222 Type::Boolean => ctx.symbols.boxed_boolean_type_idx(),
1223 Type::String => ctx.symbols.string_type_idx(),
1224 _ => {
1225 return Err(crate::codegen::internal_failure(
1226 "switch case primitive classified above",
1227 ));
1228 }
1229 }
1230 .ok_or_else(|| {
1231 crate::codegen::internal_failure("primitive types registered before switch emission")
1232 })?;
1233 emitter.instruction(Instruction::LocalGet(disc_local));
1234 emitter.instruction(Instruction::RefTestNonNull(HeapType::Concrete(heap_index)));
1235 emitter.emit_if(BlockType::Result(ValType::I32));
1236 emitter.instruction(Instruction::LocalGet(disc_local));
1237 cast::emit_cast_to(emitter, ctx, case_ty)?;
1238 let unboxed = emitter.add_anonymous_local(ctx.symbols.value_type(case_ty)?)?;
1239 emitter.instruction(Instruction::LocalSet(unboxed));
1240 emit_case_comparison(emitter, ctx, unboxed, case_ty, value)?;
1241 emitter.emit_else();
1242 emitter.instruction(Instruction::I32Const(0));
1243 emitter.emit_end();
1244
1245 Ok(())
1246}
1247
1248fn switch_case_primitive_type(value: &TypedSwitchValue) -> Option<Type> {
1249 match value {
1250 TypedSwitchValue::Number { .. }
1251 | TypedSwitchValue::Enum {
1252 value: EnumVariantPayload::Number(_),
1253 ..
1254 } => Some(Type::Number),
1255 TypedSwitchValue::String { .. }
1256 | TypedSwitchValue::Enum {
1257 value: EnumVariantPayload::String(_),
1258 ..
1259 } => Some(Type::String),
1260 TypedSwitchValue::Boolean { .. } => Some(Type::Boolean),
1261 TypedSwitchValue::Null { .. }
1262 | TypedSwitchValue::Undefined { .. }
1263 | TypedSwitchValue::Expr { .. } => None,
1264 }
1265}
1266
1267fn emit_string_compare(
1271 emitter: &mut FunctionEmitter,
1272 ctx: &CodegenCtx,
1273 disc_local: u32,
1274 disc_val: ValType,
1275 literal: &str,
1276) -> Result<(), crate::compiler_error::CompilerFailure> {
1277 let string_eq_idx = ctx.symbols.prelude_func_idx("string_eq").ok_or_else(|| {
1278 crate::codegen::internal_failure("string_eq is not imported from the prelude")
1279 })?;
1280
1281 let disc_is_nullable = matches!(
1282 disc_val,
1283 ValType::Ref(RefType {
1284 nullable: true,
1285 heap_type: HeapType::Concrete(_),
1286 })
1287 );
1288 if disc_is_nullable {
1289 emitter.instruction(Instruction::LocalGet(disc_local));
1290 emitter.instruction(Instruction::RefIsNull);
1291 emitter.emit_if(BlockType::Result(ValType::I32));
1292 emitter.instruction(Instruction::I32Const(0));
1293 emitter.emit_else();
1294 emitter.instruction(Instruction::LocalGet(disc_local));
1295 emitter.instruction(Instruction::RefAsNonNull);
1296 super::emit_const_string_by_text(emitter, ctx, literal)?;
1297 emitter.instruction(Instruction::Call(string_eq_idx));
1298 emitter.emit_end();
1299 } else {
1300 emitter.instruction(Instruction::LocalGet(disc_local));
1301 super::emit_const_string_by_text(emitter, ctx, literal)?;
1302 emitter.instruction(Instruction::Call(string_eq_idx));
1303 }
1304 Ok(())
1305}
1306
1307fn note_maybe_missing_slot(
1310 emitter: &mut FunctionEmitter,
1311 ctx: &CodegenCtx,
1312 name: &crate::Ident,
1313 slot: u32,
1314 ty: &Type,
1315 present_flag: Option<u32>,
1316) {
1317 if let Some(local) = ctx.ta.maybe_missing_locals.get(&name.span) {
1318 emitter.maybe_missing_slots.insert(
1319 slot,
1320 super::MaybeMissingSlot {
1321 message: local.message.clone(),
1322 storage: ty.clone(),
1323 present_flag,
1324 },
1325 );
1326 }
1327}
1328
1329fn present_flag_for(
1332 emitter: &mut FunctionEmitter,
1333 ctx: &CodegenCtx,
1334 name: &crate::Ident,
1335) -> Result<Option<u32>, crate::compiler_error::CompilerFailure> {
1336 if ctx
1337 .ta
1338 .maybe_missing_locals
1339 .get(&name.span)
1340 .is_some_and(|local| local.has_present_flag)
1341 {
1342 return Ok(Some(emitter.add_anonymous_local(ValType::I32)?));
1343 }
1344 Ok(None)
1345}