@@ -84,6 +84,7 @@ def __init__(
84
84
analysis_level : str ,
85
85
use_graalvm_binary : bool ,
86
86
eager_analysis : bool ,
87
+ target_files : List [str ] | None
87
88
) -> None :
88
89
self .project_dir = project_dir
89
90
self .source_code = source_code
@@ -92,6 +93,7 @@ def __init__(
92
93
self .use_graalvm_binary = use_graalvm_binary
93
94
self .eager_analysis = eager_analysis
94
95
self .analysis_level = analysis_level
96
+ self .target_files = target_files
95
97
self .application = self ._init_codeanalyzer (
96
98
analysis_level = 1 if analysis_level == AnalysisLevel .symbol_table else 2 )
97
99
# Attributes related the Java code analysis...
@@ -183,15 +185,11 @@ def _get_codeanalyzer_exec(self) -> List[str]:
183
185
resources .files ("cldk.analysis.java.codeanalyzer.bin" ) / "codeanalyzer" ) as codeanalyzer_bin_path :
184
186
codeanalyzer_exec = shlex .split (codeanalyzer_bin_path .__str__ ())
185
187
else :
186
- print (f'analysis path: { self .analysis_json_path } ' )
187
- analysis_json_path_file = Path (self .analysis_json_path ).joinpath ("analysis.json" )
188
+
188
189
if self .analysis_backend_path :
189
190
analysis_backend_path = Path (self .analysis_backend_path )
190
191
logger .info (f"Using codeanalyzer.jar from { analysis_backend_path } " )
191
192
codeanalyzer_exec = shlex .split (f"java -jar { analysis_backend_path / 'codeanalyzer.jar' } " )
192
- elif analysis_json_path_file .exists ():
193
- logger .info (f"Using existing analysis from { self .analysis_json_path } " )
194
- codeanalyzer_exec = shlex .split (f"java -jar codeanalyzer.jar" )
195
193
else :
196
194
# Since the path to codeanalyzer.jar was not provided, we'll download the latest version from GitHub.
197
195
with resources .as_file (resources .files ("cldk.analysis.java.codeanalyzer.jar" )) as codeanalyzer_jar_path :
@@ -200,7 +198,16 @@ def _get_codeanalyzer_exec(self) -> List[str]:
200
198
codeanalyzer_jar_file = self ._download_or_update_code_analyzer (codeanalyzer_jar_path )
201
199
codeanalyzer_exec = shlex .split (f"java -jar { codeanalyzer_jar_file } " )
202
200
return codeanalyzer_exec
203
-
201
+
202
+ def init_japplication (self , data : str ) -> JApplication :
203
+ """Return JApplication giving the stringified JSON as input.
204
+ Returns
205
+ -------
206
+ JApplication
207
+ The application view of the Java code with the analysis results.
208
+ """
209
+ return JApplication (** json .loads (data ))
210
+
204
211
def _init_codeanalyzer (self , analysis_level = 1 ) -> JApplication :
205
212
""" Initializes the Codeanalyzer.
206
213
@@ -214,11 +221,19 @@ def _init_codeanalyzer(self, analysis_level=1) -> JApplication:
214
221
CodeanalyzerExecutionException: If there is an error running Codeanalyzer.
215
222
"""
216
223
codeanalyzer_exec = self ._get_codeanalyzer_exec ()
217
-
224
+ codeanalyzer_args = ''
218
225
if self .analysis_json_path is None :
219
226
logger .info ("Reading analysis from the pipe." )
220
- codeanalyzer_args = codeanalyzer_exec + shlex .split (
221
- f"-i { Path (self .project_dir )} --analysis-level={ analysis_level } " )
227
+ # If target file is provided, the input is merged into a single string and passed to codeanalyzer
228
+ if self .target_files :
229
+ target_file_options = ' -t ' .join ([s .strip () for s in self .target_files ])
230
+ codeanalyzer_args = codeanalyzer_exec + shlex .split (
231
+ f"-i { Path (self .project_dir )} --analysis-level={ analysis_level } -t { target_file_options } "
232
+ )
233
+ else :
234
+ codeanalyzer_args = codeanalyzer_exec + shlex .split (
235
+ f"-i { Path (self .project_dir )} --analysis-level={ analysis_level } "
236
+ )
222
237
try :
223
238
logger .info (f"Running codeanalyzer: { ' ' .join (codeanalyzer_args )} " )
224
239
console_out : CompletedProcess [str ] = subprocess .run (
@@ -232,15 +247,29 @@ def _init_codeanalyzer(self, analysis_level=1) -> JApplication:
232
247
raise CodeanalyzerExecutionException (str (e )) from e
233
248
234
249
else :
250
+ # Check if the code analyzer needs to be run
251
+ is_run_code_analyzer = False
235
252
analysis_json_path_file = Path (self .analysis_json_path ).joinpath ("analysis.json" )
236
- if not analysis_json_path_file .exists () or self .eager_analysis :
237
- # If the analysis file does not exist, we'll run the analysis. Alternately, if the eager_analysis
238
- # flag is set, we'll run the analysis every time the object is created. This will happen regradless
239
- # of the existence of the analysis file.
240
- # Create the executable command for codeanalyzer.
253
+ # If target file is provided, the input is merged into a single string and passed to codeanalyzer
254
+ if self .target_files :
255
+ target_file_options = ' -t ' .join ([s .strip () for s in self .target_files ])
241
256
codeanalyzer_args = codeanalyzer_exec + shlex .split (
242
- f"-i { Path (self .project_dir )} --analysis-level={ analysis_level } -o { self .analysis_json_path } " )
257
+ f"-i { Path (self .project_dir )} --analysis-level={ analysis_level } "
258
+ f" -o { self .analysis_json_path } -t { target_file_options } "
259
+ )
260
+ is_run_code_analyzer = True
261
+ else :
262
+ if not analysis_json_path_file .exists () or self .eager_analysis :
263
+ # If the analysis file does not exist, we'll run the analysis. Alternately, if the eager_analysis
264
+ # flag is set, we'll run the analysis every time the object is created. This will happen regradless
265
+ # of the existence of the analysis file.
266
+ # Create the executable command for codeanalyzer.
267
+ codeanalyzer_args = codeanalyzer_exec + shlex .split (
268
+ f"-i { Path (self .project_dir )} --analysis-level={ analysis_level } -o { self .analysis_json_path } "
269
+ )
270
+ is_run_code_analyzer = True
243
271
272
+ if is_run_code_analyzer :
244
273
try :
245
274
logger .info (f"Running codeanalyzer subprocess with args { codeanalyzer_args } " )
246
275
subprocess .run (
@@ -254,7 +283,6 @@ def _init_codeanalyzer(self, analysis_level=1) -> JApplication:
254
283
255
284
except Exception as e :
256
285
raise CodeanalyzerExecutionException (str (e )) from e
257
-
258
286
with open (analysis_json_path_file ) as f :
259
287
data = json .load (f )
260
288
return JApplication (** data )
@@ -265,7 +293,6 @@ def _codeanalyzer_single_file(self):
265
293
Returns:
266
294
JApplication: The application view of the Java code with the analysis results.
267
295
"""
268
- # self.source_code: str = re.sub(r"[\r\n\t\f\v]+", lambda x: " " if x.group() in "\t\f\v" else " ", self.source_code)
269
296
codeanalyzer_exec = self ._get_codeanalyzer_exec ()
270
297
codeanalyzer_args = ["--source-analysis" , self .source_code ]
271
298
codeanalyzer_cmd = codeanalyzer_exec + codeanalyzer_args
@@ -410,8 +437,9 @@ def get_all_callers(self, target_class_name: str, target_method_signature: str,
410
437
caller_detail_dict = {}
411
438
call_graph = None
412
439
if using_symbol_table :
413
- call_graph = self .__raw_call_graph_using_symbol_table_target_method (target_class_name = target_class_name ,
414
- target_method_signature = target_method_signature )
440
+ call_graph = self .__call_graph_using_symbol_table (qualified_class_name = target_class_name ,
441
+ method_signature = target_method_signature ,
442
+ is_target_method = True )
415
443
else :
416
444
call_graph = self .call_graph
417
445
if (target_method_signature , target_class_name ) not in call_graph .nodes ():
@@ -703,10 +731,11 @@ def __call_graph_using_symbol_table(self,
703
731
cg = nx .DiGraph ()
704
732
sdg = None
705
733
if is_target_method :
706
- sdg = None
734
+ sdg = self .__raw_call_graph_using_symbol_table_target_method (target_class_name = qualified_class_name ,
735
+ target_method_signature = method_signature )
707
736
else :
708
737
sdg = self .__raw_call_graph_using_symbol_table (qualified_class_name = qualified_class_name ,
709
- method_signature = method_signature )
738
+ method_signature = method_signature )
710
739
tsu = JavaSitter ()
711
740
edge_list = [
712
741
(
@@ -733,8 +762,8 @@ def __call_graph_using_symbol_table(self,
733
762
return cg
734
763
735
764
def __raw_call_graph_using_symbol_table_target_method (self ,
736
- target_class_name : str ,
737
- target_method_signature : str ,
765
+ target_class_name : str ,
766
+ target_method_signature : str ,
738
767
cg = None ) -> list [JGraphEdgesST ]:
739
768
""" Generates call graph using symbol table information given the target method and target class
740
769
Args:
@@ -752,7 +781,7 @@ def __raw_call_graph_using_symbol_table_target_method(self,
752
781
for class_name in self .get_all_classes ():
753
782
for method in self .get_all_methods_in_class (qualified_class_name = class_name ):
754
783
method_details = self .get_method (qualified_class_name = class_name ,
755
- method_signature = method )
784
+ method_signature = method )
756
785
for call_site in method_details .call_sites :
757
786
source_method_details = None
758
787
source_class = ''
@@ -776,9 +805,9 @@ def __raw_call_graph_using_symbol_table_target_method(self,
776
805
if call_site .receiver_type != "" :
777
806
# call to any class
778
807
if self .get_class (qualified_class_name = call_site .receiver_type ):
779
- if callee_signature == target_method_signature and call_site .receiver_type == target_class_name :
808
+ if callee_signature == target_method_signature and call_site .receiver_type == target_class_name :
780
809
source_method_details = self .get_method (method_signature = method ,
781
- qualified_class_name = class_name )
810
+ qualified_class_name = class_name )
782
811
source_class = class_name
783
812
else :
784
813
# check if any method exists with the signature in the class even if the receiver type is blank
0 commit comments