use super::GaiaType;
use std::collections::HashMap;
#[derive(Debug, Default)]
pub struct TypeMappingContext {
type_cache: HashMap<String, GaiaType>,
generic_bounds: HashMap<String, Vec<GaiaType>>,
type_erasure_map: HashMap<String, String>,
}
impl TypeMappingContext {
pub fn new() -> Self {
Self::default()
}
pub fn add_generic_bound(&mut self, type_param: &str, bound: GaiaType) {
self.generic_bounds.entry(type_param.to_string())
.or_default()
.push(bound);
}
pub fn add_type_erasure(&mut self, generic_type: &str, erased_type: &str) {
self.type_erasure_map.insert(generic_type.to_string(), erased_type.to_string());
}
pub fn get_erased_type(&self, generic_type: &str) -> Option<&String> {
self.type_erasure_map.get(generic_type)
}
}
pub fn parse_type_name(type_name: &str, ctx: &mut TypeMappingContext) -> GaiaType {
if let Some(gaia_type) = ctx.type_cache.get(type_name) {
return gaia_type.clone();
}
let gaia_type = match type_name {
"bool" => GaiaType::Bool,
"byte" | "i8" => GaiaType::I8,
"short" | "i16" => GaiaType::I16,
"int" | "i32" => GaiaType::I32,
"long" | "i64" => GaiaType::I64,
"float" | "f32" => GaiaType::F32,
"double" | "f64" => GaiaType::F64,
"char" => GaiaType::U16,
"void" => GaiaType::Void,
"String" => GaiaType::String,
"Object" => GaiaType::Object,
s if s.starts_with('[') => {
let element_type = &s[1..];
let gaia_element_type = parse_type_name(element_type, ctx);
GaiaType::Array(Box::new(gaia_element_type), 0)
}
s if s.contains('<') && s.contains('>') => {
let (base_type, _type_params) = parse_generic_type(s);
if let Some(erased_type) = ctx.get_erased_type(base_type) {
GaiaType::Class(erased_type.clone())
} else {
GaiaType::Class(base_type.to_string())
}
}
s => GaiaType::Class(s.to_string()),
};
ctx.type_cache.insert(type_name.to_string(), gaia_type.clone());
gaia_type
}
pub fn map_uir_type_to_gaia_type(uir_type: &str, ctx: &mut TypeMappingContext) -> GaiaType {
parse_type_name(uir_type, ctx)
}
fn parse_generic_type(generic_type: &str) -> (&str, Vec<&str>) {
if let Some(idx) = generic_type.find('<') {
let base_type = &generic_type[..idx];
let params_str = &generic_type[idx+1..generic_type.len()-1];
let type_params = params_str.split(',').map(|p| p.trim()).collect();
(base_type, type_params)
} else {
(generic_type, Vec::new())
}
}
pub fn map_gaia_type_to_jvm_descriptor(ty: &GaiaType) -> String {
match ty {
GaiaType::Void => "V".to_string(),
GaiaType::Bool => "Z".to_string(),
GaiaType::I8 => "B".to_string(),
GaiaType::U8 => "B".to_string(),
GaiaType::I16 => "S".to_string(),
GaiaType::U16 => "S".to_string(),
GaiaType::I32 => "I".to_string(),
GaiaType::U32 => "I".to_string(),
GaiaType::I64 => "J".to_string(),
GaiaType::U64 => "J".to_string(),
GaiaType::F32 => "F".to_string(),
GaiaType::F64 => "D".to_string(),
GaiaType::String => "Ljava/lang/String;".to_string(),
GaiaType::Class(name) => format!("L{};", name.replace('.', "/")),
GaiaType::Array(inner, _) => format!("[{}", map_gaia_type_to_jvm_descriptor(inner)),
GaiaType::Object => "Ljava/lang/Object;".to_string(),
GaiaType::Interface(name) => format!("L{};", name.replace('.', "/")),
_ => "Ljava/lang/Object;".to_string(),
}
}
pub fn are_types_compatible(from: &GaiaType, to: &GaiaType) -> bool {
match (from, to) {
(f, t) if f == t => true,
(GaiaType::I8, GaiaType::I16) => true,
(GaiaType::I8, GaiaType::I32) => true,
(GaiaType::I8, GaiaType::I64) => true,
(GaiaType::I8, GaiaType::F32) => true,
(GaiaType::I8, GaiaType::F64) => true,
(GaiaType::U8, GaiaType::I16) => true,
(GaiaType::U8, GaiaType::I32) => true,
(GaiaType::U8, GaiaType::I64) => true,
(GaiaType::U8, GaiaType::F32) => true,
(GaiaType::U8, GaiaType::F64) => true,
(GaiaType::I16, GaiaType::I32) => true,
(GaiaType::I16, GaiaType::I64) => true,
(GaiaType::I16, GaiaType::F32) => true,
(GaiaType::I16, GaiaType::F64) => true,
(GaiaType::U16, GaiaType::I32) => true,
(GaiaType::U16, GaiaType::I64) => true,
(GaiaType::U16, GaiaType::F32) => true,
(GaiaType::U16, GaiaType::F64) => true,
(GaiaType::I32, GaiaType::I64) => true,
(GaiaType::I32, GaiaType::F32) => true,
(GaiaType::I32, GaiaType::F64) => true,
(GaiaType::U32, GaiaType::I64) => true,
(GaiaType::U32, GaiaType::F32) => true,
(GaiaType::U32, GaiaType::F64) => true,
(GaiaType::I64, GaiaType::F64) => true,
(GaiaType::U64, GaiaType::F64) => true,
(GaiaType::F32, GaiaType::F64) => true,
(GaiaType::String, GaiaType::Object) => true,
(GaiaType::Class(_), GaiaType::Object) => true,
(GaiaType::Interface(_), GaiaType::Object) => true,
(GaiaType::Array(_, _), GaiaType::Object) => true,
(GaiaType::Bool, GaiaType::Object) => true,
(GaiaType::I8, GaiaType::Object) => true,
(GaiaType::U8, GaiaType::Object) => true,
(GaiaType::I16, GaiaType::Object) => true,
(GaiaType::U16, GaiaType::Object) => true,
(GaiaType::I32, GaiaType::Object) => true,
(GaiaType::U32, GaiaType::Object) => true,
(GaiaType::I64, GaiaType::Object) => true,
(GaiaType::U64, GaiaType::Object) => true,
(GaiaType::F32, GaiaType::Object) => true,
(GaiaType::F64, GaiaType::Object) => true,
(GaiaType::Array(f_inner, _), GaiaType::Array(t_inner, _)) => {
are_types_compatible(f_inner, t_inner)
},
(GaiaType::Any, GaiaType::Object) => true,
_ => false,
}
}
pub fn get_common_super_type(a: &GaiaType, b: &GaiaType) -> Option<GaiaType> {
if are_types_compatible(a, b) {
return Some(b.clone());
}
if are_types_compatible(b, a) {
return Some(a.clone());
}
match (a, b) {
(GaiaType::String, _) | (_, GaiaType::String) => Some(GaiaType::Object),
(GaiaType::Class(_), _) | (_, GaiaType::Class(_)) => Some(GaiaType::Object),
(GaiaType::Interface(_), _) | (_, GaiaType::Interface(_)) => Some(GaiaType::Object),
(GaiaType::Array(_, _), _) | (_, GaiaType::Array(_, _)) => Some(GaiaType::Object),
_ => None,
}
}