From 24a9be3a4eafdacf5ca45b379d3969f3a87382ab Mon Sep 17 00:00:00 2001 From: Kamil Patryk Kozakowski Date: Mon, 22 Jun 2026 07:30:05 +0200 Subject: [PATCH] better things extraction of constants --- .../service/AbstractCallGraphEngine.java | 19 +++- .../service/HeuristicCallGraphEngineTest.java | 94 +++++++++++++++++++ 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java index fcdb1cb..62f786d 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java @@ -737,15 +737,15 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { extractConstantsFromExpression(aa.getArray(), constants); } else if (expr instanceof org.eclipse.jdt.core.dom.ArrayCreation ac && ac.getInitializer() != null) { for (Object expObj : ac.getInitializer().expressions()) { - extractConstantsFromExpression((org.eclipse.jdt.core.dom.Expression) expObj, constants); + extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) expObj, constants); } } else if (expr instanceof org.eclipse.jdt.core.dom.ArrayInitializer ai) { for (Object expObj : ai.expressions()) { - extractConstantsFromExpression((org.eclipse.jdt.core.dom.Expression) expObj, constants); + extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) expObj, constants); } } else if (expr instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) { for (Object argObj : cic.arguments()) { - extractConstantsFromExpression((org.eclipse.jdt.core.dom.Expression) argObj, constants); + extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) argObj, constants); } } else if (expr instanceof org.eclipse.jdt.core.dom.MethodInvocation mi) { String methodName = mi.getName().getIdentifier(); @@ -826,7 +826,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { } } else { for (Object argObj : cic.arguments()) { - extractConstantsFromExpression((org.eclipse.jdt.core.dom.Expression) argObj, constants); + extractConstantsFromArgument((org.eclipse.jdt.core.dom.Expression) argObj, constants); } } } @@ -852,6 +852,17 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { } } + private void extractConstantsFromArgument(org.eclipse.jdt.core.dom.Expression argObj, List constants) { + if (argObj instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation innerCic) { + String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(innerCic.getType()); + if ("String".equals(typeName)) { + extractConstantsFromExpression(argObj, constants); + } + } else { + extractConstantsFromExpression(argObj, constants); + } + } + protected org.eclipse.jdt.core.dom.Block findEnclosingBlock(org.eclipse.jdt.core.dom.ASTNode node) { org.eclipse.jdt.core.dom.ASTNode parent = node.getParent(); while (parent != null && !(parent instanceof org.eclipse.jdt.core.dom.Block)) { diff --git a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java index 2f96dee..cf46251 100644 --- a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java +++ b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/HeuristicCallGraphEngineTest.java @@ -2610,4 +2610,98 @@ class HeuristicCallGraphEngineTest { org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()) .containsExactly("CORRECT_EVENT"); // If heuristic rips both, it will fail because it contains WRONG_EVENT too! } + + @Test + void shouldNotExtractConstantsFromNestedClassInstanceCreations() throws IOException { + String source = """ + package com.example; + public class OrderController { + private OrderService service; + public void handleNestedConstructors() { + // The event contains a nested constructor. The heuristic should NOT extract WRONG_EVENT. + OuterEvent e = new OuterEvent("CORRECT_EVENT", new InnerPayload("WRONG_EVENT")); + service.process(e.getEventType()); + } + } + + class OrderService { + public void process(String event) {} + } + + class OuterEvent { + private String eventType; + private InnerPayload payload; + public OuterEvent(String eventType, InnerPayload payload) { + this.eventType = eventType; + this.payload = payload; + } + public String getEventType() { return eventType; } + } + + class InnerPayload { + private String someData; + public InnerPayload(String someData) { this.someData = someData; } + } + """; + Path tempDir = Files.createTempDirectory("callgraph_nested_constructor"); + Files.writeString(tempDir.resolve("OrderConfig.java"), source); + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context); + + EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleNestedConstructors").build(); + TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build(); + List chains = builder.findChains(List.of(entryPoint), List.of(trigger)); + + org.assertj.core.api.Assertions.assertThat(chains).hasSize(1); + org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()) + .containsExactly("CORRECT_EVENT"); + } + + @Test + void shouldNotExtractConstantsFromNestedArraysOfObjects() throws IOException { + String source = """ + package com.example; + public class OrderController { + private OrderService service; + public void handleNestedArrays() { + // The event contains a nested array of objects. The heuristic should NOT extract WRONG_EVENT. + OuterEvent e = new OuterEvent("CORRECT_EVENT", new InnerPayload[] { new InnerPayload("WRONG_EVENT") }); + service.process(e.getEventType()); + } + } + + class OrderService { + public void process(String event) {} + } + + class OuterEvent { + private String eventType; + private InnerPayload[] payloads; + public OuterEvent(String eventType, InnerPayload[] payloads) { + this.eventType = eventType; + this.payloads = payloads; + } + public String getEventType() { return eventType; } + } + + class InnerPayload { + private String someData; + public InnerPayload(String someData) { this.someData = someData; } + } + """; + Path tempDir = Files.createTempDirectory("callgraph_nested_array"); + Files.writeString(tempDir.resolve("OrderConfig.java"), source); + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context); + + EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleNestedArrays").build(); + TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build(); + List chains = builder.findChains(List.of(entryPoint), List.of(trigger)); + + org.assertj.core.api.Assertions.assertThat(chains).hasSize(1); + org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()) + .containsExactly("CORRECT_EVENT"); + } } \ No newline at end of file