1use std::borrow::Cow;
2use std::cmp;
3use std::sync::Arc;
4
5use fnv::FnvHashMap as HashMap;
6
7use crate::file::FileHash;
8use crate::location::{self, FrameLocation, Piece, Register};
9use crate::namespace::Namespace;
10use crate::range::Range;
11use crate::source::Source;
12use crate::types::{MemberOffset, ParameterType, Type, TypeOffset};
13use crate::variable::{LocalVariable, Variable, VariableOffset};
14use crate::{Address, Id, Size};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct FunctionOffset(usize);
21
22impl FunctionOffset {
23 #[inline]
24 pub(crate) fn new(offset: usize) -> FunctionOffset {
25 debug_assert!(FunctionOffset(offset) != FunctionOffset::none());
26 FunctionOffset(offset)
27 }
28
29 #[inline]
30 pub(crate) fn none() -> FunctionOffset {
31 FunctionOffset(usize::MAX)
32 }
33
34 #[inline]
35 pub(crate) fn is_none(self) -> bool {
36 self == Self::none()
37 }
38
39 #[inline]
40 pub(crate) fn is_some(self) -> bool {
41 self != Self::none()
42 }
43
44 #[inline]
45 pub(crate) fn get(self) -> Option<usize> {
46 if self.is_none() { None } else { Some(self.0) }
47 }
48}
49
50impl Default for FunctionOffset {
51 #[inline]
52 fn default() -> Self {
53 FunctionOffset::none()
54 }
55}
56
57#[derive(Debug, Default)]
59pub struct Function<'input> {
60 pub(crate) id: Id,
61 pub(crate) offset: FunctionOffset,
62 pub(crate) namespace: Option<Arc<Namespace<'input>>>,
63 pub(crate) name: Option<&'input str>,
64 pub(crate) linkage_name: Option<&'input str>,
65 pub(crate) symbol_name: Option<&'input str>,
66 pub(crate) source: Source<'input>,
67 pub(crate) address: Address,
68 pub(crate) size: Size,
69 pub(crate) ranges: Vec<Range>,
70 pub(crate) inline: bool,
71 pub(crate) declaration: bool,
72 pub(crate) parameters: Vec<ParameterType<'input>>,
73 pub(crate) return_type: TypeOffset,
74}
75
76#[derive(Debug, Default)]
80pub struct FunctionDetails<'input> {
81 pub(crate) parameters: Vec<Parameter<'input>>,
82 pub(crate) variables: Vec<LocalVariable<'input>>,
83 pub(crate) inlined_functions: Vec<InlinedFunction<'input>>,
84 pub(crate) calls: Vec<FunctionCall<'input>>,
85}
86
87impl<'input> Function<'input> {
88 pub(crate) fn from_offset<'a>(
89 hash: &'a FileHash<'input>,
90 offset: FunctionOffset,
91 ) -> Option<&'input Function<'input>> {
92 if offset.is_none() {
93 return None;
94 }
95 hash.functions_by_offset.get(&offset).cloned()
96 }
97
98 #[inline]
100 pub fn id(&self) -> usize {
101 self.id.get()
102 }
103
104 #[inline]
106 pub fn set_id(&self, id: usize) {
107 self.id.set(id)
108 }
109
110 pub fn namespace(&self) -> Option<&Namespace<'input>> {
112 self.namespace.as_deref()
113 }
114
115 #[inline]
117 pub fn name(&self) -> Option<&'input str> {
118 self.name
119 }
120
121 #[inline]
123 pub fn linkage_name(&self) -> Option<&'input str> {
124 self.linkage_name
125 }
126
127 #[inline]
131 pub fn symbol_name(&self) -> Option<&'input str> {
132 self.symbol_name
133 }
134
135 #[inline]
137 pub fn source(&self) -> &Source<'input> {
138 &self.source
139 }
140
141 #[inline]
143 pub fn address(&self) -> Option<u64> {
144 self.address.get()
145 }
146
147 #[inline]
151 pub fn size(&self) -> Option<u64> {
152 self.size.get()
153 }
154
155 pub fn ranges(&self) -> &[Range] {
157 &self.ranges
158 }
159
160 #[inline]
162 pub fn is_inline(&self) -> bool {
163 self.inline
164 }
165
166 #[inline]
168 pub fn is_declaration(&self) -> bool {
169 self.declaration
170 }
171
172 #[inline]
174 pub fn parameters(&self) -> &[ParameterType<'input>] {
175 &self.parameters
176 }
177
178 #[inline]
182 pub fn return_type<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
183 Type::from_offset(hash, self.return_type)
184 }
185
186 pub fn details(&self, hash: &FileHash<'input>) -> FunctionDetails<'input> {
188 hash.file.get_function_details(self.offset, hash)
189 }
190
191 pub fn cmp_id(
198 _hash_a: &FileHash,
199 a: &Function,
200 _hash_b: &FileHash,
201 b: &Function,
202 ) -> cmp::Ordering {
203 Namespace::cmp_ns_and_name(a.namespace(), a.name(), b.namespace(), b.name())
204 }
205}
206
207impl<'input> FunctionDetails<'input> {
208 #[inline]
210 pub fn parameters(&self) -> &[Parameter<'input>] {
211 &self.parameters
212 }
213
214 #[inline]
216 pub fn variables(&self) -> &[LocalVariable<'input>] {
217 &self.variables
218 }
219
220 #[inline]
222 pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] {
223 &self.inlined_functions
224 }
225
226 #[inline]
228 pub fn calls(&self) -> &[FunctionCall<'input>] {
229 &self.calls
230 }
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
237pub struct ParameterOffset(usize);
238
239impl ParameterOffset {
240 #[inline]
241 pub(crate) fn new(offset: usize) -> ParameterOffset {
242 debug_assert!(ParameterOffset(offset) != ParameterOffset::none());
243 ParameterOffset(offset)
244 }
245
246 #[inline]
247 pub(crate) fn none() -> ParameterOffset {
248 ParameterOffset(usize::MAX)
249 }
250}
251
252impl Default for ParameterOffset {
253 #[inline]
254 fn default() -> Self {
255 ParameterOffset::none()
256 }
257}
258
259#[derive(Debug, Default, Clone)]
261pub struct Parameter<'input> {
262 pub(crate) offset: ParameterOffset,
263 pub(crate) name: Option<&'input str>,
264 pub(crate) ty: TypeOffset,
265 pub(crate) locations: Vec<(Range, Piece)>,
267}
268
269impl<'input> Parameter<'input> {
270 #[inline]
272 pub fn name(&self) -> Option<&'input str> {
273 self.name
274 }
275
276 #[inline]
278 pub fn offset(&self) -> ParameterOffset {
279 self.offset
280 }
281
282 #[inline]
286 pub fn type_offset(&self) -> TypeOffset {
287 self.ty
288 }
289
290 #[inline]
292 pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
293 Type::from_offset(hash, self.ty)
294 }
295
296 pub fn byte_size(&self, hash: &FileHash) -> Option<u64> {
298 self.ty(hash).and_then(|v| v.byte_size(hash))
299 }
300
301 pub fn locations(&self) -> &[(Range, Piece)] {
303 &self.locations
304 }
305
306 pub fn registers(&self) -> impl Iterator<Item = (Range, Register)> + '_ {
308 location::registers(&self.locations)
309 }
310
311 pub fn register_offsets(&self) -> impl Iterator<Item = (Range, Register, i64)> + '_ {
313 location::register_offsets(&self.locations)
314 }
315
316 pub fn frame_locations(&self) -> impl Iterator<Item = (Range, FrameLocation)> + '_ {
318 location::frame_locations(&self.locations)
319 }
320
321 #[allow(dead_code)]
328 fn cmp_id(hash_a: &FileHash, a: &Parameter, hash_b: &FileHash, b: &Parameter) -> cmp::Ordering {
329 let ord = Self::cmp_type(hash_a, a, hash_b, b);
330 if ord != cmp::Ordering::Equal {
331 return ord;
332 }
333 a.name.cmp(&b.name)
334 }
335
336 pub fn cmp_type(
338 hash_a: &FileHash,
339 a: &Parameter,
340 hash_b: &FileHash,
341 b: &Parameter,
342 ) -> cmp::Ordering {
343 match (&a.ty(hash_a), &b.ty(hash_b)) {
344 (Some(ty_a), Some(ty_b)) => Type::cmp_id(hash_a, ty_a, hash_b, ty_b),
345 (Some(_), None) => cmp::Ordering::Less,
346 (None, Some(_)) => cmp::Ordering::Greater,
347 (None, None) => cmp::Ordering::Equal,
348 }
349 }
350}
351
352#[derive(Debug, Default)]
354pub struct InlinedFunction<'input> {
355 pub(crate) abstract_origin: FunctionOffset,
356 pub(crate) size: Size,
357 pub(crate) ranges: Vec<Range>,
358 pub(crate) parameters: Vec<Parameter<'input>>,
359 pub(crate) variables: Vec<LocalVariable<'input>>,
360 pub(crate) inlined_functions: Vec<InlinedFunction<'input>>,
361 pub(crate) calls: Vec<FunctionCall<'input>>,
362 pub(crate) call_source: Source<'input>,
363}
364
365impl<'input> InlinedFunction<'input> {
366 #[inline]
368 pub fn abstract_origin<'a>(&self, hash: &'a FileHash<'input>) -> Option<&'a Function<'input>> {
369 Function::from_offset(hash, self.abstract_origin)
370 }
371
372 #[inline]
374 pub fn ranges(&self) -> &[Range] {
375 &self.ranges
376 }
377
378 #[inline]
380 pub fn size(&self) -> Option<u64> {
381 self.size.get()
382 }
383
384 #[inline]
386 pub fn call_source(&self) -> &Source<'input> {
387 &self.call_source
388 }
389
390 #[inline]
392 pub fn parameters(&self) -> &[Parameter<'input>] {
393 &self.parameters
394 }
395
396 #[inline]
398 pub fn variables(&self) -> &[LocalVariable<'input>] {
399 &self.variables
400 }
401
402 #[inline]
404 pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] {
405 &self.inlined_functions
406 }
407
408 #[inline]
410 pub fn calls(&self) -> &[FunctionCall<'input>] {
411 &self.calls
412 }
413}
414
415#[derive(Debug, Default)]
417pub struct FunctionCall<'input> {
418 pub(crate) kind: FunctionCallKind,
419 pub(crate) return_address: Option<u64>,
420 pub(crate) call_address: Option<u64>,
421 pub(crate) origin: Option<FunctionCallOrigin<'input>>,
422 pub(crate) target: Vec<Piece>,
423 pub(crate) target_is_clobbered: bool,
424 pub(crate) ty: Option<TypeOffset>,
425 pub(crate) called_from_source: Source<'input>,
426 pub(crate) parameters: Vec<FunctionCallParameter<'input>>,
427}
428
429impl<'input> FunctionCall<'input> {
430 #[inline]
432 pub fn kind(&self) -> FunctionCallKind {
433 self.kind
434 }
435
436 #[inline]
438 pub fn return_address(&self) -> Option<u64> {
439 self.return_address
440 }
441
442 #[inline]
447 pub fn call_address(&self) -> Option<u64> {
448 self.call_address
449 }
450
451 #[inline]
456 pub fn origin(&self) -> Option<&FunctionCallOrigin<'input>> {
457 self.origin.as_ref()
458 }
459
460 #[inline]
464 pub fn target(&self) -> &[Piece] {
465 &self.target
466 }
467
468 #[inline]
470 pub fn target_is_clobbered(&self) -> bool {
471 self.target_is_clobbered
472 }
473
474 #[inline]
481 pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
482 if let Some(ty) = self.ty {
483 Type::from_offset(hash, ty)
484 } else {
485 None
486 }
487 }
488
489 #[inline]
491 pub fn source(&self) -> &Source<'input> {
492 &self.called_from_source
493 }
494
495 #[inline]
497 pub fn parameters(&self) -> &[FunctionCallParameter<'input>] {
498 &self.parameters
499 }
500}
501
502#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
504pub enum FunctionCallKind {
505 #[default]
507 Normal,
508 Tail,
510}
511
512#[derive(Debug)]
514pub enum FunctionCallOrigin<'input> {
515 Direct(&'input Function<'input>),
517 Indirect(FunctionCallIndirectOrigin<'input>),
519}
520
521#[derive(Debug)]
523pub enum FunctionCallIndirectOrigin<'input> {
524 Variable(&'input Variable<'input>),
526 LocalVariable(VariableOffset),
528 Parameter(ParameterOffset),
530 Member(MemberOffset),
535}
536
537#[derive(Debug, Default)]
539pub struct FunctionCallParameter<'input> {
540 pub(crate) name: Option<&'input str>,
541 pub(crate) ty: Option<TypeOffset>,
542 pub(crate) location: Vec<Piece>,
543 pub(crate) value: Vec<Piece>,
544 pub(crate) data_location: Vec<Piece>,
545 pub(crate) data_value: Vec<Piece>,
546}
547
548impl<'input> FunctionCallParameter<'input> {
549 #[inline]
551 pub fn name(&self) -> Option<&'input str> {
552 self.name
553 }
554
555 #[inline]
559 pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option<Cow<'a, Type<'input>>> {
560 if let Some(ty) = self.ty {
561 Type::from_offset(hash, ty)
562 } else {
563 None
564 }
565 }
566
567 pub fn location(&self) -> &[Piece] {
569 &self.location
570 }
571
572 pub fn value(&self) -> &[Piece] {
574 &self.value
575 }
576
577 pub fn data_location(&self) -> &[Piece] {
579 &self.data_location
580 }
581
582 pub fn data_value(&self) -> &[Piece] {
584 &self.data_value
585 }
586}
587
588#[derive(Debug, Clone, Copy)]
590pub enum FunctionInstance<'input> {
591 Normal(&'input FunctionDetails<'input>),
593 Inlined(&'input InlinedFunction<'input>),
595}
596
597impl<'input> FunctionInstance<'input> {
598 #[inline]
599 fn parameters(&self) -> &'input [Parameter<'input>] {
600 match self {
601 Self::Normal(f) => f.parameters(),
602 Self::Inlined(f) => f.parameters(),
603 }
604 }
605
606 #[inline]
607 fn variables(&self) -> &'input [LocalVariable<'input>] {
608 match self {
609 Self::Normal(f) => f.variables(),
610 Self::Inlined(f) => f.variables(),
611 }
612 }
613}
614
615pub struct FunctionHash<'input> {
619 pub function: FunctionInstance<'input>,
621 pub parameters_by_offset: HashMap<ParameterOffset, &'input Parameter<'input>>,
623 pub local_variables_by_offset: HashMap<VariableOffset, &'input LocalVariable<'input>>,
625}
626
627impl<'input> FunctionHash<'input> {
628 pub fn new(function: FunctionInstance<'input>) -> Self {
630 Self {
631 function,
632 parameters_by_offset: Self::parameters_by_offset(&function),
633 local_variables_by_offset: Self::local_variables_by_offset(&function),
634 }
635 }
636
637 fn parameters_by_offset(
638 function: &FunctionInstance<'input>,
639 ) -> HashMap<ParameterOffset, &'input Parameter<'input>> {
640 let mut parameters = HashMap::default();
641 for parameter in function.parameters() {
642 parameters.insert(parameter.offset, parameter);
643 }
644 parameters
645 }
646
647 fn local_variables_by_offset(
648 function: &FunctionInstance<'input>,
649 ) -> HashMap<VariableOffset, &'input LocalVariable<'input>> {
650 let mut local_variables = HashMap::default();
651 for local_variable in function.variables() {
652 local_variables.insert(local_variable.offset, local_variable);
653 }
654 local_variables
655 }
656}