1use std::collections::BTreeSet;
2
3use crate::codegen::bigint_pool::BigIntPool;
4use crate::codegen::closures::{ClosureMeta, ClosureSig};
5use crate::codegen::dependency_usage::DependencyUsage;
6use crate::codegen::function_adapters::AdapterMeta;
7use crate::codegen::string_pool::StringPool;
8use crate::codegen::{bounds, cast_check, throw};
9use crate::{
10 AccessorKind, BinOp, ClosureBody, ExprId, Intrinsic, PostfixTarget, Shape, StmtId, Type,
11 TypedAst, TypedChainPart, TypedExprKind, TypedObjectFieldSource, TypedStmtKind,
12 TypedSwitchValue,
13};
14
15pub struct CodegenAnalysis {
16 pub string_pool: StringPool,
17 pub bigint_pool: BigIntPool,
18 pub dependency_usage: DependencyUsage,
19 pub closure_metas: Vec<ClosureMeta>,
20 pub adapter_metas: Vec<AdapterMeta>,
21 pub mentioned_closure_sigs: Vec<ClosureSig>,
24 pub extra_field_names: Vec<String>,
25 adapter_seen: BTreeSet<crate::MangledName>,
26}
27
28impl CodegenAnalysis {
29 pub fn collect(
30 ta: &TypedAst,
31 dependencies: &[&crate::PackageDeclaration],
32 ) -> Result<Self, crate::compiler_error::CompilerFailure> {
33 let mut analysis = Self {
34 string_pool: StringPool::default(),
35 bigint_pool: BigIntPool::default(),
36 dependency_usage: DependencyUsage::empty(),
37 closure_metas: Vec::new(),
38 adapter_metas: Vec::new(),
39 mentioned_closure_sigs: Vec::new(),
40 extra_field_names: Vec::new(),
41 adapter_seen: BTreeSet::new(),
42 };
43
44 for helper in ["#toJson", "#staticValue"] {
47 analysis.dependency_usage.note_member(crate::mangle::extend(
48 &crate::mangle::prelude("ObjectConstructor"),
49 helper,
50 ));
51 }
52
53 for (id, ty) in &ta.runtime_source_types {
54 let span = ta
55 .try_expr(*id)
56 .map_err(crate::codegen::arena_failure)?
57 .span;
58 analysis
59 .visit_type(ty)
60 .map_err(|error| error.with_span(span))?;
61 analysis
62 .string_pool
63 .intern_text(&cast_check::error_prefix(ty));
64 }
65 if !ta.runtime_source_types.is_empty() {
66 for text in cast_check::TYPE_TAG_STRINGS {
67 analysis.string_pool.intern_text(text);
68 }
69 for name in [
70 "numeric",
71 "inc",
72 "dec",
73 "to_number",
74 "to_index",
75 "to_string",
76 "member",
77 "invoke",
78 "property",
79 ] {
80 analysis
81 .dependency_usage
82 .note_value(crate::mangle::prelude(&format!("__value_{name}")));
83 }
84 }
85 for (id, types) in &ta.runtime_chain_types {
86 let span = ta
87 .try_expr(*id)
88 .map_err(crate::codegen::arena_failure)?
89 .span;
90 for ty in types {
91 let ty = crate::typechecker::infer::narrowing::strip_nullish(ty);
92 analysis.visit_type_at(&ty, span)?;
93 analysis
94 .string_pool
95 .intern_text(&cast_check::error_prefix(&ty));
96 }
97 }
98
99 for shape in &ta.shapes {
100 if matches!(shape, Shape::Object { .. }) {
101 analysis
102 .dependency_usage
103 .collect_typed_object_stringify_host_value();
104 }
105 analysis.dependency_usage.note_shape(shape.clone());
106 }
107 for g in &ta.globals {
108 analysis.visit_type_at(&g.ty, g.span)?;
109 }
110 let guarded_bindings = super::init_guard::guarded_globals(ta)?
111 .into_iter()
112 .map(|guard| guard.binding)
113 .chain(
114 super::init_guard::guarded_classes(ta)?
115 .into_iter()
116 .map(|class| class.name),
117 )
118 .collect::<Vec<_>>();
119 if !guarded_bindings.is_empty() {
120 analysis
121 .dependency_usage
122 .note_type(crate::mangle::prelude("ReferenceError"));
123 }
124 for binding in guarded_bindings {
125 analysis
126 .string_pool
127 .intern_text(&super::init_guard::before_initialization_message(&binding));
128 }
129
130 if throw::body_end_may_be_reached(ta) {
131 analysis
132 .string_pool
133 .intern_text(throw::MISSING_RETURN_VALUE_MESSAGE);
134 }
135 for f in &ta.functions {
136 for p in &f.params {
137 if p.boxed {
138 analysis.note_binding_cell();
139 }
140 analysis
141 .visit_type(&p.ty)
142 .map_err(|error| error.with_span(p.name.span))?;
143 }
144 analysis.visit_type_at(&f.return_type, f.span)?;
145 analysis.walk_stmt(ta, f.body)?;
146 }
147 for &stmt_id in &ta.top_level_statements {
148 analysis.walk_stmt(ta, stmt_id)?;
149 }
150 for stmt_id in ta.class_body_roots() {
151 analysis.walk_stmt(ta, stmt_id)?;
152 }
153 for expr_id in ta.class_field_initializers() {
154 analysis.walk_expr(ta, expr_id)?;
155 }
156 if !ta.runtime_class_fields.is_empty()
157 || ta
158 .runtime_class_parameters
159 .values()
160 .any(|params| !params.is_empty())
161 || ta
162 .functions
163 .iter()
164 .any(|function| !function.generics.is_empty())
165 || ta
166 .runtime_class_contexts
167 .values()
168 .any(|contexts| !contexts.is_empty())
169 {
170 analysis
171 .mentioned_closure_sigs
172 .push(super::field_guards::signature());
173 }
174 analysis.note_narrowing_checks(ta)?;
175 analysis.note_inherited_conversions(ta, dependencies)?;
176 for test in ta.runtime_type_tests.values() {
177 analysis.note_narrowing_test(test)?;
178 }
179 analysis.note_dependency_narrowing_checks(dependencies)?;
180
181 for name in [
184 "__value_defaults_fit",
185 "__value_invoke_defaults",
186 "Number#toString",
187 "string_concat",
188 ] {
189 analysis
190 .dependency_usage
191 .note_value(crate::mangle::prelude(name));
192 }
193
194 Ok(analysis)
195 }
196
197 fn note_narrowing_checks(
202 &mut self,
203 ta: &TypedAst,
204 ) -> Result<(), crate::compiler_error::CompilerFailure> {
205 for decl in &ta.types {
206 let crate::TypedTypeDecl::Class(class) = decl else {
207 continue;
208 };
209 if class
210 .methods
211 .iter()
212 .any(|method| matches!(method.name.name.as_str(), "toString" | "toJson"))
213 {
214 self.note_conversion_dispatch()?;
215 }
216 for check in class
217 .fields
218 .iter()
219 .filter_map(|f| f.narrowing_check.as_ref())
220 {
221 self.note_narrowing_check(check)?;
222 }
223 }
224 Ok(())
225 }
226
227 fn note_inherited_conversions(
230 &mut self,
231 ta: &TypedAst,
232 dependencies: &[&crate::PackageDeclaration],
233 ) -> Result<(), crate::compiler_error::CompilerFailure> {
234 let mut parents: Vec<_> = ta
235 .types
236 .iter()
237 .filter_map(|decl| match decl {
238 crate::TypedTypeDecl::Class(class) => class.extends.as_ref(),
239 _ => None,
240 })
241 .collect();
242 let mut visited = BTreeSet::new();
243 while let Some(parent) = parents.pop() {
244 if !visited.insert(parent) {
245 continue;
246 }
247 let Some(symbol) = dependencies
248 .iter()
249 .flat_map(|package| package.runtime_types.values().chain(package.types.values()))
250 .find(|symbol| &symbol.mangled_name == parent)
251 else {
252 continue;
253 };
254 let crate::TypeKind::Class {
255 methods, extends, ..
256 } = &symbol.kind
257 else {
258 continue;
259 };
260 if methods.contains_key("toString") || methods.contains_key("toJson") {
261 self.note_conversion_dispatch()?;
262 return Ok(());
263 }
264 if let Some(extends) = extends {
265 parents.push(&extends.parent);
266 }
267 }
268 Ok(())
269 }
270
271 fn note_conversion_dispatch(&mut self) -> Result<(), crate::compiler_error::CompilerFailure> {
272 self.string_pool.intern_text("Value is not callable");
273 self.string_pool
274 .intern_text("Unbound function has no this receiver");
275 self.string_pool
276 .intern_text(&cast_check::error_prefix(&Type::String));
277 for tag in cast_check::TYPE_TAG_STRINGS {
278 self.string_pool.intern_text(tag);
279 }
280 self.mentioned_closure_sigs
281 .push(ClosureSig::of(0, &Type::String)?);
282 Ok(())
283 }
284
285 fn note_dependency_narrowing_checks(
286 &mut self,
287 dependencies: &[&crate::PackageDeclaration],
288 ) -> Result<(), crate::compiler_error::CompilerFailure> {
289 for package in dependencies {
290 for symbol in package.runtime_types.values().chain(package.types.values()) {
291 let crate::TypeKind::Class {
292 narrowing_checks, ..
293 } = &symbol.kind
294 else {
295 continue;
296 };
297 for check in narrowing_checks.values() {
298 self.note_narrowing_check(check)?;
299 }
300 }
301 }
302 Ok(())
303 }
304
305 fn note_narrowing_check(
306 &mut self,
307 check: &crate::FieldNarrowingCheck,
308 ) -> Result<(), crate::compiler_error::CompilerFailure> {
309 self.string_pool.intern_text(&check.message);
310 self.note_narrowing_test(&check.test)?;
311
312 Ok(())
313 }
314
315 fn note_narrowing_test(
316 &mut self,
317 test: &crate::FieldNarrowingTest,
318 ) -> Result<(), crate::compiler_error::CompilerFailure> {
319 match test {
320 crate::FieldNarrowingTest::Shape(shape) => {
321 self.visit_type(shape)?;
322 self.note_shape_member_names(shape);
323 }
324 crate::FieldNarrowingTest::Interface(test) => {
325 let shape = Type::Object {
326 index: test.index.clone(),
327 fields: test.members.clone(),
328 };
329 self.visit_type(&shape)?;
330 self.note_shape_member_names(&shape);
331 }
332 crate::FieldNarrowingTest::NonNull
333 | crate::FieldNarrowingTest::Substituted
334 | crate::FieldNarrowingTest::Representation => {}
335 };
336 Ok(())
337 }
338
339 fn note_shape_member_names(&mut self, ty: &Type) {
357 match ty.peel() {
358 Type::Object { fields, index } => {
359 if let Some(index) = index {
360 self.note_record_helpers();
361 self.note_shape_member_names(&index.value);
362 }
363 if !fields.is_empty() {
364 self.mentioned_closure_sigs.push(
365 crate::codegen::classes::accessor_closure_sig(AccessorKind::Get),
366 );
367 }
368 for (name, field) in fields {
369 self.extra_field_names.push(name.clone());
370 self.extra_field_names
371 .push(crate::codegen::classes::accessor_getter_name(name));
372 self.note_shape_member_names(&field.ty);
373 }
374 }
375 Type::Array(elem) => self.note_shape_member_names(elem),
376 Type::Tuple(elems) => {
377 for e in elems {
378 self.note_shape_member_names(e);
379 }
380 }
381 Type::Union(members) => {
382 for m in members {
383 self.note_shape_member_names(m);
384 }
385 }
386 _ => {}
387 }
388 }
389
390 fn visit_type_at(
400 &mut self,
401 ty: &Type,
402 span: crate::Span,
403 ) -> Result<(), crate::compiler_error::CompilerFailure> {
404 self.visit_type(ty).map_err(|error| error.with_span(span))
405 }
406
407 fn visit_type(&mut self, ty: &Type) -> Result<(), crate::compiler_error::CompilerFailure> {
408 if let Type::Object {
409 index: Some(index), ..
410 } = ty.peel()
411 {
412 self.note_record_helpers();
413 self.visit_type(&index.value)?;
414 }
415 self.dependency_usage.collect_type(ty);
416 crate::codegen::closures::walk_type(ty, &mut self.mentioned_closure_sigs)?;
417
418 Ok(())
419 }
420
421 fn note_record_helpers(&mut self) {
422 for helper in ["#getField", "#setField", "#recordValues", "#hasField"] {
423 self.dependency_usage.note_member(crate::mangle::extend(
424 &crate::mangle::prelude("ObjectConstructor"),
425 helper,
426 ));
427 }
428 }
429
430 fn walk_stmt(
431 &mut self,
432 ta: &TypedAst,
433 id: StmtId,
434 ) -> Result<(), crate::compiler_error::CompilerFailure> {
435 self.note_stmt_pre(ta, id)?;
436 match &ta.try_stmt(id).map_err(crate::codegen::arena_failure)?.kind {
437 TypedStmtKind::Let { value, .. } | TypedStmtKind::Const { value, .. } => {
438 self.walk_expr(ta, *value)?;
439 }
440 TypedStmtKind::If {
441 condition,
442 then_block,
443 else_block,
444 } => {
445 self.walk_expr(ta, *condition)?;
446 self.walk_stmt(ta, *then_block)?;
447 if let Some(else_block) = else_block {
448 self.walk_stmt(ta, *else_block)?;
449 }
450 }
451 TypedStmtKind::While { condition, body } => {
452 self.walk_expr(ta, *condition)?;
453 self.walk_stmt(ta, *body)?;
454 }
455 TypedStmtKind::For {
456 init,
457 condition,
458 update,
459 body,
460 } => {
461 if let Some(init) = init {
462 self.walk_stmt(ta, *init)?;
463 }
464 if let Some(condition) = condition {
465 self.walk_expr(ta, *condition)?;
466 }
467 if let Some(update) = update {
468 self.walk_stmt(ta, *update)?;
469 }
470 self.walk_stmt(ta, *body)?;
471 }
472 TypedStmtKind::ForOf { iter, body, .. } => {
473 self.walk_expr(ta, *iter)?;
474 self.walk_stmt(ta, *body)?;
475 }
476 TypedStmtKind::DoWhile { body, condition } => {
477 self.walk_stmt(ta, *body)?;
478 self.walk_expr(ta, *condition)?;
479 }
480 TypedStmtKind::Switch {
481 discriminant,
482 cases,
483 default,
484 ..
485 } => {
486 self.walk_expr(ta, *discriminant)?;
487 for comparison in cases
488 .iter()
489 .flat_map(crate::TypedSwitchCase::label_comparisons)
490 {
491 self.walk_expr(ta, comparison)?;
492 }
493 for case in cases {
494 self.walk_stmt(ta, case.body)?;
495 }
496 if let Some(default) = default {
497 self.walk_stmt(ta, *default)?;
498 }
499 }
500 TypedStmtKind::Break | TypedStmtKind::Continue | TypedStmtKind::ReboxLocal { .. } => {}
501 TypedStmtKind::Return(value) => {
502 if let Some(value) = value {
503 self.walk_expr(ta, *value)?;
504 }
505 }
506 TypedStmtKind::Expr(expr) => self.walk_expr(ta, *expr)?,
507 TypedStmtKind::Block(stmts) => {
508 for &stmt in stmts {
509 self.walk_stmt(ta, stmt)?;
510 }
511 }
512 TypedStmtKind::AssignLocal { value, .. }
513 | TypedStmtKind::AssignGlobal { value, .. } => self.walk_expr(ta, *value)?,
514 TypedStmtKind::AssignField {
515 receiver, value, ..
516 } => {
517 self.walk_expr(ta, *receiver)?;
518 self.walk_expr(ta, *value)?;
519 }
520 TypedStmtKind::AssignIndex {
521 receiver,
522 index,
523 value,
524 ..
525 } => {
526 self.walk_expr(ta, *receiver)?;
527 self.walk_expr(ta, *index)?;
528 self.walk_expr(ta, *value)?;
529 }
530 TypedStmtKind::NarrowRegion { source, body, .. } => {
531 self.walk_expr(ta, *source)?;
532 self.walk_stmt(ta, *body)?;
533 }
534 TypedStmtKind::Throw { value } => self.walk_expr(ta, *value)?,
535 TypedStmtKind::Try {
536 body,
537 catches,
538 finally,
539 } => {
540 self.walk_stmt(ta, *body)?;
541 for clause in catches {
542 self.walk_stmt(ta, clause.body)?;
543 }
544 if let Some(finally) = finally {
545 self.walk_stmt(ta, *finally)?;
546 }
547 }
548 }
549 self.note_stmt_post(ta, id)?;
550 Ok(())
551 }
552
553 fn walk_expr(
554 &mut self,
555 ta: &TypedAst,
556 id: ExprId,
557 ) -> Result<(), crate::compiler_error::CompilerFailure> {
558 let expr = ta.try_expr(id).map_err(crate::codegen::arena_failure)?;
559 self.note_expr_pre(ta, id)
560 .map_err(|error| error.with_span(expr.span))?;
561 let _: () = match &expr.kind {
562 TypedExprKind::Call { args, .. }
563 | TypedExprKind::McpCall { args, .. }
564 | TypedExprKind::SuperCtorCall { args, .. }
565 | TypedExprKind::SuperMethodCall { args, .. }
566 | TypedExprKind::IntrinsicCall { args, .. } => {
567 for &arg in args {
568 self.walk_expr(ta, arg)?;
569 }
570 }
571 TypedExprKind::CallClosure { callee, args } => {
572 self.walk_expr(ta, *callee)?;
573 for &arg in args {
574 self.walk_expr(ta, arg)?;
575 }
576 }
577 TypedExprKind::GenericCall {
578 args, type_args, ..
579 } => {
580 self.mentioned_closure_sigs
581 .push(super::field_guards::signature());
582 for ty in type_args {
583 self.visit_type(ty)?;
584 }
585 for arg in args {
586 self.walk_expr(ta, arg.expr)?;
587 }
588 }
589 TypedExprKind::Closure { body, .. } => match *body {
590 ClosureBody::Expr(expr) => self.walk_expr(ta, expr)?,
591 ClosureBody::Block(stmt) => self.walk_stmt(ta, stmt)?,
592 },
593 TypedExprKind::Binary { lhs, rhs, .. } => {
594 self.walk_expr(ta, *lhs)?;
595 self.walk_expr(ta, *rhs)?;
596 }
597 TypedExprKind::EffectThen { effect, result } => {
598 self.walk_expr(ta, *effect)?;
599 self.walk_expr(ta, *result)?;
600 }
601 TypedExprKind::Sequence { stmts, result } => {
602 for &stmt in stmts {
603 self.walk_stmt(ta, stmt)?;
604 }
605 self.walk_expr(ta, *result)?;
606 }
607 TypedExprKind::Unary { operand, .. }
608 | TypedExprKind::TypeofTag { value: operand, .. }
609 | TypedExprKind::InstanceOf { value: operand, .. }
610 | TypedExprKind::NonNullAssert { value: operand } => {
611 self.walk_expr(ta, *operand)?;
612 }
613 TypedExprKind::MethodCall { receiver, args, .. } => {
614 self.walk_expr(ta, *receiver)?;
615 for &arg in args {
616 self.walk_expr(ta, arg)?;
617 }
618 }
619 TypedExprKind::GenericMethodCall { receiver, args, .. } => {
620 self.walk_expr(ta, *receiver)?;
621 for arg in args {
622 self.walk_expr(ta, arg.expr)?;
623 }
624 }
625 TypedExprKind::ObjectLiteral { members, .. } => {
626 for member in members {
627 for expression in member.expressions() {
628 self.walk_expr(ta, expression)?;
629 }
630 }
631 }
632 TypedExprKind::ArrayLiteral { elements, .. } => {
633 for elem in elements {
634 self.walk_expr(ta, elem.expr_id())?;
635 }
636 }
637 TypedExprKind::TupleLiteral { elements, .. } => {
638 for &elem in elements {
639 self.walk_expr(ta, elem)?;
640 }
641 }
642 TypedExprKind::FieldAccess { receiver, .. }
643 | TypedExprKind::InterfacePropertyAccess { receiver, .. } => {
644 self.walk_expr(ta, *receiver)?;
645 }
646 TypedExprKind::IndexAccess { receiver, index } => {
647 self.walk_expr(ta, *receiver)?;
648 self.walk_expr(ta, *index)?;
649 }
650 TypedExprKind::Narrowed { source, inner, .. } => {
651 self.walk_expr(ta, *source)?;
652 self.walk_expr(ta, *inner)?;
653 }
654 TypedExprKind::Ternary { cond, then_, else_ } => {
655 self.walk_expr(ta, *cond)?;
656 self.walk_expr(ta, *then_)?;
657 self.walk_expr(ta, *else_)?;
658 }
659 TypedExprKind::NullishCoalesce { lhs, rhs } => {
660 self.walk_expr(ta, *lhs)?;
661 self.walk_expr(ta, *rhs)?;
662 }
663 TypedExprKind::OptionalChain { base, parts } => {
664 self.walk_expr(ta, *base)?;
665 for part in parts {
666 match part {
667 TypedChainPart::Index { idx, .. } => self.walk_expr(ta, *idx)?,
668 TypedChainPart::Call { args, .. }
669 | TypedChainPart::MethodCall { args, .. } => {
670 for &arg in args {
671 self.walk_expr(ta, arg)?;
672 }
673 }
674 TypedChainPart::Field { .. }
675 | TypedChainPart::InterfaceProperty { .. }
676 | TypedChainPart::NonNull { .. } => {}
677 }
678 }
679 }
680 TypedExprKind::PostfixUnary { target, .. } => match target {
681 PostfixTarget::Field { receiver, .. } => self.walk_expr(ta, *receiver)?,
682 PostfixTarget::Index {
683 receiver, index, ..
684 } => {
685 self.walk_expr(ta, *receiver)?;
686 self.walk_expr(ta, *index)?;
687 }
688 PostfixTarget::Local { .. } | PostfixTarget::Global { .. } => {}
689 },
690 TypedExprKind::Cast { value, .. } => self.walk_expr(ta, *value)?,
691 TypedExprKind::Number(_)
692 | TypedExprKind::BigInt(_)
693 | TypedExprKind::String(_)
694 | TypedExprKind::Boolean(_)
695 | TypedExprKind::Null
696 | TypedExprKind::Undefined
697 | TypedExprKind::This
698 | TypedExprKind::Regex { .. }
699 | TypedExprKind::LocalRef { .. }
700 | TypedExprKind::LocalNarrowRef { .. }
701 | TypedExprKind::GlobalRef { .. }
702 | TypedExprKind::FunctionRef { .. }
703 | TypedExprKind::NumberEnumMember { .. }
704 | TypedExprKind::StringEnumMember { .. } => {}
705 };
706 Ok(())
707 }
708
709 fn note_stmt_pre(
710 &mut self,
711 ta: &TypedAst,
712 id: StmtId,
713 ) -> Result<(), crate::compiler_error::CompilerFailure> {
714 let stmt = ta.try_stmt(id).map_err(crate::codegen::arena_failure)?;
715 if matches!(
716 stmt.kind,
717 TypedStmtKind::Let { boxed: true, .. }
718 | TypedStmtKind::AssignLocal { boxed: true, .. }
719 | TypedStmtKind::ReboxLocal { .. }
720 ) {
721 self.note_binding_cell();
722 }
723 let _: () = match &stmt.kind {
724 TypedStmtKind::Let { ty, .. } | TypedStmtKind::Const { ty, .. } => {
725 self.visit_type_at(ty, stmt.span)?;
726 }
727 TypedStmtKind::ForOf { element_ty, .. } => {
728 self.visit_type_at(element_ty, stmt.span)?;
729 }
730 TypedStmtKind::Switch {
731 discriminant_ty,
732 cases,
733 ..
734 } => {
735 self.visit_type_at(discriminant_ty, stmt.span)?;
736 for case in cases {
737 for value in &case.values {
738 if let TypedSwitchValue::Enum { enum_name, .. } = value {
739 self.dependency_usage.note_type(enum_name.clone());
740 }
741 }
742 }
743 }
744 TypedStmtKind::AssignLocal { target_ty, .. } => {
745 self.visit_type_at(target_ty, stmt.span)?;
746 }
747 TypedStmtKind::AssignGlobal {
748 mangled, target_ty, ..
749 } => {
750 self.dependency_usage.note_value(mangled.clone());
751 self.visit_type_at(target_ty, stmt.span)?;
752 }
753 TypedStmtKind::AssignField { receiver, name, .. } => {
754 self.extra_field_names.push(name.name.clone());
755 self.note_shaped_property_access(
756 ta.source_type(*receiver)
757 .map_err(crate::codegen::arena_failure)?,
758 &name.name,
759 AccessorKind::Set,
760 );
761 }
762 TypedStmtKind::AssignIndex {
763 receiver, elem_ty, ..
764 } => {
765 if ta
766 .source_type(*receiver)
767 .map_err(crate::codegen::arena_failure)?
768 .is_structural_object()
769 {
770 self.note_record_helpers();
771 }
772 self.visit_type_at(elem_ty, stmt.span)?;
773 self.note_index_check();
774 }
775 TypedStmtKind::NarrowRegion { cast_info, .. } => {
776 self.visit_type_at(&cast_info.from_ty, stmt.span)?;
777 self.visit_type_at(&cast_info.to_ty, stmt.span)?;
778 }
779 TypedStmtKind::Try { catches, .. } => {
780 for clause in catches {
783 self.visit_type_at(&clause.ty, stmt.span)?;
784 }
785 }
786 TypedStmtKind::If { .. }
787 | TypedStmtKind::While { .. }
788 | TypedStmtKind::For { .. }
789 | TypedStmtKind::DoWhile { .. }
790 | TypedStmtKind::Break
791 | TypedStmtKind::Continue
792 | TypedStmtKind::ReboxLocal { .. }
793 | TypedStmtKind::Return(_)
794 | TypedStmtKind::Expr(_)
795 | TypedStmtKind::Block(_)
796 | TypedStmtKind::Throw { .. } => {}
797 };
798 Ok(())
799 }
800
801 fn note_stmt_post(
802 &mut self,
803 ta: &TypedAst,
804 id: StmtId,
805 ) -> Result<(), crate::compiler_error::CompilerFailure> {
806 let _: () = if let TypedStmtKind::Switch { cases, .. } =
807 &ta.try_stmt(id).map_err(crate::codegen::arena_failure)?.kind
808 {
809 for case in cases {
810 for value in &case.values {
811 match value {
812 TypedSwitchValue::String { value, .. } => {
813 self.string_pool.intern_text(value);
814 }
815 TypedSwitchValue::Enum {
816 value: crate::EnumVariantPayload::String(text),
817 ..
818 } => {
819 self.string_pool.intern_text(text);
820 }
821 _ => {}
822 }
823 }
824 }
825 };
826 Ok(())
827 }
828
829 fn note_expr_pre(
830 &mut self,
831 ta: &TypedAst,
832 id: ExprId,
833 ) -> Result<(), crate::compiler_error::CompilerFailure> {
834 let expr = ta.try_expr(id).map_err(crate::codegen::arena_failure)?;
835 let has_binding_cell = match &expr.kind {
836 TypedExprKind::LocalRef { boxed: true, .. }
837 | TypedExprKind::PostfixUnary {
838 target: PostfixTarget::Local { boxed: true, .. },
839 ..
840 } => true,
841 TypedExprKind::Closure {
842 params, captured, ..
843 } => {
844 params.iter().any(|param| param.boxed)
845 || captured.iter().any(|capture| capture.boxed)
846 }
847 _ => false,
848 };
849 if has_binding_cell {
850 self.note_binding_cell();
851 }
852 if matches!(
853 expr.kind,
854 TypedExprKind::IndexAccess { .. }
855 | TypedExprKind::Binary {
856 op: crate::BinOp::In,
857 ..
858 }
859 ) {
860 self.note_record_helpers();
861 }
862 if matches!(expr.kind, TypedExprKind::PostfixUnary { .. }) {
863 self.note_record_helpers();
864 }
865 self.visit_type(&expr.ty)
866 .map_err(|error| error.with_span(expr.span))?;
867 let _: () = match &expr.kind {
868 TypedExprKind::String(text) => {
869 self.string_pool.record_expr(id, text);
870 }
871 TypedExprKind::StringEnumMember {
872 enum_mangled,
873 value,
874 ..
875 } => {
876 self.string_pool.record_expr(id, value);
877 self.dependency_usage.note_type(enum_mangled.clone());
878 }
879 TypedExprKind::BigInt(digits) => {
880 self.bigint_pool.intern_digits(digits)?;
881 self.dependency_usage
882 .collect_bigint_host_value("fromNumber");
883 }
884 TypedExprKind::GlobalRef { mangled, .. } => {
885 self.dependency_usage.note_value(mangled.clone());
886 }
887 TypedExprKind::FunctionRef { name, mangled } => {
888 self.dependency_usage.note_value(mangled.clone());
889 if self.adapter_seen.insert(mangled.clone()) {
890 self.adapter_metas.push(AdapterMeta {
891 name: name.name.clone(),
892 mangled: mangled.clone(),
893 signature: expr.ty.clone(),
894 });
895 }
896 }
897 TypedExprKind::NumberEnumMember { enum_mangled, .. } => {
898 self.dependency_usage.note_type(enum_mangled.clone());
899 }
900 TypedExprKind::Binary { op, lhs, .. } => {
901 if let Some(name) = op.bitwise_name() {
902 self.dependency_usage
903 .note_value(crate::mangle::prelude(&format!("__value_{name}")));
904 self.dependency_usage
906 .note_value(crate::mangle::prelude("__value_bitor"));
907 }
908 if matches!(op, BinOp::Pow) && matches!(expr.ty.peel(), Type::Number) {
909 self.dependency_usage
910 .note_value(crate::runtime::prelude::math::math_key("pow"));
911 }
912 if expr.ty.is_bigint() {
913 match op {
914 BinOp::Add => self.dependency_usage.collect_bigint_host_value("add"),
915 BinOp::Sub => self.dependency_usage.collect_bigint_host_value("sub"),
916 BinOp::Mul => self.dependency_usage.collect_bigint_host_value("mul"),
917 BinOp::Div => self.dependency_usage.collect_bigint_host_value("div"),
918 BinOp::Rem => self.dependency_usage.collect_bigint_host_value("mod"),
919 BinOp::Pow => self.dependency_usage.collect_bigint_host_value("pow"),
920 BinOp::Eq
921 | BinOp::BitAnd
922 | BinOp::BitOr
923 | BinOp::BitXor
924 | BinOp::Shl
925 | BinOp::Shr
926 | BinOp::UnsignedShr
927 | BinOp::NotEq
928 | BinOp::Lt
929 | BinOp::Gt
930 | BinOp::Le
931 | BinOp::Ge
932 | BinOp::And
933 | BinOp::Or
934 | BinOp::In
935 | BinOp::NullishCoalesce => {}
936 }
937 }
938 if ta
939 .try_expr(*lhs)
940 .map_err(crate::codegen::arena_failure)?
941 .ty
942 .is_bigint()
943 && matches!(
944 op,
945 BinOp::Eq | BinOp::NotEq | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge
946 )
947 {
948 self.dependency_usage.collect_bigint_host_value("cmp");
949 }
950 if matches!(op, BinOp::In)
951 && let TypedExprKind::String(name) = &ta
952 .try_expr(*lhs)
953 .map_err(crate::codegen::arena_failure)?
954 .kind
955 {
956 self.extra_field_names.push(name.clone());
957 self.extra_field_names
958 .push(crate::codegen::classes::accessor_getter_name(name));
959 self.extra_field_names
960 .push(crate::codegen::classes::accessor_setter_name(name));
961 for kind in [AccessorKind::Get, AccessorKind::Set] {
962 self.mentioned_closure_sigs
963 .push(crate::codegen::classes::accessor_closure_sig(kind));
964 }
965 }
966 }
967 TypedExprKind::Unary { op, operand } => {
968 if matches!(op, crate::UnOp::BitNot) {
969 self.dependency_usage
970 .note_value(crate::mangle::prelude("__value_bitnot"));
971 self.dependency_usage
972 .note_value(crate::mangle::prelude("__value_bitor"));
973 }
974 let operand_ty = ta
975 .try_expr(*operand)
976 .map_err(crate::codegen::arena_failure)?
977 .ty
978 .peel();
979 if matches!(op, crate::UnOp::Neg) && operand_ty.is_bigint() {
980 self.dependency_usage.collect_bigint_host_value("neg");
981 }
982 if matches!(op, crate::UnOp::Pos) && operand_ty.is_string_shaped() {
983 self.dependency_usage.collect_number_coercion_host_value();
984 }
985 }
986 TypedExprKind::Call { mangled, .. } | TypedExprKind::GenericCall { mangled, .. } => {
987 self.dependency_usage.note_value(mangled.clone());
988 }
989 TypedExprKind::McpCall { server, tool, .. } => {
990 self.dependency_usage
991 .note_value(crate::mangle::host(&format!("@mcp/{server}"), tool));
992 self.dependency_usage
993 .note_value(crate::mangle::host(crate::runtime::MCP_MODULE_NAME, "call"));
994 if !matches!(expr.ty.peel(), Type::String) {
995 self.dependency_usage.collect_json_host_values();
996 }
997 }
998 TypedExprKind::IntrinsicCall { kind, .. } => match kind {
999 Intrinsic::JsonParse | Intrinsic::JsonStringify => {
1000 self.dependency_usage.collect_json_host_values();
1001 }
1002 Intrinsic::BigIntFromString => {
1003 self.dependency_usage
1004 .collect_bigint_host_value("fromString");
1005 }
1006 Intrinsic::ToString => {
1007 self.dependency_usage
1008 .note_value(crate::mangle::prelude("__value_to_string"));
1009 }
1010 Intrinsic::Assert => {}
1011 },
1012 TypedExprKind::MethodCall {
1013 receiver,
1014 iface,
1015 name,
1016 args,
1017 ..
1018 } => {
1019 self.note_method_call(
1020 ta.source_type(*receiver)
1021 .map_err(crate::codegen::arena_failure)?,
1022 iface,
1023 name,
1024 args.len(),
1025 &expr.ty,
1026 )?;
1027 }
1028 TypedExprKind::GenericMethodCall {
1029 receiver,
1030 iface,
1031 name,
1032 args,
1033 ..
1034 } => {
1035 self.note_method_call(
1036 ta.source_type(*receiver)
1037 .map_err(crate::codegen::arena_failure)?,
1038 iface,
1039 name,
1040 args.len(),
1041 &expr.ty,
1042 )?;
1043 }
1044 TypedExprKind::InterfacePropertyAccess { iface, name, .. } => {
1045 self.dependency_usage
1046 .note_member(crate::mangle::extend(iface, &name.name));
1047 }
1048 TypedExprKind::ObjectLiteral { fields, members } => {
1049 if members
1050 .iter()
1051 .any(|m| matches!(m, crate::TypedObjectMember::Computed { .. }))
1052 {
1053 self.note_record_helpers();
1054 }
1055 if members.iter().any(|member| {
1056 matches!(
1057 member,
1058 crate::TypedObjectMember::Spread { .. }
1059 | crate::TypedObjectMember::Computed { .. }
1060 )
1061 }) {
1062 self.dependency_usage.note_member(crate::mangle::extend(
1063 &crate::mangle::prelude("ObjectConstructor"),
1064 "#spread",
1065 ));
1066 }
1067 for member in members {
1068 if let crate::TypedObjectMember::Spread { source, .. } = member
1069 && let Some(omitted) = ta.spread_omitted_fields.get(source)
1070 {
1071 self.extra_field_names.extend(omitted.iter().cloned());
1072 }
1073 }
1074 for field in fields {
1075 let mut source = Some(&field.source);
1076 while let Some(TypedObjectFieldSource::Spread {
1077 source_ty,
1078 field_name,
1079 fallback,
1080 ..
1081 }) = source
1082 {
1083 self.visit_type(source_ty)?;
1084 self.extra_field_names.push(field_name.clone());
1087 self.note_shape_member_names(source_ty);
1088 source = fallback.as_deref();
1089 }
1090 }
1091 }
1092 TypedExprKind::ArrayLiteral { element_ty, .. } => {
1093 self.visit_type(element_ty)?;
1094 }
1095 TypedExprKind::TupleLiteral { element_types, .. } => {
1096 for ty in element_types {
1097 self.visit_type(ty)?;
1098 }
1099 }
1100 TypedExprKind::FieldAccess { receiver, name } => {
1101 self.extra_field_names.push(name.name.clone());
1102 self.note_shaped_property_access(
1103 ta.source_type(*receiver)
1104 .map_err(crate::codegen::arena_failure)?,
1105 &name.name,
1106 AccessorKind::Get,
1107 );
1108 }
1109 TypedExprKind::IndexAccess { .. } => {
1110 self.note_index_check();
1111 }
1112 TypedExprKind::Closure {
1113 runtime_generics,
1114 params,
1115 captured,
1116 body,
1117 return_type,
1118 ..
1119 } => {
1120 self.closure_metas.push(ClosureMeta {
1121 this_type: ta.closure_this.get(&id).cloned(),
1122 self_name: ta.closure_names.get(&id).cloned(),
1123 runtime_generics: runtime_generics.clone(),
1124 expr_id: id,
1125 signature: expr.ty.clone(),
1126 captured: captured.clone(),
1127 params: params.clone(),
1128 body: body.clone(),
1129 return_type: return_type.clone(),
1130 });
1131 for p in params {
1132 self.visit_type(&p.ty)?;
1133 }
1134 self.visit_type(return_type)?;
1135 }
1136 TypedExprKind::Narrowed { cast_info, .. } => {
1137 self.visit_type(&cast_info.from_ty)?;
1138 self.visit_type(&cast_info.to_ty)?;
1139 }
1140 TypedExprKind::OptionalChain { base, parts } => {
1141 self.note_chain_parts(ta, id, *base, parts)?;
1142 }
1143 TypedExprKind::PostfixUnary { target, .. } => match target {
1144 PostfixTarget::Local { .. } => {
1145 self.note_postfix_target(&expr.ty)?;
1146 }
1147 PostfixTarget::Global { mangled, .. } => {
1148 self.dependency_usage.note_value(mangled.clone());
1149 self.note_postfix_target(&expr.ty)?;
1150 }
1151 PostfixTarget::Field { receiver, name, .. } => {
1152 self.note_postfix_target(&expr.ty)?;
1153 let receiver_ty = ta
1154 .source_type(*receiver)
1155 .map_err(crate::codegen::arena_failure)?;
1156 self.note_shaped_property_access(receiver_ty, &name.name, AccessorKind::Get);
1157 self.note_shaped_property_access(receiver_ty, &name.name, AccessorKind::Set);
1158 }
1159 PostfixTarget::Index { elem_ty, .. } => {
1160 self.note_postfix_target(elem_ty)?;
1161 self.note_index_check();
1162 }
1163 },
1164 TypedExprKind::NonNullAssert { .. } => {
1165 self.string_pool.intern_text(throw::NON_NULL_ASSERT_MESSAGE);
1166 }
1167 TypedExprKind::Cast {
1168 target_ty, check, ..
1169 } => {
1170 self.string_pool
1171 .intern_text(&cast_check::error_prefix(target_ty));
1172 for tag in cast_check::TYPE_TAG_STRINGS {
1173 self.string_pool.intern_text(tag);
1174 }
1175 self.visit_type(target_ty)?;
1176 if let Some(check) = check {
1177 self.visit_type(check)?;
1178 self.note_shape_member_names(check);
1179 }
1180 }
1181 TypedExprKind::Regex { source, flags } => {
1182 self.string_pool.intern_text(source);
1183 self.string_pool.intern_text(flags);
1184 }
1185 TypedExprKind::CallClosure { callee, .. } => {
1186 self.dependency_usage
1187 .note_value(crate::mangle::prelude("__value_invoke_defaults"));
1188 self.string_pool.intern_text(&cast_check::error_prefix(
1189 ta.source_type(*callee)
1190 .map_err(crate::codegen::arena_failure)?,
1191 ));
1192 for tag in cast_check::TYPE_TAG_STRINGS {
1193 self.string_pool.intern_text(tag);
1194 }
1195 }
1196 TypedExprKind::InstanceOf { class, .. } => {
1197 self.visit_type(class)?;
1200 }
1201 TypedExprKind::Number(_)
1202 | TypedExprKind::Boolean(_)
1203 | TypedExprKind::Null
1204 | TypedExprKind::Undefined
1205 | TypedExprKind::This
1206 | TypedExprKind::EffectThen { .. }
1207 | TypedExprKind::Sequence { .. }
1208 | TypedExprKind::LocalRef { .. }
1209 | TypedExprKind::LocalNarrowRef { .. }
1210 | TypedExprKind::SuperCtorCall { .. }
1211 | TypedExprKind::SuperMethodCall { .. }
1212 | TypedExprKind::TypeofTag { .. }
1213 | TypedExprKind::Ternary { .. }
1214 | TypedExprKind::NullishCoalesce { .. } => {}
1215 };
1216 Ok(())
1217 }
1218
1219 fn note_method_call(
1222 &mut self,
1223 receiver_ty: &Type,
1224 iface: &crate::MangledName,
1225 name: &crate::Ident,
1226 arity: usize,
1227 ret: &Type,
1228 ) -> Result<(), crate::compiler_error::CompilerFailure> {
1229 self.dependency_usage
1230 .note_member(crate::mangle::extend(iface, &name.name));
1231 self.string_pool.intern_text("Value is not callable");
1232 self.string_pool
1233 .intern_text("Unbound function has no this receiver");
1234 self.extra_field_names.push(name.name.clone());
1235 self.note_shape_dispatch(receiver_ty, arity, ret)?;
1236
1237 Ok(())
1238 }
1239
1240 fn note_shape_dispatch(
1251 &mut self,
1252 receiver_ty: &Type,
1253 arity: usize,
1254 ret: &Type,
1255 ) -> Result<(), crate::compiler_error::CompilerFailure> {
1256 self.string_pool.intern_text("Value is not callable");
1257 self.string_pool
1258 .intern_text("Unbound function has no this receiver");
1259 let receiver_ty = crate::typechecker::infer::narrowing::strip_nullish(receiver_ty);
1261 if !matches!(
1262 receiver_ty.peel(),
1263 Type::InterfaceRef { .. } | Type::ClassRef { .. }
1264 ) {
1265 return Ok(());
1266 }
1267 self.mentioned_closure_sigs
1268 .push(ClosureSig::of(arity, ret)?);
1269
1270 Ok(())
1271 }
1272
1273 fn note_binding_cell(&mut self) {
1274 self.string_pool
1275 .intern_text(super::function_emitter::binding_cells::UNINITIALIZED_PARAMETER_MESSAGE);
1276 }
1277
1278 fn note_chain_parts(
1281 &mut self,
1282 ta: &TypedAst,
1283 id: ExprId,
1284 base: ExprId,
1285 parts: &[TypedChainPart],
1286 ) -> Result<(), crate::compiler_error::CompilerFailure> {
1287 let source_types = ta.runtime_chain_types.get(&id);
1288 let mut receiver_ty = ta
1289 .source_type(base)
1290 .map_err(crate::codegen::arena_failure)?;
1291 for (index, part) in parts.iter().enumerate() {
1292 if let TypedChainPart::MethodCall { iface, name, .. }
1293 | TypedChainPart::InterfaceProperty { iface, name, .. } = part
1294 {
1295 self.string_pool.intern_text(iface.as_str());
1296 self.string_pool.intern_text(&name.name);
1297 }
1298 match part {
1299 TypedChainPart::Field {
1300 name, result_ty, ..
1301 } => {
1302 self.extra_field_names.push(name.name.clone());
1303 self.visit_type(result_ty)?;
1304 self.note_shaped_property_access(receiver_ty, &name.name, AccessorKind::Get);
1305 }
1306 TypedChainPart::InterfaceProperty {
1307 iface,
1308 name,
1309 result_ty,
1310 span,
1311 ..
1312 } => {
1313 if ta.authored_call_arguments(*span).is_some()
1314 && !crate::codegen::is_host_package(iface.as_str())
1315 {
1316 for helper in ["__value_member", "__value_invoke"] {
1317 self.dependency_usage
1318 .note_value(crate::mangle::prelude(helper));
1319 }
1320 }
1321 self.extra_field_names.push(name.name.clone());
1322 self.dependency_usage
1323 .note_member(crate::mangle::extend(iface, &name.name));
1324 self.visit_type(result_ty)?;
1325 }
1326 TypedChainPart::Index { result_ty, .. } => {
1327 self.note_record_helpers();
1328 self.visit_type(result_ty)?;
1329 self.note_index_check();
1332 }
1333 TypedChainPart::Call { result_ty, .. } => {
1334 self.dependency_usage
1335 .note_value(crate::mangle::prelude("__value_invoke_defaults"));
1336 self.visit_type(result_ty)?;
1337 }
1338 TypedChainPart::NonNull { result_ty, .. } => {
1339 self.visit_type(result_ty)?;
1340 self.string_pool.intern_text(throw::NON_NULL_ASSERT_MESSAGE);
1341 }
1342 TypedChainPart::MethodCall {
1343 iface,
1344 name,
1345 args,
1346 result_ty,
1347 span,
1348 ..
1349 } => {
1350 if ta.authored_call_arguments(*span).is_some()
1351 && !crate::codegen::is_host_package(iface.as_str())
1352 {
1353 for helper in ["__value_member", "__value_invoke"] {
1354 self.dependency_usage
1355 .note_value(crate::mangle::prelude(helper));
1356 }
1357 }
1358 self.extra_field_names.push(name.name.clone());
1359 self.dependency_usage
1360 .note_member(crate::mangle::extend(iface, &name.name));
1361 self.visit_type(result_ty)?;
1362 self.note_shape_dispatch(receiver_ty, args.len(), result_ty)?;
1363 }
1364 }
1365 receiver_ty = source_types.map_or_else(|| part.result_ty(), |types| &types[index + 1]);
1366 }
1367 Ok(())
1368 }
1369
1370 fn note_shaped_property_access(&mut self, receiver_ty: &Type, prop: &str, kind: AccessorKind) {
1383 if matches!(kind, AccessorKind::Set) {
1384 self.dependency_usage.note_member(crate::mangle::extend(
1385 &crate::mangle::prelude("ObjectConstructor"),
1386 "#insertField",
1387 ));
1388 }
1389 self.mentioned_closure_sigs
1390 .push(super::field_guards::signature());
1391 if !is_shaped_receiver(receiver_ty) && !matches!(receiver_ty.peel(), Type::ClassRef { .. })
1392 {
1393 return;
1394 }
1395 self.string_pool.intern_text("Value is not callable");
1396 self.string_pool
1397 .intern_text("Unbound function has no this receiver");
1398 self.extra_field_names
1401 .push(crate::codegen::classes::accessor_getter_name(prop));
1402 self.mentioned_closure_sigs
1403 .push(crate::codegen::classes::accessor_closure_sig(
1404 AccessorKind::Get,
1405 ));
1406 if matches!(kind, AccessorKind::Set) {
1407 self.extra_field_names
1408 .push(crate::codegen::classes::accessor_setter_name(prop));
1409 self.mentioned_closure_sigs
1410 .push(crate::codegen::classes::accessor_closure_sig(
1411 AccessorKind::Set,
1412 ));
1413 self.string_pool
1416 .intern_text(throw::READ_ONLY_PROPERTY_MESSAGE);
1417 }
1418 }
1419
1420 fn note_index_check(&mut self) {
1421 self.string_pool.intern_text(bounds::INDEX_OOB_MESSAGE);
1422 for ty in [Type::Array(Box::new(Type::Unknown)), Type::Uint8Array] {
1423 self.string_pool.intern_text(&cast_check::error_prefix(&ty));
1424 }
1425 for tag in cast_check::TYPE_TAG_STRINGS {
1426 self.string_pool.intern_text(tag);
1427 }
1428 }
1429
1430 fn note_postfix_target(
1431 &mut self,
1432 ty: &Type,
1433 ) -> Result<(), crate::compiler_error::CompilerFailure> {
1434 self.dependency_usage.collect_postfix_bigint_ops(ty);
1435 self.visit_type(ty)?;
1436
1437 Ok(())
1438 }
1439}
1440
1441fn is_shaped_receiver(ty: &Type) -> bool {
1445 let ty = crate::typechecker::infer::narrowing::strip_nullish(ty);
1446 match ty.peel() {
1447 Type::InterfaceRef { .. } | Type::Object { .. } => true,
1448 Type::Union(members) => members
1449 .iter()
1450 .any(|m| matches!(m.peel(), Type::InterfaceRef { .. } | Type::Object { .. })),
1451 _ => false,
1452 }
1453}