From 392d12a88822dd3136e071e57bd81e4f8643f7e6 Mon Sep 17 00:00:00 2001 From: Saurabh Sinha Date: Mon, 13 Jan 2025 12:58:16 -0500 Subject: [PATCH 1/9] Cyclomatic complexity computation for analysis level 1 Signed-off-by: Saurabh Sinha --- src/main/java/com/ibm/cldk/SymbolTable.java | 24 ++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ibm/cldk/SymbolTable.java b/src/main/java/com/ibm/cldk/SymbolTable.java index 7abe086d..4ecfb8a1 100644 --- a/src/main/java/com/ibm/cldk/SymbolTable.java +++ b/src/main/java/com/ibm/cldk/SymbolTable.java @@ -10,7 +10,7 @@ import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.*; import com.github.javaparser.ast.nodeTypes.NodeWithName; -import com.github.javaparser.ast.stmt.BlockStmt; +import com.github.javaparser.ast.stmt.*; import com.github.javaparser.ast.type.ReferenceType; import com.github.javaparser.ast.type.Type; import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; @@ -301,11 +301,33 @@ private static Pair processCallableDeclaration(CallableDeclara callableNode.setAccessedFields(getAccessedFields(body, classFields, typeName)); callableNode.setCallSites(getCallSites(body)); callableNode.setVariableDeclarations(getVariableDeclarations(body)); + callableNode.setCyclomaticComplexity(getCyclomaticComplexity(callableDecl)); String callableSignature = (callableDecl instanceof MethodDeclaration) ? callableDecl.getSignature().asString() : callableDecl.getSignature().asString().replace(callableDecl.getSignature().getName(), ""); return Pair.of(callableSignature, callableNode); } + /** + * Computes cyclomatic complexity for the given callable. + * + * @param callableDeclaration Callable to compute cyclomatic complexity for + * @return cyclomatic complexity + */ + private static int getCyclomaticComplexity(CallableDeclaration callableDeclaration) { + int ifStmtCount = callableDeclaration.findAll(IfStmt.class).size(); + int loopStmtCount = callableDeclaration.findAll(DoStmt.class).size() + + callableDeclaration.findAll(ForStmt.class).size() + + callableDeclaration.findAll(ForEachStmt.class).size() + + callableDeclaration.findAll(WhileStmt.class).size(); + int switchCaseCount = callableDeclaration.findAll(SwitchStmt.class).stream() + .map(stmt -> stmt.getEntries().size()) + .reduce(0, Integer::sum); + int conditionalExprCount = callableDeclaration.findAll(ConditionalExpr.class).size(); + int catchClauseCount = callableDeclaration.findAll(CatchClause.class).size(); + int cyclomaticComplexity = ifStmtCount + loopStmtCount + switchCaseCount + conditionalExprCount + catchClauseCount + 1; + return cyclomaticComplexity; + } + /** * Processes the given field declaration to extract information about the * declared field and From ebe64b60f97223c2f6fba0e724a37809fbd827be Mon Sep 17 00:00:00 2001 From: Saurabh Sinha Date: Mon, 13 Jan 2025 14:05:15 -0500 Subject: [PATCH 2/9] Added catch clause count to computation of cyclomatic complexity for analysis level 2 Signed-off-by: Saurabh Sinha --- src/main/java/com/ibm/cldk/SymbolTable.java | 3 +-- src/main/java/com/ibm/cldk/utils/AnalysisUtils.java | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ibm/cldk/SymbolTable.java b/src/main/java/com/ibm/cldk/SymbolTable.java index 4ecfb8a1..b767d39c 100644 --- a/src/main/java/com/ibm/cldk/SymbolTable.java +++ b/src/main/java/com/ibm/cldk/SymbolTable.java @@ -324,8 +324,7 @@ private static int getCyclomaticComplexity(CallableDeclaration callableDeclarati .reduce(0, Integer::sum); int conditionalExprCount = callableDeclaration.findAll(ConditionalExpr.class).size(); int catchClauseCount = callableDeclaration.findAll(CatchClause.class).size(); - int cyclomaticComplexity = ifStmtCount + loopStmtCount + switchCaseCount + conditionalExprCount + catchClauseCount + 1; - return cyclomaticComplexity; + return ifStmtCount + loopStmtCount + switchCaseCount + conditionalExprCount + catchClauseCount + 1; } /** diff --git a/src/main/java/com/ibm/cldk/utils/AnalysisUtils.java b/src/main/java/com/ibm/cldk/utils/AnalysisUtils.java index 89bc0f80..a9e3e8f3 100644 --- a/src/main/java/com/ibm/cldk/utils/AnalysisUtils.java +++ b/src/main/java/com/ibm/cldk/utils/AnalysisUtils.java @@ -23,6 +23,7 @@ import com.ibm.wala.ipa.callgraph.impl.DefaultEntrypoint; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.ssa.IR; +import com.ibm.wala.ssa.ISSABasicBlock; import com.ibm.wala.ssa.SSAConditionalBranchInstruction; import com.ibm.wala.ssa.SSASwitchInstruction; import com.ibm.wala.types.ClassLoaderReference; @@ -96,7 +97,11 @@ public static int getCyclomaticComplexity(IR ir) { int switchBranchCount = Arrays.stream(ir.getInstructions()) .filter(inst -> inst instanceof SSASwitchInstruction) .map(inst -> ((SSASwitchInstruction) inst).getCasesAndLabels().length).reduce(0, Integer::sum); - return conditionalBranchCount + switchBranchCount + 1; + Iterable iterableBasicBlocks = ir::getBlocks; + int catchBlockCount = (int) StreamSupport.stream(iterableBasicBlocks.spliterator(), false) + .filter(ISSABasicBlock::isCatchBlock) + .count(); + return conditionalBranchCount + switchBranchCount + catchBlockCount + 1; } public static Pair getCallableFromSymbolTable(IMethod method) { From 83d55e090908ec92af2961da84671e0e5a82c99c Mon Sep 17 00:00:00 2001 From: Saurabh Sinha Date: Wed, 15 Jan 2025 17:31:38 -0500 Subject: [PATCH 3/9] Clean up callable declaration string to remove embedded comments. Signed-off-by: Saurabh Sinha --- src/main/java/com/ibm/cldk/SymbolTable.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ibm/cldk/SymbolTable.java b/src/main/java/com/ibm/cldk/SymbolTable.java index b767d39c..32e68694 100644 --- a/src/main/java/com/ibm/cldk/SymbolTable.java +++ b/src/main/java/com/ibm/cldk/SymbolTable.java @@ -273,7 +273,9 @@ private static Pair processCallableDeclaration(CallableDeclara // add the complete declaration string, including modifiers, throws, and // parameter names - callableNode.setDeclaration(callableDecl.getDeclarationAsString(true, true, true).strip()); + callableNode.setDeclaration(callableDecl + .getDeclarationAsString(true, true, true) + .strip().replaceAll("//.*\n", "")); // add information about callable parameters: for each parameter, type, name, // annotations, From 5d147069444849d3ece4b0aba2130dde5a98e06f Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Thu, 16 Jan 2025 04:51:37 +0530 Subject: [PATCH 4/9] Fixing issue 87 with maven wrapper --- gradle.properties | 2 +- .../java/com/ibm/cldk/utils/BuildProject.java | 24 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index d8cb228c..23372ae1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=1.0.8 +version=1.0.9-dev diff --git a/src/main/java/com/ibm/cldk/utils/BuildProject.java b/src/main/java/com/ibm/cldk/utils/BuildProject.java index bd808b3e..c6d6132f 100644 --- a/src/main/java/com/ibm/cldk/utils/BuildProject.java +++ b/src/main/java/com/ibm/cldk/utils/BuildProject.java @@ -90,7 +90,25 @@ private static String getGradleCmd() { private static boolean commandExists(String command) { try { - Process process = new ProcessBuilder(command, "--version").start(); + Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(command, "--version").start(); + + // Read the output stream + BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + String line; + while ((line = reader.readLine()) != null) { + Log.info(line); + } + + // Read the error stream + BufferedReader errorReader = new BufferedReader( + new InputStreamReader(process.getErrorStream()) + ); + while ((line = errorReader.readLine()) != null) { + Log.info(line); + } + int exitCode = process.waitFor(); return exitCode == 0; } catch (IOException | InterruptedException exceptions) { @@ -100,7 +118,7 @@ private static boolean commandExists(String command) { private static boolean buildWithTool(String[] buildCommand) { Log.info("Building the project using " + buildCommand[0] + "."); - ProcessBuilder processBuilder = new ProcessBuilder(buildCommand); + ProcessBuilder processBuilder = new ProcessBuilder().directory(new File(projectRootPom)).command(buildCommand); try { Process process = processBuilder.start(); @@ -125,7 +143,7 @@ private static boolean buildWithTool(String[] buildCommand) { * @return true if Maven is installed, false otherwise. */ private static boolean isMavenInstalled() { - ProcessBuilder processBuilder = new ProcessBuilder(MAVEN_CMD, "--version"); + ProcessBuilder processBuilder = new ProcessBuilder().directory(new File(projectRootPom)).command(MAVEN_CMD, "--version"); try { Process process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); From f30e384c2ed0a38d311f8e7b4333ad57c782304c Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Thu, 16 Jan 2025 05:10:41 +0530 Subject: [PATCH 5/9] Fixing issue 87 with maven wrapper --- src/main/java/com/ibm/cldk/utils/BuildProject.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ibm/cldk/utils/BuildProject.java b/src/main/java/com/ibm/cldk/utils/BuildProject.java index c6d6132f..7f75b366 100644 --- a/src/main/java/com/ibm/cldk/utils/BuildProject.java +++ b/src/main/java/com/ibm/cldk/utils/BuildProject.java @@ -54,7 +54,7 @@ private static String getGradleCmd() { String gradleWrapperExists = new File(projectRootPom, gradleWrapper).exists() ? "true" : "false"; if (new File(projectRootPom, gradleWrapper).exists()) { - GRADLE_CMD = gradleWrapper; + GRADLE_CMD = String.valueOf(new File(projectRootPom, gradleWrapper)); } else { GRADLE_CMD = gradle; } @@ -112,6 +112,7 @@ private static boolean commandExists(String command) { int exitCode = process.waitFor(); return exitCode == 0; } catch (IOException | InterruptedException exceptions) { + exceptions.printStackTrace(); return false; } } From 6e745c4c5313c0d7d24acce81f016bc2c4192012 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Thu, 16 Jan 2025 05:24:51 +0530 Subject: [PATCH 6/9] Fixing issue 87 with maven wrapper Signed-off-by: Rahul Krishna --- .../java/com/ibm/cldk/utils/BuildProject.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/main/java/com/ibm/cldk/utils/BuildProject.java b/src/main/java/com/ibm/cldk/utils/BuildProject.java index 7f75b366..0a86dfe2 100644 --- a/src/main/java/com/ibm/cldk/utils/BuildProject.java +++ b/src/main/java/com/ibm/cldk/utils/BuildProject.java @@ -91,28 +91,9 @@ private static String getGradleCmd() { private static boolean commandExists(String command) { try { Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(command, "--version").start(); - - // Read the output stream - BufferedReader reader = new BufferedReader( - new InputStreamReader(process.getInputStream()) - ); - String line; - while ((line = reader.readLine()) != null) { - Log.info(line); - } - - // Read the error stream - BufferedReader errorReader = new BufferedReader( - new InputStreamReader(process.getErrorStream()) - ); - while ((line = errorReader.readLine()) != null) { - Log.info(line); - } - int exitCode = process.waitFor(); return exitCode == 0; } catch (IOException | InterruptedException exceptions) { - exceptions.printStackTrace(); return false; } } From 93e5efb9eb751e6cb90e285deb6c4799b7a6f60c Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Thu, 16 Jan 2025 05:27:23 +0530 Subject: [PATCH 7/9] Fixing issue 87 with maven wrapper Signed-off-by: Rahul Krishna --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 23372ae1..f3105c6f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=1.0.9-dev +version=1.0.9 From 0dcde52687d8a7cbb7015202e92bb615de48bf34 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Thu, 16 Jan 2025 05:28:24 +0530 Subject: [PATCH 8/9] Fixing issue 87 with maven wrapper Signed-off-by: Rahul Krishna --- src/main/java/com/ibm/cldk/utils/BuildProject.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ibm/cldk/utils/BuildProject.java b/src/main/java/com/ibm/cldk/utils/BuildProject.java index 0a86dfe2..6a99d4c1 100644 --- a/src/main/java/com/ibm/cldk/utils/BuildProject.java +++ b/src/main/java/com/ibm/cldk/utils/BuildProject.java @@ -228,7 +228,7 @@ public static boolean downloadLibraryDependencies(String projectPath, String pro if (pomFile.exists()) { Log.info("Found pom.xml in the project directory. Using Maven to download dependencies."); if (!commandExists(MAVEN_CMD)) - throw new IllegalStateException("Could not find a valid maven command. I did not find " + MAVEN_CMD + " in the project directory or in the system PATH."); + throw new IllegalStateException("Could not find a valid maven command. Could not find " + MAVEN_CMD + " in the project directory or in the system PATH."); String[] mavenCommand = { MAVEN_CMD, "--no-transfer-progress", "-f", @@ -240,7 +240,7 @@ public static boolean downloadLibraryDependencies(String projectPath, String pro } else if (new File(projectRoot, "build.gradle").exists() || new File(projectRoot, "build.gradle.kts").exists()) { Log.info("Found build.gradle or build.gradle.kts in the project directory. Using gradle to download dependencies."); if (!commandExists(GRADLE_CMD)) - throw new IllegalStateException("Could not find a valid Gradle command. I did not find " + GRADLE_CMD + " in the project directory or in the system PATH."); + throw new IllegalStateException("Could not find a valid Gradle command. Could not find " + GRADLE_CMD + " in the project directory or in the system PATH."); Log.info("Found build.gradle[.kts] in the project directory. Using Gradle to download dependencies."); tempInitScript = Files.writeString(tempInitScript, GRADLE_DEPENDENCIES_TASK); From ff1184b477c4b8e9a866ca22e56188ceb725bbe2 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Fri, 17 Jan 2025 14:09:01 +0530 Subject: [PATCH 9/9] Throw a more discriptive error message when build commands fail. Signed-off-by: Rahul Krishna --- .../java/com/ibm/cldk/utils/BuildProject.java | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/ibm/cldk/utils/BuildProject.java b/src/main/java/com/ibm/cldk/utils/BuildProject.java index 6a99d4c1..917d2cf0 100644 --- a/src/main/java/com/ibm/cldk/utils/BuildProject.java +++ b/src/main/java/com/ibm/cldk/utils/BuildProject.java @@ -9,6 +9,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -88,13 +89,38 @@ private static String getGradleCmd() { " }\n" + "}"; - private static boolean commandExists(String command) { + private static AbstractMap.SimpleEntry commandExists(String command) { + StringBuilder output = new StringBuilder(); try { Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(command, "--version").start(); + // Read the output stream + BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + + // Read the error stream + BufferedReader errorReader = new BufferedReader( + new InputStreamReader(process.getErrorStream()) + ); + while ((line = errorReader.readLine()) != null) { + output.append(line).append("\n"); + } + + int exitCode = process.waitFor(); - return exitCode == 0; + return new AbstractMap.SimpleEntry<>( + exitCode == 0, + output.toString().trim() + ); } catch (IOException | InterruptedException exceptions) { - return false; + return new AbstractMap.SimpleEntry<>( + false, + exceptions.getMessage() + ); } } @@ -227,8 +253,9 @@ public static boolean downloadLibraryDependencies(String projectPath, String pro File pomFile = new File(projectRoot, "pom.xml"); if (pomFile.exists()) { Log.info("Found pom.xml in the project directory. Using Maven to download dependencies."); - if (!commandExists(MAVEN_CMD)) - throw new IllegalStateException("Could not find a valid maven command. Could not find " + MAVEN_CMD + " in the project directory or in the system PATH."); + AbstractMap.SimpleEntry mavenCheck = commandExists(MAVEN_CMD); + if (!mavenCheck.getKey()) + throw new IllegalStateException("Unable to execute Maven command. Attempt failed with message\n" + mavenCheck.getValue()); String[] mavenCommand = { MAVEN_CMD, "--no-transfer-progress", "-f", @@ -239,8 +266,9 @@ public static boolean downloadLibraryDependencies(String projectPath, String pro return buildWithTool(mavenCommand); } else if (new File(projectRoot, "build.gradle").exists() || new File(projectRoot, "build.gradle.kts").exists()) { Log.info("Found build.gradle or build.gradle.kts in the project directory. Using gradle to download dependencies."); - if (!commandExists(GRADLE_CMD)) - throw new IllegalStateException("Could not find a valid Gradle command. Could not find " + GRADLE_CMD + " in the project directory or in the system PATH."); + AbstractMap.SimpleEntry gradleCheck = commandExists(GRADLE_CMD); + if (!gradleCheck.getKey()) + throw new IllegalStateException("Could not execute Gradle command. Attempt failed with message\n" + gradleCheck.getValue()); Log.info("Found build.gradle[.kts] in the project directory. Using Gradle to download dependencies."); tempInitScript = Files.writeString(tempInitScript, GRADLE_DEPENDENCIES_TASK);