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