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