use std::sync::Arc;
use indexmap::IndexMap;
use mir_types::{Location, Name, Type};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
mod interned_types {
use super::*;
use std::sync::OnceLock;
fn intern_string() -> Arc<Type> {
Arc::new(Type::string())
}
fn intern_int() -> Arc<Type> {
Arc::new(Type::int())
}
fn intern_float() -> Arc<Type> {
Arc::new(Type::float())
}
fn intern_bool() -> Arc<Type> {
Arc::new(Type::bool())
}
fn intern_mixed() -> Arc<Type> {
Arc::new(Type::mixed())
}
fn intern_null() -> Arc<Type> {
Arc::new(Type::null())
}
fn intern_void() -> Arc<Type> {
Arc::new(Type::void())
}
static STRING: OnceLock<Arc<Type>> = OnceLock::new();
static INT: OnceLock<Arc<Type>> = OnceLock::new();
static FLOAT: OnceLock<Arc<Type>> = OnceLock::new();
static BOOL: OnceLock<Arc<Type>> = OnceLock::new();
static MIXED: OnceLock<Arc<Type>> = OnceLock::new();
static NULL: OnceLock<Arc<Type>> = OnceLock::new();
static VOID: OnceLock<Arc<Type>> = OnceLock::new();
pub fn string() -> Arc<Type> {
STRING.get_or_init(intern_string).clone()
}
pub fn int() -> Arc<Type> {
INT.get_or_init(intern_int).clone()
}
pub fn float() -> Arc<Type> {
FLOAT.get_or_init(intern_float).clone()
}
pub fn bool() -> Arc<Type> {
BOOL.get_or_init(intern_bool).clone()
}
pub fn mixed() -> Arc<Type> {
MIXED.get_or_init(intern_mixed).clone()
}
pub fn null() -> Arc<Type> {
NULL.get_or_init(intern_null).clone()
}
pub fn void() -> Arc<Type> {
VOID.get_or_init(intern_void).clone()
}
static GLOBAL_UNION_INTERN: std::sync::OnceLock<dashmap::DashMap<Type, Arc<Type>>> =
std::sync::OnceLock::new();
fn global_intern_table() -> &'static dashmap::DashMap<Type, Arc<Type>> {
GLOBAL_UNION_INTERN.get_or_init(dashmap::DashMap::default)
}
pub fn intern_or_wrap(union: Type) -> Arc<Type> {
if union.types.len() == 1 && !union.possibly_undefined && !union.from_docblock {
match &union.types[0] {
mir_types::Atomic::TString => return string(),
mir_types::Atomic::TInt => return int(),
mir_types::Atomic::TFloat => return float(),
mir_types::Atomic::TBool => return bool(),
mir_types::Atomic::TMixed => return mixed(),
mir_types::Atomic::TNull => return null(),
mir_types::Atomic::TVoid => return void(),
_ => {}
}
}
if union.types.is_empty() {
return Arc::new(union);
}
let table = global_intern_table();
if let Some(existing) = table.get(&union) {
return Arc::clone(existing.value());
}
let arc = Arc::new(union.clone());
match table.entry(union) {
dashmap::mapref::entry::Entry::Occupied(o) => Arc::clone(o.get()),
dashmap::mapref::entry::Entry::Vacant(v) => {
v.insert(Arc::clone(&arc));
arc
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Visibility {
Public,
Protected,
Private,
}
impl Visibility {
pub fn is_at_least(&self, required: Visibility) -> bool {
*self <= required
}
}
impl std::fmt::Display for Visibility {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Visibility::Public => write!(f, "public"),
Visibility::Protected => write!(f, "protected"),
Visibility::Private => write!(f, "private"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TemplateParam {
pub name: Name,
pub bound: Option<Type>,
pub defining_entity: Name,
pub variance: mir_types::Variance,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FnParam {
pub name: Name,
#[serde(
deserialize_with = "deserialize_param_type",
serialize_with = "serialize_param_type"
)]
pub ty: Option<Arc<Type>>,
pub has_default: bool,
pub is_variadic: bool,
pub is_byref: bool,
pub is_optional: bool,
}
impl std::hash::Hash for FnParam {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.has_default.hash(state);
self.is_variadic.hash(state);
self.is_byref.hash(state);
self.is_optional.hash(state);
self.ty.as_deref().hash(state);
}
}
fn deserialize_param_type<'de, D>(deserializer: D) -> Result<Option<Arc<Type>>, D::Error>
where
D: serde::Deserializer<'de>,
{
Option::<Type>::deserialize(deserializer).map(|opt| opt.map(interned_types::intern_or_wrap))
}
fn serialize_param_type<S>(value: &Option<Arc<Type>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let opt = value.as_ref().map(|arc| (**arc).clone());
opt.serialize(serializer)
}
fn deserialize_return_type<'de, D>(deserializer: D) -> Result<Option<Arc<Type>>, D::Error>
where
D: serde::Deserializer<'de>,
{
Option::<Type>::deserialize(deserializer).map(|opt| opt.map(interned_types::intern_or_wrap))
}
fn serialize_return_type<S>(value: &Option<Arc<Type>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let opt = value.as_ref().map(|arc| (**arc).clone());
opt.serialize(serializer)
}
fn deserialize_params<'de, D>(deserializer: D) -> Result<Arc<[FnParam]>, D::Error>
where
D: serde::Deserializer<'de>,
{
Vec::<FnParam>::deserialize(deserializer).map(|v| Arc::from(v.into_boxed_slice()))
}
fn default_imports() -> Arc<FxHashMap<Name, Name>> {
Arc::new(FxHashMap::default())
}
fn deserialize_imports<'de, D>(deserializer: D) -> Result<Arc<FxHashMap<Name, Name>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let raw = FxHashMap::<String, String>::deserialize(deserializer)?;
let mut out: FxHashMap<Name, Name> =
FxHashMap::with_capacity_and_hasher(raw.len(), Default::default());
for (k, v) in raw {
out.insert(Name::new(&k), Name::new(&v));
}
Ok(Arc::new(out))
}
fn serialize_imports<S>(
value: &Arc<FxHashMap<Name, Name>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(value.len()))?;
for (k, v) in value.iter() {
map.serialize_entry(k.as_str(), v.as_str())?;
}
map.end()
}
fn serialize_params<S>(value: &Arc<[FnParam]>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
value.as_ref().serialize(serializer)
}
pub fn wrap_param_type(ty: Option<Type>) -> Option<Arc<Type>> {
ty.map(interned_types::intern_or_wrap)
}
pub fn wrap_return_type(ty: Option<Type>) -> Option<Arc<Type>> {
ty.map(interned_types::intern_or_wrap)
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AssertionKind {
Assert,
AssertIfTrue,
AssertIfFalse,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Assertion {
pub kind: AssertionKind,
pub param: Arc<str>,
pub ty: Type,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MethodDef {
pub name: Arc<str>,
pub fqcn: Arc<str>,
#[serde(
deserialize_with = "deserialize_params",
serialize_with = "serialize_params"
)]
pub params: Arc<[FnParam]>,
#[serde(
deserialize_with = "deserialize_return_type",
serialize_with = "serialize_return_type"
)]
pub return_type: Option<Arc<Type>>,
pub inferred_return_type: Option<Type>,
pub visibility: Visibility,
pub is_static: bool,
pub is_abstract: bool,
pub is_final: bool,
pub is_constructor: bool,
pub template_params: Vec<TemplateParam>,
pub assertions: Vec<Assertion>,
pub throws: Vec<Arc<str>>,
pub deprecated: Option<Arc<str>>,
pub is_internal: bool,
pub is_pure: bool,
pub location: Option<Location>,
#[serde(default)]
pub docstring: Option<Arc<str>>,
#[serde(default)]
pub is_virtual: bool,
}
impl MethodDef {
pub fn effective_return_type(&self) -> Option<&Type> {
self.return_type
.as_deref()
.or(self.inferred_return_type.as_ref())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PropertyDef {
pub name: Arc<str>,
pub ty: Option<Type>,
pub inferred_ty: Option<Type>,
pub visibility: Visibility,
pub is_static: bool,
pub is_readonly: bool,
pub default: Option<Type>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ConstantDef {
pub name: Arc<str>,
pub ty: Type,
pub visibility: Option<Visibility>,
#[serde(default)]
pub is_final: bool,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClassDef {
pub fqcn: Arc<str>,
pub short_name: Arc<str>,
pub parent: Option<Arc<str>>,
pub interfaces: Vec<Arc<str>>,
pub traits: Vec<Arc<str>>,
pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
pub own_properties: IndexMap<Arc<str>, PropertyDef>,
pub own_constants: IndexMap<Arc<str>, ConstantDef>,
#[serde(default)]
pub mixins: Vec<Arc<str>>,
pub template_params: Vec<TemplateParam>,
pub extends_type_args: Vec<Type>,
#[serde(default)]
pub implements_type_args: Vec<(Arc<str>, Vec<Type>)>,
pub is_abstract: bool,
pub is_final: bool,
pub is_readonly: bool,
pub deprecated: Option<Arc<str>>,
pub is_internal: bool,
pub location: Option<Location>,
#[serde(default)]
pub trait_use_locations: Vec<(Arc<str>, Location)>,
#[serde(default)]
pub type_aliases: FxHashMap<Arc<str>, Type>,
#[serde(default)]
pub pending_import_types: Vec<(Arc<str>, Arc<str>, Arc<str>)>,
}
impl ClassDef {
pub fn get_method(&self, name: &str) -> Option<&MethodDef> {
self.own_methods.get(name).map(Arc::as_ref).or_else(|| {
self.own_methods
.iter()
.find(|(k, _)| k.as_ref().eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_ref())
})
}
pub fn get_property(&self, name: &str) -> Option<&PropertyDef> {
self.own_properties.get(name)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InterfaceDef {
pub fqcn: Arc<str>,
pub short_name: Arc<str>,
pub extends: Vec<Arc<str>>,
pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
pub own_constants: IndexMap<Arc<str>, ConstantDef>,
pub template_params: Vec<TemplateParam>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TraitDef {
pub fqcn: Arc<str>,
pub short_name: Arc<str>,
pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
pub own_properties: IndexMap<Arc<str>, PropertyDef>,
pub own_constants: IndexMap<Arc<str>, ConstantDef>,
pub template_params: Vec<TemplateParam>,
pub traits: Vec<Arc<str>>,
pub location: Option<Location>,
#[serde(default)]
pub require_extends: Vec<Arc<str>>,
#[serde(default)]
pub require_implements: Vec<Arc<str>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EnumCaseDef {
pub name: Arc<str>,
pub value: Option<Type>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EnumDef {
pub fqcn: Arc<str>,
pub short_name: Arc<str>,
pub scalar_type: Option<Type>,
pub interfaces: Vec<Arc<str>>,
pub cases: IndexMap<Arc<str>, EnumCaseDef>,
pub own_methods: IndexMap<Arc<str>, Arc<MethodDef>>,
pub own_constants: IndexMap<Arc<str>, ConstantDef>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FunctionDef {
pub fqn: Arc<str>,
pub short_name: Arc<str>,
#[serde(
deserialize_with = "deserialize_params",
serialize_with = "serialize_params"
)]
pub params: Arc<[FnParam]>,
#[serde(
deserialize_with = "deserialize_return_type",
serialize_with = "serialize_return_type"
)]
pub return_type: Option<Arc<Type>>,
pub inferred_return_type: Option<Type>,
pub template_params: Vec<TemplateParam>,
pub assertions: Vec<Assertion>,
pub throws: Vec<Arc<str>>,
pub deprecated: Option<Arc<str>>,
pub is_pure: bool,
pub location: Option<Location>,
#[serde(default)]
pub docstring: Option<Arc<str>>,
}
impl FunctionDef {
pub fn effective_return_type(&self) -> Option<&Type> {
self.return_type
.as_deref()
.or(self.inferred_return_type.as_ref())
}
}
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct StubSlice {
pub classes: Vec<Arc<ClassDef>>,
pub interfaces: Vec<Arc<InterfaceDef>>,
pub traits: Vec<Arc<TraitDef>>,
pub enums: Vec<Arc<EnumDef>>,
pub functions: Vec<Arc<FunctionDef>>,
#[serde(default)]
pub constants: Vec<(Arc<str>, Type)>,
#[serde(default)]
pub file: Option<Arc<str>>,
#[serde(default)]
pub global_vars: Vec<(Arc<str>, Type)>,
#[serde(default)]
pub namespace: Option<Arc<str>>,
#[serde(
deserialize_with = "deserialize_imports",
serialize_with = "serialize_imports"
)]
#[serde(default = "default_imports")]
pub imports: Arc<FxHashMap<Name, Name>>,
#[serde(skip)]
pub is_deduped: bool,
}
use std::sync::Mutex;
type ParamCache = Mutex<FxHashMap<Vec<FnParam>, Arc<[FnParam]>>>;
static PARAM_DEDUP_CACHE: std::sync::OnceLock<ParamCache> = std::sync::OnceLock::new();
pub fn deduplicate_params_in_slice(slice: &mut StubSlice) {
let cache: &ParamCache = PARAM_DEDUP_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
let mut canonical_params = cache.lock().unwrap();
let mut deduplicate = |params: &mut Arc<[FnParam]>| {
if let Some(existing) = canonical_params.get(params.as_ref()) {
*params = existing.clone();
} else {
canonical_params.insert(params.as_ref().to_vec(), params.clone());
}
};
for cls in &mut slice.classes {
for method in Arc::make_mut(cls).own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for iface in &mut slice.interfaces {
for method in Arc::make_mut(iface).own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for tr in &mut slice.traits {
for method in Arc::make_mut(tr).own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for en in &mut slice.enums {
for method in Arc::make_mut(en).own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for func in &mut slice.functions {
deduplicate(&mut Arc::make_mut(func).params);
}
slice.is_deduped = true;
}