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