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,1722 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
import networkx as nx
|
|
3
|
+
from .CFG import CFGGraph
|
|
4
|
+
from ...utils import java_nodes
|
|
5
|
+
from loguru import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CFGGraph_java(CFGGraph):
|
|
9
|
+
def __init__(self, src_language, src_code, properties, root_node, parser):
|
|
10
|
+
super().__init__(src_language, src_code, properties, root_node, parser)
|
|
11
|
+
|
|
12
|
+
self.node_list = None
|
|
13
|
+
self.statement_types = java_nodes.statement_types
|
|
14
|
+
self.CFG_node_list = []
|
|
15
|
+
self.CFG_edge_list = []
|
|
16
|
+
self.records = {
|
|
17
|
+
"basic_blocks": {},
|
|
18
|
+
"method_list": {},
|
|
19
|
+
"constructor_list": {},
|
|
20
|
+
"return_type": {},
|
|
21
|
+
"class_list": {},
|
|
22
|
+
"extends": {},
|
|
23
|
+
"function_calls": {},
|
|
24
|
+
"constructor_calls": {},
|
|
25
|
+
"object_instantiate": {},
|
|
26
|
+
"switch_child_map": {},
|
|
27
|
+
"label_statement_map": {},
|
|
28
|
+
"return_statement_map": {},
|
|
29
|
+
"lambda_map": {},
|
|
30
|
+
}
|
|
31
|
+
self.types = ["scoped_type_identifier", "type_identifier", "generic_type"]
|
|
32
|
+
self.index_counter = max(self.index.values())
|
|
33
|
+
self.CFG_node_indices = []
|
|
34
|
+
self.symbol_table = self.parser.symbol_table
|
|
35
|
+
self.declaration = self.parser.declaration
|
|
36
|
+
self.declaration_map = self.parser.declaration_map
|
|
37
|
+
self.CFG_node_list, self.CFG_edge_list = self.CFG_java()
|
|
38
|
+
self.graph = self.to_networkx(self.CFG_node_list, self.CFG_edge_list)
|
|
39
|
+
|
|
40
|
+
def get_index(self, node):
|
|
41
|
+
return self.index[(node.start_point, node.end_point, node.type)]
|
|
42
|
+
|
|
43
|
+
def check_inner_class(self, node):
|
|
44
|
+
while node.parent is not None:
|
|
45
|
+
if node.parent.type == "class_declaration":
|
|
46
|
+
return True
|
|
47
|
+
node = node.parent
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
def get_basic_blocks(self, CFG_node_list, CFG_edge_list):
|
|
51
|
+
G = self.to_networkx(CFG_node_list, CFG_edge_list)
|
|
52
|
+
components = nx.weakly_connected_components(G)
|
|
53
|
+
# NOTE: May need to sort these components according to line number (this one is more foolproof but harder to implement) or the first element in the set (less foolproof, but I think it can be proved, easier to implement).
|
|
54
|
+
# As of now coincidentally the AST node numbering and the list of nodes in networkx are according ot the line numbers
|
|
55
|
+
block_index = 1
|
|
56
|
+
for block in components:
|
|
57
|
+
block_list = sorted(list(block))
|
|
58
|
+
self.records["basic_blocks"][block_index] = block_list
|
|
59
|
+
block_index += 1
|
|
60
|
+
|
|
61
|
+
def get_key(self, val, dictionary):
|
|
62
|
+
for key, value in dictionary.items():
|
|
63
|
+
if val in value:
|
|
64
|
+
return key
|
|
65
|
+
|
|
66
|
+
def get_containing_method(self, node):
|
|
67
|
+
while node is not None:
|
|
68
|
+
if node.type == "lambda_expression":
|
|
69
|
+
return node
|
|
70
|
+
if (
|
|
71
|
+
node.type == "method_declaration"
|
|
72
|
+
or node.type == "constructor_declaration"
|
|
73
|
+
):
|
|
74
|
+
return node
|
|
75
|
+
if node.type == "static_initializer":
|
|
76
|
+
return node
|
|
77
|
+
node = node.parent
|
|
78
|
+
while node is not None:
|
|
79
|
+
if node.type in self.statement_types["node_list_type"]:
|
|
80
|
+
return node
|
|
81
|
+
node = node.parent
|
|
82
|
+
|
|
83
|
+
def get_lambda_body(self, node):
|
|
84
|
+
"""Returns the body of a lambda expression, breadthfirst"""
|
|
85
|
+
bfs_queue = []
|
|
86
|
+
bfs_queue.append(node)
|
|
87
|
+
while bfs_queue != []:
|
|
88
|
+
top = bfs_queue.pop(0)
|
|
89
|
+
if top.type == "lambda_expression":
|
|
90
|
+
return top
|
|
91
|
+
for child in top.children:
|
|
92
|
+
bfs_queue.append(child)
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
def get_all_lambda_body(self, node):
|
|
96
|
+
"""Returns the body of a lambda expression, breadthfirst"""
|
|
97
|
+
bfs_queue = []
|
|
98
|
+
output = []
|
|
99
|
+
bfs_queue.append(node)
|
|
100
|
+
while bfs_queue != []:
|
|
101
|
+
top = bfs_queue.pop(0)
|
|
102
|
+
if top.type == "lambda_expression":
|
|
103
|
+
output.append(top)
|
|
104
|
+
# return [top]
|
|
105
|
+
for child in top.children:
|
|
106
|
+
if (
|
|
107
|
+
child.type == "lambda_expression"
|
|
108
|
+
or child.type not in self.statement_types["node_list_type"]
|
|
109
|
+
):
|
|
110
|
+
bfs_queue.append(child)
|
|
111
|
+
|
|
112
|
+
return output
|
|
113
|
+
|
|
114
|
+
def append_block_index(self, CFG_node_list):
|
|
115
|
+
new_list = []
|
|
116
|
+
for node in CFG_node_list:
|
|
117
|
+
block_index = self.get_key(node[0], self.records["basic_blocks"])
|
|
118
|
+
new_list.append((node[0], node[1], node[2], node[3], block_index))
|
|
119
|
+
return new_list
|
|
120
|
+
|
|
121
|
+
def add_edge(self, src_node, dest_node, edge_type, additional_data=None):
|
|
122
|
+
# current_node_list = list(map(lambda x: x[0], self.CFG_node_list))
|
|
123
|
+
if src_node == None or dest_node == None:
|
|
124
|
+
logger.error(
|
|
125
|
+
"Node where adding edge is attempted is none {}->{}",
|
|
126
|
+
src_node,
|
|
127
|
+
dest_node,
|
|
128
|
+
)
|
|
129
|
+
logger.warning(traceback.format_stack()[-2])
|
|
130
|
+
# print(src_node, dest_node, edge_type)
|
|
131
|
+
raise NotImplementedError
|
|
132
|
+
else:
|
|
133
|
+
for src, dest, ty, *_ in self.CFG_edge_list:
|
|
134
|
+
if src == src_node and dest == dest_node:
|
|
135
|
+
return
|
|
136
|
+
self.CFG_edge_list.append((src_node, dest_node, edge_type, additional_data))
|
|
137
|
+
|
|
138
|
+
def handle_next(self, src_node, dest_node, edge_type):
|
|
139
|
+
if dest_node == None:
|
|
140
|
+
try:
|
|
141
|
+
current_containing_method = self.get_index(
|
|
142
|
+
self.get_containing_method(src_node)
|
|
143
|
+
)
|
|
144
|
+
except:
|
|
145
|
+
return
|
|
146
|
+
try:
|
|
147
|
+
self.records["return_statement_map"][current_containing_method].append(
|
|
148
|
+
self.get_index(src_node)
|
|
149
|
+
)
|
|
150
|
+
except:
|
|
151
|
+
self.records["return_statement_map"][current_containing_method] = [
|
|
152
|
+
self.get_index(src_node)
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
else:
|
|
156
|
+
self.add_edge(
|
|
157
|
+
self.get_index(src_node), self.get_index(dest_node), edge_type
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def get_next_index(self, node_value):
|
|
161
|
+
# If exiting a method, don't return
|
|
162
|
+
next_node = node_value.next_named_sibling
|
|
163
|
+
while (
|
|
164
|
+
next_node is not None
|
|
165
|
+
and next_node.type == "block"
|
|
166
|
+
and len(list(filter(lambda child: child.is_named, next_node.children))) == 0
|
|
167
|
+
):
|
|
168
|
+
next_node = next_node.next_named_sibling
|
|
169
|
+
if next_node is not None and next_node.type == "class_declaration":
|
|
170
|
+
# Check if its a local class
|
|
171
|
+
if self.check_inner_class(node_value):
|
|
172
|
+
next_node = next_node.next_named_sibling
|
|
173
|
+
next_node_index = -1 # Just using a dummy initial value
|
|
174
|
+
if next_node == None:
|
|
175
|
+
current_node = node_value
|
|
176
|
+
while current_node.parent is not None:
|
|
177
|
+
if (
|
|
178
|
+
current_node.parent.type
|
|
179
|
+
in self.statement_types["loop_control_statement"]
|
|
180
|
+
):
|
|
181
|
+
next_node = current_node.parent
|
|
182
|
+
break
|
|
183
|
+
if current_node.parent.type == "if_statement":
|
|
184
|
+
next_node_index, next_node = self.get_next_index(
|
|
185
|
+
current_node.parent
|
|
186
|
+
)
|
|
187
|
+
break
|
|
188
|
+
next_node = current_node.next_named_sibling
|
|
189
|
+
if current_node.type in self.statement_types["definition_types"]:
|
|
190
|
+
next_node = None
|
|
191
|
+
break
|
|
192
|
+
if next_node is not None:
|
|
193
|
+
break
|
|
194
|
+
current_node = current_node.parent
|
|
195
|
+
|
|
196
|
+
if next_node == None:
|
|
197
|
+
return 2, None
|
|
198
|
+
if next_node.type in self.statement_types["definition_types"]:
|
|
199
|
+
return 2, None
|
|
200
|
+
try:
|
|
201
|
+
# WARNING: Might need to replace if with while
|
|
202
|
+
if next_node.type == "block":
|
|
203
|
+
for child in next_node.children:
|
|
204
|
+
if child.is_named:
|
|
205
|
+
next_node = child
|
|
206
|
+
break
|
|
207
|
+
# Returns the index of the next node and the node object of the next node
|
|
208
|
+
try:
|
|
209
|
+
current_containing_method = self.get_index(
|
|
210
|
+
self.get_containing_method(node_value)
|
|
211
|
+
)
|
|
212
|
+
next_containing_method = self.get_index(
|
|
213
|
+
self.get_containing_method(next_node)
|
|
214
|
+
)
|
|
215
|
+
except:
|
|
216
|
+
return 2, None
|
|
217
|
+
if current_containing_method != next_containing_method:
|
|
218
|
+
return 2, None
|
|
219
|
+
else:
|
|
220
|
+
return (self.get_index(next_node), next_node)
|
|
221
|
+
|
|
222
|
+
except Exception as e:
|
|
223
|
+
# return 2, None
|
|
224
|
+
# print("DO NOT IGNORE", e)
|
|
225
|
+
logger.warning(traceback.format_stack()[-2])
|
|
226
|
+
raise NotImplementedError
|
|
227
|
+
return next_node_index, next_node
|
|
228
|
+
|
|
229
|
+
def edge_first_line(self, current_node_key, current_node_value):
|
|
230
|
+
# We need to add an edge to the first statement in the next basic block
|
|
231
|
+
node_index = self.index[current_node_key]
|
|
232
|
+
try:
|
|
233
|
+
current_block_index = self.get_key(node_index, self.records["basic_blocks"])
|
|
234
|
+
next_block_index = current_block_index + 1
|
|
235
|
+
first_line_index = self.records["basic_blocks"][next_block_index][0]
|
|
236
|
+
src_node = node_index
|
|
237
|
+
dest_node = first_line_index
|
|
238
|
+
self.add_edge(
|
|
239
|
+
src_node, dest_node, "first_next_line"
|
|
240
|
+
) # We could maybe differentiate this
|
|
241
|
+
except:
|
|
242
|
+
# Most probably the block is empty
|
|
243
|
+
# add a direct edge to the next statement
|
|
244
|
+
if current_node_value.type == "method_declaration":
|
|
245
|
+
return
|
|
246
|
+
next_index, next_node = self.get_next_index(current_node_value)
|
|
247
|
+
self.handle_next(current_node_value, next_node, "next_line 9")
|
|
248
|
+
|
|
249
|
+
def edge_to_body(self, current_node_key, current_node_value, body_type, edge_type):
|
|
250
|
+
# We need to add an edge to the first statement in the body block
|
|
251
|
+
src_node = self.index[current_node_key]
|
|
252
|
+
body_node = current_node_value.child_by_field_name(body_type)
|
|
253
|
+
flag = False
|
|
254
|
+
while body_node.type == "block":
|
|
255
|
+
for child in body_node.children:
|
|
256
|
+
if child.is_named:
|
|
257
|
+
flag = True
|
|
258
|
+
body_node = child
|
|
259
|
+
break
|
|
260
|
+
if flag == False:
|
|
261
|
+
return
|
|
262
|
+
if (
|
|
263
|
+
body_node.is_named
|
|
264
|
+
and body_node.type in self.statement_types["node_list_type"]
|
|
265
|
+
):
|
|
266
|
+
dest_node = self.get_index(body_node)
|
|
267
|
+
self.add_edge(src_node, dest_node, edge_type)
|
|
268
|
+
|
|
269
|
+
def get_block_last_line(self, current_node_value, block_type):
|
|
270
|
+
# Find the last line in the body block
|
|
271
|
+
block_node = current_node_value.child_by_field_name(block_type)
|
|
272
|
+
if block_node is None:
|
|
273
|
+
for child in reversed(current_node_value.children):
|
|
274
|
+
if child.is_named:
|
|
275
|
+
block_node = child
|
|
276
|
+
break
|
|
277
|
+
|
|
278
|
+
if block_node.is_named is False:
|
|
279
|
+
return (current_node_value, current_node_value.type)
|
|
280
|
+
|
|
281
|
+
while block_node.type in self.statement_types["statement_holders"]:
|
|
282
|
+
named_children = list(
|
|
283
|
+
filter(
|
|
284
|
+
lambda child: child.is_named == True, reversed(block_node.children)
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
if len(named_children) == 0:
|
|
288
|
+
# It means there is an empty block - thats why no named nodes inside
|
|
289
|
+
return (current_node_value, current_node_value.type)
|
|
290
|
+
|
|
291
|
+
block_node = named_children[0]
|
|
292
|
+
if block_node.type in self.statement_types["node_list_type"]:
|
|
293
|
+
return (block_node, block_node.type)
|
|
294
|
+
return (block_node, block_node.type)
|
|
295
|
+
|
|
296
|
+
def get_class_name(self, node):
|
|
297
|
+
reference_node = node.child_by_field_name("object")
|
|
298
|
+
types = ["scoped_type_identifier", "type_identifier", "generic_type"]
|
|
299
|
+
|
|
300
|
+
if reference_node is None or reference_node.type == "this":
|
|
301
|
+
while node is not None:
|
|
302
|
+
if node.type == "class_declaration":
|
|
303
|
+
# class_index = self.get_index(node)
|
|
304
|
+
class_name = list(
|
|
305
|
+
filter(lambda child: child.type == "identifier", node.children)
|
|
306
|
+
)[0]
|
|
307
|
+
class_name = [class_name.text.decode("UTF-8")]
|
|
308
|
+
break
|
|
309
|
+
node = node.parent
|
|
310
|
+
try:
|
|
311
|
+
class_name += self.records["extends"][class_name[0]]
|
|
312
|
+
except:
|
|
313
|
+
pass
|
|
314
|
+
return class_name
|
|
315
|
+
try:
|
|
316
|
+
reference_index = self.get_index(reference_node)
|
|
317
|
+
declaration_index = self.declaration_map[reference_index]
|
|
318
|
+
class_name = [self.symbol_table["data_type"][declaration_index]]
|
|
319
|
+
try:
|
|
320
|
+
class_name += self.records["extends"][class_name[0]]
|
|
321
|
+
|
|
322
|
+
except:
|
|
323
|
+
pass
|
|
324
|
+
return class_name
|
|
325
|
+
|
|
326
|
+
except:
|
|
327
|
+
# If a static method has been explicitly called on a class name
|
|
328
|
+
if reference_node is not None:
|
|
329
|
+
if reference_node.type == "object_creation_expression":
|
|
330
|
+
class_name = list(
|
|
331
|
+
filter(
|
|
332
|
+
lambda child: child.type in types, reference_node.children
|
|
333
|
+
)
|
|
334
|
+
)[0]
|
|
335
|
+
class_name = [class_name.text.decode("UTF-8")]
|
|
336
|
+
else:
|
|
337
|
+
class_name = [reference_node.text.decode("UTF-8")]
|
|
338
|
+
try:
|
|
339
|
+
class_name += self.records["extends"][class_name[0]]
|
|
340
|
+
except:
|
|
341
|
+
pass
|
|
342
|
+
return class_name
|
|
343
|
+
|
|
344
|
+
return None
|
|
345
|
+
|
|
346
|
+
def get_return_type(self, current_node):
|
|
347
|
+
method_name = current_node.child_by_field_name("name").text.decode("UTF-8")
|
|
348
|
+
class_name = self.get_class_name(current_node)
|
|
349
|
+
if class_name is not None:
|
|
350
|
+
class_name = class_name[0]
|
|
351
|
+
method_name = (class_name, method_name)
|
|
352
|
+
else:
|
|
353
|
+
method_name = (None, method_name)
|
|
354
|
+
signature = ()
|
|
355
|
+
argument_list = current_node.child_by_field_name("arguments")
|
|
356
|
+
argument_list = list(
|
|
357
|
+
filter(lambda child: child.is_named, argument_list.children)
|
|
358
|
+
)
|
|
359
|
+
signature = self.get_signature(argument_list)
|
|
360
|
+
function_key = (method_name, signature)
|
|
361
|
+
try:
|
|
362
|
+
data_type = self.records["return_type"][function_key]
|
|
363
|
+
except:
|
|
364
|
+
data_type = "void"
|
|
365
|
+
# records["return_type"][((class_name,method_name), signature)] = return_type
|
|
366
|
+
return data_type
|
|
367
|
+
|
|
368
|
+
def get_signature(self, argument_list):
|
|
369
|
+
literal_type_map = {
|
|
370
|
+
"character_literal": "char",
|
|
371
|
+
"string_literal": "String",
|
|
372
|
+
"decimal_integer_literal": "int",
|
|
373
|
+
"boolean": "boolean",
|
|
374
|
+
"decimal_floating_point_literal": "double", # If float, won't work
|
|
375
|
+
}
|
|
376
|
+
signature = []
|
|
377
|
+
for argument in argument_list:
|
|
378
|
+
if argument.type == "identifier":
|
|
379
|
+
identifier_index = self.get_index(argument)
|
|
380
|
+
try:
|
|
381
|
+
declaration_index = self.declaration_map[identifier_index]
|
|
382
|
+
data_type = self.symbol_table["data_type"][declaration_index]
|
|
383
|
+
except Exception as e:
|
|
384
|
+
data_type = "Unknown"
|
|
385
|
+
signature.append(data_type)
|
|
386
|
+
elif argument.type == "method_invocation":
|
|
387
|
+
try:
|
|
388
|
+
data_type = self.get_return_type(argument)
|
|
389
|
+
except:
|
|
390
|
+
data_type = "Unknown"
|
|
391
|
+
signature.append(data_type)
|
|
392
|
+
elif argument.type == "field_access":
|
|
393
|
+
field_variable = argument.children[-1]
|
|
394
|
+
identifier_index = self.get_index(field_variable)
|
|
395
|
+
try:
|
|
396
|
+
declaration_index = self.declaration_map[identifier_index]
|
|
397
|
+
data_type = self.symbol_table["data_type"][declaration_index]
|
|
398
|
+
except Exception as e:
|
|
399
|
+
data_type = "Unknown"
|
|
400
|
+
signature.append(data_type)
|
|
401
|
+
elif argument.type == "this":
|
|
402
|
+
signature.append(self.get_class_name(argument)[0])
|
|
403
|
+
else:
|
|
404
|
+
try:
|
|
405
|
+
signature.append(literal_type_map[argument.type])
|
|
406
|
+
except:
|
|
407
|
+
signature.append("Unknown")
|
|
408
|
+
return tuple(signature)
|
|
409
|
+
|
|
410
|
+
def function_list(self, current_node, node_list):
|
|
411
|
+
current_index = self.get_index(current_node)
|
|
412
|
+
if current_node.type == "method_invocation":
|
|
413
|
+
parent_node = None
|
|
414
|
+
pointer_node = current_node
|
|
415
|
+
while pointer_node is not None:
|
|
416
|
+
if (
|
|
417
|
+
pointer_node.parent is not None
|
|
418
|
+
and pointer_node.parent.type
|
|
419
|
+
in self.statement_types["node_list_type"]
|
|
420
|
+
):
|
|
421
|
+
try:
|
|
422
|
+
p = pointer_node.parent
|
|
423
|
+
parent_node = node_list[(p.start_point, p.end_point, p.type)]
|
|
424
|
+
break
|
|
425
|
+
except Exception as e:
|
|
426
|
+
pass
|
|
427
|
+
pointer_node = pointer_node.parent
|
|
428
|
+
parent_index = self.get_index(parent_node)
|
|
429
|
+
# maintain a list of all method invocations
|
|
430
|
+
# IDENTIFY THE CLASS THAT THE ALIAS BELONGS TO AND USE THAT IN THE MAP MAYBE?
|
|
431
|
+
base_method_name = current_node.child_by_field_name("name").text.decode(
|
|
432
|
+
"UTF-8"
|
|
433
|
+
)
|
|
434
|
+
signature = ()
|
|
435
|
+
argument_list = current_node.child_by_field_name("arguments")
|
|
436
|
+
argument_list = list(
|
|
437
|
+
filter(lambda child: child.is_named, argument_list.children)
|
|
438
|
+
)
|
|
439
|
+
signature = self.get_signature(argument_list)
|
|
440
|
+
|
|
441
|
+
class_name_list = self.get_class_name(current_node)
|
|
442
|
+
if class_name_list is None:
|
|
443
|
+
method_name = (None, base_method_name)
|
|
444
|
+
function_key = (method_name, signature)
|
|
445
|
+
if method_name[1] != "println" and method_name[1] != "print":
|
|
446
|
+
if function_key in self.records["function_calls"].keys():
|
|
447
|
+
self.records["function_calls"][function_key].append(
|
|
448
|
+
(current_index, parent_index)
|
|
449
|
+
)
|
|
450
|
+
else:
|
|
451
|
+
self.records["function_calls"][function_key] = [
|
|
452
|
+
(current_index, parent_index)
|
|
453
|
+
]
|
|
454
|
+
else:
|
|
455
|
+
for class_name in class_name_list:
|
|
456
|
+
method_name = (class_name, base_method_name)
|
|
457
|
+
function_key = (method_name, signature)
|
|
458
|
+
if method_name[1] != "println" and method_name[1] != "print":
|
|
459
|
+
if function_key in self.records["function_calls"].keys():
|
|
460
|
+
self.records["function_calls"][function_key].append(
|
|
461
|
+
(current_index, parent_index)
|
|
462
|
+
)
|
|
463
|
+
else:
|
|
464
|
+
self.records["function_calls"][function_key] = [
|
|
465
|
+
(current_index, parent_index)
|
|
466
|
+
]
|
|
467
|
+
elif current_node.type == "object_creation_expression":
|
|
468
|
+
parent_node = None
|
|
469
|
+
pointer_node = current_node
|
|
470
|
+
while pointer_node is not None:
|
|
471
|
+
if (
|
|
472
|
+
pointer_node.parent is not None
|
|
473
|
+
and pointer_node.parent.type
|
|
474
|
+
in self.statement_types["node_list_type"]
|
|
475
|
+
):
|
|
476
|
+
try:
|
|
477
|
+
p = pointer_node.parent
|
|
478
|
+
parent_node = node_list[(p.start_point, p.end_point, p.type)]
|
|
479
|
+
# parent_node = pointer_node.parent
|
|
480
|
+
break
|
|
481
|
+
except Exception as e:
|
|
482
|
+
pass
|
|
483
|
+
pointer_node = pointer_node.parent
|
|
484
|
+
parent_index = self.get_index(parent_node)
|
|
485
|
+
type_name = list(
|
|
486
|
+
filter(lambda child: child.type in self.types, current_node.children)
|
|
487
|
+
)
|
|
488
|
+
type_name = type_name[0].text.decode("utf-8")
|
|
489
|
+
try:
|
|
490
|
+
self.records["object_instantiate"][type_name].append(
|
|
491
|
+
(current_index, parent_index)
|
|
492
|
+
)
|
|
493
|
+
except:
|
|
494
|
+
self.records["object_instantiate"][type_name] = [
|
|
495
|
+
(current_index, parent_index)
|
|
496
|
+
]
|
|
497
|
+
# break
|
|
498
|
+
signature = ()
|
|
499
|
+
argument_list = current_node.child_by_field_name("arguments")
|
|
500
|
+
argument_list = list(
|
|
501
|
+
filter(lambda child: child.is_named, argument_list.children)
|
|
502
|
+
)
|
|
503
|
+
signature = self.get_signature(argument_list)
|
|
504
|
+
method_name = (type_name, type_name)
|
|
505
|
+
function_key = (method_name, signature)
|
|
506
|
+
if function_key in self.records["constructor_calls"].keys():
|
|
507
|
+
self.records["constructor_calls"][function_key].append(
|
|
508
|
+
(current_index, parent_index)
|
|
509
|
+
)
|
|
510
|
+
else:
|
|
511
|
+
self.records["constructor_calls"][function_key] = [
|
|
512
|
+
(current_index, parent_index)
|
|
513
|
+
]
|
|
514
|
+
|
|
515
|
+
elif current_node.type == "explicit_constructor_invocation":
|
|
516
|
+
parent_node = current_node
|
|
517
|
+
parent_index = self.get_index(parent_node)
|
|
518
|
+
type_name = current_node.child_by_field_name("constructor").text.decode(
|
|
519
|
+
"utf-8"
|
|
520
|
+
)
|
|
521
|
+
if type_name == "this": # TODO: Add super also here for now
|
|
522
|
+
type_name_list = self.get_class_name(current_node)
|
|
523
|
+
for type_name in type_name_list:
|
|
524
|
+
# TODO: Replace this condition with a method level check flag
|
|
525
|
+
if type_name != "test":
|
|
526
|
+
try:
|
|
527
|
+
self.records["object_instantiate"][type_name].append(
|
|
528
|
+
(current_index, current_index)
|
|
529
|
+
)
|
|
530
|
+
except:
|
|
531
|
+
self.records["object_instantiate"][type_name] = [
|
|
532
|
+
(current_index, current_index)
|
|
533
|
+
]
|
|
534
|
+
|
|
535
|
+
signature = ()
|
|
536
|
+
argument_list = current_node.child_by_field_name("arguments")
|
|
537
|
+
argument_list = list(
|
|
538
|
+
filter(lambda child: child.is_named, argument_list.children)
|
|
539
|
+
)
|
|
540
|
+
signature = self.get_signature(argument_list)
|
|
541
|
+
method_name = (type_name, type_name)
|
|
542
|
+
function_key = (method_name, signature)
|
|
543
|
+
if function_key in self.records["constructor_calls"].keys():
|
|
544
|
+
self.records["constructor_calls"][function_key].append(
|
|
545
|
+
(current_index, parent_index)
|
|
546
|
+
)
|
|
547
|
+
else:
|
|
548
|
+
self.records["constructor_calls"][function_key] = [
|
|
549
|
+
(current_index, parent_index)
|
|
550
|
+
]
|
|
551
|
+
|
|
552
|
+
elif type_name == "super":
|
|
553
|
+
pass
|
|
554
|
+
else:
|
|
555
|
+
raise Exception("Explicit constructor invocation not handled")
|
|
556
|
+
|
|
557
|
+
elif (
|
|
558
|
+
current_node.type == "method_declaration"
|
|
559
|
+
or current_node.type == "constructor_declaration"
|
|
560
|
+
):
|
|
561
|
+
last_line, _ = self.get_block_last_line(current_node, "body")
|
|
562
|
+
try:
|
|
563
|
+
self.records["return_statement_map"][current_index].append(
|
|
564
|
+
self.get_index(last_line)
|
|
565
|
+
)
|
|
566
|
+
except:
|
|
567
|
+
self.records["return_statement_map"][current_index] = [
|
|
568
|
+
self.get_index(last_line)
|
|
569
|
+
]
|
|
570
|
+
|
|
571
|
+
elif current_node.type == "class_declaration":
|
|
572
|
+
# If constructor exits, find the last line of the constructor
|
|
573
|
+
# If no constructor, find the last line before a method starts
|
|
574
|
+
# If empty of if only methods, then just return from the class
|
|
575
|
+
constructor_node = None
|
|
576
|
+
constructor_count = 0
|
|
577
|
+
empty_flag = True
|
|
578
|
+
last_statement = None
|
|
579
|
+
class_node = current_node.child_by_field_name("body")
|
|
580
|
+
class_children = list(
|
|
581
|
+
filter(
|
|
582
|
+
lambda child: child.is_named
|
|
583
|
+
and child.type != "method_declaration"
|
|
584
|
+
and child.type != "class_declaration",
|
|
585
|
+
class_node.children,
|
|
586
|
+
)
|
|
587
|
+
)
|
|
588
|
+
for child in reversed(class_children):
|
|
589
|
+
if child.type in self.statement_types["node_list_type"]:
|
|
590
|
+
empty_flag = False
|
|
591
|
+
if (
|
|
592
|
+
last_statement is None
|
|
593
|
+
and child.type != "constructor_declaration"
|
|
594
|
+
):
|
|
595
|
+
last_statement = child
|
|
596
|
+
if child.type == "constructor_declaration":
|
|
597
|
+
constructor_node = child
|
|
598
|
+
constructor_count += 1
|
|
599
|
+
break
|
|
600
|
+
if empty_flag == True:
|
|
601
|
+
try:
|
|
602
|
+
self.records["return_statement_map"][current_index].append(
|
|
603
|
+
current_index
|
|
604
|
+
)
|
|
605
|
+
except:
|
|
606
|
+
self.records["return_statement_map"][current_index] = [
|
|
607
|
+
current_index
|
|
608
|
+
]
|
|
609
|
+
elif constructor_count == 1:
|
|
610
|
+
last_line, _ = self.get_block_last_line(constructor_node, "body")
|
|
611
|
+
try:
|
|
612
|
+
self.records["return_statement_map"][current_index].append(
|
|
613
|
+
self.get_index(last_line)
|
|
614
|
+
)
|
|
615
|
+
except:
|
|
616
|
+
self.records["return_statement_map"][current_index] = [
|
|
617
|
+
self.get_index(last_line)
|
|
618
|
+
]
|
|
619
|
+
elif last_statement is not None:
|
|
620
|
+
last_index = self.get_index(last_statement)
|
|
621
|
+
try:
|
|
622
|
+
self.records["return_statement_map"][current_index].append(
|
|
623
|
+
last_index
|
|
624
|
+
)
|
|
625
|
+
except:
|
|
626
|
+
self.records["return_statement_map"][current_index] = [last_index]
|
|
627
|
+
elif constructor_node is not None:
|
|
628
|
+
last_line, _ = self.get_block_last_line(constructor_node, "body")
|
|
629
|
+
try:
|
|
630
|
+
self.records["return_statement_map"][current_index].append(
|
|
631
|
+
self.get_index(last_line)
|
|
632
|
+
)
|
|
633
|
+
except:
|
|
634
|
+
self.records["return_statement_map"][current_index] = [
|
|
635
|
+
self.get_index(last_line)
|
|
636
|
+
]
|
|
637
|
+
|
|
638
|
+
for child in current_node.children:
|
|
639
|
+
if child.is_named:
|
|
640
|
+
self.function_list(child, node_list)
|
|
641
|
+
|
|
642
|
+
def get_all_statements(self, current_node, node_list, statements):
|
|
643
|
+
for child in current_node.children:
|
|
644
|
+
if child.is_named and child.type in self.statement_types["node_list_type"]:
|
|
645
|
+
child_key = (child.start_point, child.end_point, child.type)
|
|
646
|
+
if child_key in node_list.keys():
|
|
647
|
+
statements.append(child)
|
|
648
|
+
self.get_all_statements(child, node_list, statements)
|
|
649
|
+
|
|
650
|
+
return statements
|
|
651
|
+
|
|
652
|
+
def add_dummy_nodes(self):
|
|
653
|
+
self.CFG_node_list.append((1, 0, "start_node", "start"))
|
|
654
|
+
# self.CFG_node_list.append((2, 0, "exit_node", "exit"))
|
|
655
|
+
|
|
656
|
+
def get_signature_nodes(self, node):
|
|
657
|
+
signature = []
|
|
658
|
+
formal_parameters = node.child_by_field_name("parameters")
|
|
659
|
+
formal_parameters = list(
|
|
660
|
+
filter(lambda x: x.type == "formal_parameter", formal_parameters.children)
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
for formal_parameter in formal_parameters:
|
|
664
|
+
for child in formal_parameter.children:
|
|
665
|
+
if child.type != "identifier":
|
|
666
|
+
signature.append(child.text.decode("utf-8"))
|
|
667
|
+
return tuple(signature)
|
|
668
|
+
|
|
669
|
+
def get_class_name_nodes(self, node):
|
|
670
|
+
type_identifiers = ["type_identifier", "generic_type", "scoped_type_identifier"]
|
|
671
|
+
"Returns the class name when a method declaration or constructor declaration is passed to it"
|
|
672
|
+
|
|
673
|
+
while node is not None:
|
|
674
|
+
if node.type == "class_body" and node.parent.type == "class_declaration":
|
|
675
|
+
node = node.parent
|
|
676
|
+
class_index = self.index[(node.start_point, node.end_point, node.type)]
|
|
677
|
+
class_name = list(
|
|
678
|
+
filter(lambda child: child.type == "identifier", node.children)
|
|
679
|
+
)[0]
|
|
680
|
+
return class_index, class_name.text.decode("UTF-8")
|
|
681
|
+
elif (
|
|
682
|
+
node.type == "class_body"
|
|
683
|
+
and node.parent.type == "object_creation_expression"
|
|
684
|
+
):
|
|
685
|
+
node = node.parent
|
|
686
|
+
class_index = self.index[(node.start_point, node.end_point, node.type)]
|
|
687
|
+
class_name = list(
|
|
688
|
+
filter(lambda child: child.type in type_identifiers, node.children)
|
|
689
|
+
)[0]
|
|
690
|
+
return class_index, class_name.text.decode("UTF-8")
|
|
691
|
+
node = node.parent
|
|
692
|
+
|
|
693
|
+
def inner_function(self, current_node):
|
|
694
|
+
"""Returns true if the current node is inside an inner function"""
|
|
695
|
+
method_counter = 0
|
|
696
|
+
while current_node is not None:
|
|
697
|
+
if current_node.type == "method_declaration":
|
|
698
|
+
method_counter += 1
|
|
699
|
+
current_node = current_node.parent
|
|
700
|
+
if method_counter > 1:
|
|
701
|
+
return True
|
|
702
|
+
else:
|
|
703
|
+
return False
|
|
704
|
+
|
|
705
|
+
def returns_inner_definition(self, node, inner_node):
|
|
706
|
+
"""Returns the inner definition index if exists"""
|
|
707
|
+
for c in node.children:
|
|
708
|
+
if c.is_named and c.type == "method_declaration":
|
|
709
|
+
method_name = list(
|
|
710
|
+
filter(lambda child: child.type == "identifier", c.children)
|
|
711
|
+
)[0].text.decode("utf-8")
|
|
712
|
+
_, class_name = self.get_class_name_nodes(c)
|
|
713
|
+
signature = self.get_signature_nodes(c)
|
|
714
|
+
# TODO: Take out the common unitility functions like get class name and signture
|
|
715
|
+
inner_node.append(
|
|
716
|
+
self.records["method_list"][((class_name, method_name), signature)]
|
|
717
|
+
)
|
|
718
|
+
return
|
|
719
|
+
else:
|
|
720
|
+
self.returns_inner_definition(c, inner_node)
|
|
721
|
+
return
|
|
722
|
+
|
|
723
|
+
# def add_dummy_edges(self):
|
|
724
|
+
# for node_name, node_index in self.records["function_calls"].items():
|
|
725
|
+
# # node_index[0] is suppposed to be the statement node number of the function call
|
|
726
|
+
# # node_index[1] is the dummy index value assigned to the dummy node created for the external function call
|
|
727
|
+
# for node in node_index:
|
|
728
|
+
# if node_name not in self.records["method_list"].keys():
|
|
729
|
+
# self.add_edge(node[1], node[0], "function_call")
|
|
730
|
+
# self.add_edge(node[0], node[1], "function_return")
|
|
731
|
+
# else:
|
|
732
|
+
# self.add_edge(
|
|
733
|
+
# node[1],
|
|
734
|
+
# self.records["method_list"][node_name],
|
|
735
|
+
# "recursive_method_call",
|
|
736
|
+
# )
|
|
737
|
+
|
|
738
|
+
def get_matched_constructor(self, node):
|
|
739
|
+
for node_signature, node_ind in self.records["constructor_calls"].items():
|
|
740
|
+
for node_search in node_ind:
|
|
741
|
+
if node == node_search:
|
|
742
|
+
if node_signature in self.records["constructor_list"].keys():
|
|
743
|
+
method_index = self.records["constructor_list"][node_signature]
|
|
744
|
+
return method_index
|
|
745
|
+
|
|
746
|
+
for node_signature, node_ind in self.records["constructor_calls"].items():
|
|
747
|
+
for node_search in node_ind:
|
|
748
|
+
flag = False
|
|
749
|
+
if node == node_search:
|
|
750
|
+
for list_signature, list_index in self.records[
|
|
751
|
+
"constructor_list"
|
|
752
|
+
].items():
|
|
753
|
+
flag = True
|
|
754
|
+
list_signature_list = list(list_signature[1])
|
|
755
|
+
node_signature_list = list(node_signature[1])
|
|
756
|
+
if len(list_signature_list) != len(node_signature_list):
|
|
757
|
+
flag = False
|
|
758
|
+
else:
|
|
759
|
+
for i in range(len(list_signature_list)):
|
|
760
|
+
if (
|
|
761
|
+
list_signature_list[i] != "Unknown"
|
|
762
|
+
and node_signature_list[i] != "Unknown"
|
|
763
|
+
and list_signature_list[i] != node_signature_list[i]
|
|
764
|
+
):
|
|
765
|
+
flag = False
|
|
766
|
+
break
|
|
767
|
+
if flag == True:
|
|
768
|
+
return list_index
|
|
769
|
+
|
|
770
|
+
def add_method_call_edges(self):
|
|
771
|
+
# print("Method List")
|
|
772
|
+
# print(*self.records["method_list"].items(), sep="\n")
|
|
773
|
+
# print("Constructor List")
|
|
774
|
+
# print(*self.records["constructor_list"].items(), sep="\n")
|
|
775
|
+
# print("Function Calls")
|
|
776
|
+
# print(*self.records["function_calls"].items(), sep="\n")
|
|
777
|
+
# print("Object instantiations")
|
|
778
|
+
# print(*self.records["object_instantiate"].items(), sep="\n")
|
|
779
|
+
# print("Constuctor Calls")
|
|
780
|
+
# print(*self.records["constructor_calls"].items(), sep="\n")
|
|
781
|
+
# print("extends")
|
|
782
|
+
# print(self.records["extends"])
|
|
783
|
+
# print("Declaration Map")
|
|
784
|
+
# print(self.declaration_map)
|
|
785
|
+
# print("SymboL_TABLE.DATATYPES")
|
|
786
|
+
# print(self.symbol_table["data_type"])
|
|
787
|
+
# print("declaration")
|
|
788
|
+
# print(self.declaration)
|
|
789
|
+
# print("____________________________________________________________________________")
|
|
790
|
+
# print("return_statement_map")
|
|
791
|
+
# print(self.records["return_statement_map"])
|
|
792
|
+
|
|
793
|
+
for node_signature, node_index in self.records["function_calls"].items():
|
|
794
|
+
# node_index[1] is suppposed to be the statement node number of the function call
|
|
795
|
+
# node_index[0] is the dummy index value assigned to the dummy node created for the external function call
|
|
796
|
+
# Update: node_index[0] is no longer dummy index but the ast id of the function invocation node
|
|
797
|
+
for node in node_index:
|
|
798
|
+
if node_signature in self.records["method_list"].keys():
|
|
799
|
+
method_index = self.records["method_list"][node_signature]
|
|
800
|
+
# Find the class name before indexing records["method_list"]
|
|
801
|
+
edge_type = "method_call|" + str(node[0])
|
|
802
|
+
self.add_edge(
|
|
803
|
+
node[1], self.records["method_list"][node_signature], edge_type
|
|
804
|
+
)
|
|
805
|
+
# Add the returning edge
|
|
806
|
+
# Use the return statement index available in records. And add an edge from all the return statements to the calling line here
|
|
807
|
+
try:
|
|
808
|
+
for return_node in self.records["return_statement_map"][
|
|
809
|
+
method_index
|
|
810
|
+
]:
|
|
811
|
+
self.add_edge(return_node, node[1], "method_return")
|
|
812
|
+
except:
|
|
813
|
+
# If you can't find return statements, then add an edge from the last line
|
|
814
|
+
# Handled by adding the last line to the return statement map in self.function_calls
|
|
815
|
+
pass
|
|
816
|
+
for node_name, node_index in self.records["object_instantiate"].items():
|
|
817
|
+
for node in node_index:
|
|
818
|
+
if node_name in self.records["class_list"].keys():
|
|
819
|
+
class_index = self.records["class_list"][node_name]
|
|
820
|
+
edge_type = "constructor_call|" + str(node[0])
|
|
821
|
+
additional_data_index = self.get_matched_constructor(node)
|
|
822
|
+
additional_data = {}
|
|
823
|
+
if additional_data_index is not None:
|
|
824
|
+
additional_data = {"target_constructor": additional_data_index}
|
|
825
|
+
# for node_signature, node_ind in self.records["constructor_calls"].items():
|
|
826
|
+
# for node_search in node_ind:
|
|
827
|
+
# if node == node_search:
|
|
828
|
+
# if node_signature in self.records["constructor_list"].keys():
|
|
829
|
+
# method_index = self.records["constructor_list"][node_signature]
|
|
830
|
+
# additional_data = {"target_constructor": method_index}
|
|
831
|
+
# print(node_signature, method_index)
|
|
832
|
+
# break
|
|
833
|
+
# if additional_data:
|
|
834
|
+
# break
|
|
835
|
+
# assert additional_data
|
|
836
|
+
# print("ADDING EDGE", node[1], class_index, edge_type, additional_data)
|
|
837
|
+
self.add_edge(
|
|
838
|
+
node[1], class_index, edge_type, additional_data=additional_data
|
|
839
|
+
)
|
|
840
|
+
try:
|
|
841
|
+
for return_node in self.records["return_statement_map"][
|
|
842
|
+
class_index
|
|
843
|
+
]:
|
|
844
|
+
self.add_edge(return_node, node[1], "class_return")
|
|
845
|
+
except Exception as e:
|
|
846
|
+
# If you can't find return statements, then add an edge from the last line
|
|
847
|
+
# Handled by adding the last line to the return statement map in self.function_calls
|
|
848
|
+
pass
|
|
849
|
+
|
|
850
|
+
for node_signature, node_index in self.records["constructor_calls"].items():
|
|
851
|
+
for node in node_index:
|
|
852
|
+
if node_signature in self.records["constructor_list"].keys():
|
|
853
|
+
method_index = self.records["constructor_list"][node_signature]
|
|
854
|
+
try:
|
|
855
|
+
for return_node in self.records["return_statement_map"][
|
|
856
|
+
method_index
|
|
857
|
+
]:
|
|
858
|
+
self.add_edge(return_node, node[1], "class_return")
|
|
859
|
+
except:
|
|
860
|
+
# If you can't find return statements, then add an edge from the last line
|
|
861
|
+
# Handled by adding the last line to the return statement map in self.function_calls
|
|
862
|
+
pass
|
|
863
|
+
|
|
864
|
+
# for lambda expressions
|
|
865
|
+
# for node_key, lambda_expression in self.records["lambda_map"].items():
|
|
866
|
+
for lambda_key, statement_node in self.records["lambda_map"].items():
|
|
867
|
+
# lambda_index = self.index[node_key]
|
|
868
|
+
lambda_index = self.index[lambda_key]
|
|
869
|
+
# self.edge_to_body(node_key, lambda_expression, "body", "lambda_invocation")
|
|
870
|
+
self.add_edge(
|
|
871
|
+
self.get_index(statement_node), lambda_index, "lambda_invocation"
|
|
872
|
+
)
|
|
873
|
+
# TODO: maintain a return map for all points of return
|
|
874
|
+
try:
|
|
875
|
+
for return_node in self.records["return_statement_map"][lambda_index]:
|
|
876
|
+
self.add_edge(
|
|
877
|
+
return_node, self.get_index(statement_node), "lambda_return 1"
|
|
878
|
+
)
|
|
879
|
+
except:
|
|
880
|
+
pass
|
|
881
|
+
for lambda_expression in self.get_all_lambda_body(statement_node):
|
|
882
|
+
# Note: Might need to move this to before the try
|
|
883
|
+
last_line, line_type = self.get_block_last_line(
|
|
884
|
+
lambda_expression, "body"
|
|
885
|
+
)
|
|
886
|
+
if line_type not in self.statement_types["node_list_type"]:
|
|
887
|
+
self.add_edge(
|
|
888
|
+
self.get_index(lambda_expression),
|
|
889
|
+
self.get_index(statement_node),
|
|
890
|
+
"lambda_return 2",
|
|
891
|
+
)
|
|
892
|
+
else:
|
|
893
|
+
last_line_index = self.get_index(last_line)
|
|
894
|
+
self.add_edge(
|
|
895
|
+
last_line_index,
|
|
896
|
+
self.get_index(statement_node),
|
|
897
|
+
"lambda_return 3",
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
def return_next_node(self, node_value):
|
|
901
|
+
# node_value = node_value.parent
|
|
902
|
+
next_node = node_value.next_named_sibling
|
|
903
|
+
while (
|
|
904
|
+
next_node is None
|
|
905
|
+
and node_value.parent.type in self.statement_types["statement_holders"]
|
|
906
|
+
):
|
|
907
|
+
if node_value.parent.parent.type == "method_declaration":
|
|
908
|
+
next_node = node_value.parent.next_named_sibling
|
|
909
|
+
return next_node
|
|
910
|
+
else:
|
|
911
|
+
node_value = node_value.parent
|
|
912
|
+
next_node = node_value.next_named_sibling
|
|
913
|
+
if node_value.parent.type in self.statement_types["statement_holders"]:
|
|
914
|
+
return next_node
|
|
915
|
+
return None
|
|
916
|
+
|
|
917
|
+
def add_class_edge(self, node_value):
|
|
918
|
+
class_attributes = ["field_declaration", "static_initializer"]
|
|
919
|
+
# Not included: ["record_declaration", "method_declaration","compact_constructor_declaration","class_declaration","interface_declaration","annotation_type_declaration","enum_declaration","block","static_initializer","constructor_declaration"]
|
|
920
|
+
current_index = self.get_index(node_value)
|
|
921
|
+
current_node = node_value.child_by_field_name("body")
|
|
922
|
+
flag = False
|
|
923
|
+
# Chain all class level attributes together and append to class declaration.
|
|
924
|
+
current_fields = list(
|
|
925
|
+
filter(lambda x: x.type in class_attributes, current_node.children)
|
|
926
|
+
)
|
|
927
|
+
for field in current_fields:
|
|
928
|
+
if field.type == "static_initializer":
|
|
929
|
+
# Find the first line insdie the block
|
|
930
|
+
block = list(filter(lambda x: x.type == "block", field.children))[0]
|
|
931
|
+
try:
|
|
932
|
+
field = list(filter(lambda x: x.is_named, block.children))[-1]
|
|
933
|
+
except:
|
|
934
|
+
continue
|
|
935
|
+
field_index = self.get_index(field)
|
|
936
|
+
else:
|
|
937
|
+
field_index = self.get_index(field)
|
|
938
|
+
self.add_edge(current_index, field_index, "class_next")
|
|
939
|
+
current_index = field_index
|
|
940
|
+
|
|
941
|
+
# If constructor exists, chain it to the last field
|
|
942
|
+
constructors = list(
|
|
943
|
+
filter(lambda x: x.type == "constructor_declaration", current_node.children)
|
|
944
|
+
)
|
|
945
|
+
for constructor in constructors:
|
|
946
|
+
constructor_index = self.get_index(constructor)
|
|
947
|
+
self.add_edge(current_index, constructor_index, "constructor_next")
|
|
948
|
+
|
|
949
|
+
# In case main method exists, chain main method at the end
|
|
950
|
+
try:
|
|
951
|
+
methods = list(
|
|
952
|
+
filter(lambda x: x.type == "method_declaration", current_node.children)
|
|
953
|
+
)
|
|
954
|
+
for method in methods:
|
|
955
|
+
if self.records["main_method"] == self.get_index(method):
|
|
956
|
+
self.add_edge(
|
|
957
|
+
current_index, self.records["main_method"], "main_method_next"
|
|
958
|
+
)
|
|
959
|
+
except:
|
|
960
|
+
pass
|
|
961
|
+
|
|
962
|
+
def CFG_java(self):
|
|
963
|
+
warning_counter = 0
|
|
964
|
+
node_list = {}
|
|
965
|
+
# node_list is a dictionary that maps from (node.start_point, node.end_point, node.type) to the node object of tree-sitter
|
|
966
|
+
_, self.node_list, self.CFG_node_list, self.records = java_nodes.get_nodes(
|
|
967
|
+
root_node=self.root_node,
|
|
968
|
+
node_list=node_list,
|
|
969
|
+
graph_node_list=self.CFG_node_list,
|
|
970
|
+
index=self.index,
|
|
971
|
+
records=self.records,
|
|
972
|
+
)
|
|
973
|
+
# self.CFG_node_indices = list(map(lambda x: self.index[x], node_list.keys()))
|
|
974
|
+
# Initial for loop required for basic block creation and simple control flow within a block ----------------------------
|
|
975
|
+
for node_key, node_value in node_list.items():
|
|
976
|
+
current_node_type = node_key[2]
|
|
977
|
+
if current_node_type in self.statement_types["non_control_statement"]:
|
|
978
|
+
src_node = self.index[node_key]
|
|
979
|
+
# if node_value.next_named_sibling is not None and node_value.next_named_sibling.type == "static_initializer":
|
|
980
|
+
# if current_node_type == "field_declaration" and node_value.next_named_sibling is not None and node_value.next_named_sibling.type == "constructor_declaration":
|
|
981
|
+
# self.add_edge(src_node, self.get_index(node_value.next_named_sibling), "constructor_next 2")
|
|
982
|
+
if (
|
|
983
|
+
java_nodes.return_switch_child(node_value) is None
|
|
984
|
+
and node_value.parent is not None
|
|
985
|
+
and node_value.parent.type
|
|
986
|
+
in self.statement_types["statement_holders"]
|
|
987
|
+
):
|
|
988
|
+
# There is no switch expression in the subtree starting at this statement node
|
|
989
|
+
try:
|
|
990
|
+
check = False
|
|
991
|
+
next_node = node_value.next_named_sibling
|
|
992
|
+
if (
|
|
993
|
+
next_node is not None
|
|
994
|
+
and next_node.type == "static_initializer"
|
|
995
|
+
):
|
|
996
|
+
try:
|
|
997
|
+
child = list(
|
|
998
|
+
filter(
|
|
999
|
+
lambda x: x.type == "block", next_node.children
|
|
1000
|
+
)
|
|
1001
|
+
)[0]
|
|
1002
|
+
next_node = child
|
|
1003
|
+
except:
|
|
1004
|
+
pass
|
|
1005
|
+
while (
|
|
1006
|
+
next_node is not None
|
|
1007
|
+
and next_node.type == "block"
|
|
1008
|
+
and len(
|
|
1009
|
+
list(
|
|
1010
|
+
filter(
|
|
1011
|
+
lambda child: child.is_named, next_node.children
|
|
1012
|
+
)
|
|
1013
|
+
)
|
|
1014
|
+
)
|
|
1015
|
+
== 0
|
|
1016
|
+
):
|
|
1017
|
+
next_node = next_node.next_named_sibling
|
|
1018
|
+
# If it is a local class, skip it and go for the next node
|
|
1019
|
+
if (
|
|
1020
|
+
next_node is not None
|
|
1021
|
+
and next_node.type == "class_declaration"
|
|
1022
|
+
):
|
|
1023
|
+
# Check if its a local class
|
|
1024
|
+
if self.check_inner_class(node_value):
|
|
1025
|
+
next_node = next_node.next_named_sibling
|
|
1026
|
+
# TODO: This might break next line, check all test cases
|
|
1027
|
+
# if next_node is None and self.get_containing_method(node_value).type == "constructor_declaration":
|
|
1028
|
+
# self.handle_next(node_value, None, "constructor_return")
|
|
1029
|
+
if next_node is None and node_value.parent.type == "block":
|
|
1030
|
+
next_node = self.return_next_node(node_value)
|
|
1031
|
+
dest_node = self.get_index(next_node)
|
|
1032
|
+
check = True
|
|
1033
|
+
if (
|
|
1034
|
+
next_node.type in self.statement_types["node_list_type"]
|
|
1035
|
+
and next_node.type
|
|
1036
|
+
not in self.statement_types["definition_types"]
|
|
1037
|
+
):
|
|
1038
|
+
self.add_edge(src_node, dest_node, "next_line $")
|
|
1039
|
+
|
|
1040
|
+
flag = False
|
|
1041
|
+
while next_node.type == "block" and len(
|
|
1042
|
+
next_node.named_children
|
|
1043
|
+
):
|
|
1044
|
+
for child in next_node.children:
|
|
1045
|
+
if child.is_named:
|
|
1046
|
+
if (
|
|
1047
|
+
child.type
|
|
1048
|
+
in self.statement_types["node_list_type"]
|
|
1049
|
+
):
|
|
1050
|
+
flag = True
|
|
1051
|
+
next_node = child
|
|
1052
|
+
break
|
|
1053
|
+
else:
|
|
1054
|
+
next_node = child
|
|
1055
|
+
break
|
|
1056
|
+
|
|
1057
|
+
if flag == True:
|
|
1058
|
+
break
|
|
1059
|
+
|
|
1060
|
+
dest_node = self.get_index(next_node)
|
|
1061
|
+
if dest_node in self.records["switch_child_map"].keys():
|
|
1062
|
+
dest_node = self.records["switch_child_map"][dest_node]
|
|
1063
|
+
if (
|
|
1064
|
+
check is False
|
|
1065
|
+
and next_node.type in self.statement_types["node_list_type"]
|
|
1066
|
+
and next_node.type
|
|
1067
|
+
not in self.statement_types["definition_types"]
|
|
1068
|
+
):
|
|
1069
|
+
self.add_edge(src_node, dest_node, "next_line 1")
|
|
1070
|
+
|
|
1071
|
+
except:
|
|
1072
|
+
pass
|
|
1073
|
+
|
|
1074
|
+
elif current_node_type in self.statement_types["not_implemented"]:
|
|
1075
|
+
logger.warning("WARNING: Not implemented ", current_node_type)
|
|
1076
|
+
warning_counter += 1
|
|
1077
|
+
|
|
1078
|
+
self.get_basic_blocks(self.CFG_node_list, self.CFG_edge_list)
|
|
1079
|
+
self.CFG_node_list = self.append_block_index(self.CFG_node_list)
|
|
1080
|
+
self.CFG_node_indices = list(map(lambda x: x[0], self.CFG_node_list))
|
|
1081
|
+
self.function_list(self.root_node, node_list)
|
|
1082
|
+
self.add_dummy_nodes()
|
|
1083
|
+
# self.add_dummy_edges()
|
|
1084
|
+
# ------------------------------------------------------------------------------
|
|
1085
|
+
# At this point, the self.CFG_node_list has basic block index appended to it
|
|
1086
|
+
# ------------------------------------------------------------------------------
|
|
1087
|
+
for node_key, node_value in node_list.items():
|
|
1088
|
+
current_node_type = node_key[2]
|
|
1089
|
+
current_index = self.index[node_key]
|
|
1090
|
+
if (
|
|
1091
|
+
current_node_type == "method_declaration"
|
|
1092
|
+
or current_node_type == "constructor_declaration"
|
|
1093
|
+
):
|
|
1094
|
+
try:
|
|
1095
|
+
# If main method exists, this will work
|
|
1096
|
+
if current_index == self.records["main_method"]:
|
|
1097
|
+
self.add_edge(1, self.records["main_class"], "next")
|
|
1098
|
+
# self.add_edge(self.records["main_class"], current_index, "next")
|
|
1099
|
+
except:
|
|
1100
|
+
if node_value.parent.parent.type == "class_declaration":
|
|
1101
|
+
# We need to add an edge to the first statement in the next basic block
|
|
1102
|
+
self.add_edge(1, current_index, "next")
|
|
1103
|
+
|
|
1104
|
+
self.edge_first_line(node_key, node_value)
|
|
1105
|
+
last_line, line_type = self.get_block_last_line(node_value, "body")
|
|
1106
|
+
last_line_index = self.get_index(last_line)
|
|
1107
|
+
try:
|
|
1108
|
+
if current_case_index == self.records["main_method"]:
|
|
1109
|
+
if line_type in self.statement_types["non_control_statement"]:
|
|
1110
|
+
if (
|
|
1111
|
+
last_line_index
|
|
1112
|
+
in self.records["switch_child_map"].keys()
|
|
1113
|
+
):
|
|
1114
|
+
last_line_index = self.records["switch_child_map"][
|
|
1115
|
+
last_line_index
|
|
1116
|
+
]
|
|
1117
|
+
# self.add_edge(last_line_index, 2, "exit_next")
|
|
1118
|
+
except:
|
|
1119
|
+
# No main method in this snippet
|
|
1120
|
+
pass
|
|
1121
|
+
# ------------------------------------------------------------------------------
|
|
1122
|
+
elif current_node_type == "interface_declaration":
|
|
1123
|
+
self.edge_first_line(node_key, node_value)
|
|
1124
|
+
# ------------------------------------------------------------------------------
|
|
1125
|
+
if current_node_type == "class_declaration":
|
|
1126
|
+
self.add_class_edge(node_value)
|
|
1127
|
+
elif current_node_type == "import_declaration":
|
|
1128
|
+
next_node = node_value.next_named_sibling
|
|
1129
|
+
if next_node is not None and next_node.type != "import_declaration":
|
|
1130
|
+
try:
|
|
1131
|
+
self.add_edge(current_index, self.records["main_class"], "next")
|
|
1132
|
+
except:
|
|
1133
|
+
pass
|
|
1134
|
+
# ------------------------------------------------------------------------------------------------
|
|
1135
|
+
elif current_node_type == "synchronized_statement":
|
|
1136
|
+
self.edge_first_line(node_key, node_value)
|
|
1137
|
+
next_dest_index, next_node = self.get_next_index(node_value)
|
|
1138
|
+
last_line, line_type = self.get_block_last_line(node_value, "body")
|
|
1139
|
+
last_line_index = self.get_index(last_line)
|
|
1140
|
+
# if line_type in self.statement_types["non_control_statement"]:
|
|
1141
|
+
# self.add_edge(last_line_index, 2, "exit_next")
|
|
1142
|
+
# Add an edge from start of synchronized to the line after it to signify async execution
|
|
1143
|
+
self.handle_next(node_value, next_node, "sync_next")
|
|
1144
|
+
# ------------------------------------------------------------------------------------------------
|
|
1145
|
+
elif current_node_type == "labeled_statement":
|
|
1146
|
+
self.edge_first_line(node_key, node_value)
|
|
1147
|
+
# ------------------------------------------------------------------------------------------------
|
|
1148
|
+
elif current_node_type == "if_statement":
|
|
1149
|
+
# Find the if block body and the else block body if exists (first statement inside them, add an edge)
|
|
1150
|
+
# Find the line just after the entire if_statement
|
|
1151
|
+
next_dest_index, next_node = self.get_next_index(node_value)
|
|
1152
|
+
# consequence
|
|
1153
|
+
self.edge_to_body(node_key, node_value, "consequence", "pos_next")
|
|
1154
|
+
# Find the last line in the consequence block and add an edge to the next statement
|
|
1155
|
+
last_line, line_type = self.get_block_last_line(
|
|
1156
|
+
node_value, "consequence"
|
|
1157
|
+
)
|
|
1158
|
+
last_line_index = self.get_index(last_line)
|
|
1159
|
+
# Also add an edge from the last guy to the next statement after the if
|
|
1160
|
+
if line_type in self.statement_types["non_control_statement"]:
|
|
1161
|
+
self.handle_next(last_line, next_node, "next_line 2")
|
|
1162
|
+
|
|
1163
|
+
empty_if = False
|
|
1164
|
+
if last_line_index == current_index:
|
|
1165
|
+
empty_if = True
|
|
1166
|
+
self.handle_next(node_value, next_node, "next_line 3")
|
|
1167
|
+
if node_value.child_by_field_name("alternative") is not None:
|
|
1168
|
+
# alternative
|
|
1169
|
+
self.edge_to_body(node_key, node_value, "alternative", "neg_next")
|
|
1170
|
+
# Find the last line in the alternative block
|
|
1171
|
+
last_line, line_type = self.get_block_last_line(
|
|
1172
|
+
node_value, "alternative"
|
|
1173
|
+
)
|
|
1174
|
+
if line_type in self.statement_types["non_control_statement"]:
|
|
1175
|
+
self.handle_next(last_line, next_node, "next_line 4")
|
|
1176
|
+
|
|
1177
|
+
if empty_if and last_line_index == current_index:
|
|
1178
|
+
self.handle_next(node_value, next_node, "next_line 5")
|
|
1179
|
+
else:
|
|
1180
|
+
# When else is not there add a direct edge from if node to the next statement
|
|
1181
|
+
# if next_node is not None:
|
|
1182
|
+
self.handle_next(node_value, next_node, "next_line 6")
|
|
1183
|
+
|
|
1184
|
+
# ------------------------------------------------------------------------------------------------
|
|
1185
|
+
elif current_node_type in self.statement_types["loop_control_statement"]:
|
|
1186
|
+
# Get the node immediately after the while statement
|
|
1187
|
+
next_dest_index, next_node = self.get_next_index(node_value)
|
|
1188
|
+
|
|
1189
|
+
# Add an edge from this node to the first line in the body
|
|
1190
|
+
self.edge_to_body(node_key, node_value, "body", "pos_next")
|
|
1191
|
+
|
|
1192
|
+
# Find the last line in the body block
|
|
1193
|
+
last_line, line_type = self.get_block_last_line(node_value, "body")
|
|
1194
|
+
last_line_index = self.get_index(last_line)
|
|
1195
|
+
# Add an edge from this node to the next line after the loop statement
|
|
1196
|
+
self.handle_next(node_value, next_node, "neg_next")
|
|
1197
|
+
# Add an edge from the last statement in the body to this node
|
|
1198
|
+
if line_type in self.statement_types["non_control_statement"]:
|
|
1199
|
+
self.add_edge(last_line_index, current_index, "loop_control")
|
|
1200
|
+
|
|
1201
|
+
# Add a self loop in case of for loops
|
|
1202
|
+
if current_node_type != "while_statement":
|
|
1203
|
+
self.add_edge(current_index, current_index, "loop_update")
|
|
1204
|
+
|
|
1205
|
+
# ------------------------------------------------------------------------------------------------
|
|
1206
|
+
elif current_node_type == "do_statement":
|
|
1207
|
+
# Find the corresponding while statement
|
|
1208
|
+
# Add an edge from this node to the first line in the body
|
|
1209
|
+
# Find the last statement in the body and an edge frpom last line to the while node
|
|
1210
|
+
# Add an edge from the while node to the first line in the block or to the current do node
|
|
1211
|
+
# Add an edge from the while node to the next statement after the do_statement
|
|
1212
|
+
|
|
1213
|
+
# Get the node immediately after the while statement
|
|
1214
|
+
next_dest_index, next_node = self.get_next_index(node_value)
|
|
1215
|
+
|
|
1216
|
+
# Add an edge from this node to the first line in the body
|
|
1217
|
+
# if next_node is not None:
|
|
1218
|
+
self.edge_to_body(node_key, node_value, "body", "pos_next")
|
|
1219
|
+
|
|
1220
|
+
# Find the last line in the body block
|
|
1221
|
+
last_line, line_type = self.get_block_last_line(node_value, "body")
|
|
1222
|
+
last_line_index = self.get_index(last_line)
|
|
1223
|
+
# Search the CFG_node_list for parameterized_expression with parent do_statement with AST_id = src_node
|
|
1224
|
+
while_index = 0
|
|
1225
|
+
while_node = None
|
|
1226
|
+
|
|
1227
|
+
for k, v in node_list.items():
|
|
1228
|
+
if (
|
|
1229
|
+
k[2] == "parenthesized_expression"
|
|
1230
|
+
and self.get_index(v.parent) == current_index
|
|
1231
|
+
):
|
|
1232
|
+
while_index = self.index[k]
|
|
1233
|
+
while_node = v
|
|
1234
|
+
# break
|
|
1235
|
+
# Find the last statement in the body and an edge from last line to the while node
|
|
1236
|
+
self.add_edge(last_line_index, while_index, "next")
|
|
1237
|
+
|
|
1238
|
+
# Add an edge from the while node to the first line in the block or to the current do node
|
|
1239
|
+
# self.CFG_edge_list.append((while_node, dest_node, 'loop_control')) # First node of block
|
|
1240
|
+
self.add_edge(while_index, current_index, "loop_control") # do node
|
|
1241
|
+
|
|
1242
|
+
# Add an edge from the while node to the next statement after the do_statement
|
|
1243
|
+
self.handle_next(while_node, next_node, "neg_next")
|
|
1244
|
+
|
|
1245
|
+
# ------------------------------------------------------------------------------------------------
|
|
1246
|
+
elif current_node_type == "continue_statement":
|
|
1247
|
+
# Go up the parent chain until we reach a loop_control statement or do statement
|
|
1248
|
+
# add edge from this node to the loop_control statement or do statement
|
|
1249
|
+
|
|
1250
|
+
parent_node = node_value.parent
|
|
1251
|
+
loop_node = None
|
|
1252
|
+
while parent_node is not None:
|
|
1253
|
+
if (
|
|
1254
|
+
parent_node.type
|
|
1255
|
+
in self.statement_types["loop_control_statement"]
|
|
1256
|
+
or parent_node.type == "do_statement"
|
|
1257
|
+
):
|
|
1258
|
+
loop_node = parent_node
|
|
1259
|
+
break
|
|
1260
|
+
parent_node = parent_node.parent
|
|
1261
|
+
|
|
1262
|
+
# add edge from this node to the loop_control statement or do statement
|
|
1263
|
+
|
|
1264
|
+
src_node = self.index[node_key]
|
|
1265
|
+
dest_node = self.get_index(loop_node)
|
|
1266
|
+
label_name = list(
|
|
1267
|
+
filter(
|
|
1268
|
+
lambda child: child.type == "identifier", node_value.children
|
|
1269
|
+
)
|
|
1270
|
+
)
|
|
1271
|
+
if len(label_name) > 0:
|
|
1272
|
+
target_name = label_name[0].text.decode("UTF-8") + ":"
|
|
1273
|
+
dest_node = self.index[
|
|
1274
|
+
self.records["label_statement_map"][target_name]
|
|
1275
|
+
] # Check on this later
|
|
1276
|
+
self.CFG_edge_list.append((src_node, dest_node, "jump_next"))
|
|
1277
|
+
|
|
1278
|
+
# ------------------------------------------------------------------------------------------------
|
|
1279
|
+
elif current_node_type == "break_statement":
|
|
1280
|
+
# break with a label
|
|
1281
|
+
label_name = list(
|
|
1282
|
+
filter(
|
|
1283
|
+
lambda child: child.type == "identifier", node_value.children
|
|
1284
|
+
)
|
|
1285
|
+
)
|
|
1286
|
+
if len(label_name) > 0:
|
|
1287
|
+
target_name = label_name[0].text.decode("UTF-8") + ":"
|
|
1288
|
+
# next_dest_index = self.records['label_statement_map'][target_name]
|
|
1289
|
+
label_key = self.records["label_statement_map"][target_name]
|
|
1290
|
+
label_node = node_list[label_key]
|
|
1291
|
+
next_dest_index, next_node = self.get_next_index(label_node)
|
|
1292
|
+
# need to get the node corresponding to this index, and then get the next node
|
|
1293
|
+
self.handle_next(node_value, next_node, "jump_next")
|
|
1294
|
+
else:
|
|
1295
|
+
# if it is inside a switch, it is handled here and also refer to switch_expression,
|
|
1296
|
+
# if it is inside a loop, handle it here
|
|
1297
|
+
parent_node = node_value.parent
|
|
1298
|
+
loop_node = None
|
|
1299
|
+
while parent_node is not None:
|
|
1300
|
+
if (
|
|
1301
|
+
parent_node.type
|
|
1302
|
+
in self.statement_types["loop_control_statement"]
|
|
1303
|
+
or parent_node.type == "do_statement"
|
|
1304
|
+
or parent_node.type == "switch_expression"
|
|
1305
|
+
):
|
|
1306
|
+
loop_node = parent_node
|
|
1307
|
+
break
|
|
1308
|
+
parent_node = parent_node.parent
|
|
1309
|
+
|
|
1310
|
+
next_dest_index, next_node = self.get_next_index(loop_node)
|
|
1311
|
+
self.handle_next(node_value, next_node, "jump_next")
|
|
1312
|
+
# ------------------------------------------------------------------------------------------------
|
|
1313
|
+
elif current_node_type == "yield_statement":
|
|
1314
|
+
# if it is inside a switch, it is handled here and also refer to switch_expression,
|
|
1315
|
+
parent_node = node_value.parent
|
|
1316
|
+
loop_node = None
|
|
1317
|
+
while parent_node is not None:
|
|
1318
|
+
if (
|
|
1319
|
+
parent_node.type
|
|
1320
|
+
in self.statement_types["loop_control_statement"]
|
|
1321
|
+
or parent_node.type == "do_statement"
|
|
1322
|
+
or parent_node.type == "switch_expression"
|
|
1323
|
+
):
|
|
1324
|
+
loop_node = parent_node
|
|
1325
|
+
break
|
|
1326
|
+
parent_node = parent_node.parent
|
|
1327
|
+
|
|
1328
|
+
try:
|
|
1329
|
+
next_node = loop_node
|
|
1330
|
+
next_dest_index = self.get_index(loop_node)
|
|
1331
|
+
except:
|
|
1332
|
+
# Handle yield when no loop parent or switch parent
|
|
1333
|
+
next_dest_index, next_node = self.get_next_index(node_value)
|
|
1334
|
+
self.handle_next(node_value, next_node, "yield_exit")
|
|
1335
|
+
# ------------------------------------------------------------------------------------------------
|
|
1336
|
+
elif current_node_type == "lambda_expression":
|
|
1337
|
+
self.edge_to_body(node_key, node_value, "body", "lambda_next")
|
|
1338
|
+
|
|
1339
|
+
elif current_node_type == "switch_expression":
|
|
1340
|
+
# First check if the switch expression_statement is part of a non-control statement, and add an edge to the next line
|
|
1341
|
+
switch_parent = java_nodes.return_switch_parent_statement(
|
|
1342
|
+
node_value, self.statement_types["non_control_statement"]
|
|
1343
|
+
)
|
|
1344
|
+
if switch_parent is not None:
|
|
1345
|
+
try:
|
|
1346
|
+
next_node = switch_parent.next_named_sibling
|
|
1347
|
+
src_node = self.index[node_key]
|
|
1348
|
+
dest_node = self.get_index(next_node)
|
|
1349
|
+
if dest_node in self.records["switch_child_map"].keys():
|
|
1350
|
+
dest_node = self.records["switch_child_map"][dest_node]
|
|
1351
|
+
self.add_edge(src_node, dest_node, "next_line 7")
|
|
1352
|
+
|
|
1353
|
+
except Exception as e:
|
|
1354
|
+
pass
|
|
1355
|
+
|
|
1356
|
+
# Find all the case blocks associated with this switch node and add an edge to each of them
|
|
1357
|
+
# Find the last line in each case block and add an edge to the next case block unless it is a break statement
|
|
1358
|
+
# BUt if the block is empty, add an edge to the next case label
|
|
1359
|
+
# in case of a break statement, add an edge to the next statement outside the switch
|
|
1360
|
+
# in case of default, add an edge to the next statement outside the switch
|
|
1361
|
+
# in case of no default, add an edge from last block to the next statement outside the switch
|
|
1362
|
+
case_node_list = {}
|
|
1363
|
+
default_exists = False
|
|
1364
|
+
# default_list = []
|
|
1365
|
+
|
|
1366
|
+
# Find the next statement outside the switch
|
|
1367
|
+
next_dest_index, next_node = self.get_next_index(node_value)
|
|
1368
|
+
|
|
1369
|
+
# For each case label block, find the first statement in the block and add an edge to it
|
|
1370
|
+
for k, v in node_list.items():
|
|
1371
|
+
if (
|
|
1372
|
+
k[2] == "switch_block_statement_group" or k[2] == "switch_rule"
|
|
1373
|
+
) and self.get_index(v.parent.parent) == current_index:
|
|
1374
|
+
case_node_list[k] = v
|
|
1375
|
+
case_node_index = self.index[k]
|
|
1376
|
+
self.add_edge(current_index, case_node_index, "switch_case")
|
|
1377
|
+
case_label = list(
|
|
1378
|
+
filter(
|
|
1379
|
+
lambda child: child.type == "switch_label", v.children
|
|
1380
|
+
)
|
|
1381
|
+
)
|
|
1382
|
+
for case in case_label:
|
|
1383
|
+
default_case = list(
|
|
1384
|
+
filter(
|
|
1385
|
+
lambda child: child.type == "default", case.children
|
|
1386
|
+
)
|
|
1387
|
+
)
|
|
1388
|
+
if len(default_case) > 0:
|
|
1389
|
+
default_exists = True
|
|
1390
|
+
# If no default exists, add an edge from switch node to the next line after it
|
|
1391
|
+
if not default_exists:
|
|
1392
|
+
self.handle_next(node_value, next_node, "switch_exit")
|
|
1393
|
+
|
|
1394
|
+
for k, v in case_node_list.items():
|
|
1395
|
+
current_case_index = self.index[k]
|
|
1396
|
+
current_case = v
|
|
1397
|
+
case_statements = list(
|
|
1398
|
+
filter(
|
|
1399
|
+
lambda child: (
|
|
1400
|
+
child.is_named and child.type != "switch_label"
|
|
1401
|
+
),
|
|
1402
|
+
v.children,
|
|
1403
|
+
)
|
|
1404
|
+
)
|
|
1405
|
+
case_labels = list(
|
|
1406
|
+
filter(
|
|
1407
|
+
lambda child: (
|
|
1408
|
+
child.is_named and child.type == "switch_label"
|
|
1409
|
+
),
|
|
1410
|
+
v.children,
|
|
1411
|
+
)
|
|
1412
|
+
)
|
|
1413
|
+
next_case_node = v.next_named_sibling
|
|
1414
|
+
try:
|
|
1415
|
+
next_case_node_index = self.get_index(next_case_node)
|
|
1416
|
+
except:
|
|
1417
|
+
next_case_node_index = None
|
|
1418
|
+
|
|
1419
|
+
block_node = None
|
|
1420
|
+
if len(case_statements) == 0:
|
|
1421
|
+
default_flag = False
|
|
1422
|
+
# Check if its a default statement, then add an edge to the next statement outside the switch
|
|
1423
|
+
for case_label in case_labels:
|
|
1424
|
+
default_case = list(
|
|
1425
|
+
filter(
|
|
1426
|
+
lambda child: child.type == "default",
|
|
1427
|
+
case_label.children,
|
|
1428
|
+
)
|
|
1429
|
+
)
|
|
1430
|
+
if len(default_case) > 0:
|
|
1431
|
+
self.handle_next(
|
|
1432
|
+
current_case, next_node, "default_exit"
|
|
1433
|
+
)
|
|
1434
|
+
default_flag = True
|
|
1435
|
+
break
|
|
1436
|
+
# There is no case body, so add an edge from this case to the next case label, if exists
|
|
1437
|
+
if not default_flag and next_case_node_index is not None:
|
|
1438
|
+
self.add_edge(
|
|
1439
|
+
current_case_index, next_case_node_index, "fall through"
|
|
1440
|
+
)
|
|
1441
|
+
|
|
1442
|
+
else:
|
|
1443
|
+
# The case body exists
|
|
1444
|
+
empty_block = False
|
|
1445
|
+
if (
|
|
1446
|
+
len(case_statements) > 1
|
|
1447
|
+
and case_statements[0].type == "block"
|
|
1448
|
+
and len(
|
|
1449
|
+
list(
|
|
1450
|
+
filter(
|
|
1451
|
+
lambda child: child.is_named,
|
|
1452
|
+
case_statements[0].children,
|
|
1453
|
+
)
|
|
1454
|
+
)
|
|
1455
|
+
)
|
|
1456
|
+
== 0
|
|
1457
|
+
):
|
|
1458
|
+
empty_block = True
|
|
1459
|
+
# Empty block followed by other statement in the same case
|
|
1460
|
+
next_line = case_statements[1]
|
|
1461
|
+
next_line_index = self.get_index(next_line)
|
|
1462
|
+
self.add_edge(
|
|
1463
|
+
current_case_index, next_line_index, "next_line 8"
|
|
1464
|
+
)
|
|
1465
|
+
|
|
1466
|
+
# Find the first line in each case block and add an edge from case label to itandl empty case block
|
|
1467
|
+
|
|
1468
|
+
explicit_block = None
|
|
1469
|
+
for child in v.children:
|
|
1470
|
+
# Need to write a loop for unlimited layers of nesting
|
|
1471
|
+
if child.is_named and child.type != "switch_label":
|
|
1472
|
+
if child.type == "block":
|
|
1473
|
+
explicit_block = child
|
|
1474
|
+
for child in child.children:
|
|
1475
|
+
if (
|
|
1476
|
+
child.is_named
|
|
1477
|
+
and child.type != "switch_label"
|
|
1478
|
+
):
|
|
1479
|
+
block_node = child
|
|
1480
|
+
break
|
|
1481
|
+
else:
|
|
1482
|
+
block_node = child
|
|
1483
|
+
break
|
|
1484
|
+
if block_node is None:
|
|
1485
|
+
# Add fall through edge to next case label
|
|
1486
|
+
if (
|
|
1487
|
+
empty_block == False
|
|
1488
|
+
and next_case_node_index is not None
|
|
1489
|
+
):
|
|
1490
|
+
self.add_edge(
|
|
1491
|
+
current_case_index,
|
|
1492
|
+
next_case_node_index,
|
|
1493
|
+
"fall through",
|
|
1494
|
+
)
|
|
1495
|
+
else:
|
|
1496
|
+
first_line_index = self.get_index(block_node)
|
|
1497
|
+
self.add_edge(
|
|
1498
|
+
current_case_index, first_line_index, "case_next"
|
|
1499
|
+
)
|
|
1500
|
+
|
|
1501
|
+
# if the case contains a set of braces and the break outside it,
|
|
1502
|
+
# then add an edge from the last line inside the block to the first line outside it
|
|
1503
|
+
if explicit_block is not None:
|
|
1504
|
+
next_line = explicit_block.next_named_sibling
|
|
1505
|
+
if next_line is not None:
|
|
1506
|
+
next_line_index = self.get_index(next_line)
|
|
1507
|
+
block_node = None
|
|
1508
|
+
for child in reversed(explicit_block.children):
|
|
1509
|
+
# Need to write a loop for unlimited layers of nesting
|
|
1510
|
+
if child.is_named:
|
|
1511
|
+
if child.type == "block":
|
|
1512
|
+
for child in reversed(child.children):
|
|
1513
|
+
if child.is_named:
|
|
1514
|
+
block_node = child
|
|
1515
|
+
break
|
|
1516
|
+
else:
|
|
1517
|
+
block_node = child
|
|
1518
|
+
break
|
|
1519
|
+
if block_node is not None:
|
|
1520
|
+
last_line_index = self.get_index(block_node)
|
|
1521
|
+
if (
|
|
1522
|
+
block_node.type
|
|
1523
|
+
in self.statement_types["non_control_statement"]
|
|
1524
|
+
):
|
|
1525
|
+
self.add_edge(
|
|
1526
|
+
last_line_index,
|
|
1527
|
+
next_line_index,
|
|
1528
|
+
"fall through",
|
|
1529
|
+
)
|
|
1530
|
+
|
|
1531
|
+
# Find the last line in each case block and add an edge to the next case block unless it is a break statement
|
|
1532
|
+
block_node = None
|
|
1533
|
+
for child in reversed(v.children):
|
|
1534
|
+
# Need to write a loop for unlimited layers of nesting
|
|
1535
|
+
if child.is_named:
|
|
1536
|
+
if child.type == "block":
|
|
1537
|
+
for child in reversed(child.children):
|
|
1538
|
+
if child.is_named:
|
|
1539
|
+
block_node = child
|
|
1540
|
+
break
|
|
1541
|
+
else:
|
|
1542
|
+
block_node = child
|
|
1543
|
+
break
|
|
1544
|
+
if block_node is not None:
|
|
1545
|
+
last_line_index = self.get_index(block_node)
|
|
1546
|
+
if (
|
|
1547
|
+
block_node.type
|
|
1548
|
+
in self.statement_types["non_control_statement"]
|
|
1549
|
+
and next_case_node_index is not None
|
|
1550
|
+
):
|
|
1551
|
+
self.add_edge(
|
|
1552
|
+
last_line_index,
|
|
1553
|
+
next_case_node_index,
|
|
1554
|
+
"fall through",
|
|
1555
|
+
)
|
|
1556
|
+
|
|
1557
|
+
# in case of default, add an edge to the next statement outside the switch
|
|
1558
|
+
# in case of no default, add an edge from last block to the next statement outside the switch
|
|
1559
|
+
if next_case_node_index is None:
|
|
1560
|
+
# This is the last block
|
|
1561
|
+
if block_node is not None:
|
|
1562
|
+
if (
|
|
1563
|
+
block_node.type
|
|
1564
|
+
in self.statement_types["non_control_statement"]
|
|
1565
|
+
):
|
|
1566
|
+
self.handle_next(block_node, next_node, "switch_out")
|
|
1567
|
+
else:
|
|
1568
|
+
self.handle_next(v, next_node, "switch_out")
|
|
1569
|
+
|
|
1570
|
+
# in case of a break statement, add an edge to the next statement outside the switch
|
|
1571
|
+
# -> Handled in break statement
|
|
1572
|
+
# ------------------------------------------------------------------------------------------------
|
|
1573
|
+
elif current_node_type == "return_statement":
|
|
1574
|
+
# Check if it returns a function with an inner definition
|
|
1575
|
+
# method_list will give us the index of the inner function
|
|
1576
|
+
inner_definition = []
|
|
1577
|
+
self.returns_inner_definition(node_value, inner_definition)
|
|
1578
|
+
|
|
1579
|
+
if len(inner_definition) > 0:
|
|
1580
|
+
inner_definition = inner_definition[0]
|
|
1581
|
+
# inner_index = self.index[(inner_definition.start_point, inner_definition.end_point, inner_definition.type)]
|
|
1582
|
+
# TODOD: Perhaps remove this because inconsistent wih newly decided logic
|
|
1583
|
+
self.add_edge(current_index, inner_definition, "return_next")
|
|
1584
|
+
|
|
1585
|
+
if not self.inner_function(node_value):
|
|
1586
|
+
# pass
|
|
1587
|
+
# Instead of an edge to exit node, we update the return map
|
|
1588
|
+
# self.add_edge(current_index, 2, "return_exit")
|
|
1589
|
+
containing_method = self.get_index(
|
|
1590
|
+
self.get_containing_method(node_value)
|
|
1591
|
+
)
|
|
1592
|
+
|
|
1593
|
+
try:
|
|
1594
|
+
self.records["return_statement_map"][containing_method].append(
|
|
1595
|
+
current_index
|
|
1596
|
+
)
|
|
1597
|
+
except:
|
|
1598
|
+
self.records["return_statement_map"][containing_method] = [
|
|
1599
|
+
current_index
|
|
1600
|
+
]
|
|
1601
|
+
# else: TODO: When inner functions are properly implemented
|
|
1602
|
+
# save it in the return map?
|
|
1603
|
+
# next_dest_index, next_node = self.get_next_index(node_value)
|
|
1604
|
+
# self.add_edge(current_index, next_dest_index, "return_exit")
|
|
1605
|
+
# ------------------------------------------------------------------------------------------------
|
|
1606
|
+
# elif current_node_type == "assert_statement":
|
|
1607
|
+
# self.add_edge(current_index, 2, "assert_false")
|
|
1608
|
+
# ------------------------------------------------------------------------------------------------
|
|
1609
|
+
elif (
|
|
1610
|
+
current_node_type == "try_statement"
|
|
1611
|
+
or current_node_type == "try_with_resources_statement"
|
|
1612
|
+
):
|
|
1613
|
+
# Add edge from try block to first statement inside the block
|
|
1614
|
+
self.edge_to_body(node_key, node_value, "body", "next")
|
|
1615
|
+
catch_node_list = {}
|
|
1616
|
+
finally_node = None
|
|
1617
|
+
# Identify all catch blocks - add an edge from catch node to first line inside block
|
|
1618
|
+
for k, v in node_list.items():
|
|
1619
|
+
|
|
1620
|
+
if (
|
|
1621
|
+
k[2] == "catch_clause"
|
|
1622
|
+
and self.get_index(v.parent) == current_index
|
|
1623
|
+
):
|
|
1624
|
+
catch_node_list[k] = v
|
|
1625
|
+
self.edge_to_body(k, v, "body", "next")
|
|
1626
|
+
elif (
|
|
1627
|
+
k[2] == "finally_clause"
|
|
1628
|
+
and self.get_index(v.parent) == current_index
|
|
1629
|
+
):
|
|
1630
|
+
finally_node = v
|
|
1631
|
+
self.edge_first_line(k, v) # Not sure if this works
|
|
1632
|
+
|
|
1633
|
+
# From each line inside the try block, an edge going to each catch block
|
|
1634
|
+
try_body = node_value.child_by_field_name("body")
|
|
1635
|
+
statements = []
|
|
1636
|
+
statements = self.get_all_statements(try_body, node_list, statements)
|
|
1637
|
+
if len(statements) > 0:
|
|
1638
|
+
for statement in statements:
|
|
1639
|
+
statement_index = self.get_index(statement)
|
|
1640
|
+
for catch_node in catch_node_list.keys():
|
|
1641
|
+
catch_index = self.index[catch_node]
|
|
1642
|
+
# if statement.type != 'return_statement': The Exception can occur on the RHS so the catch_exception edge should be there
|
|
1643
|
+
self.add_edge(
|
|
1644
|
+
statement_index, catch_index, "catch_exception"
|
|
1645
|
+
)
|
|
1646
|
+
|
|
1647
|
+
# Find the next statement outside the try block
|
|
1648
|
+
next_dest_index, next_node = self.get_next_index(node_value)
|
|
1649
|
+
exit_next = None
|
|
1650
|
+
|
|
1651
|
+
# finally block is optional
|
|
1652
|
+
if finally_node is not None:
|
|
1653
|
+
# From last line of finally to next statement outside the try block
|
|
1654
|
+
last_line, line_type = self.get_block_last_line(
|
|
1655
|
+
finally_node, "body"
|
|
1656
|
+
)
|
|
1657
|
+
if line_type in self.statement_types["non_control_statement"]:
|
|
1658
|
+
self.handle_next(last_line, next_node, "finally_exit")
|
|
1659
|
+
# For the remaining portion, set finally block as next node if exists
|
|
1660
|
+
exit_next = finally_node
|
|
1661
|
+
else:
|
|
1662
|
+
exit_next = next_node
|
|
1663
|
+
|
|
1664
|
+
# From last line of try block to finally or to next statement outside the try block
|
|
1665
|
+
last_line, line_type = self.get_block_last_line(node_value, "body")
|
|
1666
|
+
# if line_type in self.statement_types['non_control_statement']: # Inside try, edges to finally should be irrespective of control or non control statements
|
|
1667
|
+
if line_type in self.statement_types["node_list_type"]:
|
|
1668
|
+
self.handle_next(last_line, exit_next, "try_exit")
|
|
1669
|
+
# From last line of every catch block to finally or to next statement outside the try block
|
|
1670
|
+
for catch_node, catch_value in catch_node_list.items():
|
|
1671
|
+
last_line, line_type = self.get_block_last_line(catch_value, "body")
|
|
1672
|
+
last_line_index = self.get_index(last_line)
|
|
1673
|
+
if line_type in self.statement_types["non_control_statement"]:
|
|
1674
|
+
self.handle_next(last_line, exit_next, "catch_exit")
|
|
1675
|
+
# Case of empty catch block
|
|
1676
|
+
elif last_line_index == self.index[catch_node]:
|
|
1677
|
+
self.handle_next(last_line, exit_next, "catch_exit")
|
|
1678
|
+
# ------------------------------------------------------------------------------------------------
|
|
1679
|
+
elif current_node_type == "throw_statement":
|
|
1680
|
+
# Control goes to the first catch in the call stack (dynamic call stack) Nothing to do statically
|
|
1681
|
+
# Essentially exits the function
|
|
1682
|
+
parent = node_value.parent
|
|
1683
|
+
# try_flag = False
|
|
1684
|
+
try_node = None
|
|
1685
|
+
while parent is not None:
|
|
1686
|
+
if parent.type == "catch_clause" or parent.type == "finally_clause":
|
|
1687
|
+
break
|
|
1688
|
+
if (
|
|
1689
|
+
parent.type == "try_statement"
|
|
1690
|
+
or parent.type == "try_with_resources_statement"
|
|
1691
|
+
):
|
|
1692
|
+
try_flag = True
|
|
1693
|
+
try_node = parent
|
|
1694
|
+
break
|
|
1695
|
+
parent = parent.parent
|
|
1696
|
+
if try_node is None:
|
|
1697
|
+
# self.add_edge(current_index, 2, "throw_exit")
|
|
1698
|
+
pass
|
|
1699
|
+
else:
|
|
1700
|
+
# try_node is like the current_node, helps us find the corresponding catch clauses
|
|
1701
|
+
|
|
1702
|
+
# Add edge from try block to first statement inside the block
|
|
1703
|
+
# self.edge_to_body(node_key, node_value, 'body', 'next')
|
|
1704
|
+
catch_node_list = {}
|
|
1705
|
+
# finally_node = None
|
|
1706
|
+
# Identify all catch blocks
|
|
1707
|
+
for k, v in node_list.items():
|
|
1708
|
+
if (
|
|
1709
|
+
k[2] == "catch_clause"
|
|
1710
|
+
and self.get_index(v.parent) == current_index
|
|
1711
|
+
):
|
|
1712
|
+
catch_node_list[k] = v
|
|
1713
|
+
# if len(catch_node_list) == 0:
|
|
1714
|
+
# self.add_edge(current_index, 2, "throw_exit")
|
|
1715
|
+
|
|
1716
|
+
self.add_method_call_edges()
|
|
1717
|
+
if warning_counter > 0:
|
|
1718
|
+
logger.warning(
|
|
1719
|
+
"Total number of warnings from unimplemented statement types: ",
|
|
1720
|
+
warning_counter,
|
|
1721
|
+
)
|
|
1722
|
+
return self.CFG_node_list, self.CFG_edge_list
|