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,1182 @@
1
+ import traceback
2
+
3
+ import networkx as nx
4
+ from loguru import logger
5
+
6
+ from ...utils import c_nodes
7
+ from .CFG import CFGGraph
8
+
9
+
10
+ class CFGGraph_c(CFGGraph):
11
+ def __init__(self, src_language, src_code, properties, root_node, parser):
12
+ super().__init__(src_language, src_code, properties, root_node, parser)
13
+
14
+ self.node_list = None
15
+ self.statement_types = c_nodes.statement_types
16
+ self.CFG_node_list = []
17
+ self.CFG_edge_list = []
18
+ self.records = {
19
+ "basic_blocks": {},
20
+ "function_list": {},
21
+ "return_type": {},
22
+ "function_calls": {},
23
+ "switch_child_map": {},
24
+ "label_statement_map": {},
25
+ "return_statement_map": {},
26
+ "function_pointer_map": {},
27
+ }
28
+ self.index_counter = max(self.index.values())
29
+ self.CFG_node_indices = []
30
+
31
+ self.symbol_table = self.parser.symbol_table
32
+ self.declaration = self.parser.declaration
33
+ self.declaration_map = self.parser.declaration_map
34
+ self.CFG_node_list, self.CFG_edge_list = self.CFG_c()
35
+ self.graph = self.to_networkx(self.CFG_node_list, self.CFG_edge_list)
36
+
37
+ def get_index(self, node):
38
+ """Get the unique index for a given AST node"""
39
+ return self.index[(node.start_point, node.end_point, node.type)]
40
+
41
+ def get_basic_blocks(self, CFG_node_list, CFG_edge_list):
42
+ """Partition CFG into basic blocks using weakly connected components"""
43
+ G = self.to_networkx(CFG_node_list, CFG_edge_list)
44
+ components = nx.weakly_connected_components(G)
45
+ block_index = 1
46
+ for block in components:
47
+ block_list = sorted(list(block))
48
+ self.records["basic_blocks"][block_index] = block_list
49
+ block_index += 1
50
+
51
+ def get_key(self, val, dictionary):
52
+ """Find the key in dictionary where val is in the value list"""
53
+ for key, value in dictionary.items():
54
+ if val in value:
55
+ return key
56
+ return None
57
+
58
+ def get_containing_function(self, node):
59
+ """Find the enclosing function definition for a given node"""
60
+ while node is not None:
61
+ if node.type == "function_definition":
62
+ return node
63
+ node = node.parent
64
+ return None
65
+
66
+ def get_next_index(self, current_node, node_list):
67
+ """
68
+ Find the next executable statement after current_node.
69
+ Handles:
70
+ - Sequential statements (next_named_sibling)
71
+ - End of blocks (traverse up to parent)
72
+ - Loop back edges
73
+ - Function boundaries
74
+ """
75
+ next_node = current_node.next_named_sibling
76
+
77
+ while next_node is None:
78
+ parent = current_node.parent
79
+ if parent is None:
80
+ return (2, None)
81
+
82
+ if parent.type in self.statement_types["loop_control_statement"]:
83
+ if (parent.start_point, parent.end_point, parent.type) in node_list:
84
+ return (self.get_index(parent), parent)
85
+
86
+ if parent.type in self.statement_types["control_statement"]:
87
+ current_node = parent
88
+ next_node = current_node.next_named_sibling
89
+ continue
90
+
91
+ if parent.type == "function_definition":
92
+ return (2, None)
93
+
94
+ if parent.type in self.statement_types["statement_holders"]:
95
+ current_node = parent
96
+ next_node = current_node.next_named_sibling
97
+ continue
98
+
99
+ current_node = parent
100
+ next_node = current_node.next_named_sibling
101
+
102
+ if (
103
+ next_node.type == "compound_statement"
104
+ and len(list(next_node.named_children)) == 0
105
+ ):
106
+ current_node = next_node
107
+ return self.get_next_index(current_node, node_list)
108
+
109
+ if next_node.type == "compound_statement":
110
+ children_list = list(next_node.named_children)
111
+ if children_list:
112
+ first_child = children_list[0]
113
+ if (
114
+ first_child.start_point,
115
+ first_child.end_point,
116
+ first_child.type,
117
+ ) in node_list:
118
+ return (self.get_index(first_child), first_child)
119
+
120
+ if (next_node.start_point, next_node.end_point, next_node.type) in node_list:
121
+ return (self.get_index(next_node), next_node)
122
+
123
+ return self.get_next_index(next_node, node_list)
124
+
125
+ def is_last_in_control_block(self, node):
126
+ """
127
+ Check if a node is the last statement in a control flow block (if/else/loop).
128
+ These nodes should NOT have edges added in the sequential flow step,
129
+ as they will be handled by the control flow step.
130
+
131
+ Handles two cases:
132
+ 1. Last statement in a compound_statement that belongs to a control structure
133
+ 2. Single statement (no braces) that is the consequence/body of a control structure
134
+ """
135
+ if node.parent is None:
136
+ return False
137
+
138
+ parent = node.parent
139
+
140
+ if parent.type == "compound_statement":
141
+ children = list(parent.named_children)
142
+ if children and children[-1] == node:
143
+ grandparent = parent.parent
144
+ if grandparent and grandparent.type in [
145
+ "if_statement",
146
+ "while_statement",
147
+ "for_statement",
148
+ "do_statement",
149
+ "else_clause",
150
+ ]:
151
+ return True
152
+
153
+ if parent.type in [
154
+ "if_statement",
155
+ "while_statement",
156
+ "for_statement",
157
+ "do_statement",
158
+ ]:
159
+ consequence = parent.child_by_field_name("consequence")
160
+ body = parent.child_by_field_name("body")
161
+
162
+ if consequence and consequence == node:
163
+ return True
164
+
165
+ if body and body == node:
166
+ return True
167
+
168
+ if parent.type == "else_clause":
169
+ children = list(parent.named_children)
170
+ if children and node in children:
171
+ return True
172
+
173
+ return False
174
+
175
+ def get_block_last_line(self, current_node, body_field="body"):
176
+ """
177
+ Find the last executable statement in a block.
178
+ Used for connecting end of if/else/loop bodies to next statement.
179
+ """
180
+ block_node = current_node.child_by_field_name(body_field)
181
+
182
+ if block_node is None:
183
+ for child in current_node.children:
184
+ if child.type == "compound_statement":
185
+ block_node = child
186
+ break
187
+
188
+ if block_node is None:
189
+ return (current_node, current_node.type)
190
+
191
+ while block_node.type in self.statement_types["statement_holders"]:
192
+ children = list(block_node.named_children)
193
+ if not children:
194
+ return (current_node, current_node.type)
195
+
196
+ last_child = children[-1]
197
+
198
+ if last_child.type in self.statement_types["node_list_type"]:
199
+ return (last_child, last_child.type)
200
+
201
+ block_node = last_child
202
+
203
+ return (block_node, block_node.type)
204
+
205
+ def edge_first_line(self, node, node_list):
206
+ """
207
+ Find the first executable line in a function/block and create edge.
208
+ Returns the first statement node and its index.
209
+ """
210
+ body_node = None
211
+
212
+ if node.type == "function_definition":
213
+ body_node = node.child_by_field_name("body")
214
+
215
+ if body_node is None:
216
+ for child in node.children:
217
+ if child.type == "compound_statement":
218
+ body_node = child
219
+ break
220
+
221
+ if body_node is None:
222
+ return None
223
+
224
+ for child in body_node.named_children:
225
+ if (child.start_point, child.end_point, child.type) in node_list:
226
+ return (self.get_index(child), child)
227
+
228
+ return None
229
+
230
+ def add_edge(self, src, dest, edge_type, additional_data=None):
231
+ """Add an edge to the CFG edge list with validation"""
232
+ if src is None or dest is None:
233
+ logger.error(f"Attempting to add edge with None: {src} -> {dest}")
234
+ return
235
+
236
+ if additional_data:
237
+ self.CFG_edge_list.append((src, dest, edge_type, additional_data))
238
+ else:
239
+ self.CFG_edge_list.append((src, dest, edge_type))
240
+
241
+ def track_function_pointers(self, root_node):
242
+ """
243
+ Track function pointer assignments in the program.
244
+ Records mappings like: fptr -> add when we see fptr = &add
245
+ """
246
+ if root_node.type == "assignment_expression":
247
+ left = root_node.child_by_field_name("left")
248
+ right = root_node.child_by_field_name("right")
249
+
250
+ if left and right:
251
+ if right.type == "pointer_expression":
252
+ arg = right.child_by_field_name("argument")
253
+ if arg and arg.type == "identifier":
254
+ ptr_var = left.text.decode("utf-8")
255
+ target_func = arg.text.decode("utf-8")
256
+ self.records["function_pointer_map"][ptr_var] = target_func
257
+
258
+ for child in root_node.children:
259
+ self.track_function_pointers(child)
260
+
261
+ def function_list(self, root_node, node_list):
262
+ """
263
+ Build a map of all function calls in the program.
264
+ Maps function signatures to their call sites.
265
+
266
+ Handles both direct calls (add(10, 5)) and function pointer calls (fptr(10, 5)).
267
+ """
268
+ if root_node.type == "call_expression":
269
+ function_node = root_node.child_by_field_name("function")
270
+ if function_node:
271
+ if function_node.type == "identifier":
272
+ func_name = function_node.text.decode("utf-8")
273
+
274
+ if func_name in self.records["function_pointer_map"]:
275
+ actual_func_name = self.records["function_pointer_map"][
276
+ func_name
277
+ ]
278
+ func_name = actual_func_name
279
+
280
+ parent_stmt = root_node
281
+ while (
282
+ parent_stmt
283
+ and parent_stmt.type
284
+ not in self.statement_types["node_list_type"]
285
+ ):
286
+ parent_stmt = parent_stmt.parent
287
+
288
+ if (
289
+ parent_stmt
290
+ and (
291
+ parent_stmt.start_point,
292
+ parent_stmt.end_point,
293
+ parent_stmt.type,
294
+ )
295
+ in node_list
296
+ ):
297
+ parent_index = self.get_index(parent_stmt)
298
+ call_index = self.get_index(function_node)
299
+
300
+ args_node = root_node.child_by_field_name("arguments")
301
+ signature = self.get_call_signature(args_node)
302
+
303
+ key = (func_name, signature)
304
+ if key not in self.records["function_calls"]:
305
+ self.records["function_calls"][key] = []
306
+ self.records["function_calls"][key].append(
307
+ (call_index, parent_index)
308
+ )
309
+
310
+ for child in root_node.children:
311
+ self.function_list(child, node_list)
312
+
313
+ def get_argument_type(self, arg_node):
314
+ """
315
+ Infer the type of an argument expression.
316
+ Returns the type as a string, or "unknown" if type cannot be determined.
317
+
318
+ Handles:
319
+ - Identifiers (variables): Look up in symbol_table
320
+ - Literals: Map to C types
321
+ - Cast expressions: Use the cast type
322
+ - Arithmetic/binary expressions: Infer from operands
323
+ - Function calls: Look up return type
324
+ - Pointer/address operations: Modify base type
325
+ - Array subscript: Get element type
326
+ - Field access: Look up field type
327
+ """
328
+
329
+ if arg_node is None:
330
+ return "unknown"
331
+
332
+ node_type = arg_node.type
333
+
334
+ if node_type == "identifier":
335
+ var_name = arg_node.text.decode("utf-8")
336
+
337
+ arg_index_key = (arg_node.start_point, arg_node.end_point, arg_node.type)
338
+ if arg_index_key in self.index:
339
+ arg_index = self.index[arg_index_key]
340
+
341
+ if arg_index in self.declaration_map:
342
+ decl_index = self.declaration_map[arg_index]
343
+ if decl_index in self.symbol_table["data_type"]:
344
+ return self.symbol_table["data_type"][decl_index]
345
+
346
+ for decl_idx, decl_name in self.declaration.items():
347
+ if decl_name == var_name:
348
+ if decl_idx in self.symbol_table["data_type"]:
349
+ var_type = self.symbol_table["data_type"][decl_idx]
350
+ if hasattr(self.parser, "expand_typedef"):
351
+ return self.parser.expand_typedef(var_type)
352
+ return var_type
353
+
354
+ return "unknown"
355
+
356
+ elif node_type == "number_literal":
357
+ text = arg_node.text.decode("utf-8").lower()
358
+ if "." in text or "e" in text:
359
+ if text.endswith("f"):
360
+ return "float"
361
+ return "double"
362
+ else:
363
+ if text.endswith("ll") or text.endswith("LL"):
364
+ return "long long"
365
+ elif text.endswith("l") or text.endswith("L"):
366
+ return "long"
367
+ elif text.endswith("u") or text.endswith("U"):
368
+ return "unsigned int"
369
+ else:
370
+ return "int"
371
+
372
+ elif node_type == "string_literal":
373
+ return "char*"
374
+
375
+ elif node_type == "char_literal":
376
+ return "char"
377
+
378
+ elif node_type == "true" or node_type == "false":
379
+ return "int"
380
+
381
+ elif node_type == "null":
382
+ return "void*"
383
+
384
+ elif node_type == "cast_expression":
385
+ type_desc = arg_node.child_by_field_name("type")
386
+ if type_desc:
387
+ for child in type_desc.children:
388
+ if child.type in [
389
+ "primitive_type",
390
+ "type_identifier",
391
+ "sized_type_specifier",
392
+ ]:
393
+ return child.text.decode("utf-8")
394
+ return "unknown"
395
+
396
+ elif node_type == "pointer_expression":
397
+ operand = arg_node.child_by_field_name("argument")
398
+ if operand:
399
+ operand_type = self.get_argument_type(operand)
400
+ if operand_type.endswith("*"):
401
+ return operand_type[:-1].strip()
402
+ return "unknown"
403
+
404
+ elif node_type == "unary_expression":
405
+ operator = arg_node.child_by_field_name("operator")
406
+ if operator and operator.text.decode("utf-8") == "&":
407
+ operand = arg_node.child_by_field_name("argument")
408
+ if operand:
409
+ operand_type = self.get_argument_type(operand)
410
+ if operand_type != "unknown":
411
+ return operand_type + "*"
412
+ return "unknown"
413
+
414
+ elif node_type == "binary_expression":
415
+ left = arg_node.child_by_field_name("left")
416
+ right = arg_node.child_by_field_name("right")
417
+
418
+ if left and right:
419
+ left_type = self.get_argument_type(left)
420
+ right_type = self.get_argument_type(right)
421
+
422
+ if "double" in left_type or "double" in right_type:
423
+ return "double"
424
+ elif "float" in left_type or "float" in right_type:
425
+ return "float"
426
+ elif "long" in left_type or "long" in right_type:
427
+ return "long"
428
+ elif left_type != "unknown":
429
+ return left_type
430
+ elif right_type != "unknown":
431
+ return right_type
432
+
433
+ return "int"
434
+
435
+ elif node_type == "subscript_expression":
436
+ array = arg_node.child_by_field_name("argument")
437
+ if array:
438
+ array_type = self.get_argument_type(array)
439
+ if array_type.endswith("[]"):
440
+ return array_type[:-2]
441
+ elif array_type.endswith("*"):
442
+ return array_type[:-1].strip()
443
+ return "unknown"
444
+
445
+ elif node_type == "field_expression":
446
+ field = arg_node.child_by_field_name("field")
447
+ argument = arg_node.child_by_field_name("argument")
448
+
449
+ if field and field.type in ["field_identifier", "identifier"]:
450
+ field_name = field.text.decode("utf-8")
451
+
452
+ if argument:
453
+ struct_type = self.get_argument_type(argument)
454
+
455
+ base_type = struct_type.rstrip("*").strip()
456
+
457
+ if hasattr(self.parser, "struct_definitions"):
458
+ field_type = self.parser.get_struct_field_type(
459
+ base_type, field_name
460
+ )
461
+ if field_type != "unknown":
462
+ return field_type
463
+
464
+ return "unknown"
465
+
466
+ elif node_type == "call_expression":
467
+ function = arg_node.child_by_field_name("function")
468
+ if function and function.type == "identifier":
469
+ func_name = function.text.decode("utf-8")
470
+
471
+ for (name, sig), return_type in self.records["return_type"].items():
472
+ if name == func_name:
473
+ return return_type if return_type else "unknown"
474
+
475
+ return "unknown"
476
+
477
+ elif node_type == "parenthesized_expression":
478
+ for child in arg_node.named_children:
479
+ return self.get_argument_type(child)
480
+ return "unknown"
481
+
482
+ elif node_type == "sizeof_expression":
483
+ return "size_t"
484
+
485
+ elif node_type == "conditional_expression":
486
+ consequence = arg_node.child_by_field_name("consequence")
487
+ if consequence:
488
+ return self.get_argument_type(consequence)
489
+ return "unknown"
490
+
491
+ elif node_type == "comma_expression":
492
+ children = list(arg_node.named_children)
493
+ if children:
494
+ return self.get_argument_type(children[-1])
495
+ return "unknown"
496
+
497
+ elif node_type == "update_expression":
498
+ argument = arg_node.child_by_field_name("argument")
499
+ if argument:
500
+ return self.get_argument_type(argument)
501
+ return "unknown"
502
+
503
+ else:
504
+ return "unknown"
505
+
506
+ def get_call_signature(self, args_node):
507
+ """
508
+ Extract the signature (parameter types) from a function call.
509
+ Uses type inference to determine actual argument types.
510
+
511
+ Returns: tuple of type strings, e.g., ("int", "char*", "double")
512
+ """
513
+ signature = []
514
+
515
+ if args_node is None:
516
+ return tuple(signature)
517
+
518
+ for arg in args_node.named_children:
519
+ arg_type = self.get_argument_type(arg)
520
+ signature.append(arg_type)
521
+
522
+ return tuple(signature)
523
+
524
+ def add_function_call_edges(self, node_list):
525
+ """
526
+ Add edges for function calls and returns.
527
+ Connects call sites to function definitions and returns back to callers.
528
+
529
+ Supports:
530
+ - Exact signature matching
531
+ - Variadic functions
532
+ - Fuzzy matching by name when signature can't be inferred
533
+ """
534
+ for (func_name, call_signature), call_sites in self.records[
535
+ "function_calls"
536
+ ].items():
537
+ func_key = (func_name, call_signature)
538
+ func_index = None
539
+
540
+ if func_key in self.records["function_list"]:
541
+ func_index = self.records["function_list"][func_key]
542
+ else:
543
+
544
+ for (def_name, def_signature), idx in self.records[
545
+ "function_list"
546
+ ].items():
547
+ if (
548
+ def_name == func_name
549
+ and len(def_signature) > 0
550
+ and def_signature[-1] == "..."
551
+ ):
552
+ required_params = def_signature[:-1]
553
+ if len(call_signature) >= len(required_params):
554
+ func_index = idx
555
+ break
556
+
557
+ if func_index is None and "unknown" in call_signature:
558
+ for (def_name, def_signature), idx in self.records[
559
+ "function_list"
560
+ ].items():
561
+ if def_name == func_name and len(def_signature) == len(
562
+ call_signature
563
+ ):
564
+ func_index = idx
565
+ break
566
+
567
+ if func_index is not None:
568
+ for call_id, parent_id in call_sites:
569
+ self.add_edge(parent_id, func_index, f"function_call|{call_id}")
570
+
571
+ parent_node = None
572
+ for key, node in node_list.items():
573
+ if self.get_index(node) == parent_id:
574
+ parent_node = node
575
+ break
576
+
577
+ is_parent_return = (
578
+ parent_node and parent_node.type == "return_statement"
579
+ )
580
+
581
+ if func_index in self.records["return_statement_map"]:
582
+ for return_id in self.records["return_statement_map"][
583
+ func_index
584
+ ]:
585
+ if not is_parent_return:
586
+ self.add_edge(return_id, parent_id, "function_return")
587
+
588
+ def find_enclosing_loop(self, node):
589
+ """Find the nearest enclosing loop for break/continue statements"""
590
+ while node:
591
+ if node.type in self.statement_types["loop_control_statement"]:
592
+ return node
593
+ node = node.parent
594
+ return None
595
+
596
+ def CFG_c(self):
597
+ """
598
+ Main CFG construction function for C language.
599
+ Returns (CFG_node_list, CFG_edge_list).
600
+
601
+ This implements the multi-pass algorithm:
602
+ 1. Extract statement nodes from AST
603
+ 2. Create initial sequential edges
604
+ 3. Create basic blocks
605
+ 4. Build function call map
606
+ 5. Add dummy nodes (start/exit)
607
+ 6. Add control flow edges
608
+ 7. Add function call edges
609
+ """
610
+
611
+ node_list, self.CFG_node_list, self.records = c_nodes.get_nodes(
612
+ self.root_node,
613
+ node_list={},
614
+ graph_node_list=[],
615
+ index=self.index,
616
+ records=self.records,
617
+ )
618
+
619
+ self.node_list = node_list
620
+
621
+ cfg_excluded_types = [
622
+ "preproc_include",
623
+ "preproc_def",
624
+ "preproc_function_def",
625
+ "preproc_call",
626
+ "preproc_if",
627
+ "preproc_ifdef",
628
+ "preproc_elif",
629
+ "preproc_else",
630
+ "compound_statement",
631
+ ]
632
+
633
+ node_list = {
634
+ key: node
635
+ for key, node in node_list.items()
636
+ if node.type not in cfg_excluded_types
637
+ and not c_nodes.is_function_declaration(node)
638
+ }
639
+
640
+ filtered_cfg_nodes = []
641
+ excluded_indices = set()
642
+ for node_tuple in self.CFG_node_list:
643
+ node_id = node_tuple[0]
644
+ node_found = False
645
+ for key, node in node_list.items():
646
+ if self.get_index(node) == node_id:
647
+ filtered_cfg_nodes.append(node_tuple)
648
+ node_found = True
649
+ break
650
+ if not node_found:
651
+ excluded_indices.add(node_id)
652
+
653
+ self.CFG_node_list = filtered_cfg_nodes
654
+
655
+ for key, node in node_list.items():
656
+ current_index = self.get_index(node)
657
+
658
+ if node.type in self.statement_types["non_control_statement"]:
659
+ if current_index in self.records["switch_child_map"]:
660
+ continue
661
+
662
+ if (
663
+ node.type == "compound_statement"
664
+ and len(list(node.named_children)) == 0
665
+ ):
666
+ continue
667
+
668
+ if self.is_last_in_control_block(node):
669
+ continue
670
+
671
+ next_index, next_node = self.get_next_index(node, node_list)
672
+
673
+ if next_node is not None:
674
+ self.add_edge(current_index, next_index, "next_line")
675
+
676
+ self.get_basic_blocks(self.CFG_node_list, self.CFG_edge_list)
677
+
678
+ updated_node_list = []
679
+ for node_tuple in self.CFG_node_list:
680
+ node_id = node_tuple[0]
681
+ block_idx = self.get_key(node_id, self.records["basic_blocks"])
682
+ if block_idx:
683
+ updated_node_list.append(node_tuple + (block_idx,))
684
+ else:
685
+ updated_node_list.append(node_tuple + (0,))
686
+ self.CFG_node_list = updated_node_list
687
+
688
+ for key, node in node_list.items():
689
+ if node.type == "function_definition":
690
+ func_name = c_nodes.get_function_name(node)
691
+ if func_name == "main":
692
+ self.records["main_function"] = self.get_index(node)
693
+ break
694
+
695
+ self.track_function_pointers(self.root_node)
696
+
697
+ self.function_list(self.root_node, node_list)
698
+
699
+ for key, node in node_list.items():
700
+ if node.type == "return_statement":
701
+ func_node = self.get_containing_function(node)
702
+ if func_node:
703
+ func_index = self.get_index(func_node)
704
+ if func_index not in self.records["return_statement_map"]:
705
+ self.records["return_statement_map"][func_index] = []
706
+ self.records["return_statement_map"][func_index].append(
707
+ self.get_index(node)
708
+ )
709
+
710
+ for key, node in node_list.items():
711
+ if node.type == "function_definition":
712
+ func_index = self.get_index(node)
713
+ last_line_result = self.get_block_last_line(node, "body")
714
+ if last_line_result:
715
+ last_node, _ = last_line_result
716
+ if (
717
+ last_node
718
+ and (last_node.start_point, last_node.end_point, last_node.type)
719
+ in node_list
720
+ ):
721
+ last_index = self.get_index(last_node)
722
+ if func_index not in self.records["return_statement_map"]:
723
+ self.records["return_statement_map"][func_index] = []
724
+ if (
725
+ last_index
726
+ not in self.records["return_statement_map"][func_index]
727
+ ):
728
+ self.records["return_statement_map"][func_index].append(
729
+ last_index
730
+ )
731
+
732
+ self.CFG_node_list.append((1, 0, "start_node", "start"))
733
+
734
+ for key, node in node_list.items():
735
+ current_index = self.get_index(node)
736
+
737
+ if node.type == "function_definition":
738
+ func_name = c_nodes.get_function_name(node)
739
+
740
+ if "main_function" in self.records:
741
+ if func_name == "main":
742
+ self.add_edge(1, current_index, "next")
743
+ else:
744
+ self.add_edge(1, current_index, "next")
745
+
746
+ first_line_result = self.edge_first_line(node, node_list)
747
+ if first_line_result:
748
+ first_index, _ = first_line_result
749
+ self.add_edge(current_index, first_index, "first_next_line")
750
+
751
+ elif node.type == "if_statement":
752
+ consequence = node.child_by_field_name("consequence")
753
+ if consequence:
754
+ if consequence.type == "compound_statement":
755
+ children_list = list(consequence.named_children)
756
+ if children_list:
757
+ first_stmt = children_list[0]
758
+ if (
759
+ first_stmt.start_point,
760
+ first_stmt.end_point,
761
+ first_stmt.type,
762
+ ) in node_list:
763
+ self.add_edge(
764
+ current_index,
765
+ self.get_index(first_stmt),
766
+ "pos_next",
767
+ )
768
+ else:
769
+ next_index, _ = self.get_next_index(node, node_list)
770
+ if next_index != 2:
771
+ self.add_edge(current_index, next_index, "pos_next")
772
+ else:
773
+ if (
774
+ consequence.start_point,
775
+ consequence.end_point,
776
+ consequence.type,
777
+ ) in node_list:
778
+ self.add_edge(
779
+ current_index, self.get_index(consequence), "pos_next"
780
+ )
781
+
782
+ last_node, _ = self.get_block_last_line(node, "consequence")
783
+ if (
784
+ last_node
785
+ and (last_node.start_point, last_node.end_point, last_node.type)
786
+ in node_list
787
+ ):
788
+ if last_node.type not in [
789
+ "return_statement",
790
+ "break_statement",
791
+ "continue_statement",
792
+ "goto_statement",
793
+ ]:
794
+ outermost_if = node
795
+ parent = node.parent
796
+ while parent and parent.type == "if_statement":
797
+ parent_alt = parent.child_by_field_name("alternative")
798
+ if parent_alt == outermost_if:
799
+ outermost_if = parent
800
+ parent = parent.parent
801
+ else:
802
+ break
803
+
804
+ next_index, _ = self.get_next_index(outermost_if, node_list)
805
+ if next_index != 2:
806
+ self.add_edge(
807
+ self.get_index(last_node), next_index, "next_line"
808
+ )
809
+
810
+ alternative = node.child_by_field_name("alternative")
811
+ if alternative:
812
+ alt_content = None
813
+ for child in alternative.named_children:
814
+ alt_content = child
815
+ break
816
+
817
+ if alt_content is None:
818
+ next_index, _ = self.get_next_index(node, node_list)
819
+ if next_index != 2:
820
+ self.add_edge(current_index, next_index, "neg_next")
821
+ elif alt_content.type == "if_statement":
822
+ self.add_edge(
823
+ current_index, self.get_index(alt_content), "neg_next"
824
+ )
825
+ elif alt_content.type == "compound_statement":
826
+ children_list = list(alt_content.named_children)
827
+ if children_list:
828
+ first_stmt = children_list[0]
829
+ if (
830
+ first_stmt.start_point,
831
+ first_stmt.end_point,
832
+ first_stmt.type,
833
+ ) in node_list:
834
+ self.add_edge(
835
+ current_index,
836
+ self.get_index(first_stmt),
837
+ "neg_next",
838
+ )
839
+ else:
840
+ next_index, _ = self.get_next_index(node, node_list)
841
+ if next_index != 2:
842
+ self.add_edge(current_index, next_index, "neg_next")
843
+
844
+ last_node = None
845
+ if children_list:
846
+ last_stmt = children_list[-1]
847
+ if (
848
+ last_stmt.start_point,
849
+ last_stmt.end_point,
850
+ last_stmt.type,
851
+ ) in node_list:
852
+ last_node = last_stmt
853
+
854
+ if (
855
+ last_node
856
+ and (
857
+ last_node.start_point,
858
+ last_node.end_point,
859
+ last_node.type,
860
+ )
861
+ in node_list
862
+ ):
863
+ if last_node.type not in [
864
+ "return_statement",
865
+ "break_statement",
866
+ "continue_statement",
867
+ "goto_statement",
868
+ ]:
869
+ outermost_if = node
870
+ parent = node.parent
871
+ while parent and parent.type == "if_statement":
872
+ parent_alt = parent.child_by_field_name(
873
+ "alternative"
874
+ )
875
+ if parent_alt:
876
+ parent_alt_content = None
877
+ for child in parent_alt.named_children:
878
+ parent_alt_content = child
879
+ break
880
+ if parent_alt_content == outermost_if:
881
+ outermost_if = parent
882
+ parent = parent.parent
883
+ else:
884
+ break
885
+ else:
886
+ break
887
+
888
+ next_index, _ = self.get_next_index(
889
+ outermost_if, node_list
890
+ )
891
+ if next_index != 2:
892
+ self.add_edge(
893
+ self.get_index(last_node),
894
+ next_index,
895
+ "next_line",
896
+ )
897
+ else:
898
+ if (
899
+ alt_content.start_point,
900
+ alt_content.end_point,
901
+ alt_content.type,
902
+ ) in node_list:
903
+ self.add_edge(
904
+ current_index, self.get_index(alt_content), "neg_next"
905
+ )
906
+
907
+ if alt_content.type not in [
908
+ "return_statement",
909
+ "break_statement",
910
+ "continue_statement",
911
+ "goto_statement",
912
+ ]:
913
+ outermost_if = node
914
+ parent = node.parent
915
+ while parent and parent.type == "if_statement":
916
+ parent_alt = parent.child_by_field_name(
917
+ "alternative"
918
+ )
919
+ if parent_alt:
920
+ parent_alt_content = None
921
+ for child in parent_alt.named_children:
922
+ parent_alt_content = child
923
+ break
924
+ if parent_alt_content == outermost_if:
925
+ outermost_if = parent
926
+ parent = parent.parent
927
+ else:
928
+ break
929
+ else:
930
+ break
931
+
932
+ next_index, _ = self.get_next_index(
933
+ outermost_if, node_list
934
+ )
935
+ if next_index != 2:
936
+ self.add_edge(
937
+ self.get_index(alt_content),
938
+ next_index,
939
+ "next_line",
940
+ )
941
+ else:
942
+ next_index, _ = self.get_next_index(node, node_list)
943
+ if next_index != 2:
944
+ self.add_edge(current_index, next_index, "neg_next")
945
+
946
+ elif node.type == "while_statement":
947
+ body = node.child_by_field_name("body")
948
+ if body:
949
+ if body.type == "compound_statement":
950
+ children_list = list(body.named_children)
951
+ if children_list:
952
+ first_stmt = children_list[0]
953
+ if (
954
+ first_stmt.start_point,
955
+ first_stmt.end_point,
956
+ first_stmt.type,
957
+ ) in node_list:
958
+ self.add_edge(
959
+ current_index,
960
+ self.get_index(first_stmt),
961
+ "pos_next",
962
+ )
963
+ else:
964
+ if (body.start_point, body.end_point, body.type) in node_list:
965
+ self.add_edge(
966
+ current_index, self.get_index(body), "pos_next"
967
+ )
968
+
969
+ last_node, _ = self.get_block_last_line(node, "body")
970
+ if (
971
+ last_node
972
+ and (last_node.start_point, last_node.end_point, last_node.type)
973
+ in node_list
974
+ ):
975
+ if last_node.type not in [
976
+ "break_statement",
977
+ "return_statement",
978
+ "goto_statement",
979
+ ]:
980
+ self.add_edge(
981
+ self.get_index(last_node), current_index, "loop_control"
982
+ )
983
+
984
+ next_index, _ = self.get_next_index(node, node_list)
985
+ if next_index != 2:
986
+ self.add_edge(current_index, next_index, "neg_next")
987
+
988
+ self.add_edge(current_index, current_index, "loop_update")
989
+
990
+ elif node.type == "for_statement":
991
+ body = node.child_by_field_name("body")
992
+ if body:
993
+ if body.type == "compound_statement":
994
+ children_list = list(body.named_children)
995
+ if children_list:
996
+ first_stmt = children_list[0]
997
+ if (
998
+ first_stmt.start_point,
999
+ first_stmt.end_point,
1000
+ first_stmt.type,
1001
+ ) in node_list:
1002
+ self.add_edge(
1003
+ current_index,
1004
+ self.get_index(first_stmt),
1005
+ "pos_next",
1006
+ )
1007
+ else:
1008
+ if (body.start_point, body.end_point, body.type) in node_list:
1009
+ self.add_edge(
1010
+ current_index, self.get_index(body), "pos_next"
1011
+ )
1012
+
1013
+ last_node, _ = self.get_block_last_line(node, "body")
1014
+ if (
1015
+ last_node
1016
+ and (last_node.start_point, last_node.end_point, last_node.type)
1017
+ in node_list
1018
+ ):
1019
+ if last_node.type not in [
1020
+ "break_statement",
1021
+ "return_statement",
1022
+ "goto_statement",
1023
+ ]:
1024
+ self.add_edge(
1025
+ self.get_index(last_node), current_index, "loop_control"
1026
+ )
1027
+
1028
+ next_index, _ = self.get_next_index(node, node_list)
1029
+ if next_index != 2:
1030
+ self.add_edge(current_index, next_index, "neg_next")
1031
+
1032
+ self.add_edge(current_index, current_index, "loop_update")
1033
+
1034
+ elif node.type == "do_statement":
1035
+ body = node.child_by_field_name("body")
1036
+ if body:
1037
+ if body.type == "compound_statement":
1038
+ children_list = list(body.named_children)
1039
+ if children_list:
1040
+ first_stmt = children_list[0]
1041
+ if (
1042
+ first_stmt.start_point,
1043
+ first_stmt.end_point,
1044
+ first_stmt.type,
1045
+ ) in node_list:
1046
+ self.add_edge(
1047
+ current_index,
1048
+ self.get_index(first_stmt),
1049
+ "pos_next",
1050
+ )
1051
+ else:
1052
+ if (body.start_point, body.end_point, body.type) in node_list:
1053
+ self.add_edge(
1054
+ current_index, self.get_index(body), "pos_next"
1055
+ )
1056
+
1057
+ last_node, _ = self.get_block_last_line(node, "body")
1058
+ if (
1059
+ last_node
1060
+ and (last_node.start_point, last_node.end_point, last_node.type)
1061
+ in node_list
1062
+ ):
1063
+ condition = node.child_by_field_name("condition")
1064
+ if condition:
1065
+ cond_key = (
1066
+ condition.start_point,
1067
+ condition.end_point,
1068
+ condition.type,
1069
+ )
1070
+ if cond_key in node_list:
1071
+ cond_index = self.get_index(condition)
1072
+ self.add_edge(
1073
+ self.get_index(last_node), cond_index, "next_line"
1074
+ )
1075
+
1076
+ condition = node.child_by_field_name("condition")
1077
+ if condition:
1078
+ cond_key = (
1079
+ condition.start_point,
1080
+ condition.end_point,
1081
+ condition.type,
1082
+ )
1083
+ if cond_key in node_list:
1084
+ cond_index = self.get_index(condition)
1085
+ self.add_edge(cond_index, current_index, "loop_control")
1086
+ next_index, _ = self.get_next_index(node, node_list)
1087
+ if next_index != 2:
1088
+ self.add_edge(cond_index, next_index, "neg_next")
1089
+
1090
+ elif node.type == "switch_statement":
1091
+ body = node.child_by_field_name("body")
1092
+ if body:
1093
+ case_statements = []
1094
+ default_stmt = None
1095
+
1096
+ def find_cases(n):
1097
+ if n.type == "case_statement":
1098
+ case_statements.append(n)
1099
+ if n.children and n.children[0].type == "default":
1100
+ nonlocal default_stmt
1101
+ default_stmt = n
1102
+ for child in n.children:
1103
+ find_cases(child)
1104
+
1105
+ find_cases(body)
1106
+
1107
+ for case in case_statements:
1108
+ if (case.start_point, case.end_point, case.type) in node_list:
1109
+ self.add_edge(
1110
+ current_index, self.get_index(case), "switch_case"
1111
+ )
1112
+
1113
+ if not default_stmt:
1114
+ next_index, _ = self.get_next_index(node, node_list)
1115
+ if next_index != 2:
1116
+ self.add_edge(current_index, next_index, "switch_exit")
1117
+
1118
+ elif node.type == "case_statement":
1119
+ children = list(node.named_children)
1120
+
1121
+ is_default = node.children and node.children[0].type == "default"
1122
+
1123
+ start_index = 0 if is_default else 1
1124
+
1125
+ if len(children) > start_index:
1126
+ for child in children[start_index:]:
1127
+ if (
1128
+ child.start_point,
1129
+ child.end_point,
1130
+ child.type,
1131
+ ) in node_list:
1132
+ self.add_edge(
1133
+ current_index, self.get_index(child), "case_next"
1134
+ )
1135
+ break
1136
+
1137
+ elif node.type == "break_statement":
1138
+ parent = node.parent
1139
+ while parent:
1140
+ if (
1141
+ parent.type in self.statement_types["loop_control_statement"]
1142
+ or parent.type == "switch_statement"
1143
+ ):
1144
+ next_index, _ = self.get_next_index(parent, node_list)
1145
+ if next_index != 2:
1146
+ self.add_edge(current_index, next_index, "jump_next")
1147
+ break
1148
+ parent = parent.parent
1149
+
1150
+ elif node.type == "continue_statement":
1151
+ loop_node = self.find_enclosing_loop(node)
1152
+ if (
1153
+ loop_node
1154
+ and (loop_node.start_point, loop_node.end_point, loop_node.type)
1155
+ in node_list
1156
+ ):
1157
+ self.add_edge(current_index, self.get_index(loop_node), "jump_next")
1158
+
1159
+ elif node.type == "goto_statement":
1160
+ label_node = node.child_by_field_name("label")
1161
+ if label_node:
1162
+ label_name = label_node.text.decode("UTF-8")
1163
+ if label_name in self.records["label_statement_map"]:
1164
+ target_key = self.records["label_statement_map"][label_name]
1165
+ if target_key in node_list:
1166
+ target_node = node_list[target_key]
1167
+ self.add_edge(
1168
+ current_index, self.get_index(target_node), "jump_next"
1169
+ )
1170
+
1171
+ elif node.type == "labeled_statement":
1172
+ if len(node.named_children) > 1:
1173
+ stmt = node.named_children[1]
1174
+ if (
1175
+ stmt
1176
+ and (stmt.start_point, stmt.end_point, stmt.type) in node_list
1177
+ ):
1178
+ self.add_edge(current_index, self.get_index(stmt), "next_line")
1179
+
1180
+ self.add_function_call_edges(node_list)
1181
+
1182
+ return self.CFG_node_list, self.CFG_edge_list