Skip to content

Commit 7e06a77

Browse files
authored
Merge pull request #48 from IBM/incremental-analysis
Implementation of incremental analysis at analysis level 1
2 parents 8c82323 + 13178d5 commit 7e06a77

File tree

3 files changed

+102
-7
lines changed

3 files changed

+102
-7
lines changed

src/main/java/com/ibm/northstar/CodeAnalyzer.java

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
package com.ibm.northstar;
1515

16-
1716
import com.github.javaparser.Problem;
17+
import com.google.common.reflect.TypeToken;
1818
import com.google.gson.*;
1919
import com.ibm.northstar.entities.JavaCompilationUnit;
2020
import com.ibm.northstar.utils.BuildProject;
@@ -27,8 +27,10 @@
2727
import picocli.CommandLine.Option;
2828

2929
import java.io.File;
30+
import java.io.FileReader;
3031
import java.io.FileWriter;
3132
import java.io.IOException;
33+
import java.lang.reflect.Type;
3234
import java.nio.file.Files;
3335
import java.nio.file.Path;
3436
import java.nio.file.Paths;
@@ -44,6 +46,9 @@ public class CodeAnalyzer implements Runnable {
4446
@Option(names = {"-i", "--input"}, description = "Path to the project root directory.")
4547
private static String input;
4648

49+
@Option(names = {"-t", "--target-files"}, description = "Paths to files to be analyzed from the input application.")
50+
private static List<String> targetFiles;
51+
4752
@Option(names = {"-s", "--source-analysis"}, description = "Analyze a single string of java source code instead the project.")
4853
private static String sourceAnalysis;
4954

@@ -62,6 +67,8 @@ public class CodeAnalyzer implements Runnable {
6267
@Option(names = {"-v", "--verbose"}, description = "Print logs to console.")
6368
private static boolean verbose = false;
6469

70+
private static final String outputFileName = "analysis.json";
71+
6572

6673
public static Gson gson = new GsonBuilder()
6774
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
@@ -109,11 +116,46 @@ private static void analyze() throws IOException, ClassHierarchyException, CallG
109116
} else {
110117
Log.warn("Failed to download library dependencies of project");
111118
}
112-
// construct symbol table for project, write parse problems to file in output directory if specified
113-
Pair<Map<String, JavaCompilationUnit>, Map<String, List<Problem>>> symbolTableExtractionResult =
114-
SymbolTable.extractAll(Paths.get(input));
119+
boolean analysisFileExists = output != null && Files.exists(Paths.get(output + File.separator + outputFileName));
120+
121+
// if target files are specified, compute symbol table information for the given files
122+
if (targetFiles != null) {
123+
Log.info(targetFiles.size() + "target files specified for analysis: " + targetFiles);
124+
125+
// if target files specified for analysis level 2, downgrade to analysis level 1
126+
if (analysisLevel > 1) {
127+
Log.warn("Incremental analysis is supported at analysis level 1 only; " +
128+
"performing analysis level 1 for target files");
129+
analysisLevel = 1;
130+
}
131+
132+
// extract symbol table for the specified files
133+
symbolTable = SymbolTable.extract(Paths.get(input), targetFiles.stream().map(Paths::get).toList()).getLeft();
134+
135+
// if analysis file exists, update it with new symbol table information for the specified fiels
136+
if (analysisFileExists) {
137+
// read symbol table information from existing analysis file
138+
Map<String, JavaCompilationUnit> existingSymbolTable = readSymbolTableFromFile(
139+
new File(output, outputFileName));
140+
if (existingSymbolTable != null) {
141+
// for each file, tag its symbol table information as "updated" and update existing symbol table
142+
for (String targetFile : targetFiles) {
143+
String targetPathAbs = Paths.get(targetFile).toAbsolutePath().toString();
144+
JavaCompilationUnit javaCompilationUnit = symbolTable.get(targetPathAbs);
145+
javaCompilationUnit.setModified(true);
146+
existingSymbolTable.put(targetPathAbs, javaCompilationUnit);
147+
}
148+
}
149+
symbolTable = existingSymbolTable;
150+
}
151+
}
152+
else {
153+
// construct symbol table for project, write parse problems to file in output directory if specified
154+
Pair<Map<String, JavaCompilationUnit>, Map<String, List<Problem>>> symbolTableExtractionResult =
155+
SymbolTable.extractAll(Paths.get(input));
115156

116-
symbolTable = symbolTableExtractionResult.getLeft();
157+
symbolTable = symbolTableExtractionResult.getLeft();
158+
}
117159

118160
if (analysisLevel > 1) {
119161
// Save SDG, and Call graph as JSON
@@ -166,4 +208,15 @@ private static void emit(String consolidatedJSONString) throws IOException {
166208
}
167209
}
168210
}
211+
212+
private static Map<String, JavaCompilationUnit> readSymbolTableFromFile(File analysisJsonFile) {
213+
Type symbolTableType = new TypeToken<Map<String, JavaCompilationUnit>>() {}.getType();
214+
try (FileReader reader = new FileReader(analysisJsonFile)) {
215+
JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
216+
return gson.fromJson(jsonObject.get("symbol_table"), symbolTableType);
217+
} catch (IOException e) {
218+
Log.error("Error reading analysis file: " + e.getMessage());
219+
}
220+
return null;
221+
}
169222
}

src/main/java/com/ibm/northstar/SymbolTable.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.github.javaparser.resolution.types.ResolvedType;
1818
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
1919
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
20+
import com.github.javaparser.symbolsolver.resolution.typesolvers.JarTypeSolver;
2021
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
2122
import com.github.javaparser.symbolsolver.utils.SymbolSolverCollectionStrategy;
2223
import com.github.javaparser.utils.ProjectRoot;
@@ -640,8 +641,8 @@ public static Pair<Map<String, JavaCompilationUnit>, Map<String, List<Problem>>>
640641
SymbolSolverCollectionStrategy symbolSolverCollectionStrategy = new SymbolSolverCollectionStrategy();
641642
ProjectRoot projectRoot = symbolSolverCollectionStrategy.collect(projectRootPath);
642643
javaSymbolSolver = (JavaSymbolSolver)symbolSolverCollectionStrategy.getParserConfiguration().getSymbolResolver().get();
643-
Map symbolTable = new LinkedHashMap<String, JavaCompilationUnit>();
644-
Map parseProblems = new HashMap<String, List<Problem>>();
644+
Map<String, JavaCompilationUnit> symbolTable = new LinkedHashMap<>();
645+
Map<String, List<Problem>> parseProblems = new HashMap<>();
645646
for (SourceRoot sourceRoot : projectRoot.getSourceRoots()) {
646647
for (ParseResult<CompilationUnit> parseResult : sourceRoot.tryToParse()) {
647648
if (parseResult.isSuccessful()) {
@@ -681,6 +682,46 @@ public static Pair<Map<String, JavaCompilationUnit>, Map<String, List<Problem>>>
681682
return Pair.of(symbolTable, parseProblems);
682683
}
683684

685+
/**
686+
* Parses the given set of Java source files from the given project and constructs the symbol table.
687+
* @param projectRootPath
688+
* @param javaFilePaths
689+
* @return
690+
* @throws IOException
691+
*/
692+
public static Pair<Map<String, JavaCompilationUnit>, Map<String, List<Problem>>> extract(
693+
Path projectRootPath,
694+
List<Path> javaFilePaths
695+
) throws IOException {
696+
697+
// create symbol solver and parser configuration
698+
SymbolSolverCollectionStrategy symbolSolverCollectionStrategy = new SymbolSolverCollectionStrategy();
699+
ProjectRoot projectRoot = symbolSolverCollectionStrategy.collect(projectRootPath);
700+
javaSymbolSolver = (JavaSymbolSolver)symbolSolverCollectionStrategy.getParserConfiguration().getSymbolResolver().get();
701+
ParserConfiguration parserConfiguration = new ParserConfiguration();
702+
parserConfiguration.setSymbolResolver(javaSymbolSolver);
703+
704+
// create java parser with the configuration
705+
JavaParser javaParser = new JavaParser(parserConfiguration);
706+
707+
Map symbolTable = new LinkedHashMap<String, JavaCompilationUnit>();
708+
Map parseProblems = new HashMap<String, List<Problem>>();
709+
710+
// parse all given files and return pair of symbol table and parse problems
711+
for (Path javaFilePath : javaFilePaths) {
712+
ParseResult<CompilationUnit> parseResult = javaParser.parse(javaFilePath);
713+
if (parseResult.isSuccessful()) {
714+
CompilationUnit compilationUnit = parseResult.getResult().get();
715+
symbolTable.put(compilationUnit.getStorage().get().getPath().toString(),
716+
processCompilationUnit(compilationUnit));
717+
} else {
718+
Log.error(parseResult.getProblems().toString());
719+
parseProblems.put(javaFilePath.toString(), parseResult.getProblems());
720+
}
721+
}
722+
return Pair.of(symbolTable, parseProblems);
723+
}
724+
684725
public static void main(String[] args) throws IOException {
685726
extractAll(Paths.get(args[0]));
686727
}

src/main/java/com/ibm/northstar/entities/JavaCompilationUnit.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ public class JavaCompilationUnit {
1010
private String comment;
1111
private List<String> imports;
1212
private Map<String, Type> typeDeclarations;
13+
private boolean isModified;
1314
}

0 commit comments

Comments
 (0)