lambda-graphs 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. lambda_graphs/__init__.py +18 -0
  2. lambda_graphs/__main__.py +7 -0
  3. lambda_graphs/cli.py +183 -0
  4. lambda_graphs/codeviews/AST/AST.py +120 -0
  5. lambda_graphs/codeviews/AST/AST_driver.py +37 -0
  6. lambda_graphs/codeviews/AST/__init__.py +0 -0
  7. lambda_graphs/codeviews/CFG/CFG.py +42 -0
  8. lambda_graphs/codeviews/CFG/CFG_c.py +1182 -0
  9. lambda_graphs/codeviews/CFG/CFG_cpp.py +5604 -0
  10. lambda_graphs/codeviews/CFG/CFG_driver.py +45 -0
  11. lambda_graphs/codeviews/CFG/CFG_java.py +1722 -0
  12. lambda_graphs/codeviews/CFG/__init__.py +0 -0
  13. lambda_graphs/codeviews/CST/CST_driver.py +70 -0
  14. lambda_graphs/codeviews/CST/__init__.py +0 -0
  15. lambda_graphs/codeviews/DFG/DFG_driver.py +42 -0
  16. lambda_graphs/codeviews/DFG/__init__.py +0 -0
  17. lambda_graphs/codeviews/SDFG/SDFG.py +135 -0
  18. lambda_graphs/codeviews/SDFG/SDFG_c.py +2041 -0
  19. lambda_graphs/codeviews/SDFG/SDFG_cpp.py +4030 -0
  20. lambda_graphs/codeviews/SDFG/SDFG_java.py +1618 -0
  21. lambda_graphs/codeviews/SDFG/__init__.py +0 -0
  22. lambda_graphs/codeviews/__init__.py +0 -0
  23. lambda_graphs/codeviews/combined_graph/__init__.py +0 -0
  24. lambda_graphs/codeviews/combined_graph/combined_driver.py +163 -0
  25. lambda_graphs/tree_parser/__init__.py +0 -0
  26. lambda_graphs/tree_parser/c_parser.py +565 -0
  27. lambda_graphs/tree_parser/cpp_parser.py +424 -0
  28. lambda_graphs/tree_parser/custom_parser.py +66 -0
  29. lambda_graphs/tree_parser/java_parser.py +254 -0
  30. lambda_graphs/tree_parser/parser_driver.py +54 -0
  31. lambda_graphs/utils/DFG_utils.py +44 -0
  32. lambda_graphs/utils/__init__.py +0 -0
  33. lambda_graphs/utils/c_nodes.py +431 -0
  34. lambda_graphs/utils/cpp_nodes.py +1331 -0
  35. lambda_graphs/utils/java_nodes.py +756 -0
  36. lambda_graphs/utils/multi_file_merger.py +444 -0
  37. lambda_graphs/utils/postprocessor.py +83 -0
  38. lambda_graphs/utils/preprocessor.py +68 -0
  39. lambda_graphs/utils/src_parser.py +23 -0
  40. lambda_graphs-0.1.4.dist-info/METADATA +380 -0
  41. lambda_graphs-0.1.4.dist-info/RECORD +45 -0
  42. lambda_graphs-0.1.4.dist-info/WHEEL +5 -0
  43. lambda_graphs-0.1.4.dist-info/entry_points.txt +2 -0
  44. lambda_graphs-0.1.4.dist-info/licenses/LICENSE +21 -0
  45. lambda_graphs-0.1.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2041 @@
1
+ import copy
2
+ import time
3
+ from collections import defaultdict
4
+
5
+ import networkx as nx
6
+ from deepdiff import DeepDiff
7
+ from loguru import logger
8
+
9
+ from ...utils.c_nodes import statement_types
10
+ from ...utils.src_parser import traverse_tree
11
+
12
+ assignment = ["assignment_expression"]
13
+ def_statement = ["init_declarator"]
14
+ declaration_statement = ["declaration"]
15
+ increment_statement = ["update_expression"]
16
+ variable_type = ["identifier"]
17
+ function_calls = ["call_expression"]
18
+ literal_types = [
19
+ "number_literal",
20
+ "string_literal",
21
+ "char_literal",
22
+ "true",
23
+ "false",
24
+ "null",
25
+ ]
26
+
27
+ input_functions = [
28
+ "scanf",
29
+ "gets",
30
+ "fgets",
31
+ "getline",
32
+ "fscanf",
33
+ "sscanf",
34
+ "fread",
35
+ "read",
36
+ "recv",
37
+ "recvfrom",
38
+ "getchar",
39
+ "fgetc",
40
+ ]
41
+
42
+ inner_types = ["declaration", "expression_statement"]
43
+ handled_types = (
44
+ assignment
45
+ + def_statement
46
+ + increment_statement
47
+ + function_calls
48
+ + ["function_definition", "return_statement", "for_statement", "switch_statement"]
49
+ )
50
+
51
+ debug = False
52
+
53
+
54
+ def st(child):
55
+ """Get text from AST node"""
56
+ if child is None:
57
+ return ""
58
+ else:
59
+ return child.text.decode()
60
+
61
+
62
+ def get_index(node, index):
63
+ """Get unique index for AST node"""
64
+ try:
65
+ return index[(node.start_point, node.end_point, node.type)]
66
+ except:
67
+ return None
68
+
69
+
70
+ def read_index(index, idx):
71
+ """Get AST node key from index value"""
72
+ return list(index.keys())[list(index.values()).index(idx)]
73
+
74
+
75
+ def scope_check(parent_scope, child_scope):
76
+ """
77
+ Check if a definition's scope can reach a use's scope.
78
+
79
+ A definition at scope [0, 1, 2] can reach uses at:
80
+ - [0, 1, 2] (same scope)
81
+ - [0, 1, 2, 3] (nested scope)
82
+
83
+ But NOT:
84
+ - [0, 1] (parent scope)
85
+ - [0, 1, 3] (sibling scope)
86
+ """
87
+ for p in parent_scope:
88
+ if p not in child_scope:
89
+ return False
90
+ return True
91
+
92
+
93
+ def set_add(lst, item):
94
+ """Add item to list if not already present (set-like behavior)"""
95
+ for entry in lst:
96
+ if item == entry:
97
+ return
98
+ lst.append(item)
99
+
100
+
101
+ def set_union(first_list, second_list):
102
+ """Union of two lists (set-like)"""
103
+ resulting_list = list(first_list)
104
+ resulting_list.extend(x for x in second_list if x not in resulting_list)
105
+ return resulting_list
106
+
107
+
108
+ def set_difference(first_list, second_list):
109
+ """Difference of two lists (set-like)"""
110
+ return [item for item in first_list if item not in second_list]
111
+
112
+
113
+ def return_first_parent_of_types(node, parent_types, stop_types=None):
114
+ """Find first parent of given types"""
115
+ if stop_types is None:
116
+ stop_types = []
117
+
118
+ if node.type in parent_types:
119
+ return node
120
+
121
+ while node.parent is not None:
122
+ if node.type in stop_types:
123
+ return None
124
+ if node.parent.type in parent_types:
125
+ return node.parent
126
+ node = node.parent
127
+
128
+ return None
129
+
130
+
131
+ def recursively_get_children_of_types(
132
+ node, st_types, check_list=None, index=None, result=None, stop_types=None
133
+ ):
134
+ """Recursively find child nodes of given types"""
135
+ if isinstance(st_types, str):
136
+ st_types = [st_types]
137
+ if stop_types is None:
138
+ stop_types = []
139
+ if result is None:
140
+ result = []
141
+
142
+ if node.type in stop_types:
143
+ return result
144
+
145
+ if check_list and index:
146
+ result.extend(
147
+ [
148
+ child
149
+ for child in node.children
150
+ if child.type in st_types and get_index(child, index) in check_list
151
+ ]
152
+ )
153
+ else:
154
+ result.extend([child for child in node.children if child.type in st_types])
155
+
156
+ for child in node.named_children:
157
+ if child.type not in stop_types:
158
+ result = recursively_get_children_of_types(
159
+ child,
160
+ st_types,
161
+ result=result,
162
+ stop_types=stop_types,
163
+ index=index,
164
+ check_list=check_list,
165
+ )
166
+
167
+ return result
168
+
169
+
170
+ class Identifier:
171
+ """Represents a variable at a specific line with scope information"""
172
+
173
+ def __init__(self, parser, node, line=None, declaration=False, full_ref=None):
174
+ self.core = st(node)
175
+ self.unresolved_name = st(full_ref) if full_ref else st(node)
176
+ self.name = self._resolve_name(node, full_ref, parser)
177
+ self.line = line
178
+ self.declaration = declaration
179
+ self.satisfied = False
180
+
181
+ variable_index = get_index(node, parser.index)
182
+ if variable_index and variable_index in parser.symbol_table["scope_map"]:
183
+ self.variable_scope = parser.symbol_table["scope_map"][variable_index]
184
+ if variable_index in parser.declaration_map:
185
+ decl_index = parser.declaration_map[variable_index]
186
+ self.scope = parser.symbol_table["scope_map"].get(decl_index, [0])
187
+ else:
188
+ self.scope = [0]
189
+
190
+ if declaration:
191
+ self.scope = self.variable_scope
192
+ else:
193
+ self.variable_scope = [0]
194
+ self.scope = [0]
195
+
196
+ if line is not None:
197
+ self.real_line_no = read_index(parser.index, line)[0][0]
198
+
199
+ def _resolve_name(self, node, full_ref, parser):
200
+ """Resolve identifier name for C"""
201
+ if full_ref is None:
202
+ return st(node)
203
+
204
+ if full_ref.type == "field_expression":
205
+ obj = full_ref.child_by_field_name("argument")
206
+ field = full_ref.child_by_field_name("field")
207
+ return st(obj) + "." + st(field)
208
+
209
+ if full_ref.type == "pointer_expression":
210
+ arg = full_ref.child_by_field_name("argument")
211
+ return "*" + st(arg)
212
+
213
+ if full_ref.type == "subscript_expression":
214
+ arg = full_ref.child_by_field_name("argument")
215
+ return st(arg)
216
+
217
+ return st(node)
218
+
219
+ def __eq__(self, other):
220
+ return (
221
+ self.name == other.name
222
+ and self.line == other.line
223
+ and sorted(self.scope) == sorted(other.scope)
224
+ )
225
+
226
+ def __hash__(self):
227
+ return hash((self.name, self.line, str(self.scope)))
228
+
229
+ def __str__(self):
230
+ result = [self.name]
231
+ if self.line:
232
+ result += [str(self.real_line_no)]
233
+ result += ["|".join(map(str, self.scope))]
234
+ else:
235
+ result += ["?"]
236
+ return f"{{{','.join(result)}}}"
237
+
238
+
239
+ class Literal:
240
+ """Represents a literal constant (number, string, etc.) as a data flow source"""
241
+
242
+ def __init__(self, parser, node, line=None):
243
+ self.core = st(node)
244
+ self.name = f"LITERAL_{st(node)}"
245
+ self.value = st(node)
246
+ self.line = line
247
+ self.declaration = True
248
+ self.satisfied = False
249
+ self.scope = [0]
250
+ self.variable_scope = [0]
251
+
252
+ if line is not None:
253
+ self.real_line_no = read_index(parser.index, line)[0][0]
254
+
255
+ def __eq__(self, other):
256
+ return self.name == other.name and self.line == other.line
257
+
258
+ def __hash__(self):
259
+ return hash((self.name, self.line))
260
+
261
+ def __str__(self):
262
+ result = [f"Literal({self.value})"]
263
+ if self.line:
264
+ result += [str(self.real_line_no)]
265
+ else:
266
+ result += ["?"]
267
+ return f"{{{','.join(result)}}}"
268
+
269
+
270
+ def extract_identifier_from_declarator(declarator_node):
271
+ """Extract identifier from declarator (may be wrapped in pointer/array)"""
272
+ if declarator_node.type == "identifier":
273
+ return declarator_node
274
+ elif declarator_node.type == "pointer_declarator":
275
+ for child in declarator_node.children:
276
+ if child.type in ["identifier", "pointer_declarator", "array_declarator"]:
277
+ return extract_identifier_from_declarator(child)
278
+ elif declarator_node.type == "array_declarator":
279
+ if declarator_node.children:
280
+ return extract_identifier_from_declarator(declarator_node.children[0])
281
+ elif declarator_node.type == "function_declarator":
282
+ for child in declarator_node.children:
283
+ if child.type in [
284
+ "identifier",
285
+ "pointer_declarator",
286
+ "parenthesized_declarator",
287
+ ]:
288
+ return extract_identifier_from_declarator(child)
289
+ elif declarator_node.type == "parenthesized_declarator":
290
+ for child in declarator_node.children:
291
+ if child.type in ["identifier", "pointer_declarator", "array_declarator"]:
292
+ return extract_identifier_from_declarator(child)
293
+ return None
294
+
295
+
296
+ def extract_param_identifier(param_node):
297
+ """Extract identifier from parameter_declaration"""
298
+ for child in param_node.children:
299
+ if child.type == "identifier":
300
+ return child
301
+ elif child.type in [
302
+ "pointer_declarator",
303
+ "array_declarator",
304
+ "function_declarator",
305
+ ]:
306
+ return extract_identifier_from_declarator(child)
307
+ return None
308
+
309
+
310
+ def extract_operator_text(assign_node, left_node, right_node):
311
+ """Extract operator from assignment (=, +=, -=, etc.)"""
312
+ left_text = left_node.text
313
+ right_text = right_node.text
314
+ operator_bytes = (
315
+ assign_node.text.split(left_text, 1)[-1].rsplit(right_text, 1)[0].strip()
316
+ )
317
+ return operator_bytes.decode()
318
+
319
+
320
+ def add_entry(
321
+ parser,
322
+ rda_table,
323
+ statement_id,
324
+ used=None,
325
+ defined=None,
326
+ declaration=False,
327
+ core=None,
328
+ ):
329
+ """
330
+ Add variable USE or DEF to RDA table.
331
+
332
+ Args:
333
+ parser: C parser
334
+ rda_table: RDA table
335
+ statement_id: Statement where this occurs
336
+ used: Variable being used (read)
337
+ defined: Variable being defined (written)
338
+ declaration: True if declaration
339
+ core: Full reference node
340
+ """
341
+ if statement_id not in rda_table:
342
+ rda_table[statement_id] = defaultdict(list)
343
+
344
+ if not used and not defined:
345
+ return
346
+
347
+ current_node = used or defined
348
+ if core is None:
349
+ core = current_node
350
+
351
+ if current_node.type in literal_types:
352
+ if used:
353
+ set_add(
354
+ rda_table[statement_id]["use"],
355
+ Literal(parser, current_node, statement_id),
356
+ )
357
+ return
358
+
359
+ if current_node.type == "field_expression":
360
+ if defined is not None:
361
+ set_add(
362
+ rda_table[statement_id]["def"],
363
+ Identifier(
364
+ parser,
365
+ current_node.child_by_field_name("argument"),
366
+ statement_id,
367
+ full_ref=None,
368
+ declaration=declaration,
369
+ ),
370
+ )
371
+ else:
372
+ set_add(
373
+ rda_table[statement_id]["use"],
374
+ Identifier(
375
+ parser, current_node.child_by_field_name("argument"), full_ref=None
376
+ ),
377
+ )
378
+ return
379
+
380
+ if used and used.type == "unary_expression":
381
+ operator = None
382
+ argument = None
383
+ for child in used.children:
384
+ if child.type == "&":
385
+ operator = child
386
+ elif child.is_named:
387
+ argument = child
388
+
389
+ if operator is not None and argument is not None:
390
+ if argument.type in variable_type:
391
+ arg_index = get_index(argument, parser.index)
392
+ if arg_index and arg_index in parser.symbol_table["scope_map"]:
393
+ set_add(
394
+ rda_table[statement_id]["use"],
395
+ Identifier(parser, argument, full_ref=argument),
396
+ )
397
+ elif arg_index and arg_index in parser.method_map:
398
+ set_add(
399
+ rda_table[statement_id]["use"],
400
+ Identifier(parser, argument, full_ref=argument),
401
+ )
402
+ return
403
+
404
+ if current_node.type == "pointer_expression":
405
+ pointer = current_node.child_by_field_name("argument")
406
+
407
+ if defined is not None:
408
+ pointer_index = get_index(pointer, parser.index)
409
+ if pointer_index and pointer_index in parser.symbol_table["scope_map"]:
410
+ set_add(
411
+ rda_table[statement_id]["use"],
412
+ Identifier(parser, pointer, full_ref=pointer),
413
+ )
414
+
415
+ set_add(
416
+ rda_table[statement_id]["def"],
417
+ Identifier(
418
+ parser,
419
+ pointer,
420
+ statement_id,
421
+ full_ref=core,
422
+ declaration=declaration,
423
+ ),
424
+ )
425
+ else:
426
+ set_add(
427
+ rda_table[statement_id]["use"],
428
+ Identifier(parser, pointer, full_ref=core),
429
+ )
430
+ return
431
+
432
+ if current_node.type == "subscript_expression":
433
+ array = current_node.child_by_field_name("argument")
434
+ index_expr = current_node.child_by_field_name("index")
435
+
436
+ if defined is not None:
437
+ set_add(
438
+ rda_table[statement_id]["def"],
439
+ Identifier(
440
+ parser, array, statement_id, full_ref=core, declaration=declaration
441
+ ),
442
+ )
443
+ set_add(
444
+ rda_table[statement_id]["use"], Identifier(parser, array, full_ref=core)
445
+ )
446
+
447
+ if index_expr:
448
+ if index_expr.type in variable_type:
449
+ index_id = get_index(index_expr, parser.index)
450
+ if index_id and index_id in parser.symbol_table["scope_map"]:
451
+ set_add(
452
+ rda_table[statement_id]["use"],
453
+ Identifier(parser, index_expr, full_ref=index_expr),
454
+ )
455
+ elif index_expr.type in literal_types:
456
+ set_add(
457
+ rda_table[statement_id]["use"],
458
+ Literal(parser, index_expr, statement_id),
459
+ )
460
+ else:
461
+ identifiers_in_index = recursively_get_children_of_types(
462
+ index_expr,
463
+ variable_type + ["field_expression"],
464
+ index=parser.index,
465
+ check_list=parser.symbol_table["scope_map"],
466
+ )
467
+ for identifier in identifiers_in_index:
468
+ set_add(
469
+ rda_table[statement_id]["use"],
470
+ Identifier(parser, identifier, full_ref=identifier),
471
+ )
472
+ literals_in_index = recursively_get_children_of_types(
473
+ index_expr, literal_types, index=parser.index
474
+ )
475
+ for literal in literals_in_index:
476
+ set_add(
477
+ rda_table[statement_id]["use"],
478
+ Literal(parser, literal, statement_id),
479
+ )
480
+ return
481
+
482
+ node_index = get_index(current_node, parser.index)
483
+ if node_index is None or node_index not in parser.symbol_table["scope_map"]:
484
+ return
485
+
486
+ if defined is not None:
487
+ set_add(
488
+ rda_table[statement_id]["def"],
489
+ Identifier(
490
+ parser, defined, statement_id, full_ref=core, declaration=declaration
491
+ ),
492
+ )
493
+ else:
494
+ set_add(
495
+ rda_table[statement_id]["use"],
496
+ Identifier(parser, used, full_ref=core, declaration=declaration),
497
+ )
498
+
499
+
500
+ def is_return_value_used(call_expr_statement):
501
+ """
502
+ Check if a function call's return value is actually used.
503
+
504
+ Args:
505
+ call_expr_statement: The expression_statement or parent node containing the call
506
+
507
+ Returns:
508
+ True if return value is used (assigned, passed as argument, returned, in conditional)
509
+ False if return value is discarded (standalone statement like fn();)
510
+ """
511
+ if call_expr_statement.type == "expression_statement":
512
+ if len(call_expr_statement.named_children) == 1:
513
+ child = call_expr_statement.named_children[0]
514
+ if child.type == "call_expression":
515
+ return False
516
+
517
+ parent = call_expr_statement.parent
518
+ while parent:
519
+ parent_type = parent.type
520
+
521
+ if parent_type in ["init_declarator", "assignment_expression"]:
522
+ return True
523
+
524
+ if parent_type == "return_statement":
525
+ return True
526
+
527
+ if parent_type == "argument_list":
528
+ return True
529
+
530
+ if parent_type in [
531
+ "if_statement",
532
+ "while_statement",
533
+ "for_statement",
534
+ "do_while_statement",
535
+ "switch_statement",
536
+ ]:
537
+ return True
538
+
539
+ if parent_type == "expression_statement":
540
+ return False
541
+
542
+ parent = parent.parent
543
+
544
+ return False
545
+
546
+
547
+ def collect_function_metadata(parser):
548
+ """
549
+ Collect metadata about all functions in the program.
550
+
551
+ For each function, track:
552
+ - Function name
553
+ - Parameter names and whether they're pointers
554
+ - Parameter indices
555
+
556
+ Returns:
557
+ Dict mapping function_name -> {
558
+ "params": [(param_name, is_pointer, param_index), ...],
559
+ "node": function_definition AST node
560
+ }
561
+ """
562
+ metadata = {}
563
+
564
+ for node in traverse_tree(parser.tree.root_node):
565
+ if node.type == "function_definition":
566
+ func_name = None
567
+ params = []
568
+
569
+ for child in node.named_children:
570
+ if child.type == "function_declarator":
571
+ declarator = child.child_by_field_name("declarator")
572
+ if declarator:
573
+ if declarator.type == "identifier":
574
+ func_name = st(declarator)
575
+ elif declarator.type == "pointer_declarator":
576
+ inner = declarator
577
+ while inner and inner.type == "pointer_declarator":
578
+ inner_declarator = inner.child_by_field_name(
579
+ "declarator"
580
+ )
581
+ if inner_declarator:
582
+ inner = inner_declarator
583
+ else:
584
+ break
585
+ if inner and inner.type == "identifier":
586
+ func_name = st(inner)
587
+
588
+ param_list = child.child_by_field_name("parameters")
589
+ if param_list:
590
+ param_idx = 0
591
+ for param in param_list.named_children:
592
+ if param.type == "parameter_declaration":
593
+ param_name = None
594
+ is_pointer = False
595
+
596
+ for p_child in param.named_children:
597
+ if p_child.type == "pointer_declarator":
598
+ is_pointer = True
599
+ inner = p_child
600
+ while inner:
601
+ if inner.type == "identifier":
602
+ param_name = st(inner)
603
+ break
604
+ elif inner.type == "pointer_declarator":
605
+ inner_decl = inner.child_by_field_name(
606
+ "declarator"
607
+ )
608
+ if inner_decl:
609
+ inner = inner_decl
610
+ else:
611
+ break
612
+ else:
613
+ break
614
+ elif p_child.type == "array_declarator":
615
+ is_pointer = True
616
+ param_name = st(
617
+ extract_identifier_from_declarator(p_child)
618
+ )
619
+ elif p_child.type == "identifier":
620
+ if param_name is None:
621
+ param_name = st(p_child)
622
+
623
+ if param_name:
624
+ params.append((param_name, is_pointer, param_idx))
625
+ param_idx += 1
626
+ break
627
+
628
+ if func_name:
629
+ metadata[func_name] = {"params": params, "node": node}
630
+
631
+ return metadata
632
+
633
+
634
+ def analyze_pointer_modifications(parser, function_metadata):
635
+ """
636
+ Analyze which pointer parameters are modified within each function.
637
+
638
+ A pointer parameter is considered "modified" if:
639
+ - It's dereferenced on the left side of an assignment: *param = ...
640
+ - Used in pointer arithmetic with assignment: param[i] = ...
641
+ - Passed to another function that modifies it (transitive)
642
+
643
+ Args:
644
+ parser: Parser with AST
645
+ function_metadata: Dict from collect_function_metadata()
646
+
647
+ Returns:
648
+ Dict mapping function_name -> set of parameter indices that are modified
649
+ """
650
+ modifications = {}
651
+
652
+ for func_name, meta in function_metadata.items():
653
+ modified_params = set()
654
+ func_node = meta["node"]
655
+
656
+ param_name_to_idx = {}
657
+ for param_name, is_pointer, param_idx in meta["params"]:
658
+ if is_pointer:
659
+ param_name_to_idx[param_name] = param_idx
660
+
661
+ if not param_name_to_idx:
662
+ modifications[func_name] = modified_params
663
+ continue
664
+
665
+ for node in traverse_tree(func_node):
666
+ if node.type == "assignment_expression":
667
+ left = node.child_by_field_name("left")
668
+ if left:
669
+ if left.type == "pointer_expression":
670
+ arg = left.child_by_field_name("argument")
671
+ if arg and arg.type == "identifier":
672
+ var_name = st(arg)
673
+ if var_name in param_name_to_idx:
674
+ modified_params.add(param_name_to_idx[var_name])
675
+
676
+ elif left.type == "subscript_expression":
677
+ array_arg = left.child_by_field_name("argument")
678
+ if array_arg and array_arg.type == "identifier":
679
+ var_name = st(array_arg)
680
+ if var_name in param_name_to_idx:
681
+ modified_params.add(param_name_to_idx[var_name])
682
+
683
+ elif left.type == "field_expression":
684
+ obj = left.child_by_field_name("argument")
685
+ if obj and obj.type == "identifier":
686
+ var_name = st(obj)
687
+ if var_name in param_name_to_idx:
688
+ modified_params.add(param_name_to_idx[var_name])
689
+
690
+ elif node.type == "update_expression":
691
+ arg = node.child_by_field_name("argument")
692
+ if arg:
693
+ if arg.type == "parenthesized_expression":
694
+ inner_children = [c for c in arg.named_children if c.is_named]
695
+ if inner_children:
696
+ arg = inner_children[0]
697
+
698
+ if arg.type == "pointer_expression":
699
+ inner_arg = arg.child_by_field_name("argument")
700
+ if inner_arg and inner_arg.type == "identifier":
701
+ var_name = st(inner_arg)
702
+ if var_name in param_name_to_idx:
703
+ modified_params.add(param_name_to_idx[var_name])
704
+ elif arg.type == "subscript_expression":
705
+ array_arg = arg.child_by_field_name("argument")
706
+ if array_arg and array_arg.type == "identifier":
707
+ var_name = st(array_arg)
708
+ if var_name in param_name_to_idx:
709
+ modified_params.add(param_name_to_idx[var_name])
710
+ elif arg.type == "field_expression":
711
+ obj = arg.child_by_field_name("argument")
712
+ if obj and obj.type == "identifier":
713
+ var_name = st(obj)
714
+ if var_name in param_name_to_idx:
715
+ modified_params.add(param_name_to_idx[var_name])
716
+
717
+ modifications[func_name] = modified_params
718
+
719
+ return modifications
720
+
721
+
722
+ def build_rda_table(
723
+ parser, CFG_results, function_metadata=None, pointer_modifications=None
724
+ ):
725
+ """
726
+ Build RDA table by traversing AST and tracking DEF/USE.
727
+
728
+ Follows Java SDFG pattern: traverse tree stopping at identifiers,
729
+ check each node's type to determine DEF/USE relationships.
730
+
731
+ Returns:
732
+ rda_table: Dict mapping statement_id to {"def": set, "use": set}
733
+ """
734
+ rda_table = {}
735
+ index = parser.index
736
+ tree = parser.tree
737
+
738
+ inner_types = ["parenthesized_expression", "binary_expression", "unary_expression"]
739
+ handled_cases = ["compound_statement", "translation_unit"]
740
+
741
+ for root_node in traverse_tree(tree, variable_type):
742
+ if not root_node.is_named:
743
+ continue
744
+
745
+ if root_node.type == "return_statement":
746
+ parent_id = get_index(root_node, index)
747
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
748
+ continue
749
+
750
+ return_expr = (
751
+ root_node.named_children[0] if root_node.named_children else None
752
+ )
753
+ if (
754
+ return_expr
755
+ and return_expr.type
756
+ in variable_type
757
+ + ["field_expression", "pointer_expression", "subscript_expression"]
758
+ + literal_types
759
+ ):
760
+ add_entry(parser, rda_table, parent_id, used=return_expr)
761
+ else:
762
+ vars_used = recursively_get_children_of_types(
763
+ root_node,
764
+ variable_type
765
+ + [
766
+ "field_expression",
767
+ "pointer_expression",
768
+ "subscript_expression",
769
+ ],
770
+ index=parser.index,
771
+ check_list=parser.symbol_table["scope_map"],
772
+ )
773
+ for var in vars_used:
774
+ add_entry(parser, rda_table, parent_id, used=var)
775
+
776
+ literals_used = recursively_get_children_of_types(
777
+ root_node, literal_types, index=parser.index
778
+ )
779
+ for literal in literals_used:
780
+ add_entry(parser, rda_table, parent_id, used=literal)
781
+
782
+ elif root_node.type in def_statement:
783
+ parent_statement = return_first_parent_of_types(
784
+ root_node, statement_types["node_list_type"]
785
+ )
786
+ if parent_statement is None:
787
+ continue
788
+
789
+ parent_id = get_index(parent_statement, index)
790
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
791
+ if parent_statement and parent_statement.type in inner_types:
792
+ parent_statement = return_first_parent_of_types(
793
+ parent_statement, statement_types["node_list_type"]
794
+ )
795
+ parent_id = get_index(parent_statement, index)
796
+ elif parent_statement.type in handled_cases:
797
+ continue
798
+ else:
799
+ continue
800
+
801
+ declarator = root_node.child_by_field_name("declarator")
802
+ if declarator is None and len(root_node.children) > 0:
803
+ declarator = root_node.children[0]
804
+
805
+ var_identifier = extract_identifier_from_declarator(declarator)
806
+
807
+ if var_identifier:
808
+ add_entry(
809
+ parser,
810
+ rda_table,
811
+ parent_id,
812
+ defined=var_identifier,
813
+ declaration=True,
814
+ )
815
+
816
+ initializer = root_node.child_by_field_name("value")
817
+ if initializer:
818
+ if (
819
+ initializer.type
820
+ in variable_type
821
+ + [
822
+ "field_expression",
823
+ "pointer_expression",
824
+ "subscript_expression",
825
+ "unary_expression",
826
+ ]
827
+ + literal_types
828
+ ):
829
+ add_entry(parser, rda_table, parent_id, used=initializer)
830
+ else:
831
+ vars_used = recursively_get_children_of_types(
832
+ initializer,
833
+ variable_type + ["field_expression"],
834
+ index=parser.index,
835
+ check_list=parser.symbol_table["scope_map"],
836
+ )
837
+ for var in vars_used:
838
+ add_entry(parser, rda_table, parent_id, used=var)
839
+ literals_used = recursively_get_children_of_types(
840
+ initializer, literal_types, index=parser.index
841
+ )
842
+ for literal in literals_used:
843
+ add_entry(parser, rda_table, parent_id, used=literal)
844
+
845
+ elif root_node.type in declaration_statement:
846
+ parent_id = get_index(root_node, index)
847
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
848
+ continue
849
+
850
+ has_init_declarator = any(
851
+ child.type == "init_declarator" for child in root_node.named_children
852
+ )
853
+ if has_init_declarator:
854
+ continue
855
+
856
+ for child in root_node.named_children:
857
+ if child.type == "identifier":
858
+ child_id = get_index(child, index)
859
+ if child_id:
860
+ from collections import defaultdict
861
+
862
+ if parent_id not in rda_table:
863
+ rda_table[parent_id] = defaultdict(list)
864
+ ident = Identifier(parser, child, parent_id, declaration=True)
865
+ ident.scope = [0]
866
+ ident.variable_scope = [0]
867
+ set_add(rda_table[parent_id]["def"], ident)
868
+ elif child.type in [
869
+ "pointer_declarator",
870
+ "array_declarator",
871
+ "function_declarator",
872
+ "parenthesized_declarator",
873
+ ]:
874
+ var_identifier = extract_identifier_from_declarator(child)
875
+ if var_identifier:
876
+ var_id = get_index(var_identifier, index)
877
+ if var_id:
878
+ add_entry(
879
+ parser,
880
+ rda_table,
881
+ parent_id,
882
+ defined=var_identifier,
883
+ declaration=True,
884
+ )
885
+
886
+ elif root_node.type in assignment:
887
+ parent_statement = return_first_parent_of_types(
888
+ root_node, statement_types["node_list_type"]
889
+ )
890
+ if parent_statement is None:
891
+ continue
892
+
893
+ parent_id = get_index(parent_statement, index)
894
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
895
+ if parent_statement and parent_statement.type in inner_types:
896
+ parent_statement = return_first_parent_of_types(
897
+ parent_statement, statement_types["node_list_type"]
898
+ )
899
+ parent_id = get_index(parent_statement, index)
900
+ elif parent_statement.type in handled_cases:
901
+ continue
902
+ else:
903
+ continue
904
+
905
+ left_node = root_node.child_by_field_name("left")
906
+ right_node = root_node.child_by_field_name("right")
907
+
908
+ if left_node is None or right_node is None:
909
+ continue
910
+
911
+ operator_text = extract_operator_text(root_node, left_node, right_node)
912
+
913
+ if operator_text != "=":
914
+ add_entry(parser, rda_table, parent_id, used=left_node)
915
+ else:
916
+ if left_node.type == "field_expression":
917
+ add_entry(parser, rda_table, parent_id, used=left_node)
918
+ elif left_node.type in variable_type:
919
+ left_node_index = get_index(left_node, index)
920
+ if (
921
+ left_node_index
922
+ and left_node_index in parser.symbol_table["scope_map"]
923
+ ):
924
+ is_init_declarator = False
925
+ check_parent = root_node.parent
926
+ while check_parent:
927
+ if check_parent.type == "init_declarator":
928
+ is_init_declarator = True
929
+ break
930
+ if check_parent.type in statement_types.get(
931
+ "node_list_type", []
932
+ ):
933
+ break
934
+ check_parent = check_parent.parent
935
+
936
+ if not is_init_declarator:
937
+ add_entry(parser, rda_table, parent_id, used=left_node)
938
+
939
+ add_entry(parser, rda_table, parent_id, defined=left_node)
940
+
941
+ if (
942
+ right_node.type
943
+ in variable_type
944
+ + [
945
+ "field_expression",
946
+ "pointer_expression",
947
+ "subscript_expression",
948
+ "unary_expression",
949
+ ]
950
+ + literal_types
951
+ ):
952
+ add_entry(parser, rda_table, parent_id, used=right_node)
953
+ else:
954
+ vars_used = recursively_get_children_of_types(
955
+ right_node,
956
+ variable_type + ["field_expression"],
957
+ index=parser.index,
958
+ check_list=parser.symbol_table["scope_map"],
959
+ )
960
+ for var in vars_used:
961
+ add_entry(parser, rda_table, parent_id, used=var)
962
+ literals_used = recursively_get_children_of_types(
963
+ right_node, literal_types, index=parser.index
964
+ )
965
+ for literal in literals_used:
966
+ add_entry(parser, rda_table, parent_id, used=literal)
967
+
968
+ elif root_node.type in increment_statement:
969
+ parent_statement = return_first_parent_of_types(
970
+ root_node, statement_types["node_list_type"]
971
+ )
972
+ if parent_statement is None:
973
+ continue
974
+
975
+ parent_id = get_index(parent_statement, index)
976
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
977
+ if parent_statement and parent_statement.type in inner_types:
978
+ parent_statement = return_first_parent_of_types(
979
+ parent_statement, statement_types["node_list_type"]
980
+ )
981
+ parent_id = get_index(parent_statement, index)
982
+ elif parent_statement.type in handled_cases:
983
+ continue
984
+ else:
985
+ continue
986
+
987
+ if root_node.type in variable_type + ["field_expression"]:
988
+ add_entry(parser, rda_table, parent_id, used=root_node)
989
+ add_entry(parser, rda_table, parent_id, defined=root_node)
990
+ else:
991
+ identifiers = recursively_get_children_of_types(
992
+ root_node,
993
+ variable_type + ["field_expression"],
994
+ index=parser.index,
995
+ check_list=parser.symbol_table["scope_map"],
996
+ )
997
+ for identifier in identifiers:
998
+ add_entry(parser, rda_table, parent_id, used=identifier)
999
+ add_entry(parser, rda_table, parent_id, defined=identifier)
1000
+
1001
+ elif root_node.type in function_calls:
1002
+ parent_statement = return_first_parent_of_types(
1003
+ root_node, statement_types["node_list_type"]
1004
+ )
1005
+ if parent_statement is None:
1006
+ continue
1007
+
1008
+ parent_id = get_index(parent_statement, index)
1009
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1010
+ if parent_statement and parent_statement.type in inner_types:
1011
+ parent_statement = return_first_parent_of_types(
1012
+ parent_statement, statement_types["node_list_type"]
1013
+ )
1014
+ parent_id = get_index(parent_statement, index)
1015
+ elif parent_statement.type in handled_cases:
1016
+ continue
1017
+ else:
1018
+ continue
1019
+
1020
+ function_name = None
1021
+ if root_node.children and root_node.children[0].type == "identifier":
1022
+ function_name = st(root_node.children[0])
1023
+
1024
+ is_input_function = function_name in input_functions
1025
+
1026
+ for child in root_node.children[1:]:
1027
+ if is_input_function and child.type == "argument_list":
1028
+ for arg in child.named_children:
1029
+ if arg.type == "pointer_expression":
1030
+ inner_arg = arg.child_by_field_name("argument")
1031
+ if inner_arg:
1032
+ if inner_arg.type in variable_type:
1033
+ add_entry(
1034
+ parser,
1035
+ rda_table,
1036
+ parent_id,
1037
+ defined=inner_arg,
1038
+ declaration=False,
1039
+ )
1040
+ elif inner_arg.type in [
1041
+ "field_expression",
1042
+ "subscript_expression",
1043
+ ]:
1044
+ add_entry(
1045
+ parser,
1046
+ rda_table,
1047
+ parent_id,
1048
+ defined=inner_arg,
1049
+ declaration=False,
1050
+ )
1051
+ if inner_arg.type == "subscript_expression":
1052
+ index_expr = inner_arg.child_by_field_name(
1053
+ "index"
1054
+ )
1055
+ if index_expr:
1056
+ vars_in_index = (
1057
+ recursively_get_children_of_types(
1058
+ index_expr,
1059
+ variable_type
1060
+ + ["field_expression"],
1061
+ index=parser.index,
1062
+ check_list=parser.symbol_table[
1063
+ "scope_map"
1064
+ ],
1065
+ )
1066
+ )
1067
+ for var in vars_in_index:
1068
+ add_entry(
1069
+ parser,
1070
+ rda_table,
1071
+ parent_id,
1072
+ used=var,
1073
+ )
1074
+ elif arg.type in variable_type + ["field_expression"]:
1075
+ add_entry(parser, rda_table, parent_id, used=arg)
1076
+ elif arg.type in literal_types:
1077
+ add_entry(parser, rda_table, parent_id, used=arg)
1078
+ else:
1079
+ identifiers_used = recursively_get_children_of_types(
1080
+ arg,
1081
+ variable_type + ["field_expression"],
1082
+ index=parser.index,
1083
+ check_list=parser.symbol_table["scope_map"],
1084
+ )
1085
+ for identifier in identifiers_used:
1086
+ add_entry(parser, rda_table, parent_id, used=identifier)
1087
+ literals_used = recursively_get_children_of_types(
1088
+ arg, literal_types, index=parser.index
1089
+ )
1090
+ for literal in literals_used:
1091
+ add_entry(parser, rda_table, parent_id, used=literal)
1092
+ elif not is_input_function and child.type == "argument_list":
1093
+ modifies_params = set()
1094
+ if (
1095
+ function_name
1096
+ and function_metadata
1097
+ and function_name in function_metadata
1098
+ ):
1099
+ if (
1100
+ pointer_modifications
1101
+ and function_name in pointer_modifications
1102
+ ):
1103
+ modifies_params = pointer_modifications[function_name]
1104
+
1105
+ for arg_idx, arg in enumerate(child.named_children):
1106
+ is_modified_param = arg_idx in modifies_params
1107
+
1108
+ if is_modified_param and arg.type == "pointer_expression":
1109
+ inner_arg = arg.child_by_field_name("argument")
1110
+ if inner_arg:
1111
+ if inner_arg.type in variable_type:
1112
+ add_entry(
1113
+ parser, rda_table, parent_id, used=inner_arg
1114
+ )
1115
+ add_entry(
1116
+ parser,
1117
+ rda_table,
1118
+ parent_id,
1119
+ defined=inner_arg,
1120
+ declaration=False,
1121
+ )
1122
+ elif inner_arg.type in [
1123
+ "field_expression",
1124
+ "subscript_expression",
1125
+ ]:
1126
+ add_entry(
1127
+ parser, rda_table, parent_id, used=inner_arg
1128
+ )
1129
+ add_entry(
1130
+ parser,
1131
+ rda_table,
1132
+ parent_id,
1133
+ defined=inner_arg,
1134
+ declaration=False,
1135
+ )
1136
+ if inner_arg.type == "subscript_expression":
1137
+ index_expr = inner_arg.child_by_field_name(
1138
+ "index"
1139
+ )
1140
+ if index_expr:
1141
+ vars_in_index = (
1142
+ recursively_get_children_of_types(
1143
+ index_expr,
1144
+ variable_type
1145
+ + ["field_expression"],
1146
+ index=parser.index,
1147
+ check_list=parser.symbol_table[
1148
+ "scope_map"
1149
+ ],
1150
+ )
1151
+ )
1152
+ for var in vars_in_index:
1153
+ add_entry(
1154
+ parser,
1155
+ rda_table,
1156
+ parent_id,
1157
+ used=var,
1158
+ )
1159
+ elif arg.type in variable_type + ["field_expression"]:
1160
+ add_entry(parser, rda_table, parent_id, used=arg)
1161
+ elif arg.type in literal_types:
1162
+ add_entry(parser, rda_table, parent_id, used=arg)
1163
+ else:
1164
+ identifiers_used = recursively_get_children_of_types(
1165
+ arg,
1166
+ variable_type + ["field_expression"],
1167
+ index=parser.index,
1168
+ check_list=parser.symbol_table["scope_map"],
1169
+ )
1170
+ for identifier in identifiers_used:
1171
+ add_entry(parser, rda_table, parent_id, used=identifier)
1172
+ literals_used = recursively_get_children_of_types(
1173
+ arg, literal_types, index=parser.index
1174
+ )
1175
+ for literal in literals_used:
1176
+ add_entry(parser, rda_table, parent_id, used=literal)
1177
+
1178
+ elif child.type in variable_type + ["field_expression"]:
1179
+ add_entry(parser, rda_table, parent_id, used=child)
1180
+ elif child.type in literal_types:
1181
+ add_entry(parser, rda_table, parent_id, used=child)
1182
+ else:
1183
+ identifiers_used = recursively_get_children_of_types(
1184
+ child,
1185
+ variable_type + ["field_expression"],
1186
+ index=parser.index,
1187
+ check_list=parser.symbol_table["scope_map"],
1188
+ )
1189
+ for identifier in identifiers_used:
1190
+ add_entry(parser, rda_table, parent_id, used=identifier)
1191
+ literals_used = recursively_get_children_of_types(
1192
+ child, literal_types, index=parser.index
1193
+ )
1194
+ for literal in literals_used:
1195
+ add_entry(parser, rda_table, parent_id, used=literal)
1196
+
1197
+ elif root_node.type == "function_definition":
1198
+ parent_id = get_index(root_node, index)
1199
+ if parent_id is None:
1200
+ continue
1201
+
1202
+ for child in root_node.named_children:
1203
+ if child.type == "function_declarator":
1204
+ func_name_node = child.child_by_field_name("declarator")
1205
+ if func_name_node and func_name_node.type in variable_type:
1206
+ func_name_idx = get_index(func_name_node, index)
1207
+ if (
1208
+ func_name_idx
1209
+ and func_name_idx in parser.symbol_table["scope_map"]
1210
+ ):
1211
+ add_entry(
1212
+ parser,
1213
+ rda_table,
1214
+ parent_id,
1215
+ defined=func_name_node,
1216
+ declaration=True,
1217
+ )
1218
+
1219
+ param_list = child.child_by_field_name("parameters")
1220
+ if param_list:
1221
+ for param in param_list.named_children:
1222
+ if param.type == "parameter_declaration":
1223
+ param_id = extract_param_identifier(param)
1224
+ if param_id:
1225
+ add_entry(
1226
+ parser,
1227
+ rda_table,
1228
+ parent_id,
1229
+ defined=param_id,
1230
+ declaration=True,
1231
+ )
1232
+ break
1233
+
1234
+ elif root_node.type == "switch_statement":
1235
+ parent_id = get_index(root_node, index)
1236
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1237
+ continue
1238
+
1239
+ condition = root_node.child_by_field_name("condition")
1240
+ if condition:
1241
+ identifiers_used = recursively_get_children_of_types(
1242
+ condition,
1243
+ variable_type + ["field_expression"],
1244
+ index=parser.index,
1245
+ check_list=parser.symbol_table["scope_map"],
1246
+ )
1247
+ for identifier in identifiers_used:
1248
+ add_entry(parser, rda_table, parent_id, used=identifier)
1249
+
1250
+ elif root_node.type == "for_statement":
1251
+ parent_id = get_index(root_node, index)
1252
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1253
+ continue
1254
+
1255
+ condition = root_node.child_by_field_name("condition")
1256
+ if condition:
1257
+ identifiers_used = recursively_get_children_of_types(
1258
+ condition,
1259
+ variable_type + ["field_expression"],
1260
+ index=parser.index,
1261
+ check_list=parser.symbol_table["scope_map"],
1262
+ )
1263
+ for identifier in identifiers_used:
1264
+ add_entry(parser, rda_table, parent_id, used=identifier)
1265
+
1266
+ elif root_node.type == "while_statement":
1267
+ parent_id = get_index(root_node, index)
1268
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1269
+ continue
1270
+
1271
+ condition = root_node.child_by_field_name("condition")
1272
+ if condition:
1273
+ identifiers_used = recursively_get_children_of_types(
1274
+ condition,
1275
+ variable_type + ["field_expression"],
1276
+ index=parser.index,
1277
+ check_list=parser.symbol_table["scope_map"],
1278
+ )
1279
+ for identifier in identifiers_used:
1280
+ add_entry(parser, rda_table, parent_id, used=identifier)
1281
+
1282
+ elif (
1283
+ root_node.type == "parenthesized_expression"
1284
+ and root_node.parent is not None
1285
+ and root_node.parent.type == "do_statement"
1286
+ ):
1287
+ parent_id = get_index(root_node, index)
1288
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1289
+ continue
1290
+
1291
+ identifiers_used = recursively_get_children_of_types(
1292
+ root_node,
1293
+ variable_type + ["field_expression"],
1294
+ index=parser.index,
1295
+ check_list=parser.symbol_table["scope_map"],
1296
+ )
1297
+ for identifier in identifiers_used:
1298
+ add_entry(parser, rda_table, parent_id, used=identifier)
1299
+
1300
+ elif root_node.type == "do_statement":
1301
+ pass
1302
+
1303
+ elif root_node.type == "if_statement":
1304
+ parent_id = get_index(root_node, index)
1305
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1306
+ continue
1307
+
1308
+ condition = root_node.child_by_field_name("condition")
1309
+ if condition:
1310
+ identifiers_used = recursively_get_children_of_types(
1311
+ condition,
1312
+ variable_type + ["field_expression"],
1313
+ index=parser.index,
1314
+ check_list=parser.symbol_table["scope_map"],
1315
+ )
1316
+ for identifier in identifiers_used:
1317
+ add_entry(parser, rda_table, parent_id, used=identifier)
1318
+
1319
+ elif root_node.type == "conditional_expression":
1320
+ parent_statement = return_first_parent_of_types(
1321
+ root_node, statement_types["node_list_type"]
1322
+ )
1323
+ if parent_statement is None:
1324
+ continue
1325
+
1326
+ parent_id = get_index(parent_statement, index)
1327
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1328
+ continue
1329
+
1330
+ condition = root_node.child_by_field_name("condition")
1331
+ if condition:
1332
+ if condition.type in variable_type + ["field_expression"]:
1333
+ add_entry(parser, rda_table, parent_id, used=condition)
1334
+ else:
1335
+ identifiers_used = recursively_get_children_of_types(
1336
+ condition,
1337
+ variable_type + ["field_expression"],
1338
+ index=parser.index,
1339
+ check_list=parser.symbol_table["scope_map"],
1340
+ )
1341
+ for identifier in identifiers_used:
1342
+ add_entry(parser, rda_table, parent_id, used=identifier)
1343
+ literals_used = recursively_get_children_of_types(
1344
+ condition, literal_types, index=parser.index
1345
+ )
1346
+ for literal in literals_used:
1347
+ add_entry(parser, rda_table, parent_id, used=literal)
1348
+
1349
+ consequence = root_node.child_by_field_name("consequence")
1350
+ if consequence:
1351
+ if consequence.type in variable_type + ["field_expression"]:
1352
+ add_entry(parser, rda_table, parent_id, used=consequence)
1353
+ else:
1354
+ identifiers_used = recursively_get_children_of_types(
1355
+ consequence,
1356
+ variable_type + ["field_expression"],
1357
+ index=parser.index,
1358
+ check_list=parser.symbol_table["scope_map"],
1359
+ )
1360
+ for identifier in identifiers_used:
1361
+ add_entry(parser, rda_table, parent_id, used=identifier)
1362
+ literals_used = recursively_get_children_of_types(
1363
+ consequence, literal_types, index=parser.index
1364
+ )
1365
+ for literal in literals_used:
1366
+ add_entry(parser, rda_table, parent_id, used=literal)
1367
+
1368
+ alternative = root_node.child_by_field_name("alternative")
1369
+ if alternative:
1370
+ if alternative.type in variable_type + ["field_expression"]:
1371
+ add_entry(parser, rda_table, parent_id, used=alternative)
1372
+ else:
1373
+ identifiers_used = recursively_get_children_of_types(
1374
+ alternative,
1375
+ variable_type + ["field_expression"],
1376
+ index=parser.index,
1377
+ check_list=parser.symbol_table["scope_map"],
1378
+ )
1379
+ for identifier in identifiers_used:
1380
+ add_entry(parser, rda_table, parent_id, used=identifier)
1381
+ literals_used = recursively_get_children_of_types(
1382
+ alternative, literal_types, index=parser.index
1383
+ )
1384
+ for literal in literals_used:
1385
+ add_entry(parser, rda_table, parent_id, used=literal)
1386
+
1387
+ else:
1388
+ if root_node.type not in variable_type:
1389
+ continue
1390
+
1391
+ in_do_while_condition = False
1392
+ temp_parent = root_node.parent
1393
+ while temp_parent is not None:
1394
+ if (
1395
+ temp_parent.type == "parenthesized_expression"
1396
+ and temp_parent.parent is not None
1397
+ and temp_parent.parent.type == "do_statement"
1398
+ ):
1399
+ in_do_while_condition = True
1400
+ break
1401
+ temp_parent = temp_parent.parent
1402
+
1403
+ if in_do_while_condition:
1404
+ continue
1405
+
1406
+ handled_types = (
1407
+ def_statement
1408
+ + assignment
1409
+ + increment_statement
1410
+ + function_calls
1411
+ + ["return_statement"]
1412
+ )
1413
+ parent_statement = return_first_parent_of_types(
1414
+ root_node,
1415
+ statement_types["non_control_statement"]
1416
+ + statement_types["control_statement"],
1417
+ stop_types=statement_types.get("statement_holders", []) + handled_types,
1418
+ )
1419
+
1420
+ if parent_statement is None:
1421
+ continue
1422
+
1423
+ parent_id = get_index(parent_statement, index)
1424
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1425
+ continue
1426
+
1427
+ immediate_parent = root_node.parent
1428
+ if immediate_parent and immediate_parent.type == "pointer_expression":
1429
+ continue
1430
+
1431
+ add_entry(parser, rda_table, parent_id, used=root_node)
1432
+
1433
+ return rda_table
1434
+
1435
+
1436
+ def start_rda(index, rda_table, cfg_graph, pre_solve=False):
1437
+ """
1438
+ Perform Reaching Definitions Analysis.
1439
+
1440
+ Fixed-point iteration algorithm:
1441
+ Initialize all IN/OUT to empty
1442
+ Repeat until convergence:
1443
+ For each statement s:
1444
+ IN[s] = union of OUT[p] for predecessors p
1445
+ OUT[s] = (IN[s] - KILL[s]) ∪ DEF[s]
1446
+ """
1447
+ graph = copy.deepcopy(cfg_graph)
1448
+ if pre_solve:
1449
+ remove_edges = []
1450
+ for edge in graph.edges:
1451
+ edge_data = graph.edges[edge]
1452
+ if "label" in edge_data and edge_data["label"] in [
1453
+ "function_call",
1454
+ "function_return",
1455
+ ]:
1456
+ remove_edges.append(edge)
1457
+ graph.remove_edges_from(remove_edges)
1458
+
1459
+ cfg = graph
1460
+ nodes = graph.nodes
1461
+
1462
+ old_result = {}
1463
+ for node in nodes:
1464
+ old_result[node] = {"IN": set(), "OUT": set()}
1465
+
1466
+ new_result = copy.deepcopy(old_result)
1467
+ iteration = 0
1468
+
1469
+ while True:
1470
+ iteration += 1
1471
+
1472
+ for node in nodes:
1473
+ predecessors = [s for (s, t) in cfg.in_edges(node)]
1474
+
1475
+ in_set = set()
1476
+ for pred in predecessors:
1477
+ in_set = in_set.union(old_result[pred]["OUT"])
1478
+
1479
+ new_result[node]["IN"] = in_set
1480
+
1481
+ def_info = rda_table[node]["def"] if node in rda_table else set()
1482
+ names_defined = [d.name for d in def_info]
1483
+
1484
+ surviving_defs = set()
1485
+ for incoming_def in in_set:
1486
+ if incoming_def.name not in names_defined:
1487
+ surviving_defs.add(incoming_def)
1488
+
1489
+ new_result[node]["OUT"] = surviving_defs.union(def_info)
1490
+
1491
+ ddiff = DeepDiff(old_result, new_result, ignore_order=True)
1492
+ if ddiff == {}:
1493
+ if debug:
1494
+ logger.info("RDA: Converged in {} iterations", iteration)
1495
+ break
1496
+
1497
+ old_result = copy.deepcopy(new_result)
1498
+
1499
+ return new_result
1500
+
1501
+
1502
+ def add_edge(final_graph, source, target, attrib=None):
1503
+ """Add edge to graph with attributes, preventing duplicates"""
1504
+ if attrib is not None:
1505
+ used_def = attrib.get("used_def", None)
1506
+ edge_type = attrib.get("edge_type", None)
1507
+ dataflow_type = attrib.get("dataflow_type", None)
1508
+
1509
+ for u, v, k, data in final_graph.edges(keys=True, data=True):
1510
+ if (
1511
+ u == source
1512
+ and v == target
1513
+ and data.get("used_def") == used_def
1514
+ and data.get("edge_type") == edge_type
1515
+ and data.get("dataflow_type") == dataflow_type
1516
+ ):
1517
+ return
1518
+
1519
+ final_graph.add_edge(source, target)
1520
+ if attrib is not None:
1521
+ edge_keys = [
1522
+ k for u, v, k in final_graph.edges(keys=True) if u == source and v == target
1523
+ ]
1524
+ if edge_keys:
1525
+ edge_key = max(edge_keys)
1526
+ else:
1527
+ edge_key = 0
1528
+ nx.set_edge_attributes(final_graph, {(source, target, edge_key): attrib})
1529
+
1530
+
1531
+ def name_match_with_fields(name1, name2):
1532
+ """Check if two names match (handling struct fields)"""
1533
+ if name1 == name2:
1534
+ return True
1535
+
1536
+ if name1.startswith(name2 + ".") or name2.startswith(name1 + "."):
1537
+ return True
1538
+
1539
+ return False
1540
+
1541
+
1542
+ def get_required_edges_from_def_to_use(
1543
+ index, cfg, rda_solution, rda_table, graph_nodes, processed_edges, properties
1544
+ ):
1545
+ """
1546
+ Generate DFG edges from RDA solution.
1547
+
1548
+ For each statement s:
1549
+ For each use u in USE[s]:
1550
+ For each def d in IN[s]:
1551
+ If d.name == u.name and scope_check(d.scope, u.scope):
1552
+ Add edge: d.line → s
1553
+ """
1554
+ final_graph = copy.deepcopy(cfg)
1555
+ final_graph.remove_edges_from(list(final_graph.edges()))
1556
+
1557
+ for node in graph_nodes:
1558
+ if node not in rda_table:
1559
+ continue
1560
+
1561
+ use_info = rda_table[node]["use"]
1562
+
1563
+ for used in use_info:
1564
+ if isinstance(used, Literal):
1565
+ used.satisfied = True
1566
+ continue
1567
+
1568
+ for available_def in rda_solution[node]["IN"]:
1569
+ if available_def.name == used.name:
1570
+ if scope_check(available_def.scope, used.scope):
1571
+ add_edge(
1572
+ final_graph,
1573
+ available_def.line,
1574
+ node,
1575
+ {
1576
+ "dataflow_type": "comesFrom",
1577
+ "edge_type": "DFG_edge",
1578
+ "color": "#00A3FF",
1579
+ "used_def": used.name,
1580
+ },
1581
+ )
1582
+ used.satisfied = True
1583
+
1584
+ elif "." in used.name or "." in available_def.name:
1585
+ if name_match_with_fields(used.name, available_def.name):
1586
+ if scope_check(available_def.scope, used.scope):
1587
+ add_edge(
1588
+ final_graph,
1589
+ available_def.line,
1590
+ node,
1591
+ {
1592
+ "dataflow_type": "comesFrom",
1593
+ "edge_type": "DFG_edge",
1594
+ "color": "#00A3FF",
1595
+ "used_def": used.name,
1596
+ },
1597
+ )
1598
+ used.satisfied = True
1599
+
1600
+ if not used.satisfied:
1601
+ for def_node in graph_nodes:
1602
+ if def_node not in rda_table:
1603
+ continue
1604
+ for definition in rda_table[def_node]["def"]:
1605
+ if definition.name == used.name:
1606
+ node_type = (
1607
+ read_index(index, def_node)[-1]
1608
+ if def_node in index.values()
1609
+ else None
1610
+ )
1611
+ if node_type == "function_definition":
1612
+ add_edge(
1613
+ final_graph,
1614
+ def_node,
1615
+ node,
1616
+ {
1617
+ "dataflow_type": "comesFrom",
1618
+ "edge_type": "DFG_edge",
1619
+ "color": "#00A3FF",
1620
+ "used_def": used.name,
1621
+ },
1622
+ )
1623
+ used.satisfied = True
1624
+ break
1625
+
1626
+ if properties.get("last_def", False):
1627
+ killed_defs = rda_solution[node]["IN"] - rda_solution[node]["OUT"]
1628
+ for killed_def in killed_defs:
1629
+ node_type = read_index(index, node)[-1]
1630
+ def_node_type = read_index(index, killed_def.line)[-1]
1631
+ ignore_types = [
1632
+ "for_statement",
1633
+ "while_statement",
1634
+ "if_statement",
1635
+ "switch_statement",
1636
+ ]
1637
+ if node_type not in ignore_types and def_node_type not in ignore_types:
1638
+ add_edge(
1639
+ final_graph,
1640
+ killed_def.line,
1641
+ node,
1642
+ {"color": "orange", "dataflow_type": "lastDef"},
1643
+ )
1644
+
1645
+ for edge in processed_edges:
1646
+ add_edge(
1647
+ final_graph,
1648
+ edge[0],
1649
+ edge[1],
1650
+ {"dataflow_type": "parameter", "edge_type": "DFG_edge"},
1651
+ )
1652
+
1653
+ return final_graph
1654
+
1655
+
1656
+ def rda_cfg_map(rda_solution, CFG_results):
1657
+ """Create debug graph showing RDA info on CFG edges"""
1658
+ graph = copy.deepcopy(CFG_results.graph)
1659
+
1660
+ for edge in list(graph.edges):
1661
+ out_set = rda_solution[edge[0]]["OUT"]
1662
+ in_set = rda_solution[edge[1]]["IN"]
1663
+
1664
+ intersection = [d for d in out_set if d in in_set]
1665
+
1666
+ if intersection:
1667
+ edge_data = graph.get_edge_data(*edge)
1668
+ edge_data["rda_info"] = ",".join([str(d) for d in intersection])
1669
+ else:
1670
+ graph.remove_edge(*edge)
1671
+
1672
+ return graph
1673
+
1674
+
1675
+ def collect_call_site_information(parser, function_metadata, cfg_graph):
1676
+ """
1677
+ Collect information about function call sites for interprocedural analysis.
1678
+
1679
+ For each call site, track:
1680
+ - Which function is called
1681
+ - Which arguments are passed by reference (&var)
1682
+ - The mapping of actual arguments to formal parameters
1683
+
1684
+ Returns:
1685
+ List of dicts with call site information:
1686
+ {
1687
+ "call_site_node": AST node,
1688
+ "call_site_id": CFG node ID,
1689
+ "function_name": str,
1690
+ "pass_by_ref_args": [(arg_idx, var_name, var_node), ...]
1691
+ }
1692
+ """
1693
+ call_sites = []
1694
+ index = parser.index
1695
+
1696
+ for node in traverse_tree(parser.tree.root_node):
1697
+ if node.type == "call_expression":
1698
+ function_name = None
1699
+ if node.children and node.children[0].type == "identifier":
1700
+ function_name = st(node.children[0])
1701
+
1702
+ if not function_name or function_name not in function_metadata:
1703
+ continue
1704
+
1705
+ parent_statement = return_first_parent_of_types(
1706
+ node, statement_types["node_list_type"]
1707
+ )
1708
+ if not parent_statement:
1709
+ continue
1710
+
1711
+ call_site_id = get_index(parent_statement, index)
1712
+ if call_site_id is None or call_site_id not in cfg_graph.nodes:
1713
+ continue
1714
+
1715
+ pass_by_ref_args = []
1716
+ for child in node.children[1:]:
1717
+ if child.type == "argument_list":
1718
+ func_params = function_metadata[function_name]["params"]
1719
+
1720
+ for arg_idx, arg in enumerate(child.named_children):
1721
+ if arg_idx < len(func_params):
1722
+ param_name, is_pointer, param_idx = func_params[arg_idx]
1723
+
1724
+ if is_pointer:
1725
+ if arg.type == "pointer_expression":
1726
+ has_ampersand = False
1727
+ arg_node = None
1728
+ for arg_child in arg.children:
1729
+ if arg_child.type == "&":
1730
+ has_ampersand = True
1731
+ elif arg_child.is_named:
1732
+ arg_node = arg_child
1733
+
1734
+ if has_ampersand and arg_node:
1735
+ if arg_node.type == "identifier":
1736
+ var_name = st(arg_node)
1737
+ pass_by_ref_args.append(
1738
+ (arg_idx, var_name, arg_node)
1739
+ )
1740
+ elif arg.type == "identifier":
1741
+ var_name = st(arg)
1742
+ arg_index = get_index(arg, index)
1743
+ if (
1744
+ arg_index
1745
+ and arg_index
1746
+ in parser.symbol_table["scope_map"]
1747
+ ):
1748
+ pass_by_ref_args.append(
1749
+ (arg_idx, var_name, arg)
1750
+ )
1751
+
1752
+ if pass_by_ref_args:
1753
+ call_sites.append(
1754
+ {
1755
+ "call_site_node": node,
1756
+ "call_site_id": call_site_id,
1757
+ "function_name": function_name,
1758
+ "pass_by_ref_args": pass_by_ref_args,
1759
+ }
1760
+ )
1761
+
1762
+ return call_sites
1763
+
1764
+
1765
+ def find_modification_sites(parser, function_metadata, pointer_modifications):
1766
+ """
1767
+ Find all modification sites for pointer parameters inside functions.
1768
+
1769
+ For each function, find all statements where a pointer parameter is modified.
1770
+
1771
+ Returns:
1772
+ Dict mapping function_name -> list of (param_idx, modification_node, statement_id)
1773
+ """
1774
+ modification_sites = {}
1775
+ index = parser.index
1776
+
1777
+ for func_name, meta in function_metadata.items():
1778
+ modifications = []
1779
+ func_node = meta["node"]
1780
+ modified_params = pointer_modifications.get(func_name, set())
1781
+
1782
+ param_name_to_idx = {}
1783
+ for param_name, is_pointer, param_idx in meta["params"]:
1784
+ if is_pointer and param_idx in modified_params:
1785
+ param_name_to_idx[param_name] = param_idx
1786
+
1787
+ if not param_name_to_idx:
1788
+ modification_sites[func_name] = modifications
1789
+ continue
1790
+
1791
+ for node in traverse_tree(func_node):
1792
+ modification_param_idx = None
1793
+ mod_node = None
1794
+
1795
+ if node.type == "assignment_expression":
1796
+ left = node.child_by_field_name("left")
1797
+ if left:
1798
+ if left.type == "pointer_expression":
1799
+ arg = left.child_by_field_name("argument")
1800
+ if arg and arg.type == "identifier":
1801
+ var_name = st(arg)
1802
+ if var_name in param_name_to_idx:
1803
+ modification_param_idx = param_name_to_idx[var_name]
1804
+ mod_node = node
1805
+
1806
+ elif left.type == "subscript_expression":
1807
+ array_arg = left.child_by_field_name("argument")
1808
+ if array_arg and array_arg.type == "identifier":
1809
+ var_name = st(array_arg)
1810
+ if var_name in param_name_to_idx:
1811
+ modification_param_idx = param_name_to_idx[var_name]
1812
+ mod_node = node
1813
+
1814
+ elif node.type == "update_expression":
1815
+ arg = node.child_by_field_name("argument")
1816
+ if arg:
1817
+ if arg.type == "parenthesized_expression":
1818
+ inner_children = [c for c in arg.named_children if c.is_named]
1819
+ if inner_children:
1820
+ arg = inner_children[0]
1821
+
1822
+ if arg.type == "pointer_expression":
1823
+ inner_arg = arg.child_by_field_name("argument")
1824
+ if inner_arg and inner_arg.type == "identifier":
1825
+ var_name = st(inner_arg)
1826
+ if var_name in param_name_to_idx:
1827
+ modification_param_idx = param_name_to_idx[var_name]
1828
+ mod_node = node
1829
+ elif arg.type == "subscript_expression":
1830
+ array_arg = arg.child_by_field_name("argument")
1831
+ if array_arg and array_arg.type == "identifier":
1832
+ var_name = st(array_arg)
1833
+ if var_name in param_name_to_idx:
1834
+ modification_param_idx = param_name_to_idx[var_name]
1835
+ mod_node = node
1836
+
1837
+ if modification_param_idx is not None and mod_node is not None:
1838
+ parent_statement = return_first_parent_of_types(
1839
+ mod_node, statement_types["node_list_type"]
1840
+ )
1841
+ if parent_statement:
1842
+ statement_id = get_index(parent_statement, index)
1843
+ if statement_id is not None:
1844
+ modifications.append(
1845
+ (modification_param_idx, mod_node, statement_id)
1846
+ )
1847
+
1848
+ modification_sites[func_name] = modifications
1849
+
1850
+ return modification_sites
1851
+
1852
+
1853
+ def add_interprocedural_edges(
1854
+ final_graph,
1855
+ parser,
1856
+ call_sites,
1857
+ modification_sites,
1858
+ function_metadata,
1859
+ cfg_graph,
1860
+ rda_table,
1861
+ ):
1862
+ """
1863
+ Add interprocedural DFG edges for pass-by-reference.
1864
+
1865
+ For each call site where &var is passed to a pointer parameter:
1866
+ 1. Add edge from call site to function definition (argument-parameter binding)
1867
+ 2. Add edges from modification sites inside function to uses after the call
1868
+
1869
+ Args:
1870
+ final_graph: The DFG graph to add edges to
1871
+ parser: C parser
1872
+ call_sites: List of call site information from collect_call_site_information()
1873
+ modification_sites: Dict from find_modification_sites()
1874
+ function_metadata: Dict from collect_function_metadata()
1875
+ cfg_graph: Control flow graph
1876
+ rda_table: RDA table with def/use information
1877
+ """
1878
+ index = parser.index
1879
+
1880
+ for call_site_info in call_sites:
1881
+ call_site_id = call_site_info["call_site_id"]
1882
+ function_name = call_site_info["function_name"]
1883
+ pass_by_ref_args = call_site_info["pass_by_ref_args"]
1884
+
1885
+ if (
1886
+ function_name not in function_metadata
1887
+ or function_name not in modification_sites
1888
+ ):
1889
+ continue
1890
+
1891
+ func_meta = function_metadata[function_name]
1892
+ func_node = func_meta["node"]
1893
+ func_def_id = get_index(func_node, index)
1894
+
1895
+ if func_def_id is None:
1896
+ continue
1897
+
1898
+ for arg_idx, var_name, var_node in pass_by_ref_args:
1899
+ add_edge(
1900
+ final_graph,
1901
+ call_site_id,
1902
+ func_def_id,
1903
+ {
1904
+ "dataflow_type": "comesFrom",
1905
+ "edge_type": "DFG_edge",
1906
+ "color": "#00A3FF",
1907
+ "used_def": var_name,
1908
+ "interprocedural": "call_to_function",
1909
+ },
1910
+ )
1911
+
1912
+ mods = modification_sites.get(function_name, [])
1913
+ for mod_param_idx, mod_node, mod_statement_id in mods:
1914
+ if mod_param_idx == arg_idx:
1915
+ successors = []
1916
+ visited = set()
1917
+ queue = [call_site_id]
1918
+
1919
+ while queue:
1920
+ current = queue.pop(0)
1921
+ if current in visited:
1922
+ continue
1923
+ visited.add(current)
1924
+
1925
+ uses_var = False
1926
+ defines_var = False
1927
+ if current != call_site_id and current in rda_table:
1928
+ for used in rda_table[current]["use"]:
1929
+ if used.name == var_name:
1930
+ uses_var = True
1931
+ successors.append(current)
1932
+ break
1933
+
1934
+ for defined in rda_table[current]["def"]:
1935
+ if defined.name == var_name:
1936
+ defines_var = True
1937
+ break
1938
+
1939
+ if (
1940
+ current == call_site_id
1941
+ or not uses_var
1942
+ or (uses_var and defines_var)
1943
+ ):
1944
+ for edge in cfg_graph.out_edges(current):
1945
+ if edge[1] not in visited:
1946
+ queue.append(edge[1])
1947
+
1948
+ for use_site in successors:
1949
+ add_edge(
1950
+ final_graph,
1951
+ mod_statement_id,
1952
+ use_site,
1953
+ {
1954
+ "dataflow_type": "comesFrom",
1955
+ "edge_type": "DFG_edge",
1956
+ "color": "#00A3FF",
1957
+ "used_def": var_name,
1958
+ "interprocedural": "modification_to_use",
1959
+ },
1960
+ )
1961
+
1962
+
1963
+ def dfg_c(properties, CFG_results):
1964
+ """
1965
+ Main driver for generating DFG for C programs.
1966
+
1967
+ Args:
1968
+ properties: Configuration dict with DFG options
1969
+ CFG_results: CFG driver results with graph, parser, etc.
1970
+
1971
+ Returns:
1972
+ (final_graph, debug_graph, rda_table, rda_solution)
1973
+ """
1974
+ parser = CFG_results.parser
1975
+ index = parser.index
1976
+ tree = parser.tree
1977
+
1978
+ cfg_graph = copy.deepcopy(CFG_results.graph)
1979
+ node_list = CFG_results.node_list
1980
+
1981
+ processed_edges = []
1982
+ for edge in list(cfg_graph.edges()):
1983
+ edge_data = cfg_graph.get_edge_data(*edge)
1984
+ if edge_data and len(edge_data) > 0:
1985
+ edge_data = edge_data[0]
1986
+ if edge_data.get("label") == "function_return":
1987
+ return_statement = node_list.get(read_index(index, edge[0]))
1988
+ call_site_node = node_list.get(read_index(index, edge[1]))
1989
+
1990
+ if return_statement and return_statement.type == "return_statement":
1991
+ if return_statement.named_children:
1992
+ if call_site_node and is_return_value_used(call_site_node):
1993
+ processed_edges.append(edge)
1994
+
1995
+ function_metadata = collect_function_metadata(parser)
1996
+ pointer_modifications = analyze_pointer_modifications(parser, function_metadata)
1997
+
1998
+ start_rda_init_time = time.time()
1999
+ rda_table = build_rda_table(
2000
+ parser, CFG_results, function_metadata, pointer_modifications
2001
+ )
2002
+ end_rda_init_time = time.time()
2003
+
2004
+ start_rda_time = time.time()
2005
+ rda_solution = start_rda(index, rda_table, cfg_graph, pre_solve=True)
2006
+ end_rda_time = time.time()
2007
+
2008
+ final_graph = get_required_edges_from_def_to_use(
2009
+ index,
2010
+ cfg_graph,
2011
+ rda_solution,
2012
+ rda_table,
2013
+ cfg_graph.nodes,
2014
+ processed_edges,
2015
+ properties,
2016
+ )
2017
+
2018
+ call_sites = collect_call_site_information(parser, function_metadata, cfg_graph)
2019
+ modification_sites = find_modification_sites(
2020
+ parser, function_metadata, pointer_modifications
2021
+ )
2022
+ add_interprocedural_edges(
2023
+ final_graph,
2024
+ parser,
2025
+ call_sites,
2026
+ modification_sites,
2027
+ function_metadata,
2028
+ cfg_graph,
2029
+ rda_table,
2030
+ )
2031
+
2032
+ if debug:
2033
+ logger.info(
2034
+ "RDA init: {:.3f}s, RDA: {:.3f}s",
2035
+ end_rda_init_time - start_rda_init_time,
2036
+ end_rda_time - start_rda_time,
2037
+ )
2038
+
2039
+ debug_graph = rda_cfg_map(rda_solution, CFG_results)
2040
+
2041
+ return final_graph, debug_graph, rda_table, rda_solution