spimdisasm 2.0.0-alpha.1

MIPS disassembler
Documentation
/* SPDX-FileCopyrightText: © 2024-2025 Decompollaborate */
/* SPDX-License-Identifier: MIT */

use alloc::vec::Vec;
use core::hash::Hash;
use rabbitizer::access_type::AccessType;

use crate::{
    addresses::{Size, Vram},
    collections::{addended_ordered_map::SizedValue, unordered_map::UnorderedMap},
    metadata::SymbolType,
};

#[derive(Debug, Clone)]
pub struct ReferencedAddress {
    vram: Vram,
    user_declared: bool,

    referenced_by: Vec<Vram>,

    access_types: UnorderedMap<AccessType, u32>,

    user_declared_type: Option<SymbolType>,
    autodetected_types: UnorderedMap<SymbolType, u32>,

    user_declared_size: Option<Size>,
    autodetected_size: Option<Size>,

    table_labels: Vec<Vram>,

    add_gp_to_pointed_data: bool,
}

impl ReferencedAddress {
    pub(crate) fn new(vram: Vram) -> Self {
        Self {
            vram,
            user_declared: false,

            referenced_by: Vec::new(),

            access_types: UnorderedMap::new(),

            user_declared_type: None,
            autodetected_types: UnorderedMap::new(),

            user_declared_size: None,
            autodetected_size: None,

            table_labels: Vec::new(),

            add_gp_to_pointed_data: false,
        }
    }

    pub(crate) fn new_user_declared(vram: Vram) -> Self {
        Self {
            user_declared: true,
            ..Self::new(vram)
        }
    }

    pub const fn vram(&self) -> Vram {
        self.vram
    }
    pub const fn user_declared(&self) -> bool {
        self.user_declared
    }
    pub fn referenced_by(&self) -> &[Vram] {
        &self.referenced_by
    }

    pub fn access_type(&self) -> Option<AccessType> {
        if self.access_types.len() == 1 {
            self.access_types
                .iter()
                .next()
                .map(|(access, _count)| *access)
        } else {
            None
        }
    }
    pub(crate) fn all_access_types(&self) -> &UnorderedMap<AccessType, u32> {
        &self.access_types
    }

    pub fn sym_type(&self) -> Option<SymbolType> {
        if let Some(typ) = self.user_declared_type {
            Some(typ)
        } else if self.autodetected_types.len() == 1 {
            self.autodetected_types
                .iter()
                .next()
                .map(|(typ, _count)| *typ)
        } else {
            self.access_type().and_then(SymbolType::from_access_type)
        }
    }

    pub fn size(&self) -> Option<Size> {
        if let Some(size) = self.user_declared_size {
            Some(size)
        } else if self.user_declared_type.is_some() {
            None
        } else if let Some(size) = self.autodetected_size {
            Some(size)
        } else if !self.autodetected_types.is_empty() {
            None
        } else {
            self.access_type()
                .and_then(|x| x.min_size().map(|x| Size::new(x.into())))
        }
    }
    pub fn user_declared_size(&self) -> Option<Size> {
        self.user_declared_size
    }
    pub fn autodetected_size(&self) -> Option<Size> {
        self.autodetected_size
    }

    pub fn alignment(&self) -> Option<u8> {
        if self.user_declared_type.is_some() || !self.autodetected_types.is_empty() {
            None
        } else {
            self.access_type().and_then(|x| x.min_alignment())
        }
    }

    pub fn reference_counter(&self) -> usize {
        self.referenced_by.len()
    }

    pub(crate) fn is_trustable_function(&self) -> bool {
        // """Checks if the function symbol should be trusted based on the current disassembler settings"""

        /*
        if self.unknownSegment:
            return False

        if self.isGotLocal:
            return False

        if self.isAutocreatedSymFromOtherSizedSym:
            return True
        */

        match self.sym_type() {
            Some(SymbolType::Function) => true,
            None => {
                // Users may not type the symbol as a function.
                self.user_declared
            }
            Some(_) => false,
        }
        /*
        currentType = self.getTypeSpecial()

        if self.isUserDeclared:
            if currentType == SymbolSpecialType.branchlabel:
                return False
            return True

        if self.isAutogenerated and currentType == SymbolSpecialType.function:
            return True

        if rsp:
            return True

        return False
        */
    }

    pub fn table_labels(&self) -> &[Vram] {
        &self.table_labels
    }

    #[must_use]
    pub(crate) fn add_gp_to_pointed_data(&self) -> bool {
        self.add_gp_to_pointed_data
    }
}

impl ReferencedAddress {
    pub(crate) fn add_referenced_by(&mut self, specific_address: Vram) {
        self.referenced_by.push(specific_address);
    }

    pub(crate) fn set_access_type(&mut self, access_type: AccessType) {
        *self.access_types.entry(access_type).or_default() += 1;
    }

    pub(crate) fn set_user_declared_type(&mut self, typ: SymbolType) {
        self.user_declared_type = Some(typ);
    }
    pub(crate) fn set_sym_type(&mut self, sym_type: SymbolType) {
        *self.autodetected_types.entry(sym_type).or_default() += 1;
    }

    pub(crate) fn set_user_declared_size(&mut self, size: Size) {
        self.user_declared_size = Some(size);
    }
    pub(crate) fn set_autodetected_size(&mut self, size: Size) {
        self.autodetected_size = Some(size);
    }

    pub(crate) fn add_table_label(&mut self, label: Vram) {
        self.table_labels.push(label);
    }

    pub(crate) fn set_from_other_reference(&mut self, other: Self) {
        for other_vram in other.referenced_by {
            self.add_referenced_by(other_vram);
        }
        for (access_type, count) in other.access_types {
            *self.access_types.entry(access_type).or_default() += count;
        }
        if self.user_declared_type.is_none() {
            self.user_declared_type = other.user_declared_type;
        }
        for (sym_type, count) in other.autodetected_types {
            *self.autodetected_types.entry(sym_type).or_default() += count;
        }
        if self.user_declared_size.is_none() {
            self.user_declared_size = other.user_declared_size;
        }
        if self.autodetected_size.is_none() {
            self.autodetected_size = other.autodetected_size;
        }
        for label in other.table_labels {
            self.add_table_label(label);
        }
    }

    pub(crate) fn set_add_gp_to_pointed_data(&mut self) {
        self.add_gp_to_pointed_data = true;
    }
}

impl PartialEq for ReferencedAddress {
    fn eq(&self, other: &Self) -> bool {
        self.vram == other.vram
    }
}
impl PartialOrd for ReferencedAddress {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        self.vram.partial_cmp(&other.vram)
    }
}
impl Hash for ReferencedAddress {
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.vram.hash(state);
    }
}

impl SizedValue for ReferencedAddress {
    fn size(&self) -> Size {
        self.size().unwrap_or(Size::new(1))
    }
}