1use std::collections::{BTreeMap, BTreeSet};
19
20use wasm_encoder::{
21 EntityType, FieldType, GlobalType, HeapType, ImportSection, RefType, StorageType, StructType,
22 SubType, TypeSection, ValType,
23};
24
25use crate::codegen::classes::{method_vtable_slot, shadows_inherited_field};
26use crate::codegen::dependency_usage::DependencyUsage;
27use crate::codegen::intrinsics::IntrinsicTypeIndices;
28use crate::codegen::symbol_table::{MethodSlotAbi, SymbolTable};
29use crate::codegen::{internal_failure, next_index, wasm_u32};
30use crate::compiler_error::CompilerFailure;
31use crate::{MangledName, Param, TypeKind, TypedAst, TypedTypeDecl};
32
33pub struct ImportedClassLayout {
37 pub private_members: BTreeSet<String>,
38 pub fields: Vec<String>,
40 pub optional_fields: std::collections::BTreeSet<String>,
41 pub narrowing_checks: BTreeMap<String, crate::FieldNarrowingCheck>,
42 pub methods: Vec<ImportedSlot>,
44 pub ctor_params: Vec<crate::TypedParam>,
47}
48
49pub struct ImportedSlot {
50 pub name: String,
51 pub owner: MangledName,
54 pub param_tys: Vec<crate::Type>,
57 pub argument_metadata: Option<String>,
58 pub ret_ty: crate::Type,
59 pub generic: bool,
60}
61
62struct ClassInfo<'a> {
67 generics: &'a [String],
68 package: &'a str,
69 compiled_class: bool,
70 runtime_generics: &'a BTreeSet<MangledName>,
71 name: &'a str,
72 extends: Option<MangledName>,
73 fields: &'a BTreeMap<String, crate::FieldSig>,
74 narrowing_checks: &'a BTreeMap<String, crate::FieldNarrowingCheck>,
75 methods: &'a BTreeMap<String, crate::MethodSig>,
76 constructor: &'a [Param],
77 accessors: &'a [crate::AccessorSig],
80 statics: &'a BTreeMap<String, crate::MethodSig>,
81 static_visibility: &'a BTreeMap<String, crate::Visibility>,
82 static_fields: &'a BTreeMap<String, crate::FieldSig>,
83}
84
85impl<'a> ClassInfo<'a> {
86 fn public_statics(&self) -> impl Iterator<Item = (&'a String, &'a crate::MethodSig)> {
89 self.statics.iter().filter(|(name, _)| {
90 self.static_visibility.get(*name) != Some(&crate::Visibility::Private)
91 })
92 }
93
94 fn public_static_fields(&self) -> impl Iterator<Item = (&'a String, &'a crate::FieldSig)> {
95 self.static_fields
96 .iter()
97 .filter(|(_, field)| field.visibility != crate::Visibility::Private)
98 }
99}
100
101fn vtable_methods(class: &ClassInfo<'_>) -> BTreeMap<String, (Vec<crate::Type>, crate::Type)> {
106 let mut out: BTreeMap<String, (Vec<crate::Type>, crate::Type)> = class
107 .methods
108 .iter()
109 .map(|(name, sig)| {
110 (
111 name.clone(),
112 (
113 sig.params.iter().map(|p| p.ty.clone()).collect(),
114 sig.ret.clone(),
115 ),
116 )
117 })
118 .collect();
119 for acc in class.accessors {
120 match acc {
121 crate::AccessorSig::Getter { name, ret_ty } => {
122 out.insert(
123 crate::codegen::classes::accessor_getter_name(name),
124 (Vec::new(), ret_ty.clone()),
125 );
126 }
127 crate::AccessorSig::Setter { name, param } => {
128 out.insert(
129 crate::codegen::classes::accessor_setter_name(name),
130 (vec![param.ty.clone()], crate::Type::Void),
131 );
132 }
133 }
134 }
135 out
136}
137
138fn host_module_for<'a>(class: &ClassInfo<'a>) -> &'a str {
143 class.package
144}
145
146#[allow(clippy::too_many_arguments)]
150pub fn reconstruct(
151 dependencies: &[&crate::PackageDeclaration],
152 ta: &TypedAst,
153 usage: &DependencyUsage,
154 intrinsics: IntrinsicTypeIndices,
155 file: crate::FileId,
156 types: &mut TypeSection,
157 next_type_idx: &mut u32,
158 imports: &mut ImportSection,
159 next_func_idx: &mut u32,
160 next_global_idx: &mut u32,
161 symbols: &mut SymbolTable,
162) -> Result<BTreeMap<MangledName, ImportedClassLayout>, crate::compiler_error::CompilerFailure> {
163 let info = collect_class_info(dependencies);
164 let order = needed_in_topo_order(&info, ta, usage)?;
165
166 let mut layouts: BTreeMap<MangledName, ImportedClassLayout> = BTreeMap::new();
167 for mangled in &order {
168 let class = class_info(&info, mangled)?;
169 let layout = reconstruct_one(
170 mangled,
171 class,
172 &info,
173 &layouts,
174 intrinsics,
175 file,
176 types,
177 next_type_idx,
178 imports,
179 next_func_idx,
180 next_global_idx,
181 symbols,
182 )?;
183 layouts.insert(mangled.clone(), layout);
184 }
185 for mangled in &order {
187 import_statics(
188 mangled,
189 class_info(&info, mangled)?,
190 usage,
191 types,
192 next_type_idx,
193 imports,
194 next_func_idx,
195 next_global_idx,
196 symbols,
197 )?;
198 }
199 Ok(layouts)
200}
201
202#[allow(clippy::too_many_arguments)]
225fn import_statics(
226 mangled: &MangledName,
227 class: &ClassInfo<'_>,
228 usage: &DependencyUsage,
229 types: &mut TypeSection,
230 next_type_idx: &mut u32,
231 imports: &mut ImportSection,
232 next_func_idx: &mut u32,
233 next_global_idx: &mut u32,
234 symbols: &mut SymbolTable,
235) -> Result<(), crate::compiler_error::CompilerFailure> {
236 for (name, sig) in class.public_statics() {
237 let key = crate::mangle::static_member(mangled, name);
238 if !usage.is_value_used(&key) {
239 continue;
240 }
241 let mut param_types: Vec<ValType> = sig
242 .params
243 .iter()
244 .map(|p| symbols.value_type(&p.ty))
245 .collect::<Result<_, _>>()?;
246 if class.runtime_generics.contains(&key) {
247 symbols.runtime_generic_functions.insert(key.clone());
248 param_types.push(super::runtime_descriptors::environment_type(symbols)?);
249 }
250 let results = symbols.wasm_result(&sig.ret)?;
251 let sig_idx = next_index(next_type_idx)?;
252 types.ty().function(param_types, results);
253 imports.import(
254 host_module_for(class),
255 key.as_str(),
256 EntityType::Function(sig_idx),
257 );
258 let idx = next_index(next_func_idx)?;
259 symbols.record_imported_fn(
260 key,
261 idx,
262 sig.params.iter().map(|p| p.ty.clone()).collect(),
263 sig.ret.clone(),
264 false,
265 );
266 }
267 for (name, field) in class.public_static_fields() {
268 let key = crate::mangle::static_member(mangled, name);
269 if !usage.is_value_used(&key) {
270 continue;
271 }
272 imports.import(
275 host_module_for(class),
276 key.as_str(),
277 EntityType::Global(GlobalType {
278 val_type: super::global_val_type(&field.ty, symbols)?,
279 mutable: true,
280 shared: false,
281 }),
282 );
283 symbols.record_typed_global(key, next_index(next_global_idx)?, field.ty.clone());
284 }
285 Ok(())
286}
287
288fn collect_class_info<'a>(
289 dependencies: &'a [&'a crate::PackageDeclaration],
290) -> BTreeMap<MangledName, ClassInfo<'a>> {
291 let mut info = BTreeMap::new();
292 for defs in dependencies {
293 for sym in defs.runtime_types.values().chain(defs.types.values()) {
294 if let TypeKind::Class {
295 generics,
296 extends,
297 fields,
298 narrowing_checks,
299 methods,
300 accessors,
301 constructor,
302 statics,
303 static_visibility,
304 static_fields,
305 ..
306 } = &sym.kind
307 {
308 info.insert(
309 sym.mangled_name.clone(),
310 ClassInfo {
311 generics,
312 runtime_generics: &defs.runtime_generics,
313 package: defs.package_name.as_str(),
314 compiled_class: defs.runtime_types.contains_key(sym.mangled_name.as_str()),
315 name: sym.name.as_str(),
316 extends: extends.as_ref().map(|e| e.parent.clone()),
319 fields,
320 narrowing_checks,
321 methods,
322 constructor,
323 accessors,
324 statics,
325 static_visibility,
326 static_fields,
327 },
328 );
329 }
330 }
331 }
332 info
333}
334
335fn needed_in_topo_order(
339 info: &BTreeMap<MangledName, ClassInfo<'_>>,
340 ta: &TypedAst,
341 usage: &DependencyUsage,
342) -> Result<Vec<MangledName>, CompilerFailure> {
343 let mut roots: BTreeSet<MangledName> = BTreeSet::new();
344 for mangled in info.keys() {
345 if usage.is_type_reachable_by_name(mangled) {
348 roots.insert(mangled.clone());
349 }
350 }
351 for decl in &ta.types {
353 if let TypedTypeDecl::Class(c) = decl
354 && let Some(parent) = &c.extends
355 && info.contains_key(parent)
356 {
357 roots.insert(parent.clone());
358 }
359 }
360
361 let mut order: Vec<MangledName> = Vec::new();
362 let mut seen: BTreeSet<&MangledName> = BTreeSet::new();
363 for root in &roots {
364 push_ancestors_first(info, root, &mut seen, &mut order)?;
365 }
366 Ok(order)
367}
368
369fn push_ancestors_first<'a>(
374 info: &'a BTreeMap<MangledName, ClassInfo<'_>>,
375 root: &'a MangledName,
376 seen: &mut BTreeSet<&'a MangledName>,
377 order: &mut Vec<MangledName>,
378) -> Result<(), CompilerFailure> {
379 let mut chain: Vec<&MangledName> = Vec::new();
380 let mut next = Some(root);
381 while let Some(mangled) = next {
382 if seen.contains(mangled) {
383 break;
384 }
385 let class = info.get(mangled).ok_or_else(|| {
386 internal_failure(format!(
387 "the dependency closure passed to codegen has no declaration for \
388 `{mangled}`, an ancestor of imported class `{root}`"
389 ))
390 })?;
391 if chain.contains(&mangled) {
392 return Err(internal_failure(format!(
393 "imported class `{mangled}` inherits from itself"
394 )));
395 }
396 chain.push(mangled);
397 next = class.extends.as_ref();
398 }
399 for mangled in chain.into_iter().rev() {
400 seen.insert(mangled);
401 order.push(mangled.clone());
402 }
403 Ok(())
404}
405
406fn class_info<'i, 'a>(
407 info: &'i BTreeMap<MangledName, ClassInfo<'a>>,
408 mangled: &MangledName,
409) -> Result<&'i ClassInfo<'a>, CompilerFailure> {
410 info.get(mangled).ok_or_else(|| {
411 internal_failure(format!(
412 "imported class `{mangled}` has no reconstructed declaration"
413 ))
414 })
415}
416
417#[allow(clippy::too_many_arguments)]
418fn reconstruct_one(
419 mangled: &MangledName,
420 class: &ClassInfo<'_>,
421 info: &BTreeMap<MangledName, ClassInfo<'_>>,
422 layouts: &BTreeMap<MangledName, ImportedClassLayout>,
423 intrinsics: IntrinsicTypeIndices,
424 file: crate::FileId,
425 types: &mut TypeSection,
426 next_type_idx: &mut u32,
427 imports: &mut ImportSection,
428 next_func_idx: &mut u32,
429 next_global_idx: &mut u32,
430 symbols: &mut SymbolTable,
431) -> Result<ImportedClassLayout, crate::compiler_error::CompilerFailure> {
432 let parent_layout = match &class.extends {
434 Some(parent) => Some(layouts.get(parent).ok_or_else(|| {
435 internal_failure(format!(
436 "imported class `{mangled}` was reconstructed before its parent `{parent}`"
437 ))
438 })?),
439 None => None,
440 };
441 let mut fields: Vec<String> = parent_layout.map(|l| l.fields.clone()).unwrap_or_default();
445 for name in class
446 .fields
447 .keys()
448 .filter(|name| !class.accessors.iter().any(|a| a.name() == name.as_str()))
449 {
450 if shadows_inherited_field(fields.iter().map(String::as_str), name) {
451 continue;
452 }
453 fields.push(name.clone());
454 }
455 let mut optional_fields = parent_layout
456 .map(|layout| layout.optional_fields.clone())
457 .unwrap_or_default();
458 for (name, field) in class.fields {
459 if field.optional {
460 optional_fields.insert(name.clone());
461 } else {
462 optional_fields.remove(name);
463 }
464 }
465 let mut narrowing_checks = parent_layout
466 .map(|layout| layout.narrowing_checks.clone())
467 .unwrap_or_default();
468 narrowing_checks.extend(class.narrowing_checks.clone());
469
470 let methods = vtable_methods(class);
473
474 let mut slots: Vec<ImportedSlot> = parent_layout
477 .map(|l| {
478 l.methods
479 .iter()
480 .map(|s| ImportedSlot {
481 name: s.name.clone(),
482 owner: s.owner.clone(),
483 param_tys: s.param_tys.clone(),
484 argument_metadata: s.argument_metadata.clone(),
485 ret_ty: s.ret_ty.clone(),
486 generic: s.generic,
487 })
488 .collect()
489 })
490 .unwrap_or_default();
491 for (name, (param_tys, ret_ty)) in &methods {
492 let argument_metadata = match class.methods.get(name) {
493 Some(sig) => super::call_arguments::metadata(sig.params.iter().map(|param| {
494 (
495 param
496 .default
497 .as_ref()
498 .or(param.optional.then_some(&crate::DefaultValue::Undefined)),
499 param.rest,
500 )
501 })),
502 None => None,
503 };
504 let generic = class
505 .methods
506 .get(name)
507 .is_some_and(|sig| !sig.generics.is_empty());
508 if let Some(slot) = slots.iter_mut().find(|s| &s.name == name) {
509 slot.owner = mangled.clone();
512 slot.param_tys = param_tys.clone();
513 slot.argument_metadata = argument_metadata;
514 slot.ret_ty = ret_ty.clone();
515 slot.generic = generic;
516 } else {
517 slots.push(ImportedSlot {
518 name: name.clone(),
519 owner: mangled.clone(),
520 param_tys: param_tys.clone(),
521 argument_metadata,
522 ret_ty: ret_ty.clone(),
523 generic,
524 });
525 }
526 }
527
528 for (name, (param_tys, ret)) in &methods {
532 let originates = class
533 .extends
534 .as_ref()
535 .and_then(|p| symbols.class_method_sig(p, name))
536 .is_none();
537 if !originates {
538 continue;
539 }
540 let mut params: Vec<ValType> = vec![ref_to(intrinsics.object)];
541 params.extend(
542 param_tys
543 .iter()
544 .map(|_| symbols.value_type(&crate::Type::Unknown))
545 .collect::<Result<Vec<_>, _>>()?,
546 );
547 let results = symbols.slot_wasm_result(ret)?;
548 let abi = MethodSlotAbi {
549 params: params.iter().skip(1).copied().collect(),
550 ret: results.first().copied(),
551 };
552 let sig_idx = next_index(next_type_idx)?;
553 types.ty().function(params, results);
554 symbols.record_class_method_sig(mangled.clone(), name.clone(), sig_idx);
555 symbols.record_class_method_abi(mangled.clone(), name.clone(), abi);
556 }
557 for slot in &slots {
560 if symbols.class_method_sig(mangled, &slot.name).is_none() {
561 let sig = match symbols.class_method_sig(&slot.owner, &slot.name) {
562 Some(sig) => sig,
563 None => ancestor_sig(info, symbols, class, &slot.name)?.ok_or_else(|| {
564 internal_failure("an inherited method slot's signature was not reconstructed")
565 })?,
566 };
567 symbols.record_class_method_sig(mangled.clone(), slot.name.clone(), sig);
568 }
569 if symbols.class_method_abi(mangled, &slot.name).is_none()
570 && let Some(abi) = symbols
571 .class_method_abi(&slot.owner, &slot.name)
572 .cloned()
573 .or_else(|| {
574 class
575 .extends
576 .as_ref()
577 .and_then(|p| symbols.class_method_abi(p, &slot.name).cloned())
578 })
579 {
580 symbols.record_class_method_abi(mangled.clone(), slot.name.clone(), abi);
581 }
582 }
583
584 let vtable_idx = next_index(next_type_idx)?;
586 let struct_idx = next_index(next_type_idx)?;
587 symbols.record_class_vtable_type(mangled.clone(), vtable_idx);
588 symbols.record_class_struct_type(mangled.clone(), struct_idx);
589
590 let (vtable_super, struct_super) = match &class.extends {
593 Some(parent) => (
594 symbols.class_vtable_type_idx(parent),
595 symbols.class_struct_type_idx(parent),
596 ),
597 None => (Some(intrinsics.class_vtable), Some(intrinsics.object_shape)),
598 };
599 let (Some(vtable_super), Some(struct_super)) = (vtable_super, struct_super) else {
600 return Err(internal_failure(
601 "an imported class's parent types were not reconstructed first",
602 ));
603 };
604 symbols.record_struct_supertype(vtable_idx, vtable_super);
605 symbols.record_struct_supertype(struct_idx, struct_super);
606
607 let mut vtable_fields: Vec<FieldType> = vec![
608 fieldtype_ref(intrinsics.to_string_fn),
609 fieldtype_ref(intrinsics.to_json_fn),
610 fieldtype_ref(intrinsics.equals_fn),
611 fieldtype_ref(intrinsics.hash_fn),
612 fieldtype_ref_null(intrinsics.class_vtable),
613 FieldType {
614 element_type: StorageType::Val(ValType::I32),
615 mutable: false,
616 },
617 ];
618 for slot in &slots {
619 let sig = symbols
620 .class_method_sig(mangled, &slot.name)
621 .ok_or_else(|| internal_failure("an imported class method slot has no signature"))?;
622 vtable_fields.push(fieldtype_ref(sig));
623 }
624 let vtable = substruct(vtable_fields, Some(vtable_super));
625
626 let struct_ty = substruct(
627 vec![
628 fieldtype_ref(vtable_idx),
629 FieldType {
630 mutable: true,
631 ..fieldtype_ref(intrinsics.field_names)
632 },
633 FieldType {
634 mutable: true,
635 ..fieldtype_ref(intrinsics.object_fields)
636 },
637 FieldType {
638 element_type: StorageType::Val(ValType::Ref(RefType::ANYREF)),
639 mutable: true,
640 },
641 FieldType {
642 element_type: StorageType::Val(ValType::I64),
643 mutable: true,
644 },
645 ],
646 Some(struct_super),
647 );
648 types.ty().rec([vtable, struct_ty]);
649
650 imports.import(
655 host_module_for(class),
656 crate::codegen::classes::vtable_global_export_name(mangled).as_str(),
657 EntityType::Global(GlobalType {
658 val_type: ref_to(vtable_idx),
659 mutable: false,
660 shared: false,
661 }),
662 );
663 symbols.record_class_vtable_global(mangled.clone(), next_index(next_global_idx)?);
664
665 for (i, name) in fields.iter().enumerate() {
668 symbols.record_class_field_slot(mangled.clone(), name.clone(), wasm_u32(i)?);
669 }
670 for (name, check) in &narrowing_checks {
671 symbols.record_class_field_narrowing_check(mangled.clone(), name.clone(), check.clone());
672 }
673 for (i, slot) in slots.iter().enumerate() {
674 if class.compiled_class
677 && !slot.generic
678 && !slot.name.starts_with("get ")
679 && !slot.name.starts_with("set ")
680 {
681 symbols.record_class_payload_method(mangled.clone(), slot.name.clone());
682 }
683 symbols.record_class_method_slot(
684 mangled.clone(),
685 slot.name.clone(),
686 method_vtable_slot(i)?,
687 );
688 }
689
690 symbols
691 .class_type_parameters
692 .insert(mangled.clone(), class.generics.to_vec());
693 let parent_guarded = match &class.extends {
694 Some(parent) => {
695 symbols
696 .recorded_class_guard_layout(parent)?
697 .has_instance_guards
698 }
699 None => false,
700 };
701 let named_payload_len = fields
702 .len()
703 .checked_add(slots.iter().filter(|slot| !slot.generic).count())
704 .ok_or_else(|| internal_failure("an imported class payload is too large"))?;
705 symbols.record_class_guard_layout(
706 mangled.clone(),
707 class.extends.as_ref(),
708 wasm_u32(named_payload_len)?,
709 (!class.generics.is_empty() || !narrowing_checks.is_empty() || parent_guarded)
710 && class.compiled_class,
711 )?;
712
713 let ctor_params: Vec<ValType> = class
715 .constructor
716 .iter()
717 .map(|p| symbols.slot_value_type(&p.ty))
718 .collect::<Result<_, _>>()?;
719 let ctor_param_tys: Vec<crate::Type> = class.constructor.iter().map(|p| p.ty.clone()).collect();
720 symbols.record_class_ctor_abi(mangled.clone(), ctor_params.clone());
721 let class_ref = crate::Type::class_ref(
722 crate::Package(class.package.to_string()),
723 class.name.to_string(),
724 mangled.clone(),
725 Vec::new(),
726 );
727
728 let mut entry_params = ctor_params.clone();
730 if symbols
731 .recorded_class_guard_layout(mangled)?
732 .has_instance_guards
733 {
734 entry_params.push(ref_to(intrinsics.object_fields));
735 }
736 let ctor_sig = next_index(next_type_idx)?;
737 types.ty().function(entry_params, [ref_to(struct_idx)]);
738 let ctor_mangled = crate::mangle::extend(mangled, "constructor");
739 imports.import(
740 host_module_for(class),
741 ctor_mangled.as_str(),
742 EntityType::Function(ctor_sig),
743 );
744 let ctor_idx = next_index(next_func_idx)?;
745 symbols.record_imported_fn(ctor_mangled, ctor_idx, ctor_param_tys, class_ref, false);
746
747 let mut init_params = vec![ref_to(intrinsics.object)];
749 init_params.extend(ctor_params);
750 let init_sig = next_index(next_type_idx)?;
751 types.ty().function(init_params, Vec::<ValType>::new());
752 let init_mangled = crate::mangle::extend(mangled, "constructor_init");
753 imports.import(
754 host_module_for(class),
755 init_mangled.as_str(),
756 EntityType::Function(init_sig),
757 );
758 let init_idx = next_index(next_func_idx)?;
759 symbols.record_class_ctor_init_func(mangled.clone(), init_idx);
760
761 for name in methods.keys() {
764 let sig = symbols
765 .class_method_sig(mangled, name)
766 .ok_or_else(|| internal_failure("an imported class's own method has no signature"))?;
767 let method_mangled = crate::mangle::extend(mangled, name);
768 imports.import(
769 host_module_for(class),
770 method_mangled.as_str(),
771 EntityType::Function(sig),
772 );
773 let idx = next_index(next_func_idx)?;
774 symbols.record_class_method_func(mangled.clone(), name.clone(), idx);
775 }
776
777 let ctor_typed_params = class
778 .constructor
779 .iter()
780 .map(|p| crate::TypedParam {
781 name: crate::Ident {
782 name: p.name.clone(),
783 span: crate::Span::at(file),
784 },
785 ty: p.ty.clone(),
786 optional: p.optional,
787 boxed: false,
788 rest: p.rest,
789 default: p.default.clone(),
790 })
791 .collect();
792
793 let mut private_members = parent_layout
794 .map(|layout| layout.private_members.clone())
795 .unwrap_or_default();
796 for (name, field) in class.fields {
797 if field.visibility == crate::Visibility::Private {
798 private_members.insert(name.clone());
799 }
800 }
801 for accessor in class.accessors {
802 if class
803 .fields
804 .get(accessor.name())
805 .is_some_and(|field| field.visibility == crate::Visibility::Private)
806 {
807 private_members.insert(super::classes::accessor_getter_name(accessor.name()));
808 private_members.insert(super::classes::accessor_setter_name(accessor.name()));
809 }
810 }
811 Ok(ImportedClassLayout {
812 private_members,
813 fields,
814 optional_fields,
815 narrowing_checks,
816 methods: slots,
817 ctor_params: ctor_typed_params,
818 })
819}
820
821fn ancestor_sig(
824 info: &BTreeMap<MangledName, ClassInfo<'_>>,
825 symbols: &SymbolTable,
826 class: &ClassInfo<'_>,
827 method: &str,
828) -> Result<Option<u32>, CompilerFailure> {
829 let mut visited: BTreeSet<&MangledName> = BTreeSet::new();
830 let mut cur = class.extends.as_ref();
831 while let Some(m) = cur {
832 if !visited.insert(m) {
833 return Err(internal_failure(format!(
834 "imported class `{m}` inherits from itself"
835 )));
836 }
837 if let Some(sig) = symbols.class_method_sig(m, method) {
838 return Ok(Some(sig));
839 }
840 cur = info.get(m).and_then(|c| c.extends.as_ref());
841 }
842 Ok(None)
843}
844
845fn ref_to(idx: u32) -> ValType {
846 ValType::Ref(RefType {
847 nullable: false,
848 heap_type: HeapType::Concrete(idx),
849 })
850}
851
852fn fieldtype_ref(idx: u32) -> FieldType {
853 FieldType {
854 element_type: StorageType::Val(ref_to(idx)),
855 mutable: false,
856 }
857}
858
859fn fieldtype_ref_null(idx: u32) -> FieldType {
860 FieldType {
861 element_type: StorageType::Val(ValType::Ref(RefType {
862 nullable: true,
863 heap_type: HeapType::Concrete(idx),
864 })),
865 mutable: false,
866 }
867}
868
869fn substruct(fields: Vec<FieldType>, supertype: Option<u32>) -> SubType {
870 SubType {
871 is_final: false,
872 supertype_idx: supertype,
873 composite_type: wasm_encoder::CompositeType {
874 inner: wasm_encoder::CompositeInnerType::Struct(StructType {
875 fields: fields.into_boxed_slice(),
876 }),
877 shared: false,
878 descriptor: None,
879 describes: None,
880 },
881 }
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887 use crate::compile::{PackageSourceModule, compile_package_checked};
888 use crate::compiler_error::CompilerFailure;
889
890 fn declaration() -> crate::PackageDeclaration {
891 let modules = [PackageSourceModule {
892 path: crate::ModulePath::from("lib"),
893 source: "export class A { x: number = 1 } export class B extends A {}",
894 }];
895 compile_package_checked("dep", crate::ModulePath::from("lib"), &modules, &[])
896 .unwrap()
897 .declaration
898 }
899
900 fn subclass_of(parent: &MangledName) -> TypedAst {
902 let (mut ta, diagnostics) = crate::compile::typecheck_to_typed_ast(
903 "class C {} function main(): void {}",
904 crate::FileId(0),
905 );
906 assert!(diagnostics.is_empty(), "{diagnostics:?}");
907 for decl in &mut ta.types {
908 if let TypedTypeDecl::Class(class) = decl {
909 class.extends = Some(parent.clone());
910 }
911 }
912 ta
913 }
914
915 fn mangled(declaration: &crate::PackageDeclaration, name: &str) -> MangledName {
916 declaration
917 .runtime_types
918 .values()
919 .chain(declaration.types.values())
920 .find(|symbol| symbol.name == name)
921 .unwrap()
922 .mangled_name
923 .clone()
924 }
925
926 #[test]
927 fn incomplete_or_cyclic_dependency_closures_are_internal_failures() {
928 let declaration = declaration();
929 let a = mangled(&declaration, "A");
930 let b = mangled(&declaration, "B");
931 let ta = subclass_of(&b);
932 let usage = DependencyUsage::empty();
933
934 let dependencies = [&declaration];
935 let info = collect_class_info(&dependencies);
936 assert_eq!(
937 needed_in_topo_order(&info, &ta, &usage).unwrap(),
938 [a.clone(), b.clone()]
939 );
940
941 let mut incomplete = declaration.clone();
944 incomplete
945 .runtime_types
946 .retain(|_, symbol| symbol.mangled_name != a);
947 incomplete
948 .types
949 .retain(|_, symbol| symbol.mangled_name != a);
950 let dependencies = [&incomplete];
951 let info = collect_class_info(&dependencies);
952 assert!(matches!(
953 needed_in_topo_order(&info, &ta, &usage),
954 Err(CompilerFailure::Internal { .. })
955 ));
956
957 let mut cyclic = declaration;
958 for symbol in cyclic
959 .runtime_types
960 .values_mut()
961 .chain(cyclic.types.values_mut())
962 {
963 if symbol.mangled_name == a
964 && let TypeKind::Class { extends, .. } = &mut symbol.kind
965 {
966 *extends = Some(crate::ClassExtends {
967 parent: b.clone(),
968 args: Vec::new(),
969 });
970 }
971 }
972 let dependencies = [&cyclic];
973 let info = collect_class_info(&dependencies);
974 assert!(matches!(
975 needed_in_topo_order(&info, &ta, &usage),
976 Err(CompilerFailure::Internal { .. })
977 ));
978 }
979}