use std::borrow::Cow;
use std::cell::Cell;
use std::cmp;
use std::rc::Rc;
use std::usize;
use crate::cfi::Cfi;
use crate::file::FileHash;
use crate::location::{self, FrameLocation, Piece, Register};
use crate::namespace::Namespace;
use crate::range::Range;
use crate::source::Source;
use crate::types::{ParameterType, Type, TypeOffset};
use crate::variable::LocalVariable;
use crate::{Address, Size};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FunctionOffset(usize);
impl FunctionOffset {
#[inline]
pub(crate) fn new(offset: usize) -> FunctionOffset {
debug_assert!(FunctionOffset(offset) != FunctionOffset::none());
FunctionOffset(offset)
}
#[inline]
pub(crate) fn none() -> FunctionOffset {
FunctionOffset(usize::MAX)
}
#[inline]
pub(crate) fn is_none(self) -> bool {
self == Self::none()
}
#[inline]
pub(crate) fn is_some(self) -> bool {
self != Self::none()
}
#[inline]
pub(crate) fn get(self) -> Option<usize> {
if self.is_none() {
None
} else {
Some(self.0)
}
}
}
impl Default for FunctionOffset {
#[inline]
fn default() -> Self {
FunctionOffset::none()
}
}
#[derive(Debug, Default)]
pub struct Function<'input> {
pub(crate) id: Cell<usize>,
pub(crate) offset: FunctionOffset,
pub(crate) namespace: Option<Rc<Namespace<'input>>>,
pub(crate) name: Option<&'input str>,
pub(crate) linkage_name: Option<&'input str>,
pub(crate) symbol_name: Option<&'input str>,
pub(crate) source: Source<'input>,
pub(crate) address: Address,
pub(crate) size: Size,
pub(crate) inline: bool,
pub(crate) declaration: bool,
pub(crate) parameters: Vec<ParameterType<'input>>,
pub(crate) return_type: TypeOffset,
}
#[derive(Debug, Default)]
pub struct FunctionDetails<'input> {
pub(crate) parameters: Vec<Parameter<'input>>,
pub(crate) variables: Vec<LocalVariable<'input>>,
pub(crate) inlined_functions: Vec<InlinedFunction<'input>>,
}
impl<'input> Function<'input> {
pub(crate) fn from_offset<'a>(
hash: &'a FileHash<'input>,
offset: FunctionOffset,
) -> Option<&'a Function<'input>> {
if offset.is_none() {
return None;
}
hash.functions_by_offset.get(&offset).cloned()
}
#[inline]
pub fn id(&self) -> usize {
self.id.get()
}
#[inline]
pub fn set_id(&self, id: usize) {
self.id.set(id)
}
pub fn namespace(&self) -> Option<&Namespace> {
self.namespace.as_ref().map(|x| &**x)
}
#[inline]
pub fn name(&self) -> Option<&str> {
self.name
}
#[inline]
pub fn linkage_name(&self) -> Option<&str> {
self.linkage_name
}
#[inline]
pub fn symbol_name(&self) -> Option<&str> {
self.symbol_name
}
#[inline]
pub fn source(&self) -> &Source<'input> {
&self.source
}
#[inline]
pub fn address(&self) -> Option<u64> {
self.address.get()
}
#[inline]
pub fn size(&self) -> Option<u64> {
self.size.get()
}
pub fn range(&self) -> Option<Range> {
if let (Some(address), Some(size)) = (self.address(), self.size()) {
Some(Range {
begin: address,
end: address + size,
})
} else {
None
}
}
#[inline]
pub fn is_inline(&self) -> bool {
self.inline
}
#[inline]
pub fn is_declaration(&self) -> bool {
self.declaration
}
#[inline]
pub fn parameters(&self) -> &[ParameterType<'input>] {
&self.parameters
}
#[inline]
pub fn return_type<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
Type::from_offset(hash, self.return_type)
}
pub fn details(&self, hash: &FileHash<'input>) -> FunctionDetails<'input> {
hash.file.get_function_details(self.offset, hash)
}
pub fn cfi(&self, hash: &FileHash<'input>) -> Vec<Cfi> {
hash.file.get_cfi(self.address, self.size)
}
pub fn cmp_id(
_hash_a: &FileHash,
a: &Function,
_hash_b: &FileHash,
b: &Function,
) -> cmp::Ordering {
Namespace::cmp_ns_and_name(a.namespace(), a.name(), b.namespace(), b.name())
}
}
impl<'input> FunctionDetails<'input> {
#[inline]
pub fn parameters(&self) -> &[Parameter<'input>] {
&self.parameters
}
#[inline]
pub fn variables(&self) -> &[LocalVariable<'input>] {
&self.variables
}
#[inline]
pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] {
&self.inlined_functions
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct ParameterOffset(usize);
impl ParameterOffset {
#[inline]
pub(crate) fn new(offset: usize) -> ParameterOffset {
debug_assert!(ParameterOffset(offset) != ParameterOffset::none());
ParameterOffset(offset)
}
#[inline]
pub(crate) fn none() -> ParameterOffset {
ParameterOffset(usize::MAX)
}
}
impl Default for ParameterOffset {
#[inline]
fn default() -> Self {
ParameterOffset::none()
}
}
#[derive(Debug, Default, Clone)]
pub struct Parameter<'input> {
pub(crate) offset: ParameterOffset,
pub(crate) name: Option<&'input str>,
pub(crate) ty: TypeOffset,
pub(crate) locations: Vec<(Range, Piece)>,
}
impl<'input> Parameter<'input> {
#[inline]
pub fn name(&self) -> Option<&'input str> {
self.name
}
#[inline]
pub fn type_offset(&self) -> TypeOffset {
self.ty
}
#[inline]
pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
Type::from_offset(hash, self.ty)
}
pub fn byte_size(&self, hash: &FileHash) -> Option<u64> {
self.ty(hash).and_then(|v| v.byte_size(hash))
}
pub fn registers<'a>(&'a self) -> impl Iterator<Item = (Range, Register)> + 'a {
location::registers(&self.locations)
}
pub fn register_offsets<'a>(&'a self) -> impl Iterator<Item = (Range, Register, i64)> + 'a {
location::register_offsets(&self.locations)
}
pub fn frame_locations<'a>(&'a self) -> impl Iterator<Item = FrameLocation> + 'a {
location::frame_locations(&self.locations)
}
#[allow(dead_code)]
fn cmp_id(hash_a: &FileHash, a: &Parameter, hash_b: &FileHash, b: &Parameter) -> cmp::Ordering {
let ord = Self::cmp_type(hash_a, a, hash_b, b);
if ord != cmp::Ordering::Equal {
return ord;
}
a.name.cmp(&b.name)
}
pub fn cmp_type(
hash_a: &FileHash,
a: &Parameter,
hash_b: &FileHash,
b: &Parameter,
) -> cmp::Ordering {
match (a.ty(hash_a), b.ty(hash_b)) {
(Some(ref ty_a), Some(ref ty_b)) => Type::cmp_id(hash_a, ty_a, hash_b, ty_b),
(Some(_), None) => cmp::Ordering::Less,
(None, Some(_)) => cmp::Ordering::Greater,
(None, None) => cmp::Ordering::Equal,
}
}
}
#[derive(Debug, Default)]
pub struct InlinedFunction<'input> {
pub(crate) abstract_origin: FunctionOffset,
pub(crate) size: Size,
pub(crate) parameters: Vec<Parameter<'input>>,
pub(crate) variables: Vec<LocalVariable<'input>>,
pub(crate) inlined_functions: Vec<InlinedFunction<'input>>,
pub(crate) call_source: Source<'input>,
}
impl<'input> InlinedFunction<'input> {
#[inline]
pub fn abstract_origin<'a>(&self, hash: &'a FileHash<'input>) -> Option<&'a Function<'input>> {
Function::from_offset(hash, self.abstract_origin)
}
#[inline]
pub fn size(&self) -> Option<u64> {
self.size.get()
}
#[inline]
pub fn call_source(&self) -> &Source<'input> {
&self.call_source
}
#[inline]
pub fn parameters(&self) -> &[Parameter<'input>] {
&self.parameters
}
#[inline]
pub fn variables(&self) -> &[LocalVariable<'input>] {
&self.variables
}
#[inline]
pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] {
&self.inlined_functions
}
}