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