diff --git a/src/main/java/com/ibm/northstar/SymbolTable.java b/src/main/java/com/ibm/northstar/SymbolTable.java index 88a993a0..8ff95358 100644 --- a/src/main/java/com/ibm/northstar/SymbolTable.java +++ b/src/main/java/com/ibm/northstar/SymbolTable.java @@ -4,6 +4,7 @@ import com.github.javaparser.ParseResult; import com.github.javaparser.ParserConfiguration; import com.github.javaparser.Problem; +import com.github.javaparser.ast.AccessSpecifier; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; @@ -12,6 +13,7 @@ import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.type.ReferenceType; import com.github.javaparser.ast.type.Type; +import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; import com.github.javaparser.resolution.types.ResolvedType; import com.github.javaparser.symbolsolver.JavaSymbolSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; @@ -498,12 +500,21 @@ private static List getCallSites(Optional callableBody) { Log.debug("Could not resolve method call: " + methodCallExpr + ": " + exception.getMessage()); } + // Resolve access qualifier + AccessSpecifier accessSpecifier = AccessSpecifier.NONE; + try { + ResolvedMethodDeclaration resolvedMethodDeclaration = methodCallExpr.resolve(); + accessSpecifier = resolvedMethodDeclaration.accessSpecifier(); + } + catch (RuntimeException exception) { + Log.debug("Could not resolve access specifier for method call: " + methodCallExpr + ": " + exception.getMessage()); + } // resolve arguments of the method call to types List arguments = methodCallExpr.getArguments().stream() .map(SymbolTable::resolveExpression).collect(Collectors.toList()); // add a new call site object callSites.add(createCallSite(methodCallExpr, methodCallExpr.getNameAsString(), receiverName, declaringType, - arguments, returnType, calleeSignature, isStaticCall, false)); + arguments, returnType, calleeSignature, isStaticCall, false, accessSpecifier)); } for (ObjectCreationExpr objectCreationExpr : callableBody.get().findAll(ObjectCreationExpr.class)) { @@ -525,7 +536,7 @@ private static List getCallSites(Optional callableBody) { // add a new call site object callSites.add(createCallSite(objectCreationExpr, "", objectCreationExpr.getScope().isPresent() ? objectCreationExpr.getScope().get().toString() : "", - instantiatedType, arguments, instantiatedType, calleeSignature,false, true)); + instantiatedType, arguments, instantiatedType, calleeSignature,false, true, AccessSpecifier.NONE)); } return callSites; @@ -546,7 +557,7 @@ private static List getCallSites(Optional callableBody) { */ private static CallSite createCallSite(Expression callExpr, String calleeName, String receiverExpr, String receiverType, List arguments, String returnType, - String calleeSignature, boolean isStaticCall, boolean isConstructorCall) { + String calleeSignature, boolean isStaticCall, boolean isConstructorCall, AccessSpecifier accessSpecifier) { CallSite callSite = new CallSite(); callSite.setMethodName(calleeName); callSite.setReceiverExpr(receiverExpr); @@ -556,6 +567,10 @@ private static CallSite createCallSite(Expression callExpr, String calleeName, S callSite.setCalleeSignature(calleeSignature); callSite.setStaticCall(isStaticCall); callSite.setConstructorCall(isConstructorCall); + callSite.setPrivate(accessSpecifier.equals(AccessSpecifier.PRIVATE)); + callSite.setPublic(accessSpecifier.equals(AccessSpecifier.PUBLIC)); + callSite.setProtected(accessSpecifier.equals(AccessSpecifier.PROTECTED)); + callSite.setUnspecified(accessSpecifier.equals(AccessSpecifier.NONE)); if (callExpr.getRange().isPresent()) { callSite.setStartLine(callExpr.getRange().get().begin.line); callSite.setStartColumn(callExpr.getRange().get().begin.column); diff --git a/src/main/java/com/ibm/northstar/entities/CallSite.java b/src/main/java/com/ibm/northstar/entities/CallSite.java index 0c466d46..cc7e3d7f 100644 --- a/src/main/java/com/ibm/northstar/entities/CallSite.java +++ b/src/main/java/com/ibm/northstar/entities/CallSite.java @@ -12,6 +12,11 @@ public class CallSite { private List argumentTypes; private String returnType; private String calleeSignature; + // Access specifiers + private boolean isPublic = false; + private boolean isProtected = false; + private boolean isPrivate = false; + private boolean isUnspecified = false; private boolean isStaticCall; private boolean isConstructorCall; private int startLine; diff --git a/src/main/java/com/ibm/northstar/utils/BuildProject.java b/src/main/java/com/ibm/northstar/utils/BuildProject.java index cea5addf..317e7c6f 100644 --- a/src/main/java/com/ibm/northstar/utils/BuildProject.java +++ b/src/main/java/com/ibm/northstar/utils/BuildProject.java @@ -7,6 +7,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; import java.util.List; import static com.ibm.northstar.utils.ProjectDirectoryScanner.classFilesStream; @@ -17,6 +18,31 @@ public class BuildProject { private static final String LIB_DEPS_DOWNLOAD_DIR = "_library_dependencies"; private static final String MAVEN_CMD = System.getProperty("os.name").toLowerCase().contains("windows") ? "mvn.cmd" : "mvn"; private static final String GRADLE_CMD = System.getProperty("os.name").toLowerCase().contains("windows") ? "gradlew.bat" : "gradlew"; + public static Path tempInitScript; + static { + try { + tempInitScript = Files.createTempFile("gradle-init-", ".gradle"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private static final String GRADLE_DEPENDENCIES_TASK = "allprojects { afterEvaluate { project -> task downloadDependencies(type: Copy) {\n" + + " def configs = project.configurations.findAll { it.canBeResolved }\n\n" + + " dependsOn configs\n" + + " from configs\n" + + " into project.hasProperty('outputDir') ? project.property('outputDir') : \"${project.buildDir}/libs\"\n\n" + + " doFirst {\n" + + " println \"Downloading dependencies for project ${project.name} to: ${destinationDir}\"\n" + + " configs.each { config ->\n" + + " println \"Configuration: ${config.name}\"\n" + + " config.resolvedConfiguration.resolvedArtifacts.each { artifact ->\n" + + " println \"\t${artifact.moduleVersion.id}:${artifact.extension}\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; private static boolean buildWithTool(String[] buildCommand) { Log.info("Building the project using " + buildCommand[0] + "."); @@ -30,6 +56,7 @@ private static boolean buildWithTool(String[] buildCommand) { Log.info(line); } int exitCode = process.waitFor(); + process.getErrorStream().transferTo(System.err); Log.info(buildCommand[0].toUpperCase() + " build exited with code " + exitCode); return exitCode == 0; } catch (IOException | InterruptedException e) { @@ -73,10 +100,10 @@ private static boolean mavenBuild(String projectPath) { return false; } String[] mavenCommand = { - MAVEN_CMD, "clean", "compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip", - "-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip", - "-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true", - "-Drat.skip=true", "-Dspotless.check.skip=true" }; + MAVEN_CMD, "clean", "compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip", + "-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip", + "-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true", + "-Drat.skip=true", "-Dspotless.check.skip=true"}; return buildWithTool(mavenCommand); } @@ -84,13 +111,13 @@ private static boolean mavenBuild(String projectPath) { public static boolean gradleBuild(String projectPath) { // Adjust Gradle command as needed String gradleWrapper = projectPath + File.separator + GRADLE_CMD; - String[] gradleCommand = { gradleWrapper, "clean", "compileJava", "-p", projectPath }; + String[] gradleCommand = {gradleWrapper, "clean", "compileJava", "-p", projectPath}; return buildWithTool(gradleCommand); } private static boolean buildProject(String projectPath, String build) { File pomFile = new File(projectPath, "pom.xml"); - if (build ==null) { + if (build == null) { return true; } else if (build.equals("auto")) { if (pomFile.exists()) { @@ -100,8 +127,7 @@ private static boolean buildProject(String projectPath, String build) { Log.info("Did not find a pom.xml in the project directory. Using Gradle to build the project."); return gradleBuild(projectPath); // Otherwise, use Gradle } - } - else { + } else { // Update command with a project path build = build.replace(MAVEN_CMD, MAVEN_CMD + " -f " + projectPath); Log.info("Using custom build command: " + build); @@ -127,14 +153,14 @@ public static List buildProjectAndStreamClassFiles(String projectPath, Str * @param projectPath Path to the project under analysis * @return true if dependency download succeeds; false otherwise */ - public static boolean downloadLibraryDependencies(String projectPath) { + public static boolean downloadLibraryDependencies(String projectPath) throws IOException { // created download dir if it does not exist libDownloadPath = Paths.get(projectPath, LIB_DEPS_DOWNLOAD_DIR).toAbsolutePath(); if (!Files.exists(libDownloadPath)) { try { Files.createDirectory(libDownloadPath); } catch (IOException e) { - Log.error("Error creating library dependency directory for " + projectPath + ": " +e.getMessage()); + Log.error("Error creating library dependency directory for " + projectPath + ": " + e.getMessage()); return false; } } @@ -142,16 +168,20 @@ public static boolean downloadLibraryDependencies(String projectPath) { if (pomFile.exists()) { Log.info("Found pom.xml in the project directory. Using Maven to download dependencies."); String[] mavenCommand = { - MAVEN_CMD, "--no-transfer-progress", "-f", - Paths.get(projectPath, "pom.xml").toString(), - "dependency:copy-dependencies", - "-DoutputDirectory=" + libDownloadPath.toString() + MAVEN_CMD, "--no-transfer-progress", "-f", + Paths.get(projectPath, "pom.xml").toString(), + "dependency:copy-dependencies", + "-DoutputDirectory=" + libDownloadPath.toString() }; return buildWithTool(mavenCommand); - } else { - // TODO: implement for gradle - return false; + } else if (new File(projectPath, "build.gradle").exists() || new File(projectPath, "build.gradle.kts").exists()) { + Log.info("Found build.gradle[.kts] in the project directory. Using Gradle to download dependencies."); + tempInitScript = Files.writeString(tempInitScript, GRADLE_DEPENDENCIES_TASK); + String[] gradleCommand = {projectPath + File.separator + GRADLE_CMD, "--init-script", tempInitScript.toFile().getAbsolutePath(), "downloadDependencies", "-PoutputDir="+libDownloadPath.toString()}; + System.out.println(Arrays.toString(gradleCommand)); + return buildWithTool(gradleCommand); } + return false; } public static void cleanLibraryDependencies() { @@ -167,5 +197,12 @@ public static void cleanLibraryDependencies() { Log.error("Error deleting library dependency directory: " + e.getMessage()); } } + if (tempInitScript != null) { + try { + Files.delete(tempInitScript); + } catch (IOException e) { + Log.error("Error deleting temporary Gradle init script: " + e.getMessage()); + } + } } }