1use crate::{
2 analysis::{
3 senryx::{
4 contracts::{
5 abstract_state::AlignState,
6 property::{CisRangeItem, ContractualInvariantState, PropertyContract},
7 },
8 symbolic_analysis::{AnaOperand, SymbolicDef},
9 },
10 utils::fn_info::{display_hashmap, get_pointee, is_ptr, is_ref, is_slice, reverse_op},
11 },
12 rap_debug, rap_warn,
13};
14use rustc_hir::def_id::DefId;
15use rustc_middle::mir::BinOp;
16use rustc_middle::mir::Local;
17use rustc_middle::ty::TyKind;
18use rustc_middle::ty::{Ty, TyCtxt};
19use serde::de;
20use std::collections::{HashMap, HashSet, VecDeque};
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.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 cis: ContractualInvariantState<'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 cis: ContractualInvariantState::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 cis: ContractualInvariantState::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 cis: ContractualInvariantState::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 {
252 let body = tcx.optimized_mir(def_id);
253 let locals = body.local_decls.clone();
254 let fn_sig = tcx.fn_sig(def_id).skip_binder();
255 let param_len = fn_sig.inputs().skip_binder().len();
256 let mut var_map: HashMap<usize, VariableNode<'_>> = HashMap::new();
257 let mut obj_cnt = 0;
258 for (idx, local) in locals.iter().enumerate() {
259 let local_ty = local.ty;
260 let mut node = VariableNode::new_default(idx, Some(local_ty));
261 if local_ty.to_string().contains("MaybeUninit") {
262 node.ots.init = false;
263 }
264 var_map.insert(idx, node);
265 }
266 Self {
267 tcx,
268 def_id,
269 local_len: locals.len(),
270 variables: var_map,
271 }
272 }
273
274 pub fn init_self_with_inter(&mut self, inter_result: InterResultNode<'tcx>) {
276 let self_node = self.get_var_node(1).unwrap().clone();
277 if self_node.ty.unwrap().is_ref() {
278 let obj_node = self.get_var_node(self.get_point_to_id(1)).unwrap();
279 self.dfs_insert_inter_results(inter_result, obj_node.id);
280 } else {
281 self.dfs_insert_inter_results(inter_result, self_node.id);
282 }
283 }
284
285 pub fn dfs_insert_inter_results(&mut self, inter_result: InterResultNode<'tcx>, local: usize) {
287 let new_id = self.generate_node_id();
288 let node = self.get_var_node_mut(local).unwrap();
289 node.ots = inter_result.states;
291 node.const_value = inter_result.const_value;
292 if inter_result.point_to.is_some() {
293 let new_node = inter_result.point_to.unwrap();
294 node.points_to = Some(new_id);
295 self.insert_node(
296 new_id,
297 new_node.ty.clone(),
298 local,
299 None,
300 new_node.states.clone(),
301 );
302 self.dfs_insert_inter_results(*new_node, new_id);
303 }
304 for (field_idx, field_inter) in inter_result.fields {
305 let field_node_id = self.insert_field_node(local, field_idx, field_inter.ty.clone());
306 self.dfs_insert_inter_results(field_inter, field_node_id);
307 }
308 }
309
310 pub fn init_arg(&mut self) {
312 let body = self.tcx.optimized_mir(self.def_id);
314 let locals = body.local_decls.clone();
315 let fn_sig = self.tcx.fn_sig(self.def_id).skip_binder();
316 let param_len = fn_sig.inputs().skip_binder().len();
317 for idx in 1..param_len + 1 {
318 let local_ty = locals[Local::from(idx)].ty;
319 self.generate_ptr_with_obj_node(local_ty, idx);
320 }
321 let cis_results = crate::analysis::utils::fn_info::generate_contract_from_annotation(
323 self.tcx,
324 self.def_id,
325 );
326 for (base, fields, contract) in cis_results {
327 if fields.len() == 0 {
328 self.insert_cis_for_arg(base, contract);
329 } else {
330 let mut cur_base = base;
331 let mut field_node = base;
332 for field in fields {
333 field_node = self.insert_field_node(cur_base, field.0, Some(field.1));
334 self.generate_ptr_with_obj_node(field.1, field_node);
336 cur_base = field_node;
337 }
338 self.insert_cis_for_arg(field_node, contract);
339 }
340 }
341 }
342
343 fn insert_cis_for_arg(&mut self, local: usize, contract: PropertyContract<'tcx>) {
345 let node = self.get_var_node_mut(local).unwrap();
346 node.cis.add_contract(contract);
347 }
348
349 pub fn generate_ptr_with_obj_node(&mut self, local_ty: Ty<'tcx>, idx: usize) -> usize {
351 let new_id = self.generate_node_id();
352 if is_ptr(local_ty) {
353 self.get_var_node_mut(idx).unwrap().points_to = Some(new_id);
355 self.get_var_node_mut(idx).unwrap().ots = States::new_unknown();
356 self.insert_node(
358 new_id,
359 Some(get_pointee(local_ty)),
360 idx,
361 None,
362 States::new_unknown(),
363 );
364 self.add_bound_for_obj(new_id, local_ty);
365 } else if is_ref(local_ty) {
366 self.get_var_node_mut(idx).unwrap().points_to = Some(new_id);
368 self.insert_node(
370 new_id,
371 Some(get_pointee(local_ty)),
372 idx,
373 None,
374 States::new(get_pointee(local_ty)),
375 );
376 self.add_bound_for_obj(new_id, local_ty);
377 }
378 new_id
379 }
380
381 fn add_bound_for_obj(&mut self, new_id: usize, local_ty: Ty<'tcx>) {
383 let new_node = self.get_var_node_mut(new_id).unwrap();
384 let new_node_ty = get_pointee(local_ty);
385 let contract = if is_slice(new_node_ty).is_some() {
386 let inner_ty = is_slice(new_node_ty).unwrap();
387 PropertyContract::new_obj_boundary(inner_ty, CisRangeItem::new_unknown())
388 } else {
389 PropertyContract::new_obj_boundary(new_node_ty, CisRangeItem::new_value(1))
390 };
391 new_node.cis.add_contract(contract);
392 }
393
394 pub fn check_ptr(&mut self, arg: usize) -> usize {
396 if self.get_var_node_mut(arg).unwrap().ty.is_none() {
397 display_hashmap(&self.variables, 1);
398 };
399 let node_ty = self.get_var_node_mut(arg).unwrap().ty.unwrap();
400 if is_ptr(node_ty) || is_ref(node_ty) {
401 return self.generate_ptr_with_obj_node(node_ty, arg);
402 }
403 arg
404 }
405
406 pub fn get_local_ty_by_place(&self, arg: usize) -> Option<Ty<'tcx>> {
408 let body = self.tcx.optimized_mir(self.def_id);
409 let locals = body.local_decls.clone();
410 if arg < locals.len() {
411 return Some(locals[Local::from(arg)].ty);
412 } else {
413 return self.get_var_node(arg).unwrap().ty;
415 }
416 }
417
418 pub fn get_obj_ty_through_chain(&self, arg: usize) -> Option<Ty<'tcx>> {
420 let var = self.get_var_node(arg).unwrap();
421 if let Some(pointed_idx) = var.points_to {
423 self.get_obj_ty_through_chain(pointed_idx)
424 } else {
425 var.ty
426 }
427 }
428
429 pub fn get_point_to_id(&self, arg: usize) -> usize {
431 let var = self.get_var_node(arg).unwrap();
432 if let Some(pointed_idx) = var.points_to {
433 pointed_idx
434 } else {
435 arg
436 }
437 }
438
439 pub fn is_local(&self, node_id: usize) -> bool {
441 self.local_len > node_id
442 }
443}
444
445impl<'tcx> DominatedGraph<'tcx> {
448 pub fn generate_node_id(&self) -> usize {
450 if self.variables.len() == 0 || *self.variables.keys().max().unwrap() < self.local_len {
451 return self.local_len;
452 }
453 *self.variables.keys().max().unwrap() + 1
454 }
455
456 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(_, ty, _) = 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) {
618 let lv_node = self.get_var_node_mut(lv).unwrap().clone();
619 if lv_node.alias_set.contains(&rv) {
620 return;
621 }
622 for lv_pointed_by in lv_node.pointed_by.clone() {
623 self.point(lv_pointed_by, rv);
624 }
625 let lv_node = self.get_var_node_mut(lv).unwrap();
626 lv_node.alias_set.remove(&lv);
627 let lv_ty = lv_node.ty;
628 let lv_states = lv_node.ots.clone();
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() {
633 rv_node.ty = lv_ty;
634 }
635 }
636
637 pub fn copy_node(&mut self, lv: usize, rv: usize) {
639 let rv_node = self.get_var_node_mut(rv).unwrap().clone();
640 let lv_node = self.get_var_node_mut(lv).unwrap();
641 let lv_ty = lv_node.ty.unwrap();
642 lv_node.ots = rv_node.ots;
643 lv_node.cis = rv_node.cis;
644 lv_node.is_dropped = rv_node.is_dropped;
645 lv_node.offset_from = rv_node.offset_from;
646 let lv_id = lv_node.id;
647 if rv_node.points_to.is_some() {
648 self.point(lv_id, rv_node.points_to.unwrap());
649 }
650 }
651
652 fn break_node_connection(&mut self, lv: usize, rv: usize) {
654 let rv_node = self.get_var_node_mut(rv).unwrap();
655 rv_node.pointed_by.remove(&lv);
656 let lv_node = self.get_var_node_mut(lv).unwrap();
657 lv_node.points_to = None;
658 }
659
660 fn init_self_node(&mut self, self_id: usize, ty: Option<Ty<'tcx>>, state: States<'tcx>) {
662 let node = self.get_var_node_mut(self_id).unwrap();
663 node.ty = ty;
664 node.ots = state;
665 }
666
667 fn insert_node(
669 &mut self,
670 dv: usize,
671 ty: Option<Ty<'tcx>>,
672 parent_id: usize,
673 child_id: Option<usize>,
674 state: States<'tcx>,
675 ) {
676 self.variables.insert(
677 dv,
678 VariableNode::new(dv, child_id, HashSet::from([parent_id]), ty, state),
679 );
680 }
681
682 fn delete_node(&mut self, idx: usize) {
684 let node = self.get_var_node(idx).unwrap().clone();
685 for pre_idx in &node.pointed_by.clone() {
686 let pre_node = self.get_var_node_mut(*pre_idx).unwrap();
687 pre_node.points_to = None;
688 }
689 if let Some(to) = &node.points_to.clone() {
690 let next_node = self.get_var_node_mut(*to).unwrap();
691 next_node.pointed_by.remove(&idx);
692 }
693 self.variables.remove(&idx);
694 }
695
696 pub fn set_drop(&mut self, idx: usize) -> bool {
698 if let Some(ori_node) = self.get_var_node_mut(idx) {
699 if ori_node.is_dropped == true {
700 return false;
702 }
703 ori_node.is_dropped = true;
704 }
705 true
706 }
707
708 pub fn update_value(&mut self, arg: usize, value: usize) {
710 let node = self.get_var_node_mut(arg).unwrap();
711 node.const_value = value;
712 node.ots.init = true;
713 }
714
715 pub fn insert_patial_op(&mut self, p1: usize, p2: usize, op: &BinOp) {
717 let p1_node = self.get_var_node_mut(p1).unwrap();
718 p1_node
719 .cis
720 .add_contract(PropertyContract::new_patial_order(p2, *op));
721 let p2_node = self.get_var_node_mut(p2).unwrap();
722 p2_node
723 .cis
724 .add_contract(PropertyContract::new_patial_order(p1, reverse_op(*op)));
725 }
726}
727
728impl<'tcx> DominatedGraph<'tcx> {
730 pub fn to_dot_graph(&self) -> String {
732 let mut dot = String::new();
733 writeln!(dot, "digraph DominatedGraph {{").unwrap();
734
735 writeln!(
736 dot,
737 " graph [compound=true, splines=polyline, nodesep=0.5, ranksep=0.5];"
738 )
739 .unwrap();
740 writeln!(dot, " node [shape=plain, fontname=\"Courier\"];").unwrap();
741 writeln!(dot, " edge [fontname=\"Courier\"];").unwrap();
742
743 let mut isolated_nodes = Vec::new();
744 let mut connected_nodes = Vec::new();
745
746 let mut keys: Vec<&usize> = self.variables.keys().collect();
747 keys.sort();
748
749 for id in keys {
750 if let Some(node) = self.variables.get(id) {
751 let is_isolated =
752 node.points_to.is_none() && node.field.is_empty() && node.pointed_by.is_empty();
753
754 if is_isolated {
755 isolated_nodes.push(*id);
756 } else {
757 connected_nodes.push(*id);
758 }
759 }
760 }
761
762 if !isolated_nodes.is_empty() {
763 writeln!(dot, " subgraph cluster_isolated {{").unwrap();
764 writeln!(dot, " label = \"Isolated Variables (Grid Layout)\";").unwrap();
765 writeln!(dot, " style = dashed;").unwrap();
766 writeln!(dot, " color = grey;").unwrap();
767
768 let total_iso = isolated_nodes.len();
769 let cols = (total_iso as f64).sqrt().ceil() as usize;
770
771 for id in &isolated_nodes {
772 if let Some(node) = self.variables.get(id) {
773 let node_label = self.generate_node_label(node);
774 writeln!(dot, " {} [label=<{}>];", id, node_label).unwrap();
775 }
776 }
777
778 for chunk in isolated_nodes.chunks(cols) {
779 write!(dot, " {{ rank=same;").unwrap();
780 for id in chunk {
781 write!(dot, " {};", id).unwrap();
782 }
783 writeln!(dot, " }}").unwrap();
784 }
785
786 for i in 0..isolated_nodes.chunks(cols).len() {
787 let current_row_idx = i * cols;
788 let next_row_idx = (i + 1) * cols;
789
790 if next_row_idx < total_iso {
791 let curr_id = isolated_nodes[current_row_idx];
792 let next_id = isolated_nodes[next_row_idx];
793 writeln!(
794 dot,
795 " {} -> {} [style=invis, weight=100];",
796 curr_id, next_id
797 )
798 .unwrap();
799 }
800 }
801
802 writeln!(dot, " }}").unwrap();
803 }
804
805 if !connected_nodes.is_empty() {
806 writeln!(dot, " subgraph cluster_connected {{").unwrap();
807 writeln!(dot, " label = \"Reference Graph\";").unwrap();
808 writeln!(dot, " color = black;").unwrap();
809
810 for id in &connected_nodes {
811 if let Some(node) = self.variables.get(id) {
812 let node_label = self.generate_node_label(node);
813 writeln!(dot, " {} [label=<{}>];", id, node_label).unwrap();
814 }
815 }
816 writeln!(dot, " }}").unwrap();
817 }
818
819 writeln!(dot, "").unwrap();
821 for id in &connected_nodes {
822 if let Some(node) = self.variables.get(id) {
823 if let Some(target) = node.points_to {
824 writeln!(
825 dot,
826 " {} -> {} [label=\"ptr\", color=\"blue\", fontcolor=\"blue\"];",
827 id, target
828 )
829 .unwrap();
830 }
831
832 let mut field_indices: Vec<&usize> = node.field.keys().collect();
833 field_indices.sort();
834 for field_idx in field_indices {
835 let target_id = node.field.get(field_idx).unwrap();
836 writeln!(
837 dot,
838 " {} -> {} [label=\".{}\", style=\"dashed\"];",
839 id, target_id, field_idx
840 )
841 .unwrap();
842 }
843 }
844 }
845
846 writeln!(dot, "}}").unwrap();
847 dot
848 }
849
850 fn generate_node_label(&self, node: &VariableNode<'tcx>) -> String {
852 let ty_str = match node.ty {
853 Some(t) => format!("{:?}", t),
854 None => "None".to_string(),
855 };
856 let safe_ty = html_escape(&ty_str);
857
858 format!(
859 r#"<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
860 <tr><td colspan="2"><b>ID: {}</b></td></tr>
861 <tr><td align="left">Type:</td><td align="left">{}</td></tr>
862 <tr><td align="left">Const:</td><td align="left">{}</td></tr>
863 </table>"#,
864 node.id, safe_ty, node.const_value,
865 )
866 }
867
868 pub fn display_dominated_graph(&self) {
870 const TABLE_WIDTH: usize = 145; println!(
872 "\n{:=^width$}",
873 " Dominated Graph Report ",
874 width = TABLE_WIDTH
875 );
876
877 let mut sorted_ids: Vec<&usize> = self.variables.keys().collect();
878 sorted_ids.sort();
879
880 if sorted_ids.is_empty() {
881 println!(" [Empty Graph]");
882 println!("{:=^width$}\n", "", width = TABLE_WIDTH);
883 return;
884 }
885
886 let sep = format!(
889 "+{:-^6}+{:-^25}+{:-^8}+{:-^15}+{:-^25}+{:-^40}+",
890 "", "", "", "", "", ""
891 );
892 println!("{}", sep);
893 println!(
894 "| {:^6} | {:^25} | {:^8} | {:^15} | {:^25} | {:^40} |",
895 "ID", "Type", "Pt-To", "Fields", "Offset", "States"
896 );
897 println!("{}", sep);
898
899 for id in sorted_ids {
900 let node = &self.variables[id];
901
902 let ty_str = node
904 .ty
905 .map(|t| format!("{:?}", t))
906 .unwrap_or_else(|| "None".to_string());
907
908 let pt_str = node
910 .points_to
911 .map(|p| format!("_{}", p))
912 .unwrap_or_else(|| "-".to_string());
913
914 let fields_str = if node.field.is_empty() {
916 "-".to_string()
917 } else {
918 let mut fs: Vec<String> = node
919 .field
920 .iter()
921 .map(|(k, v)| format!(".{}->_{}", k, v))
922 .collect();
923 fs.sort(); fs.join(", ")
925 };
926
927 let offset_str = if let Some(def) = &node.offset_from {
929 match def {
930 SymbolicDef::PtrOffset(op, base, idx, _) => {
932 let op_str = match op {
933 BinOp::Add => "+",
934 BinOp::Sub => "-",
935 _ => "?",
936 };
937 let idx_str = match idx {
938 AnaOperand::Local(l) => format!("_{}", l),
939 AnaOperand::Const(c) => format!("{}", c),
940 };
941 format!("_{} {} {}", base, op_str, idx_str)
942 }
943 SymbolicDef::Binary(BinOp::Offset, base, idx) => {
944 let idx_str = match idx {
945 AnaOperand::Local(l) => format!("_{}", l),
946 AnaOperand::Const(c) => format!("{}", c),
947 };
948 format!("_{} + {}", base, idx_str)
949 }
950 _ => format!("{:?}", def),
951 }
952 } else {
953 "-".to_string()
954 };
955
956 let mut states_vec = Vec::new();
958 match &node.ots.align {
960 AlignState::Aligned(ty) => {
961 let node_ty = node.ty.unwrap();
962 if is_ptr(node_ty) || is_ref(node_ty) {
963 states_vec.push(format!("Align({:?})", ty));
964 }
965 }
966 AlignState::Unaligned(ty) => states_vec.push(format!("Unalign({:?})", ty)),
967 AlignState::Unknown => states_vec.push("Unknown".to_string()),
968 }
969 let states_str = if states_vec.is_empty() {
970 "-".to_string()
971 } else {
972 states_vec.join(", ")
973 };
974
975 println!(
977 "| {:<6} | {:<25} | {:<8} | {:<15} | {:<25} | {:<40} |",
978 id,
979 self.safe_truncate_str(&ty_str, 25),
980 pt_str,
981 self.safe_truncate_str(&fields_str, 15),
982 self.safe_truncate_str(&offset_str, 25),
983 self.safe_truncate_str(&states_str, 40)
984 );
985 }
986
987 println!("{}", sep);
988 println!("{:=^width$}\n", " End Graph ", width = TABLE_WIDTH);
989 }
990
991 fn safe_truncate_str(&self, s: &str, max_width: usize) -> String {
993 if s.chars().count() <= max_width {
994 return s.to_string();
995 }
996 let truncated: String = s.chars().take(max_width - 2).collect();
997 format!("{}..", truncated)
998 }
999}
1000
1001fn html_escape(input: &str) -> String {
1003 input
1004 .replace("&", "&")
1005 .replace("<", "<")
1006 .replace(">", ">")
1007 .replace("\"", """)
1008}
1009
1010impl<'tcx> DominatedGraph<'tcx> {
1011 pub fn update_from_offset_def(
1014 &mut self,
1015 target_local: usize,
1017 base_local: usize,
1019 offset_def: SymbolicDef<'tcx>,
1021 ) {
1022 let base_point_to = self.get_point_to_id(base_local);
1025 self.point(target_local, base_point_to);
1026
1027 if let Some(node) = self.get_var_node_mut(target_local) {
1030 node.offset_from = Some(offset_def);
1031
1032 rap_debug!(
1033 "Graph Update: _{} is offset of _{} (via update_from_offset_def)",
1034 target_local,
1035 base_local
1036 );
1037 }
1038 }
1039}