1use crate::{
9 analysis::utils::fn_info::{display_hashmap, get_pointee, is_ptr, is_ref, is_slice},
10 check::senryx::{
11 contract::{AlignState, ContractExpr, ContractFactSet, PropertyContract},
12 symbolic_analysis::{AnaOperand, SymbolicDef},
13 },
14};
15use rustc_hir::def_id::DefId;
16use rustc_middle::mir::BinOp;
17use rustc_middle::mir::Local;
18use rustc_middle::ty::TyKind;
19use rustc_middle::ty::{Ty, TyCtxt};
20use std::collections::{HashMap, HashSet};
21use std::fmt::Write;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct States<'tcx> {
26 pub nonnull: bool,
27 pub allocator_consistency: bool,
28 pub init: bool,
29 pub align: AlignState<'tcx>,
30 pub valid_string: bool,
31 pub valid_cstr: bool,
32}
33
34impl<'tcx> States<'tcx> {
35 pub fn new(ty: Ty<'tcx>) -> Self {
37 Self {
38 nonnull: true,
39 allocator_consistency: true,
40 init: true,
41 align: AlignState::Aligned(ty),
42 valid_string: true,
43 valid_cstr: true,
44 }
45 }
46
47 pub fn new_unknown() -> Self {
49 Self {
50 nonnull: false,
51 allocator_consistency: false,
52 init: false,
53 align: AlignState::Unknown,
54 valid_string: false,
55 valid_cstr: false,
56 }
57 }
58
59 pub fn merge_states(&mut self, other: &States<'tcx>) {
61 self.nonnull &= other.nonnull;
62 self.allocator_consistency &= other.allocator_consistency;
63 self.init &= other.init;
64 self.align = self.align.merge(&other.align);
65 self.valid_string &= other.valid_string;
66 self.valid_cstr &= other.valid_cstr;
67 }
68}
69
70#[derive(Debug, Clone)]
72pub struct InterResultNode<'tcx> {
73 pub point_to: Option<Box<InterResultNode<'tcx>>>,
74 pub fields: HashMap<usize, InterResultNode<'tcx>>,
75 pub ty: Option<Ty<'tcx>>,
76 pub states: States<'tcx>,
77 pub const_value: usize,
78}
79
80impl<'tcx> InterResultNode<'tcx> {
81 pub fn new_default(ty: Option<Ty<'tcx>>) -> Self {
83 Self {
84 point_to: None,
85 fields: HashMap::new(),
86 ty,
87 states: States::new(ty.unwrap()),
88 const_value: 0, }
90 }
91
92 pub fn construct_from_var_node(chain: DominatedGraph<'tcx>, var_id: usize) -> Self {
94 let var_node = chain.get_var_node(var_id).unwrap();
95 let point_node = if var_node.points_to.is_none() {
96 None
97 } else {
98 Some(Box::new(Self::construct_from_var_node(
99 chain.clone(),
100 var_node.points_to.unwrap(),
101 )))
102 };
103 let fields = var_node
104 .field
105 .iter()
106 .map(|(k, v)| (*k, Self::construct_from_var_node(chain.clone(), *v)))
107 .collect();
108 Self {
109 point_to: point_node,
110 fields,
111 ty: var_node.ty.clone(),
112 states: var_node.ots.clone(),
113 const_value: var_node.const_value,
114 }
115 }
116
117 pub fn merge(&mut self, other: InterResultNode<'tcx>) {
119 if self.ty != other.ty {
120 return;
121 }
122 self.states.merge_states(&other.states);
124
125 match (&mut self.point_to, other.point_to) {
127 (Some(self_ptr), Some(other_ptr)) => self_ptr.merge(*other_ptr),
128 (None, Some(other_ptr)) => {
129 self.point_to = Some(other_ptr.clone());
130 }
131 _ => {}
132 }
133 for (field_id, other_node) in &other.fields {
135 match self.fields.get_mut(field_id) {
136 Some(self_node) => self_node.merge(other_node.clone()),
137 None => {
138 self.fields.insert(*field_id, other_node.clone());
139 }
140 }
141 }
142 self.const_value = std::cmp::max(self.const_value, other.const_value);
144 }
145}
146
147#[derive(Clone, Debug)]
149pub struct FunctionSummary<'tcx> {
150 pub return_def: Option<SymbolicDef<'tcx>>,
151}
152
153impl<'tcx> FunctionSummary<'tcx> {
154 pub fn new(def: Option<SymbolicDef<'tcx>>) -> Self {
156 Self { return_def: def }
157 }
158}
159
160#[derive(Debug, Clone)]
162pub struct VariableNode<'tcx> {
163 pub id: usize,
164 pub alias_set: HashSet<usize>,
165 pub points_to: Option<usize>,
166 pub pointed_by: HashSet<usize>,
167 pub field: HashMap<usize, usize>,
168 pub ty: Option<Ty<'tcx>>,
169 pub is_dropped: bool,
170 pub ots: States<'tcx>,
171 pub const_value: usize,
172 pub facts: ContractFactSet<'tcx>,
173 pub offset_from: Option<SymbolicDef<'tcx>>,
174}
175
176impl<'tcx> VariableNode<'tcx> {
177 pub fn new(
179 id: usize,
180 points_to: Option<usize>,
181 pointed_by: HashSet<usize>,
182 ty: Option<Ty<'tcx>>,
183 ots: States<'tcx>,
184 ) -> Self {
185 VariableNode {
186 id,
187 alias_set: HashSet::from([id]),
188 points_to,
189 pointed_by,
190 field: HashMap::new(),
191 ty,
192 is_dropped: false,
193 ots,
194 const_value: 0,
195 facts: ContractFactSet::new_default(),
196 offset_from: None,
197 }
198 }
199
200 pub fn new_default(id: usize, ty: Option<Ty<'tcx>>) -> Self {
202 VariableNode {
203 id,
204 alias_set: HashSet::from([id]),
205 points_to: None,
206 pointed_by: HashSet::new(),
207 field: HashMap::new(),
208 ty,
209 is_dropped: false,
210 ots: States::new(ty.unwrap()),
211 const_value: 0,
212 facts: ContractFactSet::new_default(),
213 offset_from: None,
214 }
215 }
216
217 pub fn new_with_states(id: usize, ty: Option<Ty<'tcx>>, ots: States<'tcx>) -> Self {
219 VariableNode {
220 id,
221 alias_set: HashSet::from([id]),
222 points_to: None,
223 pointed_by: HashSet::new(),
224 field: HashMap::new(),
225 ty,
226 is_dropped: false,
227 ots,
228 const_value: 0,
229 facts: ContractFactSet::new_default(),
230 offset_from: None,
231 }
232 }
233}
234
235#[derive(Clone)]
237pub struct DominatedGraph<'tcx> {
238 pub tcx: TyCtxt<'tcx>,
240 pub def_id: DefId,
242 pub local_len: usize,
244 pub variables: HashMap<usize, VariableNode<'tcx>>,
246}
247
248impl<'tcx> DominatedGraph<'tcx> {
249 pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId) -> Self {
254 let body = tcx.optimized_mir(def_id);
255 let locals = body.local_decls.clone();
256 let mut var_map: HashMap<usize, VariableNode<'_>> = HashMap::new();
257 for (idx, local) in locals.iter().enumerate() {
258 let local_ty = local.ty;
259 let mut node = VariableNode::new_default(idx, Some(local_ty));
260 if local_ty.to_string().contains("MaybeUninit") {
261 node.ots.init = false;
262 }
263 var_map.insert(idx, node);
264 }
265 Self {
266 tcx,
267 def_id,
268 local_len: locals.len(),
269 variables: var_map,
270 }
271 }
272
273 pub fn init_self_with_inter(&mut self, inter_result: InterResultNode<'tcx>) {
275 let self_node = self.get_var_node(1).unwrap().clone();
276 if self_node.ty.unwrap().is_ref() {
277 let obj_node = self.get_var_node(self.get_point_to_id(1)).unwrap();
278 self.dfs_insert_inter_results(inter_result, obj_node.id);
279 } else {
280 self.dfs_insert_inter_results(inter_result, self_node.id);
281 }
282 }
283
284 pub fn dfs_insert_inter_results(&mut self, inter_result: InterResultNode<'tcx>, local: usize) {
286 let new_id = self.generate_node_id();
287 let node = self.get_var_node_mut(local).unwrap();
288 node.ots = inter_result.states;
290 node.const_value = inter_result.const_value;
291 if inter_result.point_to.is_some() {
292 let new_node = inter_result.point_to.unwrap();
293 node.points_to = Some(new_id);
294 self.insert_node(
295 new_id,
296 new_node.ty.clone(),
297 local,
298 None,
299 new_node.states.clone(),
300 );
301 self.dfs_insert_inter_results(*new_node, new_id);
302 }
303 for (field_idx, field_inter) in inter_result.fields {
304 let field_node_id = self.insert_field_node(local, field_idx, field_inter.ty.clone());
305 self.dfs_insert_inter_results(field_inter, field_node_id);
306 }
307 }
308
309 pub fn init_arg(&mut self) {
311 let body = self.tcx.optimized_mir(self.def_id);
313 let locals = body.local_decls.clone();
314 let fn_sig = self.tcx.fn_sig(self.def_id).skip_binder();
315 let param_len = fn_sig.inputs().skip_binder().len();
316 for idx in 1..param_len + 1 {
317 let local_ty = locals[Local::from(idx)].ty;
318 self.generate_ptr_with_obj_node(local_ty, idx);
319 }
320 let fact_results = crate::analysis::utils::fn_info::generate_contract_from_annotation(
322 self.tcx,
323 self.def_id,
324 );
325 for (base, fields, contract) in fact_results {
326 if fields.len() == 0 {
327 self.insert_fact_for_arg(base, contract);
328 } else {
329 let mut cur_base = base;
330 let mut field_node = base;
331 for field in fields {
332 field_node = self.insert_field_node(cur_base, field.0, Some(field.1));
333 self.generate_ptr_with_obj_node(field.1, field_node);
335 cur_base = field_node;
336 }
337 self.insert_fact_for_arg(field_node, contract);
338 }
339 }
340 }
341
342 fn insert_fact_for_arg(&mut self, local: usize, contract: PropertyContract<'tcx>) {
344 let node = self.get_var_node_mut(local).unwrap();
345 node.facts.add_contract(contract);
346 }
347
348 pub fn generate_ptr_with_obj_node(&mut self, local_ty: Ty<'tcx>, idx: usize) -> usize {
350 let new_id = self.generate_node_id();
351 if is_ptr(local_ty) {
352 self.get_var_node_mut(idx).unwrap().points_to = Some(new_id);
354 self.get_var_node_mut(idx).unwrap().ots = States::new_unknown();
355 self.insert_node(
357 new_id,
358 Some(get_pointee(local_ty)),
359 idx,
360 None,
361 States::new_unknown(),
362 );
363 self.add_bound_for_obj(new_id, local_ty);
364 } else if is_ref(local_ty) {
365 self.get_var_node_mut(idx).unwrap().points_to = Some(new_id);
367 self.insert_node(
369 new_id,
370 Some(get_pointee(local_ty)),
371 idx,
372 None,
373 States::new(get_pointee(local_ty)),
374 );
375 self.add_bound_for_obj(new_id, local_ty);
376 }
377 new_id
378 }
379
380 fn add_bound_for_obj(&mut self, new_id: usize, local_ty: Ty<'tcx>) {
382 let new_node = self.get_var_node_mut(new_id).unwrap();
383 let new_node_ty = get_pointee(local_ty);
384 let contract = if is_slice(new_node_ty).is_some() {
385 let inner_ty = is_slice(new_node_ty).unwrap();
386 PropertyContract::new_obj_boundary(inner_ty, ContractExpr::new_unknown())
387 } else {
388 PropertyContract::new_obj_boundary(new_node_ty, ContractExpr::new_value(1))
389 };
390 new_node.facts.add_contract(contract);
391 }
392
393 pub fn check_ptr(&mut self, arg: usize) -> usize {
395 if self.get_var_node_mut(arg).unwrap().ty.is_none() {
396 display_hashmap(&self.variables, 1);
397 };
398 let node_ty = self.get_var_node_mut(arg).unwrap().ty.unwrap();
399 if is_ptr(node_ty) || is_ref(node_ty) {
400 return self.generate_ptr_with_obj_node(node_ty, arg);
401 }
402 arg
403 }
404
405 pub fn get_local_ty_by_place(&self, arg: usize) -> Option<Ty<'tcx>> {
407 let body = self.tcx.optimized_mir(self.def_id);
408 let locals = body.local_decls.clone();
409 if arg < locals.len() {
410 return Some(locals[Local::from(arg)].ty);
411 } else {
412 return self.get_var_node(arg).unwrap().ty;
414 }
415 }
416
417 pub fn get_obj_ty_through_chain(&self, arg: usize) -> Option<Ty<'tcx>> {
419 let var = self.get_var_node(arg).unwrap();
420 if let Some(pointed_idx) = var.points_to {
422 self.get_obj_ty_through_chain(pointed_idx)
423 } else {
424 var.ty
425 }
426 }
427
428 pub fn get_point_to_id(&self, arg: usize) -> usize {
430 let var = self.get_var_node(arg).unwrap();
431 if let Some(pointed_idx) = var.points_to {
432 pointed_idx
433 } else {
434 arg
435 }
436 }
437
438 pub fn is_local(&self, node_id: usize) -> bool {
440 self.local_len > node_id
441 }
442}
443
444impl<'tcx> DominatedGraph<'tcx> {
447 pub fn generate_node_id(&self) -> usize {
449 if self.variables.len() == 0 || *self.variables.keys().max().unwrap() < self.local_len {
450 return self.local_len;
451 }
452 *self.variables.keys().max().unwrap() + 1
453 }
454
455 pub fn get_field_node_id(
457 &mut self,
458 local: usize,
459 field_idx: usize,
460 ty: Option<Ty<'tcx>>,
461 ) -> usize {
462 let node = self.get_var_node(local).unwrap();
463 if let Some(alias_local) = node.field.get(&field_idx) {
464 *alias_local
465 } else {
466 self.insert_field_node(local, field_idx, ty)
467 }
468 }
469
470 pub fn insert_field_node(
472 &mut self,
473 local: usize,
474 field_idx: usize,
475 ty: Option<Ty<'tcx>>,
476 ) -> usize {
477 let new_id = self.generate_node_id();
478 self.variables
479 .insert(new_id, VariableNode::new_default(new_id, ty));
480 let mut_node = self.get_var_node_mut(local).unwrap();
481 mut_node.field.insert(field_idx, new_id);
482 return new_id;
483 }
484
485 pub fn find_var_id_with_fields_seq(&mut self, local: usize, fields: &Vec<usize>) -> usize {
487 let mut cur = local;
488 for field in fields.clone() {
489 let mut cur_node = self.get_var_node(cur).unwrap();
490 if let TyKind::Ref(_, _, _) = cur_node.ty.unwrap().kind() {
491 let point_to = self.get_point_to_id(cur);
492 cur_node = self.get_var_node(point_to).unwrap();
493 }
494 if cur_node.field.get(&field).is_some() {
496 cur = *cur_node.field.get(&field).unwrap();
497 continue;
498 }
499 match cur_node.ty.unwrap().kind() {
501 TyKind::Adt(adt_def, substs) => {
502 if adt_def.is_struct() {
503 for (idx, field_def) in adt_def.all_fields().enumerate() {
504 if idx == field {
505 cur = self.get_field_node_id(
506 cur,
507 field,
508 Some(field_def.ty(self.tcx, substs)),
509 );
510 }
511 }
512 }
513 }
514 _ => {
516 rap_warn!("ty {:?}, field: {:?}", cur_node.ty.unwrap(), field);
517 rap_warn!(
518 "set field type as None! --- src: Dominated Graph / find_var_id_with_fields_seq"
519 );
520 cur = self.get_field_node_id(cur, field, None);
521 }
522 }
523 }
524 return cur;
525 }
526
527 pub fn point(&mut self, lv: usize, rv: usize) {
531 if let Some(rv_node) = self.get_var_node_mut(rv) {
535 rv_node.pointed_by.insert(lv);
536 } else {
537 rap_debug!("Graph Point Error: Target node _{} not found", rv);
538 return;
539 }
540
541 let old_points_to = if let Some(lv_node) = self.get_var_node_mut(lv) {
544 let old = lv_node.points_to;
545 lv_node.points_to = Some(rv);
546
547 if let Some(lv_ty) = lv_node.ty
551 && is_ref(lv_ty)
552 {
553 let pointee_ty = get_pointee(lv_ty);
554 lv_node.ots.align = AlignState::Aligned(pointee_ty);
555
556 rap_debug!(
557 "Graph Point: Refined Ref _{} ({:?}) to Aligned via point()",
558 lv,
559 pointee_ty
560 );
561 }
562
563 old
564 } else {
565 None
566 };
567
568 if let Some(to) = old_points_to {
570 if to != rv {
572 if let Some(ori_to_node) = self.get_var_node_mut(to) {
573 ori_to_node.pointed_by.remove(&lv);
574 }
575 }
576 }
577 }
578
579 pub fn get_var_nod_id(&self, local_id: usize) -> usize {
581 self.get_var_node(local_id).unwrap().id
582 }
583
584 pub fn get_map_idx_node(&self, local_id: usize) -> &VariableNode<'tcx> {
586 self.variables.get(&local_id).unwrap()
587 }
588
589 pub fn get_var_node(&self, local_id: usize) -> Option<&VariableNode<'tcx>> {
591 for (_idx, var_node) in &self.variables {
592 if var_node.alias_set.contains(&local_id) {
593 return Some(var_node);
594 }
595 }
596 rap_warn!("def id:{:?}, local_id: {local_id}", self.def_id);
597 display_hashmap(&self.variables, 1);
598 None
599 }
600
601 pub fn get_var_node_mut(&mut self, local_id: usize) -> Option<&mut VariableNode<'tcx>> {
603 let va = self.variables.clone();
604 for (_idx, var_node) in &mut self.variables {
605 if var_node.alias_set.contains(&local_id) {
606 return Some(var_node);
607 }
608 }
609 rap_warn!("def id:{:?}, local_id: {local_id}", self.def_id);
610 display_hashmap(&va, 1);
611 None
612 }
613
614 pub fn merge(&mut self, lv: usize, rv: usize) {
619 let lv_node = self.get_var_node_mut(lv).unwrap().clone();
620 if lv_node.alias_set.contains(&rv) {
621 return;
622 }
623 for lv_pointed_by in lv_node.pointed_by.clone() {
624 self.point(lv_pointed_by, rv);
625 }
626 let lv_node = self.get_var_node_mut(lv).unwrap();
627 lv_node.alias_set.remove(&lv);
628 let lv_ty = lv_node.ty;
629 let rv_node = self.get_var_node_mut(rv).unwrap();
630 rv_node.alias_set.insert(lv);
631 if rv_node.ty.is_none() {
632 rv_node.ty = lv_ty;
633 }
634 }
635
636 pub fn copy_node(&mut self, lv: usize, rv: usize) {
641 let rv_node = self.get_var_node_mut(rv).unwrap().clone();
642 let lv_node = self.get_var_node_mut(lv).unwrap();
643 lv_node.ots = rv_node.ots;
644 lv_node.facts = rv_node.facts;
645 lv_node.is_dropped = rv_node.is_dropped;
646 lv_node.offset_from = rv_node.offset_from;
647 let lv_id = lv_node.id;
648 if rv_node.points_to.is_some() {
649 self.point(lv_id, rv_node.points_to.unwrap());
650 }
651 }
652
653 fn insert_node(
655 &mut self,
656 dv: usize,
657 ty: Option<Ty<'tcx>>,
658 parent_id: usize,
659 child_id: Option<usize>,
660 state: States<'tcx>,
661 ) {
662 self.variables.insert(
663 dv,
664 VariableNode::new(dv, child_id, HashSet::from([parent_id]), ty, state),
665 );
666 }
667
668 pub fn set_drop(&mut self, idx: usize) -> bool {
670 if let Some(ori_node) = self.get_var_node_mut(idx) {
671 if ori_node.is_dropped == true {
672 return false;
674 }
675 ori_node.is_dropped = true;
676 }
677 true
678 }
679
680 pub fn update_value(&mut self, arg: usize, value: usize) {
682 let node = self.get_var_node_mut(arg).unwrap();
683 node.const_value = value;
684 node.ots.init = true;
685 }
686
687 pub fn insert_partial_order(&mut self, p1: usize, p2: usize, op: &BinOp) {
689 let contract = PropertyContract::new_partial_order(p1, p2, *op);
690 let p1_node = self.get_var_node_mut(p1).unwrap();
691 p1_node.facts.add_contract(contract.clone());
692 let p2_node = self.get_var_node_mut(p2).unwrap();
693 p2_node.facts.add_contract(contract);
694 }
695}
696
697impl<'tcx> DominatedGraph<'tcx> {
699 pub fn to_dot_graph(&self) -> String {
701 let mut dot = String::new();
702 writeln!(dot, "digraph DominatedGraph {{").unwrap();
703
704 writeln!(
705 dot,
706 " graph [compound=true, splines=polyline, nodesep=0.5, ranksep=0.5];"
707 )
708 .unwrap();
709 writeln!(dot, " node [shape=plain, fontname=\"Courier\"];").unwrap();
710 writeln!(dot, " edge [fontname=\"Courier\"];").unwrap();
711
712 let mut isolated_nodes = Vec::new();
713 let mut connected_nodes = Vec::new();
714
715 let mut keys: Vec<&usize> = self.variables.keys().collect();
716 keys.sort();
717
718 for id in keys {
719 if let Some(node) = self.variables.get(id) {
720 let is_isolated =
721 node.points_to.is_none() && node.field.is_empty() && node.pointed_by.is_empty();
722
723 if is_isolated {
724 isolated_nodes.push(*id);
725 } else {
726 connected_nodes.push(*id);
727 }
728 }
729 }
730
731 if !isolated_nodes.is_empty() {
732 writeln!(dot, " subgraph cluster_isolated {{").unwrap();
733 writeln!(dot, " label = \"Isolated Variables (Grid Layout)\";").unwrap();
734 writeln!(dot, " style = dashed;").unwrap();
735 writeln!(dot, " color = grey;").unwrap();
736
737 let total_iso = isolated_nodes.len();
738 let cols = (total_iso as f64).sqrt().ceil() as usize;
739
740 for id in &isolated_nodes {
741 if let Some(node) = self.variables.get(id) {
742 let node_label = self.generate_node_label(node);
743 writeln!(dot, " {} [label=<{}>];", id, node_label).unwrap();
744 }
745 }
746
747 for chunk in isolated_nodes.chunks(cols) {
748 write!(dot, " {{ rank=same;").unwrap();
749 for id in chunk {
750 write!(dot, " {};", id).unwrap();
751 }
752 writeln!(dot, " }}").unwrap();
753 }
754
755 for i in 0..isolated_nodes.chunks(cols).len() {
756 let current_row_idx = i * cols;
757 let next_row_idx = (i + 1) * cols;
758
759 if next_row_idx < total_iso {
760 let curr_id = isolated_nodes[current_row_idx];
761 let next_id = isolated_nodes[next_row_idx];
762 writeln!(
763 dot,
764 " {} -> {} [style=invis, weight=100];",
765 curr_id, next_id
766 )
767 .unwrap();
768 }
769 }
770
771 writeln!(dot, " }}").unwrap();
772 }
773
774 if !connected_nodes.is_empty() {
775 writeln!(dot, " subgraph cluster_connected {{").unwrap();
776 writeln!(dot, " label = \"Reference Graph\";").unwrap();
777 writeln!(dot, " color = black;").unwrap();
778
779 for id in &connected_nodes {
780 if let Some(node) = self.variables.get(id) {
781 let node_label = self.generate_node_label(node);
782 writeln!(dot, " {} [label=<{}>];", id, node_label).unwrap();
783 }
784 }
785 writeln!(dot, " }}").unwrap();
786 }
787
788 writeln!(dot, "").unwrap();
790 for id in &connected_nodes {
791 if let Some(node) = self.variables.get(id) {
792 if let Some(target) = node.points_to {
793 writeln!(
794 dot,
795 " {} -> {} [label=\"ptr\", color=\"blue\", fontcolor=\"blue\"];",
796 id, target
797 )
798 .unwrap();
799 }
800
801 let mut field_indices: Vec<&usize> = node.field.keys().collect();
802 field_indices.sort();
803 for field_idx in field_indices {
804 let target_id = node.field.get(field_idx).unwrap();
805 writeln!(
806 dot,
807 " {} -> {} [label=\".{}\", style=\"dashed\"];",
808 id, target_id, field_idx
809 )
810 .unwrap();
811 }
812 }
813 }
814
815 writeln!(dot, "}}").unwrap();
816 dot
817 }
818
819 fn generate_node_label(&self, node: &VariableNode<'tcx>) -> String {
821 let ty_str = match node.ty {
822 Some(t) => format!("{:?}", t),
823 None => "None".to_string(),
824 };
825 let safe_ty = html_escape(&ty_str);
826
827 format!(
828 r#"<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
829 <tr><td colspan="2"><b>ID: {}</b></td></tr>
830 <tr><td align="left">Type:</td><td align="left">{}</td></tr>
831 <tr><td align="left">Const:</td><td align="left">{}</td></tr>
832 </table>"#,
833 node.id, safe_ty, node.const_value,
834 )
835 }
836
837 pub fn display_dominated_graph(&self) {
839 const TABLE_WIDTH: usize = 145; println!(
841 "\n{:=^width$}",
842 " Dominated Graph Report ",
843 width = TABLE_WIDTH
844 );
845
846 let mut sorted_ids: Vec<&usize> = self.variables.keys().collect();
847 sorted_ids.sort();
848
849 if sorted_ids.is_empty() {
850 println!(" [Empty Graph]");
851 println!("{:=^width$}\n", "", width = TABLE_WIDTH);
852 return;
853 }
854
855 let sep = format!(
858 "+{:-^6}+{:-^25}+{:-^8}+{:-^15}+{:-^25}+{:-^40}+",
859 "", "", "", "", "", ""
860 );
861 println!("{}", sep);
862 println!(
863 "| {:^6} | {:^25} | {:^8} | {:^15} | {:^25} | {:^40} |",
864 "ID", "Type", "Pt-To", "Fields", "Offset", "States"
865 );
866 println!("{}", sep);
867
868 for id in sorted_ids {
869 let node = &self.variables[id];
870
871 let ty_str = node
873 .ty
874 .map(|t| format!("{:?}", t))
875 .unwrap_or_else(|| "None".to_string());
876
877 let pt_str = node
879 .points_to
880 .map(|p| format!("_{}", p))
881 .unwrap_or_else(|| "-".to_string());
882
883 let fields_str = if node.field.is_empty() {
885 "-".to_string()
886 } else {
887 let mut fs: Vec<String> = node
888 .field
889 .iter()
890 .map(|(k, v)| format!(".{}->_{}", k, v))
891 .collect();
892 fs.sort(); fs.join(", ")
894 };
895
896 let offset_str = if let Some(def) = &node.offset_from {
898 match def {
899 SymbolicDef::PtrOffset(op, base, idx, _) => {
901 let op_str = match op {
902 BinOp::Add => "+",
903 BinOp::Sub => "-",
904 _ => "?",
905 };
906 let idx_str = match idx {
907 AnaOperand::Local(l) => format!("_{}", l),
908 AnaOperand::Const(c) => format!("{}", c),
909 };
910 format!("_{} {} {}", base, op_str, idx_str)
911 }
912 SymbolicDef::Binary(BinOp::Offset, base, idx) => {
913 let idx_str = match idx {
914 AnaOperand::Local(l) => format!("_{}", l),
915 AnaOperand::Const(c) => format!("{}", c),
916 };
917 format!("_{} + {}", base, idx_str)
918 }
919 _ => format!("{:?}", def),
920 }
921 } else {
922 "-".to_string()
923 };
924
925 let mut states_vec = Vec::new();
927 match &node.ots.align {
929 AlignState::Aligned(ty) => {
930 let node_ty = node.ty.unwrap();
931 if is_ptr(node_ty) || is_ref(node_ty) {
932 states_vec.push(format!("Align({:?})", ty));
933 }
934 }
935 AlignState::Unaligned(ty) => states_vec.push(format!("Unalign({:?})", ty)),
936 AlignState::Unknown => states_vec.push("Unknown".to_string()),
937 }
938 let states_str = if states_vec.is_empty() {
939 "-".to_string()
940 } else {
941 states_vec.join(", ")
942 };
943
944 println!(
946 "| {:<6} | {:<25} | {:<8} | {:<15} | {:<25} | {:<40} |",
947 id,
948 self.safe_truncate_str(&ty_str, 25),
949 pt_str,
950 self.safe_truncate_str(&fields_str, 15),
951 self.safe_truncate_str(&offset_str, 25),
952 self.safe_truncate_str(&states_str, 40)
953 );
954 }
955
956 println!("{}", sep);
957 println!("{:=^width$}\n", " End Graph ", width = TABLE_WIDTH);
958 }
959
960 fn safe_truncate_str(&self, s: &str, max_width: usize) -> String {
962 if s.chars().count() <= max_width {
963 return s.to_string();
964 }
965 let truncated: String = s.chars().take(max_width - 2).collect();
966 format!("{}..", truncated)
967 }
968}
969
970fn html_escape(input: &str) -> String {
972 input
973 .replace("&", "&")
974 .replace("<", "<")
975 .replace(">", ">")
976 .replace("\"", """)
977}
978
979impl<'tcx> DominatedGraph<'tcx> {
980 pub fn update_from_offset_def(
983 &mut self,
984 target_local: usize,
986 base_local: usize,
988 offset_def: SymbolicDef<'tcx>,
990 ) {
991 let base_point_to = self.get_point_to_id(base_local);
994 self.point(target_local, base_point_to);
995
996 if let Some(node) = self.get_var_node_mut(target_local) {
999 node.offset_from = Some(offset_def);
1000
1001 rap_debug!(
1002 "Graph Update: _{} is offset of _{} (via update_from_offset_def)",
1003 target_local,
1004 base_local
1005 );
1006 }
1007 }
1008}