1use std::collections::{BTreeMap, BTreeSet};
19
20use wasm_encoder::{
21 BlockType, CodeSection, CompositeInnerType, CompositeType, ConstExpr, FieldType, Function,
22 FunctionSection, GlobalSection, GlobalType, HeapType, Instruction, RefType, StorageType,
23 StructType, SubType, TypeSection, ValType,
24};
25
26use crate::codegen::CodegenCtx;
27use crate::codegen::closures::ClosureSig;
28use crate::codegen::function_emitter::FunctionEmitter;
29use crate::codegen::imported_classes::ImportedClassLayout;
30use crate::codegen::intrinsics::IntrinsicTypeIndices;
31use crate::codegen::symbol_table::{FieldSetup, MethodSlotAbi, SymbolTable};
32use crate::codegen::{GuardedBodies, internal_failure, next_index, parameter_local, wasm_u32};
33use crate::compiler_error::CompilerFailure;
34use crate::{MangledName, TypedAst, TypedClassDecl, TypedTypeDecl};
35
36pub(crate) const UNIVERSAL_STUB_COUNT: usize = 4;
39pub(crate) const VTABLE_PARENT_SLOT: u32 = UNIVERSAL_STUB_COUNT as u32;
41pub(crate) const VTABLE_DEFAULT_JSON_SLOT: u32 = VTABLE_PARENT_SLOT + 1;
43pub(crate) const VTABLE_METHOD_SLOT_BASE: u32 = VTABLE_DEFAULT_JSON_SLOT + 1;
45
46struct MethodSlot {
51 name: String,
52 owner: MangledName,
53 sig_idx: u32,
54 param_tys: Vec<crate::Type>,
58 argument_metadata: Option<String>,
59 ret_ty: crate::Type,
60 generic: bool,
62}
63
64struct ClassLayout {
65 private_members: BTreeSet<String>,
66 generics: Vec<String>,
67 mangled: MangledName,
68 name: String,
69 name_span: crate::Span,
71 parent: Option<MangledName>,
72 fields: Vec<FieldLayout>,
74 methods: Vec<MethodSlot>,
76 own_methods: Vec<OwnMethod>,
78 adapter_slots: Vec<usize>,
83 ctor_params: Vec<crate::TypedParam>,
86 ctor_body: Option<crate::StmtId>,
87 field_setup: Vec<FieldSetup>,
91 accessor_names: Vec<String>,
95 struct_type_idx: u32,
96 vtable_type_idx: u32,
97 ctor_func_idx: u32,
98 ctor_sig_idx: u32,
99 ctor_init_func_idx: u32,
103 ctor_init_sig_idx: u32,
104 getter_func_idx: u32,
105 setter_func_idx: u32,
106 equals_func_idx: u32,
109 to_string_func_idx: u32,
112 to_json_func_idx: u32,
115 hash_func_idx: u32,
118 guarded_bodies: GuardedBodies,
119}
120
121impl ClassLayout {
122 fn payload_slots(&self) -> impl Iterator<Item = &MethodSlot> {
128 self.methods.iter().filter(|s| !s.generic)
129 }
130
131 fn payload_field_names(&self) -> Vec<super::field_names::FieldName> {
136 let mut names: Vec<_> = self
137 .fields
138 .iter()
139 .map(|f| super::field_names::FieldName {
140 name: f.name.clone(),
141 optional: f.optional,
142 is_accessor: false,
143 is_private: self.private_members.contains(&f.name),
144 })
145 .collect();
146 names.extend(self.payload_slots().map(|s| super::field_names::FieldName {
147 name: s.name.clone(),
148 optional: false,
149 is_accessor: s.name.starts_with("get ") || s.name.starts_with("set "),
150 is_private: self.private_members.contains(&s.name),
151 }));
152 names
153 }
154}
155
156#[derive(Clone)]
157struct FieldLayout {
158 name: String,
159 optional: bool,
160 narrowing_check: Option<Box<crate::FieldNarrowingCheck>>,
163}
164
165#[derive(Clone)]
167struct OwnMethod {
168 name: String,
169 name_span: crate::Span,
170 frame_name: String,
172 params: Vec<crate::TypedParam>,
173 body: crate::StmtId,
174 return_type: crate::Type,
175 generic: bool,
178}
179
180pub struct ClassPlan {
182 package_name: String,
183 classes: Vec<ClassLayout>,
184 universal_stubs: [u32; UNIVERSAL_STUB_COUNT],
188}
189
190impl ClassPlan {
191 pub fn is_empty(&self) -> bool {
192 self.classes.is_empty()
193 }
194
195 pub fn collect(
199 ta: &TypedAst,
200 imported: &BTreeMap<MangledName, ImportedClassLayout>,
201 ) -> Result<Self, crate::compiler_error::CompilerFailure> {
202 let by_mangled: BTreeMap<&MangledName, &TypedClassDecl> = ta
203 .types
204 .iter()
205 .filter_map(|d| match d {
206 TypedTypeDecl::Class(c) => Some((&c.mangled_name, c)),
207 _ => None,
208 })
209 .collect();
210
211 let order = topo_order(&by_mangled)?;
212 let mut private_members: BTreeMap<MangledName, BTreeSet<String>> = imported
213 .iter()
214 .map(|(name, layout)| (name.clone(), layout.private_members.clone()))
215 .collect();
216
217 let mut layouts: BTreeMap<MangledName, ResolvedLayout> = BTreeMap::new();
220 let mut ordered: Vec<MangledName> = Vec::new();
221 let mut inherited_fields: BTreeMap<MangledName, BTreeSet<String>> = BTreeMap::new();
225
226 for mangled in &order {
227 let decl = class_decl(&by_mangled, mangled)?;
228 let mut private = match &decl.extends {
229 Some(parent) => private_members
230 .get(parent)
231 .cloned()
232 .ok_or_else(|| unavailable_parent(parent))?,
233 None => BTreeSet::new(),
234 };
235 for field in &decl.fields {
236 if field.visibility == crate::Visibility::Private {
237 private.insert(field.name.name.clone());
238 }
239 }
240 for accessor in &decl.accessors {
241 if accessor.visibility() == crate::Visibility::Private {
242 private.insert(accessor_getter_name(&accessor.name().name));
243 private.insert(accessor_setter_name(&accessor.name().name));
244 }
245 }
246 private_members.insert(mangled.clone(), private);
247 let (parent_fields, parent_methods) = match &decl.extends {
250 Some(parent) => parent_prefix(parent, &layouts, imported)?,
251 None => (Vec::new(), Vec::new()),
252 };
253
254 let mut own_fields: Vec<&crate::TypedClassField> = decl.fields.iter().collect();
261 own_fields.sort_by(|a, b| a.name.name.cmp(&b.name.name));
262 let synth_methods = accessor_methods(decl);
265 let mut sorted_methods: Vec<(&crate::TypedClassMethod, String)> = decl
266 .methods
267 .iter()
268 .map(|m| (m, format!("{}.{}", decl.name.name, m.name.name)))
269 .chain(synth_methods.iter().map(|(m, frame)| (m, frame.clone())))
270 .collect();
271 sorted_methods.sort_by(|(a, _), (b, _)| a.name.name.cmp(&b.name.name));
272
273 inherited_fields.insert(
274 mangled.clone(),
275 parent_fields.iter().map(|f| f.name.clone()).collect(),
276 );
277 let mut fields: Vec<FieldLayout> = parent_fields;
278 for f in &own_fields {
279 if let Some(slot) = fields.iter_mut().find(|e| e.name == f.name.name) {
284 slot.optional = f.optional;
285 if let Some(check) = f.narrowing_check.clone() {
286 slot.narrowing_check = Some(check);
287 }
288 continue;
289 }
290 fields.push(FieldLayout {
291 name: f.name.name.clone(),
292 optional: f.optional,
293 narrowing_check: f.narrowing_check.clone(),
294 });
295 }
296
297 let mut methods: Vec<SlotDraft> = parent_methods;
298 for (m, _) in &sorted_methods {
299 let param_tys: Vec<crate::Type> = m.params.iter().map(|p| p.ty.clone()).collect();
300 let argument_metadata = super::call_arguments::typed_metadata(&m.params);
301 if let Some(slot) = methods.iter_mut().find(|s| s.name == m.name.name) {
302 slot.owner = decl.mangled_name.clone();
305 slot.param_tys = param_tys;
306 slot.argument_metadata = argument_metadata;
307 slot.ret_ty = m.return_type.clone();
308 slot.generic = !m.generics.is_empty();
309 } else {
310 methods.push(SlotDraft {
311 name: m.name.name.clone(),
312 owner: decl.mangled_name.clone(),
313 param_tys,
314 argument_metadata,
315 ret_ty: m.return_type.clone(),
316 generic: !m.generics.is_empty(),
317 });
318 }
319 }
320 let own_methods: Vec<OwnMethod> = sorted_methods
321 .into_iter()
322 .map(|(m, frame_name)| OwnMethod {
323 name: m.name.name.clone(),
324 name_span: m.name.span,
325 frame_name,
326 params: m.params.clone(),
327 body: m.body,
328 return_type: m.return_type.clone(),
329 generic: !m.generics.is_empty(),
330 })
331 .collect();
332 layouts.insert(mangled.clone(), (fields, methods, own_methods));
333 ordered.push(mangled.clone());
334 }
335
336 let mut ctor_params_by_mangled: BTreeMap<MangledName, Vec<crate::TypedParam>> = imported
342 .iter()
343 .map(|(m, l)| (m.clone(), l.ctor_params.clone()))
344 .collect();
345 let mut classes: Vec<ClassLayout> = Vec::with_capacity(ordered.len());
346 for mangled in ordered {
347 let decl = class_decl(&by_mangled, &mangled)?;
348 let (fields, method_drafts, own_methods) = layouts
349 .remove(&mangled)
350 .ok_or_else(|| internal_failure("a class layout was resolved out of order"))?;
351 let ctor_params = decl.effective_ctor_params().to_vec();
357 let ctor_body = decl.constructor.as_ref().map(|c| c.body);
358 ctor_params_by_mangled.insert(mangled.clone(), ctor_params.clone());
359 let inherited = inherited_fields.remove(&mangled).ok_or_else(|| {
360 internal_failure("a class's inherited field names were not recorded")
361 })?;
362 let field_setup = field_setup_steps(decl, &inherited)?;
363 let accessor_names: Vec<String> = decl
365 .accessors
366 .iter()
367 .map(|a| a.name().name.clone())
368 .collect::<std::collections::BTreeSet<_>>()
369 .into_iter()
370 .collect();
371 let private_members = private_members
372 .remove(&mangled)
373 .ok_or_else(|| internal_failure("a class's private members were not recorded"))?;
374 classes.push(ClassLayout {
375 private_members,
376 generics: ta
377 .runtime_class_parameters
378 .get(&mangled)
379 .cloned()
380 .unwrap_or_default(),
381 mangled: mangled.clone(),
382 name: decl.name.name.clone(),
383 name_span: decl.name.span,
384 parent: decl.extends.clone(),
385 fields,
386 field_setup,
387 accessor_names,
388 methods: method_drafts
389 .into_iter()
390 .map(|d| MethodSlot {
391 name: d.name,
392 owner: d.owner,
393 sig_idx: 0, param_tys: d.param_tys,
395 argument_metadata: d.argument_metadata,
396 ret_ty: d.ret_ty,
397 generic: d.generic,
398 })
399 .collect(),
400 own_methods,
401 adapter_slots: Vec::new(),
402 ctor_params,
403 ctor_body,
404 struct_type_idx: 0,
405 vtable_type_idx: 0,
406 ctor_func_idx: 0,
407 ctor_sig_idx: 0,
408 ctor_init_func_idx: 0,
409 ctor_init_sig_idx: 0,
410 getter_func_idx: 0,
411 setter_func_idx: 0,
412 equals_func_idx: 0,
413 to_string_func_idx: 0,
414 to_json_func_idx: 0,
415 hash_func_idx: 0,
416 guarded_bodies: GuardedBodies::default(),
417 });
418 }
419
420 Ok(ClassPlan {
421 package_name: ta.package_name.to_string(),
422 classes,
423 universal_stubs: [0; UNIVERSAL_STUB_COUNT],
424 })
425 }
426
427 fn class_ref_ty(&self, class: &ClassLayout) -> crate::Type {
429 crate::Type::class_ref(
430 crate::Package(self.package_name.clone()),
431 class.name.clone(),
432 class.mangled.clone(),
433 Vec::new(),
434 )
435 }
436
437 pub fn field_name_lists(&self) -> Vec<Vec<super::field_names::FieldName>> {
441 self.classes
442 .iter()
443 .map(ClassLayout::payload_field_names)
444 .collect()
445 }
446
447 pub fn accessor_property_names(&self) -> Vec<String> {
451 self.classes
452 .iter()
453 .flat_map(|c| c.accessor_names.iter().cloned())
454 .collect()
455 }
456
457 pub fn reserve_and_emit_types(
461 &mut self,
462 types: &mut TypeSection,
463 next_type_idx: &mut u32,
464 symbols: &mut SymbolTable,
465 intrinsics: IntrinsicTypeIndices,
466 ) -> Result<(), crate::compiler_error::CompilerFailure> {
467 if self.classes.is_empty() {
468 return Ok(());
469 }
470 let mut sig_by_name: BTreeMap<(MangledName, String), (u32, MethodSlotAbi)> =
477 BTreeMap::new();
478 for class in &self.classes {
479 for slot in &class.methods {
480 let parent_has = match &class.parent {
483 Some(p) => inherited_slot_sig(&sig_by_name, symbols, p, &slot.name)?,
484 None => None,
485 };
486 if let Some(parent_entry) = parent_has {
487 sig_by_name.insert((class.mangled.clone(), slot.name.clone()), parent_entry);
488 continue;
489 }
490 let method = class
493 .own_methods
494 .iter()
495 .find(|m| m.name == slot.name)
496 .ok_or_else(|| {
497 internal_failure("a class method slot originates in a class without it")
498 })?;
499 let mut params: Vec<ValType> = vec![ref_to(intrinsics.object)];
500 params.extend(
503 method
504 .params
505 .iter()
506 .map(|_| symbols.value_type(&crate::Type::Unknown))
507 .collect::<Result<Vec<_>, _>>()?,
508 );
509 let results = symbols.slot_wasm_result(&method.return_type)?;
510 let abi = MethodSlotAbi {
511 params: params.iter().skip(1).copied().collect(),
512 ret: results.first().copied(),
513 };
514 let sig_idx = next_index(next_type_idx)?;
515 types.ty().function(params, results);
516 sig_by_name.insert((class.mangled.clone(), slot.name.clone()), (sig_idx, abi));
517 }
518 }
519 for class in &mut self.classes {
521 for slot in &mut class.methods {
522 slot.sig_idx = sig_by_name
523 .get(&(class.mangled.clone(), slot.name.clone()))
524 .ok_or_else(|| internal_failure("a class method slot has no signature"))?
525 .0;
526 }
527 }
528
529 for class in &mut self.classes {
532 class.vtable_type_idx = next_index(next_type_idx)?;
533 class.struct_type_idx = next_index(next_type_idx)?;
534 symbols.record_class_vtable_type(class.mangled.clone(), class.vtable_type_idx);
535 symbols.record_class_struct_type(class.mangled.clone(), class.struct_type_idx);
536 let vtable_super = vtable_supertype_idx(class, intrinsics, symbols)?;
537 let struct_super = struct_supertype_idx(class, intrinsics, symbols)?;
538 symbols.record_struct_supertype(class.vtable_type_idx, vtable_super);
539 symbols.record_struct_supertype(class.struct_type_idx, struct_super);
540 }
541
542 for class in &self.classes {
550 let vtable = self.vtable_subtype(class, intrinsics, symbols)?;
551 let strukt = self.struct_subtype(class, intrinsics, symbols)?;
552 types.ty().rec([vtable, strukt]);
553 }
554
555 for class in &self.classes {
557 symbols
558 .class_type_parameters
559 .insert(class.mangled.clone(), class.generics.clone());
560 let parent_guarded = match &class.parent {
561 Some(parent) => {
562 symbols
563 .recorded_class_guard_layout(parent)?
564 .has_instance_guards
565 }
566 None => false,
567 };
568 symbols.record_class_guard_layout(
569 class.mangled.clone(),
570 class.parent.as_ref(),
571 wasm_u32(class.payload_field_names().len())?,
572 !class.generics.is_empty()
573 || parent_guarded
574 || class
575 .fields
576 .iter()
577 .any(|field| field.narrowing_check.is_some()),
578 )?;
579 for (i, f) in class.fields.iter().enumerate() {
580 symbols.record_class_field_slot(
581 class.mangled.clone(),
582 f.name.clone(),
583 wasm_u32(i)?,
584 );
585 if let Some(check) = &f.narrowing_check {
586 symbols.record_class_field_narrowing_check(
587 class.mangled.clone(),
588 f.name.clone(),
589 (**check).clone(),
590 );
591 }
592 }
593 for (i, slot) in class.methods.iter().enumerate() {
597 if !slot.generic && !slot.name.starts_with("get ") && !slot.name.starts_with("set ")
598 {
599 symbols.record_class_payload_method(class.mangled.clone(), slot.name.clone());
600 }
601 symbols.record_class_method_slot(
602 class.mangled.clone(),
603 slot.name.clone(),
604 method_vtable_slot(i)?,
605 );
606 symbols.record_class_method_sig(
607 class.mangled.clone(),
608 slot.name.clone(),
609 slot.sig_idx,
610 );
611 if let Some((_, abi)) = sig_by_name.get(&(class.mangled.clone(), slot.name.clone()))
612 {
613 symbols.record_class_method_abi(
614 class.mangled.clone(),
615 slot.name.clone(),
616 abi.clone(),
617 );
618 }
619 }
620 }
621
622 let object_idx = intrinsics.object;
629 for class in &mut self.classes {
630 let params: Vec<ValType> = class
631 .ctor_params
632 .iter()
633 .map(|p| symbols.slot_value_type(&p.ty))
634 .collect::<Result<_, _>>()?;
635 symbols.record_class_ctor_abi(class.mangled.clone(), params.clone());
636 let ret = ref_to(class.struct_type_idx);
637 let mut entry_params = params.clone();
638 if symbols
639 .recorded_class_guard_layout(&class.mangled)?
640 .has_instance_guards
641 {
642 entry_params.push(ref_to(intrinsics.object_fields));
643 }
644 class.ctor_sig_idx = next_index(next_type_idx)?;
645 types.ty().function(entry_params, vec![ret]);
646
647 let mut init_params = vec![ref_to(object_idx)];
648 init_params.extend(params);
649 class.ctor_init_sig_idx = next_index(next_type_idx)?;
650 types.ty().function(init_params, Vec::new());
651 }
652 Ok(())
653 }
654
655 fn vtable_subtype(
656 &self,
657 class: &ClassLayout,
658 intrinsics: IntrinsicTypeIndices,
659 symbols: &SymbolTable,
660 ) -> Result<SubType, CompilerFailure> {
661 let supertype = vtable_supertype_idx(class, intrinsics, symbols)?;
662 let mut fields: Vec<FieldType> = vec![
663 fieldtype_ref(intrinsics.to_string_fn),
664 fieldtype_ref(intrinsics.to_json_fn),
665 fieldtype_ref(intrinsics.equals_fn),
666 fieldtype_ref(intrinsics.hash_fn),
667 fieldtype_ref_null(intrinsics.class_vtable),
668 FieldType {
669 element_type: StorageType::Val(ValType::I32),
670 mutable: false,
671 },
672 ];
673 for slot in &class.methods {
674 fields.push(fieldtype_ref(slot.sig_idx));
675 }
676 Ok(substruct(fields, Some(supertype)))
677 }
678
679 fn struct_subtype(
680 &self,
681 class: &ClassLayout,
682 intrinsics: IntrinsicTypeIndices,
683 symbols: &SymbolTable,
684 ) -> Result<SubType, CompilerFailure> {
685 let supertype = struct_supertype_idx(class, intrinsics, symbols)?;
686 let fields: Vec<FieldType> = vec![
690 fieldtype_ref(class.vtable_type_idx),
691 FieldType {
692 mutable: true,
693 ..fieldtype_ref(intrinsics.field_names)
694 },
695 FieldType {
696 mutable: true,
697 ..fieldtype_ref(intrinsics.object_fields)
698 },
699 FieldType {
700 element_type: StorageType::Val(ValType::Ref(RefType::ANYREF)),
701 mutable: true,
702 },
703 FieldType {
704 element_type: StorageType::Val(ValType::I64),
705 mutable: true,
706 },
707 ];
708 Ok(substruct(fields, Some(supertype)))
709 }
710
711 pub fn allocate_funcs(
714 &mut self,
715 next_func_idx: &mut u32,
716 symbols: &mut SymbolTable,
717 ) -> Result<(), CompilerFailure> {
718 if self.classes.is_empty() {
719 return Ok(());
720 }
721 self.universal_stubs = [
722 next_index(next_func_idx)?,
723 next_index(next_func_idx)?,
724 next_index(next_func_idx)?,
725 next_index(next_func_idx)?,
726 ];
727 let ctor_infos: Vec<(MangledName, Vec<crate::Type>, crate::Type)> = self
730 .classes
731 .iter()
732 .map(|c| {
733 (
734 crate::mangle::extend(&c.mangled, "constructor"),
735 c.ctor_params.iter().map(|p| p.ty.clone()).collect(),
736 self.class_ref_ty(c),
737 )
738 })
739 .collect();
740 for (class, (ctor_mangled, ctor_param_tys, ret)) in self.classes.iter_mut().zip(ctor_infos)
741 {
742 class.ctor_func_idx = next_index(next_func_idx)?;
745 symbols.record_local_fn(ctor_mangled, class.ctor_func_idx, ctor_param_tys, ret)?;
746 class.ctor_init_func_idx = next_index(next_func_idx)?;
747 symbols.record_class_ctor_init_func(class.mangled.clone(), class.ctor_init_func_idx);
748 symbols.record_class_field_setup(class.mangled.clone(), class.field_setup.clone());
749 for method in &class.own_methods {
750 let idx = next_index(next_func_idx)?;
751 symbols.record_class_method_func(class.mangled.clone(), method.name.clone(), idx);
752 }
753 let mut adapter_slots: Vec<usize> = Vec::new();
761 for (i, slot) in class.methods.iter().enumerate() {
762 if slot.generic {
763 continue;
764 }
765 let reused = (slot.owner != class.mangled)
766 .then(|| symbols.class_method_adapter_func_idx(&slot.owner, &slot.name))
767 .flatten();
768 let idx = match reused {
769 Some(idx) => idx,
770 None => next_index(next_func_idx)?,
771 };
772 if reused.is_none() {
773 adapter_slots.push(i);
774 symbols.record_class_method_adapter_func(
780 slot.owner.clone(),
781 slot.name.clone(),
782 idx,
783 );
784 }
785 symbols.record_class_method_adapter_func(
786 class.mangled.clone(),
787 slot.name.clone(),
788 idx,
789 );
790 }
791 class.adapter_slots = adapter_slots;
792 class.getter_func_idx = next_index(next_func_idx)?;
795 class.setter_func_idx = next_index(next_func_idx)?;
796 class.equals_func_idx = next_index(next_func_idx)?;
797 class.to_string_func_idx = next_index(next_func_idx)?;
798 class.to_json_func_idx = next_index(next_func_idx)?;
799 class.hash_func_idx = next_index(next_func_idx)?;
800 class.guarded_bodies = GuardedBodies {
802 equals: next_index(next_func_idx)?,
803 to_json: next_index(next_func_idx)?,
804 hash: next_index(next_func_idx)?,
805 };
806 }
807 Ok(())
808 }
809
810 pub fn emit_function_entries(
813 &self,
814 functions: &mut FunctionSection,
815 symbols: &SymbolTable,
816 intrinsics: IntrinsicTypeIndices,
817 ) -> Result<(), crate::compiler_error::CompilerFailure> {
818 if self.classes.is_empty() {
819 return Ok(());
820 }
821 functions.function(intrinsics.to_string_fn);
822 functions.function(intrinsics.to_json_fn);
823 functions.function(intrinsics.equals_fn);
824 functions.function(intrinsics.hash_fn);
825 for class in &self.classes {
826 functions.function(class.ctor_sig_idx);
827 functions.function(class.ctor_init_sig_idx);
828 for method in &class.own_methods {
829 let sig_idx = class
830 .methods
831 .iter()
832 .find(|s| s.name == method.name)
833 .ok_or_else(|| internal_failure("a class's own method has no vtable slot"))?
834 .sig_idx;
835 functions.function(sig_idx);
836 }
837 for &i in &class.adapter_slots {
838 let ty = symbols
839 .closure_func_type_idx(slot_closure_sig(class.methods.get(i).ok_or_else(
840 || {
841 crate::codegen::internal_failure(
842 "class adapter refers to a missing method slot",
843 )
844 },
845 )?)?)
846 .ok_or_else(|| {
847 crate::codegen::internal_failure(
848 "class method closure sig registered (closures::class_member_sigs)",
849 )
850 })?;
851 functions.function(ty);
852 }
853 functions.function(intrinsics.field_getter);
854 functions.function(intrinsics.field_setter);
855 functions.function(intrinsics.equals_fn);
856 functions.function(intrinsics.to_string_fn);
857 functions.function(intrinsics.to_json_fn);
858 functions.function(intrinsics.hash_fn);
859 functions.function(intrinsics.equals_fn);
860 functions.function(intrinsics.to_json_fn);
861 functions.function(intrinsics.hash_fn);
862 }
863 Ok(())
864 }
865
866 pub fn emit_globals(
869 &self,
870 globals: &mut GlobalSection,
871 symbols: &mut SymbolTable,
872 next_global_idx: &mut u32,
873 intrinsics: IntrinsicTypeIndices,
874 ) -> Result<(), CompilerFailure> {
875 for class in &self.classes {
876 let parent_link = match &class.parent {
883 Some(p) => {
884 Instruction::GlobalGet(symbols.class_vtable_global_idx(p).ok_or_else(|| {
885 internal_failure("a parent vtable global was not recorded before its child")
886 })?)
887 }
888 None => Instruction::RefNull(HeapType::Concrete(intrinsics.class_vtable)),
889 };
890 let mut instrs: Vec<Instruction<'_>> = vec![
891 Instruction::RefFunc(class.to_string_func_idx),
892 Instruction::RefFunc(class.to_json_func_idx),
893 Instruction::RefFunc(class.equals_func_idx),
894 Instruction::RefFunc(class.hash_func_idx),
895 parent_link,
896 Instruction::I32Const(i32::from(
897 !class.methods.iter().any(|slot| slot.name == "toJson"),
898 )),
899 ];
900 for slot in &class.methods {
901 let func = symbols
902 .class_method_func_idx(&slot.owner, &slot.name)
903 .ok_or_else(|| internal_failure("a class method body was not allocated"))?;
904 instrs.push(Instruction::RefFunc(func));
905 }
906 instrs.push(Instruction::StructNew(class.vtable_type_idx));
907 globals.global(
908 GlobalType {
909 val_type: ref_to(class.vtable_type_idx),
910 mutable: false,
911 shared: false,
912 },
913 &ConstExpr::extended(instrs),
914 );
915 symbols.record_class_vtable_global(class.mangled.clone(), next_index(next_global_idx)?);
916
917 globals.global(
920 GlobalType {
921 val_type: ref_to(intrinsics.field_getter),
922 mutable: false,
923 shared: false,
924 },
925 &ConstExpr::extended([Instruction::RefFunc(class.getter_func_idx)]),
926 );
927 symbols.record_class_getter_global(class.mangled.clone(), next_index(next_global_idx)?);
928
929 globals.global(
930 GlobalType {
931 val_type: ref_to(intrinsics.field_setter),
932 mutable: false,
933 shared: false,
934 },
935 &ConstExpr::extended([Instruction::RefFunc(class.setter_func_idx)]),
936 );
937 symbols.record_class_setter_global(class.mangled.clone(), next_index(next_global_idx)?);
938 }
939 Ok(())
940 }
941
942 pub fn emit_bodies(
947 &self,
948 code: &mut CodeSection,
949 debug: &mut super::dwarf::DebugFunctions,
950 ctx: &crate::codegen::CodegenCtx<'_>,
951 ) -> Result<(), crate::compiler_error::CompilerFailure> {
952 if self.classes.is_empty() {
953 return Ok(());
954 }
955 for _ in 0..UNIVERSAL_STUB_COUNT {
959 code.function(&stub_body());
960 }
961 for class in &self.classes {
962 code.function(&self.emit_ctor_body(class, ctx)?);
963 debug.write(
966 code,
967 self.emit_ctor_init(class, ctx)?,
968 format!("new {}", class.name),
969 class.name_span,
970 )?;
971 for method in &class.own_methods {
972 if method.generic {
973 code.function(&stub_body());
974 continue;
975 }
976 debug.write(
977 code,
978 self.emit_method_body(class, method, ctx)?,
979 method.frame_name.clone(),
980 method.name_span,
981 )?;
982 }
983 for &i in &class.adapter_slots {
984 code.function(&self.emit_method_adapter_body(
985 class,
986 class.methods.get(i).ok_or_else(|| {
987 crate::codegen::internal_failure(
988 "class adapter refers to a missing method slot",
989 )
990 })?,
991 ctx,
992 )?);
993 }
994 code.function(&stub_body());
996 code.function(&stub_body());
997 let intrinsics = ctx
998 .symbols
999 .intrinsic_type_indices()
1000 .ok_or_else(|| internal_failure("intrinsic types are not declared"))?;
1001 let bodies = class.guarded_bodies;
1002 code.function(&super::vtable_walk::guarded_body(
1003 bodies.equals,
1004 2,
1005 ValType::I32,
1006 ctx.symbols,
1007 )?);
1008 let string_vtable_global_idx = ctx
1009 .symbols
1010 .prelude_global_idx("string_vtable")
1011 .ok_or_else(|| {
1012 internal_failure("string_vtable is not imported from the prelude")
1013 })?;
1014 let user_method_thunk =
1018 |name: &str| -> Result<Option<Function>, crate::compiler_error::CompilerFailure> {
1019 class
1020 .methods
1021 .iter()
1022 .find(|s| s.name == name)
1023 .map(|slot| emit_universal_slot_thunk(ctx, &slot.name))
1024 .transpose()
1025 };
1026 let extends_error = ctx
1027 .symbols
1028 .class_struct_type_idx(&crate::mangle::prelude("Error"))
1029 .is_some_and(|error_type| {
1030 ctx.symbols.ref_fits_slot(
1031 RefType {
1032 nullable: false,
1033 heap_type: HeapType::Concrete(class.struct_type_idx),
1034 },
1035 RefType {
1036 nullable: false,
1037 heap_type: HeapType::Concrete(error_type),
1038 },
1039 )
1040 });
1041 let to_string = match user_method_thunk("toString")? {
1042 Some(thunk) => thunk,
1043 None if class.fields.iter().any(|field| field.name == "toString") => {
1044 emit_class_to_string_field_body(ctx.symbols, intrinsics)?
1045 }
1046 None if extends_error => {
1047 emit_error_slot_body(ctx.symbols, intrinsics, 0, intrinsics.to_string_fn)?
1048 }
1049 None => emit_class_to_string_body(intrinsics, string_vtable_global_idx)?,
1050 };
1051 code.function(&to_string);
1052 code.function(&super::vtable_walk::guarded_body(
1053 bodies.to_json,
1054 1,
1055 ref_to(intrinsics.string),
1056 ctx.symbols,
1057 )?);
1058 code.function(&super::vtable_walk::guarded_body(
1059 bodies.hash,
1060 1,
1061 ValType::I32,
1062 ctx.symbols,
1063 )?);
1064 let field_count = wasm_u32(class.fields.len())?;
1065 if extends_error {
1066 let mut equals = Function::new([]);
1067 equals.instruction(&Instruction::LocalGet(0));
1068 equals.instruction(&Instruction::LocalGet(1));
1069 equals.instruction(&Instruction::RefEq);
1070 equals.instruction(&Instruction::End);
1071 code.function(&equals);
1072 } else {
1073 code.function(&emit_class_equals_body(field_count, intrinsics));
1074 }
1075 let to_json = match user_method_thunk("toJson")? {
1076 Some(thunk) => thunk,
1077 None => emit_class_to_json_body(ctx.symbols)?,
1078 };
1079 code.function(&to_json);
1080 if extends_error {
1081 code.function(&emit_error_slot_body(
1082 ctx.symbols,
1083 intrinsics,
1084 3,
1085 intrinsics.hash_fn,
1086 )?);
1087 } else {
1088 code.function(&emit_class_hash_body(field_count, intrinsics));
1089 }
1090 }
1091 Ok(())
1092 }
1093
1094 fn emit_method_adapter_body(
1108 &self,
1109 class: &ClassLayout,
1110 slot: &MethodSlot,
1111 ctx: &crate::codegen::CodegenCtx<'_>,
1112 ) -> Result<Function, crate::compiler_error::CompilerFailure> {
1113 use crate::codegen::function_emitter::{FunctionEmitter, cast};
1114
1115 slot_closure_sig(slot)?;
1116 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1117 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1118 })?;
1119 let object_ref_null = ValType::Ref(RefType {
1120 nullable: true,
1121 heap_type: HeapType::Concrete(intrinsics.object),
1122 });
1123 let any_ref = ValType::Ref(RefType {
1124 nullable: false,
1125 heap_type: HeapType::ANY,
1126 });
1127 let mut wasm_params: Vec<(crate::Ident, ValType)> = vec![(
1128 crate::Ident {
1129 name: "$__env__".to_string(),
1130 span: crate::Span::at(ctx.file),
1131 },
1132 any_ref,
1133 )];
1134 for (i, _) in slot.param_tys.iter().enumerate() {
1135 wasm_params.push((
1136 crate::Ident {
1137 name: format!("$__arg{i}__"),
1138 span: crate::Span::at(ctx.file),
1139 },
1140 object_ref_null,
1141 ));
1142 }
1143 let mut emitter = FunctionEmitter::new(ctx, &wasm_params)?;
1144 let method_func = ctx
1147 .symbols
1148 .class_method_func_idx(&slot.owner, &slot.name)
1149 .ok_or_else(|| {
1150 crate::codegen::internal_failure("method body func allocated or imported")
1151 })?;
1152
1153 let abi = ctx
1157 .symbols
1158 .class_method_abi(&class.mangled, &slot.name)
1159 .cloned()
1160 .ok_or_else(|| {
1161 crate::codegen::internal_failure("class method slot ABI is not registered")
1162 })?;
1163 if abi.params.len() != slot.param_tys.len() {
1164 return Err(crate::codegen::internal_failure(
1165 "class method slot ABI has the wrong parameter count",
1166 ));
1167 }
1168 emitter.instruction(Instruction::LocalGet(0));
1169 if slot.argument_metadata.is_some() {
1170 super::call_arguments::unwrap(&mut emitter, ctx)?;
1171 }
1172 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
1173 intrinsics.object,
1174 )));
1175 for (i, ty) in slot.param_tys.iter().enumerate() {
1176 emitter.instruction(Instruction::LocalGet(parameter_local(i)?));
1177 if abi.params.get(i) != Some(&object_ref_null) {
1178 crate::codegen::cast_check::emit_checked_parameter_cast_on_stack(
1179 &mut emitter,
1180 ctx,
1181 &crate::Type::Unknown,
1182 ty,
1183 )?;
1184 }
1185 }
1186 emitter.instruction(Instruction::Call(method_func));
1187 match abi.ret {
1188 Some(ValType::F64) => cast::emit_box(&mut emitter, ctx, &crate::Type::Number)?,
1189 Some(ValType::I32) => cast::emit_box(&mut emitter, ctx, &crate::Type::Boolean)?,
1190 None if matches!(slot.ret_ty.peel(), crate::Type::Never) => {
1193 emitter.instruction(Instruction::Unreachable);
1194 }
1195 _ => {}
1196 }
1197 emitter.build()
1198 }
1199
1200 fn emit_method_body(
1214 &self,
1215 class: &ClassLayout,
1216 method: &OwnMethod,
1217 ctx: &crate::codegen::CodegenCtx<'_>,
1218 ) -> Result<(Function, Vec<(u64, crate::Span)>), crate::compiler_error::CompilerFailure> {
1219 use crate::codegen::function_emitter::{
1220 FunctionEmitter, ReturnTarget, emit_body_end, stmt,
1221 };
1222
1223 let object_idx = ctx
1224 .symbols
1225 .intrinsic_type_indices()
1226 .ok_or_else(|| internal_failure("intrinsic types are not declared"))?
1227 .object;
1228 let abi = ctx
1229 .symbols
1230 .class_method_abi(&class.mangled, &method.name)
1231 .cloned()
1232 .ok_or_else(|| internal_failure("class method slot ABI is not registered"))?;
1233 let mut wasm_params: Vec<(crate::Ident, ValType)> = vec![(
1234 crate::Ident {
1235 name: "this".to_string(),
1236 span: crate::Span::at(ctx.file),
1237 },
1238 ref_to(object_idx),
1239 )];
1240 wasm_params.extend(slot_params(ctx, &method.params, &abi.params)?);
1241 let mut emitter = FunctionEmitter::new(ctx, &wasm_params)?;
1242 let param_slots = rebind_erased_params(&mut emitter, ctx, &method.params, &abi.params)?;
1243 emitter.prepare_parameter_initialization(Some(method.body))?;
1244 emitter.emit_boxed_param_prologue(&method.params, ¶m_slots)?;
1246
1247 let this_slot = emitter.add_anonymous_local(ref_to(class.struct_type_idx))?;
1249 emitter.instruction(Instruction::LocalGet(0));
1250 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
1251 class.struct_type_idx,
1252 )));
1253 emitter.instruction(Instruction::LocalSet(this_slot));
1254 emitter.set_this_local(this_slot)?;
1255 super::field_guards::bind_receiver(&mut emitter, ctx, this_slot, &class.mangled)?;
1256
1257 {
1258 emitter.set_return_target(ReturnTarget::Slot(match abi.ret {
1259 Some(ty) => ty,
1260 None => ctx.symbols.slot_value_type(&method.return_type)?,
1261 }));
1262 }
1263 stmt::emit_statement(&mut emitter, ctx, method.body)?;
1264 emit_body_end(&mut emitter, ctx, &method.return_type)?;
1265 emitter.build_with_lines()
1266 }
1267
1268 fn emit_ctor_body(
1274 &self,
1275 class: &ClassLayout,
1276 ctx: &crate::codegen::CodegenCtx<'_>,
1277 ) -> Result<Function, crate::compiler_error::CompilerFailure> {
1278 use crate::codegen::function_emitter::FunctionEmitter;
1279
1280 let ctor_slots = ctor_slot_types(ctx, &class.mangled)?;
1283 let mut wasm_params = slot_params(ctx, &class.ctor_params, &ctor_slots)?;
1284 let guarded = ctx
1285 .symbols
1286 .recorded_class_guard_layout(&class.mangled)?
1287 .has_instance_guards;
1288 if guarded {
1289 let intr = ctx
1290 .symbols
1291 .intrinsic_type_indices()
1292 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?;
1293 wasm_params.push((
1294 crate::Ident {
1295 name: "$field_guards".into(),
1296 span: crate::Span::at(ctx.file),
1297 },
1298 ref_to(intr.object_fields),
1299 ));
1300 }
1301 let mut emitter = FunctionEmitter::new(ctx, &wasm_params)?;
1302
1303 let this_slot = emitter.add_anonymous_local(ref_to(class.struct_type_idx))?;
1304 emitter.set_this_local(this_slot)?;
1305
1306 let dynamic_methods: Vec<&MethodSlot> = class.payload_slots().collect();
1312 let n_fields = wasm_u32(class.fields.len())?;
1313 let field_names = class.payload_field_names();
1314 let push_global =
1315 |em: &mut FunctionEmitter, idx: u32| em.instruction(Instruction::GlobalGet(idx));
1316 push_global(
1317 &mut emitter,
1318 ctx.symbols
1319 .class_vtable_global_idx(&class.mangled)
1320 .ok_or_else(|| crate::codegen::internal_failure("vtable global"))?,
1321 );
1322 super::field_names::emit_instance_names(&mut emitter, ctx, &field_names, |_| false)?;
1323 let intrinsics = ctx.symbols.intrinsic_type_indices().ok_or_else(|| {
1324 crate::codegen::internal_failure("intrinsics declared by codegen entry")
1325 })?;
1326 let layout = ctx.symbols.recorded_class_guard_layout(&class.mangled)?;
1329 let named_len = wasm_u32(field_names.len())?;
1330 let guard_slots = if layout.has_instance_guards {
1331 guard_slot_count(layout.inheritance_depth, named_len)?
1332 } else {
1333 0
1334 };
1335 let payload_len = named_len
1336 .checked_add(guard_slots)
1337 .ok_or_else(|| internal_failure("a class instance payload is too large"))?;
1338 for slot in 0..payload_len {
1339 if slot < named_len {
1340 super::function_emitter::expr::emit_undefined(&mut emitter, ctx)?;
1341 } else {
1342 emitter.instruction(Instruction::RefNull(HeapType::Concrete(intrinsics.object)));
1343 }
1344 }
1345 emitter.instruction(Instruction::ArrayNewFixed {
1346 array_type_index: intrinsics.object_fields,
1347 array_size: payload_len,
1348 });
1349 emitter.instruction(Instruction::RefNull(HeapType::ANY));
1350 emitter.instruction(Instruction::I64Const(0));
1351 emitter.instruction(Instruction::StructNew(class.struct_type_idx));
1352 emitter.instruction(Instruction::LocalSet(this_slot));
1353
1354 if layout.has_instance_guards {
1355 emitter.instruction(Instruction::LocalGet(this_slot));
1356 emitter.instruction(Instruction::StructGet {
1357 struct_type_index: class.struct_type_idx,
1358 field_index: 2,
1359 });
1360 emitter.instruction(Instruction::I32Const(named_len.cast_signed()));
1361 emitter.instruction(Instruction::LocalGet(wasm_u32(class.ctor_params.len())?));
1362 emitter.instruction(Instruction::I32Const(0));
1363 emitter.instruction(Instruction::I32Const(guard_slots.cast_signed()));
1364 emitter.instruction(Instruction::ArrayCopy {
1365 array_type_index_dst: intrinsics.object_fields,
1366 array_type_index_src: intrinsics.object_fields,
1367 });
1368 }
1369
1370 if !dynamic_methods.is_empty() {
1374 let closure_vtable = ctx.symbols.closure_vtable_global_idx().ok_or_else(|| {
1375 crate::codegen::internal_failure(
1376 "closure vtable global emitted when class methods exist",
1377 )
1378 })?;
1379 for (j, method) in dynamic_methods.iter().enumerate() {
1380 let method_slot = n_fields
1381 .checked_add(wasm_u32(j)?)
1382 .ok_or_else(|| internal_failure("a class instance payload is too large"))?;
1383 let closure_struct = ctx
1384 .symbols
1385 .closure_struct_type_idx(slot_closure_sig(method)?)
1386 .ok_or_else(|| {
1387 crate::codegen::internal_failure("class method closure struct registered")
1388 })?;
1389 let adapter = ctx
1390 .symbols
1391 .class_method_adapter_func_idx(&class.mangled, &method.name)
1392 .ok_or_else(|| {
1393 crate::codegen::internal_failure(
1394 "class method adapter allocated or reused from the owner",
1395 )
1396 })?;
1397 emitter.instruction(Instruction::LocalGet(this_slot));
1398 emitter.instruction(Instruction::StructGet {
1399 struct_type_index: class.struct_type_idx,
1400 field_index: 2,
1401 });
1402 emitter.instruction(Instruction::I32Const(method_slot.cast_signed()));
1403 emitter.instruction(Instruction::GlobalGet(closure_vtable));
1404 emitter.instruction(Instruction::RefFunc(adapter));
1405 emitter.instruction(Instruction::LocalGet(this_slot));
1406 if let Some(metadata) = &method.argument_metadata {
1407 super::call_arguments::wrap(&mut emitter, ctx, metadata)?;
1408 }
1409 emitter.instruction(Instruction::I64Const(0));
1410 emitter.instruction(Instruction::StructNew(closure_struct));
1411 emitter.instruction(Instruction::ArraySet(intrinsics.object_fields));
1412 if let Some(field_slot) = class.fields.iter().position(|f| f.name == method.name) {
1413 emit_fill_optional_method(
1414 &mut emitter,
1415 (this_slot, class.struct_type_idx),
1416 intrinsics.object_fields,
1417 method_slot,
1418 wasm_u32(field_slot)?,
1419 );
1420 }
1421 }
1422 }
1423
1424 emitter.instruction(Instruction::LocalGet(this_slot));
1428 for i in 0..wasm_u32(class.ctor_params.len())? {
1429 emitter.instruction(Instruction::LocalGet(i));
1430 }
1431 emitter.instruction(Instruction::Call(class.ctor_init_func_idx));
1432
1433 emitter.instruction(Instruction::LocalGet(this_slot));
1436 emitter.build()
1437 }
1438
1439 fn emit_ctor_init(
1445 &self,
1446 class: &ClassLayout,
1447 ctx: &crate::codegen::CodegenCtx<'_>,
1448 ) -> Result<(Function, Vec<(u64, crate::Span)>), crate::compiler_error::CompilerFailure> {
1449 use crate::codegen::function_emitter::{FunctionEmitter, cast, stmt};
1450
1451 let object_idx = ctx
1452 .symbols
1453 .intrinsic_type_indices()
1454 .ok_or_else(|| internal_failure("intrinsic types are not declared"))?
1455 .object;
1456 let mut wasm_params: Vec<(crate::Ident, ValType)> = vec![(
1457 crate::Ident {
1458 name: "this".to_string(),
1459 span: crate::Span::at(ctx.file),
1460 },
1461 ref_to(object_idx),
1462 )];
1463 let ctor_slots = ctor_slot_types(ctx, &class.mangled)?;
1464 wasm_params.extend(slot_params(ctx, &class.ctor_params, &ctor_slots)?);
1465 let mut emitter = FunctionEmitter::new(ctx, &wasm_params)?;
1466 let param_slots = rebind_erased_params(&mut emitter, ctx, &class.ctor_params, &ctor_slots)?;
1467 emitter.prepare_parameter_initialization(class.ctor_body)?;
1468 emitter.emit_boxed_param_prologue(&class.ctor_params, ¶m_slots)?;
1470
1471 let this_slot = emitter.add_anonymous_local(ref_to(class.struct_type_idx))?;
1473 emitter.instruction(Instruction::LocalGet(0));
1474 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
1475 class.struct_type_idx,
1476 )));
1477 emitter.instruction(Instruction::LocalSet(this_slot));
1478 emitter.set_this_local(this_slot)?;
1479 super::field_guards::bind_receiver(&mut emitter, ctx, this_slot, &class.mangled)?;
1480 emitter.set_ctor_class(class.mangled.clone());
1481
1482 if let Some(body) = class.ctor_body {
1483 if class.parent.is_none() {
1484 super::function_emitter::expr::emit_class_field_setup_phase(
1485 &mut emitter,
1486 ctx,
1487 &class.mangled,
1488 super::function_emitter::expr::FieldSetupPhase::Initializers,
1489 )?;
1490 }
1491 let defaults = ctx
1492 .ta
1493 .parameter_default_prologues
1494 .get(&body)
1495 .copied()
1496 .unwrap_or(0);
1497 if class.parent.is_none() && defaults > 0 {
1498 let body = ctx.ta.try_stmt(body).map_err(super::arena_failure)?;
1499 let crate::TypedStmtKind::Block(statements) = &body.kind else {
1500 return Err(internal_failure(
1501 "constructor default prologue is not a block",
1502 ));
1503 };
1504 let prologue = statements.get(..defaults).ok_or_else(|| {
1505 internal_failure("constructor default prologue is incomplete")
1506 })?;
1507 let body = statements.get(defaults..).ok_or_else(|| {
1508 internal_failure("constructor body is missing after defaults")
1509 })?;
1510 emitter.push_scope();
1511 for &statement in prologue {
1512 stmt::emit_statement(&mut emitter, ctx, statement)?;
1513 }
1514 super::function_emitter::expr::emit_class_field_setup_phase(
1515 &mut emitter,
1516 ctx,
1517 &class.mangled,
1518 super::function_emitter::expr::FieldSetupPhase::ParameterProperties,
1519 )?;
1520 for &statement in body {
1521 stmt::emit_statement(&mut emitter, ctx, statement)?;
1522 }
1523 emitter.pop_scope()?;
1524 } else {
1525 if class.parent.is_none() {
1526 super::function_emitter::expr::emit_class_field_setup_phase(
1527 &mut emitter,
1528 ctx,
1529 &class.mangled,
1530 super::function_emitter::expr::FieldSetupPhase::ParameterProperties,
1531 )?;
1532 }
1533 stmt::emit_statement(&mut emitter, ctx, body)?;
1534 }
1535 } else {
1536 if let Some(parent) = &class.parent {
1539 let parent_init =
1540 ctx.symbols
1541 .class_ctor_init_func_idx(parent)
1542 .ok_or_else(|| {
1543 internal_failure("a parent constructor init was not allocated")
1544 })?;
1545 let parent_slots = ctor_slot_types(ctx, parent)?;
1551 emitter.instruction(Instruction::LocalGet(this_slot));
1552 if param_slots.len() != class.ctor_params.len() {
1553 return Err(internal_failure(
1554 "constructor parameters and their locals differ in number",
1555 ));
1556 }
1557 for (i, (p, local)) in class.ctor_params.iter().zip(¶m_slots).enumerate() {
1558 emitter.instruction(Instruction::LocalGet(*local));
1559 if let Some(slot) = parent_slots.get(i).copied() {
1560 cast::emit_coerce_to_wasm_slot(&mut emitter, ctx, &p.ty, slot)?;
1561 }
1562 }
1563 emitter.instruction(Instruction::Call(parent_init));
1564 }
1565 crate::codegen::function_emitter::expr::emit_class_field_setup(
1566 &mut emitter,
1567 ctx,
1568 &class.mangled,
1569 )?;
1570 }
1571 emitter.build_with_lines()
1572 }
1573
1574 pub fn exported_funcs(
1582 &self,
1583 symbols: &SymbolTable,
1584 is_exported: impl Fn(&MangledName) -> bool,
1585 ) -> Result<Vec<(MangledName, u32)>, CompilerFailure> {
1586 let mut out: Vec<(MangledName, u32)> = Vec::new();
1587 for class in &self.classes {
1588 if !is_exported(&class.mangled) {
1589 continue;
1590 }
1591 out.push((
1592 crate::mangle::extend(&class.mangled, "constructor"),
1593 class.ctor_func_idx,
1594 ));
1595 out.push((
1596 crate::mangle::extend(&class.mangled, "constructor_init"),
1597 class.ctor_init_func_idx,
1598 ));
1599 for method in &class.own_methods {
1600 if method.generic {
1601 continue;
1602 }
1603 let idx = symbols
1604 .class_method_func_idx(&class.mangled, &method.name)
1605 .ok_or_else(|| internal_failure("a class method body was not allocated"))?;
1606 out.push((crate::mangle::extend(&class.mangled, &method.name), idx));
1607 }
1608 }
1609 Ok(out)
1610 }
1611
1612 pub fn exported_vtable_globals(
1616 &self,
1617 symbols: &SymbolTable,
1618 is_exported: impl Fn(&MangledName) -> bool,
1619 ) -> Result<Vec<(MangledName, u32)>, CompilerFailure> {
1620 self.classes
1621 .iter()
1622 .filter(|class| is_exported(&class.mangled))
1623 .map(|class| {
1624 let global_idx = symbols
1625 .class_vtable_global_idx(&class.mangled)
1626 .ok_or_else(|| internal_failure("a class vtable global was not emitted"))?;
1627 Ok((vtable_global_export_name(&class.mangled), global_idx))
1628 })
1629 .collect()
1630 }
1631
1632 pub fn declared_funcs(&self, symbols: &SymbolTable) -> Vec<u32> {
1635 if self.classes.is_empty() {
1636 return Vec::new();
1637 }
1638 let mut out: Vec<u32> = self.universal_stubs.to_vec();
1639 for class in &self.classes {
1640 for slot in &class.methods {
1644 if let Some(idx) = symbols.class_method_func_idx(&slot.owner, &slot.name) {
1645 out.push(idx);
1646 }
1647 }
1648 for slot in class.payload_slots() {
1652 if let Some(idx) = symbols.class_method_adapter_func_idx(&class.mangled, &slot.name)
1653 {
1654 out.push(idx);
1655 }
1656 }
1657 out.push(class.getter_func_idx);
1658 out.push(class.setter_func_idx);
1659 out.push(class.equals_func_idx);
1660 out.push(class.to_string_func_idx);
1661 out.push(class.to_json_func_idx);
1662 out.push(class.hash_func_idx);
1663 }
1664 out.sort_unstable();
1665 out.dedup();
1666 out
1667 }
1668}
1669
1670fn field_setup_steps(
1679 decl: &TypedClassDecl,
1680 inherited: &BTreeSet<String>,
1681) -> Result<Vec<FieldSetup>, CompilerFailure> {
1682 let mut steps = Vec::new();
1683 if let Some(ctor) = &decl.constructor {
1684 for field in decl.fields.iter().filter(|f| f.auto_assigned) {
1685 if let Some(param) = ctor.params.iter().find(|p| p.name.name == field.name.name) {
1686 steps.push(FieldSetup::ParamCopy {
1687 field: field.name.name.clone(),
1688 param: Box::new(param.clone()),
1689 });
1690 }
1691 }
1692 }
1693 for field in &decl.fields {
1694 if let Some(value) = field.initializer {
1695 steps.push(FieldSetup::Init {
1696 field: field.name.name.clone(),
1697 value,
1698 });
1699 } else if redeclares_uninitialized_slot(field, inherited) {
1700 steps.push(FieldSetup::Reset {
1701 field: field.name.name.clone(),
1702 });
1703 }
1704 }
1705 Ok(steps)
1706}
1707
1708pub(crate) fn method_vtable_slot(slot: usize) -> Result<u32, CompilerFailure> {
1711 VTABLE_METHOD_SLOT_BASE
1712 .checked_add(wasm_u32(slot)?)
1713 .ok_or_else(|| internal_failure("a class has too many vtable slots"))
1714}
1715
1716pub(super) fn guard_slot_count(
1719 inheritance_depth: u32,
1720 named_len: u32,
1721) -> Result<u32, CompilerFailure> {
1722 inheritance_depth
1723 .checked_add(1)
1724 .zip(named_len.checked_add(1))
1725 .and_then(|(levels, per_level)| levels.checked_mul(per_level))
1726 .ok_or_else(|| internal_failure("a class guard payload is too large"))
1727}
1728
1729fn class_decl<'a>(
1730 by_mangled: &BTreeMap<&MangledName, &'a TypedClassDecl>,
1731 mangled: &MangledName,
1732) -> Result<&'a TypedClassDecl, CompilerFailure> {
1733 by_mangled
1734 .get(mangled)
1735 .copied()
1736 .ok_or_else(|| internal_failure("class order names an undeclared class"))
1737}
1738
1739fn redeclares_uninitialized_slot(
1747 field: &crate::TypedClassField,
1748 inherited: &BTreeSet<String>,
1749) -> bool {
1750 field.optional
1751 && !field.auto_assigned
1752 && !field.method_signature
1753 && inherited.contains(&field.name.name)
1754}
1755
1756pub(crate) fn vtable_global_export_name(mangled: &MangledName) -> MangledName {
1760 crate::mangle::extend(mangled, "vtable global")
1761}
1762
1763pub(crate) fn accessor_getter_name(prop: &str) -> String {
1768 format!("get {prop}")
1769}
1770
1771pub(crate) fn accessor_setter_name(prop: &str) -> String {
1773 format!("set {prop}")
1774}
1775
1776pub(crate) fn accessor_closure_sig(kind: crate::AccessorKind) -> ClosureSig {
1782 match kind {
1783 crate::AccessorKind::Get => ClosureSig {
1784 arity: 0,
1785 is_void: false,
1786 },
1787 crate::AccessorKind::Set => ClosureSig {
1788 arity: 1,
1789 is_void: true,
1790 },
1791 }
1792}
1793
1794pub(crate) fn shadows_inherited_field<'a>(
1806 inherited: impl IntoIterator<Item = &'a str>,
1807 name: &str,
1808) -> bool {
1809 inherited.into_iter().any(|f| f == name)
1810}
1811
1812fn accessor_methods(decl: &TypedClassDecl) -> Vec<(crate::TypedClassMethod, String)> {
1822 decl.accessors
1823 .iter()
1824 .map(|acc| {
1825 let (kind, name, params, return_type) = match acc {
1826 crate::TypedClassAccessor::Getter { name, ret_ty, .. } => (
1827 "get",
1828 accessor_getter_name(&name.name),
1829 Vec::new(),
1830 ret_ty.clone(),
1831 ),
1832 crate::TypedClassAccessor::Setter { name, param, .. } => (
1833 "set",
1834 accessor_setter_name(&name.name),
1835 vec![param.clone()],
1836 crate::Type::Void,
1837 ),
1838 };
1839 let frame_name = format!("{kind} {}.{}", decl.name.name, acc.name().name);
1840 let method = crate::TypedClassMethod {
1841 name: crate::Ident {
1842 name,
1843 span: acc.name().span,
1844 },
1845 generics: Vec::new(),
1846 params,
1847 return_type,
1848 body: acc.body(),
1849 visibility: acc.visibility(),
1850 doc: None,
1851 };
1852 (method, frame_name)
1853 })
1854 .collect()
1855}
1856
1857fn emit_fill_optional_method(
1861 emitter: &mut crate::codegen::function_emitter::FunctionEmitter<'_>,
1862 (this_slot, struct_type_index): (u32, u32),
1863 object_fields: u32,
1864 method_slot: u32,
1865 field_slot: u32,
1866) {
1867 let payload = |emitter: &mut crate::codegen::function_emitter::FunctionEmitter<'_>| {
1868 emitter.instruction(Instruction::LocalGet(this_slot));
1869 emitter.instruction(Instruction::StructGet {
1870 struct_type_index,
1871 field_index: 2,
1872 });
1873 };
1874 payload(emitter);
1875 emitter.instruction(Instruction::I32Const(field_slot.cast_signed()));
1876 payload(emitter);
1877 emitter.instruction(Instruction::I32Const(method_slot.cast_signed()));
1878 emitter.instruction(Instruction::ArrayGet(object_fields));
1879 emitter.instruction(Instruction::ArraySet(object_fields));
1880}
1881
1882fn slot_closure_sig(
1883 slot: &MethodSlot,
1884) -> Result<crate::codegen::closures::ClosureSig, crate::compiler_error::CompilerFailure> {
1885 let sig = crate::Type::Function {
1886 params: slot.param_tys.clone(),
1887 ret: Box::new(slot.ret_ty.clone()),
1888 predicate: None,
1889 optional: 0,
1890 has_rest: false,
1891 };
1892 crate::codegen::closures::classify(&sig)
1893}
1894
1895#[derive(Clone)]
1896struct SlotDraft {
1897 name: String,
1898 owner: MangledName,
1899 param_tys: Vec<crate::Type>,
1900 argument_metadata: Option<String>,
1901 ret_ty: crate::Type,
1902 generic: bool,
1903}
1904
1905type ResolvedLayout = (Vec<FieldLayout>, Vec<SlotDraft>, Vec<OwnMethod>);
1908
1909fn parent_prefix(
1913 parent: &MangledName,
1914 layouts: &BTreeMap<MangledName, ResolvedLayout>,
1915 imported: &BTreeMap<MangledName, ImportedClassLayout>,
1916) -> Result<(Vec<FieldLayout>, Vec<SlotDraft>), CompilerFailure> {
1917 if let Some((fields, methods, _)) = layouts.get(parent) {
1918 return Ok((fields.clone(), methods.clone()));
1919 }
1920 let layout = imported
1921 .get(parent)
1922 .ok_or_else(|| unavailable_parent(parent))?;
1923 let fields = layout
1924 .fields
1925 .iter()
1926 .map(|name| FieldLayout {
1927 name: name.clone(),
1928 optional: layout.optional_fields.contains(name),
1929 narrowing_check: layout.narrowing_checks.get(name).cloned().map(Box::new),
1930 })
1931 .collect();
1932 let methods = layout
1933 .methods
1934 .iter()
1935 .map(|s| SlotDraft {
1936 name: s.name.clone(),
1937 owner: s.owner.clone(),
1938 param_tys: s.param_tys.clone(),
1939 argument_metadata: s.argument_metadata.clone(),
1940 ret_ty: s.ret_ty.clone(),
1941 generic: s.generic,
1942 })
1943 .collect();
1944 Ok((fields, methods))
1945}
1946
1947fn unavailable_parent(parent: &MangledName) -> CompilerFailure {
1948 internal_failure(format!(
1949 "parent class `{parent}` was not laid out before its subclass"
1950 ))
1951}
1952
1953fn inherited_slot_sig(
1957 sig_by_name: &BTreeMap<(MangledName, String), (u32, MethodSlotAbi)>,
1958 symbols: &SymbolTable,
1959 parent: &MangledName,
1960 slot: &str,
1961) -> Result<Option<(u32, MethodSlotAbi)>, CompilerFailure> {
1962 if let Some(entry) = sig_by_name.get(&(parent.clone(), slot.to_owned())) {
1963 return Ok(Some(entry.clone()));
1964 }
1965 let Some(sig) = symbols.class_method_sig(parent, slot) else {
1966 return Ok(None);
1967 };
1968 let abi = symbols
1969 .class_method_abi(parent, slot)
1970 .cloned()
1971 .ok_or_else(|| internal_failure("an inherited method slot has no recorded ABI"))?;
1972 Ok(Some((sig, abi)))
1973}
1974
1975fn topo_order(
1980 by_mangled: &BTreeMap<&MangledName, &TypedClassDecl>,
1981) -> Result<Vec<MangledName>, CompilerFailure> {
1982 let mut visited: BTreeSet<&MangledName> = BTreeSet::new();
1983 let mut out: Vec<MangledName> = Vec::new();
1984 for &start in by_mangled.keys() {
1985 let mut chain: Vec<&MangledName> = Vec::new();
1986 let mut next = Some(start);
1987 while let Some(mangled) = next {
1988 if visited.contains(mangled) {
1989 break;
1990 }
1991 let Some(decl) = by_mangled.get(mangled) else {
1992 break;
1993 };
1994 if chain.contains(&mangled) {
1995 return Err(internal_failure("class inheritance is cyclic"));
1996 }
1997 chain.push(mangled);
1998 next = decl.extends.as_ref();
1999 }
2000 for mangled in chain.into_iter().rev() {
2001 visited.insert(mangled);
2002 out.push(mangled.clone());
2003 }
2004 }
2005 Ok(out)
2006}
2007
2008fn slot_param_type(
2013 ctx: &CodegenCtx<'_>,
2014 slot_types: &[ValType],
2015 i: usize,
2016 p: &crate::TypedParam,
2017) -> Result<ValType, crate::compiler_error::CompilerFailure> {
2018 Ok(match slot_types.get(i) {
2019 Some(ty) => *ty,
2020 None => ctx.symbols.slot_value_type(&p.ty)?,
2021 })
2022}
2023
2024fn ctor_slot_types(
2029 ctx: &CodegenCtx<'_>,
2030 class: &MangledName,
2031) -> Result<Vec<ValType>, CompilerFailure> {
2032 ctx.symbols
2033 .class_ctor_abi(class)
2034 .map(<[ValType]>::to_vec)
2035 .ok_or_else(|| {
2036 internal_failure(format!(
2037 "class `{class}` has no recorded constructor signature"
2038 ))
2039 })
2040}
2041
2042fn slot_params(
2044 ctx: &CodegenCtx<'_>,
2045 params: &[crate::TypedParam],
2046 slot_types: &[ValType],
2047) -> Result<Vec<(crate::Ident, ValType)>, crate::compiler_error::CompilerFailure> {
2048 params
2049 .iter()
2050 .enumerate()
2051 .map(|(i, p)| Ok((p.name.clone(), slot_param_type(ctx, slot_types, i, p)?)))
2052 .collect::<Result<_, crate::compiler_error::CompilerFailure>>()
2053}
2054
2055fn rebind_erased_params(
2062 emitter: &mut FunctionEmitter<'_>,
2063 ctx: &CodegenCtx<'_>,
2064 params: &[crate::TypedParam],
2065 slot_types: &[ValType],
2066) -> Result<Vec<u32>, crate::compiler_error::CompilerFailure> {
2067 let mut param_slots = Vec::with_capacity(params.len());
2068 for (i, p) in params.iter().enumerate() {
2069 let param_local = parameter_local(i)?;
2070 let own_vt = ctx.symbols.value_type(&p.ty)?;
2071 if slot_param_type(ctx, slot_types, i, p)? == own_vt {
2072 param_slots.push(param_local);
2073 continue;
2074 }
2075 let shadow = emitter.add_anonymous_local(own_vt)?;
2076 emitter.instruction(Instruction::LocalGet(param_local));
2077 crate::codegen::cast_check::emit_checked_parameter_cast_on_stack(
2078 emitter,
2079 ctx,
2080 &crate::Type::Unknown,
2081 &p.ty,
2082 )?;
2083 emitter.instruction(Instruction::LocalSet(shadow));
2084 emitter.rebind_in_innermost_scope(&p.name.name, shadow, own_vt)?;
2085 param_slots.push(shadow);
2086 }
2087 Ok(param_slots)
2088}
2089
2090fn stub_body() -> Function {
2091 let mut f = Function::new([]);
2092 f.instruction(&Instruction::Unreachable);
2093 f.instruction(&Instruction::End);
2094 f
2095}
2096
2097fn emit_error_slot_body(
2101 symbols: &SymbolTable,
2102 intrinsics: IntrinsicTypeIndices,
2103 slot: u32,
2104 slot_fn_type: u32,
2105) -> Result<Function, CompilerFailure> {
2106 let vtable = symbols
2107 .class_vtable_global_idx(&crate::mangle::prelude("Error"))
2108 .ok_or_else(|| internal_failure("Error vtable is not imported for its subclass"))?;
2109 let mut body = Function::new([]);
2110 body.instruction(&Instruction::LocalGet(0));
2111 body.instruction(&Instruction::GlobalGet(vtable));
2112 body.instruction(&Instruction::StructGet {
2113 struct_type_index: intrinsics.vtable,
2114 field_index: slot,
2115 });
2116 body.instruction(&Instruction::CallRef(slot_fn_type));
2117 body.instruction(&Instruction::End);
2118 Ok(body)
2119}
2120
2121fn emit_class_equals_body(n_fields: u32, intrinsics: IntrinsicTypeIndices) -> Function {
2132 let locals: Vec<(u32, ValType)> = if n_fields == 0 {
2140 Vec::new()
2141 } else {
2142 vec![
2143 (
2144 2,
2145 ValType::Ref(RefType {
2146 nullable: true,
2147 heap_type: HeapType::Concrete(intrinsics.object),
2148 }),
2149 ),
2150 (1, ref_to(intrinsics.object)),
2151 (1, ref_to(intrinsics.equals_fn)),
2152 (2, ref_to(intrinsics.object_fields)),
2153 ]
2154 };
2155 let mut f = Function::new(locals);
2156 let (a, b) = (0u32, 1u32);
2157 let (a_fields, b_fields) = (6u32, 7u32);
2158
2159 f.instruction(&Instruction::LocalGet(a));
2160 f.instruction(&Instruction::LocalGet(b));
2161 f.instruction(&Instruction::RefEq);
2162 f.instruction(&Instruction::If(BlockType::Empty));
2163 f.instruction(&Instruction::I32Const(1));
2164 f.instruction(&Instruction::Return);
2165 f.instruction(&Instruction::End);
2166
2167 for operand in [a, b] {
2168 f.instruction(&Instruction::LocalGet(operand));
2169 f.instruction(&Instruction::StructGet {
2170 struct_type_index: intrinsics.object,
2171 field_index: 0,
2172 });
2173 }
2174 f.instruction(&Instruction::RefEq);
2175 f.instruction(&Instruction::I32Eqz);
2176 f.instruction(&Instruction::If(BlockType::Empty));
2177 f.instruction(&Instruction::I32Const(0));
2178 f.instruction(&Instruction::Return);
2179 f.instruction(&Instruction::End);
2180
2181 if n_fields > 0 {
2182 for (operand, dest) in [(a, a_fields), (b, b_fields)] {
2185 f.instruction(&Instruction::LocalGet(operand));
2186 f.instruction(&Instruction::RefCastNonNull(HeapType::Concrete(
2187 intrinsics.object_shape,
2188 )));
2189 f.instruction(&Instruction::StructGet {
2190 struct_type_index: intrinsics.object_shape,
2191 field_index: 2,
2192 });
2193 f.instruction(&Instruction::LocalSet(dest));
2194 }
2195 for slot in 0..n_fields {
2196 emit_payload_slot_equals(&mut f, slot, intrinsics);
2197 }
2198 }
2199
2200 f.instruction(&Instruction::I32Const(1));
2201 f.instruction(&Instruction::End);
2202 f
2203}
2204
2205fn emit_payload_slot_equals(f: &mut Function, slot: u32, intrinsics: IntrinsicTypeIndices) {
2210 let (elem_a, elem_b, elem_a_nn, eq_fn) = (2u32, 3u32, 4u32, 5u32);
2211 let (a_fields, b_fields) = (6u32, 7u32);
2212
2213 for (fields, dest) in [(a_fields, elem_a), (b_fields, elem_b)] {
2214 f.instruction(&Instruction::LocalGet(fields));
2215 f.instruction(&Instruction::I32Const(slot.cast_signed()));
2216 f.instruction(&Instruction::ArrayGet(intrinsics.object_fields));
2217 f.instruction(&Instruction::LocalSet(dest));
2218 }
2219
2220 f.instruction(&Instruction::LocalGet(elem_a));
2221 f.instruction(&Instruction::RefIsNull);
2222 f.instruction(&Instruction::If(BlockType::Empty));
2223 f.instruction(&Instruction::LocalGet(elem_b));
2224 f.instruction(&Instruction::RefIsNull);
2225 f.instruction(&Instruction::I32Eqz);
2226 f.instruction(&Instruction::If(BlockType::Empty));
2227 f.instruction(&Instruction::I32Const(0));
2228 f.instruction(&Instruction::Return);
2229 f.instruction(&Instruction::End);
2230 f.instruction(&Instruction::Else);
2231 f.instruction(&Instruction::LocalGet(elem_b));
2232 f.instruction(&Instruction::RefIsNull);
2233 f.instruction(&Instruction::If(BlockType::Empty));
2234 f.instruction(&Instruction::I32Const(0));
2235 f.instruction(&Instruction::Return);
2236 f.instruction(&Instruction::End);
2237
2238 f.instruction(&Instruction::LocalGet(elem_a));
2239 f.instruction(&Instruction::RefAsNonNull);
2240 f.instruction(&Instruction::LocalTee(elem_a_nn));
2241 f.instruction(&Instruction::StructGet {
2242 struct_type_index: intrinsics.object,
2243 field_index: 0,
2244 });
2245 f.instruction(&Instruction::StructGet {
2246 struct_type_index: intrinsics.vtable,
2247 field_index: 2,
2248 });
2249 f.instruction(&Instruction::LocalSet(eq_fn));
2250
2251 f.instruction(&Instruction::LocalGet(elem_a_nn));
2252 f.instruction(&Instruction::LocalGet(elem_b));
2253 f.instruction(&Instruction::RefAsNonNull);
2254 f.instruction(&Instruction::LocalGet(eq_fn));
2255 f.instruction(&Instruction::CallRef(intrinsics.equals_fn));
2256 f.instruction(&Instruction::I32Eqz);
2257 f.instruction(&Instruction::If(BlockType::Empty));
2258 f.instruction(&Instruction::I32Const(0));
2259 f.instruction(&Instruction::Return);
2260 f.instruction(&Instruction::End);
2261 f.instruction(&Instruction::End);
2262}
2263
2264fn emit_universal_slot_thunk(
2268 ctx: &CodegenCtx,
2269 method: &str,
2270) -> Result<Function, crate::compiler_error::CompilerFailure> {
2271 let mut emitter = super::function_emitter::FunctionEmitter::new(
2272 ctx,
2273 &[(
2274 crate::Ident {
2275 name: "self".into(),
2276 span: crate::Span::at(crate::FileId(0)),
2277 },
2278 ctx.symbols.value_type(&crate::Type::Unknown)?,
2279 )],
2280 )?;
2281 emitter.instruction(Instruction::LocalGet(0));
2282 super::function_emitter::expr::emit_interface_method_via_shape_with_receiver_on_stack(
2283 &mut emitter,
2284 ctx,
2285 method,
2286 &[],
2287 &crate::Type::String,
2288 None,
2289 )?;
2290 emitter.build()
2291}
2292
2293fn emit_class_to_string_body(
2294 intrinsics: IntrinsicTypeIndices,
2295 string_vtable_global_idx: u32,
2296) -> Result<Function, CompilerFailure> {
2297 let mut f = Function::new([]);
2298 crate::codegen::intrinsics::push_string_literal(
2299 &mut f,
2300 intrinsics,
2301 string_vtable_global_idx,
2302 "[object Object]",
2303 )?;
2304 f.instruction(&Instruction::End);
2305 Ok(f)
2306}
2307
2308fn emit_class_to_string_field_body(
2312 symbols: &SymbolTable,
2313 intrinsics: IntrinsicTypeIndices,
2314) -> Result<Function, CompilerFailure> {
2315 let object_vtable = symbols
2316 .prelude_global_idx("object_vtable")
2317 .ok_or_else(|| internal_failure("object_vtable is not imported from the prelude"))?;
2318 let mut f = Function::new([]);
2319 f.instruction(&Instruction::LocalGet(0));
2320 f.instruction(&Instruction::GlobalGet(object_vtable));
2321 f.instruction(&Instruction::StructGet {
2322 struct_type_index: intrinsics.vtable,
2323 field_index: 0,
2324 });
2325 f.instruction(&Instruction::CallRef(intrinsics.to_string_fn));
2326 f.instruction(&Instruction::End);
2327 Ok(f)
2328}
2329
2330fn emit_class_to_json_body(symbols: &SymbolTable) -> Result<Function, CompilerFailure> {
2333 let serializer = symbols
2334 .prelude_func_idx("ObjectConstructor##toJson")
2335 .ok_or_else(|| internal_failure("the object serializer is not imported"))?;
2336 let mut f = Function::new([]);
2337 f.instruction(&Instruction::LocalGet(0));
2338 f.instruction(&Instruction::Call(serializer));
2339 f.instruction(&Instruction::End);
2340 Ok(f)
2341}
2342
2343fn emit_class_hash_body(n_fields: u32, intrinsics: IntrinsicTypeIndices) -> Function {
2348 const FNV_BASIS: i32 = 0x811c9dc5_u32.cast_signed();
2349 const FNV_PRIME: i32 = 0x01000193;
2350
2351 if n_fields == 0 {
2352 let mut f = Function::new([]);
2353 f.instruction(&Instruction::I32Const(FNV_BASIS));
2354 f.instruction(&Instruction::End);
2355 return f;
2356 }
2357
2358 let locals: Vec<(u32, ValType)> = vec![
2365 (1, ref_to(intrinsics.object_fields)),
2366 (1, ValType::I32),
2367 (
2368 1,
2369 ValType::Ref(RefType {
2370 nullable: true,
2371 heap_type: HeapType::Concrete(intrinsics.object),
2372 }),
2373 ),
2374 (1, ref_to(intrinsics.object)),
2375 (1, ref_to(intrinsics.hash_fn)),
2376 ];
2377 let mut f = Function::new(locals);
2378 let (fields_arr, hash, elem, elem_nn, hash_fn) = (1u32, 2u32, 3u32, 4u32, 5u32);
2379
2380 f.instruction(&Instruction::LocalGet(0));
2381 f.instruction(&Instruction::RefCastNonNull(HeapType::Concrete(
2382 intrinsics.object_shape,
2383 )));
2384 f.instruction(&Instruction::StructGet {
2385 struct_type_index: intrinsics.object_shape,
2386 field_index: 2,
2387 });
2388 f.instruction(&Instruction::LocalSet(fields_arr));
2389
2390 f.instruction(&Instruction::I32Const(FNV_BASIS));
2391 f.instruction(&Instruction::LocalSet(hash));
2392
2393 for slot in 0..n_fields {
2394 f.instruction(&Instruction::LocalGet(fields_arr));
2395 f.instruction(&Instruction::I32Const(slot.cast_signed()));
2396 f.instruction(&Instruction::ArrayGet(intrinsics.object_fields));
2397 f.instruction(&Instruction::LocalSet(elem));
2398
2399 f.instruction(&Instruction::LocalGet(elem));
2400 f.instruction(&Instruction::RefIsNull);
2401 f.instruction(&Instruction::If(BlockType::Result(ValType::I32)));
2402 f.instruction(&Instruction::I32Const(0));
2403 f.instruction(&Instruction::Else);
2404 f.instruction(&Instruction::LocalGet(elem));
2405 f.instruction(&Instruction::RefAsNonNull);
2406 f.instruction(&Instruction::LocalTee(elem_nn));
2407 f.instruction(&Instruction::StructGet {
2408 struct_type_index: intrinsics.object,
2409 field_index: 0,
2410 });
2411 f.instruction(&Instruction::StructGet {
2412 struct_type_index: intrinsics.vtable,
2413 field_index: 3,
2414 });
2415 f.instruction(&Instruction::LocalSet(hash_fn));
2416 f.instruction(&Instruction::LocalGet(elem_nn));
2417 f.instruction(&Instruction::LocalGet(hash_fn));
2418 f.instruction(&Instruction::CallRef(intrinsics.hash_fn));
2419 f.instruction(&Instruction::End);
2420
2421 f.instruction(&Instruction::LocalGet(hash));
2423 f.instruction(&Instruction::I32Xor);
2424 f.instruction(&Instruction::I32Const(FNV_PRIME));
2425 f.instruction(&Instruction::I32Mul);
2426 f.instruction(&Instruction::LocalSet(hash));
2427 }
2428
2429 f.instruction(&Instruction::LocalGet(hash));
2430 f.instruction(&Instruction::End);
2431 f
2432}
2433
2434#[derive(Clone, Debug)]
2441pub(crate) enum RewritableMembers {
2442 Named(std::collections::BTreeSet<String>),
2443 Every,
2445}
2446
2447impl Default for RewritableMembers {
2448 fn default() -> Self {
2449 Self::Named(std::collections::BTreeSet::new())
2450 }
2451}
2452
2453impl RewritableMembers {
2454 pub(crate) fn of(ta: &TypedAst) -> Result<Self, crate::compiler_error::CompilerFailure> {
2455 let mut members = Self::default();
2456 for id in ta.stmt_ids().map_err(super::arena_failure)? {
2457 match &ta.try_stmt(id).map_err(super::arena_failure)?.kind {
2458 crate::TypedStmtKind::AssignField { name, .. } => {
2459 members.note_name(&name.name);
2460 }
2461 crate::TypedStmtKind::AssignIndex { index, elem_ty, .. } => {
2462 members.note_index(ta, *index, elem_ty)?;
2463 }
2464 _ => {}
2465 }
2466 }
2467 for id in ta.expr_ids().map_err(super::arena_failure)? {
2468 let crate::TypedExprKind::PostfixUnary { target, .. } =
2469 &ta.try_expr(id).map_err(super::arena_failure)?.kind
2470 else {
2471 continue;
2472 };
2473 match target {
2474 crate::PostfixTarget::Field { name, .. } => {
2475 members.note_name(&name.name);
2476 }
2477 crate::PostfixTarget::Index { index, elem_ty, .. } => {
2478 members.note_index(ta, *index, elem_ty)?;
2479 }
2480 _ => {}
2481 }
2482 }
2483 Ok(members)
2484 }
2485
2486 fn note_index(
2487 &mut self,
2488 ta: &TypedAst,
2489 index: crate::ExprId,
2490 elem_ty: &crate::Type,
2491 ) -> Result<(), crate::compiler_error::CompilerFailure> {
2492 if !may_hold_function(elem_ty) {
2493 return Ok(());
2494 }
2495 match &ta.try_expr(index).map_err(super::arena_failure)?.kind {
2496 crate::TypedExprKind::String(name) => self.note_name(name),
2497 _ => *self = Self::Every,
2498 }
2499 Ok(())
2500 }
2501
2502 fn note_name(&mut self, name: &str) {
2503 if let Self::Named(names) = self {
2504 names.insert(name.to_string());
2505 }
2506 }
2507
2508 pub(crate) fn includes(&self, name: &str) -> bool {
2509 match self {
2510 Self::Named(names) => names.contains(name),
2511 Self::Every => true,
2512 }
2513 }
2514}
2515
2516fn may_hold_function(ty: &crate::Type) -> bool {
2517 match ty.peel() {
2518 crate::Type::Union(members) => members.iter().any(may_hold_function),
2519 crate::Type::Function { .. }
2520 | crate::Type::Unknown
2521 | crate::Type::NonPrimitive
2522 | crate::Type::TypeVar(_)
2523 | crate::Type::GenericParam { .. }
2524 | crate::Type::Error => true,
2525 _ => false,
2526 }
2527}
2528
2529fn ref_to(idx: u32) -> ValType {
2530 ValType::Ref(RefType {
2531 nullable: false,
2532 heap_type: HeapType::Concrete(idx),
2533 })
2534}
2535
2536fn fieldtype_ref(idx: u32) -> FieldType {
2537 FieldType {
2538 element_type: StorageType::Val(ref_to(idx)),
2539 mutable: false,
2540 }
2541}
2542
2543fn fieldtype_ref_null(idx: u32) -> FieldType {
2544 FieldType {
2545 element_type: StorageType::Val(ValType::Ref(RefType {
2546 nullable: true,
2547 heap_type: HeapType::Concrete(idx),
2548 })),
2549 mutable: false,
2550 }
2551}
2552
2553fn vtable_supertype_idx(
2556 class: &ClassLayout,
2557 intrinsics: IntrinsicTypeIndices,
2558 symbols: &SymbolTable,
2559) -> Result<u32, CompilerFailure> {
2560 match &class.parent {
2561 Some(p) => symbols.class_vtable_type_idx(p).ok_or_else(|| {
2562 internal_failure("a parent vtable type was not reserved before its child")
2563 }),
2564 None => Ok(intrinsics.class_vtable),
2565 }
2566}
2567
2568fn struct_supertype_idx(
2571 class: &ClassLayout,
2572 intrinsics: IntrinsicTypeIndices,
2573 symbols: &SymbolTable,
2574) -> Result<u32, CompilerFailure> {
2575 match &class.parent {
2576 Some(p) => symbols.class_struct_type_idx(p).ok_or_else(|| {
2577 internal_failure("a parent struct type was not reserved before its child")
2578 }),
2579 None => Ok(intrinsics.object_shape),
2580 }
2581}
2582
2583fn substruct(fields: Vec<FieldType>, supertype: Option<u32>) -> SubType {
2584 SubType {
2585 is_final: false,
2586 supertype_idx: supertype,
2587 composite_type: CompositeType {
2588 inner: CompositeInnerType::Struct(StructType {
2589 fields: fields.into_boxed_slice(),
2590 }),
2591 shared: false,
2592 descriptor: None,
2593 describes: None,
2594 },
2595 }
2596}
2597
2598#[cfg(test)]
2599mod tests {
2600 use super::*;
2601 use crate::codegen::intrinsics::declare_intrinsic_types;
2602 use crate::runtime::intrinsic_types::build_intrinsic_types;
2603 use wasm_encoder::{
2604 ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, HeapType, Module, RefType,
2605 TypeSection, ValType,
2606 };
2607 use wasmtime::Config;
2608
2609 #[test]
2615 fn field_only_class_rec_group_canonicalizes_to_intrinsic_error() {
2616 let mut types = TypeSection::new();
2617 let intrinsics = declare_intrinsic_types(&mut types);
2618 let vtable_type_idx = crate::codegen::intrinsics::INTRINSIC_TYPE_COUNT;
2619 let struct_type_idx = vtable_type_idx + 1;
2620 let layout = ClassLayout {
2621 private_members: BTreeSet::new(),
2622 generics: Vec::new(),
2623 mangled: crate::mangle::prelude("ErrorWitness"),
2624 name: "ErrorWitness".to_string(),
2625 name_span: crate::Span::at(crate::FileId(0)),
2626 parent: None,
2627 fields: vec![
2628 FieldLayout {
2629 name: "message".to_string(),
2630 optional: false,
2631 narrowing_check: None,
2632 },
2633 FieldLayout {
2634 name: "name".to_string(),
2635 optional: false,
2636 narrowing_check: None,
2637 },
2638 ],
2639 methods: Vec::new(),
2640 own_methods: Vec::new(),
2641 adapter_slots: Vec::new(),
2642 ctor_params: Vec::new(),
2643 ctor_body: None,
2644 field_setup: Vec::new(),
2645 accessor_names: Vec::new(),
2646 struct_type_idx,
2647 vtable_type_idx,
2648 ctor_func_idx: 0,
2649 ctor_sig_idx: 0,
2650 ctor_init_func_idx: 0,
2651 ctor_init_sig_idx: 0,
2652 getter_func_idx: 0,
2653 setter_func_idx: 0,
2654 equals_func_idx: 0,
2655 to_string_func_idx: 0,
2656 to_json_func_idx: 0,
2657 hash_func_idx: 0,
2658 guarded_bodies: GuardedBodies::default(),
2659 };
2660 let plan = ClassPlan {
2661 package_name: "witness".to_string(),
2662 classes: Vec::new(),
2663 universal_stubs: [0; UNIVERSAL_STUB_COUNT],
2664 };
2665 let symbols = SymbolTable::default();
2666 let vtable = plan.vtable_subtype(&layout, intrinsics, &symbols).unwrap();
2667 let strukt = plan.struct_subtype(&layout, intrinsics, &symbols).unwrap();
2668 types.ty().rec([vtable, strukt]);
2669
2670 let mut module = Module::new();
2671 module.section(&types);
2672 let mut globals = GlobalSection::new();
2673 let mut exports = ExportSection::new();
2674 for (name, idx) in [("vtable", vtable_type_idx), ("struct", struct_type_idx)] {
2675 globals.global(
2676 GlobalType {
2677 val_type: ValType::Ref(RefType {
2678 nullable: true,
2679 heap_type: HeapType::Concrete(idx),
2680 }),
2681 mutable: false,
2682 shared: false,
2683 },
2684 &ConstExpr::ref_null(HeapType::Concrete(idx)),
2685 );
2686 exports.export(name, ExportKind::Global, idx - vtable_type_idx);
2687 }
2688 module.section(&globals);
2689 module.section(&exports);
2690
2691 let mut config = Config::new();
2692 config.wasm_gc(true);
2693 config.wasm_function_references(true);
2694 let engine = wasmtime::Engine::new(&config).unwrap();
2695 let intr = build_intrinsic_types(&engine).unwrap();
2696 let module = wasmtime::Module::new(&engine, module.finish()).unwrap();
2697 let recovered = |name: &str| {
2698 module
2699 .get_export(name)
2700 .unwrap()
2701 .global()
2702 .unwrap()
2703 .content()
2704 .as_ref()
2705 .map(|r| r.heap_type().clone())
2706 .unwrap()
2707 .as_concrete_struct()
2708 .unwrap()
2709 .clone()
2710 };
2711 assert!(wasmtime::StructType::eq(
2712 &intr.error_vtable,
2713 &recovered("vtable")
2714 ));
2715 assert!(wasmtime::StructType::eq(&intr.error, &recovered("struct")));
2716 }
2717}
2718
2719#[cfg(test)]
2720mod closure_adapter_invariant_tests {
2721 use super::*;
2722 use crate::codegen::invariant_tests::{assert_internal, with_context};
2723
2724 #[test]
2725 fn class_closure_adapter_requires_registered_method_and_slot_abi() {
2726 let (ta, diagnostics) = crate::compile::typecheck_to_typed_ast(
2727 "class C { f(a: number): number { return a; } } function main(): number { return new C().f(1); }",
2728 crate::FileId(0),
2729 );
2730 assert!(diagnostics.is_empty(), "{diagnostics:?}");
2731 let plan = ClassPlan::collect(&ta, &BTreeMap::new()).unwrap();
2732 let class = &plan.classes[0];
2733 let slot = &class.methods[0];
2734 let mut symbols = crate::codegen::tests::mock_symbols_with_intrinsics();
2735 with_context(&ta, &symbols, |ctx| {
2736 assert_internal(plan.emit_method_adapter_body(class, slot, ctx).unwrap_err());
2737 });
2738 symbols.record_class_method_func(slot.owner.clone(), slot.name.clone(), 1);
2739 with_context(&ta, &symbols, |ctx| {
2740 assert_internal(plan.emit_method_adapter_body(class, slot, ctx).unwrap_err());
2741 });
2742 symbols.record_class_method_abi(
2743 class.mangled.clone(),
2744 slot.name.clone(),
2745 MethodSlotAbi {
2746 params: vec![],
2747 ret: Some(ValType::F64),
2748 },
2749 );
2750 with_context(&ta, &symbols, |ctx| {
2751 assert_internal(plan.emit_method_adapter_body(class, slot, ctx).unwrap_err());
2752 });
2753 }
2754}
2755
2756#[cfg(test)]
2757mod layout_invariant_tests {
2758 use super::*;
2759 use crate::compiler_error::CompilerFailure;
2760
2761 fn typed(source: &str) -> TypedAst {
2762 let (ta, diagnostics) = crate::compile::typecheck_to_typed_ast(source, crate::FileId(0));
2763 assert!(diagnostics.is_empty(), "{diagnostics:?}");
2764 ta
2765 }
2766
2767 fn class_mut<'a>(ta: &'a mut TypedAst, name: &str) -> &'a mut TypedClassDecl {
2768 ta.types
2769 .iter_mut()
2770 .find_map(|decl| match decl {
2771 TypedTypeDecl::Class(class) if class.name.name == name => Some(class),
2772 _ => None,
2773 })
2774 .unwrap()
2775 }
2776
2777 fn assert_internal(result: Result<ClassPlan, CompilerFailure>) {
2778 assert!(
2779 matches!(result, Err(CompilerFailure::Internal { .. })),
2780 "expected an internal failure"
2781 );
2782 }
2783
2784 #[test]
2785 fn missing_or_cyclic_parents_stop_layout_instead_of_dropping_the_prefix() {
2786 let source = "class A { x: number = 1 } class B extends A { y: number = 2 } \
2787 function main(): number { return new B().x; }";
2788 let intact = typed(source);
2789 let plan = ClassPlan::collect(&intact, &BTreeMap::new()).unwrap();
2790 let b = plan.classes.iter().find(|class| class.name == "B").unwrap();
2791 assert_eq!(b.fields.len(), 2, "B inherits A's field prefix");
2792
2793 let mut orphan = intact.clone();
2795 orphan
2796 .types
2797 .retain(|decl| !matches!(decl, TypedTypeDecl::Class(class) if class.name.name == "A"));
2798 assert_internal(ClassPlan::collect(&orphan, &BTreeMap::new()));
2799
2800 let mut cyclic = intact;
2801 let b_name = class_mut(&mut cyclic, "B").mangled_name.clone();
2802 class_mut(&mut cyclic, "A").extends = Some(b_name);
2803 assert_internal(ClassPlan::collect(&cyclic, &BTreeMap::new()));
2804 }
2805
2806 #[test]
2807 fn guard_payload_arithmetic_is_checked() {
2808 assert_eq!(guard_slot_count(0, 0).unwrap(), 1);
2809 assert_eq!(guard_slot_count(2, 3).unwrap(), 12);
2810 assert!(guard_slot_count(u32::MAX, 0).is_err());
2811 assert!(guard_slot_count(1, u32::MAX).is_err());
2812 assert!(guard_slot_count(u32::MAX / 2, 3).is_err());
2813 }
2814}