use alloc::sync::Arc;
use core::{fmt, hash::Hash};
use rabbitizer::access_type::AccessType;
use crate::{
addresses::{Rom, Size, Vram},
collections::{addended_ordered_map::SizedValue, unordered_map::UnorderedMap},
config::Compiler,
section_type::SectionType,
};
use super::{
ParentSectionMetadata, ReferrerInfo, Referrers, SymbolMetadataNameDisplay,
SymbolNameGenerationSettings, SymbolType,
};
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum GeneratedBy {
Autogenerated,
UserDeclared,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct StringInfo {
is_maybe_string: bool,
failed_string_decoding: bool,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum GotAccessKind {
Local,
Global,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GotInfo {
access_kind: GotAccessKind,
got_index: Option<usize>, }
impl GotInfo {
pub fn access_kind(&self) -> GotAccessKind {
self.access_kind
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum RodataMigrationBehavior {
Default,
ForceMigrate,
ForceNotMigrate,
MigrateToSpecificFunction(Arc<str>),
}
impl Default for RodataMigrationBehavior {
fn default() -> Self {
Self::Default
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum OwnerSegmentKind {
Global,
Overlay(Arc<str>),
Unknown,
User,
}
impl OwnerSegmentKind {
pub fn is_unknown_segment(&self) -> bool {
matches!(self, OwnerSegmentKind::Unknown)
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub struct SymbolMetadata {
generated_by: GeneratedBy,
vram: Vram,
owner_segment_kind: OwnerSegmentKind,
symbol_name_generation_settings: SymbolNameGenerationSettings,
rom: Option<Rom>,
user_declared_name: Option<Arc<str>>,
user_declared_name_end: Option<Arc<str>>,
user_declared_size: Option<Size>,
autodetected_size: Option<Size>,
user_declared_type: Option<SymbolType>,
autodetected_type: Option<SymbolType>,
section_type: Option<SectionType>,
is_defined: bool,
access_types: UnorderedMap<AccessType, u32>,
c_string_info: Option<StringInfo>,
sym_referrers: Referrers,
got_info: Option<GotInfo>,
accessed_as_gp_rel: bool,
auto_created_pad_by: Option<Vram>,
rodata_migration_behavior: RodataMigrationBehavior,
allow_ref_with_addend: bool,
is_mips1_double: bool,
visibility: Option<Arc<str>>,
compiler: Option<Compiler>,
parent_metadata: Option<ParentSectionMetadata>,
trailing_padding_size: Option<Size>,
add_gp_to_pointed_data: bool,
}
impl SymbolMetadata {
pub(crate) fn new(
generated_by: GeneratedBy,
vram: Vram,
owner_segment_kind: OwnerSegmentKind,
symbol_name_generation_settings: SymbolNameGenerationSettings,
) -> Self {
Self {
generated_by,
vram,
owner_segment_kind,
symbol_name_generation_settings,
rom: None,
user_declared_name: None,
user_declared_name_end: None,
user_declared_size: None,
autodetected_size: None,
user_declared_type: None,
autodetected_type: None,
section_type: None,
is_defined: false,
access_types: UnorderedMap::new(),
c_string_info: None,
sym_referrers: Referrers::new(),
got_info: None,
accessed_as_gp_rel: false,
auto_created_pad_by: None,
rodata_migration_behavior: RodataMigrationBehavior::Default,
allow_ref_with_addend: true,
is_mips1_double: false,
visibility: None,
compiler: None,
parent_metadata: None,
trailing_padding_size: None,
add_gp_to_pointed_data: false,
}
}
pub fn generated_by(&self) -> GeneratedBy {
self.generated_by
}
pub const fn vram(&self) -> Vram {
self.vram
}
pub fn symbol_name_generation_settings(&self) -> &SymbolNameGenerationSettings {
&self.symbol_name_generation_settings
}
pub(crate) fn symbol_name_generation_settings_mut(
&mut self,
) -> &mut SymbolNameGenerationSettings {
&mut self.symbol_name_generation_settings
}
pub fn rom(&self) -> Option<Rom> {
self.rom
}
pub(crate) fn rom_mut(&mut self) -> &mut Option<Rom> {
&mut self.rom
}
pub fn display_name(&self) -> SymbolMetadataNameDisplay<'_> {
SymbolMetadataNameDisplay::new(self)
}
pub(crate) fn user_declared_name(&self) -> Option<Arc<str>> {
self.user_declared_name.clone()
}
pub(crate) fn set_user_declared_name(&mut self, name: Arc<str>) {
self.user_declared_name = Some(name);
}
pub(crate) fn set_user_declared_name_end(&mut self, name: Arc<str>) {
self.user_declared_name_end = Some(name);
}
pub fn user_declared_size(&self) -> Option<Size> {
self.user_declared_size
}
pub(crate) fn user_declared_size_mut(&mut self) -> &mut Option<Size> {
&mut self.user_declared_size
}
pub fn autodetected_size(&self) -> Option<Size> {
self.autodetected_size
}
pub(crate) fn autodetected_size_mut(&mut self) -> &mut Option<Size> {
&mut self.autodetected_size
}
pub fn size(&self) -> Option<Size> {
if let Some(siz) = self.user_declared_size {
return Some(siz);
}
if let Some(siz) = self.autodetected_size {
return Some(siz);
}
None
}
pub fn sym_type(&self) -> Option<SymbolType> {
if let Some(t) = self.user_declared_type {
Some(t)
} else {
self.autodetected_type()
}
}
pub fn user_declared_type(&self) -> Option<SymbolType> {
self.user_declared_type
}
pub fn autodetected_type(&self) -> Option<SymbolType> {
if let Some(t) = self.autodetected_type {
Some(t)
} else if self.access_types.len() == 1 {
SymbolType::from_access_type(
*self
.access_types
.iter()
.next()
.expect("Should not panic since we already checked its length")
.0,
)
} else {
None
}
}
pub(crate) fn set_type(&mut self, new_type: SymbolType, generated_by: GeneratedBy) {
match generated_by {
GeneratedBy::Autogenerated => self.autodetected_type = Some(new_type),
GeneratedBy::UserDeclared => self.user_declared_type = Some(new_type),
}
}
pub fn section_type(&self) -> Option<SectionType> {
self.section_type
}
pub(crate) fn section_type_mut(&mut self) -> &mut Option<SectionType> {
&mut self.section_type
}
pub fn is_defined(&self) -> bool {
self.is_defined
}
pub(crate) fn set_defined(&mut self) {
self.is_defined = true;
}
pub fn all_access_types(&self) -> &UnorderedMap<AccessType, u32> {
&self.access_types
}
pub(crate) fn set_access_type(&mut self, access_type: (AccessType, bool)) {
*self.access_types.entry(access_type.0).or_default() += 1;
}
pub fn reference_counter(&self) -> usize {
self.sym_referrers.reference_counter()
}
pub fn sym_referrers(&self) -> &Referrers {
&self.sym_referrers
}
pub(crate) fn add_referenced_info(&mut self, referrer: ReferrerInfo) {
self.sym_referrers.add(referrer);
}
pub fn owner_segment_kind(&self) -> &OwnerSegmentKind {
&self.owner_segment_kind
}
pub fn got_info(&self) -> Option<&GotInfo> {
self.got_info.as_ref()
}
pub(crate) fn set_got_access_kind(&mut self, kind: GotAccessKind) {
if let Some(got_info) = &mut self.got_info {
got_info.access_kind = kind;
} else {
self.got_info = Some(GotInfo {
access_kind: kind,
got_index: None,
});
}
}
pub(crate) fn autogenerated_pad_info(&self) -> Option<AutogeneratedPadInfo> {
if self.reference_counter() == 0 && self.generated_by == GeneratedBy::Autogenerated {
match self.auto_created_pad_by {
None => {
if self.owner_segment_kind == OwnerSegmentKind::Global {
Some(AutogeneratedPadInfo::Unreferenced)
} else {
None
}
}
Some(vram) => Some(AutogeneratedPadInfo::CreatedBy(vram)),
}
} else {
None
}
}
pub(crate) fn set_auto_created_pad_by(&mut self, vram: Vram) {
self.auto_created_pad_by = Some(vram);
}
#[must_use]
pub fn rodata_migration_behavior(&self) -> &RodataMigrationBehavior {
&self.rodata_migration_behavior
}
#[must_use]
pub(crate) fn rodata_migration_behavior_mut(&mut self) -> &mut RodataMigrationBehavior {
&mut self.rodata_migration_behavior
}
pub fn allow_ref_with_addend(&self) -> bool {
self.allow_ref_with_addend
}
pub(crate) fn set_allow_ref_with_addend(&mut self, val: bool) {
self.allow_ref_with_addend = val;
}
pub fn visibility(&self) -> Option<Arc<str>> {
self.visibility.clone()
}
pub(crate) fn set_visibility(&mut self, visibility: Arc<str>) {
self.visibility = Some(visibility)
}
pub(crate) fn compiler(&self) -> Option<Compiler> {
self.compiler
}
pub(crate) fn set_compiler(&mut self, compiler: Compiler) {
self.compiler = Some(compiler);
}
pub(crate) fn parent_metadata(&self) -> Option<&ParentSectionMetadata> {
self.parent_metadata.as_ref()
}
pub(crate) fn set_parent_metadata(&mut self, parent_metadata: ParentSectionMetadata) {
self.parent_metadata = Some(parent_metadata);
}
pub fn trailing_padding_size(&self) -> Option<Size> {
self.trailing_padding_size
}
pub(crate) fn set_trailing_padding_size(&mut self, size: Size) {
self.trailing_padding_size = Some(size);
}
#[must_use]
pub(crate) fn add_gp_to_pointed_data(&self) -> bool {
self.add_gp_to_pointed_data
}
pub(crate) fn set_add_gp_to_pointed_data(&mut self) {
self.add_gp_to_pointed_data = true;
}
}
impl SymbolMetadata {
pub(crate) fn is_trustable_function(&self) -> bool {
match self.sym_type() {
Some(SymbolType::Function) => true,
None => {
self.generated_by() == GeneratedBy::UserDeclared
}
Some(_) => false,
}
}
fn is_maybe_const_variable(&self) -> bool {
if self.section_type != Some(SectionType::Rodata) {
return false;
}
if let Some(sym_type) = self.sym_type() {
match sym_type {
SymbolType::Function => false,
SymbolType::Jumptable => false,
SymbolType::GccExceptTable => false,
SymbolType::Byte => true,
SymbolType::Short => true,
SymbolType::Word => true,
SymbolType::DWord => true,
SymbolType::Float32 => {
if let (Some(size), Some(padding)) = (self.size(), self.trailing_padding_size())
{
size != padding + Size::new(4)
} else {
false
}
}
SymbolType::Float64 => {
if let (Some(size), Some(padding)) = (self.size(), self.trailing_padding_size())
{
size != padding + Size::new(8)
} else {
false
}
}
SymbolType::CString => false,
SymbolType::VirtualTable => true,
SymbolType::UserCustom => !self.compiler.is_some_and(|x| x.forbids_const_structs()),
}
} else {
true
}
}
pub(crate) fn is_late_rodata(&self) -> bool {
self.sym_type()
.is_some_and(|x| x.is_late_rodata(self.compiler))
}
pub(crate) fn is_migrable(&self) -> bool {
match self.rodata_migration_behavior {
RodataMigrationBehavior::MigrateToSpecificFunction(_) => true,
RodataMigrationBehavior::ForceMigrate => true,
RodataMigrationBehavior::ForceNotMigrate => false,
RodataMigrationBehavior::Default => {
if self.is_mips1_double {
true
} else if !self.sym_referrers.reference_symbols().is_empty()
|| self.sym_referrers.reference_functions().len() > 1
{
false
} else if self.is_maybe_const_variable() {
self.compiler.is_some_and(|x| x.allow_rdata_migration())
} else {
true
}
}
}
}
}
impl PartialEq for SymbolMetadata {
fn eq(&self, other: &Self) -> bool {
self.vram == other.vram && self.rom == other.rom
}
}
impl PartialOrd for SymbolMetadata {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
match self.vram.partial_cmp(&other.vram) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
};
self.rom.partial_cmp(&other.rom)
}
}
impl Hash for SymbolMetadata {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.vram.hash(state);
self.rom.hash(state);
}
}
impl fmt::Debug for SymbolMetadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SymbolMetadata {{ vram: 0x{}, name: \"{}\" }}",
self.vram,
self.display_name()
)
}
}
impl SizedValue for SymbolMetadata {
fn size(&self) -> Size {
self.size().unwrap_or(Size::new(1))
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum AutogeneratedPadInfo {
Unreferenced,
CreatedBy(Vram),
}