1use std::collections::{BTreeMap, BTreeSet};
10
11use crate::{Shape, Span, Type, TypedAst};
12use serde::{Deserialize, Serialize};
13
14#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
21pub struct PackageDeclaration {
22 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
25 pub runtime_functions: BTreeMap<crate::MangledName, RuntimeFunction>,
26 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
27 pub runtime_globals: BTreeMap<crate::MangledName, Type>,
28 pub package_name: String,
30 pub mcp_server: Option<String>,
35 pub values: BTreeMap<String, ValueSymbol>,
36 pub types: BTreeMap<String, TypeSymbol>,
38 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
41 pub runtime_types: BTreeMap<String, TypeSymbol>,
42 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
44 pub runtime_generics: BTreeSet<crate::MangledName>,
45 pub shapes: Vec<crate::Shape>,
47 pub namespaces: BTreeMap<String, NamespaceSymbol>,
49 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
53 pub closure_caches: BTreeSet<crate::MangledName>,
54}
55
56#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
57pub struct RuntimeFunction {
58 pub params: Vec<Type>,
59 pub ret: Type,
60}
61
62impl PackageDeclaration {
63 pub fn new() -> Self {
65 Self::with_package(crate::mangle::USER_PACKAGE)
66 }
67
68 pub fn with_package(package_name: impl Into<String>) -> Self {
69 Self {
70 package_name: package_name.into(),
71 ..Self::default()
72 }
73 }
74
75 pub fn from_typed_ast(ast: &TypedAst) -> Self {
77 let mut defs = PackageDeclaration::with_package(ast.package_name.clone());
78 for f in &ast.functions {
79 if !f.generics.is_empty() {
80 defs.runtime_generics.insert(f.mangled_name.clone());
81 }
82 defs.values
83 .entry(f.name.name.clone())
84 .or_insert_with(|| ValueSymbol {
85 name: f.name.name.clone(),
86 mangled_name: f.mangled_name.clone(),
87 declaration_span: f.name.span,
88 kind: ValueKind::Function {
89 generics: f.generics.clone(),
90 params: f.params.iter().map(param_from_typed).collect(),
91 ret: f.return_type.clone(),
92 type_predicate: f.type_predicate.clone(),
93 doc: f.doc.clone(),
94 },
95 });
96 }
97 for g in &ast.globals {
98 defs.values
99 .entry(g.name.name.clone())
100 .or_insert_with(|| ValueSymbol {
101 name: g.name.name.clone(),
102 mangled_name: g.mangled_name.clone(),
103 declaration_span: g.name.span,
104 kind: match g.kind {
105 crate::GlobalKind::Let => ValueKind::Let {
106 ty: g.ty.clone(),
107 doc: g.doc.clone(),
108 },
109 crate::GlobalKind::Const => ValueKind::Const {
110 ty: g.ty.clone(),
111 doc: g.doc.clone(),
112 },
113 },
114 });
115 }
116 for ty_decl in &ast.types {
118 if let crate::TypedTypeDecl::Alias(alias) = ty_decl {
119 defs.types
120 .entry(alias.name.name.clone())
121 .or_insert_with(|| TypeSymbol {
122 name: alias.name.name.clone(),
123 mangled_name: crate::mangle::package_symbol(
124 &ast.package_name,
125 &alias.name.name,
126 ),
127 declaration_span: alias.name.span,
128 kind: TypeKind::Alias {
129 generics: alias.generics.clone(),
130 ty: alias.ty.clone(),
131 doc: alias.doc.clone(),
132 },
133 });
134 }
135 }
136 defs.refresh_shapes();
137 defs
138 }
139
140 pub fn type_symbol(&self, mangled: &crate::MangledName) -> Option<&TypeSymbol> {
145 self.runtime_types
146 .get(mangled.as_str())
147 .or_else(|| symbol_named(self.types.values(), mangled))
148 .or_else(|| {
149 self.namespaces
150 .values()
151 .find_map(|namespace| namespace_type_symbol(namespace, mangled))
152 })
153 }
154
155 pub fn refresh_shapes(&mut self) {
156 self.shapes = self.collect_shapes();
157 }
158
159 pub fn collect_shapes(&self) -> Vec<Shape> {
160 let mut collector = PackageShapeCollector::default();
161 for value in self.values.values() {
162 collector.collect_value(value);
163 }
164 for ty in self.types.values() {
165 collector.collect_type_symbol(ty);
166 }
167 for namespace in self.namespaces.values() {
168 collector.collect_namespace(namespace);
169 }
170 collector.shapes
171 }
172
173 pub fn check_type_limits(&self) -> Result<(), crate::type_size::TypeTooLarge> {
178 check_namespace_limits(&self.namespaces)?;
179 let mut result = Ok(());
180 self.for_each_type(&mut |ty| {
181 if result.is_ok() {
182 result = crate::type_size::check(ty);
183 }
184 });
185 result
186 }
187
188 fn for_each_type(&self, visit: &mut dyn FnMut(&Type)) {
192 let Self {
193 runtime_functions,
194 runtime_globals,
195 package_name: _,
196 mcp_server: _,
197 values,
198 types,
199 runtime_types,
200 runtime_generics: _,
201 shapes,
202 namespaces,
203 closure_caches: _,
204 } = self;
205 for RuntimeFunction { params, ret } in runtime_functions.values() {
206 params.iter().for_each(&mut *visit);
207 visit(ret);
208 }
209 runtime_globals.values().for_each(&mut *visit);
210 values.values().for_each(|value| value_types(value, visit));
211 types
212 .values()
213 .chain(runtime_types.values())
214 .for_each(|symbol| type_symbol_types(symbol, visit));
215 for shape in shapes {
216 match shape {
217 Shape::Object { fields, index } => {
218 fields.values().for_each(|field| visit(&field.ty));
219 if let Some(index) = index {
220 visit(&index.value);
221 }
222 }
223 Shape::Array(element) => visit(element),
224 Shape::Tuple(members) => {
225 members.iter().for_each(&mut *visit);
226 }
227 Shape::Union(members) => {
228 members.iter().for_each(&mut *visit);
229 }
230 }
231 }
232 let mut pending: Vec<&NamespaceSymbol> = namespaces.values().collect();
233 while let Some(namespace) = pending.pop() {
234 let NamespaceSymbol {
235 name: _,
236 mangled_prefix: _,
237 declaration_span: _,
238 values,
239 types,
240 namespaces,
241 doc: _,
242 } = namespace;
243 values.values().for_each(|value| value_types(value, visit));
244 types
245 .values()
246 .for_each(|symbol| type_symbol_types(symbol, visit));
247 pending.extend(namespaces.values());
248 }
249 }
250}
251
252fn check_namespace_limits(
253 namespaces: &BTreeMap<String, NamespaceSymbol>,
254) -> Result<(), crate::type_size::TypeTooLarge> {
255 use crate::compiler_limits::{
256 MAX_NAMESPACE_DEPTH, MAX_NAMESPACE_NODES, MAX_NAMESPACE_PATH_BYTES,
257 };
258 use crate::type_size::TypeTooLarge;
259
260 let mut pending = vec![(namespaces.iter(), 0usize)];
262 let mut nodes = 0;
263 let mut path_bytes = 0usize;
264 while let Some((siblings, prefix_bytes)) = pending.last_mut() {
265 let Some((name, namespace)) = siblings.next() else {
266 pending.pop();
267 continue;
268 };
269 let path_len = prefix_bytes.saturating_add(name.len());
270 nodes += 1;
271 path_bytes = path_bytes.saturating_add(path_len);
272 for name in namespace.types.keys() {
273 path_bytes =
274 path_bytes.saturating_add(path_len.saturating_add(1).saturating_add(name.len()));
275 if path_bytes > MAX_NAMESPACE_PATH_BYTES {
276 return Err(TypeTooLarge::NamespaceMetadata);
277 }
278 }
279 if nodes > MAX_NAMESPACE_NODES
280 || pending.len() > MAX_NAMESPACE_DEPTH
281 || path_bytes > MAX_NAMESPACE_PATH_BYTES
282 {
283 return Err(TypeTooLarge::NamespaceMetadata);
284 }
285 if !namespace.namespaces.is_empty() {
286 pending.push((namespace.namespaces.iter(), path_len.saturating_add(1)));
287 }
288 }
289 Ok(())
290}
291
292fn namespace_type_symbol<'a>(
293 namespace: &'a NamespaceSymbol,
294 mangled: &crate::MangledName,
295) -> Option<&'a TypeSymbol> {
296 symbol_named(namespace.types.values(), mangled).or_else(|| {
297 namespace
298 .namespaces
299 .values()
300 .find_map(|child| namespace_type_symbol(child, mangled))
301 })
302}
303
304fn symbol_named<'a>(
305 mut symbols: impl Iterator<Item = &'a TypeSymbol>,
306 mangled: &crate::MangledName,
307) -> Option<&'a TypeSymbol> {
308 symbols.find(|symbol| symbol.mangled_name == *mangled)
309}
310
311fn value_types(value: &ValueSymbol, visit: &mut dyn FnMut(&Type)) {
312 match &value.kind {
313 ValueKind::Function {
314 generics: _,
315 params,
316 ret,
317 type_predicate,
318 doc: _,
319 } => {
320 params.iter().for_each(|param| visit(¶m.ty));
321 visit(ret);
322 if let Some(predicate) = type_predicate {
323 visit(&predicate.asserted_type);
324 }
325 }
326 ValueKind::Let { ty, doc: _ } | ValueKind::Const { ty, doc: _ } => visit(ty),
327 }
328}
329
330fn type_symbol_types(symbol: &TypeSymbol, visit: &mut dyn FnMut(&Type)) {
331 match &symbol.kind {
332 TypeKind::Interface {
333 generics: _,
334 methods,
335 properties,
336 index,
337 dispatch: _,
338 doc: _,
339 } => {
340 methods
341 .values()
342 .for_each(|method| method_types(method, visit));
343 properties.values().for_each(|property| visit(&property.ty));
344 if let Some(index) = index {
345 visit(&index.value);
346 }
347 }
348 TypeKind::NumberEnum { .. } | TypeKind::StringEnum { .. } => {}
349 TypeKind::Alias {
350 generics: _,
351 ty,
352 doc: _,
353 } => visit(ty),
354 TypeKind::Class {
355 generics: _,
356 fields,
357 narrowing_checks,
358 methods,
359 method_visibility: _,
360 accessors,
361 constructor,
362 constructor_visibility: _,
363 statics,
364 static_visibility: _,
365 static_fields,
366 extends,
367 implements: _,
368 doc: _,
369 } => {
370 fields
371 .values()
372 .chain(static_fields.values())
373 .for_each(|field| visit(&field.ty));
374 for check in narrowing_checks.values() {
375 narrowing_check_types(check, visit);
376 }
377 methods
378 .values()
379 .chain(statics.values())
380 .for_each(|method| method_types(method, visit));
381 for accessor in accessors {
382 match accessor {
383 AccessorSig::Getter { name: _, ret_ty } => visit(ret_ty),
384 AccessorSig::Setter { name: _, param } => visit(¶m.ty),
385 }
386 }
387 constructor.iter().for_each(|param| visit(¶m.ty));
388 if let Some(extends) = extends {
389 extends.args.iter().for_each(&mut *visit);
390 }
391 }
392 }
393}
394
395fn narrowing_check_types(check: &crate::FieldNarrowingCheck, visit: &mut dyn FnMut(&Type)) {
396 let crate::FieldNarrowingCheck {
397 declaration: _,
398 test,
399 minimal_test_target,
400 message: _,
401 } = check;
402 if let Some(target) = minimal_test_target {
403 visit(target);
404 }
405 match test {
406 crate::FieldNarrowingTest::Shape(ty) => visit(ty),
407 crate::FieldNarrowingTest::Interface(interface) => {
408 let crate::InterfaceNarrowingTest {
409 index,
410 members,
411 methods: _,
412 non_shape_carriers,
413 shape_allowed: _,
414 nullable: _,
415 undefined: _,
416 } = interface;
417 if let Some(index) = index {
418 visit(&index.value);
419 }
420 members.values().for_each(|member| visit(&member.ty));
421 for carrier in non_shape_carriers {
422 match carrier {
423 crate::InterfaceCarrier::Array(element)
424 | crate::InterfaceCarrier::Set(element) => visit(element),
425 crate::InterfaceCarrier::Map(key, value) => {
426 visit(key);
427 visit(value);
428 }
429
430 crate::InterfaceCarrier::Number
431 | crate::InterfaceCarrier::Boolean
432 | crate::InterfaceCarrier::String
433 | crate::InterfaceCarrier::BigInt
434 | crate::InterfaceCarrier::Uint8Array
435 | crate::InterfaceCarrier::ArrayAny
436 | crate::InterfaceCarrier::MapAny
437 | crate::InterfaceCarrier::SetAny
438 | crate::InterfaceCarrier::RegExp
439 | crate::InterfaceCarrier::RegExpMatch
440 | crate::InterfaceCarrier::TemporalInstant
441 | crate::InterfaceCarrier::TemporalDuration
442 | crate::InterfaceCarrier::TemporalZonedDateTime
443 | crate::InterfaceCarrier::TemporalPlainDate
444 | crate::InterfaceCarrier::TemporalPlainTime
445 | crate::InterfaceCarrier::TemporalPlainDateTime
446 | crate::InterfaceCarrier::TemporalPlainYearMonth
447 | crate::InterfaceCarrier::TemporalPlainMonthDay
448 | crate::InterfaceCarrier::ObjectShape
449 | crate::InterfaceCarrier::Url
450 | crate::InterfaceCarrier::FsStat
451 | crate::InterfaceCarrier::FsPeek
452 | crate::InterfaceCarrier::FsDirEntry
453 | crate::InterfaceCarrier::FsInfo
454 | crate::InterfaceCarrier::FsMountInfo
455 | crate::InterfaceCarrier::FsFileWriter
456 | crate::InterfaceCarrier::HttpResponse
457 | crate::InterfaceCarrier::HttpDownloadResult
458 | crate::InterfaceCarrier::SessionEntry
459 | crate::InterfaceCarrier::SessionPage => {}
460 }
461 }
462 }
463 crate::FieldNarrowingTest::NonNull
464 | crate::FieldNarrowingTest::Substituted
465 | crate::FieldNarrowingTest::Representation => {}
466 }
467}
468
469fn method_types(method: &MethodSig, visit: &mut dyn FnMut(&Type)) {
470 let MethodSig {
471 optional: _,
472 generics: _,
473 params,
474 ret,
475 predicate,
476 doc: _,
477 } = method;
478 params.iter().for_each(|param| visit(¶m.ty));
479 visit(ret);
480 if let Some(predicate) = predicate {
481 visit(&predicate.asserted_type);
482 }
483}
484
485#[derive(Default)]
486struct PackageShapeCollector {
487 shapes: Vec<Shape>,
488 seen: BTreeSet<Shape>,
489}
490
491impl PackageShapeCollector {
492 fn collect_value(&mut self, value: &ValueSymbol) {
493 match &value.kind {
494 ValueKind::Function {
495 params,
496 ret,
497 type_predicate,
498 ..
499 } => {
500 for param in params {
501 self.collect_type(¶m.ty);
502 }
503 self.collect_type(ret);
504 if let Some(predicate) = type_predicate {
505 self.collect_type(&predicate.asserted_type);
506 }
507 }
508 ValueKind::Let { ty, .. } | ValueKind::Const { ty, .. } => self.collect_type(ty),
509 }
510 }
511
512 fn collect_type_symbol(&mut self, ty: &TypeSymbol) {
513 match &ty.kind {
514 TypeKind::Interface {
515 methods,
516 properties,
517 index,
518 ..
519 } => {
520 if let Some(index) = index {
521 self.collect_type(&index.value);
522 }
523 for method in methods.values() {
524 for param in &method.params {
525 self.collect_type(¶m.ty);
526 }
527 self.collect_type(&method.ret);
528 if let Some(predicate) = &method.predicate {
529 self.collect_type(&predicate.asserted_type);
530 }
531 }
532 for property in properties.values() {
533 self.collect_type(&property.ty);
534 }
535 }
536 TypeKind::Class {
537 fields,
538 methods,
539 accessors,
540 constructor,
541 ..
542 } => {
543 for field in fields.values() {
544 self.collect_type(&field.ty);
545 }
546 for method in methods.values() {
547 for param in &method.params {
548 self.collect_type(¶m.ty);
549 }
550 self.collect_type(&method.ret);
551 }
552 for acc in accessors {
553 match acc {
554 AccessorSig::Getter { ret_ty, .. } => self.collect_type(ret_ty),
555 AccessorSig::Setter { param, .. } => self.collect_type(¶m.ty),
556 }
557 }
558 for param in constructor {
559 self.collect_type(¶m.ty);
560 }
561 }
562 TypeKind::Alias { ty, .. } => self.collect_type(ty),
563 TypeKind::NumberEnum { .. } | TypeKind::StringEnum { .. } => {}
564 }
565 }
566
567 fn collect_namespace(&mut self, namespace: &NamespaceSymbol) {
568 for value in namespace.values.values() {
569 self.collect_value(value);
570 }
571 for ty in namespace.types.values() {
572 self.collect_type_symbol(ty);
573 }
574 for child in namespace.namespaces.values() {
575 self.collect_namespace(child);
576 }
577 }
578
579 fn collect_type(&mut self, ty: &Type) {
580 match ty {
581 Type::Object { fields, .. } => {
582 self.collect_shape(ty);
583 for field in fields.values() {
584 self.collect_type(&field.ty);
585 }
586 }
587 Type::Array(elem) => {
588 self.collect_shape(ty);
589 self.collect_type(elem);
590 }
591 Type::Tuple(elements) => {
592 self.collect_shape(ty);
593 for elem in elements {
594 self.collect_type(elem);
595 }
596 }
597 Type::Union(members) => {
598 self.collect_shape(ty);
599 for member in members {
600 self.collect_type(member);
601 }
602 }
603 Type::Function { params, ret, .. } => {
604 for param in params {
605 self.collect_type(param);
606 }
607 self.collect_type(ret);
608 }
609 Type::InterfaceRef { args, .. }
610 | Type::ClassRef { args, .. }
611 | Type::AliasRef { args, .. } => {
612 for arg in args {
613 self.collect_type(arg);
614 }
615 }
616 Type::Alias { ty, .. } | Type::Refined { ty, .. } | Type::Readonly(ty) => {
617 self.collect_type(ty);
618 }
619 Type::Number
620 | Type::BigInt
621 | Type::BigIntLiteral(_)
622 | Type::NumberLiteral(_)
623 | Type::String
624 | Type::StringLiteral(_)
625 | Type::Uint8Array
626 | Type::Boolean
627 | Type::BooleanLiteral(_)
628 | Type::Null
629 | Type::Undefined
630 | Type::Void
631 | Type::Never
632 | Type::Unknown
633 | Type::Error
634 | Type::TypeVar(_)
635 | Type::GenericParam { .. }
636 | Type::NumberEnum { .. }
637 | Type::StringEnum { .. } => {}
638 }
639 }
640
641 fn collect_shape(&mut self, ty: &Type) {
642 let Some(shape) = Shape::from_type(ty) else {
643 return;
644 };
645 if self.seen.insert(shape.clone()) {
646 self.shapes.push(shape);
647 }
648 }
649}
650
651#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
652pub struct ValueSymbol {
653 pub name: String,
655 pub mangled_name: crate::MangledName,
657 pub declaration_span: Span,
658 pub kind: ValueKind,
659}
660
661#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
662pub struct Param {
663 pub name: String,
664 pub ty: Type,
665 pub default: Option<DefaultValue>,
666 #[serde(default)]
671 pub optional: bool,
672 pub rest: bool,
674}
675
676impl Param {
677 pub fn is_omittable(&self) -> bool {
679 self.optional || self.default.is_some()
680 }
681}
682
683pub(crate) fn optional_parameter_count(params: &[Param]) -> usize {
685 params
686 .iter()
687 .rev()
688 .skip_while(|param| param.rest)
689 .take_while(|param| param.is_omittable())
690 .count()
691}
692
693impl Param {
694 pub fn new(name: impl Into<String>, ty: Type) -> Self {
695 Self {
696 name: name.into(),
697 ty,
698 default: None,
699 optional: false,
700 rest: false,
701 }
702 }
703
704 pub fn with_default(name: impl Into<String>, ty: Type, default: DefaultValue) -> Self {
705 Self {
706 name: name.into(),
707 ty,
708 default: Some(default),
709 optional: true,
710 rest: false,
711 }
712 }
713
714 pub fn optional(name: impl Into<String>, ty: Type) -> Self {
716 Self {
717 optional: true,
718 ..Self::new(name, ty)
719 }
720 }
721
722 pub fn rest(name: impl Into<String>, ty: Type) -> Self {
724 Self {
725 name: name.into(),
726 ty,
727 default: None,
728 optional: false,
729 rest: true,
730 }
731 }
732
733 pub fn anon(ty: Type) -> Self {
735 Self {
736 name: String::new(),
737 ty,
738 default: None,
739 optional: false,
740 rest: false,
741 }
742 }
743}
744
745pub(crate) fn param_from_typed(p: &crate::TypedParam) -> Param {
748 Param {
749 name: p.name.name.clone(),
750 ty: p.ty.clone(),
751 default: p.default.clone(),
752 optional: p.optional,
753 rest: p.rest,
754 }
755}
756
757#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
760pub enum DefaultValue {
761 Undefined,
762 Number(#[serde(with = "crate::artifact_f64")] f64),
763 OmittedNumber {
767 #[serde(with = "crate::artifact_f64")]
768 omitted: f64,
769 #[serde(with = "crate::artifact_f64")]
770 undefined: f64,
771 },
772 String(String),
773 Boolean(bool),
774 Null,
775 EmptyArray,
778 EmptyObject,
782 GlobalConst(crate::MangledName),
784 EnumVariant {
786 enum_mangled: crate::MangledName,
787 variant: String,
788 value: EnumVariantValue,
789 },
790}
791
792#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
793pub enum EnumVariantValue {
794 Number(#[serde(with = "crate::artifact_f64")] f64),
795 String(String),
796}
797
798#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
799pub enum ValueKind {
800 Function {
801 generics: Vec<String>,
803 params: Vec<Param>,
804 ret: Type,
805 type_predicate: Option<crate::TypePredicate>,
807 doc: Option<crate::DocComment>,
808 },
809 Let {
810 ty: Type,
814 doc: Option<crate::DocComment>,
815 },
816 Const {
817 ty: Type,
818 doc: Option<crate::DocComment>,
819 },
820}
821
822#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
823pub struct TypeSymbol {
824 pub name: String,
825 pub mangled_name: crate::MangledName,
827 pub declaration_span: Span,
828 pub kind: TypeKind,
829}
830
831#[allow(clippy::large_enum_variant)]
835#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
836pub enum TypeKind {
837 Interface {
840 generics: Vec<String>,
841 methods: BTreeMap<String, MethodSig>,
842 properties: BTreeMap<String, PropertySig>,
845 index: Option<crate::IndexSignature>,
846 dispatch: Dispatch,
847 doc: Option<crate::DocComment>,
848 },
849 NumberEnum {
851 #[serde(with = "crate::artifact_f64::pairs")]
852 variants: Vec<(String, f64)>,
853 doc: Option<crate::DocComment>,
854 },
855 StringEnum {
857 variants: Vec<(String, String)>,
858 doc: Option<crate::DocComment>,
859 },
860 Alias {
863 generics: Vec<String>,
864 ty: Type,
865 doc: Option<crate::DocComment>,
866 },
867 Class {
874 generics: Vec<String>,
878 fields: BTreeMap<String, FieldSig>,
879 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
883 narrowing_checks: BTreeMap<String, crate::FieldNarrowingCheck>,
884 methods: BTreeMap<String, MethodSig>,
885 method_visibility: BTreeMap<String, crate::Visibility>,
888 accessors: Vec<AccessorSig>,
894 constructor: Vec<Param>,
896 #[serde(default, skip_serializing_if = "crate::Visibility::is_public")]
899 constructor_visibility: crate::Visibility,
900 #[serde(default)]
904 statics: BTreeMap<String, MethodSig>,
905 #[serde(default)]
907 static_visibility: BTreeMap<String, crate::Visibility>,
908 #[serde(default)]
911 static_fields: BTreeMap<String, FieldSig>,
912 extends: Option<ClassExtends>,
914 implements: Vec<crate::MangledName>,
916 doc: Option<crate::DocComment>,
917 },
918 }
920
921#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
925pub struct ClassExtends {
926 pub parent: crate::MangledName,
927 pub args: Vec<Type>,
930}
931
932impl ClassExtends {
933 pub fn plain(parent: crate::MangledName) -> Self {
935 Self {
936 parent,
937 args: Vec::new(),
938 }
939 }
940}
941
942#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
943pub enum Dispatch {
944 Direct,
946 Static,
948 VTable,
950}
951
952#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
953pub struct MethodSig {
954 #[serde(default)]
956 pub optional: bool,
957 pub generics: Vec<String>,
958 pub params: Vec<Param>,
959 pub ret: Type,
960 pub predicate: Option<crate::TypePredicate>,
962 pub doc: Option<crate::DocComment>,
963}
964
965#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
969pub enum AccessorSig {
970 Getter { name: String, ret_ty: Type },
971 Setter { name: String, param: Param },
972}
973
974impl AccessorSig {
975 pub fn name(&self) -> &str {
976 match self {
977 AccessorSig::Getter { name, .. } | AccessorSig::Setter { name, .. } => name,
978 }
979 }
980}
981
982#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
990pub struct PropertySig {
991 pub ty: Type,
992 pub readonly: bool,
993 pub optional: bool,
994 #[serde(default)]
995 pub intrinsic: bool,
996 pub doc: Option<crate::DocComment>,
997}
998
999#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1003pub struct FieldSig {
1004 pub ty: Type,
1005 pub visibility: crate::Visibility,
1006 pub readonly: bool,
1007 pub optional: bool,
1008 pub doc: Option<crate::DocComment>,
1009}
1010
1011#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1016pub struct NamespaceSymbol {
1017 pub name: String,
1018 pub mangled_prefix: crate::MangledName,
1020 pub declaration_span: Span,
1022 pub values: BTreeMap<String, ValueSymbol>,
1023 pub types: BTreeMap<String, TypeSymbol>,
1025 pub namespaces: BTreeMap<String, NamespaceSymbol>,
1026 pub doc: Option<crate::DocComment>,
1027}
1028
1029impl Drop for NamespaceSymbol {
1030 fn drop(&mut self) {
1031 drop_namespace_children(&mut self.namespaces);
1032 }
1033}
1034
1035fn drop_namespace_children(namespaces: &mut BTreeMap<String, NamespaceSymbol>) {
1036 if namespaces.is_empty() {
1037 return;
1038 }
1039 let mut pending = Vec::new();
1040 if pending.try_reserve(1).is_err() {
1041 drop_namespace_children_without_allocation(namespaces);
1042 return;
1043 }
1044 pending.push(std::mem::take(namespaces).into_values());
1045 while let Some(siblings) = pending.last_mut() {
1046 let Some(mut child) = siblings.next() else {
1047 pending.pop();
1048 continue;
1049 };
1050 if child.namespaces.is_empty() {
1051 continue;
1052 }
1053 if pending.try_reserve(1).is_err() {
1054 drop_namespace_children_without_allocation(&mut child.namespaces);
1055 } else {
1056 pending.push(std::mem::take(&mut child.namespaces).into_values());
1057 }
1058 }
1060}
1061
1062fn drop_namespace_children_without_allocation(namespaces: &mut BTreeMap<String, NamespaceSymbol>) {
1063 while !namespaces.is_empty() {
1066 let mut branch = &mut *namespaces;
1067 loop {
1068 if branch
1069 .first_key_value()
1070 .is_some_and(|(_, child)| child.namespaces.is_empty())
1071 {
1072 branch.pop_first();
1073 break;
1074 }
1075 branch = &mut branch
1078 .first_entry()
1079 .expect("nonempty namespace branch")
1080 .into_mut()
1081 .namespaces;
1082 }
1083 }
1084}
1085
1086#[cfg(test)]
1087mod tests {
1088 use super::PackageDeclaration;
1089 use crate::{Asi, Token, TokenKind, Type, capture, check, desugar, infer, parse};
1090
1091 fn typed_ast(source: &str) -> crate::TypedAst {
1092 let mut asi = Asi::new(source, crate::FileId(0));
1093 let mut tokens: Vec<Token> = Vec::new();
1094 loop {
1095 let tok = asi.next_token();
1096 let is_eof = matches!(tok.kind, TokenKind::Eof);
1097 tokens.push(tok);
1098 if is_eof {
1099 break;
1100 }
1101 }
1102 let _ = asi.into_diagnostics();
1103 let (ast, _) = parse(source, tokens, crate::FileId(0));
1104 let (prelude_defs, host_defs, _) =
1105 crate::runtime::prelude::cached_runtime_package_declarations();
1106 let mut packages = Vec::with_capacity(prelude_defs.len() + host_defs.len());
1107 packages.extend(prelude_defs.iter());
1108 packages.extend(host_defs.iter());
1109 let (mut ta, _) = infer(source, "main", &ast, &packages);
1110 let _ = check(&ta);
1111 ta = capture(ta).unwrap();
1112 ta = desugar(ta, crate::FileId(0)).unwrap();
1113 ta
1114 }
1115
1116 #[test]
1117 fn collects_object_shape_from_let_annotation() {
1118 let ta = typed_ast("let p: { x: number; y: number } = { x: 1, y: 2 };");
1119 let defs = PackageDeclaration::from_typed_ast(&ta);
1120 let object = defs
1121 .shapes
1122 .iter()
1123 .find_map(|s| match s {
1124 crate::Shape::Object { fields, .. } => Some(fields),
1125 _ => None,
1126 })
1127 .expect("object shape in defs.shapes");
1128 assert_eq!(
1129 object.get("x"),
1130 Some(&crate::ObjectField::required(Type::Number)),
1131 );
1132 assert_eq!(
1133 object.get("y"),
1134 Some(&crate::ObjectField::required(Type::Number)),
1135 );
1136 }
1137
1138 #[test]
1139 fn preserves_readonly_and_writable_object_shapes() {
1140 let ta =
1141 typed_ast("let a: { readonly x: number } = { x: 1 }; let b: { x: number } = { x: 2 };");
1142 let defs = PackageDeclaration::from_typed_ast(&ta);
1143 let readonly_flags: std::collections::BTreeSet<bool> = defs
1144 .shapes
1145 .iter()
1146 .filter_map(|shape| {
1147 let crate::Shape::Object { fields, .. } = shape else {
1148 return None;
1149 };
1150 fields.get("x").map(|field| field.readonly)
1151 })
1152 .collect();
1153 assert_eq!(
1154 readonly_flags,
1155 std::collections::BTreeSet::from([false, true])
1156 );
1157 }
1158
1159 #[test]
1160 fn collects_array_shape_from_let_annotation() {
1161 let ta = typed_ast("let xs: number[] = [1, 2, 3];");
1162 let defs = PackageDeclaration::from_typed_ast(&ta);
1163 assert!(
1164 defs.shapes
1165 .iter()
1166 .any(|s| matches!(s, crate::Shape::Array(_))),
1167 "expected an Array shape in defs.shapes: {:?}",
1168 defs.shapes,
1169 );
1170 }
1171
1172 #[test]
1173 fn deduplicates_identical_object_shapes() {
1174 let ta = typed_ast("let a = { x: 1 }; let b = { x: 2 };");
1175 let defs = PackageDeclaration::from_typed_ast(&ta);
1176 let object_count = defs
1177 .shapes
1178 .iter()
1179 .filter(|s| matches!(s, crate::Shape::Object { .. }))
1180 .count();
1181 assert_eq!(
1182 object_count, 1,
1183 "expected one Object entry, got {object_count}"
1184 );
1185 }
1186
1187 #[test]
1188 fn distinguishes_object_shapes_with_different_fields() {
1189 let ta = typed_ast("let a = { x: 1 }; let b = { y: 2 };");
1190 let defs = PackageDeclaration::from_typed_ast(&ta);
1191 let object_count = defs
1192 .shapes
1193 .iter()
1194 .filter(|s| matches!(s, crate::Shape::Object { .. }))
1195 .count();
1196 assert_eq!(object_count, 2);
1197 }
1198
1199 #[test]
1200 fn canonical_shape_is_stable_across_modules() {
1201 let ta_a = typed_ast("let p = { x: 1, y: 2 };");
1202 let ta_b = typed_ast("let q = { y: 2, x: 1 };");
1203 let defs_a = PackageDeclaration::from_typed_ast(&ta_a);
1204 let defs_b = PackageDeclaration::from_typed_ast(&ta_b);
1205 let key_a = defs_a
1206 .shapes
1207 .iter()
1208 .find_map(|s| match s {
1209 crate::Shape::Object { .. } => Some(s.canonical_display()),
1210 _ => None,
1211 })
1212 .expect("object shape in defs_a");
1213 let key_b = defs_b
1214 .shapes
1215 .iter()
1216 .find_map(|s| match s {
1217 crate::Shape::Object { .. } => Some(s.canonical_display()),
1218 _ => None,
1219 })
1220 .expect("object shape in defs_b");
1221 assert_eq!(key_a, key_b, "canonical key should match across modules");
1222 }
1223
1224 #[test]
1225 fn nested_array_of_object_collects_both() {
1226 let ta = typed_ast("let xs: { x: number }[] = [];");
1227 let defs = PackageDeclaration::from_typed_ast(&ta);
1228 let has_array = defs
1229 .shapes
1230 .iter()
1231 .any(|s| matches!(s, crate::Shape::Array(_)));
1232 let has_object = defs
1233 .shapes
1234 .iter()
1235 .any(|s| matches!(s, crate::Shape::Object { .. }));
1236 assert!(has_array, "expected Array shape");
1237 assert!(has_object, "expected Object shape");
1238 }
1239}
1240
1241#[cfg(test)]
1242mod type_limit_checks {
1243 use std::collections::BTreeMap;
1244
1245 use super::*;
1246 use crate::compiler_limits::MAX_TYPE_DEPTH;
1247 use crate::type_size::TypeTooLarge;
1248
1249 fn nested(depth: u32) -> Type {
1250 (1..depth).fold(Type::Number, |inner, _| Type::Array(Box::new(inner)))
1251 }
1252
1253 fn namespace(name: &str) -> NamespaceSymbol {
1254 NamespaceSymbol {
1255 name: name.into(),
1256 mangled_prefix: crate::mangle::prelude(name),
1257 declaration_span: Span::at(crate::FileId(0)),
1258 values: BTreeMap::new(),
1259 types: BTreeMap::new(),
1260 namespaces: BTreeMap::new(),
1261 doc: None,
1262 }
1263 }
1264
1265 fn alias(ty: Type) -> TypeSymbol {
1266 TypeSymbol {
1267 name: "Deep".into(),
1268 mangled_name: crate::mangle::prelude("Deep"),
1269 declaration_span: Span::at(crate::FileId(0)),
1270 kind: TypeKind::Alias {
1271 generics: Vec::new(),
1272 ty,
1273 doc: None,
1274 },
1275 }
1276 }
1277
1278 #[test]
1279 fn namespace_cleanup_can_run_without_allocating_a_frontier() {
1280 let mut child = namespace("leaf");
1281 for _ in 0..1_000 {
1282 let mut parent = namespace("branch");
1283 parent.namespaces.insert("child".into(), child);
1284 parent
1285 .namespaces
1286 .insert("sibling".into(), namespace("sibling"));
1287 child = parent;
1288 }
1289 drop_namespace_children_without_allocation(&mut child.namespaces);
1290 assert!(child.namespaces.is_empty());
1291 }
1292
1293 #[test]
1294 fn a_type_in_a_nested_namespace_is_checked() {
1295 crate::type_size::tests::on_compiler_stack(|| {
1296 let mut inner = namespace("Inner");
1297 inner
1298 .types
1299 .insert("Deep".into(), alias(nested(MAX_TYPE_DEPTH + 1)));
1300 let mut outer = namespace("Outer");
1301 outer.namespaces.insert("Inner".into(), inner);
1302 let mut declaration = PackageDeclaration::with_package("dep");
1303 assert_eq!(declaration.check_type_limits(), Ok(()));
1304 declaration.namespaces.insert("Outer".into(), outer);
1305 assert_eq!(declaration.check_type_limits(), Err(TypeTooLarge::Depth));
1306 });
1307 }
1308
1309 #[test]
1310 fn runtime_declarations_are_checked() {
1311 crate::type_size::tests::on_compiler_stack(|| {
1312 let mut declaration = PackageDeclaration::with_package("dep");
1313 declaration.runtime_functions.insert(
1314 crate::mangle::prelude("f"),
1315 RuntimeFunction {
1316 params: vec![nested(MAX_TYPE_DEPTH)],
1317 ret: Type::Void,
1318 },
1319 );
1320 declaration
1321 .runtime_types
1322 .insert("Deep".into(), alias(nested(MAX_TYPE_DEPTH)));
1323 assert_eq!(declaration.check_type_limits(), Ok(()));
1324 declaration
1325 .runtime_globals
1326 .insert(crate::mangle::prelude("g"), nested(MAX_TYPE_DEPTH + 1));
1327 assert_eq!(declaration.check_type_limits(), Err(TypeTooLarge::Depth));
1328 });
1329 }
1330}
1331
1332#[cfg(test)]
1333mod non_finite_artifact_roundtrip {
1334 use super::{DefaultValue, EnumVariantValue, TypeKind};
1335 use crate::Type;
1336 use crate::types::LiteralF64;
1337
1338 #[test]
1341 fn non_finite_defaults_survive_json() {
1342 for value in [f64::INFINITY, f64::NEG_INFINITY, f64::NAN] {
1343 let json = serde_json::to_string(&DefaultValue::Number(value)).unwrap();
1344 assert!(
1345 !json.contains("null"),
1346 "non-finite default serialized to null: {json}"
1347 );
1348 let DefaultValue::Number(back) = serde_json::from_str(&json).unwrap() else {
1349 panic!("round-trip changed the variant: {json}");
1350 };
1351 assert_eq!(back.to_bits(), value.to_bits(), "round-trip lost {value}");
1352 }
1353 }
1354
1355 #[test]
1356 fn finite_defaults_still_serialize_as_numbers() {
1357 let json = serde_json::to_string(&DefaultValue::Number(1.5)).unwrap();
1358 assert_eq!(json, r#"{"Number":1.5}"#);
1359 let DefaultValue::Number(back) = serde_json::from_str(&json).unwrap() else {
1360 panic!("round-trip changed the variant");
1361 };
1362 assert_eq!(back, 1.5);
1363 }
1364
1365 #[test]
1366 fn non_finite_literal_types_survive_json() {
1367 let ty = Type::NumberLiteral(LiteralF64(f64::INFINITY));
1368 let json = serde_json::to_string(&ty).unwrap();
1369 assert_eq!(serde_json::from_str::<Type>(&json).unwrap(), ty);
1370 }
1371
1372 #[test]
1373 fn non_finite_enum_values_survive_json() {
1374 let value = EnumVariantValue::Number(f64::INFINITY);
1375 let json = serde_json::to_string(&value).unwrap();
1376 assert_eq!(
1377 serde_json::from_str::<EnumVariantValue>(&json).unwrap(),
1378 value
1379 );
1380 }
1381
1382 #[test]
1384 fn non_finite_enum_variants_survive_json() {
1385 let kind = TypeKind::NumberEnum {
1386 variants: vec![
1387 ("Huge".to_string(), f64::INFINITY),
1388 ("One".to_string(), 1.0),
1389 ],
1390 doc: None,
1391 };
1392 let json = serde_json::to_string(&kind).unwrap();
1393 assert!(
1394 json.contains(r#"["Huge","Infinity"]"#),
1395 "non-finite enum variant did not survive serialization: {json}"
1396 );
1397 let TypeKind::NumberEnum { variants, .. } = serde_json::from_str(&json).unwrap() else {
1398 panic!("round-trip changed the variant: {json}");
1399 };
1400 assert_eq!(variants[0].1.to_bits(), f64::INFINITY.to_bits());
1401 assert_eq!(variants[1].1, 1.0);
1402 }
1403}