1use std::sync::Arc;
2
3use indexmap::IndexMap;
4use mir_types::{Location, Name, Type};
5use rustc_hash::FxHashMap;
6use serde::{Deserialize, Serialize};
7
8mod interned_types {
15 use super::*;
16 use std::sync::OnceLock;
17
18 fn intern_string() -> Arc<Type> {
19 Arc::new(Type::string())
20 }
21
22 fn intern_int() -> Arc<Type> {
23 Arc::new(Type::int())
24 }
25
26 fn intern_float() -> Arc<Type> {
27 Arc::new(Type::float())
28 }
29
30 fn intern_bool() -> Arc<Type> {
31 Arc::new(Type::bool())
32 }
33
34 fn intern_mixed() -> Arc<Type> {
35 Arc::new(Type::mixed())
36 }
37
38 fn intern_null() -> Arc<Type> {
39 Arc::new(Type::null())
40 }
41
42 fn intern_void() -> Arc<Type> {
43 Arc::new(Type::void())
44 }
45
46 static STRING: OnceLock<Arc<Type>> = OnceLock::new();
47 static INT: OnceLock<Arc<Type>> = OnceLock::new();
48 static FLOAT: OnceLock<Arc<Type>> = OnceLock::new();
49 static BOOL: OnceLock<Arc<Type>> = OnceLock::new();
50 static MIXED: OnceLock<Arc<Type>> = OnceLock::new();
51 static NULL: OnceLock<Arc<Type>> = OnceLock::new();
52 static VOID: OnceLock<Arc<Type>> = OnceLock::new();
53
54 pub fn string() -> Arc<Type> {
55 STRING.get_or_init(intern_string).clone()
56 }
57
58 pub fn int() -> Arc<Type> {
59 INT.get_or_init(intern_int).clone()
60 }
61
62 pub fn float() -> Arc<Type> {
63 FLOAT.get_or_init(intern_float).clone()
64 }
65
66 pub fn bool() -> Arc<Type> {
67 BOOL.get_or_init(intern_bool).clone()
68 }
69
70 pub fn mixed() -> Arc<Type> {
71 MIXED.get_or_init(intern_mixed).clone()
72 }
73
74 pub fn null() -> Arc<Type> {
75 NULL.get_or_init(intern_null).clone()
76 }
77
78 pub fn void() -> Arc<Type> {
79 VOID.get_or_init(intern_void).clone()
80 }
81
82 static GLOBAL_UNION_INTERN: std::sync::OnceLock<dashmap::DashMap<Type, Arc<Type>>> =
95 std::sync::OnceLock::new();
96
97 fn global_intern_table() -> &'static dashmap::DashMap<Type, Arc<Type>> {
98 GLOBAL_UNION_INTERN.get_or_init(dashmap::DashMap::default)
99 }
100
101 pub fn intern_or_wrap(union: Type) -> Arc<Type> {
103 if union.types.len() == 1 && !union.possibly_undefined && !union.from_docblock {
106 match &union.types[0] {
107 mir_types::Atomic::TString => return string(),
108 mir_types::Atomic::TInt => return int(),
109 mir_types::Atomic::TFloat => return float(),
110 mir_types::Atomic::TBool => return bool(),
111 mir_types::Atomic::TMixed => return mixed(),
112 mir_types::Atomic::TNull => return null(),
113 mir_types::Atomic::TVoid => return void(),
114 _ => {}
115 }
116 }
117 if union.types.is_empty() {
120 return Arc::new(union);
121 }
122 let table = global_intern_table();
124 if let Some(existing) = table.get(&union) {
125 return Arc::clone(existing.value());
126 }
127 let arc = Arc::new(union.clone());
128 match table.entry(union) {
132 dashmap::mapref::entry::Entry::Occupied(o) => Arc::clone(o.get()),
133 dashmap::mapref::entry::Entry::Vacant(v) => {
134 v.insert(Arc::clone(&arc));
135 arc
136 }
137 }
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
146pub enum Visibility {
147 Public,
148 Protected,
149 Private,
150}
151
152impl Visibility {
153 pub fn is_at_least(&self, required: Visibility) -> bool {
154 *self <= required
155 }
156}
157
158impl std::fmt::Display for Visibility {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 match self {
161 Visibility::Public => write!(f, "public"),
162 Visibility::Protected => write!(f, "protected"),
163 Visibility::Private => write!(f, "private"),
164 }
165 }
166}
167
168fn serialize_template_bound<S>(value: &Option<Arc<Type>>, serializer: S) -> Result<S::Ok, S::Error>
169where
170 S: serde::Serializer,
171{
172 value.as_deref().serialize(serializer)
173}
174
175fn deserialize_template_bound<'de, D>(deserializer: D) -> Result<Option<Arc<Type>>, D::Error>
176where
177 D: serde::Deserializer<'de>,
178{
179 Option::<Type>::deserialize(deserializer).map(|opt| opt.map(interned_types::intern_or_wrap))
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
183pub struct TemplateParam {
184 pub name: Name,
185 #[serde(
189 serialize_with = "serialize_template_bound",
190 deserialize_with = "deserialize_template_bound"
191 )]
192 pub bound: Option<Arc<Type>>,
193 pub defining_entity: Name,
195 pub variance: mir_types::Variance,
196}
197
198#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
199pub struct FnParam {
200 pub name: Name,
201 #[serde(
205 deserialize_with = "deserialize_param_type",
206 serialize_with = "serialize_param_type"
207 )]
208 pub ty: Option<Arc<Type>>,
209 pub has_default: bool,
212 pub is_variadic: bool,
213 pub is_byref: bool,
214 pub is_optional: bool,
215}
216
217impl std::hash::Hash for FnParam {
218 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
219 self.name.hash(state);
220 self.has_default.hash(state);
221 self.is_variadic.hash(state);
222 self.is_byref.hash(state);
223 self.is_optional.hash(state);
224 self.ty.as_deref().hash(state);
228 }
229}
230
231fn deserialize_param_type<'de, D>(deserializer: D) -> Result<Option<Arc<Type>>, D::Error>
233where
234 D: serde::Deserializer<'de>,
235{
236 Option::<Type>::deserialize(deserializer).map(|opt| opt.map(interned_types::intern_or_wrap))
237}
238
239fn serialize_param_type<S>(value: &Option<Arc<Type>>, serializer: S) -> Result<S::Ok, S::Error>
240where
241 S: serde::Serializer,
242{
243 let opt = value.as_ref().map(|arc| (**arc).clone());
244 opt.serialize(serializer)
245}
246
247fn deserialize_return_type<'de, D>(deserializer: D) -> Result<Option<Arc<Type>>, D::Error>
248where
249 D: serde::Deserializer<'de>,
250{
251 Option::<Type>::deserialize(deserializer).map(|opt| opt.map(interned_types::intern_or_wrap))
252}
253
254fn serialize_return_type<S>(value: &Option<Arc<Type>>, serializer: S) -> Result<S::Ok, S::Error>
255where
256 S: serde::Serializer,
257{
258 let opt = value.as_ref().map(|arc| (**arc).clone());
259 opt.serialize(serializer)
260}
261
262fn deserialize_params<'de, D>(deserializer: D) -> Result<Arc<[FnParam]>, D::Error>
263where
264 D: serde::Deserializer<'de>,
265{
266 Vec::<FnParam>::deserialize(deserializer).map(|v| Arc::from(v.into_boxed_slice()))
267}
268
269fn default_imports() -> Arc<FxHashMap<Name, Name>> {
270 Arc::new(FxHashMap::default())
271}
272
273fn deserialize_imports<'de, D>(deserializer: D) -> Result<Arc<FxHashMap<Name, Name>>, D::Error>
278where
279 D: serde::Deserializer<'de>,
280{
281 let raw = FxHashMap::<String, String>::deserialize(deserializer)?;
282 let mut out: FxHashMap<Name, Name> =
283 FxHashMap::with_capacity_and_hasher(raw.len(), Default::default());
284 for (k, v) in raw {
285 out.insert(Name::new(&k), Name::new(&v));
286 }
287 Ok(Arc::new(out))
288}
289
290fn serialize_imports<S>(
294 value: &Arc<FxHashMap<Name, Name>>,
295 serializer: S,
296) -> Result<S::Ok, S::Error>
297where
298 S: serde::Serializer,
299{
300 use serde::ser::SerializeMap;
301 let mut map = serializer.serialize_map(Some(value.len()))?;
302 for (k, v) in value.iter() {
303 map.serialize_entry(k.as_str(), v.as_str())?;
304 }
305 map.end()
306}
307
308fn serialize_params<S>(value: &Arc<[FnParam]>, serializer: S) -> Result<S::Ok, S::Error>
309where
310 S: serde::Serializer,
311{
312 value.as_ref().serialize(serializer)
313}
314
315pub fn wrap_param_type(ty: Option<Type>) -> Option<Arc<Type>> {
317 ty.map(interned_types::intern_or_wrap)
318}
319
320pub fn wrap_return_type(ty: Option<Type>) -> Option<Arc<Type>> {
322 ty.map(interned_types::intern_or_wrap)
323}
324
325pub fn wrap_property_type(ty: Option<Type>) -> Option<Arc<Type>> {
329 ty.map(interned_types::intern_or_wrap)
330}
331
332pub fn wrap_template_bound(ty: Option<Type>) -> Option<Arc<Type>> {
334 ty.map(interned_types::intern_or_wrap)
335}
336
337pub fn wrap_var_type(ty: Type) -> Arc<Type> {
342 interned_types::intern_or_wrap(ty)
343}
344
345#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
350pub enum AssertionKind {
351 Assert,
352 AssertIfTrue,
353 AssertIfFalse,
354}
355
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
357pub struct Assertion {
358 pub kind: AssertionKind,
359 pub param: Arc<str>,
360 pub ty: Type,
361}
362
363#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
368pub struct MethodDef {
369 pub name: Arc<str>,
370 pub fqcn: Arc<str>,
371 #[serde(
372 deserialize_with = "deserialize_params",
373 serialize_with = "serialize_params"
374 )]
375 pub params: Arc<[FnParam]>,
376 #[serde(
380 deserialize_with = "deserialize_return_type",
381 serialize_with = "serialize_return_type"
382 )]
383 pub return_type: Option<Arc<Type>>,
384 #[serde(
389 deserialize_with = "deserialize_return_type",
390 serialize_with = "serialize_return_type"
391 )]
392 pub inferred_return_type: Option<Arc<Type>>,
393 pub visibility: Visibility,
394 pub is_static: bool,
395 pub is_abstract: bool,
396 pub is_final: bool,
397 pub is_constructor: bool,
398 pub template_params: Vec<TemplateParam>,
399 pub assertions: Vec<Assertion>,
400 pub throws: Vec<Arc<str>>,
401 pub deprecated: Option<Arc<str>>,
402 pub is_internal: bool,
403 pub is_pure: bool,
404 #[serde(default)]
406 pub no_named_arguments: bool,
407 #[serde(default)]
409 pub is_override: bool,
410 pub location: Option<Location>,
411 #[serde(default)]
414 pub docstring: Option<Arc<str>>,
415 #[serde(default)]
418 pub is_virtual: bool,
419 #[serde(default)]
422 pub taint_sink_params: Vec<(Arc<str>, Arc<str>)>,
423 #[serde(default)]
426 pub if_this_is: Option<Arc<Type>>,
427}
428
429impl MethodDef {
430 pub fn effective_return_type(&self) -> Option<&Type> {
431 self.return_type
432 .as_deref()
433 .or(self.inferred_return_type.as_deref())
434 }
435}
436
437#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
442pub struct PropertyDef {
443 pub name: Arc<str>,
444 #[serde(
451 deserialize_with = "deserialize_param_type",
452 serialize_with = "serialize_param_type"
453 )]
454 pub ty: Option<Arc<Type>>,
455 #[serde(
456 deserialize_with = "deserialize_param_type",
457 serialize_with = "serialize_param_type"
458 )]
459 pub inferred_ty: Option<Arc<Type>>,
460 pub visibility: Visibility,
461 pub is_static: bool,
462 pub is_readonly: bool,
463 #[serde(
464 deserialize_with = "deserialize_param_type",
465 serialize_with = "serialize_param_type"
466 )]
467 pub default: Option<Arc<Type>>,
468 pub location: Option<Location>,
469 #[serde(default)]
471 pub deprecated: Option<Arc<str>>,
472 #[serde(default)]
478 pub has_native_type: bool,
479}
480
481#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
486pub struct ConstantDef {
487 pub name: Arc<str>,
488 pub ty: Type,
489 pub visibility: Option<Visibility>,
490 #[serde(default)]
491 pub is_final: bool,
492 pub location: Option<Location>,
493 #[serde(default)]
495 pub deprecated: Option<Arc<str>>,
496}
497
498#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
503pub struct ClassDef {
504 pub fqcn: Arc<str>,
505 pub short_name: Arc<str>,
506 pub parent: Option<Arc<str>>,
507 pub interfaces: Vec<Arc<str>>,
508 pub traits: Vec<Arc<str>>,
509 pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
510 pub own_properties: IndexMap<Arc<str>, PropertyDef>,
511 pub own_constants: IndexMap<Arc<str>, ConstantDef>,
512 #[serde(default)]
513 pub mixins: Vec<Arc<str>>,
514 pub template_params: Vec<TemplateParam>,
515 pub extends_type_args: Vec<Type>,
517 #[serde(default)]
519 pub implements_type_args: Vec<(Arc<str>, Vec<Type>)>,
520 pub is_abstract: bool,
521 pub is_final: bool,
522 pub is_readonly: bool,
523 pub deprecated: Option<Arc<str>>,
524 pub is_internal: bool,
525 #[serde(default)]
529 pub attribute_flags: Option<i64>,
530 pub location: Option<Location>,
531 #[serde(default)]
535 pub trait_use_locations: Vec<(Arc<str>, Location)>,
536 #[serde(default)]
538 pub type_aliases: FxHashMap<Arc<str>, Type>,
539 #[serde(default)]
541 pub pending_import_types: Vec<(Arc<str>, Arc<str>, Arc<str>)>,
542 #[serde(default)]
546 pub trait_insteadof: IndexMap<Arc<str>, Vec<Arc<str>>>,
547 #[serde(default)]
551 #[allow(clippy::type_complexity)]
552 pub trait_aliases: FxHashMap<Arc<str>, (Option<Arc<str>>, Arc<str>)>,
553}
554
555impl ClassDef {
556 pub fn get_method(&self, name: &str) -> Option<&MethodDef> {
557 self.own_methods.get(name).map(Arc::as_ref).or_else(|| {
561 self.own_methods
562 .iter()
563 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(name))
564 .map(|(_, v)| v.as_ref())
565 })
566 }
567
568 pub fn get_property(&self, name: &str) -> Option<&PropertyDef> {
569 self.own_properties.get(name)
570 }
571}
572
573#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
578pub struct InterfaceDef {
579 pub fqcn: Arc<str>,
580 pub short_name: Arc<str>,
581 pub extends: Vec<Arc<str>>,
582 pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
583 pub own_constants: IndexMap<Arc<str>, ConstantDef>,
584 pub template_params: Vec<TemplateParam>,
585 pub location: Option<Location>,
586 #[serde(default)]
588 pub deprecated: Option<Arc<str>>,
589 #[serde(default)]
591 pub own_properties: IndexMap<Arc<str>, PropertyDef>,
592 #[serde(default)]
594 pub seal_properties: bool,
595}
596
597#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
602pub struct TraitDef {
603 pub fqcn: Arc<str>,
604 pub short_name: Arc<str>,
605 pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
606 pub own_properties: IndexMap<Arc<str>, PropertyDef>,
607 pub own_constants: IndexMap<Arc<str>, ConstantDef>,
608 pub template_params: Vec<TemplateParam>,
609 pub traits: Vec<Arc<str>>,
611 pub location: Option<Location>,
612 #[serde(default)]
614 pub require_extends: Vec<Arc<str>>,
615 #[serde(default)]
617 pub require_implements: Vec<Arc<str>>,
618 #[serde(default)]
620 pub deprecated: Option<Arc<str>>,
621}
622
623#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
628pub struct EnumCaseDef {
629 pub name: Arc<str>,
630 pub value: Option<Type>,
631 pub location: Option<Location>,
632 #[serde(default)]
634 pub deprecated: Option<Arc<str>>,
635}
636
637#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
638pub struct EnumDef {
639 pub fqcn: Arc<str>,
640 pub short_name: Arc<str>,
641 pub scalar_type: Option<Type>,
642 pub interfaces: Vec<Arc<str>>,
643 pub cases: IndexMap<Arc<str>, EnumCaseDef>,
644 pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
645 pub own_constants: IndexMap<Arc<str>, ConstantDef>,
646 pub location: Option<Location>,
647}
648
649#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
654pub struct FunctionDef {
655 pub fqn: Arc<str>,
656 pub short_name: Arc<str>,
657 #[serde(
658 deserialize_with = "deserialize_params",
659 serialize_with = "serialize_params"
660 )]
661 pub params: Arc<[FnParam]>,
662 #[serde(
665 deserialize_with = "deserialize_return_type",
666 serialize_with = "serialize_return_type"
667 )]
668 pub return_type: Option<Arc<Type>>,
669 #[serde(
672 deserialize_with = "deserialize_return_type",
673 serialize_with = "serialize_return_type"
674 )]
675 pub inferred_return_type: Option<Arc<Type>>,
676 pub template_params: Vec<TemplateParam>,
677 pub assertions: Vec<Assertion>,
678 pub throws: Vec<Arc<str>>,
679 pub deprecated: Option<Arc<str>>,
680 pub is_pure: bool,
681 #[serde(default)]
683 pub no_named_arguments: bool,
684 pub location: Option<Location>,
685 #[serde(default)]
688 pub docstring: Option<Arc<str>>,
689 #[serde(default)]
692 pub taint_sink_params: Vec<(Arc<str>, Arc<str>)>,
693}
694
695impl FunctionDef {
696 pub fn effective_return_type(&self) -> Option<&Type> {
697 self.return_type
698 .as_deref()
699 .or(self.inferred_return_type.as_deref())
700 }
701}
702
703#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
713pub struct StubSlice {
714 pub classes: Vec<Arc<ClassDef>>,
715 pub interfaces: Vec<Arc<InterfaceDef>>,
716 pub traits: Vec<Arc<TraitDef>>,
717 pub enums: Vec<Arc<EnumDef>>,
718 pub functions: Vec<Arc<FunctionDef>>,
719 #[serde(default)]
720 pub constants: Vec<(Arc<str>, Type)>,
721 #[serde(default)]
724 pub file: Option<Arc<str>>,
725 #[serde(default)]
729 pub global_vars: Vec<(Arc<str>, Type)>,
730 #[serde(default)]
734 pub namespace: Option<Arc<str>>,
735 #[serde(
745 deserialize_with = "deserialize_imports",
746 serialize_with = "serialize_imports"
747 )]
748 #[serde(default = "default_imports")]
749 pub imports: Arc<FxHashMap<Name, Name>>,
750 #[serde(skip)]
753 pub is_deduped: bool,
754}
755
756use std::sync::Mutex;
761
762type ParamCache = Mutex<FxHashMap<Vec<FnParam>, Arc<[FnParam]>>>;
763
764static PARAM_DEDUP_CACHE: std::sync::OnceLock<ParamCache> = std::sync::OnceLock::new();
768
769pub fn deduplicate_params_in_slice(slice: &mut StubSlice) {
777 let cache: &ParamCache = PARAM_DEDUP_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
778 let mut canonical_params = cache.lock().unwrap();
779
780 let mut deduplicate = |params: &mut Arc<[FnParam]>| {
781 if let Some(existing) = canonical_params.get(params.as_ref()) {
782 *params = existing.clone();
783 } else {
784 canonical_params.insert(params.as_ref().to_vec(), params.clone());
785 }
786 };
787
788 for cls in &mut slice.classes {
790 for method in Arc::make_mut(cls).own_methods.values_mut() {
791 deduplicate(&mut Arc::make_mut(method).params);
792 }
793 }
794
795 for iface in &mut slice.interfaces {
797 for method in Arc::make_mut(iface).own_methods.values_mut() {
798 deduplicate(&mut Arc::make_mut(method).params);
799 }
800 }
801
802 for tr in &mut slice.traits {
804 for method in Arc::make_mut(tr).own_methods.values_mut() {
805 deduplicate(&mut Arc::make_mut(method).params);
806 }
807 }
808
809 for en in &mut slice.enums {
811 for method in Arc::make_mut(en).own_methods.values_mut() {
812 deduplicate(&mut Arc::make_mut(method).params);
813 }
814 }
815
816 for func in &mut slice.functions {
818 deduplicate(&mut Arc::make_mut(func).params);
819 }
820 slice.is_deduped = true;
821}