Skip to content

Commit 6300e0a

Browse files
committed
Support new parser and listener
Merge branch 'java_listerner_2' into 'master' See merge request persper/code-analytics!55
2 parents 2ed97da + ed2b460 commit 6300e0a

File tree

2 files changed

+146
-5
lines changed

2 files changed

+146
-5
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import os
2+
import ast
3+
from collections import defaultdict
4+
from antlr4 import *
5+
from persper.parser.java.JavaParserListener import JavaParserListener
6+
from persper.parser.java.JavaParser import JavaParser
7+
import logging
8+
9+
DEBUG = ast.literal_eval(os.environ.get('DEBUG_JAVA', "True"))
10+
logging.basicConfig(filename='java.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')
11+
12+
13+
class FunctionStatsListener(JavaParserListener):
14+
"""
15+
This class is responsible to get name of the function and
16+
range of the function which is the start and end line of the function.
17+
"""
18+
19+
def __init__(self):
20+
self.function_names = list()
21+
self.function_ranges = list()
22+
self.current_function_name = None
23+
24+
def enterMethodDeclaration(self, ctx=JavaParser.MethodDeclarationContext):
25+
try:
26+
name = ctx.IDENTIFIER().getText()
27+
self.current_function_name = name
28+
self.function_names.append(name)
29+
self.function_ranges.append([ctx.start.line, ctx.stop.line])
30+
except Exception as e:
31+
if DEBUG:
32+
raise
33+
else:
34+
logging.exception("[FunctionStatsListener][METHOD DECLARATION]")
35+
self.function_names = []
36+
self.function_ranges = []
37+
38+
39+
class FunctionCallerListener(JavaParserListener):
40+
def __init__(self):
41+
self.function_names = list()
42+
43+
def enterMethodDeclaration(self, ctx=JavaParser.MethodDeclarationContext):
44+
try:
45+
name = ctx.IDENTIFIER().getText()
46+
self.function_names.append(name)
47+
except Exception as e:
48+
if DEBUG:
49+
raise
50+
else:
51+
logging.exception("[FunctionCallerListener][METHOD DECLARATION]")
52+
self.function_names = []
53+
54+
55+
class FunctionCalleeListener(JavaParserListener):
56+
def __init__(self):
57+
self.function_caller_callee_map = defaultdict(list)
58+
self.current_function_name = None
59+
60+
def enterMethodDeclaration(self, ctx=JavaParser.MethodDeclarationContext):
61+
try:
62+
name = ctx.IDENTIFIER().getText()
63+
self.current_function_name = name
64+
except Exception as e:
65+
if DEBUG:
66+
raise
67+
else:
68+
logging.exception("[FunctionCalleeListener][METHOD DECLARATION]")
69+
self.current_function_name = None
70+
71+
def enterMethodCall(self, ctx: JavaParser.MethodCallContext):
72+
try:
73+
# The condition is there to avoid functions like `this()` or `super()`
74+
if ctx.IDENTIFIER():
75+
name = ctx.IDENTIFIER().getText()
76+
if self.current_function_name:
77+
self.function_caller_callee_map[self.current_function_name].append(
78+
name)
79+
except Exception as e:
80+
if DEBUG:
81+
raise
82+
else:
83+
logging.exception("[FunctionCalleeListener][METHOD CALL]")
84+
85+
86+
def get_function_range_java(tree):
87+
walker = ParseTreeWalker()
88+
collector = FunctionStatsListener()
89+
walker.walk(collector, tree)
90+
return collector.function_names, collector.function_ranges
91+
92+
93+
def get_all_function_caller(tree):
94+
walker = ParseTreeWalker()
95+
collector = FunctionCallerListener()
96+
walker.walk(collector, tree)
97+
return collector.function_names
98+
99+
100+
def get_caller_callee_map(tree):
101+
walker = ParseTreeWalker()
102+
collector = FunctionCalleeListener()
103+
walker.walk(collector, tree)
104+
return collector.function_caller_callee_map
105+
106+
107+
def update_graph(ccgraph, ast_list, change_stats, new_fname_to_old_fname):
108+
for ast in ast_list:
109+
filename = ast.filename
110+
tree = ast.tree
111+
for function in get_all_function_caller(tree):
112+
113+
if function not in ccgraph:
114+
ccgraph.add_node(function, [filename])
115+
else:
116+
files = ccgraph.files(function)
117+
old_filename = new_fname_to_old_fname.get(filename, None)
118+
# Case: Rename
119+
if old_filename:
120+
files.add(filename)
121+
if old_filename in files:
122+
files.remove(old_filename)
123+
ccgraph.update_node_files(function, files)
124+
# Case: New
125+
elif filename not in files:
126+
files.add(filename)
127+
ccgraph.update_node_files(function, files)
128+
129+
for call, callee in get_caller_callee_map(tree).items():
130+
for callee_name in callee:
131+
if callee_name not in ccgraph:
132+
# Pass [] to files argument since we don't know
133+
# which file this node belongs to
134+
ccgraph.add_node(callee_name, [])
135+
ccgraph.add_edge(call, callee_name)
136+
137+
for func, fstat in change_stats.items():
138+
if func not in ccgraph:
139+
print("%s in change_stats but not in ccgraph" % func)
140+
continue
141+
ccgraph.update_node_history(func, fstat['adds'], fstat['dels'])

persper/analytics/java.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from persper.analytics.patch_parser import PatchParser
55
from persper.analytics.c import function_change_stats
66
from persper.analytics.call_graph.java.ast_creater import ASTCreater
7-
from persper.analytics.call_graph.java.Java8Lexer import Java8Lexer
8-
from persper.analytics.call_graph.java.Java8Parser import Java8Parser
9-
from persper.analytics.call_graph.java.java import get_function_range_java, update_graph
7+
from persper.parser.java.JavaLexer import JavaLexer
8+
from persper.parser.java.JavaParser import JavaParser
9+
from persper.analytics.call_graph.java.java2 import get_function_range_java, update_graph
1010

1111

1212
class JavaGraphServer(GraphServer):
@@ -39,15 +39,15 @@ def update_graph(self, old_filename, old_src, new_filename, new_src, patch):
3939

4040
# Parse source codes into ASTs
4141
if old_src:
42-
ast_obj = ASTCreater(Java8Parser, Java8Lexer,
42+
ast_obj = ASTCreater(JavaParser, JavaLexer,
4343
old_filename, old_src)
4444
ast_obj()
4545
old_ast = ast_obj.tree
4646
if old_ast is None:
4747
return -1
4848

4949
if new_src:
50-
ast_obj = ASTCreater(Java8Parser, Java8Lexer,
50+
ast_obj = ASTCreater(JavaParser, JavaLexer,
5151
new_filename, new_src)
5252
ast_obj()
5353
new_ast = ast_obj.tree

0 commit comments

Comments
 (0)