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 }
1413 }
1414
1415 emitter.instruction(Instruction::LocalGet(this_slot));
1419 for i in 0..wasm_u32(class.ctor_params.len())? {
1420 emitter.instruction(Instruction::LocalGet(i));
1421 }
1422 emitter.instruction(Instruction::Call(class.ctor_init_func_idx));
1423
1424 emitter.instruction(Instruction::LocalGet(this_slot));
1427 emitter.build()
1428 }
1429
1430 fn emit_ctor_init(
1436 &self,
1437 class: &ClassLayout,
1438 ctx: &crate::codegen::CodegenCtx<'_>,
1439 ) -> Result<(Function, Vec<(u64, crate::Span)>), crate::compiler_error::CompilerFailure> {
1440 use crate::codegen::function_emitter::{FunctionEmitter, cast, stmt};
1441
1442 let object_idx = ctx
1443 .symbols
1444 .intrinsic_type_indices()
1445 .ok_or_else(|| internal_failure("intrinsic types are not declared"))?
1446 .object;
1447 let mut wasm_params: Vec<(crate::Ident, ValType)> = vec![(
1448 crate::Ident {
1449 name: "this".to_string(),
1450 span: crate::Span::at(ctx.file),
1451 },
1452 ref_to(object_idx),
1453 )];
1454 let ctor_slots = ctor_slot_types(ctx, &class.mangled)?;
1455 wasm_params.extend(slot_params(ctx, &class.ctor_params, &ctor_slots)?);
1456 let mut emitter = FunctionEmitter::new(ctx, &wasm_params)?;
1457 let param_slots = rebind_erased_params(&mut emitter, ctx, &class.ctor_params, &ctor_slots)?;
1458 emitter.prepare_parameter_initialization(class.ctor_body)?;
1459 emitter.emit_boxed_param_prologue(&class.ctor_params, ¶m_slots)?;
1461
1462 let this_slot = emitter.add_anonymous_local(ref_to(class.struct_type_idx))?;
1464 emitter.instruction(Instruction::LocalGet(0));
1465 emitter.instruction(Instruction::RefCastNonNull(HeapType::Concrete(
1466 class.struct_type_idx,
1467 )));
1468 emitter.instruction(Instruction::LocalSet(this_slot));
1469 emitter.set_this_local(this_slot)?;
1470 super::field_guards::bind_receiver(&mut emitter, ctx, this_slot, &class.mangled)?;
1471 emitter.set_ctor_class(class.mangled.clone());
1472
1473 if let Some(body) = class.ctor_body {
1474 if class.parent.is_none() {
1475 super::function_emitter::expr::emit_class_field_setup_phase(
1476 &mut emitter,
1477 ctx,
1478 &class.mangled,
1479 super::function_emitter::expr::FieldSetupPhase::Initializers,
1480 )?;
1481 }
1482 let defaults = ctx
1483 .ta
1484 .parameter_default_prologues
1485 .get(&body)
1486 .copied()
1487 .unwrap_or(0);
1488 if class.parent.is_none() && defaults > 0 {
1489 let body = ctx.ta.try_stmt(body).map_err(super::arena_failure)?;
1490 let crate::TypedStmtKind::Block(statements) = &body.kind else {
1491 return Err(internal_failure(
1492 "constructor default prologue is not a block",
1493 ));
1494 };
1495 let prologue = statements.get(..defaults).ok_or_else(|| {
1496 internal_failure("constructor default prologue is incomplete")
1497 })?;
1498 let body = statements.get(defaults..).ok_or_else(|| {
1499 internal_failure("constructor body is missing after defaults")
1500 })?;
1501 emitter.push_scope();
1502 for &statement in prologue {
1503 stmt::emit_statement(&mut emitter, ctx, statement)?;
1504 }
1505 super::function_emitter::expr::emit_class_field_setup_phase(
1506 &mut emitter,
1507 ctx,
1508 &class.mangled,
1509 super::function_emitter::expr::FieldSetupPhase::ParameterProperties,
1510 )?;
1511 for &statement in body {
1512 stmt::emit_statement(&mut emitter, ctx, statement)?;
1513 }
1514 emitter.pop_scope()?;
1515 } else {
1516 if class.parent.is_none() {
1517 super::function_emitter::expr::emit_class_field_setup_phase(
1518 &mut emitter,
1519 ctx,
1520 &class.mangled,
1521 super::function_emitter::expr::FieldSetupPhase::ParameterProperties,
1522 )?;
1523 }
1524 stmt::emit_statement(&mut emitter, ctx, body)?;
1525 }
1526 } else {
1527 if let Some(parent) = &class.parent {
1530 let parent_init =
1531 ctx.symbols
1532 .class_ctor_init_func_idx(parent)
1533 .ok_or_else(|| {
1534 internal_failure("a parent constructor init was not allocated")
1535 })?;
1536 let parent_slots = ctor_slot_types(ctx, parent)?;
1542 emitter.instruction(Instruction::LocalGet(this_slot));
1543 if param_slots.len() != class.ctor_params.len() {
1544 return Err(internal_failure(
1545 "constructor parameters and their locals differ in number",
1546 ));
1547 }
1548 for (i, (p, local)) in class.ctor_params.iter().zip(¶m_slots).enumerate() {
1549 emitter.instruction(Instruction::LocalGet(*local));
1550 if let Some(slot) = parent_slots.get(i).copied() {
1551 cast::emit_coerce_to_wasm_slot(&mut emitter, ctx, &p.ty, slot)?;
1552 }
1553 }
1554 emitter.instruction(Instruction::Call(parent_init));
1555 }
1556 crate::codegen::function_emitter::expr::emit_class_field_setup(
1557 &mut emitter,
1558 ctx,
1559 &class.mangled,
1560 )?;
1561 }
1562 emitter.build_with_lines()
1563 }
1564
1565 pub fn exported_funcs(
1573 &self,
1574 symbols: &SymbolTable,
1575 is_exported: impl Fn(&MangledName) -> bool,
1576 ) -> Result<Vec<(MangledName, u32)>, CompilerFailure> {
1577 let mut out: Vec<(MangledName, u32)> = Vec::new();
1578 for class in &self.classes {
1579 if !is_exported(&class.mangled) {
1580 continue;
1581 }
1582 out.push((
1583 crate::mangle::extend(&class.mangled, "constructor"),
1584 class.ctor_func_idx,
1585 ));
1586 out.push((
1587 crate::mangle::extend(&class.mangled, "constructor_init"),
1588 class.ctor_init_func_idx,
1589 ));
1590 for method in &class.own_methods {
1591 if method.generic {
1592 continue;
1593 }
1594 let idx = symbols
1595 .class_method_func_idx(&class.mangled, &method.name)
1596 .ok_or_else(|| internal_failure("a class method body was not allocated"))?;
1597 out.push((crate::mangle::extend(&class.mangled, &method.name), idx));
1598 }
1599 }
1600 Ok(out)
1601 }
1602
1603 pub fn exported_vtable_globals(
1607 &self,
1608 symbols: &SymbolTable,
1609 is_exported: impl Fn(&MangledName) -> bool,
1610 ) -> Result<Vec<(MangledName, u32)>, CompilerFailure> {
1611 self.classes
1612 .iter()
1613 .filter(|class| is_exported(&class.mangled))
1614 .map(|class| {
1615 let global_idx = symbols
1616 .class_vtable_global_idx(&class.mangled)
1617 .ok_or_else(|| internal_failure("a class vtable global was not emitted"))?;
1618 Ok((vtable_global_export_name(&class.mangled), global_idx))
1619 })
1620 .collect()
1621 }
1622
1623 pub fn declared_funcs(&self, symbols: &SymbolTable) -> Vec<u32> {
1626 if self.classes.is_empty() {
1627 return Vec::new();
1628 }
1629 let mut out: Vec<u32> = self.universal_stubs.to_vec();
1630 for class in &self.classes {
1631 for slot in &class.methods {
1635 if let Some(idx) = symbols.class_method_func_idx(&slot.owner, &slot.name) {
1636 out.push(idx);
1637 }
1638 }
1639 for slot in class.payload_slots() {
1643 if let Some(idx) = symbols.class_method_adapter_func_idx(&class.mangled, &slot.name)
1644 {
1645 out.push(idx);
1646 }
1647 }
1648 out.push(class.getter_func_idx);
1649 out.push(class.setter_func_idx);
1650 out.push(class.equals_func_idx);
1651 out.push(class.to_string_func_idx);
1652 out.push(class.to_json_func_idx);
1653 out.push(class.hash_func_idx);
1654 }
1655 out.sort_unstable();
1656 out.dedup();
1657 out
1658 }
1659}
1660
1661fn field_setup_steps(
1670 decl: &TypedClassDecl,
1671 inherited: &BTreeSet<String>,
1672) -> Result<Vec<FieldSetup>, CompilerFailure> {
1673 let mut steps = Vec::new();
1674 if let Some(ctor) = &decl.constructor {
1675 for field in decl.fields.iter().filter(|f| f.auto_assigned) {
1676 if let Some(param) = ctor.params.iter().find(|p| p.name.name == field.name.name) {
1677 steps.push(FieldSetup::ParamCopy {
1678 field: field.name.name.clone(),
1679 param: Box::new(param.clone()),
1680 });
1681 }
1682 }
1683 }
1684 for field in &decl.fields {
1685 if let Some(value) = field.initializer {
1686 steps.push(FieldSetup::Init {
1687 field: field.name.name.clone(),
1688 value,
1689 });
1690 } else if redeclares_uninitialized_slot(field, inherited) {
1691 steps.push(FieldSetup::Reset {
1692 field: field.name.name.clone(),
1693 });
1694 }
1695 }
1696 Ok(steps)
1697}
1698
1699pub(crate) fn method_vtable_slot(slot: usize) -> Result<u32, CompilerFailure> {
1702 VTABLE_METHOD_SLOT_BASE
1703 .checked_add(wasm_u32(slot)?)
1704 .ok_or_else(|| internal_failure("a class has too many vtable slots"))
1705}
1706
1707pub(super) fn guard_slot_count(
1710 inheritance_depth: u32,
1711 named_len: u32,
1712) -> Result<u32, CompilerFailure> {
1713 inheritance_depth
1714 .checked_add(1)
1715 .zip(named_len.checked_add(1))
1716 .and_then(|(levels, per_level)| levels.checked_mul(per_level))
1717 .ok_or_else(|| internal_failure("a class guard payload is too large"))
1718}
1719
1720fn class_decl<'a>(
1721 by_mangled: &BTreeMap<&MangledName, &'a TypedClassDecl>,
1722 mangled: &MangledName,
1723) -> Result<&'a TypedClassDecl, CompilerFailure> {
1724 by_mangled
1725 .get(mangled)
1726 .copied()
1727 .ok_or_else(|| internal_failure("class order names an undeclared class"))
1728}
1729
1730fn redeclares_uninitialized_slot(
1737 field: &crate::TypedClassField,
1738 inherited: &BTreeSet<String>,
1739) -> bool {
1740 field.optional && !field.auto_assigned && inherited.contains(&field.name.name)
1741}
1742
1743pub(crate) fn vtable_global_export_name(mangled: &MangledName) -> MangledName {
1747 crate::mangle::extend(mangled, "vtable global")
1748}
1749
1750pub(crate) fn accessor_getter_name(prop: &str) -> String {
1755 format!("get {prop}")
1756}
1757
1758pub(crate) fn accessor_setter_name(prop: &str) -> String {
1760 format!("set {prop}")
1761}
1762
1763pub(crate) fn accessor_closure_sig(kind: crate::AccessorKind) -> ClosureSig {
1769 match kind {
1770 crate::AccessorKind::Get => ClosureSig {
1771 arity: 0,
1772 is_void: false,
1773 },
1774 crate::AccessorKind::Set => ClosureSig {
1775 arity: 1,
1776 is_void: true,
1777 },
1778 }
1779}
1780
1781pub(crate) fn shadows_inherited_field<'a>(
1793 inherited: impl IntoIterator<Item = &'a str>,
1794 name: &str,
1795) -> bool {
1796 inherited.into_iter().any(|f| f == name)
1797}
1798
1799fn accessor_methods(decl: &TypedClassDecl) -> Vec<(crate::TypedClassMethod, String)> {
1809 decl.accessors
1810 .iter()
1811 .map(|acc| {
1812 let (kind, name, params, return_type) = match acc {
1813 crate::TypedClassAccessor::Getter { name, ret_ty, .. } => (
1814 "get",
1815 accessor_getter_name(&name.name),
1816 Vec::new(),
1817 ret_ty.clone(),
1818 ),
1819 crate::TypedClassAccessor::Setter { name, param, .. } => (
1820 "set",
1821 accessor_setter_name(&name.name),
1822 vec![param.clone()],
1823 crate::Type::Void,
1824 ),
1825 };
1826 let frame_name = format!("{kind} {}.{}", decl.name.name, acc.name().name);
1827 let method = crate::TypedClassMethod {
1828 name: crate::Ident {
1829 name,
1830 span: acc.name().span,
1831 },
1832 generics: Vec::new(),
1833 params,
1834 return_type,
1835 body: acc.body(),
1836 visibility: acc.visibility(),
1837 doc: None,
1838 };
1839 (method, frame_name)
1840 })
1841 .collect()
1842}
1843
1844fn slot_closure_sig(
1845 slot: &MethodSlot,
1846) -> Result<crate::codegen::closures::ClosureSig, crate::compiler_error::CompilerFailure> {
1847 let sig = crate::Type::Function {
1848 params: slot.param_tys.clone(),
1849 ret: Box::new(slot.ret_ty.clone()),
1850 predicate: None,
1851 optional: 0,
1852 has_rest: false,
1853 };
1854 crate::codegen::closures::classify(&sig)
1855}
1856
1857#[derive(Clone)]
1858struct SlotDraft {
1859 name: String,
1860 owner: MangledName,
1861 param_tys: Vec<crate::Type>,
1862 argument_metadata: Option<String>,
1863 ret_ty: crate::Type,
1864 generic: bool,
1865}
1866
1867type ResolvedLayout = (Vec<FieldLayout>, Vec<SlotDraft>, Vec<OwnMethod>);
1870
1871fn parent_prefix(
1875 parent: &MangledName,
1876 layouts: &BTreeMap<MangledName, ResolvedLayout>,
1877 imported: &BTreeMap<MangledName, ImportedClassLayout>,
1878) -> Result<(Vec<FieldLayout>, Vec<SlotDraft>), CompilerFailure> {
1879 if let Some((fields, methods, _)) = layouts.get(parent) {
1880 return Ok((fields.clone(), methods.clone()));
1881 }
1882 let layout = imported
1883 .get(parent)
1884 .ok_or_else(|| unavailable_parent(parent))?;
1885 let fields = layout
1886 .fields
1887 .iter()
1888 .map(|name| FieldLayout {
1889 name: name.clone(),
1890 optional: layout.optional_fields.contains(name),
1891 narrowing_check: layout.narrowing_checks.get(name).cloned().map(Box::new),
1892 })
1893 .collect();
1894 let methods = layout
1895 .methods
1896 .iter()
1897 .map(|s| SlotDraft {
1898 name: s.name.clone(),
1899 owner: s.owner.clone(),
1900 param_tys: s.param_tys.clone(),
1901 argument_metadata: s.argument_metadata.clone(),
1902 ret_ty: s.ret_ty.clone(),
1903 generic: s.generic,
1904 })
1905 .collect();
1906 Ok((fields, methods))
1907}
1908
1909fn unavailable_parent(parent: &MangledName) -> CompilerFailure {
1910 internal_failure(format!(
1911 "parent class `{parent}` was not laid out before its subclass"
1912 ))
1913}
1914
1915fn inherited_slot_sig(
1919 sig_by_name: &BTreeMap<(MangledName, String), (u32, MethodSlotAbi)>,
1920 symbols: &SymbolTable,
1921 parent: &MangledName,
1922 slot: &str,
1923) -> Result<Option<(u32, MethodSlotAbi)>, CompilerFailure> {
1924 if let Some(entry) = sig_by_name.get(&(parent.clone(), slot.to_owned())) {
1925 return Ok(Some(entry.clone()));
1926 }
1927 let Some(sig) = symbols.class_method_sig(parent, slot) else {
1928 return Ok(None);
1929 };
1930 let abi = symbols
1931 .class_method_abi(parent, slot)
1932 .cloned()
1933 .ok_or_else(|| internal_failure("an inherited method slot has no recorded ABI"))?;
1934 Ok(Some((sig, abi)))
1935}
1936
1937fn topo_order(
1942 by_mangled: &BTreeMap<&MangledName, &TypedClassDecl>,
1943) -> Result<Vec<MangledName>, CompilerFailure> {
1944 let mut visited: BTreeSet<&MangledName> = BTreeSet::new();
1945 let mut out: Vec<MangledName> = Vec::new();
1946 for &start in by_mangled.keys() {
1947 let mut chain: Vec<&MangledName> = Vec::new();
1948 let mut next = Some(start);
1949 while let Some(mangled) = next {
1950 if visited.contains(mangled) {
1951 break;
1952 }
1953 let Some(decl) = by_mangled.get(mangled) else {
1954 break;
1955 };
1956 if chain.contains(&mangled) {
1957 return Err(internal_failure("class inheritance is cyclic"));
1958 }
1959 chain.push(mangled);
1960 next = decl.extends.as_ref();
1961 }
1962 for mangled in chain.into_iter().rev() {
1963 visited.insert(mangled);
1964 out.push(mangled.clone());
1965 }
1966 }
1967 Ok(out)
1968}
1969
1970fn slot_param_type(
1975 ctx: &CodegenCtx<'_>,
1976 slot_types: &[ValType],
1977 i: usize,
1978 p: &crate::TypedParam,
1979) -> Result<ValType, crate::compiler_error::CompilerFailure> {
1980 Ok(match slot_types.get(i) {
1981 Some(ty) => *ty,
1982 None => ctx.symbols.slot_value_type(&p.ty)?,
1983 })
1984}
1985
1986fn ctor_slot_types(
1991 ctx: &CodegenCtx<'_>,
1992 class: &MangledName,
1993) -> Result<Vec<ValType>, CompilerFailure> {
1994 ctx.symbols
1995 .class_ctor_abi(class)
1996 .map(<[ValType]>::to_vec)
1997 .ok_or_else(|| {
1998 internal_failure(format!(
1999 "class `{class}` has no recorded constructor signature"
2000 ))
2001 })
2002}
2003
2004fn slot_params(
2006 ctx: &CodegenCtx<'_>,
2007 params: &[crate::TypedParam],
2008 slot_types: &[ValType],
2009) -> Result<Vec<(crate::Ident, ValType)>, crate::compiler_error::CompilerFailure> {
2010 params
2011 .iter()
2012 .enumerate()
2013 .map(|(i, p)| Ok((p.name.clone(), slot_param_type(ctx, slot_types, i, p)?)))
2014 .collect::<Result<_, crate::compiler_error::CompilerFailure>>()
2015}
2016
2017fn rebind_erased_params(
2024 emitter: &mut FunctionEmitter<'_>,
2025 ctx: &CodegenCtx<'_>,
2026 params: &[crate::TypedParam],
2027 slot_types: &[ValType],
2028) -> Result<Vec<u32>, crate::compiler_error::CompilerFailure> {
2029 let mut param_slots = Vec::with_capacity(params.len());
2030 for (i, p) in params.iter().enumerate() {
2031 let param_local = parameter_local(i)?;
2032 let own_vt = ctx.symbols.value_type(&p.ty)?;
2033 if slot_param_type(ctx, slot_types, i, p)? == own_vt {
2034 param_slots.push(param_local);
2035 continue;
2036 }
2037 let shadow = emitter.add_anonymous_local(own_vt)?;
2038 emitter.instruction(Instruction::LocalGet(param_local));
2039 crate::codegen::cast_check::emit_checked_parameter_cast_on_stack(
2040 emitter,
2041 ctx,
2042 &crate::Type::Unknown,
2043 &p.ty,
2044 )?;
2045 emitter.instruction(Instruction::LocalSet(shadow));
2046 emitter.rebind_in_innermost_scope(&p.name.name, shadow, own_vt)?;
2047 param_slots.push(shadow);
2048 }
2049 Ok(param_slots)
2050}
2051
2052fn stub_body() -> Function {
2053 let mut f = Function::new([]);
2054 f.instruction(&Instruction::Unreachable);
2055 f.instruction(&Instruction::End);
2056 f
2057}
2058
2059fn emit_error_slot_body(
2063 symbols: &SymbolTable,
2064 intrinsics: IntrinsicTypeIndices,
2065 slot: u32,
2066 slot_fn_type: u32,
2067) -> Result<Function, CompilerFailure> {
2068 let vtable = symbols
2069 .class_vtable_global_idx(&crate::mangle::prelude("Error"))
2070 .ok_or_else(|| internal_failure("Error vtable is not imported for its subclass"))?;
2071 let mut body = Function::new([]);
2072 body.instruction(&Instruction::LocalGet(0));
2073 body.instruction(&Instruction::GlobalGet(vtable));
2074 body.instruction(&Instruction::StructGet {
2075 struct_type_index: intrinsics.vtable,
2076 field_index: slot,
2077 });
2078 body.instruction(&Instruction::CallRef(slot_fn_type));
2079 body.instruction(&Instruction::End);
2080 Ok(body)
2081}
2082
2083fn emit_class_equals_body(n_fields: u32, intrinsics: IntrinsicTypeIndices) -> Function {
2094 let locals: Vec<(u32, ValType)> = if n_fields == 0 {
2102 Vec::new()
2103 } else {
2104 vec![
2105 (
2106 2,
2107 ValType::Ref(RefType {
2108 nullable: true,
2109 heap_type: HeapType::Concrete(intrinsics.object),
2110 }),
2111 ),
2112 (1, ref_to(intrinsics.object)),
2113 (1, ref_to(intrinsics.equals_fn)),
2114 (2, ref_to(intrinsics.object_fields)),
2115 ]
2116 };
2117 let mut f = Function::new(locals);
2118 let (a, b) = (0u32, 1u32);
2119 let (a_fields, b_fields) = (6u32, 7u32);
2120
2121 f.instruction(&Instruction::LocalGet(a));
2122 f.instruction(&Instruction::LocalGet(b));
2123 f.instruction(&Instruction::RefEq);
2124 f.instruction(&Instruction::If(BlockType::Empty));
2125 f.instruction(&Instruction::I32Const(1));
2126 f.instruction(&Instruction::Return);
2127 f.instruction(&Instruction::End);
2128
2129 for operand in [a, b] {
2130 f.instruction(&Instruction::LocalGet(operand));
2131 f.instruction(&Instruction::StructGet {
2132 struct_type_index: intrinsics.object,
2133 field_index: 0,
2134 });
2135 }
2136 f.instruction(&Instruction::RefEq);
2137 f.instruction(&Instruction::I32Eqz);
2138 f.instruction(&Instruction::If(BlockType::Empty));
2139 f.instruction(&Instruction::I32Const(0));
2140 f.instruction(&Instruction::Return);
2141 f.instruction(&Instruction::End);
2142
2143 if n_fields > 0 {
2144 for (operand, dest) in [(a, a_fields), (b, b_fields)] {
2147 f.instruction(&Instruction::LocalGet(operand));
2148 f.instruction(&Instruction::RefCastNonNull(HeapType::Concrete(
2149 intrinsics.object_shape,
2150 )));
2151 f.instruction(&Instruction::StructGet {
2152 struct_type_index: intrinsics.object_shape,
2153 field_index: 2,
2154 });
2155 f.instruction(&Instruction::LocalSet(dest));
2156 }
2157 for slot in 0..n_fields {
2158 emit_payload_slot_equals(&mut f, slot, intrinsics);
2159 }
2160 }
2161
2162 f.instruction(&Instruction::I32Const(1));
2163 f.instruction(&Instruction::End);
2164 f
2165}
2166
2167fn emit_payload_slot_equals(f: &mut Function, slot: u32, intrinsics: IntrinsicTypeIndices) {
2172 let (elem_a, elem_b, elem_a_nn, eq_fn) = (2u32, 3u32, 4u32, 5u32);
2173 let (a_fields, b_fields) = (6u32, 7u32);
2174
2175 for (fields, dest) in [(a_fields, elem_a), (b_fields, elem_b)] {
2176 f.instruction(&Instruction::LocalGet(fields));
2177 f.instruction(&Instruction::I32Const(slot.cast_signed()));
2178 f.instruction(&Instruction::ArrayGet(intrinsics.object_fields));
2179 f.instruction(&Instruction::LocalSet(dest));
2180 }
2181
2182 f.instruction(&Instruction::LocalGet(elem_a));
2183 f.instruction(&Instruction::RefIsNull);
2184 f.instruction(&Instruction::If(BlockType::Empty));
2185 f.instruction(&Instruction::LocalGet(elem_b));
2186 f.instruction(&Instruction::RefIsNull);
2187 f.instruction(&Instruction::I32Eqz);
2188 f.instruction(&Instruction::If(BlockType::Empty));
2189 f.instruction(&Instruction::I32Const(0));
2190 f.instruction(&Instruction::Return);
2191 f.instruction(&Instruction::End);
2192 f.instruction(&Instruction::Else);
2193 f.instruction(&Instruction::LocalGet(elem_b));
2194 f.instruction(&Instruction::RefIsNull);
2195 f.instruction(&Instruction::If(BlockType::Empty));
2196 f.instruction(&Instruction::I32Const(0));
2197 f.instruction(&Instruction::Return);
2198 f.instruction(&Instruction::End);
2199
2200 f.instruction(&Instruction::LocalGet(elem_a));
2201 f.instruction(&Instruction::RefAsNonNull);
2202 f.instruction(&Instruction::LocalTee(elem_a_nn));
2203 f.instruction(&Instruction::StructGet {
2204 struct_type_index: intrinsics.object,
2205 field_index: 0,
2206 });
2207 f.instruction(&Instruction::StructGet {
2208 struct_type_index: intrinsics.vtable,
2209 field_index: 2,
2210 });
2211 f.instruction(&Instruction::LocalSet(eq_fn));
2212
2213 f.instruction(&Instruction::LocalGet(elem_a_nn));
2214 f.instruction(&Instruction::LocalGet(elem_b));
2215 f.instruction(&Instruction::RefAsNonNull);
2216 f.instruction(&Instruction::LocalGet(eq_fn));
2217 f.instruction(&Instruction::CallRef(intrinsics.equals_fn));
2218 f.instruction(&Instruction::I32Eqz);
2219 f.instruction(&Instruction::If(BlockType::Empty));
2220 f.instruction(&Instruction::I32Const(0));
2221 f.instruction(&Instruction::Return);
2222 f.instruction(&Instruction::End);
2223 f.instruction(&Instruction::End);
2224}
2225
2226fn emit_universal_slot_thunk(
2230 ctx: &CodegenCtx,
2231 method: &str,
2232) -> Result<Function, crate::compiler_error::CompilerFailure> {
2233 let mut emitter = super::function_emitter::FunctionEmitter::new(
2234 ctx,
2235 &[(
2236 crate::Ident {
2237 name: "self".into(),
2238 span: crate::Span::at(crate::FileId(0)),
2239 },
2240 ctx.symbols.value_type(&crate::Type::Unknown)?,
2241 )],
2242 )?;
2243 emitter.instruction(Instruction::LocalGet(0));
2244 super::function_emitter::expr::emit_interface_method_via_shape_with_receiver_on_stack(
2245 &mut emitter,
2246 ctx,
2247 method,
2248 &[],
2249 &crate::Type::String,
2250 None,
2251 )?;
2252 emitter.build()
2253}
2254
2255fn emit_class_to_string_body(
2256 intrinsics: IntrinsicTypeIndices,
2257 string_vtable_global_idx: u32,
2258) -> Result<Function, CompilerFailure> {
2259 let mut f = Function::new([]);
2260 crate::codegen::intrinsics::push_string_literal(
2261 &mut f,
2262 intrinsics,
2263 string_vtable_global_idx,
2264 "[object Object]",
2265 )?;
2266 f.instruction(&Instruction::End);
2267 Ok(f)
2268}
2269
2270fn emit_class_to_string_field_body(
2274 symbols: &SymbolTable,
2275 intrinsics: IntrinsicTypeIndices,
2276) -> Result<Function, CompilerFailure> {
2277 let object_vtable = symbols
2278 .prelude_global_idx("object_vtable")
2279 .ok_or_else(|| internal_failure("object_vtable is not imported from the prelude"))?;
2280 let mut f = Function::new([]);
2281 f.instruction(&Instruction::LocalGet(0));
2282 f.instruction(&Instruction::GlobalGet(object_vtable));
2283 f.instruction(&Instruction::StructGet {
2284 struct_type_index: intrinsics.vtable,
2285 field_index: 0,
2286 });
2287 f.instruction(&Instruction::CallRef(intrinsics.to_string_fn));
2288 f.instruction(&Instruction::End);
2289 Ok(f)
2290}
2291
2292fn emit_class_to_json_body(symbols: &SymbolTable) -> Result<Function, CompilerFailure> {
2295 let serializer = symbols
2296 .prelude_func_idx("ObjectConstructor##toJson")
2297 .ok_or_else(|| internal_failure("the object serializer is not imported"))?;
2298 let mut f = Function::new([]);
2299 f.instruction(&Instruction::LocalGet(0));
2300 f.instruction(&Instruction::Call(serializer));
2301 f.instruction(&Instruction::End);
2302 Ok(f)
2303}
2304
2305fn emit_class_hash_body(n_fields: u32, intrinsics: IntrinsicTypeIndices) -> Function {
2310 const FNV_BASIS: i32 = 0x811c9dc5_u32.cast_signed();
2311 const FNV_PRIME: i32 = 0x01000193;
2312
2313 if n_fields == 0 {
2314 let mut f = Function::new([]);
2315 f.instruction(&Instruction::I32Const(FNV_BASIS));
2316 f.instruction(&Instruction::End);
2317 return f;
2318 }
2319
2320 let locals: Vec<(u32, ValType)> = vec![
2327 (1, ref_to(intrinsics.object_fields)),
2328 (1, ValType::I32),
2329 (
2330 1,
2331 ValType::Ref(RefType {
2332 nullable: true,
2333 heap_type: HeapType::Concrete(intrinsics.object),
2334 }),
2335 ),
2336 (1, ref_to(intrinsics.object)),
2337 (1, ref_to(intrinsics.hash_fn)),
2338 ];
2339 let mut f = Function::new(locals);
2340 let (fields_arr, hash, elem, elem_nn, hash_fn) = (1u32, 2u32, 3u32, 4u32, 5u32);
2341
2342 f.instruction(&Instruction::LocalGet(0));
2343 f.instruction(&Instruction::RefCastNonNull(HeapType::Concrete(
2344 intrinsics.object_shape,
2345 )));
2346 f.instruction(&Instruction::StructGet {
2347 struct_type_index: intrinsics.object_shape,
2348 field_index: 2,
2349 });
2350 f.instruction(&Instruction::LocalSet(fields_arr));
2351
2352 f.instruction(&Instruction::I32Const(FNV_BASIS));
2353 f.instruction(&Instruction::LocalSet(hash));
2354
2355 for slot in 0..n_fields {
2356 f.instruction(&Instruction::LocalGet(fields_arr));
2357 f.instruction(&Instruction::I32Const(slot.cast_signed()));
2358 f.instruction(&Instruction::ArrayGet(intrinsics.object_fields));
2359 f.instruction(&Instruction::LocalSet(elem));
2360
2361 f.instruction(&Instruction::LocalGet(elem));
2362 f.instruction(&Instruction::RefIsNull);
2363 f.instruction(&Instruction::If(BlockType::Result(ValType::I32)));
2364 f.instruction(&Instruction::I32Const(0));
2365 f.instruction(&Instruction::Else);
2366 f.instruction(&Instruction::LocalGet(elem));
2367 f.instruction(&Instruction::RefAsNonNull);
2368 f.instruction(&Instruction::LocalTee(elem_nn));
2369 f.instruction(&Instruction::StructGet {
2370 struct_type_index: intrinsics.object,
2371 field_index: 0,
2372 });
2373 f.instruction(&Instruction::StructGet {
2374 struct_type_index: intrinsics.vtable,
2375 field_index: 3,
2376 });
2377 f.instruction(&Instruction::LocalSet(hash_fn));
2378 f.instruction(&Instruction::LocalGet(elem_nn));
2379 f.instruction(&Instruction::LocalGet(hash_fn));
2380 f.instruction(&Instruction::CallRef(intrinsics.hash_fn));
2381 f.instruction(&Instruction::End);
2382
2383 f.instruction(&Instruction::LocalGet(hash));
2385 f.instruction(&Instruction::I32Xor);
2386 f.instruction(&Instruction::I32Const(FNV_PRIME));
2387 f.instruction(&Instruction::I32Mul);
2388 f.instruction(&Instruction::LocalSet(hash));
2389 }
2390
2391 f.instruction(&Instruction::LocalGet(hash));
2392 f.instruction(&Instruction::End);
2393 f
2394}
2395
2396#[derive(Clone, Debug)]
2403pub(crate) enum RewritableMembers {
2404 Named(std::collections::BTreeSet<String>),
2405 Every,
2407}
2408
2409impl Default for RewritableMembers {
2410 fn default() -> Self {
2411 Self::Named(std::collections::BTreeSet::new())
2412 }
2413}
2414
2415impl RewritableMembers {
2416 pub(crate) fn of(ta: &TypedAst) -> Result<Self, crate::compiler_error::CompilerFailure> {
2417 let mut members = Self::default();
2418 for id in ta.stmt_ids().map_err(super::arena_failure)? {
2419 match &ta.try_stmt(id).map_err(super::arena_failure)?.kind {
2420 crate::TypedStmtKind::AssignField { name, .. } => {
2421 members.note_name(&name.name);
2422 }
2423 crate::TypedStmtKind::AssignIndex { index, elem_ty, .. } => {
2424 members.note_index(ta, *index, elem_ty)?;
2425 }
2426 _ => {}
2427 }
2428 }
2429 for id in ta.expr_ids().map_err(super::arena_failure)? {
2430 let crate::TypedExprKind::PostfixUnary { target, .. } =
2431 &ta.try_expr(id).map_err(super::arena_failure)?.kind
2432 else {
2433 continue;
2434 };
2435 match target {
2436 crate::PostfixTarget::Field { name, .. } => {
2437 members.note_name(&name.name);
2438 }
2439 crate::PostfixTarget::Index { index, elem_ty, .. } => {
2440 members.note_index(ta, *index, elem_ty)?;
2441 }
2442 _ => {}
2443 }
2444 }
2445 Ok(members)
2446 }
2447
2448 fn note_index(
2449 &mut self,
2450 ta: &TypedAst,
2451 index: crate::ExprId,
2452 elem_ty: &crate::Type,
2453 ) -> Result<(), crate::compiler_error::CompilerFailure> {
2454 if !may_hold_function(elem_ty) {
2455 return Ok(());
2456 }
2457 match &ta.try_expr(index).map_err(super::arena_failure)?.kind {
2458 crate::TypedExprKind::String(name) => self.note_name(name),
2459 _ => *self = Self::Every,
2460 }
2461 Ok(())
2462 }
2463
2464 fn note_name(&mut self, name: &str) {
2465 if let Self::Named(names) = self {
2466 names.insert(name.to_string());
2467 }
2468 }
2469
2470 pub(crate) fn includes(&self, name: &str) -> bool {
2471 match self {
2472 Self::Named(names) => names.contains(name),
2473 Self::Every => true,
2474 }
2475 }
2476}
2477
2478fn may_hold_function(ty: &crate::Type) -> bool {
2479 match ty.peel() {
2480 crate::Type::Union(members) => members.iter().any(may_hold_function),
2481 crate::Type::Function { .. }
2482 | crate::Type::Unknown
2483 | crate::Type::TypeVar(_)
2484 | crate::Type::GenericParam { .. }
2485 | crate::Type::Error => true,
2486 _ => false,
2487 }
2488}
2489
2490fn ref_to(idx: u32) -> ValType {
2491 ValType::Ref(RefType {
2492 nullable: false,
2493 heap_type: HeapType::Concrete(idx),
2494 })
2495}
2496
2497fn fieldtype_ref(idx: u32) -> FieldType {
2498 FieldType {
2499 element_type: StorageType::Val(ref_to(idx)),
2500 mutable: false,
2501 }
2502}
2503
2504fn fieldtype_ref_null(idx: u32) -> FieldType {
2505 FieldType {
2506 element_type: StorageType::Val(ValType::Ref(RefType {
2507 nullable: true,
2508 heap_type: HeapType::Concrete(idx),
2509 })),
2510 mutable: false,
2511 }
2512}
2513
2514fn vtable_supertype_idx(
2517 class: &ClassLayout,
2518 intrinsics: IntrinsicTypeIndices,
2519 symbols: &SymbolTable,
2520) -> Result<u32, CompilerFailure> {
2521 match &class.parent {
2522 Some(p) => symbols.class_vtable_type_idx(p).ok_or_else(|| {
2523 internal_failure("a parent vtable type was not reserved before its child")
2524 }),
2525 None => Ok(intrinsics.class_vtable),
2526 }
2527}
2528
2529fn struct_supertype_idx(
2532 class: &ClassLayout,
2533 intrinsics: IntrinsicTypeIndices,
2534 symbols: &SymbolTable,
2535) -> Result<u32, CompilerFailure> {
2536 match &class.parent {
2537 Some(p) => symbols.class_struct_type_idx(p).ok_or_else(|| {
2538 internal_failure("a parent struct type was not reserved before its child")
2539 }),
2540 None => Ok(intrinsics.object_shape),
2541 }
2542}
2543
2544fn substruct(fields: Vec<FieldType>, supertype: Option<u32>) -> SubType {
2545 SubType {
2546 is_final: false,
2547 supertype_idx: supertype,
2548 composite_type: CompositeType {
2549 inner: CompositeInnerType::Struct(StructType {
2550 fields: fields.into_boxed_slice(),
2551 }),
2552 shared: false,
2553 descriptor: None,
2554 describes: None,
2555 },
2556 }
2557}
2558
2559#[cfg(test)]
2560mod tests {
2561 use super::*;
2562 use crate::codegen::intrinsics::declare_intrinsic_types;
2563 use crate::runtime::intrinsic_types::build_intrinsic_types;
2564 use wasm_encoder::{
2565 ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType, HeapType, Module, RefType,
2566 TypeSection, ValType,
2567 };
2568 use wasmtime::Config;
2569
2570 #[test]
2576 fn field_only_class_rec_group_canonicalizes_to_intrinsic_error() {
2577 let mut types = TypeSection::new();
2578 let intrinsics = declare_intrinsic_types(&mut types);
2579 let vtable_type_idx = crate::codegen::intrinsics::INTRINSIC_TYPE_COUNT;
2580 let struct_type_idx = vtable_type_idx + 1;
2581 let layout = ClassLayout {
2582 private_members: BTreeSet::new(),
2583 generics: Vec::new(),
2584 mangled: crate::mangle::prelude("ErrorWitness"),
2585 name: "ErrorWitness".to_string(),
2586 name_span: crate::Span::at(crate::FileId(0)),
2587 parent: None,
2588 fields: vec![
2589 FieldLayout {
2590 name: "message".to_string(),
2591 optional: false,
2592 narrowing_check: None,
2593 },
2594 FieldLayout {
2595 name: "name".to_string(),
2596 optional: false,
2597 narrowing_check: None,
2598 },
2599 ],
2600 methods: Vec::new(),
2601 own_methods: Vec::new(),
2602 adapter_slots: Vec::new(),
2603 ctor_params: Vec::new(),
2604 ctor_body: None,
2605 field_setup: Vec::new(),
2606 accessor_names: Vec::new(),
2607 struct_type_idx,
2608 vtable_type_idx,
2609 ctor_func_idx: 0,
2610 ctor_sig_idx: 0,
2611 ctor_init_func_idx: 0,
2612 ctor_init_sig_idx: 0,
2613 getter_func_idx: 0,
2614 setter_func_idx: 0,
2615 equals_func_idx: 0,
2616 to_string_func_idx: 0,
2617 to_json_func_idx: 0,
2618 hash_func_idx: 0,
2619 guarded_bodies: GuardedBodies::default(),
2620 };
2621 let plan = ClassPlan {
2622 package_name: "witness".to_string(),
2623 classes: Vec::new(),
2624 universal_stubs: [0; UNIVERSAL_STUB_COUNT],
2625 };
2626 let symbols = SymbolTable::default();
2627 let vtable = plan.vtable_subtype(&layout, intrinsics, &symbols).unwrap();
2628 let strukt = plan.struct_subtype(&layout, intrinsics, &symbols).unwrap();
2629 types.ty().rec([vtable, strukt]);
2630
2631 let mut module = Module::new();
2632 module.section(&types);
2633 let mut globals = GlobalSection::new();
2634 let mut exports = ExportSection::new();
2635 for (name, idx) in [("vtable", vtable_type_idx), ("struct", struct_type_idx)] {
2636 globals.global(
2637 GlobalType {
2638 val_type: ValType::Ref(RefType {
2639 nullable: true,
2640 heap_type: HeapType::Concrete(idx),
2641 }),
2642 mutable: false,
2643 shared: false,
2644 },
2645 &ConstExpr::ref_null(HeapType::Concrete(idx)),
2646 );
2647 exports.export(name, ExportKind::Global, idx - vtable_type_idx);
2648 }
2649 module.section(&globals);
2650 module.section(&exports);
2651
2652 let mut config = Config::new();
2653 config.wasm_gc(true);
2654 config.wasm_function_references(true);
2655 let engine = wasmtime::Engine::new(&config).unwrap();
2656 let intr = build_intrinsic_types(&engine).unwrap();
2657 let module = wasmtime::Module::new(&engine, module.finish()).unwrap();
2658 let recovered = |name: &str| {
2659 module
2660 .get_export(name)
2661 .unwrap()
2662 .global()
2663 .unwrap()
2664 .content()
2665 .as_ref()
2666 .map(|r| r.heap_type().clone())
2667 .unwrap()
2668 .as_concrete_struct()
2669 .unwrap()
2670 .clone()
2671 };
2672 assert!(wasmtime::StructType::eq(
2673 &intr.error_vtable,
2674 &recovered("vtable")
2675 ));
2676 assert!(wasmtime::StructType::eq(&intr.error, &recovered("struct")));
2677 }
2678}
2679
2680#[cfg(test)]
2681mod closure_adapter_invariant_tests {
2682 use super::*;
2683 use crate::codegen::invariant_tests::{assert_internal, with_context};
2684
2685 #[test]
2686 fn class_closure_adapter_requires_registered_method_and_slot_abi() {
2687 let (ta, diagnostics) = crate::compile::typecheck_to_typed_ast(
2688 "class C { f(a: number): number { return a; } } function main(): number { return new C().f(1); }",
2689 crate::FileId(0),
2690 );
2691 assert!(diagnostics.is_empty(), "{diagnostics:?}");
2692 let plan = ClassPlan::collect(&ta, &BTreeMap::new()).unwrap();
2693 let class = &plan.classes[0];
2694 let slot = &class.methods[0];
2695 let mut symbols = crate::codegen::tests::mock_symbols_with_intrinsics();
2696 with_context(&ta, &symbols, |ctx| {
2697 assert_internal(plan.emit_method_adapter_body(class, slot, ctx).unwrap_err());
2698 });
2699 symbols.record_class_method_func(slot.owner.clone(), slot.name.clone(), 1);
2700 with_context(&ta, &symbols, |ctx| {
2701 assert_internal(plan.emit_method_adapter_body(class, slot, ctx).unwrap_err());
2702 });
2703 symbols.record_class_method_abi(
2704 class.mangled.clone(),
2705 slot.name.clone(),
2706 MethodSlotAbi {
2707 params: vec![],
2708 ret: Some(ValType::F64),
2709 },
2710 );
2711 with_context(&ta, &symbols, |ctx| {
2712 assert_internal(plan.emit_method_adapter_body(class, slot, ctx).unwrap_err());
2713 });
2714 }
2715}
2716
2717#[cfg(test)]
2718mod layout_invariant_tests {
2719 use super::*;
2720 use crate::compiler_error::CompilerFailure;
2721
2722 fn typed(source: &str) -> TypedAst {
2723 let (ta, diagnostics) = crate::compile::typecheck_to_typed_ast(source, crate::FileId(0));
2724 assert!(diagnostics.is_empty(), "{diagnostics:?}");
2725 ta
2726 }
2727
2728 fn class_mut<'a>(ta: &'a mut TypedAst, name: &str) -> &'a mut TypedClassDecl {
2729 ta.types
2730 .iter_mut()
2731 .find_map(|decl| match decl {
2732 TypedTypeDecl::Class(class) if class.name.name == name => Some(class),
2733 _ => None,
2734 })
2735 .unwrap()
2736 }
2737
2738 fn assert_internal(result: Result<ClassPlan, CompilerFailure>) {
2739 assert!(
2740 matches!(result, Err(CompilerFailure::Internal { .. })),
2741 "expected an internal failure"
2742 );
2743 }
2744
2745 #[test]
2746 fn missing_or_cyclic_parents_stop_layout_instead_of_dropping_the_prefix() {
2747 let source = "class A { x: number = 1 } class B extends A { y: number = 2 } \
2748 function main(): number { return new B().x; }";
2749 let intact = typed(source);
2750 let plan = ClassPlan::collect(&intact, &BTreeMap::new()).unwrap();
2751 let b = plan.classes.iter().find(|class| class.name == "B").unwrap();
2752 assert_eq!(b.fields.len(), 2, "B inherits A's field prefix");
2753
2754 let mut orphan = intact.clone();
2756 orphan
2757 .types
2758 .retain(|decl| !matches!(decl, TypedTypeDecl::Class(class) if class.name.name == "A"));
2759 assert_internal(ClassPlan::collect(&orphan, &BTreeMap::new()));
2760
2761 let mut cyclic = intact;
2762 let b_name = class_mut(&mut cyclic, "B").mangled_name.clone();
2763 class_mut(&mut cyclic, "A").extends = Some(b_name);
2764 assert_internal(ClassPlan::collect(&cyclic, &BTreeMap::new()));
2765 }
2766
2767 #[test]
2768 fn guard_payload_arithmetic_is_checked() {
2769 assert_eq!(guard_slot_count(0, 0).unwrap(), 1);
2770 assert_eq!(guard_slot_count(2, 3).unwrap(), 12);
2771 assert!(guard_slot_count(u32::MAX, 0).is_err());
2772 assert!(guard_slot_count(1, u32::MAX).is_err());
2773 assert!(guard_slot_count(u32::MAX / 2, 3).is_err());
2774 }
2775}