use std::borrow::Cow;
use std::cmp;
use std::sync::Arc;
use fnv::FnvHashMap as HashMap;
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::{MemberOffset, ParameterType, Type, TypeOffset};
use crate::variable::{LocalVariable, Variable, VariableOffset};
use crate::{Address, Id, 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: Id,
pub(crate) offset: FunctionOffset,
pub(crate) namespace: Option<Arc<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) ranges: Vec<Range>,
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>>,
pub(crate) calls: Vec<FunctionCall<'input>>,
}
impl<'input> Function<'input> {
pub(crate) fn from_offset<'a>(
hash: &'a FileHash<'input>,
offset: FunctionOffset,
) -> Option<&'input 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<'input>> {
self.namespace.as_deref()
}
#[inline]
pub fn name(&self) -> Option<&'input str> {
self.name
}
#[inline]
pub fn linkage_name(&self) -> Option<&'input str> {
self.linkage_name
}
#[inline]
pub fn symbol_name(&self) -> Option<&'input 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 ranges(&self) -> &[Range] {
&self.ranges
}
#[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 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
}
#[inline]
pub fn calls(&self) -> &[FunctionCall<'input>] {
&self.calls
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub 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 offset(&self) -> ParameterOffset {
self.offset
}
#[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 locations(&self) -> &[(Range, Piece)] {
&self.locations
}
pub fn registers(&self) -> impl Iterator<Item = (Range, Register)> + '_ {
location::registers(&self.locations)
}
pub fn register_offsets(&self) -> impl Iterator<Item = (Range, Register, i64)> + '_ {
location::register_offsets(&self.locations)
}
pub fn frame_locations(&self) -> impl Iterator<Item = (Range, FrameLocation)> + '_ {
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(ty_a), Some(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) ranges: Vec<Range>,
pub(crate) parameters: Vec<Parameter<'input>>,
pub(crate) variables: Vec<LocalVariable<'input>>,
pub(crate) inlined_functions: Vec<InlinedFunction<'input>>,
pub(crate) calls: Vec<FunctionCall<'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 ranges(&self) -> &[Range] {
&self.ranges
}
#[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
}
#[inline]
pub fn calls(&self) -> &[FunctionCall<'input>] {
&self.calls
}
}
#[derive(Debug, Default)]
pub struct FunctionCall<'input> {
pub(crate) kind: FunctionCallKind,
pub(crate) return_address: Option<u64>,
pub(crate) call_address: Option<u64>,
pub(crate) origin: Option<FunctionCallOrigin<'input>>,
pub(crate) target: Vec<Piece>,
pub(crate) target_is_clobbered: bool,
pub(crate) ty: Option<TypeOffset>,
pub(crate) called_from_source: Source<'input>,
pub(crate) parameters: Vec<FunctionCallParameter<'input>>,
}
impl<'input> FunctionCall<'input> {
#[inline]
pub fn kind(&self) -> FunctionCallKind {
self.kind
}
#[inline]
pub fn return_address(&self) -> Option<u64> {
self.return_address
}
#[inline]
pub fn call_address(&self) -> Option<u64> {
self.call_address
}
#[inline]
pub fn origin(&self) -> Option<&FunctionCallOrigin<'input>> {
self.origin.as_ref()
}
#[inline]
pub fn target(&self) -> &[Piece] {
&self.target
}
#[inline]
pub fn target_is_clobbered(&self) -> bool {
self.target_is_clobbered
}
#[inline]
pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
if let Some(ty) = self.ty {
Type::from_offset(hash, ty)
} else {
None
}
}
#[inline]
pub fn source(&self) -> &Source<'input> {
&self.called_from_source
}
#[inline]
pub fn parameters(&self) -> &[FunctionCallParameter<'input>] {
&self.parameters
}
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
pub enum FunctionCallKind {
#[default]
Normal,
Tail,
}
#[derive(Debug)]
pub enum FunctionCallOrigin<'input> {
Direct(&'input Function<'input>),
Indirect(FunctionCallIndirectOrigin<'input>),
}
#[derive(Debug)]
pub enum FunctionCallIndirectOrigin<'input> {
Variable(&'input Variable<'input>),
LocalVariable(VariableOffset),
Parameter(ParameterOffset),
Member(MemberOffset),
}
#[derive(Debug, Default)]
pub struct FunctionCallParameter<'input> {
pub(crate) name: Option<&'input str>,
pub(crate) ty: Option<TypeOffset>,
pub(crate) location: Vec<Piece>,
pub(crate) value: Vec<Piece>,
pub(crate) data_location: Vec<Piece>,
pub(crate) data_value: Vec<Piece>,
}
impl<'input> FunctionCallParameter<'input> {
#[inline]
pub fn name(&self) -> Option<&'input str> {
self.name
}
#[inline]
pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
if let Some(ty) = self.ty {
Type::from_offset(hash, ty)
} else {
None
}
}
pub fn location(&self) -> &[Piece] {
&self.location
}
pub fn value(&self) -> &[Piece] {
&self.value
}
pub fn data_location(&self) -> &[Piece] {
&self.data_location
}
pub fn data_value(&self) -> &[Piece] {
&self.data_value
}
}
#[derive(Debug, Clone, Copy)]
pub enum FunctionInstance<'input> {
Normal(&'input FunctionDetails<'input>),
Inlined(&'input InlinedFunction<'input>),
}
impl<'input> FunctionInstance<'input> {
#[inline]
fn parameters(&self) -> &'input [Parameter<'input>] {
match self {
Self::Normal(f) => f.parameters(),
Self::Inlined(f) => f.parameters(),
}
}
#[inline]
fn variables(&self) -> &'input [LocalVariable<'input>] {
match self {
Self::Normal(f) => f.variables(),
Self::Inlined(f) => f.variables(),
}
}
}
pub struct FunctionHash<'input> {
pub function: FunctionInstance<'input>,
pub parameters_by_offset: HashMap<ParameterOffset, &'input Parameter<'input>>,
pub local_variables_by_offset: HashMap<VariableOffset, &'input LocalVariable<'input>>,
}
impl<'input> FunctionHash<'input> {
pub fn new(function: FunctionInstance<'input>) -> Self {
Self {
function,
parameters_by_offset: Self::parameters_by_offset(&function),
local_variables_by_offset: Self::local_variables_by_offset(&function),
}
}
fn parameters_by_offset(
function: &FunctionInstance<'input>,
) -> HashMap<ParameterOffset, &'input Parameter<'input>> {
let mut parameters = HashMap::default();
for parameter in function.parameters() {
parameters.insert(parameter.offset, parameter);
}
parameters
}
fn local_variables_by_offset(
function: &FunctionInstance<'input>,
) -> HashMap<VariableOffset, &'input LocalVariable<'input>> {
let mut local_variables = HashMap::default();
for local_variable in function.variables() {
local_variables.insert(local_variable.offset, local_variable);
}
local_variables
}
}