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