lambda-graphs 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. lambda_graphs/__init__.py +18 -0
  2. lambda_graphs/__main__.py +7 -0
  3. lambda_graphs/cli.py +183 -0
  4. lambda_graphs/codeviews/AST/AST.py +120 -0
  5. lambda_graphs/codeviews/AST/AST_driver.py +37 -0
  6. lambda_graphs/codeviews/AST/__init__.py +0 -0
  7. lambda_graphs/codeviews/CFG/CFG.py +42 -0
  8. lambda_graphs/codeviews/CFG/CFG_c.py +1182 -0
  9. lambda_graphs/codeviews/CFG/CFG_cpp.py +5604 -0
  10. lambda_graphs/codeviews/CFG/CFG_driver.py +45 -0
  11. lambda_graphs/codeviews/CFG/CFG_java.py +1722 -0
  12. lambda_graphs/codeviews/CFG/__init__.py +0 -0
  13. lambda_graphs/codeviews/CST/CST_driver.py +70 -0
  14. lambda_graphs/codeviews/CST/__init__.py +0 -0
  15. lambda_graphs/codeviews/DFG/DFG_driver.py +42 -0
  16. lambda_graphs/codeviews/DFG/__init__.py +0 -0
  17. lambda_graphs/codeviews/SDFG/SDFG.py +135 -0
  18. lambda_graphs/codeviews/SDFG/SDFG_c.py +2041 -0
  19. lambda_graphs/codeviews/SDFG/SDFG_cpp.py +4030 -0
  20. lambda_graphs/codeviews/SDFG/SDFG_java.py +1618 -0
  21. lambda_graphs/codeviews/SDFG/__init__.py +0 -0
  22. lambda_graphs/codeviews/__init__.py +0 -0
  23. lambda_graphs/codeviews/combined_graph/__init__.py +0 -0
  24. lambda_graphs/codeviews/combined_graph/combined_driver.py +163 -0
  25. lambda_graphs/tree_parser/__init__.py +0 -0
  26. lambda_graphs/tree_parser/c_parser.py +565 -0
  27. lambda_graphs/tree_parser/cpp_parser.py +424 -0
  28. lambda_graphs/tree_parser/custom_parser.py +66 -0
  29. lambda_graphs/tree_parser/java_parser.py +254 -0
  30. lambda_graphs/tree_parser/parser_driver.py +54 -0
  31. lambda_graphs/utils/DFG_utils.py +44 -0
  32. lambda_graphs/utils/__init__.py +0 -0
  33. lambda_graphs/utils/c_nodes.py +431 -0
  34. lambda_graphs/utils/cpp_nodes.py +1331 -0
  35. lambda_graphs/utils/java_nodes.py +756 -0
  36. lambda_graphs/utils/multi_file_merger.py +444 -0
  37. lambda_graphs/utils/postprocessor.py +83 -0
  38. lambda_graphs/utils/preprocessor.py +68 -0
  39. lambda_graphs/utils/src_parser.py +23 -0
  40. lambda_graphs-0.1.4.dist-info/METADATA +380 -0
  41. lambda_graphs-0.1.4.dist-info/RECORD +45 -0
  42. lambda_graphs-0.1.4.dist-info/WHEEL +5 -0
  43. lambda_graphs-0.1.4.dist-info/entry_points.txt +2 -0
  44. lambda_graphs-0.1.4.dist-info/licenses/LICENSE +21 -0
  45. lambda_graphs-0.1.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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