1pub(super) mod binding_cells;
4pub mod cast;
5pub mod expr;
6mod finally;
7pub mod json;
8pub mod mcp;
9pub mod stmt;
10
11use std::collections::{BTreeMap, HashMap, HashSet};
12
13use wasm_encoder::{BlockType, Encode, Function, Instruction, ValType};
14
15use crate::codegen::CodegenCtx;
16use crate::compiler_error::CompilerFailure;
17use crate::{ExprId, Ident, Span, Type};
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub enum ExprContext {
21 Value,
22 Statement,
23}
24
25#[derive(Clone, Debug, Default)]
27pub enum ReturnTarget {
28 #[default]
30 NoResult,
31 Slot(ValType),
35 Declared(Type),
37}
38
39pub struct FunctionEmitter<'a> {
40 pub(super) cast_diagnostic: Option<super::cast_diagnostics::Locals>,
41 pub runtime_type_params: BTreeMap<String, (u32, u32)>,
42 ctx: &'a CodegenCtx<'a>,
43
44 next_local_index: u32,
45 parameter_types: Vec<ValType>,
46 locals: Vec<(u32, ValType)>,
47 instructions: Vec<Instruction<'static>>,
48 sized_instructions: usize,
51 instruction_bytes: usize,
53 scopes: Vec<Scope>,
55
56 wasm_block_depth: u32,
57 loop_contexts: Vec<LoopLabels>,
58 #[allow(dead_code)]
59 return_block_depth: u32,
60
61 return_target: ReturnTarget,
62
63 finally_stack: Vec<finally::FinallyFrame>,
65
66 this_local: Option<u32>,
69 pub(super) dynamic_this: bool,
70 pub(super) call_receiver: Option<u32>,
71
72 ctor_class: Option<crate::MangledName>,
76
77 source_mappings: Vec<(usize, Span)>,
78 locals_limit_span: Option<Span>,
81 local_entry_bytes: usize,
83 body_limit_instruction: Option<usize>,
86
87 single_evaluations: Vec<(ExprId, u32)>,
93 parameter_input_slots: BTreeMap<String, (u32, Type)>,
94 uninitialized_parameters: HashSet<Ident>,
95 parameter_binding_cells: HashMap<Ident, u32>,
96 pub(super) uninitialized_cells: HashSet<u32>,
100}
101
102#[derive(Clone, Copy, Default)]
109struct Binding {
110 declared: Option<(u32, ValType)>,
113 shadow: Option<(u32, ValType)>,
114}
115
116#[derive(Default)]
117struct Scope {
118 bindings: BTreeMap<String, Binding>,
119 narrow_sources: BTreeMap<String, crate::ExprId>,
120}
121
122impl Scope {
123 fn new() -> Self {
124 Self::default()
125 }
126 fn define(&mut self, name: String, index: u32, ty: ValType) {
129 self.bindings.insert(
130 name,
131 Binding {
132 declared: Some((index, ty)),
133 shadow: None,
134 },
135 );
136 }
137 fn set_shadow(&mut self, name: String, index: u32, ty: ValType) {
138 self.bindings.entry(name).or_default().shadow = Some((index, ty));
139 }
140 fn clear_shadow(&mut self, name: &str) {
141 if let Some(binding) = self.bindings.get_mut(name) {
142 binding.shadow = None;
143 }
144 }
145 fn clear_all_shadows(&mut self) {
146 for binding in self.bindings.values_mut() {
147 binding.shadow = None;
148 }
149 }
150 fn lookup(&self, name: &str) -> Option<(u32, ValType)> {
152 self.bindings
153 .get(name)
154 .and_then(|b| b.shadow.or(b.declared))
155 }
156 fn lookup_declared(&self, name: &str) -> Option<(u32, ValType)> {
157 self.bindings.get(name).and_then(|b| b.declared)
158 }
159}
160
161struct LoopLabels {
162 break_depth: u32,
163 continue_depth: u32,
164 is_switch: bool,
166 finally_count_at_push: usize,
168}
169
170impl<'a> FunctionEmitter<'a> {
171 pub fn new(
173 ctx: &'a CodegenCtx<'a>,
174 params: &[(Ident, ValType)],
175 ) -> Result<Self, CompilerFailure> {
176 let parameter_count = crate::codegen::wasm_u32(params.len())?;
177 super::FunctionLimit::exceeded(0, parameter_count).map_or(Ok(()), |limit| {
178 Err(limit.failure(params.last().map(|(name, _)| name.span)))
179 })?;
180 let mut emitter = Self {
181 ctx,
182 runtime_type_params: BTreeMap::new(),
183 next_local_index: parameter_count,
184 parameter_types: params.iter().map(|(_, ty)| *ty).collect(),
185 locals: Vec::new(),
186 instructions: Vec::new(),
187 sized_instructions: 0,
188 instruction_bytes: 0,
189 scopes: vec![Scope::new()],
190 wasm_block_depth: 0,
191 loop_contexts: Vec::new(),
192 return_block_depth: 0,
193 return_target: ReturnTarget::NoResult,
194 finally_stack: Vec::new(),
195 this_local: None,
196 dynamic_this: false,
197 call_receiver: None,
198 ctor_class: None,
199 source_mappings: Vec::new(),
200 locals_limit_span: None,
201 body_limit_instruction: None,
202 local_entry_bytes: 0,
203 single_evaluations: Vec::new(),
204 parameter_input_slots: BTreeMap::new(),
205 uninitialized_parameters: HashSet::new(),
206 parameter_binding_cells: HashMap::new(),
207 uninitialized_cells: HashSet::new(),
208 cast_diagnostic: None,
209 };
210 let mut scope = Scope::new();
211 for (index, (name, ty)) in params.iter().enumerate() {
212 scope.define(name.name.clone(), crate::codegen::wasm_u32(index)?, *ty);
213 }
214 emitter.scopes = vec![scope];
215 Ok(emitter)
216 }
217
218 pub fn define_local(&mut self, name: &Ident, ty: ValType) -> Result<u32, CompilerFailure> {
219 let index = self.add_anonymous_local(ty)?;
220 self.rebind_in_innermost_scope(&name.name, index, ty)?;
221 Ok(index)
222 }
223
224 pub fn evaluated_slot(&self, id: ExprId) -> Option<u32> {
227 self.single_evaluations
228 .iter()
229 .find(|(recorded, _)| *recorded == id)
230 .map(|(_, slot)| *slot)
231 }
232
233 pub fn record_single_evaluation(
236 &mut self,
237 id: ExprId,
238 slot: u32,
239 ) -> Result<(), CompilerFailure> {
240 self.local_type(slot)?;
241 self.single_evaluations.push((id, slot));
242 Ok(())
243 }
244
245 pub fn single_evaluation_mark(&self) -> usize {
249 self.single_evaluations.len()
250 }
251
252 pub fn end_single_evaluations(&mut self, mark: usize) -> Result<(), CompilerFailure> {
255 if mark > self.single_evaluations.len() {
256 return Err(self.state_failure("invalid single evaluation mark"));
257 }
258 self.single_evaluations.truncate(mark);
259 Ok(())
260 }
261
262 pub fn add_anonymous_local(&mut self, ty: ValType) -> Result<u32, CompilerFailure> {
263 let index = self.next_local_index;
264 let next = index
265 .checked_add(1)
266 .ok_or_else(|| self.state_failure("local index overflow"))?;
267 if let Some(limit) = super::FunctionLimit::exceeded(0, next) {
268 return Err(limit.failure(self.last_source_span()));
269 }
270 let mut entry = vec![1u8];
272 ty.encode(&mut entry);
273 self.local_entry_bytes = self.local_entry_bytes.saturating_add(entry.len());
274 self.locals.push((1, ty));
275 self.next_local_index = next;
276 Ok(index)
277 }
278
279 pub fn require_local_slot(&self, name: &str) -> Result<u32, CompilerFailure> {
280 let index = self
281 .local_slot(name)
282 .ok_or_else(|| self.state_failure(format!("local `{name}` is not defined")))?;
283 self.local_type(index)?;
284 Ok(index)
285 }
286
287 pub fn narrowed_read_slot(&self, name: &str) -> Option<(u32, ValType)> {
293 self.scopes
294 .iter()
295 .rev()
296 .find_map(|scope| scope.lookup(name))
297 }
298
299 pub fn local_slot(&self, name: &str) -> Option<u32> {
301 self.scopes
302 .iter()
303 .rev()
304 .find_map(|scope| scope.lookup_declared(name))
305 .map(|(index, _)| index)
306 }
307
308 pub fn write_slot(&mut self, name: &str) -> Result<u32, CompilerFailure> {
315 let index = self.require_local_slot(name)?;
316 self.clear_narrow_shadows(name);
317 Ok(index)
318 }
319
320 pub fn set_return_target(&mut self, target: ReturnTarget) {
321 self.return_target = target;
322 }
323
324 pub fn return_target(&self) -> &ReturnTarget {
325 &self.return_target
326 }
327
328 pub fn set_this_local(&mut self, index: u32) -> Result<(), CompilerFailure> {
329 self.local_type(index)?;
330 self.this_local = Some(index);
331 Ok(())
332 }
333
334 pub fn this_local(&self) -> Option<u32> {
335 self.this_local
336 }
337
338 pub fn set_ctor_class(&mut self, mangled: crate::MangledName) {
339 self.ctor_class = Some(mangled);
340 }
341
342 pub fn ctor_class(&self) -> Option<&crate::MangledName> {
343 self.ctor_class.as_ref()
344 }
345
346 pub fn rebind_in_innermost_scope(
350 &mut self,
351 name: &str,
352 index: u32,
353 ty: ValType,
354 ) -> Result<(), CompilerFailure> {
355 self.check_local_type(index, ty)?;
356 let scope = self
357 .scopes
358 .last_mut()
359 .expect("emitter retains its root scope");
360 scope.define(name.to_string(), index, ty);
361 Ok(())
362 }
363
364 pub fn install_narrow_shadow(
367 &mut self,
368 name: &str,
369 index: u32,
370 ty: ValType,
371 ) -> Result<(), CompilerFailure> {
372 self.check_local_type(index, ty)?;
373 let scope = self
374 .scopes
375 .last_mut()
376 .expect("emitter retains its root scope");
377 scope.set_shadow(name.to_string(), index, ty);
378 Ok(())
379 }
380
381 pub fn register_narrow_source(
384 &mut self,
385 name: &str,
386 source: crate::ExprId,
387 ) -> Result<(), CompilerFailure> {
388 self.ctx
389 .ta
390 .try_expr(source)
391 .map_err(crate::codegen::arena_failure)?;
392 let scope = self
393 .scopes
394 .last_mut()
395 .expect("emitter retains its root scope");
396 scope.narrow_sources.insert(name.to_string(), source);
397 Ok(())
398 }
399
400 pub fn narrow_source(&self, name: &str) -> Option<crate::ExprId> {
401 self.scopes
402 .iter()
403 .rev()
404 .find_map(|scope| scope.narrow_sources.get(name).copied())
405 }
406
407 pub fn clear_narrow_shadows(&mut self, name: &str) {
411 for scope in self.scopes.iter_mut().rev() {
412 scope.clear_shadow(name);
413 if scope.lookup_declared(name).is_some() {
414 return;
415 }
416 }
417 }
418
419 pub fn clear_all_narrow_shadows(&mut self) {
424 for scope in &mut self.scopes {
425 scope.clear_all_shadows();
426 }
427 }
428
429 pub fn push_scope(&mut self) {
430 self.scopes.push(Scope::new());
431 }
432
433 pub fn pop_scope(&mut self) -> Result<(), CompilerFailure> {
435 if self.scopes.len() <= 1 {
436 return Err(self.state_failure("cannot pop the root emitter scope"));
437 }
438 self.scopes.pop();
439 Ok(())
440 }
441
442 pub fn emit_boxed_param_prologue(
446 &mut self,
447 params: &[crate::TypedParam],
448 typed_slots: &[u32],
449 ) -> Result<(), crate::compiler_error::CompilerFailure> {
450 if params.len() != typed_slots.len() {
451 return Err(self.state_failure("typed slots must have one entry per parameter"));
452 }
453 let mut boxes = Vec::new();
455 for (p, &typed_slot) in params.iter().zip(typed_slots) {
456 self.check_local_type(typed_slot, self.ctx.symbols.value_type(&p.ty)?)?;
457 if self.require_local_slot(&p.name.name)? != typed_slot {
458 return Err(self.state_failure("typed parameter slot does not match its binding"));
459 }
460 self.parameter_input_slots
461 .insert(p.name.name.clone(), (typed_slot, p.ty.clone()));
462 if !p.boxed {
463 continue;
464 }
465 let box_idx =
466 self.ctx.symbols.box_type_idx(&p.ty)?.ok_or_else(|| {
467 self.state_failure("box type registered for every boxed param")
468 })?;
469 boxes.push((p, typed_slot, box_idx));
470 }
471 let count = self
472 .next_local_index
473 .checked_add(crate::codegen::wasm_u32(boxes.len())?)
474 .ok_or_else(|| self.state_failure("boxed parameter local count overflow"))?;
475 if let Some(limit) = super::FunctionLimit::exceeded(0, count) {
476 return Err(limit.failure(self.last_source_span()));
477 }
478 for (p, typed_slot, box_idx) in boxes {
479 let box_val = ValType::Ref(wasm_encoder::RefType {
480 nullable: false,
481 heap_type: wasm_encoder::HeapType::Concrete(box_idx),
482 });
483 let shadow_idx = self.add_anonymous_local(box_val)?;
484 let uninitialized = self.uninitialized_parameters.contains(&p.name);
485 if uninitialized {
486 self.uninitialized_cells.insert(shadow_idx);
487 }
488 self.instruction(Instruction::LocalGet(typed_slot));
489 self.instruction(Instruction::I32Const(i32::from(!uninitialized)));
490 self.instruction(Instruction::StructNew(box_idx));
491 self.instruction(Instruction::LocalSet(shadow_idx));
492 self.rebind_in_innermost_scope(&p.name.name, shadow_idx, box_val)?;
493 }
494 Ok(())
495 }
496
497 pub fn instruction(&mut self, inst: Instruction<'static>) {
499 self.instructions.push(inst);
500 }
501
502 pub fn instructions(&mut self, insts: impl IntoIterator<Item = Instruction<'static>>) {
503 self.instructions.extend(insts);
504 }
505
506 pub fn body_size(&mut self) -> usize {
511 let locals_count = u32::try_from(self.locals.len()).unwrap_or(u32::MAX);
512 let declarations_and_end_bytes = super::leb128_u32_size(locals_count)
514 .saturating_add(self.local_entry_bytes)
515 .saturating_add(1);
516 let mut encoded = Vec::new();
517 for (index, instruction) in self
518 .instructions
519 .iter()
520 .enumerate()
521 .skip(self.sized_instructions)
522 {
523 instruction.encode(&mut encoded);
524 let body_bytes = self
525 .instruction_bytes
526 .saturating_add(encoded.len())
527 .saturating_add(declarations_and_end_bytes);
528 if self.body_limit_instruction.is_none() && !super::FunctionLimit::body_fits(body_bytes)
529 {
530 self.body_limit_instruction = Some(index);
531 }
532 }
533 self.sized_instructions = self.instructions.len();
534 self.instruction_bytes = self.instruction_bytes.saturating_add(encoded.len());
535 self.instruction_bytes
536 .saturating_add(declarations_and_end_bytes)
537 }
538
539 pub fn body_limit_span(&self) -> Option<Span> {
542 self.body_limit_instruction
543 .and_then(|instruction| self.source_span_before(instruction))
544 }
545
546 pub fn locals_limit_span(&self) -> Option<Span> {
549 self.locals_limit_span
550 }
551
552 pub fn local_count(&self) -> u32 {
554 self.next_local_index
555 }
556
557 pub fn last_mapped_span(&self) -> Option<Span> {
559 self.source_mappings.last().map(|(_, span)| *span)
560 }
561
562 pub fn last_source_span(&self) -> Option<Span> {
565 self.source_span_before(self.instructions.len())
566 }
567
568 fn source_span_before(&self, instruction: usize) -> Option<Span> {
570 let mapped = self
571 .source_mappings
572 .partition_point(|(index, _)| *index <= instruction);
573 self.source_mappings
574 .iter()
575 .take(mapped)
576 .rev()
577 .map(|(_, span)| *span)
578 .find(|span| !span.is_placeholder())
579 }
580
581 pub fn record_span(&mut self, span: Span) {
582 self.source_mappings.push((self.instructions.len(), span));
583 }
584
585 pub fn emit_block(&mut self, ty: BlockType) {
586 self.instructions.push(Instruction::Block(ty));
587 if let Some(depth) = self.ctx.latch(
588 self.wasm_block_depth
589 .checked_add(1)
590 .ok_or_else(|| self.state_failure("block depth overflow")),
591 ) {
592 self.wasm_block_depth = depth;
593 }
594 }
595
596 pub fn emit_if(&mut self, ty: BlockType) {
597 self.instructions.push(Instruction::If(ty));
598 if let Some(depth) = self.ctx.latch(
599 self.wasm_block_depth
600 .checked_add(1)
601 .ok_or_else(|| self.state_failure("block depth overflow")),
602 ) {
603 self.wasm_block_depth = depth;
604 }
605 }
606
607 pub fn emit_loop(&mut self, ty: BlockType) {
608 self.instructions.push(Instruction::Loop(ty));
609 if let Some(depth) = self.ctx.latch(
610 self.wasm_block_depth
611 .checked_add(1)
612 .ok_or_else(|| self.state_failure("block depth overflow")),
613 ) {
614 self.wasm_block_depth = depth;
615 }
616 }
617
618 pub fn emit_else(&mut self) {
620 self.instructions.push(Instruction::Else);
621 }
622
623 pub fn emit_end(&mut self) {
624 let Some(depth) = self.ctx.latch(
625 self.wasm_block_depth
626 .checked_sub(1)
627 .ok_or_else(|| self.state_failure("end without an active block")),
628 ) else {
629 return;
630 };
631 self.wasm_block_depth = depth;
632 self.instructions.push(Instruction::End);
633 }
634
635 pub fn bump_block_depth(&mut self) {
638 if let Some(depth) = self.ctx.latch(
639 self.wasm_block_depth
640 .checked_add(1)
641 .ok_or_else(|| self.state_failure("block depth overflow")),
642 ) {
643 self.wasm_block_depth = depth;
644 }
645 }
646
647 pub fn wasm_result_type(
649 &self,
650 ctx: &CodegenCtx,
651 ) -> Result<Option<ValType>, crate::compiler_error::CompilerFailure> {
652 Ok(match &self.return_target {
653 ReturnTarget::Slot(slot) => Some(*slot),
654 ReturnTarget::Declared(ret) => Some(ctx.symbols.value_type(ret)?),
655 ReturnTarget::NoResult => None,
656 })
657 }
658
659 pub fn break_finally_floor(&self) -> usize {
660 self.loop_contexts
661 .last()
662 .map_or(0, |c| c.finally_count_at_push)
663 }
664
665 pub fn continue_finally_floor(&self) -> usize {
667 self.loop_contexts
668 .iter()
669 .rev()
670 .find(|c| !c.is_switch)
671 .map_or(0, |c| c.finally_count_at_push)
672 }
673
674 pub fn emit_while_open(&mut self) {
675 if self.wasm_block_depth > u32::MAX - 2 {
676 self.ctx
677 .record_failure(self.state_failure("loop block depth overflow"));
678 return;
679 }
680 self.emit_block(BlockType::Empty);
681 self.emit_loop(BlockType::Empty);
682 self.loop_contexts.push(LoopLabels {
683 break_depth: self.wasm_block_depth - 2,
684 continue_depth: self.wasm_block_depth - 1,
685 is_switch: false,
686 finally_count_at_push: self.finally_stack.len(),
687 });
688 }
689
690 pub fn emit_switch_open(&mut self) {
692 if self.wasm_block_depth == u32::MAX {
693 self.ctx
694 .record_failure(self.state_failure("switch block depth overflow"));
695 return;
696 }
697 self.emit_block(BlockType::Empty);
698 self.loop_contexts.push(LoopLabels {
699 break_depth: self.wasm_block_depth - 1,
700 continue_depth: 0,
702 is_switch: true,
703 finally_count_at_push: self.finally_stack.len(),
704 });
705 }
706
707 pub fn emit_switch_close(&mut self) {
708 if !self.loop_contexts.last().is_some_and(|frame| {
709 frame.is_switch && frame.break_depth.checked_add(1) == Some(self.wasm_block_depth)
710 }) {
711 self.ctx
712 .record_failure(self.state_failure("mismatched switch close"));
713 return;
714 }
715 self.loop_contexts.pop();
716 self.emit_end();
717 }
718
719 pub fn emit_while_close(&mut self) {
720 if !self.loop_contexts.last().is_some_and(|frame| {
721 !frame.is_switch && frame.break_depth.checked_add(2) == Some(self.wasm_block_depth)
722 }) {
723 self.ctx
724 .record_failure(self.state_failure("mismatched loop close"));
725 return;
726 }
727 self.loop_contexts.pop();
728 self.emit_end(); self.emit_end(); }
731
732 pub fn break_label(&self) -> Result<u32, CompilerFailure> {
733 let ctx = self
734 .loop_contexts
735 .last()
736 .ok_or_else(|| self.state_failure("break outside loop or switch"))?;
737 self.branch_depth(ctx.break_depth)
738 }
739
740 pub fn continue_label(&self) -> Result<u32, CompilerFailure> {
742 let ctx = self
743 .loop_contexts
744 .iter()
745 .rev()
746 .find(|c| !c.is_switch)
747 .ok_or_else(|| self.state_failure("continue outside loop"))?;
748 self.branch_depth(ctx.continue_depth)
749 }
750
751 pub fn return_label(&self) -> Result<u32, CompilerFailure> {
752 self.branch_depth(self.return_block_depth)
753 }
754
755 pub fn branch_depth(&self, target_depth: u32) -> Result<u32, CompilerFailure> {
756 self.wasm_block_depth
757 .checked_sub(target_depth)
758 .and_then(|depth| depth.checked_sub(1))
759 .ok_or_else(|| self.state_failure("branch target is outside the active blocks"))
760 }
761
762 pub fn emit_const_string(
764 &mut self,
765 string_type_idx: u32,
766 raw_string_type_idx: u32,
767 string_vtable_global_idx: u32,
768 consts_data_idx: u32,
769 code_units: u32,
770 ) {
771 self.instruction(Instruction::GlobalGet(string_vtable_global_idx));
772 self.instruction(Instruction::I32Const(0));
773 self.instruction(Instruction::I32Const(code_units as i32));
775 self.instruction(Instruction::ArrayNewData {
776 array_type_index: raw_string_type_idx,
777 array_data_index: consts_data_idx,
778 });
779 self.instruction(Instruction::I64Const(0));
780 self.instruction(Instruction::StructNew(string_type_idx));
781 }
782
783 pub fn build(self) -> Result<Function, CompilerFailure> {
786 Ok(self.build_with_lines()?.0)
787 }
788
789 pub fn build_with_lines(self) -> Result<(Function, Vec<(u64, Span)>), CompilerFailure> {
792 self.ctx.check_failure()?;
793 self.validate_state()?;
794 let mut func = Function::new(self.locals.iter().copied());
795 let mut byte_offsets: Vec<(u64, Span)> = Vec::with_capacity(self.source_mappings.len());
796 let mut next_mapping = 0usize;
797 let mut body_limit_instruction = None;
798
799 for (i, inst) in self.instructions.iter().enumerate() {
800 while next_mapping < self.source_mappings.len()
801 && self.source_mappings[next_mapping].0 == i
802 {
803 let offset = func.byte_len() as u64;
804 byte_offsets.push((offset, self.source_mappings[next_mapping].1));
805 next_mapping += 1;
806 }
807 func.instruction(inst);
808 if body_limit_instruction.is_none()
810 && !super::FunctionLimit::body_fits(func.byte_len().saturating_add(1))
811 {
812 body_limit_instruction = Some(i);
813 }
814 }
815 let end_offset = func.byte_len() as u64;
817 while next_mapping < self.source_mappings.len() {
818 byte_offsets.push((end_offset, self.source_mappings[next_mapping].1));
819 next_mapping += 1;
820 }
821 func.instruction(&Instruction::End);
822 self.ctx.check_finished_function(super::FinishedFunction {
823 body_bytes: func.byte_len(),
824 body_limit_span: body_limit_instruction
825 .and_then(|instruction| self.source_span_before(instruction)),
826 locals: self.next_local_index,
827 locals_limit_span: self.locals_limit_span,
828 });
829 self.ctx.check_failure()?;
830 Ok((func, byte_offsets))
831 }
832 fn validate_state(&self) -> Result<(), CompilerFailure> {
834 if self.scopes.len() != 1
835 || self.wasm_block_depth != 0
836 || !self.loop_contexts.is_empty()
837 || !self.finally_stack.is_empty()
838 || !self.single_evaluations.is_empty()
839 {
840 return Err(self.state_failure("unfinished emitter scope or control-flow state"));
841 }
842 let count = self
843 .parameter_types
844 .len()
845 .checked_add(self.locals.len())
846 .ok_or_else(|| self.state_failure("local count overflow"))?;
847 if crate::codegen::wasm_u32(count)? != self.next_local_index
848 || self.locals.iter().any(|(count, _)| *count != 1)
849 {
850 return Err(
851 self.state_failure("local declaration count does not match allocated slots")
852 );
853 }
854 for scope in &self.scopes {
855 for binding in scope.bindings.values() {
856 for (index, ty) in binding.declared.iter().chain(binding.shadow.iter()) {
857 self.check_local_type(*index, *ty)?;
858 }
859 }
860 }
861 if let Some(index) = self.this_local {
862 self.local_type(index)?;
863 }
864 if let Some(index) = self.call_receiver {
865 self.local_type(index)?;
866 }
867 for (index, _) in self.runtime_type_params.values() {
868 self.local_type(*index)?;
869 }
870 self.validate_instructions()
871 }
872
873 fn validate_instructions(&self) -> Result<(), CompilerFailure> {
874 let mut blocks = Vec::new();
875 for (position, instruction) in self.instructions.iter().enumerate() {
876 let failure = |message| {
877 let error = crate::codegen::internal_failure(message);
878 self.source_span_before(position)
879 .map_or(error.clone(), |span| error.with_span(span))
880 };
881 match instruction {
882 Instruction::LocalGet(index)
883 | Instruction::LocalSet(index)
884 | Instruction::LocalTee(index) => {
885 self.local_type(*index)
886 .map_err(|_| failure("instruction uses an unallocated local"))?;
887 }
888 Instruction::Block(_) | Instruction::Loop(_) => blocks.push(false),
889 Instruction::TryTable(_, catches) => {
890 for catch in catches.iter() {
891 let (wasm_encoder::Catch::One { label, .. }
892 | wasm_encoder::Catch::OneRef { label, .. }
893 | wasm_encoder::Catch::All { label }
894 | wasm_encoder::Catch::AllRef { label }) = catch;
895 if u64::from(*label) > blocks.len() as u64 {
896 return Err(failure("catch branch depth exceeds active blocks"));
897 }
898 }
899 blocks.push(false);
900 }
901 Instruction::If(_) => blocks.push(true),
902 Instruction::Else => {
903 let block = blocks
904 .last_mut()
905 .ok_or_else(|| failure("else without an if block"))?;
906 if !*block {
907 return Err(failure("else without an unmatched if block"));
908 }
909 *block = false;
910 }
911 Instruction::End => {
912 blocks
913 .pop()
914 .ok_or_else(|| failure("end without an active block"))?;
915 }
916 Instruction::Br(depth)
917 | Instruction::BrIf(depth)
918 | Instruction::BrOnNull(depth)
919 | Instruction::BrOnNonNull(depth)
920 | Instruction::BrOnCast {
921 relative_depth: depth,
922 ..
923 }
924 | Instruction::BrOnCastFail {
925 relative_depth: depth,
926 ..
927 }
928 | Instruction::BrOnCastDescEq {
929 relative_depth: depth,
930 ..
931 }
932 | Instruction::BrOnCastDescEqFail {
933 relative_depth: depth,
934 ..
935 } => {
936 if u64::from(*depth) > blocks.len() as u64 {
938 return Err(failure("branch depth exceeds active blocks"));
939 }
940 }
941 Instruction::BrTable(depths, default)
942 if depths
943 .iter()
944 .chain(std::iter::once(default))
945 .any(|depth| u64::from(*depth) > blocks.len() as u64) =>
946 {
947 return Err(failure("branch table depth exceeds active blocks"));
948 }
949 _ => {}
950 }
951 }
952 if !blocks.is_empty() {
953 return Err(self.state_failure("unclosed Wasm blocks"));
954 }
955 Ok(())
956 }
957
958 fn state_failure(&self, message: impl Into<String>) -> CompilerFailure {
959 let failure = crate::codegen::internal_failure(message);
960 self.last_source_span()
961 .map_or(failure.clone(), |span| failure.with_span(span))
962 }
963
964 fn local_type(&self, index: u32) -> Result<ValType, CompilerFailure> {
965 let index = usize::try_from(index)
966 .map_err(|_| self.state_failure("local index does not fit usize"))?;
967 if let Some(ty) = self.parameter_types.get(index) {
968 return Ok(*ty);
969 }
970 index
971 .checked_sub(self.parameter_types.len())
972 .and_then(|offset| self.locals.get(offset))
973 .map(|(_, ty)| *ty)
974 .ok_or_else(|| self.state_failure("local index is not allocated"))
975 }
976
977 fn check_local_type(&self, index: u32, ty: ValType) -> Result<(), CompilerFailure> {
978 if self.local_type(index)? != ty {
979 return Err(self.state_failure("local slot type does not match its metadata"));
980 }
981 Ok(())
982 }
983}
984
985pub(crate) fn emit_const_string_by_text(
990 emitter: &mut FunctionEmitter,
991 ctx: &CodegenCtx,
992 text: &str,
993) -> Result<(), crate::compiler_error::CompilerFailure> {
994 let pool_idx = ctx.strings.lookup_text(text).ok_or_else(|| {
995 crate::codegen::internal_failure(format!("`{text}` was not interned by the string pool"))
996 })?;
997 emit_pooled_string(emitter, ctx, pool_idx)
998}
999
1000pub(crate) fn emit_pooled_string(
1002 emitter: &mut FunctionEmitter,
1003 ctx: &CodegenCtx,
1004 pool_idx: usize,
1005) -> Result<(), crate::compiler_error::CompilerFailure> {
1006 use crate::codegen::internal_failure;
1007 let code_units = ctx.strings.code_units(pool_idx)?;
1008 let string_type_idx = ctx
1009 .symbols
1010 .string_type_idx()
1011 .ok_or_else(|| internal_failure("the $string intrinsic is not registered"))?;
1012 let raw_string_type_idx = ctx
1013 .symbols
1014 .raw_string_type_idx()
1015 .ok_or_else(|| internal_failure("the $rawString intrinsic is not registered"))?;
1016 let vtable_global_idx = ctx
1017 .symbols
1018 .prelude_global_idx("string_vtable")
1019 .ok_or_else(|| internal_failure("string_vtable is not imported from the prelude"))?;
1020 emitter.emit_const_string(
1021 string_type_idx,
1022 raw_string_type_idx,
1023 vtable_global_idx,
1024 crate::codegen::wasm_u32(pool_idx)?,
1025 code_units,
1026 );
1027 Ok(())
1028}
1029
1030pub(crate) fn emit_inline_string_literal(
1033 emitter: &mut FunctionEmitter,
1034 ctx: &CodegenCtx,
1035 text: &str,
1036) -> Result<(), crate::compiler_error::CompilerFailure> {
1037 use crate::codegen::internal_failure;
1038 let string_type_idx = ctx
1039 .symbols
1040 .string_type_idx()
1041 .ok_or_else(|| internal_failure("the $string intrinsic is not registered"))?;
1042 let raw_string_type_idx = ctx
1043 .symbols
1044 .raw_string_type_idx()
1045 .ok_or_else(|| internal_failure("the $rawString intrinsic is not registered"))?;
1046 let vtable_global_idx = ctx
1047 .symbols
1048 .prelude_global_idx("string_vtable")
1049 .ok_or_else(|| internal_failure("string_vtable is not imported from the prelude"))?;
1050 emitter.instruction(Instruction::GlobalGet(vtable_global_idx));
1051 let mut len = 0usize;
1052 for unit in text.encode_utf16() {
1053 emitter.instruction(Instruction::I32Const(i32::from(unit)));
1054 len = len.saturating_add(1);
1055 }
1056 emitter.instruction(Instruction::ArrayNewFixed {
1057 array_type_index: raw_string_type_idx,
1058 array_size: crate::codegen::wasm_u32(len)?,
1059 });
1060 emitter.instruction(Instruction::I64Const(0));
1061 emitter.instruction(Instruction::StructNew(string_type_idx));
1062 Ok(())
1063}
1064
1065pub fn emit_function(
1066 generics: &[String],
1067 ctx: &CodegenCtx<'_>,
1068 params: &[crate::TypedParam],
1069 body: crate::StmtId,
1070 return_type: &crate::Type,
1071) -> Result<(Function, Vec<(u64, Span)>), crate::compiler_error::CompilerFailure> {
1072 let mut wasm_params: Vec<(Ident, ValType)> = params
1073 .iter()
1074 .map(|p| Ok((p.name.clone(), ctx.symbols.value_type(&p.ty)?)))
1075 .collect::<Result<_, crate::compiler_error::CompilerFailure>>()?;
1076 if !generics.is_empty() {
1077 wasm_params.push((
1078 Ident {
1079 name: "$types".into(),
1080 span: Span::at(ctx.file),
1081 },
1082 crate::codegen::runtime_descriptors::environment_type(ctx.symbols)?,
1083 ));
1084 }
1085 let mut emitter = FunctionEmitter::new(ctx, &wasm_params)?;
1086 crate::codegen::runtime_descriptors::bind(
1087 &mut emitter,
1088 generics,
1089 crate::codegen::wasm_u32(params.len())?,
1090 )?;
1091
1092 emitter.set_return_target(ReturnTarget::Declared(return_type.clone()));
1093 let typed_slots: Vec<u32> = (0..crate::codegen::wasm_u32(params.len())?).collect();
1094 emitter.prepare_parameter_initialization(Some(body))?;
1095 emitter.emit_boxed_param_prologue(params, &typed_slots)?;
1096 stmt::emit_statement(&mut emitter, ctx, body)?;
1097 emit_body_end(&mut emitter, ctx, return_type)?;
1098 emitter.build_with_lines()
1099}
1100
1101pub fn emit_closure_function(
1104 ctx: &CodegenCtx<'_>,
1105 meta: &crate::codegen::closures::ClosureMeta,
1106) -> Result<(Function, Vec<(u64, Span)>), crate::compiler_error::CompilerFailure> {
1107 let signature = crate::codegen::closures::classify(&meta.signature)?;
1108 if usize::from(signature.arity) != meta.params.len() {
1109 return Err(crate::codegen::internal_failure(
1110 "closure body disagrees with its registered signature",
1111 ));
1112 }
1113 let intrinsics = ctx
1114 .symbols
1115 .intrinsic_type_indices()
1116 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
1117 let object_ref = ValType::Ref(wasm_encoder::RefType {
1118 nullable: true,
1119 heap_type: wasm_encoder::HeapType::Concrete(intrinsics.object),
1120 });
1121 let any_ref = ValType::Ref(wasm_encoder::RefType {
1122 nullable: false,
1123 heap_type: wasm_encoder::HeapType::ANY,
1124 });
1125
1126 let env_name = Ident {
1127 name: "$__env__".to_string(),
1128 span: Span::at(ctx.file),
1129 };
1130 let mut wasm_params: Vec<(Ident, ValType)> = vec![(env_name, any_ref)];
1131 for p in &meta.params {
1132 wasm_params.push((p.name.clone(), object_ref));
1133 }
1134 let mut emitter = FunctionEmitter::new(ctx, &wasm_params)?;
1135
1136 let env_type_idx = ctx.symbols.env_type_idx(meta.expr_id).ok_or_else(|| {
1137 crate::codegen::internal_failure("env type registered for every closure expression")
1138 })?;
1139 let env_typed_val = ValType::Ref(wasm_encoder::RefType {
1140 nullable: false,
1141 heap_type: wasm_encoder::HeapType::Concrete(env_type_idx),
1142 });
1143 let env_typed_local = emitter.add_anonymous_local(env_typed_val)?;
1144 if meta.this_type.is_some() {
1145 crate::codegen::this_binding::load_receiver(&mut emitter, ctx)?;
1146 }
1147 emitter.instructions.push(Instruction::LocalGet(0));
1148 if meta.this_type.is_some() {
1149 emitter.instruction(Instruction::RefCastNonNull(
1150 wasm_encoder::HeapType::Concrete(
1151 ctx.symbols
1152 .this_environment_type
1153 .ok_or_else(|| crate::codegen::internal_failure("this environment"))?,
1154 ),
1155 ));
1156 emitter.instruction(Instruction::StructGet {
1157 struct_type_index: ctx
1158 .symbols
1159 .this_environment_type
1160 .ok_or_else(|| crate::codegen::internal_failure("this environment"))?,
1161 field_index: 0,
1162 });
1163 }
1164 if crate::codegen::call_arguments::typed_metadata(&meta.params).is_some() {
1165 crate::codegen::call_arguments::unwrap(&mut emitter, ctx)?;
1166 }
1167 emitter.instructions.push(Instruction::RefCastNonNull(
1168 wasm_encoder::HeapType::Concrete(env_type_idx),
1169 ));
1170 emitter
1171 .instructions
1172 .push(Instruction::LocalSet(env_typed_local));
1173
1174 if let Some(name) = &meta.self_name {
1175 let slot = emitter.define_local(name, ctx.symbols.value_type(&meta.signature)?)?;
1176 emitter.instruction(Instruction::LocalGet(env_typed_local));
1177 emitter.instruction(Instruction::StructGet {
1178 struct_type_index: env_type_idx,
1179 field_index: crate::codegen::wasm_u32(
1180 meta.captured
1181 .len()
1182 .checked_add(usize::from(!meta.runtime_generics.is_empty()))
1183 .ok_or_else(|| {
1184 crate::codegen::internal_failure("capture field index overflow")
1185 })?,
1186 )?,
1187 });
1188 cast::emit_cast_to(&mut emitter, ctx, &meta.signature)?;
1189 emitter.instruction(Instruction::LocalSet(slot));
1190 }
1191 if !meta.runtime_generics.is_empty() {
1192 let types = emitter.add_anonymous_local(
1193 crate::codegen::runtime_descriptors::environment_type(ctx.symbols)?,
1194 )?;
1195 emitter.instruction(Instruction::LocalGet(env_typed_local));
1196 emitter.instruction(Instruction::StructGet {
1197 struct_type_index: env_type_idx,
1198 field_index: crate::codegen::wasm_u32(meta.captured.len())?,
1199 });
1200 emitter.instruction(Instruction::LocalSet(types));
1201 crate::codegen::runtime_descriptors::bind(&mut emitter, &meta.runtime_generics, types)?;
1202 }
1203 let mut typed_slots: Vec<u32> = Vec::with_capacity(meta.params.len());
1204 for (i, p) in meta.params.iter().enumerate() {
1205 let wasm_slot = crate::codegen::wasm_u32(i)?
1206 .checked_add(1)
1207 .ok_or_else(|| crate::codegen::internal_failure("parameter index overflow"))?;
1208 let typed_ty = ctx.symbols.value_type(&p.ty)?;
1209 let typed_local = emitter.add_anonymous_local(typed_ty)?;
1210 emitter.instructions.push(Instruction::LocalGet(wasm_slot));
1211 crate::codegen::cast_check::emit_checked_parameter_cast_on_stack(
1212 &mut emitter,
1213 ctx,
1214 &crate::Type::Unknown,
1215 &p.ty,
1216 )?;
1217 emitter
1218 .instructions
1219 .push(Instruction::LocalSet(typed_local));
1220 emitter.rebind_in_innermost_scope(&p.name.name, typed_local, typed_ty)?;
1221 typed_slots.push(typed_local);
1222 }
1223
1224 for (i, c) in meta.captured.iter().enumerate() {
1226 let local_ty = if c.boxed {
1227 let box_idx = ctx.symbols.box_type_idx(&c.ty)?.ok_or_else(|| {
1228 crate::codegen::internal_failure("box type registered for every boxed capture")
1229 })?;
1230 ValType::Ref(wasm_encoder::RefType {
1231 nullable: false,
1232 heap_type: wasm_encoder::HeapType::Concrete(box_idx),
1233 })
1234 } else {
1235 ctx.symbols.value_type(&c.ty)?
1236 };
1237 let captured_local = emitter.define_local(&c.name, local_ty)?;
1238 if c.boxed && !ctx.ta.parameter_default_prologues.is_empty() {
1240 emitter.uninitialized_cells.insert(captured_local);
1241 }
1242 emitter
1243 .instructions
1244 .push(Instruction::LocalGet(env_typed_local));
1245 emitter.instructions.push(Instruction::StructGet {
1246 struct_type_index: env_type_idx,
1247 field_index: crate::codegen::wasm_u32(i)?,
1248 });
1249 if !c.boxed && matches!(c.ty.peel(), crate::Type::ClassRef { .. }) {
1253 cast::emit_cast_to(&mut emitter, ctx, &c.ty)?;
1254 } else if !c.boxed
1255 && let ValType::Ref(local_ref) = local_ty
1256 && !local_ref.nullable
1257 {
1258 emitter.instructions.push(Instruction::RefAsNonNull);
1263 }
1264 emitter
1265 .instructions
1266 .push(Instruction::LocalSet(captured_local));
1267 if c.name.name == crate::typechecker::capture::THIS_BINDING {
1271 emitter.set_this_local(captured_local)?;
1272 emitter.dynamic_this = c.ty == crate::Type::Unknown;
1273 }
1274 }
1275
1276 emitter.prepare_parameter_initialization(match meta.body {
1277 crate::ClosureBody::Block(body) => Some(body),
1278 crate::ClosureBody::Expr(_) => None,
1279 })?;
1280 emitter.emit_boxed_param_prologue(&meta.params, &typed_slots)?;
1281
1282 emitter.set_return_target(closure_return_target(ctx, &meta.return_type)?);
1283
1284 match meta.body {
1285 crate::ClosureBody::Expr(e) => {
1286 expr::emit_expr(&mut emitter, ctx, e)?;
1287 cast::emit_coerce_to_return_slot(
1288 &mut emitter,
1289 ctx,
1290 &ctx.ta
1291 .try_expr(e)
1292 .map_err(crate::codegen::arena_failure)?
1293 .ty,
1294 )?;
1295 }
1296 crate::ClosureBody::Block(b) => {
1297 stmt::emit_statement(&mut emitter, ctx, b)?;
1298 emit_body_end(&mut emitter, ctx, &meta.return_type)?;
1299 }
1300 }
1301
1302 emitter.build_with_lines()
1303}
1304
1305fn closure_return_target(
1311 ctx: &CodegenCtx<'_>,
1312 ret: &Type,
1313) -> Result<ReturnTarget, CompilerFailure> {
1314 let _ = ret;
1315 let intrinsics = ctx
1316 .symbols
1317 .intrinsic_type_indices()
1318 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1319 Ok(ReturnTarget::Slot(ValType::Ref(wasm_encoder::RefType {
1320 nullable: true,
1321 heap_type: wasm_encoder::HeapType::Concrete(intrinsics.object),
1322 })))
1323}
1324
1325pub(crate) fn emit_body_end(
1333 emitter: &mut FunctionEmitter<'_>,
1334 ctx: &CodegenCtx<'_>,
1335 return_type: &Type,
1336) -> Result<(), crate::compiler_error::CompilerFailure> {
1337 let accepts_undefined = return_type.is_void()
1338 || matches!(return_type.peel(), Type::Unknown | Type::Undefined)
1339 || matches!(return_type.peel(), Type::Union(members) if members.iter().any(|member| matches!(member.peel(), Type::Undefined | Type::Void)));
1340 if !accepts_undefined {
1341 use crate::codegen::throw;
1342 if throw::body_end_may_be_reached(ctx.ta) {
1343 throw::emit_type_error_throw(emitter, ctx, throw::MISSING_RETURN_VALUE_MESSAGE);
1344 }
1345 emitter.instruction(Instruction::Unreachable);
1346 return Ok(());
1347 }
1348 expr::emit_undefined(emitter, ctx)?;
1349 cast::emit_coerce_to_return_slot(emitter, ctx, &Type::Undefined)?;
1350 emitter.instruction(Instruction::Return);
1351 Ok(())
1352}
1353
1354#[cfg(test)]
1355mod tests {
1356 use super::{ExprContext, FunctionEmitter};
1357 use crate::codegen::{
1358 CodegenCtx, SymbolTable, bigint_pool::BigIntPool, string_pool::StringPool,
1359 };
1360 use crate::{Ident, LineIndex, Span, TypedAst};
1361 use wasm_encoder::{
1362 BlockType, ExportKind, ExportSection, Function, FunctionSection, Instruction, Module,
1363 TypeSection, ValType,
1364 };
1365
1366 struct Fixture {
1367 ta: TypedAst,
1368 strings: StringPool,
1369 bigints: BigIntPool,
1370 symbols: SymbolTable,
1371 line_index: LineIndex,
1372 validator_bodies: crate::codegen::recursive_validators::ValidatorBodies,
1373 type_info: crate::TypeInfoTable,
1374 }
1375
1376 fn fixture() -> Fixture {
1377 Fixture {
1378 ta: TypedAst::new(),
1379 strings: StringPool::default(),
1380 bigints: BigIntPool::default(),
1381 symbols: SymbolTable::default(),
1382 line_index: LineIndex::new("").unwrap(),
1383 validator_bodies: crate::codegen::recursive_validators::ValidatorBodies::collect(
1384 &TypedAst::new(),
1385 &[],
1386 ),
1387 type_info: crate::TypeInfoTable::default(),
1388 }
1389 }
1390
1391 fn cx_of<'a>(f: &'a Fixture) -> CodegenCtx<'a> {
1392 CodegenCtx {
1393 ta: &f.ta,
1394 strings: &f.strings,
1395 bigints: &f.bigints,
1396 symbols: &f.symbols,
1397 source: "",
1398 line_index: &f.line_index,
1399 file: crate::FileId(0),
1400 validator_bodies: &f.validator_bodies,
1401 type_info: &f.type_info,
1402 package_string_global_idx: None,
1403 failure: std::cell::Cell::new(None),
1404 validator_steps_left: std::cell::Cell::new(
1405 crate::compiler_limits::MAX_INLINE_VALIDATOR_STEPS,
1406 ),
1407 validator_root: std::cell::Cell::new(None),
1408 check_is_standalone: std::cell::Cell::new(false),
1409 }
1410 }
1411
1412 fn ident(name: &str) -> Ident {
1413 Ident {
1414 name: name.to_string(),
1415 span: Span::at(crate::FileId(0)),
1416 }
1417 }
1418
1419 fn ident_param(name: &str, ty: ValType) -> (Ident, ValType) {
1420 (ident(name), ty)
1421 }
1422
1423 fn validate_in_module(f: wasm_encoder::Function, params: Vec<ValType>, results: Vec<ValType>) {
1424 let mut module = Module::new();
1425 let mut types = TypeSection::new();
1426 types.ty().function(params, results);
1427 module.section(&types);
1428 let mut functions = FunctionSection::new();
1429 functions.function(0);
1430 module.section(&functions);
1431 let mut exports = ExportSection::new();
1432 exports.export("f", ExportKind::Func, 0);
1433 module.section(&exports);
1434 let mut code = wasm_encoder::CodeSection::new();
1435 code.function(&f);
1436 module.section(&code);
1437 wasmparser::Validator::new()
1438 .validate_all(&module.finish())
1439 .expect("module validates");
1440 }
1441
1442 #[test]
1443 fn body_size_is_the_encoded_body_size() {
1444 let f = fixture();
1445 let cx = cx_of(&f);
1446 let mut emitter = FunctionEmitter::new(&cx, &[ident_param("p", ValType::I32)]).unwrap();
1447 assert_eq!(emitter.body_size(), 2, "no locals, then `end`");
1448 let any = ValType::Ref(wasm_encoder::RefType {
1449 nullable: true,
1450 heap_type: wasm_encoder::HeapType::ANY,
1451 });
1452 let concrete = ValType::Ref(wasm_encoder::RefType {
1453 nullable: false,
1454 heap_type: wasm_encoder::HeapType::Concrete(300),
1455 });
1456 for i in 0..200 {
1457 emitter
1458 .add_anonymous_local([ValType::F64, any, concrete][i % 3])
1459 .unwrap();
1460 emitter.instruction(Instruction::I32Const(i as i32 * 1_000));
1461 emitter.instruction(Instruction::Drop);
1462 if i % 50 == 0 {
1463 let _ = emitter.body_size();
1464 }
1465 }
1466 let size = emitter.body_size();
1467 assert_eq!(size, emitter.build().unwrap().byte_len());
1468 }
1469
1470 #[test]
1471 fn empty_function_validates() {
1472 let f = fixture();
1473 let cx = cx_of(&f);
1474 let emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1475 let func = emitter.build().unwrap();
1476 validate_in_module(func, vec![], vec![]);
1477 }
1478
1479 #[test]
1480 fn params_become_named_locals_at_low_indices() {
1481 let f = fixture();
1482 let cx = cx_of(&f);
1483 let emitter = FunctionEmitter::new(
1484 &cx,
1485 &[
1486 ident_param("a", ValType::F64),
1487 ident_param("b", ValType::F64),
1488 ],
1489 )
1490 .unwrap();
1491 assert_eq!(emitter.local_slot("a"), Some(0));
1492 assert_eq!(emitter.local_slot("b"), Some(1));
1493 }
1494
1495 #[test]
1496 fn define_local_returns_next_index_after_params() {
1497 let f = fixture();
1498 let cx = cx_of(&f);
1499 let mut emitter = FunctionEmitter::new(
1500 &cx,
1501 &[
1502 ident_param("a", ValType::F64),
1503 ident_param("b", ValType::F64),
1504 ],
1505 )
1506 .unwrap();
1507 let c = emitter.define_local(&ident("c"), ValType::I32).unwrap();
1508 assert_eq!(c, 2);
1509 assert_eq!(emitter.local_slot("c"), Some(2));
1510 }
1511
1512 #[test]
1513 fn anonymous_local_is_unnamed() {
1514 let f = fixture();
1515 let cx = cx_of(&f);
1516 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1517 let idx = emitter.add_anonymous_local(ValType::I32).unwrap();
1518 assert_eq!(idx, 0);
1519 assert!(emitter.local_slot("anything").is_none());
1520 }
1521
1522 #[test]
1523 fn nested_scopes_resolve_innermost_first() {
1524 let f = fixture();
1525 let cx = cx_of(&f);
1526 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1527 let outer = emitter.define_local(&ident("x"), ValType::I32).unwrap();
1528 emitter.push_scope();
1529 let inner = emitter.define_local(&ident("x"), ValType::I32).unwrap();
1530 assert_eq!(emitter.local_slot("x"), Some(inner));
1531 emitter.pop_scope().unwrap();
1532 assert_eq!(emitter.local_slot("x"), Some(outer));
1533 }
1534
1535 #[test]
1536 fn pop_scope_drops_inner_locals() {
1537 let f = fixture();
1538 let cx = cx_of(&f);
1539 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1540 emitter.push_scope();
1541 emitter.define_local(&ident("temp"), ValType::I32).unwrap();
1542 assert!(emitter.local_slot("temp").is_some());
1543 emitter.pop_scope().unwrap();
1544 assert!(emitter.local_slot("temp").is_none());
1545 }
1546
1547 #[test]
1548 fn block_depth_tracks_emit_end() {
1549 let f = fixture();
1550 let cx = cx_of(&f);
1551 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1552 assert_eq!(emitter.wasm_block_depth, 0);
1553 emitter.emit_block(BlockType::Empty);
1554 assert_eq!(emitter.wasm_block_depth, 1);
1555 emitter.emit_block(BlockType::Empty);
1556 assert_eq!(emitter.wasm_block_depth, 2);
1557 emitter.emit_end();
1558 assert_eq!(emitter.wasm_block_depth, 1);
1559 emitter.emit_end();
1560 assert_eq!(emitter.wasm_block_depth, 0);
1561 }
1562
1563 #[test]
1564 fn break_label_for_innermost_loop() {
1565 let f = fixture();
1566 let cx = cx_of(&f);
1567 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1568 emitter.emit_while_open();
1569 assert_eq!(emitter.break_label().unwrap(), 1);
1570 }
1571
1572 #[test]
1573 fn continue_label_for_innermost_loop() {
1574 let f = fixture();
1575 let cx = cx_of(&f);
1576 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1577 emitter.emit_while_open();
1578 assert_eq!(emitter.continue_label().unwrap(), 0);
1579 }
1580
1581 #[test]
1582 fn nested_loops_break_label_targets_inner() {
1583 let f = fixture();
1584 let cx = cx_of(&f);
1585 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1586 emitter.emit_while_open(); emitter.emit_while_open(); assert_eq!(emitter.break_label().unwrap(), 1);
1589 assert_eq!(emitter.continue_label().unwrap(), 0);
1590 emitter.emit_while_close();
1591 assert_eq!(emitter.break_label().unwrap(), 1);
1592 assert_eq!(emitter.continue_label().unwrap(), 0);
1593 emitter.emit_while_close();
1594 }
1595
1596 #[test]
1597 fn build_produces_valid_function() {
1598 let f = fixture();
1599 let cx = cx_of(&f);
1600 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1601 emitter.instruction(Instruction::Unreachable);
1602 let func = emitter.build().unwrap();
1603 validate_in_module(func, vec![], vec![ValType::F64]);
1604 }
1605
1606 #[test]
1607 fn build_emits_declared_locals_in_order() {
1608 let f = fixture();
1609 let cx = cx_of(&f);
1610 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1611 emitter.add_anonymous_local(ValType::I32).unwrap();
1612 emitter.add_anonymous_local(ValType::F64).unwrap();
1613 let func = emitter.build().unwrap();
1614 validate_in_module(func, vec![], vec![]);
1615 }
1616
1617 #[test]
1618 fn emit_const_string_emits_vtable_loaded_struct_new() {
1619 use wasm_encoder::Encode;
1620 let f = fixture();
1621 let cx = cx_of(&f);
1622 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1623 emitter.emit_const_string(
1624 3, 0,
1625 5, 3, 5,
1626 );
1627 let actual = emitter.build().unwrap();
1628 let mut actual_bytes = Vec::new();
1629 actual.encode(&mut actual_bytes);
1630
1631 let mut expected = Function::new(std::iter::empty());
1632 expected.instruction(&Instruction::GlobalGet(5));
1633 expected.instruction(&Instruction::I32Const(0));
1634 expected.instruction(&Instruction::I32Const(5));
1635 expected.instruction(&Instruction::ArrayNewData {
1636 array_type_index: 0,
1637 array_data_index: 3,
1638 });
1639 expected.instruction(&Instruction::I64Const(0));
1640 expected.instruction(&Instruction::StructNew(3));
1641 expected.instruction(&Instruction::End);
1642 let mut expected_bytes = Vec::new();
1643 expected.encode(&mut expected_bytes);
1644
1645 assert_eq!(actual_bytes, expected_bytes);
1646 }
1647
1648 #[test]
1649 fn record_span_keeps_mappings_in_order() {
1650 let f = fixture();
1651 let cx = cx_of(&f);
1652 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1653
1654 emitter.record_span(Span::new(crate::FileId(0), 0, 5).unwrap());
1655 emitter.instruction(Instruction::I32Const(1));
1656 emitter.record_span(Span::new(crate::FileId(0), 7, 9).unwrap());
1657 emitter.instruction(Instruction::I32Const(2));
1658 emitter.record_span(Span::new(crate::FileId(0), 11, 15).unwrap());
1659 emitter.instruction(Instruction::I32Add);
1660
1661 assert_eq!(
1662 emitter.source_mappings,
1663 vec![
1664 (0, Span::new(crate::FileId(0), 0, 5).unwrap()),
1665 (1, Span::new(crate::FileId(0), 7, 9).unwrap()),
1666 (2, Span::new(crate::FileId(0), 11, 15).unwrap()),
1667 ]
1668 );
1669 }
1670
1671 #[test]
1672 fn expr_context_distinct_variants() {
1673 assert_ne!(ExprContext::Value, ExprContext::Statement);
1674 }
1675 fn param(name: &str, ty: crate::Type, boxed: bool) -> crate::TypedParam {
1676 crate::TypedParam {
1677 name: ident(name),
1678 ty,
1679 optional: false,
1680 boxed,
1681 rest: false,
1682 default: None,
1683 }
1684 }
1685
1686 fn assert_internal<T>(
1687 result: Result<T, crate::compiler_error::CompilerFailure>,
1688 message: &str,
1689 ) {
1690 match result {
1691 Err(crate::compiler_error::CompilerFailure::Internal {
1692 stage,
1693 message: actual,
1694 ..
1695 }) => {
1696 assert_eq!(stage, crate::compiler_error::CompilerStage::Codegen);
1697 assert!(actual.contains(message), "{actual}");
1698 }
1699 _ => panic!("expected internal failure containing {message}"),
1700 }
1701 }
1702
1703 #[test]
1704 fn root_scope_and_missing_locals_are_checked() {
1705 let f = fixture();
1706 let cx = cx_of(&f);
1707 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1708 assert_internal(emitter.require_local_slot("missing"), "not defined");
1709 assert_internal(emitter.write_slot("missing"), "not defined");
1710 assert_internal(emitter.pop_scope(), "root");
1711 assert_eq!(emitter.scopes.len(), 1);
1712 validate_in_module(
1713 FunctionEmitter::new(&cx, &[]).unwrap().build().unwrap(),
1714 vec![],
1715 vec![],
1716 );
1717 }
1718
1719 #[test]
1720 fn local_metadata_and_evaluation_marks_are_checked() {
1721 let f = fixture();
1722 let cx = cx_of(&f);
1723 let mut emitter = FunctionEmitter::new(&cx, &[ident_param("p", ValType::I32)]).unwrap();
1724 assert_internal(
1725 emitter.rebind_in_innermost_scope("p", 1, ValType::I32),
1726 "not allocated",
1727 );
1728 assert_internal(emitter.install_narrow_shadow("p", 0, ValType::F64), "type");
1729 assert_internal(emitter.set_this_local(1), "not allocated");
1730 assert_internal(emitter.end_single_evaluations(1), "mark");
1731 assert_eq!(emitter.local_slot("p"), Some(0));
1732 assert_eq!(emitter.narrowed_read_slot("p"), Some((0, ValType::I32)));
1733 emitter.scopes[0].define("bad".into(), 8, ValType::I32);
1734 assert_internal(emitter.build(), "not allocated");
1735 }
1736
1737 #[test]
1738 fn parameter_prologue_rejects_lengths_types_and_wrong_binding_slots() {
1739 let f = fixture();
1740 let cx = cx_of(&f);
1741 let mut emitter = FunctionEmitter::new(
1742 &cx,
1743 &[
1744 ident_param("p", ValType::F64),
1745 ident_param("q", ValType::I32),
1746 ],
1747 )
1748 .unwrap();
1749 let params = [param("p", crate::Type::Number, false)];
1750 assert_internal(emitter.emit_boxed_param_prologue(¶ms, &[]), "one entry");
1751 assert_internal(
1752 emitter.emit_boxed_param_prologue(¶ms, &[9]),
1753 "not allocated",
1754 );
1755 assert_internal(emitter.emit_boxed_param_prologue(¶ms, &[1]), "type");
1756 let wrong_binding = [param("q", crate::Type::Number, false)];
1757 assert_internal(
1758 emitter.emit_boxed_param_prologue(&wrong_binding, &[0]),
1759 "binding",
1760 );
1761 emitter.emit_boxed_param_prologue(¶ms, &[0]).unwrap();
1762 assert!(emitter.instructions.is_empty());
1763 assert_eq!(emitter.local_count(), 2);
1764 }
1765
1766 #[test]
1767 fn boxed_parameter_registration_failure_emits_no_prologue() {
1768 let f = fixture();
1769 let cx = cx_of(&f);
1770 let mut emitter = FunctionEmitter::new(&cx, &[ident_param("p", ValType::F64)]).unwrap();
1771 let span = Span::new(crate::FileId(0), 1, 3).unwrap();
1772 emitter.record_span(span);
1773 let error = emitter
1774 .emit_boxed_param_prologue(&[param("p", crate::Type::Number, true)], &[0])
1775 .unwrap_err();
1776 assert!(
1777 matches!(error, crate::compiler_error::CompilerFailure::Internal { span: Some(actual), .. } if actual == span)
1778 );
1779 assert!(emitter.instructions.is_empty());
1780 assert_eq!(emitter.local_count(), 1);
1781 assert_eq!(emitter.local_slot("p"), Some(0));
1782 }
1783
1784 #[test]
1785 fn local_capacity_and_counter_overflow_fail_before_allocation() {
1786 let f = fixture();
1787 let cx = cx_of(&f);
1788 let params = vec![
1789 ident_param("p", ValType::I32);
1790 crate::compiler_limits::MAX_FUNCTION_LOCALS as usize + 1
1791 ];
1792 assert!(matches!(
1793 FunctionEmitter::new(&cx, ¶ms),
1794 Err(crate::compiler_error::CompilerFailure::Limit { .. })
1795 ));
1796 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1797 emitter.next_local_index = crate::compiler_limits::MAX_FUNCTION_LOCALS;
1798 assert!(matches!(
1799 emitter.add_anonymous_local(ValType::I32),
1800 Err(crate::compiler_error::CompilerFailure::Limit { .. })
1801 ));
1802 assert!(emitter.locals.is_empty());
1803 emitter.next_local_index = u32::MAX;
1804 assert_internal(emitter.add_anonymous_local(ValType::I32), "overflow");
1805 assert!(emitter.locals.is_empty());
1806 }
1807
1808 #[test]
1809 fn invalid_block_operations_discard_the_body() {
1810 let f = fixture();
1811 let cx = cx_of(&f);
1812 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1813 emitter.emit_end();
1814 assert_eq!(emitter.wasm_block_depth, 0);
1815 assert_internal(emitter.build(), "end without");
1816 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1817 emitter.wasm_block_depth = u32::MAX;
1818 emitter.emit_while_open();
1819 assert_internal(emitter.build(), "overflow");
1820 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1821 emitter.emit_switch_open();
1822 emitter.emit_while_close();
1823 assert_internal(emitter.build(), "mismatched loop");
1824 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1825 emitter.emit_if(BlockType::Empty);
1826 assert_internal(emitter.build(), "unfinished");
1827 validate_in_module(
1828 FunctionEmitter::new(&cx, &[]).unwrap().build().unwrap(),
1829 vec![],
1830 vec![],
1831 );
1832 }
1833
1834 #[test]
1835 fn labels_reject_missing_or_stale_targets_and_continue_skips_switch() {
1836 let f = fixture();
1837 let cx = cx_of(&f);
1838 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1839 assert_internal(emitter.break_label(), "outside loop");
1840 assert_internal(emitter.continue_label(), "outside loop");
1841 assert_internal(emitter.return_label(), "outside the active");
1842 emitter.emit_while_open();
1843 emitter.emit_switch_open();
1844 assert_eq!(emitter.break_label().unwrap(), 0);
1845 assert_eq!(emitter.continue_label().unwrap(), 1);
1846 emitter.emit_switch_close();
1847 emitter.loop_contexts.last_mut().unwrap().continue_depth = 20;
1848 assert_internal(emitter.continue_label(), "outside the active");
1849 }
1850
1851 #[test]
1852 fn malformed_raw_instructions_never_return_function_bytes() {
1853 let f = fixture();
1854 let cx = cx_of(&f);
1855 for instruction in [
1856 Instruction::LocalGet(0),
1857 Instruction::Br(1),
1858 Instruction::Else,
1859 Instruction::End,
1860 Instruction::BrTable(std::borrow::Cow::Owned(vec![1]), 0),
1861 Instruction::TryTable(
1862 BlockType::Empty,
1863 std::borrow::Cow::Owned(vec![wasm_encoder::Catch::AllRef { label: 1 }]),
1864 ),
1865 ] {
1866 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1867 emitter.instruction(instruction);
1868 assert!(matches!(
1869 emitter.build(),
1870 Err(crate::compiler_error::CompilerFailure::Internal { .. })
1871 ));
1872 }
1873 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1874 emitter.instruction(Instruction::Block(BlockType::Empty));
1875 assert_internal(emitter.build(), "unclosed");
1876 }
1877
1878 #[test]
1879 fn cast_branch_depths_are_checked_before_bytes_are_returned() {
1880 let f = fixture();
1881 let cx = cx_of(&f);
1882 let from_ref_type = wasm_encoder::RefType::ANYREF;
1883 let to_ref_type = wasm_encoder::RefType::EQREF;
1884 for instruction in [
1885 Instruction::BrOnCast {
1886 relative_depth: 1,
1887 from_ref_type,
1888 to_ref_type,
1889 },
1890 Instruction::BrOnCastFail {
1891 relative_depth: 1,
1892 from_ref_type,
1893 to_ref_type,
1894 },
1895 Instruction::BrOnCastDescEq {
1896 relative_depth: 1,
1897 from_ref_type,
1898 to_ref_type,
1899 },
1900 Instruction::BrOnCastDescEqFail {
1901 relative_depth: 1,
1902 from_ref_type,
1903 to_ref_type,
1904 },
1905 ] {
1906 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1907 emitter.instruction(instruction);
1908 assert_internal(emitter.build(), "branch depth");
1909 }
1910 }
1911
1912 #[test]
1913 fn interned_string_and_closure_result_metadata_are_fallible() {
1914 let f = fixture();
1915 let cx = cx_of(&f);
1916 let mut emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1917 assert_internal(
1918 super::emit_const_string_by_text(&mut emitter, &cx, "absent"),
1919 "not interned",
1920 );
1921 assert_internal(
1922 super::emit_pooled_string(&mut emitter, &cx, usize::MAX),
1923 "pool",
1924 );
1925 assert!(emitter.instructions.is_empty());
1926 assert_internal(
1927 super::closure_return_target(&cx, &crate::Type::Number),
1928 "intrinsics",
1929 );
1930 assert_internal(
1931 super::closure_return_target(&cx, &crate::Type::Void),
1932 "intrinsics",
1933 );
1934 }
1935
1936 #[test]
1937 fn latched_failure_prevents_finalization_and_retains_source() {
1938 let f = fixture();
1939 let cx = cx_of(&f);
1940 let emitter = FunctionEmitter::new(&cx, &[]).unwrap();
1941 let span = Span::new(crate::FileId(0), 2, 4).unwrap();
1942 cx.record_failure(
1943 crate::codegen::internal_failure("injected emitter failure").with_span(span),
1944 );
1945 let error = emitter.build_with_lines().unwrap_err();
1946 assert!(
1947 matches!(error, crate::compiler_error::CompilerFailure::Internal { span: Some(actual), .. } if actual == span)
1948 );
1949 validate_in_module(
1950 FunctionEmitter::new(&cx, &[]).unwrap().build().unwrap(),
1951 vec![],
1952 vec![],
1953 );
1954 }
1955}