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