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