Skip to content

Commit 8ced60a

Browse files
committed
Issue 103: Add implementation for Initialization Blocks modeled on Callable.
Signed-off-by: Rahul Krishna <[email protected]>
1 parent cd6adb9 commit 8ced60a

File tree

6 files changed

+89
-13
lines changed

6 files changed

+89
-13
lines changed

settings.gradle

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,4 @@ distributed under the License is distributed on an "AS IS" BASIS,
99
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1010
See the License for the specific language governing permissions and
1111
limitations under the License.
12-
*/
13-
pluginManagement {
14-
plugins {
15-
id 'org.jetbrains.kotlin.jvm' version '2.1.10'
16-
}
17-
}
18-
plugins {
19-
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
20-
}
12+
*/

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

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
import com.github.javaparser.ast.type.ClassOrInterfaceType;
1616
import com.github.javaparser.ast.type.ReferenceType;
1717
import com.github.javaparser.ast.type.Type;
18+
import com.github.javaparser.resolution.TypeSolver;
1819
import com.github.javaparser.resolution.UnsolvedSymbolException;
1920
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
2021
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
2122
import com.github.javaparser.resolution.types.ResolvedType;
2223
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
24+
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
2325
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
2426
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
2527
import com.github.javaparser.symbolsolver.utils.SymbolSolverCollectionStrategy;
@@ -172,9 +174,16 @@ else if (typeDecl instanceof RecordDeclaration) {
172174
typeNode = new com.ibm.cldk.entities.Type();
173175
}
174176

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 */
177+
/* set common attributes of types that available in type declarations:
178+
is nested type, is class or interface declaration, is enum declaration,
179+
comments, parent class, callable declarations, field declarations
180+
*/
181+
// Discover initialization blocks
182+
typeNode.setInitializationBlocks(typeDecl.findAll(InitializerDeclaration.class).stream()
183+
.map(initializerDeclaration -> {
184+
return createInitializationBlock(initializerDeclaration, parseResult.getStorage().map(s -> s.getPath().toString()).orElse("<in-memory>"));
185+
})
186+
.collect(Collectors.toList()));
178187
// Set fields indicating nested, class/interface, enum, annotation, and record types
179188
typeNode.setNestedType(typeDecl.isNestedType());
180189
typeNode.setClassOrInterfaceDeclaration(typeDecl.isClassOrInterfaceDeclaration());
@@ -213,7 +222,38 @@ else if (typeDecl instanceof RecordDeclaration) {
213222
return cUnit;
214223
}
215224

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

572620
/**
573621
* Processes the given field declaration to extract information about the

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import lombok.Data;
44

55
import java.util.List;
6+
import java.util.stream.Collector;
67

78
@Data
89
public class InitializationBlock {
@@ -19,4 +20,5 @@ public class InitializationBlock {
1920
private List<CallSite> callSites;
2021
private List<VariableDeclaration> variableDeclarations;
2122
private int cyclomaticComplexity;
23+
2224
}

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
}

src/test/resources/test-applications/init-blocks-test/app/src/main/java/org/example/App.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class App {
1212
initializeStaticFields();
1313
} catch (Exception e) {
1414
System.err.println("Error in static block: " + e.getMessage());
15+
throw new RuntimeException(e);
1516
}
1617
}
1718

0 commit comments

Comments
 (0)