1use std::borrow::Cow;
2use std::collections::{BTreeMap, BTreeSet};
3
4use crate::{
5 MangledName, NamespaceSymbol, PackageDeclaration, Shape, Type, TypeKind, TypeSymbol, ValueKind,
6 ValueSymbol,
7};
8
9pub(super) const RECORD_HELPERS: [&str; 6] = [
12 "#getField",
13 "#setField",
14 "#recordValues",
15 "#hasField",
16 "#quotedRecordKeys",
17 "#matchesKeyDomain",
18];
19
20pub struct DependencyValue<'a> {
21 pub package: &'a PackageDeclaration,
22 pub export_name: Cow<'a, str>,
23 pub symbol: &'a ValueSymbol,
24}
25
26pub struct DependencyType<'a> {
27 pub package: &'a PackageDeclaration,
28 pub name: &'a str,
29 pub symbol: &'a TypeSymbol,
30 pub full: bool,
31}
32
33#[derive(Debug)]
34pub struct DependencyUsage {
35 values: BTreeSet<MangledName>,
36 members: BTreeSet<MangledName>,
37 types: BTreeSet<MangledName>,
38 member_types: BTreeSet<MangledName>,
39 shapes: BTreeSet<Shape>,
40 collected_shapes: BTreeSet<Shape>,
43 uses_bigint: bool,
44}
45
46impl DependencyUsage {
47 pub(crate) fn empty() -> Self {
48 Self {
49 values: BTreeSet::new(),
50 members: BTreeSet::new(),
51 types: BTreeSet::new(),
52 member_types: BTreeSet::new(),
53 shapes: BTreeSet::new(),
54 collected_shapes: BTreeSet::new(),
55 uses_bigint: false,
56 }
57 }
58
59 pub(crate) fn finish(mut self, dependencies: &[&PackageDeclaration]) -> Self {
60 self.collect_codegen_prelude_values();
61 for package in dependencies {
62 for symbol in package.runtime_types.values() {
63 if let TypeKind::Class {
64 narrowing_checks, ..
65 } = &symbol.kind
66 && !narrowing_checks.is_empty()
67 {
68 self.note_type(symbol.mangled_name.clone());
69 }
70 }
71 }
72 self.resolve_dependency_shapes(dependencies);
73 self
74 }
75
76 pub fn is_value_used(&self, mangled: &MangledName) -> bool {
77 self.values.contains(mangled)
78 }
79
80 pub fn is_member_used(&self, mangled: &MangledName) -> bool {
81 self.members.contains(mangled)
82 }
83
84 pub fn is_type_used(&self, mangled: &MangledName) -> bool {
85 self.types.contains(mangled)
86 }
87
88 pub fn is_shape_used(&self, shape: &Shape) -> bool {
89 self.shapes.contains(shape)
90 }
91
92 pub fn uses_bigint(&self) -> bool {
93 self.uses_bigint
94 }
95
96 pub(crate) fn note_value(&mut self, mangled: MangledName) {
97 self.values.insert(mangled);
98 }
99
100 pub(crate) fn note_member(&mut self, mangled: MangledName) {
101 self.members.insert(mangled);
102 }
103
104 pub(crate) fn note_type(&mut self, mangled: MangledName) {
105 self.types.insert(mangled);
106 }
107
108 pub(crate) fn note_shape(&mut self, shape: Shape) {
109 self.shapes.insert(shape);
110 }
111
112 pub fn is_host_value_used(&self, module: &str, name: &str) -> bool {
113 self.values.contains(&crate::mangle::host(module, name))
114 }
115
116 pub fn dependency_values<'a>(
117 &self,
118 dependencies: &'a [&'a PackageDeclaration],
119 ) -> Vec<DependencyValue<'a>> {
120 let mut values = Vec::new();
121 for package in dependencies {
122 values.extend(package.values.iter().filter_map(|(name, symbol)| {
123 self.is_value_used(&symbol.mangled_name)
124 .then_some(DependencyValue {
125 package,
126 export_name: Cow::Borrowed(name.as_str()),
127 symbol,
128 })
129 }));
130 collect_used_namespace_values(self, package, "", &package.namespaces, &mut values);
131 }
132 values
133 }
134
135 pub fn dependency_types<'a>(
136 &self,
137 dependencies: &'a [&'a PackageDeclaration],
138 ) -> Vec<DependencyType<'a>> {
139 let mut types = Vec::new();
140 for package in dependencies {
141 collect_used_types(self, package, &package.types, &mut types);
142 for (name, symbol) in &package.runtime_types {
143 if self.is_type_reachable(symbol)
144 && !package
145 .types
146 .values()
147 .any(|public| public.mangled_name == symbol.mangled_name)
148 {
149 types.push(DependencyType {
150 package,
151 name,
152 symbol,
153 full: self.is_type_fully_used(symbol),
154 });
155 }
156 }
157 collect_used_namespace_types(self, package, &package.namespaces, &mut types);
158 }
159 types
160 }
161
162 pub fn dependency_shapes<'a>(
163 &self,
164 dependencies: &'a [&'a PackageDeclaration],
165 ) -> Vec<&'a Shape> {
166 dependencies
167 .iter()
168 .flat_map(|package| package.shapes.iter())
169 .filter(|shape| self.is_shape_used(shape))
170 .collect()
171 }
172
173 pub fn is_type_fully_used(&self, ty: &TypeSymbol) -> bool {
174 self.is_type_used(&ty.mangled_name)
175 }
176
177 pub fn is_type_reachable(&self, ty: &TypeSymbol) -> bool {
178 self.is_type_used(&ty.mangled_name) || self.member_types.contains(&ty.mangled_name)
179 }
180
181 pub fn is_type_reachable_by_name(&self, mangled: &MangledName) -> bool {
185 self.types.contains(mangled) || self.member_types.contains(mangled)
186 }
187
188 pub fn is_interface_member_used(&self, ty: &TypeSymbol, member: &str) -> bool {
189 self.is_type_used(&ty.mangled_name)
190 || self.is_member_used(&crate::mangle::extend(&ty.mangled_name, member))
191 }
192
193 fn resolve_dependency_shapes(&mut self, dependencies: &[&PackageDeclaration]) {
194 let mut values: BTreeMap<MangledName, &crate::ValueSymbol> = BTreeMap::new();
195 let mut types: BTreeMap<MangledName, &crate::TypeSymbol> = BTreeMap::new();
196 let mut members: BTreeMap<MangledName, (&crate::TypeSymbol, &str)> = BTreeMap::new();
197 let mut statics: BTreeMap<MangledName, (&crate::TypeSymbol, &str)> = BTreeMap::new();
201
202 for defs in dependencies {
203 collect_values(&defs.values, &mut values);
204 collect_namespace_values(&defs.namespaces, &mut values);
205 for ty_sym in defs.runtime_types.values().chain(defs.types.values()) {
206 types.insert(ty_sym.mangled_name.clone(), ty_sym);
207 if let TypeKind::Interface {
208 methods,
209 properties,
210 ..
211 } = &ty_sym.kind
212 {
213 for name in methods.keys() {
214 members.insert(
215 crate::mangle::extend(&ty_sym.mangled_name, name),
216 (ty_sym, name),
217 );
218 }
219 for name in properties.keys() {
220 members.insert(
221 crate::mangle::extend(&ty_sym.mangled_name, name),
222 (ty_sym, name),
223 );
224 }
225 }
226 if let TypeKind::Class {
227 statics: static_methods,
228 static_fields,
229 ..
230 } = &ty_sym.kind
231 {
232 for name in static_methods.keys().chain(static_fields.keys()) {
233 statics.insert(
234 crate::mangle::static_member(&ty_sym.mangled_name, name),
235 (ty_sym, name),
236 );
237 }
238 }
239 }
240 }
241
242 let mut resolved_values = BTreeSet::new();
243 let mut resolved_members = BTreeSet::new();
244 let mut resolved_types = BTreeSet::new();
245 loop {
246 let mut changed = false;
247
248 for mangled in self.values.clone() {
249 if !resolved_values.insert(mangled.clone()) {
250 continue;
251 }
252 if let Some(sym) = values.get(&mangled) {
253 changed = true;
254 self.collect_value_symbol(sym);
255 }
256 if let Some((ty_sym, member_name)) = statics.get(&mangled) {
257 changed = true;
258 self.member_types.insert(ty_sym.mangled_name.clone());
259 self.collect_type_symbol(ty_sym);
260 if let TypeKind::Class {
261 statics: static_methods,
262 static_fields,
263 ..
264 } = &ty_sym.kind
265 {
266 if let Some(sig) = static_methods.get(*member_name) {
267 for p in &sig.params {
268 self.collect_type(&p.ty);
269 }
270 self.collect_type(&sig.ret);
271 }
272 if let Some(field) = static_fields.get(*member_name) {
273 self.collect_type(&field.ty);
274 }
275 }
276 }
277 }
278
279 for mangled in self.members.clone() {
280 if !resolved_members.insert(mangled.clone()) {
281 continue;
282 }
283 if let Some(sym) = values.get(&mangled) {
284 changed = true;
285 self.values.insert(mangled.clone());
286 self.collect_value_symbol(sym);
287 }
288 if let Some((ty_sym, member_name)) = members.get(&mangled) {
289 changed = true;
290 self.member_types.insert(ty_sym.mangled_name.clone());
291 self.collect_type_symbol(ty_sym);
292 if let TypeKind::Interface {
293 methods,
294 properties,
295 ..
296 } = &ty_sym.kind
297 {
298 if let Some(sig) = methods.get(*member_name) {
299 for p in &sig.params {
300 self.collect_type(&p.ty);
301 }
302 self.collect_type(&sig.ret);
303 }
304 if let Some(sig) = properties.get(*member_name) {
305 self.collect_type(&sig.ty);
306 }
307 }
308 }
309 }
310
311 for mangled in self.types.clone() {
312 if !resolved_types.insert(mangled.clone()) {
313 continue;
314 }
315 if let Some(sym) = types.get(&mangled) {
316 changed = true;
317 self.collect_type_symbol(sym);
318 if let TypeKind::Interface {
323 methods,
324 properties,
325 ..
326 } = &sym.kind
327 {
328 for name in methods.keys().chain(properties.keys()) {
329 self.members
330 .insert(crate::mangle::extend(&sym.mangled_name, name));
331 }
332 }
333 }
334 }
335
336 if !changed {
337 break;
338 }
339 }
340 }
341
342 fn collect_value_symbol(&mut self, value: &crate::ValueSymbol) {
343 match &value.kind {
344 ValueKind::Function { params, ret, .. } => {
345 for p in params {
346 self.collect_type(&p.ty);
347 }
348 self.collect_type(ret);
349 }
350 ValueKind::Let { ty, .. } | ValueKind::Const { ty, .. } => {
351 self.collect_type(ty);
352 }
353 }
354 }
355
356 fn collect_type_symbol(&mut self, ty_sym: &crate::TypeSymbol) {
357 match &ty_sym.kind {
358 TypeKind::Alias { ty, .. } => self.collect_type(ty),
359 TypeKind::Interface {
360 properties,
361 methods,
362 ..
363 } => {
364 for sig in properties.values() {
365 self.collect_type(&sig.ty);
366 }
367 for sig in methods.values() {
368 for p in &sig.params {
369 self.collect_type(&p.ty);
370 }
371 self.collect_type(&sig.ret);
372 }
373 }
374 TypeKind::Class {
375 fields,
376 methods,
377 statics,
378 static_fields,
379 constructor,
380 ..
381 } => {
382 for sig in fields.values() {
383 self.collect_type(&sig.ty);
384 }
385 for sig in methods.values().chain(statics.values()) {
386 for p in &sig.params {
387 self.collect_type(&p.ty);
388 }
389 self.collect_type(&sig.ret);
390 }
391 for sig in static_fields.values() {
392 self.collect_type(&sig.ty);
393 }
394 for p in constructor {
395 self.collect_type(&p.ty);
396 }
397 }
398 TypeKind::NumberEnum { .. } | TypeKind::StringEnum { .. } => {}
399 }
400 }
401
402 pub(crate) fn collect_type(&mut self, ty: &Type) {
405 if let Some(shape) = Shape::from_type(ty) {
406 if !self.collected_shapes.insert(shape.clone()) {
410 return;
411 }
412 if matches!(shape, Shape::Object { .. }) {
413 self.collect_typed_object_stringify_host_value();
414 }
415 self.shapes.insert(shape);
416 }
417 match ty.peel() {
418 Type::TemplateLiteral { substitutions, .. } => {
419 self.note_member(crate::mangle::extend(
420 &crate::mangle::prelude("ObjectConstructor"),
421 "#matchesKeyDomain",
422 ));
423 for inner in substitutions {
424 self.collect_type(inner);
425 }
426 }
427 Type::Object { fields, index } => {
428 if let Some(index) = index {
429 self.collect_type(&index.value);
430 for helper in RECORD_HELPERS {
431 self.note_member(crate::mangle::extend(
432 &crate::mangle::prelude("ObjectConstructor"),
433 helper,
434 ));
435 }
436 }
437 for field in fields.values() {
438 self.collect_type(&field.ty);
439 }
440 }
441 Type::Array(elem) => self.collect_type(elem),
442 Type::Tuple(elements) => {
443 for elem in elements {
444 self.collect_type(elem);
445 }
446 }
447 Type::Union(members) => {
448 for member in members {
449 self.collect_type(member);
450 }
451 }
452 Type::Function { params, ret, .. } => {
453 for p in params {
454 self.collect_type(p);
455 }
456 self.collect_type(ret);
457 }
458 Type::InterfaceRef { mangled, args, .. }
459 | Type::ClassRef { mangled, args, .. }
460 | Type::AliasRef { mangled, args, .. } => {
461 self.types.insert(mangled.clone());
462 for arg in args {
463 self.collect_type(arg);
464 }
465 }
466 Type::NumberEnum { mangled, .. } | Type::StringEnum { mangled, .. } => {
467 self.types.insert(mangled.clone());
468 }
469 Type::Alias { ty, .. } | Type::Refined { ty, .. } | Type::Readonly(ty) => {
470 self.collect_type(ty);
471 }
472 Type::BigInt | Type::BigIntLiteral(_) => {
473 self.uses_bigint = true;
474 }
475 Type::Number
476 | Type::NumberLiteral(_)
477 | Type::String
478 | Type::StringLiteral(_)
479 | Type::Uint8Array
480 | Type::Boolean
481 | Type::BooleanLiteral(_)
482 | Type::Null
483 | Type::Undefined
484 | Type::Void
485 | Type::Never
486 | Type::Unknown
487 | Type::NonPrimitive
488 | Type::Error
489 | Type::TypeVar(_)
490 | Type::GenericParam { .. }
491 | Type::KeyOf(_)
492 | Type::IndexedAccess { .. } => {}
493 }
494 }
495
496 pub(crate) fn collect_json_host_values(&mut self) {
497 for name in [
498 "parse",
499 "parseTypedObject",
500 "parseTypedValue",
501 "stringify",
502 "stringifyPrettyNumber",
503 "stringifyPrettyString",
504 "diagnosticPathField",
505 "diagnosticPathIndex",
506 "diagnosticMismatch",
507 "diagnosticMissingFields",
508 "stringifyTypedObject",
509 ] {
510 self.values
511 .insert(crate::mangle::host(crate::runtime::JSON_MODULE_NAME, name));
512 }
513 }
514
515 pub(crate) fn collect_typed_object_stringify_host_value(&mut self) {
516 self.values.insert(crate::mangle::host(
517 crate::runtime::JSON_MODULE_NAME,
518 "stringifyTypedObject",
519 ));
520 }
521
522 pub(crate) fn collect_bigint_host_value(&mut self, name: &str) {
523 self.uses_bigint = true;
524 self.values.insert(crate::mangle::host(
525 crate::runtime::BIGINT_MODULE_NAME,
526 name,
527 ));
528 }
529
530 pub(crate) fn collect_number_coercion_host_value(&mut self) {
532 self.values.insert(crate::mangle::host(
533 crate::runtime::NUMBER_MODULE_NAME,
534 "toNumber",
535 ));
536 }
537
538 pub(crate) fn collect_postfix_bigint_ops(&mut self, ty: &Type) {
541 if ty.is_bigint() {
542 self.collect_bigint_host_value("fromNumber");
543 self.collect_bigint_host_value("add");
544 self.collect_bigint_host_value("sub");
545 }
546 }
547
548 fn collect_codegen_prelude_values(&mut self) {
549 for name in [
550 "isFinite",
551 "isNaN",
552 "string_cmp",
553 "string_concat",
554 "string_eq",
555 "vtable_walk_enter",
556 "vtable_walk_leave",
557 ] {
558 self.values.insert(crate::mangle::prelude(name));
559 }
560 for (iface, member) in [
561 ("Boolean", "toJson"),
562 ("Boolean", "toString"),
563 ("Number", "toJson"),
564 ("Number", "toString"),
565 ("RegExpConstructor", "new"),
566 ("String", "toJson"),
567 ("String", "toString"),
568 ] {
569 self.members.insert(crate::mangle::extend(
570 &crate::mangle::prelude(iface),
571 member,
572 ));
573 }
574 self.note_type(crate::mangle::prelude("Error"));
578 self.note_type(crate::mangle::prelude("MissingValueError"));
579 self.note_type(crate::mangle::prelude("RangeError"));
580 self.note_type(crate::mangle::prelude("ReferenceError"));
581 self.note_type(crate::mangle::prelude("TypeError"));
582 }
583}
584
585fn collect_values<'a>(
586 values: &'a BTreeMap<String, crate::ValueSymbol>,
587 out: &mut BTreeMap<MangledName, &'a crate::ValueSymbol>,
588) {
589 for value in values.values() {
590 out.insert(value.mangled_name.clone(), value);
591 }
592}
593
594fn collect_namespace_values<'a>(
595 namespaces: &'a BTreeMap<String, crate::NamespaceSymbol>,
596 out: &mut BTreeMap<MangledName, &'a crate::ValueSymbol>,
597) {
598 for namespace in namespaces.values() {
599 collect_values(&namespace.values, out);
600 collect_namespace_values(&namespace.namespaces, out);
601 }
602}
603
604fn collect_used_namespace_values<'a>(
605 usage: &DependencyUsage,
606 package: &'a PackageDeclaration,
607 parent_path: &str,
608 namespaces: &'a BTreeMap<String, NamespaceSymbol>,
609 out: &mut Vec<DependencyValue<'a>>,
610) {
611 for (ns_name, namespace) in namespaces {
612 let path = if parent_path.is_empty() {
613 ns_name.clone()
614 } else {
615 format!("{parent_path}#{ns_name}")
616 };
617 for (local_name, symbol) in &namespace.values {
618 if usage.is_value_used(&symbol.mangled_name) {
619 out.push(DependencyValue {
620 package,
621 export_name: Cow::Owned(format!("{path}#{local_name}")),
622 symbol,
623 });
624 }
625 }
626 collect_used_namespace_values(usage, package, &path, &namespace.namespaces, out);
627 }
628}
629
630fn collect_used_types<'a>(
631 usage: &DependencyUsage,
632 package: &'a PackageDeclaration,
633 types: &'a BTreeMap<String, TypeSymbol>,
634 out: &mut Vec<DependencyType<'a>>,
635) {
636 for (name, symbol) in types {
637 if usage.is_type_reachable(symbol) {
638 out.push(DependencyType {
639 package,
640 name,
641 symbol,
642 full: usage.is_type_fully_used(symbol),
643 });
644 }
645 }
646}
647
648fn collect_used_namespace_types<'a>(
649 usage: &DependencyUsage,
650 package: &'a PackageDeclaration,
651 namespaces: &'a BTreeMap<String, NamespaceSymbol>,
652 out: &mut Vec<DependencyType<'a>>,
653) {
654 for namespace in namespaces.values() {
655 collect_used_types(usage, package, &namespace.types, out);
656 collect_used_namespace_types(usage, package, &namespace.namespaces, out);
657 }
658}