8000 Merge pull request #123 from codellm-devkit/101-add-a-support-to-be-a… · codellm-devkit/codeanalyzer-java@86235d0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 86235d0

Browse files
authored
Merge pull request #123 from codellm-devkit/101-add-a-support-to-be-able-to-caputer-static-and-instance-initialization-blocks-in-java-classes
Feature Request Issue 101: Add a support to be able to Capture static and instance initialization blocks in java classes
2 parents 7fb8148 + c60958b commit 86235d0

File tree

12 files changed

+538
-5
lines changed

12 files changed

+538
-5
lines changed

.github/workflows/release_config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
{
2828
"title": "## \uD83D\uDEE0 Other Updates",
2929
"labels": ["other", "kind/dependency-change"]
30+
},
31+
{
32+
"title": "## 🚨 Breaking Changes",
33+
"labels": ["breaking"]
3034
}
3135
],
3236
"ignore_labels": [

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

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import com.ibm.cldk.entities.*;
3434
import com.ibm.cldk.utils.Log;
3535
import org.apache.commons.lang3.tuple.Pair;
36-
import org.checkerframework.checker.units.qual.C;
3736

3837
import java.io.IOException;
3938
import java.nio.file.Path;
@@ -172,9 +171,16 @@ else if (typeDecl instanceof RecordDeclaration) {
172171
typeNode = new com.ibm.cldk.entities.Type();
173172
}
174173

175-
/* set common attributes of types that available in type declarations:
176-
is nested type, is class or interface declaration, is enum declaration,
177-
comments, parent class, callable declarations, field declarations */
174+
/* set common attributes of types that available in type declarations:
175+
is nested type, is class or interface declaration, is enum declaration,
176+
comments, parent class, callable declarations, field declarations
177+
*/
178+
// Discover initialization blocks
179+
typeNode.setInitializationBlocks(typeDecl.findAll(InitializerDeclaration.class).stream()
180+
.map(initializerDeclaration -> {
181+
return createInitializationBlock(initializerDeclaration, parseResult.getStorage().map(s -> s.getPath().toString()).orElse("<in-memory>"));
182+
})
183+
.collect(Collectors.toList()));
178184
// Set fields indicating nested, class/interface, enum, annotation, and record types
179185
typeNode.setNestedType(typeDecl.isNestedType());
180186
typeNode.setClassOrInterfaceDeclaration(typeDecl.isClassOrInterfaceDeclaration());
@@ -213,7 +219,38 @@ else if (typeDecl instanceof RecordDeclaration) {
213219
return cUnit;
214220
}
215221

216-
222+
private static InitializationBlock createInitializationBlock(InitializerDeclaration initializerDeclaration, String filePath) {
223+
InitializationBlock initializationBlock = new InitializationBlock();
224+
initializationBlock.setFilePath(filePath);
225+
initializationBlock.setComment(initializerDeclaration.getComment().isPresent() ? initializerDeclaration.getComment().get().asString() : "");
226+
initializationBlock.setAnnotations(initializerDeclaration.getAnnotations().stream().map(a -> a.toString().strip()).collect(Collectors.toList()));
227+
// add exceptions declared in "throws" clause
228+
initializationBlock.setThrownExceptions(initializerDeclaration.getBody().getStatements().stream().filter(Statement::isThrowStmt).map(throwStmt -> {
229+
try {
230+
return javaSymbolSolver.calculateType(throwStmt.asThrowStmt().getExpression()).describe();
231+
} catch (Exception e) {
232+
return throwStmt.asThrowStmt().getExpression().toString();
233+
}
234+
}).collect(Collectors.toList()));
235+
initializationBlock.setCode(initializerDeclaration.getBody().toString());
236+
initializationBlock.setStartLine(initializerDeclaration.getRange().isPresent() ? initializerDeclaration.getRange().get().begin.line : -1);
237+
initializationBlock.setEndLine(initializerDeclaration.getRange().isPresent() ? initializerDeclaration.getRange().get().end.line : -1);
238+
initializationBlock.setStatic(initializerDeclaration.isStatic());
239+
initializationBlock.setReferencedTypes(getReferencedTypes(Optional.ofNullable(initializerDeclaration.getBody())));
240+
initializationBlock.setAccessedFields(getAccessedFields(Optional.ofNullable(initializerDeclaration.getBody()), Collections.emptyList(), ""));
241+
initializationBlock.setCallSites(getCallSites(Optional.ofNullable(initializerDeclaration.getBody())));
242+
initializationBlock.setVariableDeclarations(getVariableDeclarations(Optional.ofNullable(initializerDeclaration.getBody())));
243+
initializationBlock.setCyclomaticComplexity(getCyclomaticComplexity(initializerDeclaration));
244+
return initializationBlock;
245+
}
246+
/**
247+
* Processes the given record to extract information about the
248+
* declared field and returns a JSON object containing the extracted
249+
* information.
250+
*
251+
* @param recordDecl field declaration to be processed
252+
* @return Field object containing extracted information
253+
*/
217254
private static List<RecordComponent> processRecordComponents(RecordDeclaration recordDecl) {
218255
return recordDecl.getParameters().stream().map(
219256
parameter -> {
@@ -568,6 +605,14 @@ private static int getCyclomaticComplexity(CallableDeclaration callableDeclarati
568605
int catchClauseCount = callableDeclaration.findAll(CatchClause.class).size();
569606
return ifStmtCount + loopStmtCount + switchCaseCount + conditionalExprCount + catchClauseCount + 1;
570607
}
608+
private static int getCyclomaticComplexity(InitializerDeclaration initializerDeclaration) {
609+
int ifStmtCount = initializerDeclaration.findAll(IfStmt.class).size();
610+
int loopStmtCount = initializerDeclaration.findAll(DoStmt.class).size() + initializerDeclaration.findAll(ForStmt.class).size() + initializerDeclaration.findAll(ForEachStmt.class).size() + initializerDeclaration.findAll(WhileStmt.class).size();
611+
int switchCaseCount = initializerDeclaration.findAll(SwitchStmt.class).stream().map(stmt -> stmt.getEntries().size()).reduce(0, Integer::sum);
612+
int conditionalExprCount = initializerDeclaration.findAll(ConditionalExpr.class).size();
613+
int catchClauseCount = initializerDeclaration.findAll(CatchClause.class).size();
614+
return ifStmtCount + loopStmtCount + switchCaseCount + conditionalExprCount + catchClauseCount + 1;
615+
}
571616

572617
/**
573618
* Processes the given field declaration to extract information about the
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.ibm.cldk.entities;
2+
3+
import lombok.Data;
4+
5+
import java.util.List;
6+
import java.util.stream.Collector;
7+
8+
@Data
9+
public class InitializationBlock {
10+
private String filePath;
11+
private String comment;
12+
private List<String> annotations;
13+
private List<String> thrownExceptions;
14+
private String code;
15+
private int startLine;
16+
private int endLine;
17+
private boolean isStatic;
18+
private List<String> referencedTypes;
19+
private List<String> accessedFields;
20+
private List<CallSite> callSites;
21+
private List<VariableDeclaration> variableDeclarations;
22+
private int cyclomaticComplexity;
23+
24+
}

src/main/java/com/ibm/cldk/entities/Type.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ public class Type {
2828
private List<Field> fieldDeclarations = new ArrayList<>();
2929
private List<EnumConstant> enumConstants = new ArrayList<>();
3030
private List<RecordComponent> recordComponents = new ArrayList<>();
31+
private List<InitializationBlock> initializationBlocks = new ArrayList<>();
3132
private boolean isEntrypointClass = false;
3233
}

src/test/java/com/ibm/cldk/CodeAnalyzerIntegrationTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class CodeAnalyzerIntegrationTest {
5555
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/plantsbywebsphere")), "/test-applications/plantsbywebsphere")
5656
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/call-graph-test")), "/test-applications/call-graph-test")
5757
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/record-class-test")), "/test-applications/record-class-test")
58+
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/init-blocks-test")), "/test-applications/init-blocks-test")
5859
.withCopyFileToContainer(MountableFile.forHostPath(Paths.get(System.getProperty("user.dir")).resolve("src/test/resources/test-applications/mvnw-working-test")), "/test-applications/mvnw-working-test");
5960

6061
@Container
@@ -332,4 +333,35 @@ void parametersInCallableMustHaveStartAndEndLineAndColumns() throws IOException,
332333
}
333334
}
334335
}
336+
337+
@Test
338+
void mustBeAbleToResolveInitializationBlocks() throws IOException, InterruptedException {
339+
var runCodeAnalyzerOnCallGraphTest = container.execInContainer(
340+
"bash", "-c",
341+
String.format(
342+
"export JAVA_HOME=%s && java -jar /opt/jars/codeanalyzer-%s.jar --input=/test-applications/init-blocks-test --analysis-level=1",
343+
javaHomePath, codeanalyzerVersion
344+
)
345+
);
346+
347+
// Read the output JSON
348+
Gson gson = new Gson();
349+
JsonObject jsonObject = gson.fromJson(runCodeAnalyzerOnCallGraphTest.getStdout(), JsonObject.class);
350+
JsonObject symbolTable = jsonObject.getAsJsonObject("symbol_table");
351+
for (Map.Entry<String, JsonElement> element : symbolTable.entrySet()) {
352+
String key = element.getKey();
353+
if (!key.endsWith("App.java")) {
354+
continue;
355+
}
356+
JsonObject type = element.getValue().getAsJsonObject();
357+
if (type.has("type_declarations")) {
358+
JsonObject typeDeclarations = type.getAsJsonObject("type_declarations");
359+
JsonArray initializationBlocks = typeDeclarations.getAsJsonObject("org.example.App").getAsJsonArray("initialization_blocks");
360+
// There should be 2 blocks
361+
Assertions.assertEquals(2, initializationBlocks.size(), "Callable should have 1 parameter");
362+
Assertions.assertTrue(initializationBlocks.get(0).getAsJsonObject().get("is_static").getAsBoolean(), "Static block should be marked as static");
363+
Assertions.assertFalse(initializationBlocks.get(1).getAsJsonObject().get("is_static").getAsBoolean(), "Instance block should be marked as not static");
364+
}
365+
}
366+
}
335367
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# https://help.github.com/articles/dealing-with-line-endings/
3+
#
4+
# Linux start script should use lf
5+
/gradlew text eol=lf
6+
7+
# These are Windows script files and should use crlf
8+
*.bat text eol=crlf
9+
10+
# Binary files should be left untouched
11+
*.jar binary
12+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Ignore Gradle project-specific cache directory
2+
.gradle
3+
4+
# Ignore Gradle build output directory
5+
build
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.example;
2+
3+
import java.util.List;
4+
5+
public class App {
6+
private static String staticMessage;
7+
8+
static {
9+
try {
10+
staticMessage = "Static block initialized";
11+
System.out.println("Static initialization block executed.");
12+
initializeStaticFields();
13+
} catch (Exception e) {
14+
System.err.println("Error in static block: " + e.getMessage());
15+
throw new RuntimeException(e);
16+
}
17+
}
18+
19+
{
20+
try {
21+
System.out.println("Instance initialization block executed.");
22+
initializeInstanceFields();
23+
} catch (Exception e) {
24+
System.err.println("Error in instance block: " + e.getMessage());
25+
}
26+
}
27+
28+
public App() {
29+
System.out.println("Constructor executed.");
30+
}
31+
32+
private static void initializeStaticFields() {
33+
System.out.println("Initializing static fields.");
34+
}
35+
36+
private void initializeInstanceFields() {
37+
System.out.println("Initializing instance fields.");
38+
}
39+
40+
public static void main(String[] args) {
41+
new App();
42+
}
43+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This file was generated by the Gradle 'init' task.
2+
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
3+
4+
org.gradle.configuration-cache=true
5+
org.gradle.parallel=true
6+
org.gradle.caching=true
7+

0 commit comments

Comments
 (0)
0