use std::sync::Arc;
use indexmap::IndexMap;
use mir_types::Union;
use serde::{Deserialize, Serialize};
mod interned_types {
use super::*;
use std::sync::OnceLock;
fn intern_string() -> Arc<Union> {
Arc::new(Union::string())
}
fn intern_int() -> Arc<Union> {
Arc::new(Union::int())
}
fn intern_float() -> Arc<Union> {
Arc::new(Union::float())
}
fn intern_bool() -> Arc<Union> {
Arc::new(Union::bool())
}
fn intern_mixed() -> Arc<Union> {
Arc::new(Union::mixed())
}
fn intern_null() -> Arc<Union> {
Arc::new(Union::null())
}
fn intern_void() -> Arc<Union> {
Arc::new(Union::void())
}
static STRING: OnceLock<Arc<Union>> = OnceLock::new();
static INT: OnceLock<Arc<Union>> = OnceLock::new();
static FLOAT: OnceLock<Arc<Union>> = OnceLock::new();
static BOOL: OnceLock<Arc<Union>> = OnceLock::new();
static MIXED: OnceLock<Arc<Union>> = OnceLock::new();
static NULL: OnceLock<Arc<Union>> = OnceLock::new();
static VOID: OnceLock<Arc<Union>> = OnceLock::new();
pub fn string() -> Arc<Union> {
STRING.get_or_init(intern_string).clone()
}
pub fn int() -> Arc<Union> {
INT.get_or_init(intern_int).clone()
}
pub fn float() -> Arc<Union> {
FLOAT.get_or_init(intern_float).clone()
}
pub fn bool() -> Arc<Union> {
BOOL.get_or_init(intern_bool).clone()
}
pub fn mixed() -> Arc<Union> {
MIXED.get_or_init(intern_mixed).clone()
}
pub fn null() -> Arc<Union> {
NULL.get_or_init(intern_null).clone()
}
pub fn void() -> Arc<Union> {
VOID.get_or_init(intern_void).clone()
}
pub fn intern_or_wrap(union: Union) -> Arc<Union> {
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(),
_ => {}
}
}
Arc::new(union)
}
}
#[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, Serialize, Deserialize)]
pub struct TemplateParam {
pub name: Arc<str>,
pub bound: Option<Union>,
pub defining_entity: Arc<str>,
pub variance: mir_types::Variance,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FnParam {
pub name: Arc<str>,
#[serde(
deserialize_with = "deserialize_param_type",
serialize_with = "serialize_param_type"
)]
pub ty: Option<Arc<Union>>,
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);
if let Some(ty) = &self.ty {
(Arc::as_ptr(ty) as usize).hash(state);
} else {
0u8.hash(state);
}
}
}
fn deserialize_param_type<'de, D>(deserializer: D) -> Result<Option<Arc<Union>>, D::Error>
where
D: serde::Deserializer<'de>,
{
Option::<Union>::deserialize(deserializer).map(|opt| opt.map(interned_types::intern_or_wrap))
}
fn serialize_param_type<S>(value: &Option<Arc<Union>>, 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<Union>>, D::Error>
where
D: serde::Deserializer<'de>,
{
Option::<Union>::deserialize(deserializer).map(|opt| opt.map(interned_types::intern_or_wrap))
}
fn serialize_return_type<S>(value: &Option<Arc<Union>>, 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 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<Union>) -> Option<Arc<Union>> {
ty.map(interned_types::intern_or_wrap)
}
pub fn wrap_return_type(ty: Option<Union>) -> Option<Arc<Union>> {
ty.map(interned_types::intern_or_wrap)
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Location {
pub file: Arc<str>,
pub line: u32,
pub line_end: u32,
pub col_start: u16,
pub col_end: u16,
}
impl Location {
pub fn new(file: Arc<str>, line: u32, line_end: u32, col_start: u16, col_end: u16) -> Self {
Self {
file,
line,
line_end,
col_start,
col_end,
}
}
}
#[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: Union,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MethodStorage {
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<Union>>,
pub inferred_return_type: Option<Union>,
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>,
}
impl MethodStorage {
pub fn effective_return_type(&self) -> Option<&Union> {
self.return_type
.as_deref()
.or(self.inferred_return_type.as_ref())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PropertyStorage {
pub name: Arc<str>,
pub ty: Option<Union>,
pub inferred_ty: Option<Union>,
pub visibility: Visibility,
pub is_static: bool,
pub is_readonly: bool,
pub default: Option<Union>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ConstantStorage {
pub name: Arc<str>,
pub ty: Union,
pub visibility: Option<Visibility>,
#[serde(default)]
pub is_final: bool,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClassStorage {
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<MethodStorage>>,
pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
#[serde(default)]
pub mixins: Vec<Arc<str>>,
pub template_params: Vec<TemplateParam>,
pub extends_type_args: Vec<Union>,
#[serde(default)]
pub implements_type_args: Vec<(Arc<str>, Vec<Union>)>,
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 type_aliases: std::collections::HashMap<Arc<str>, Union>,
#[serde(default)]
pub pending_import_types: Vec<(Arc<str>, Arc<str>, Arc<str>)>,
}
impl ClassStorage {
pub fn get_method(&self, name: &str) -> Option<&MethodStorage> {
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<&PropertyStorage> {
self.own_properties.get(name)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InterfaceStorage {
pub fqcn: Arc<str>,
pub short_name: Arc<str>,
pub extends: Vec<Arc<str>>,
pub own_methods: IndexMap<Arc<str>, Arc<MethodStorage>>,
pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
pub template_params: Vec<TemplateParam>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TraitStorage {
pub fqcn: Arc<str>,
pub short_name: Arc<str>,
pub own_methods: IndexMap<Arc<str>, Arc<MethodStorage>>,
pub own_properties: IndexMap<Arc<str>, PropertyStorage>,
pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
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 EnumCaseStorage {
pub name: Arc<str>,
pub value: Option<Union>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EnumStorage {
pub fqcn: Arc<str>,
pub short_name: Arc<str>,
pub scalar_type: Option<Union>,
pub interfaces: Vec<Arc<str>>,
pub cases: IndexMap<Arc<str>, EnumCaseStorage>,
pub own_methods: IndexMap<Arc<str>, Arc<MethodStorage>>,
pub own_constants: IndexMap<Arc<str>, ConstantStorage>,
pub location: Option<Location>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FunctionStorage {
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<Union>>,
pub inferred_return_type: Option<Union>,
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>,
}
impl FunctionStorage {
pub fn effective_return_type(&self) -> Option<&Union> {
self.return_type
.as_deref()
.or(self.inferred_return_type.as_ref())
}
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct StubSlice {
pub classes: Vec<ClassStorage>,
pub interfaces: Vec<InterfaceStorage>,
pub traits: Vec<TraitStorage>,
pub enums: Vec<EnumStorage>,
pub functions: Vec<FunctionStorage>,
#[serde(default)]
pub constants: Vec<(Arc<str>, Union)>,
#[serde(default)]
pub file: Option<Arc<str>>,
#[serde(default)]
pub global_vars: Vec<(Arc<str>, Union)>,
#[serde(default)]
pub namespace: Option<Arc<str>>,
#[serde(default)]
pub imports: std::collections::HashMap<String, String>,
}
use std::sync::Mutex;
static PARAM_DEDUP_CACHE: std::sync::OnceLock<Mutex<Vec<Arc<[FnParam]>>>> =
std::sync::OnceLock::new();
pub fn deduplicate_params_in_slice(slice: &mut StubSlice) {
let cache = PARAM_DEDUP_CACHE.get_or_init(|| Mutex::new(Vec::new()));
let mut canonical_params = cache.lock().expect("param dedup cache poisoned");
let mut deduplicate = |params: &mut Arc<[FnParam]>| {
for existing in canonical_params.iter() {
if existing.as_ref() == params.as_ref() {
*params = existing.clone();
return;
}
}
canonical_params.push(params.clone());
};
for cls in &mut slice.classes {
for method in cls.own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for iface in &mut slice.interfaces {
for method in iface.own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for tr in &mut slice.traits {
for method in tr.own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for en in &mut slice.enums {
for method in en.own_methods.values_mut() {
deduplicate(&mut Arc::make_mut(method).params);
}
}
for func in &mut slice.functions {
deduplicate(&mut func.params);
}
}