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.peel() 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::Refined { ty, .. } | Type::Readonly(ty) => walk_type(ty, out)?,
356 Type::Function { params, ret, .. } => {
357 out.push(classify(ty)?);
358 for p in params {
359 walk_type(p, out)?;
360 }
361 walk_type(ret, out)?;
362 }
363 Type::Array(elem) => walk_type(elem, out)?,
364 Type::Tuple(elements) => {
365 for e in elements {
366 walk_type(e, out)?;
367 }
368 }
369 Type::Object { fields, index } => {
370 if let Some(index) = index {
371 walk_type(&index.value, out)?;
372 }
373 for f in fields.values() {
374 walk_type(&f.ty, out)?;
375 }
376 }
377 Type::InterfaceRef { args, .. } | Type::ClassRef { args, .. } => {
378 for a in args {
379 walk_type(a, out)?;
380 }
381 }
382 Type::AliasRef { args, .. } => {
387 for a in args {
388 walk_type(a, out)?;
389 }
390 }
391 Type::Union(members) => {
392 for m in members {
393 walk_type(m, out)?;
394 }
395 }
396 Type::Alias { ty: inner, .. } => walk_type(inner, out)?,
397 Type::Number
398 | Type::NumberLiteral(_)
399 | Type::BigInt
400 | Type::BigIntLiteral(_)
401 | Type::Boolean
402 | Type::BooleanLiteral(_)
403 | Type::String
404 | Type::StringLiteral(_)
405 | Type::Uint8Array
406 | Type::Void
407 | Type::Never
408 | Type::Null
409 | Type::Undefined
410 | Type::Error
411 | Type::TypeVar(_)
412 | Type::GenericParam { .. }
413 | Type::Unknown
414 | Type::NumberEnum { .. }
415 | Type::StringEnum { .. } => {}
416 };
417 Ok(())
418}
419
420pub fn emit_func_and_struct_types<I>(
421 signatures: I,
422 types: &mut TypeSection,
423 symbols: &mut SymbolTable,
424 next_type_idx: &mut u32,
425) -> Result<(), crate::compiler_error::CompilerFailure>
426where
427 I: IntoIterator<Item = ClosureSig>,
428{
429 let intrinsics = symbols
430 .intrinsic_type_indices()
431 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
432 let signatures = signatures.into_iter().flat_map(|sig| {
433 [
434 sig,
435 ClosureSig {
436 is_void: !sig.is_void,
437 ..sig
438 },
439 ]
440 });
441 let assigned = emit_arity_closures(signatures, types, intrinsics, next_type_idx)?;
442 super::closure_coercions::emit_vtable_type(types, symbols, next_type_idx)?;
443 for (sig, (fn_idx, struct_idx)) in assigned {
444 symbols.record_closure_func_type(sig, fn_idx);
445 symbols.record_closure_struct_type(sig, struct_idx);
446 symbols.record_struct_supertype(struct_idx, closure_struct_supertype(intrinsics));
447 }
448 Ok(())
449}
450
451pub fn emit_arity_closures<I>(
453 signatures: I,
454 types: &mut TypeSection,
455 intrinsics: IntrinsicTypeIndices,
456 next_type_idx: &mut u32,
457) -> Result<BTreeMap<ClosureSig, (u32, u32)>, crate::compiler_error::CompilerFailure>
458where
459 I: IntoIterator<Item = ClosureSig>,
460{
461 let mut seen: BTreeSet<ClosureSig> = BTreeSet::new();
462 let unique: Vec<ClosureSig> = signatures
463 .into_iter()
464 .filter(|sig| seen.insert(*sig))
465 .collect();
466
467 ensure_index_capacity(*next_type_idx, unique.len().checked_mul(2), 0)?;
468 let mut assigned: BTreeMap<ClosureSig, (u32, u32)> = BTreeMap::new();
469
470 let mut fn_indices: Vec<u32> = Vec::with_capacity(unique.len());
471 for sig in &unique {
472 let func_ty = closure_func_type(*sig, intrinsics);
473 types.ty().subtype(&SubType {
474 is_final: false,
475 supertype_idx: None,
476 composite_type: CompositeType {
477 inner: CompositeInnerType::Func(func_ty),
478 shared: false,
479 descriptor: None,
480 describes: None,
481 },
482 });
483 fn_indices.push(*next_type_idx);
484 *next_type_idx += 1;
485 }
486
487 for (sig, &fn_idx) in unique.iter().zip(fn_indices.iter()) {
488 let fields = vec![
489 FieldType {
490 element_type: StorageType::Val(ValType::Ref(RefType {
491 nullable: false,
492 heap_type: HeapType::Concrete(intrinsics.vtable),
493 })),
494 mutable: false,
495 },
496 FieldType {
497 element_type: StorageType::Val(ValType::Ref(RefType {
498 nullable: false,
499 heap_type: HeapType::Concrete(fn_idx),
500 })),
501 mutable: false,
502 },
503 FieldType {
504 element_type: StorageType::Val(ValType::Ref(RefType {
505 nullable: false,
506 heap_type: HeapType::ANY,
507 })),
508 mutable: false,
509 },
510 FieldType {
511 element_type: StorageType::Val(ValType::I64),
512 mutable: true,
513 },
514 ];
515 types.ty().subtype(&SubType {
516 is_final: false,
517 supertype_idx: Some(closure_struct_supertype(intrinsics)),
518 composite_type: CompositeType {
519 inner: CompositeInnerType::Struct(StructType {
520 fields: fields.into_boxed_slice(),
521 }),
522 shared: false,
523 descriptor: None,
524 describes: None,
525 },
526 });
527 let struct_idx = *next_type_idx;
528 *next_type_idx += 1;
529 assigned.insert(*sig, (fn_idx, struct_idx));
530 }
531
532 Ok(assigned)
533}
534
535pub fn emit_env_types(
537 metas: &[ClosureMeta],
538 types: &mut TypeSection,
539 symbols: &mut SymbolTable,
540 next_type_idx: &mut u32,
541) -> Result<(), crate::compiler_error::CompilerFailure> {
542 ensure_index_capacity(*next_type_idx, Some(metas.len()), 2)?;
543 let string = symbols
544 .string_type_idx()
545 .ok_or_else(|| crate::codegen::internal_failure("string intrinsic"))?;
546 types.ty().struct_([
547 FieldType {
548 element_type: StorageType::Val(ValType::Ref(RefType {
549 nullable: true,
550 heap_type: HeapType::ANY,
551 })),
552 mutable: false,
553 },
554 FieldType {
555 element_type: StorageType::Val(ValType::Ref(RefType {
556 nullable: false,
557 heap_type: HeapType::Concrete(string),
558 })),
559 mutable: false,
560 },
561 FieldType {
562 element_type: StorageType::Val(ValType::I64),
563 mutable: true,
564 },
565 ]);
566 symbols.call_metadata_type = Some(*next_type_idx);
567 *next_type_idx += 1;
568 let object = symbols
569 .intrinsic_type_indices()
570 .ok_or_else(|| crate::codegen::internal_failure("intrinsics"))?
571 .object;
572 types.ty().struct_([
573 FieldType {
574 element_type: StorageType::Val(ValType::Ref(RefType {
575 nullable: false,
576 heap_type: HeapType::ANY,
577 })),
578 mutable: false,
579 },
580 FieldType {
581 element_type: StorageType::Val(ValType::Ref(RefType {
582 nullable: true,
583 heap_type: HeapType::Concrete(object),
584 })),
585 mutable: false,
586 },
587 ]);
588 symbols.this_environment_type = Some(*next_type_idx);
589 *next_type_idx += 1;
590 for meta in metas {
591 let mut fields: Vec<FieldType> = meta
592 .captured
593 .iter()
594 .map(|c| {
595 Ok(FieldType {
596 element_type: StorageType::Val(env_field_type(c, symbols)?),
597 mutable: false,
598 })
599 })
600 .collect::<Result<_, crate::compiler_error::CompilerFailure>>()?;
601 if !meta.runtime_generics.is_empty() {
602 fields.push(FieldType {
603 element_type: StorageType::Val(super::runtime_descriptors::environment_type(
604 symbols,
605 )?),
606 mutable: false,
607 });
608 }
609 if meta.self_name.is_some() {
610 fields.push(FieldType {
611 element_type: StorageType::Val(ValType::Ref(RefType {
612 nullable: true,
613 heap_type: HeapType::Concrete(
614 symbols
615 .intrinsic_type_indices()
616 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared"))?
617 .object,
618 ),
619 })),
620 mutable: true,
621 });
622 }
623 types.ty().subtype(&SubType {
624 is_final: false,
625 supertype_idx: None,
626 composite_type: CompositeType {
627 inner: CompositeInnerType::Struct(StructType {
628 fields: fields.into_boxed_slice(),
629 }),
630 shared: false,
631 descriptor: None,
632 describes: None,
633 },
634 });
635 symbols.record_env_type(meta.expr_id, *next_type_idx);
636 *next_type_idx += 1;
637 }
638 Ok(())
639}
640
641fn closure_struct_supertype(intrinsics: IntrinsicTypeIndices) -> u32 {
644 intrinsics.closure
645}
646
647fn closure_func_type(sig: ClosureSig, intrinsics: IntrinsicTypeIndices) -> FuncType {
648 let object_ref = ValType::Ref(RefType {
649 nullable: true,
650 heap_type: HeapType::Concrete(intrinsics.object),
651 });
652 let mut wasm_params = vec![ValType::Ref(RefType {
653 nullable: false,
654 heap_type: HeapType::ANY,
655 })];
656 for _ in 0..sig.arity {
657 wasm_params.push(object_ref);
658 }
659 let results = vec![object_ref];
660 FuncType::new(wasm_params, results)
661}
662
663fn env_field_type(
664 c: &CapturedVar,
665 symbols: &SymbolTable,
666) -> Result<ValType, crate::compiler_error::CompilerFailure> {
667 Ok(if c.boxed {
668 let box_idx = symbols.box_type_idx(&c.ty)?.ok_or_else(|| {
669 crate::codegen::internal_failure("box type registered for every boxed capture")
670 })?;
671 ValType::Ref(RefType {
672 nullable: false,
673 heap_type: HeapType::Concrete(box_idx),
674 })
675 } else {
676 symbols.value_type(&c.ty)?
677 })
678}
679
680#[derive(Clone, Copy, Debug)]
681pub struct ClosureMethods {
682 pub to_string_func: u32,
683 pub to_json_func: u32,
684 pub equals_func: u32,
685 pub hash_func: u32,
686 pub vtable_global_idx: u32,
688}
689
690pub fn allocate_methods(
691 next_func_idx: &mut u32,
692) -> Result<ClosureMethods, crate::compiler_error::CompilerFailure> {
693 ensure_index_capacity(*next_func_idx, Some(4), 0)?;
694 let to_string_func = take(next_func_idx);
695 let to_json_func = take(next_func_idx);
696 let equals_func = take(next_func_idx);
697 let hash_func = take(next_func_idx);
698 Ok(ClosureMethods {
699 to_string_func,
700 to_json_func,
701 equals_func,
702 hash_func,
703 vtable_global_idx: 0,
704 })
705}
706
707fn take(counter: &mut u32) -> u32 {
708 let v = *counter;
709 *counter += 1;
710 v
711}
712
713pub fn emit_method_function_entries(
715 functions: &mut wasm_encoder::FunctionSection,
716 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
717) {
718 functions.function(intrinsics.to_string_fn);
719 functions.function(intrinsics.to_json_fn);
720 functions.function(intrinsics.equals_fn);
721 functions.function(intrinsics.hash_fn);
722}
723
724pub fn emit_vtable_global(
725 globals: &mut wasm_encoder::GlobalSection,
726 methods: &mut ClosureMethods,
727 symbols: &mut SymbolTable,
728 next_global_idx: &mut u32,
729 intrinsics: crate::codegen::intrinsics::IntrinsicTypeIndices,
730) -> Result<(), crate::compiler_error::CompilerFailure> {
731 ensure_index_capacity(*next_global_idx, Some(1), 0)?;
732 let init = wasm_encoder::ConstExpr::extended([
733 wasm_encoder::Instruction::RefFunc(methods.to_string_func),
734 wasm_encoder::Instruction::RefFunc(methods.to_json_func),
735 wasm_encoder::Instruction::RefFunc(methods.equals_func),
736 wasm_encoder::Instruction::RefFunc(methods.hash_func),
737 wasm_encoder::Instruction::StructNew(intrinsics.vtable),
738 ]);
739 globals.global(
740 wasm_encoder::GlobalType {
741 val_type: ValType::Ref(RefType {
742 nullable: false,
743 heap_type: HeapType::Concrete(intrinsics.vtable),
744 }),
745 mutable: false,
746 shared: false,
747 },
748 &init,
749 );
750 let idx = take(next_global_idx);
751 methods.vtable_global_idx = idx;
752 symbols.set_closure_vtable_global(idx);
753
754 Ok(())
755}
756
757pub fn emit_method_bodies(
758 code: &mut wasm_encoder::CodeSection,
759 _methods: ClosureMethods,
760 symbols: &SymbolTable,
761) -> Result<(), crate::compiler_error::CompilerFailure> {
762 let intrinsics = symbols
763 .intrinsic_type_indices()
764 .ok_or_else(|| crate::codegen::internal_failure("intrinsics declared by codegen entry"))?;
765 let string_vtable_global_idx = symbols
766 .prelude_global_idx("string_vtable")
767 .ok_or_else(|| crate::codegen::internal_failure("string_vtable imported from prelude"))?;
768
769 let mut to_string = wasm_encoder::Function::new(std::iter::empty());
770 crate::codegen::intrinsics::push_string_literal(
771 &mut to_string,
772 intrinsics,
773 string_vtable_global_idx,
774 "[object Function]",
775 )?;
776 to_string.instruction(&wasm_encoder::Instruction::End);
777 code.function(&to_string);
778
779 let mut to_json = wasm_encoder::Function::new(std::iter::empty());
780 crate::codegen::intrinsics::push_string_literal(
781 &mut to_json,
782 intrinsics,
783 string_vtable_global_idx,
784 "null",
785 )?;
786 to_json.instruction(&wasm_encoder::Instruction::End);
787 code.function(&to_json);
788
789 code.function(&super::closure_coercions::emit_equals(symbols)?);
790
791 let mut hash = wasm_encoder::Function::new(std::iter::empty());
792 let host_vtable = symbols
793 .prelude_global_idx("closure_vtable")
794 .ok_or_else(|| crate::codegen::internal_failure("closure_vtable imported from prelude"))?;
795 hash.instruction(&wasm_encoder::Instruction::LocalGet(0));
796 hash.instruction(&wasm_encoder::Instruction::GlobalGet(host_vtable));
797 hash.instruction(&wasm_encoder::Instruction::StructGet {
798 struct_type_index: intrinsics.vtable,
799 field_index: 3,
800 });
801 hash.instruction(&wasm_encoder::Instruction::CallRef(intrinsics.hash_fn));
802 hash.instruction(&wasm_encoder::Instruction::End);
803 code.function(&hash);
804
805 Ok(())
806}
807
808pub fn declared_funcs(methods: ClosureMethods) -> Vec<u32> {
810 vec![
811 methods.to_string_func,
812 methods.to_json_func,
813 methods.equals_func,
814 methods.hash_func,
815 ]
816}
817
818pub(super) fn ensure_index_capacity(
820 next: u32,
821 count: Option<usize>,
822 fixed: u32,
823) -> Result<(), crate::compiler_error::CompilerFailure> {
824 let end = count
825 .and_then(|count| u32::try_from(count).ok())
826 .and_then(|count| count.checked_add(fixed))
827 .and_then(|count| next.checked_add(count));
828 if end.is_none() {
829 return Err(crate::compiler_error::CompilerFailure::Limit {
830 stage: crate::compiler_error::CompilerStage::Codegen,
831 span: None,
832 message: "closure declarations exceed the Wasm index space".into(),
833 help: vec!["reduce the number of declarations".into()],
834 });
835 }
836 Ok(())
837}
838
839#[cfg(test)]
840mod tests {
841 use super::*;
842
843 #[test]
844 fn dependency_collection_checks_nested_callable_types() {
845 let signature = Type::Function {
846 params: vec![Type::Number; 256],
847 ret: Box::new(Type::Void),
848 predicate: None,
849 optional: 0,
850 has_rest: false,
851 };
852 let shapes = [Shape::Array(Box::new(Type::Array(Box::new(signature))))];
853 let error = collect_from_dependencies([], shapes.iter(), [], &DependencyUsage::empty())
854 .unwrap_err();
855 assert!(matches!(
856 error,
857 crate::compiler_error::CompilerFailure::Limit {
858 stage: crate::compiler_error::CompilerStage::Codegen,
859 span: None,
860 ..
861 }
862 ));
863 assert!(error.to_string().contains("256"));
864 }
865}