1use std::collections::{BTreeMap, BTreeSet};
30
31use wasm_encoder::{
32 CompositeInnerType, CompositeType, FieldType, FuncType, HeapType, RefType, StorageType,
33 StructType, SubType, TypeSection, ValType,
34};
35
36use crate::codegen::dependency_usage::{DependencyType, DependencyUsage};
37use crate::codegen::intrinsics::IntrinsicTypeIndices;
38use crate::codegen::symbol_table::SymbolTable;
39use crate::{
40 CapturedVar, ClosureBody, Dispatch, ExprId, Shape, Type, TypeKind, TypedAst, TypedParam,
41 ValueKind, ValueSymbol,
42};
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
45pub struct ClosureSig {
46 pub arity: u8,
47 pub is_void: bool,
48}
49
50impl ClosureSig {
51 pub fn of(arity: usize, ret: &Type) -> Result<Self, crate::compiler_error::CompilerFailure> {
54 let arity = crate::compiler_limits::checked_closure_arity(arity).map_err(|error| {
55 crate::compiler_error::CompilerFailure::Limit {
56 stage: crate::compiler_error::CompilerStage::Codegen,
57 span: None,
58 message: error.to_string(),
59 help: vec!["group arguments into an object or a rest parameter".into()],
60 }
61 })?;
62 Ok(Self {
63 arity,
64 is_void: ret.is_void(),
65 })
66 }
67}
68
69pub fn classify(sig: &Type) -> Result<ClosureSig, crate::compiler_error::CompilerFailure> {
70 let Type::Function { params, ret, .. } = sig.lowered() else {
71 return Err(super::internal_failure(
72 "closure classification requires a function type",
73 ));
74 };
75 ClosureSig::of(params.len(), ret)
76}
77
78#[derive(Clone, Debug)]
79pub struct ClosureMeta {
80 pub this_type: Option<Type>,
81 pub self_name: Option<crate::Ident>,
82 pub runtime_generics: Vec<String>,
83 pub expr_id: ExprId,
84 pub signature: Type,
85 pub captured: Vec<CapturedVar>,
86 pub params: Vec<TypedParam>,
87 pub body: ClosureBody,
88 pub return_type: Type,
89}
90
91pub fn collect_from_dependencies<'a>(
95 values: impl IntoIterator<Item = &'a ValueSymbol>,
96 shapes: impl IntoIterator<Item = &'a Shape>,
97 types: impl IntoIterator<Item = &'a DependencyType<'a>>,
98 usage: &DependencyUsage,
99) -> Result<Vec<ClosureSig>, crate::compiler_error::CompilerFailure> {
100 let mut out: Vec<ClosureSig> = Vec::new();
101 for value_sym in values {
102 let ValueKind::Function { params, ret, .. } = &value_sym.kind else {
103 continue;
104 };
105 for p in params {
106 walk_type(&p.ty, &mut out)?;
107 }
108 walk_type(ret, &mut out)?;
109 }
110 for shape in shapes {
111 match shape {
112 crate::Shape::Object { fields, index } => {
113 if let Some(index) = index {
114 walk_type(&index.value, &mut out)?;
115 }
116 for f in fields.values() {
117 walk_type(&f.ty, &mut out)?;
118 }
119 }
120 crate::Shape::Array(elem) => walk_type(elem, &mut out)?,
121 crate::Shape::Tuple(elements) => {
122 for e in elements {
123 walk_type(e, &mut out)?;
124 }
125 }
126 crate::Shape::Union(members) => {
127 for m in members {
128 walk_type(m, &mut out)?;
129 }
130 }
131 }
132 }
133 for dependency_type in types {
134 let ty_sym = dependency_type.symbol;
135 if let TypeKind::Interface {
136 dispatch,
137 methods,
138 properties,
139 ..
140 } = &ty_sym.kind
141 {
142 if *dispatch == Dispatch::VTable {
143 for (name, sig) in methods {
144 if usage.is_interface_member_used(ty_sym, name) {
145 dispatched_member_sigs(
146 sig.params.iter().map(|p| &p.ty),
147 &sig.ret,
148 &mut out,
149 )?;
150 }
151 }
152 for (name, prop) in properties {
153 if usage.is_interface_member_used(ty_sym, name) {
154 walk_type(&prop.ty, &mut out)?;
155 }
156 }
157 } else {
158 for (name, sig) in methods {
159 if usage.is_interface_member_used(ty_sym, name) {
160 walk_signature_types(sig.params.iter().map(|p| &p.ty), &sig.ret, &mut out)?;
161 }
162 }
163 for (name, prop) in properties {
164 if usage.is_interface_member_used(ty_sym, name) {
165 walk_type(&prop.ty, &mut out)?;
166 }
167 }
168 }
169 } else if let TypeKind::Class {
170 accessors,
171 methods,
172 constructor,
173 fields,
174 statics,
175 static_fields,
176 ..
177 } = &ty_sym.kind
178 {
179 for sig in methods.values() {
185 if sig.generics.is_empty() {
186 dispatched_member_sigs(sig.params.iter().map(|p| &p.ty), &sig.ret, &mut out)?;
187 }
188 }
189 for p in constructor {
190 walk_type(&p.ty, &mut out)?;
191 }
192 for f in fields.values() {
193 walk_type(&f.ty, &mut out)?;
194 }
195 for sig in statics.values() {
196 walk_signature_types(sig.params.iter().map(|p| &p.ty), &sig.ret, &mut out)?;
197 }
198 for f in static_fields.values() {
199 walk_type(&f.ty, &mut out)?;
200 }
201 accessor_sigs(accessors, &mut out)?;
202 }
203 }
204 Ok(out)
205}
206
207pub fn class_member_sigs(
212 ta: &TypedAst,
213 dependencies: &[&crate::PackageDeclaration],
214) -> Result<Vec<ClosureSig>, crate::compiler_error::CompilerFailure> {
215 let mut out: Vec<ClosureSig> = Vec::new();
216 for decl in &ta.types {
217 let crate::TypedTypeDecl::Class(class) = decl else {
218 continue;
219 };
220 for f in &class.fields {
221 walk_type(&f.ty, &mut out)?;
222 }
223 for p in class.effective_ctor_params() {
224 walk_type(&p.ty, &mut out)?;
225 }
226 for method in &class.methods {
227 if method.generics.is_empty() {
228 dispatched_member_sigs(
229 method.params.iter().map(|p| &p.ty),
230 &method.return_type,
231 &mut out,
232 )
233 .map_err(|error| error.with_span(method.name.span))?;
234 }
235 }
236 class_accessor_sigs(&class.accessors, &mut out)?;
237 inherited_dependency_sigs(class.extends.clone(), dependencies, &mut out)?;
238 }
239 Ok(out)
240}
241
242fn walk_signature_types<'a>(
244 params: impl IntoIterator<Item = &'a Type>,
245 ret: &Type,
246 out: &mut Vec<ClosureSig>,
247) -> Result<(), crate::compiler_error::CompilerFailure> {
248 for ty in params {
249 walk_type(ty, out)?;
250 }
251 walk_type(ret, out)?;
252
253 Ok(())
254}
255
256fn dispatched_member_sigs<'a>(
260 params: impl ExactSizeIterator<Item = &'a Type>,
261 ret: &Type,
262 out: &mut Vec<ClosureSig>,
263) -> Result<(), crate::compiler_error::CompilerFailure> {
264 out.push(ClosureSig::of(params.len(), ret)?);
265 walk_signature_types(params, ret, out)?;
266
267 Ok(())
268}
269
270fn accessor_sigs(
273 accessors: &[crate::AccessorSig],
274 out: &mut Vec<ClosureSig>,
275) -> Result<(), crate::compiler_error::CompilerFailure> {
276 for acc in accessors {
277 match acc {
278 crate::AccessorSig::Getter { ret_ty, .. } => {
279 dispatched_member_sigs(std::iter::empty(), ret_ty, out)?;
280 }
281 crate::AccessorSig::Setter { param, .. } => {
282 dispatched_member_sigs(std::iter::once(¶m.ty), &Type::Void, out)?;
283 }
284 }
285 }
286 Ok(())
287}
288
289fn class_accessor_sigs(
291 accessors: &[crate::TypedClassAccessor],
292 out: &mut Vec<ClosureSig>,
293) -> Result<(), crate::compiler_error::CompilerFailure> {
294 for acc in accessors {
295 match acc {
296 crate::TypedClassAccessor::Getter { ret_ty, .. } => {
297 dispatched_member_sigs(std::iter::empty(), ret_ty, out)?;
298 }
299 crate::TypedClassAccessor::Setter { param, .. } => {
300 dispatched_member_sigs(std::iter::once(¶m.ty), &Type::Void, out)?;
301 }
302 }
303 }
304 Ok(())
305}
306
307fn inherited_dependency_sigs(
312 mut parent: Option<crate::MangledName>,
313 dependencies: &[&crate::PackageDeclaration],
314 out: &mut Vec<ClosureSig>,
315) -> Result<(), crate::compiler_error::CompilerFailure> {
316 let mut seen: std::collections::BTreeSet<crate::MangledName> =
317 std::collections::BTreeSet::new();
318 while let Some(mangled) = parent {
319 if !seen.insert(mangled.clone()) {
320 return Ok(()); }
322 let Some(sym) = dependencies
323 .iter()
324 .flat_map(|d| d.types.values())
325 .find(|t| t.mangled_name == mangled)
326 else {
327 return Ok(());
328 };
329 let crate::TypeKind::Class {
330 methods,
331 accessors,
332 extends,
333 ..
334 } = &sym.kind
335 else {
336 return Ok(());
337 };
338 for sig in methods.values() {
339 if sig.generics.is_empty() {
340 dispatched_member_sigs(sig.params.iter().map(|p| &p.ty), &sig.ret, out)?;
341 }
342 }
343 accessor_sigs(accessors, out)?;
344 parent = extends.as_ref().map(|e| e.parent.clone());
345 }
346 Ok(())
347}
348
349pub(crate) fn walk_type(
351 ty: &Type,
352 out: &mut Vec<ClosureSig>,
353) -> Result<(), crate::compiler_error::CompilerFailure> {
354 match ty {
355 Type::TemplateLiteral { substitutions, .. } => {
356 for inner in substitutions {
357 walk_type(inner, out)?;
358 }
359 }
360 Type::Refined { ty, .. } | Type::Readonly(ty) => walk_type(ty, out)?,
361 Type::Function { params, ret, .. } => {
362 out.push(classify(ty)?);
363 for p in params {
364 walk_type(p, out)?;
365 }
366 walk_type(ret, out)?;
367 }
368 Type::Array(elem) => walk_type(elem, out)?,
369 Type::Tuple(elements) => {
370 for e in elements {
371 walk_type(e, out)?;
372 }
373 }
374 Type::Object { fields, index } => {
375 if let Some(index) = index {
376 walk_type(&index.value, out)?;
377 }
378 for f in fields.values() {
379 walk_type(&f.ty, out)?;
380 }
381 }
382 Type::InterfaceRef { args, .. } | Type::ClassRef { args, .. } => {
383 for a in args {
384 walk_type(a, out)?;
385 }
386 }
387 Type::AliasRef { args, .. } => {
392 for a in args {
393 walk_type(a, out)?;
394 }
395 }
396 Type::Union(members) => {
397 for m in members {
398 walk_type(m, out)?;
399 }
400 }
401 Type::Alias { ty: inner, .. } => walk_type(inner, out)?,
402 Type::Number
403 | Type::NumberLiteral(_)
404 | Type::BigInt
405 | Type::BigIntLiteral(_)
406 | Type::Boolean
407 | Type::BooleanLiteral(_)
408 | Type::String
409 | Type::StringLiteral(_)
410 | Type::Uint8Array
411 | Type::Void
412 | Type::Never
413 | Type::Null
414 | Type::Undefined
415 | Type::Error
416 | Type::TypeVar(_)
417 | Type::GenericParam { .. }
418 | Type::KeyOf(_)
419 | Type::IndexedAccess { .. }
420 | Type::Unknown
421 | Type::NonPrimitive
422 | Type::NumberEnum { .. }
423 | Type::StringEnum { .. } => {}
424 };
425 Ok(())
426}
427
428pub fn emit_func_and_struct_types<I>(
429 signatures: I,
430 types: &mut TypeSection,
431 symbols: &mut SymbolTable,
432 next_type_idx: &mut u32,
433) -> Result<(), crate::compiler_error::CompilerFailure>
434where
435 I: IntoIterator<Item = ClosureSig>,
436{
437 let intrinsics = symbols
438 .intrinsic_type_indices()
439 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
440 let signatures = signatures.into_iter().flat_map(|sig| {
441 [
442 sig,
443 ClosureSig {
444 is_void: !sig.is_void,
445 ..sig
446 },
447 ]
448 });
449 let assigned = emit_arity_closures(signatures, types, intrinsics, next_type_idx)?;
450 super::closure_coercions::emit_vtable_type(types, symbols, next_type_idx)?;
451 for (sig, (fn_idx, struct_idx)) in assigned {
452 symbols.record_closure_func_type(sig, fn_idx);
453 symbols.record_closure_struct_type(sig, struct_idx);
454 symbols.record_struct_supertype(struct_idx, closure_struct_supertype(intrinsics));
455 }
456 Ok(())
457}
458
459pub fn emit_arity_closures<I>(
461 signatures: I,
462 types: &mut TypeSection,
463 intrinsics: IntrinsicTypeIndices,
464 next_type_idx: &mut u32,
465) -> Result<BTreeMap<ClosureSig, (u32, u32)>, crate::compiler_error::CompilerFailure>
466where
467 I: IntoIterator<Item = ClosureSig>,
468{
469 let mut seen: BTreeSet<ClosureSig> = BTreeSet::new();
470 let unique: Vec<ClosureSig> = signatures
471 .into_iter()
472 .filter(|sig| seen.insert(*sig))
473 .collect();
474
475 ensure_index_capacity(*next_type_idx, unique.len().checked_mul(2), 0)?;
476 let mut assigned: BTreeMap<ClosureSig, (u32, u32)> = BTreeMap::new();
477
478 let mut fn_indices: Vec<u32> = Vec::with_capacity(unique.len());
479 for sig in &unique {
480 let func_ty = closure_func_type(*sig, intrinsics);
481 types.ty().subtype(&SubType {
482 is_final: false,
483 supertype_idx: None,
484 composite_type: CompositeType {
485 inner: CompositeInnerType::Func(func_ty),
486 shared: false,
487 descriptor: None,
488 describes: None,
489 },
490 });
491 fn_indices.push(*next_type_idx);
492 *next_type_idx += 1;
493 }
494
495 for (sig, &fn_idx) in unique.iter().zip(fn_indices.iter()) {
496 let fields = vec![
497 FieldType {
498 element_type: StorageType::Val(ValType::Ref(RefType {
499 nullable: false,
500 heap_type: HeapType::Concrete(intrinsics.vtable),
501 })),
502 mutable: false,
503 },
504 FieldType {
505 element_type: StorageType::Val(ValType::Ref(RefType {
506 nullable: false,
507 heap_type: HeapType::Concrete(fn_idx),
508 })),
509 mutable: false,
510 },
511 FieldType {
512 element_type: StorageType::Val(ValType::Ref(RefType {
513 nullable: false,
514 heap_type: HeapType::ANY,
515 })),
516 mutable: false,
517 },
518 FieldType {
519 element_type: StorageType::Val(ValType::I64),
520 mutable: true,
521 },
522 ];
523 types.ty().subtype(&SubType {
524 is_final: false,
525 supertype_idx: Some(closure_struct_supertype(intrinsics)),
526 composite_type: CompositeType {
527 inner: CompositeInnerType::Struct(StructType {
528 fields: fields.into_boxed_slice(),
529 }),
530 shared: false,
531 descriptor: None,
532 describes: None,
533 },
534 });
535 let struct_idx = *next_type_idx;
536 *next_type_idx += 1;
537 assigned.insert(*sig, (fn_idx, struct_idx));
538 }
539
540 Ok(assigned)
541}
542
543pub fn emit_env_types(
545 metas: &[ClosureMeta],
546 types: &mut TypeSection,
547 symbols: &mut SymbolTable,
548 next_type_idx: &mut u32,
549) -> Result<(), crate::compiler_error::CompilerFailure> {
550 ensure_index_capacity(*next_type_idx, Some(metas.len()), 2)?;
551 let string = symbols
552 .string_type_idx()
553 .ok_or_else(|| crate::codegen::internal_failure("string intrinsic"))?;
554 types.ty().struct_([
555 FieldType {
556 element_type: StorageType::Val(ValType::Ref(RefType {
557 nullable: true,
558 heap_type: HeapType::ANY,
559 })),
560 mutable: false,
561 },
562 FieldType {
563 element_type: StorageType::Val(ValType::Ref(RefType {
564 nullable: false,
565 heap_type: HeapType::Concrete(string),
566 })),
567 mutable: false,
568 },
569 FieldType {
570 element_type: StorageType::Val(ValType::I64),
571 mutable: true,
572 },
573 ]);
574 symbols.call_metadata_type = Some(*next_type_idx);
575 *next_type_idx += 1;
576 let object = symbols
577 .intrinsic_type_indices()
578 .ok_or_else(|| crate::codegen::internal_failure("intrinsics"))?
579 .object;
580 types.ty().struct_([
581 FieldType {
582 element_type: StorageType::Val(ValType::Ref(RefType {
583 nullable: false,
584 heap_type: HeapType::ANY,
585 })),
586 mutable: false,
587 },
588 FieldType {
589 element_type: StorageType::Val(ValType::Ref(RefType {
590 nullable: true,
591 heap_type: HeapType::Concrete(object),
592 })),
593 mutable: false,
594 },
595 ]);
596 symbols.this_environment_type = Some(*next_type_idx);
597 *next_type_idx += 1;
598 for meta in metas {
599 let mut fields: Vec<FieldType> = meta
600 .captured
601 .iter()
602 .map(|c| {
603 Ok(FieldType {
604 element_type: StorageType::Val(env_field_type(c, symbols)?),
605 mutable: false,
606 })
607 })
608 .collect::<Result<_, crate::compiler_error::CompilerFailure>>()?;
609 if !meta.runtime_generics.is_empty() {
610 fields.push(FieldType {
611 element_type: StorageType::Val(super::runtime_descriptors::environment_type(
612 symbols,
613 )?),
614 mutable: false,
615 });
616 }
617 if meta.self_name.is_some() {
618 fields.push(FieldType {
619 element_type: StorageType::Val(ValType::Ref(RefType {
620 nullable: true,
621 heap_type: HeapType::Concrete(
622 symbols
623 .intrinsic_type_indices()
624 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
625 .object,
626 ),
627 })),
628 mutable: true,
629 });
630 }
631 types.ty().subtype(&SubType {
632 is_final: false,
633 supertype_idx: None,
634 composite_type: CompositeType {
635 inner: CompositeInnerType::Struct(StructType {
636 fields: fields.into_boxed_slice(),
637 }),
638 shared: false,
639 descriptor: None,
640 describes: None,
641 },
642 });
643 symbols.record_env_type(meta.expr_id, *next_type_idx);
644 *next_type_idx += 1;
645 }
646 Ok(())
647}
648
649fn closure_struct_supertype(intrinsics: IntrinsicTypeIndices) -> u32 {
652 intrinsics.closure
653}
654
655fn closure_func_type(sig: ClosureSig, intrinsics: IntrinsicTypeIndices) -> FuncType {
656 let object_ref = ValType::Ref(RefType {
657 nullable: true,
658 heap_type: HeapType::Concrete(intrinsics.object),
659 });
660 let mut wasm_params = vec![ValType::Ref(RefType {
661 nullable: false,
662 heap_type: HeapType::ANY,
663 })];
664 for _ in 0..sig.arity {
665 wasm_params.push(object_ref);
666 }
667 let results = vec![object_ref];
668 FuncType::new(wasm_params, results)
669}
670
671fn env_field_type(
672 c: &CapturedVar,
673 symbols: &SymbolTable,
674) -> Result<ValType, crate::compiler_error::CompilerFailure> {
675 Ok(if c.boxed {
676 let box_idx = symbols.box_type_idx(&c.ty)?.ok_or_else(|| {
677 crate::codegen::internal_failure("box type registered for every boxed capture")
678 })?;
679 ValType::Ref(RefType {
680 nullable: false,
681 heap_type: HeapType::Concrete(box_idx),
682 })
683 } else {
684 symbols.value_type(&c.ty)?
685 })
686}
687
688#[derive(Clone, Copy, Debug)]
689pub struct ClosureMethods {
690 pub to_string_func: u32,
691 pub to_json_func: u32,
692 pub equals_func: u32,
693 pub hash_func: u32,
694 pub vtable_global_idx: u32,
696}
697
698pub fn allocate_methods(
699 next_func_idx: &mut u32,
700) -> Result<ClosureMethods, crate::compiler_error::CompilerFailure> {
701 ensure_index_capacity(*next_func_idx, Some(4), 0)?;
702 let to_string_func = take(next_func_idx);
703 let to_json_func = take(next_func_idx);
704 let equals_func = take(next_func_idx);
705 let hash_func = take(next_func_idx);
706 Ok(ClosureMethods {
707 to_string_func,
708 to_json_func,
709 equals_func,
710 hash_func,
711 vtable_global_idx: 0,
712 })
713}
714
715fn take(counter: &mut u32) -> u32 {
716 let v = *counter;
717 *counter += 1;
718 v
719}
720
721pub fn emit_method_function_entries(
723 functions: &mut wasm_encoder::FunctionSection,
724 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
725) {
726 functions.function(intrinsics.to_string_fn);
727 functions.function(intrinsics.to_json_fn);
728 functions.function(intrinsics.equals_fn);
729 functions.function(intrinsics.hash_fn);
730}
731
732pub fn emit_vtable_global(
733 globals: &mut wasm_encoder::GlobalSection,
734 methods: &mut ClosureMethods,
735 symbols: &mut SymbolTable,
736 next_global_idx: &mut u32,
737 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
738) -> Result<(), crate::compiler_error::CompilerFailure> {
739 ensure_index_capacity(*next_global_idx, Some(1), 0)?;
740 let init = wasm_encoder::ConstExpr::extended([
741 wasm_encoder::Instruction::RefFunc(methods.to_string_func),
742 wasm_encoder::Instruction::RefFunc(methods.to_json_func),
743 wasm_encoder::Instruction::RefFunc(methods.equals_func),
744 wasm_encoder::Instruction::RefFunc(methods.hash_func),
745 wasm_encoder::Instruction::StructNew(intrinsics.vtable),
746 ]);
747 globals.global(
748 wasm_encoder::GlobalType {
749 val_type: ValType::Ref(RefType {
750 nullable: false,
751 heap_type: HeapType::Concrete(intrinsics.vtable),
752 }),
753 mutable: false,
754 shared: false,
755 },
756 &init,
757 );
758 let idx = take(next_global_idx);
759 methods.vtable_global_idx = idx;
760 symbols.set_closure_vtable_global(idx);
761
762 Ok(())
763}
764
765pub fn emit_method_bodies(
766 code: &mut wasm_encoder::CodeSection,
767 _methods: ClosureMethods,
768 symbols: &SymbolTable,
769) -> Result<(), crate::compiler_error::CompilerFailure> {
770 let intrinsics = symbols
771 .intrinsic_type_indices()
772 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
773 let string_vtable_global_idx = symbols
774 .prelude_global_idx("string_vtable")
775 .ok_or_else(|| crate::codegen::internal_failure("string_vtable imported from prelude"))?;
776
777 let mut to_string = wasm_encoder::Function::new(std::iter::empty());
778 crate::codegen::intrinsics::push_string_literal(
779 &mut to_string,
780 intrinsics,
781 string_vtable_global_idx,
782 "[object Function]",
783 )?;
784 to_string.instruction(&wasm_encoder::Instruction::End);
785 code.function(&to_string);
786
787 let mut to_json = wasm_encoder::Function::new(std::iter::empty());
788 crate::codegen::intrinsics::push_string_literal(
789 &mut to_json,
790 intrinsics,
791 string_vtable_global_idx,
792 "null",
793 )?;
794 to_json.instruction(&wasm_encoder::Instruction::End);
795 code.function(&to_json);
796
797 code.function(&super::closure_coercions::emit_equals(symbols)?);
798
799 let mut hash = wasm_encoder::Function::new(std::iter::empty());
800 let host_vtable = symbols
801 .prelude_global_idx("closure_vtable")
802 .ok_or_else(|| crate::codegen::internal_failure("closure_vtable imported from prelude"))?;
803 hash.instruction(&wasm_encoder::Instruction::LocalGet(0));
804 hash.instruction(&wasm_encoder::Instruction::GlobalGet(host_vtable));
805 hash.instruction(&wasm_encoder::Instruction::StructGet {
806 struct_type_index: intrinsics.vtable,
807 field_index: 3,
808 });
809 hash.instruction(&wasm_encoder::Instruction::CallRef(intrinsics.hash_fn));
810 hash.instruction(&wasm_encoder::Instruction::End);
811 code.function(&hash);
812
813 Ok(())
814}
815
816pub fn declared_funcs(methods: ClosureMethods) -> Vec<u32> {
818 vec![
819 methods.to_string_func,
820 methods.to_json_func,
821 methods.equals_func,
822 methods.hash_func,
823 ]
824}
825
826pub(super) fn ensure_index_capacity(
828 next: u32,
829 count: Option<usize>,
830 fixed: u32,
831) -> Result<(), crate::compiler_error::CompilerFailure> {
832 let end = count
833 .and_then(|count| u32::try_from(count).ok())
834 .and_then(|count| count.checked_add(fixed))
835 .and_then(|count| next.checked_add(count));
836 if end.is_none() {
837 return Err(crate::compiler_error::CompilerFailure::Limit {
838 stage: crate::compiler_error::CompilerStage::Codegen,
839 span: None,
840 message: "closure declarations exceed the Wasm index space".into(),
841 help: vec!["reduce the number of declarations".into()],
842 });
843 }
844 Ok(())
845}
846
847#[cfg(test)]
848mod tests {
849 use super::*;
850
851 #[test]
852 fn dependency_collection_checks_nested_callable_types() {
853 let signature = Type::Function {
854 params: vec![Type::Number; 256],
855 ret: Box::new(Type::Void),
856 predicate: None,
857 optional: 0,
858 has_rest: false,
859 };
860 let shapes = [Shape::Array(Box::new(Type::Array(Box::new(signature))))];
861 let error = collect_from_dependencies([], shapes.iter(), [], &DependencyUsage::empty())
862 .unwrap_err();
863 assert!(matches!(
864 error,
865 crate::compiler_error::CompilerFailure::Limit {
866 stage: crate::compiler_error::CompilerStage::Codegen,
867 span: None,
868 ..
869 }
870 ));
871 assert!(error.to_string().contains("256"));
872 }
873}