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.
- lambda_graphs/__init__.py +18 -0
- lambda_graphs/__main__.py +7 -0
- lambda_graphs/cli.py +183 -0
- lambda_graphs/codeviews/AST/AST.py +120 -0
- lambda_graphs/codeviews/AST/AST_driver.py +37 -0
- lambda_graphs/codeviews/AST/__init__.py +0 -0
- lambda_graphs/codeviews/CFG/CFG.py +42 -0
- lambda_graphs/codeviews/CFG/CFG_c.py +1182 -0
- lambda_graphs/codeviews/CFG/CFG_cpp.py +5604 -0
- lambda_graphs/codeviews/CFG/CFG_driver.py +45 -0
- lambda_graphs/codeviews/CFG/CFG_java.py +1722 -0
- lambda_graphs/codeviews/CFG/__init__.py +0 -0
- lambda_graphs/codeviews/CST/CST_driver.py +70 -0
- lambda_graphs/codeviews/CST/__init__.py +0 -0
- lambda_graphs/codeviews/DFG/DFG_driver.py +42 -0
- lambda_graphs/codeviews/DFG/__init__.py +0 -0
- lambda_graphs/codeviews/SDFG/SDFG.py +135 -0
- lambda_graphs/codeviews/SDFG/SDFG_c.py +2041 -0
- lambda_graphs/codeviews/SDFG/SDFG_cpp.py +4030 -0
- lambda_graphs/codeviews/SDFG/SDFG_java.py +1618 -0
- lambda_graphs/codeviews/SDFG/__init__.py +0 -0
- lambda_graphs/codeviews/__init__.py +0 -0
- lambda_graphs/codeviews/combined_graph/__init__.py +0 -0
- lambda_graphs/codeviews/combined_graph/combined_driver.py +163 -0
- lambda_graphs/tree_parser/__init__.py +0 -0
- lambda_graphs/tree_parser/c_parser.py +565 -0
- lambda_graphs/tree_parser/cpp_parser.py +424 -0
- lambda_graphs/tree_parser/custom_parser.py +66 -0
- lambda_graphs/tree_parser/java_parser.py +254 -0
- lambda_graphs/tree_parser/parser_driver.py +54 -0
- lambda_graphs/utils/DFG_utils.py +44 -0
- lambda_graphs/utils/__init__.py +0 -0
- lambda_graphs/utils/c_nodes.py +431 -0
- lambda_graphs/utils/cpp_nodes.py +1331 -0
- lambda_graphs/utils/java_nodes.py +756 -0
- lambda_graphs/utils/multi_file_merger.py +444 -0
- lambda_graphs/utils/postprocessor.py +83 -0
- lambda_graphs/utils/preprocessor.py +68 -0
- lambda_graphs/utils/src_parser.py +23 -0
- lambda_graphs-0.1.4.dist-info/METADATA +380 -0
- lambda_graphs-0.1.4.dist-info/RECORD +45 -0
- lambda_graphs-0.1.4.dist-info/WHEEL +5 -0
- lambda_graphs-0.1.4.dist-info/entry_points.txt +2 -0
- lambda_graphs-0.1.4.dist-info/licenses/LICENSE +21 -0
- 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
|