From 9bea5c46874aa3587688741a0264c7b2c1da865d Mon Sep 17 00:00:00 2001 From: Kamil Patryk Kozakowski Date: Mon, 6 Jul 2026 18:25:48 +0200 Subject: [PATCH] 7 --- .../HeuristicEventMatchingEngine.java | 11 +++++ .../service/AbstractCallGraphEngine.java | 40 +++++++++---------- .../analysis/service/CallGraphPathFinder.java | 24 ++++++----- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/enricher/matching/HeuristicEventMatchingEngine.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/enricher/matching/HeuristicEventMatchingEngine.java index 2c88a47..4627560 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/enricher/matching/HeuristicEventMatchingEngine.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/enricher/matching/HeuristicEventMatchingEngine.java @@ -6,11 +6,22 @@ import java.util.List; public class HeuristicEventMatchingEngine implements EventMatchingEngine { + private final java.util.Map, Boolean> matchesCache = new java.util.HashMap<>(); + @Override public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) { if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) { return false; } + java.util.Map.Entry cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(stateMachineEvent, triggerPoint); + if (matchesCache.containsKey(cacheKey)) return matchesCache.get(cacheKey); + + boolean result = calculateMatches(stateMachineEvent, triggerPoint); + matchesCache.put(cacheKey, result); + return result; + } + + private boolean calculateMatches(Event stateMachineEvent, TriggerPoint triggerPoint) { String rawTriggerEvent = triggerPoint.getEvent(); String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName(); 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 0ca622d..9694dac 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 @@ -21,6 +21,21 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { protected final VariableTracer variableTracer; protected final ConstantExtractor constantExtractor; protected final ConstructorAnalyzer constructorAnalyzer; + private final Map parsedNodeCache = new HashMap<>(); + + private ASTNode parseExpressionString(String expr) { + if (expr == null) return null; + return parsedNodeCache.computeIfAbsent(expr, e -> { + ASTParser parser = ASTParser.newParser(AST.JLS17); + parser.setKind(ASTParser.K_EXPRESSION); + parser.setSource(e.toCharArray()); + try { + return parser.createAST(null); + } catch (Exception ex) { + return null; + } + }); + } @FunctionalInterface protected interface RhsResolver { @@ -348,10 +363,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { .build(); } - ASTParser exprParser = ASTParser.newParser(AST.JLS17); - exprParser.setKind(ASTParser.K_EXPRESSION); - exprParser.setSource(resolvedValue.toCharArray()); - ASTNode exprNode = exprParser.createAST(null); + ASTNode exprNode = parseExpressionString(resolvedValue); String varName = null; String methodName = null; @@ -552,10 +564,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { if (extractedFinalTraced[0] != null) { resolvedValue = extractedFinalTraced[0] + extractedFinalTraced[1]; if (extractedFinalTraced[0].contains("new ") && extractedFinalTraced[0].contains("{")) { - ASTParser p = ASTParser.newParser(AST.JLS17); - p.setKind(ASTParser.K_EXPRESSION); - p.setSource(resolvedValue.toCharArray()); - ASTNode newNode = p.createAST(null); + ASTNode newNode = parseExpressionString(resolvedValue); if (newNode instanceof Expression) { List traced = variableTracer.traceVariableAll((Expression) newNode); for (Expression ex : traced) { @@ -581,10 +590,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { if (polymorphicEvents.isEmpty() && variableTracer != null) { String initializer = variableTracer.traceLocalVariable(methodFqn, varName); if (initializer != null && !initializer.equals(varName)) { - ASTParser mapParser = ASTParser.newParser(AST.JLS17); - mapParser.setKind(ASTParser.K_EXPRESSION); - mapParser.setSource(initializer.toCharArray()); - ASTNode mapNode = mapParser.createAST(null); + ASTNode mapNode = parseExpressionString(initializer); if (mapNode instanceof Expression) { List mapTraced = variableTracer.traceVariableAll((Expression) mapNode); for (Expression t : mapTraced) { @@ -935,10 +941,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { } protected List evaluateGetterOnReceiver(String resolvedValue, String methodSuffix, String entryMethod, List path, Map> callGraph) { - ASTParser exprParser = ASTParser.newParser(AST.JLS17); - exprParser.setKind(ASTParser.K_EXPRESSION); - exprParser.setSource(resolvedValue.toCharArray()); - ASTNode exprNode = exprParser.createAST(null); + ASTNode exprNode = parseExpressionString(resolvedValue); if (exprNode instanceof SuperMethodInvocation smi) { String getterName = smi.getName().getIdentifier(); @@ -1499,10 +1502,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { if (arg == null) return null; String current = arg; while (current.contains("(") && !current.startsWith("new ")) { - org.eclipse.jdt.core.dom.ASTParser argParser = org.eclipse.jdt.core.dom.ASTParser.newParser(org.eclipse.jdt.core.dom.AST.JLS17); - argParser.setKind(org.eclipse.jdt.core.dom.ASTParser.K_EXPRESSION); - argParser.setSource(current.toCharArray()); - org.eclipse.jdt.core.dom.ASTNode argNode = argParser.createAST(null); + org.eclipse.jdt.core.dom.ASTNode argNode = parseExpressionString(current); if (argNode instanceof org.eclipse.jdt.core.dom.MethodInvocation miArg && !miArg.arguments().isEmpty()) { boolean shouldUnpack = false; org.eclipse.jdt.core.dom.Expression recv = miArg.getExpression(); diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java index 29d34d4..288969e 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/CallGraphPathFinder.java @@ -11,6 +11,9 @@ public class CallGraphPathFinder { private final CodebaseContext context; private final Map, Boolean> heuristicCache = new HashMap<>(); + private final Map, List>> memoCache = new HashMap<>(); + private final Map, Boolean> unreachableCache = new HashMap<>(); + public CallGraphPathFinder(CodebaseContext context) { this.context = context; } @@ -50,16 +53,17 @@ public class CallGraphPathFinder { } public List> findAllPaths(String start, String target, Map> graph, Set visited) { - return findAllPaths(start, target, graph, visited, new HashSet<>(), new boolean[]{false}, new HashMap<>()); + return findAllPaths(start, target, graph, visited, new boolean[]{false}); } - public List> findAllPaths(String start, String target, Map> graph, Set visited, Set unreachable, boolean[] parentHitCycle, Map>> memo) { + public List> findAllPaths(String start, String target, Map> graph, Set visited, boolean[] parentHitCycle) { List> result = new ArrayList<>(); - if (unreachable.contains(start)) { + java.util.Map.Entry cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(start, target); + if (unreachableCache.containsKey(cacheKey)) { return result; } - if (memo.containsKey(start)) { - List> cached = memo.get(start); + if (memoCache.containsKey(cacheKey)) { + List> cached = memoCache.get(cacheKey); for (List path : cached) { boolean hasCycle = false; for (String node : path) { @@ -95,7 +99,7 @@ public class CallGraphPathFinder { List path = new ArrayList<>(List.of(start, target)); result.add(path); } else { - List> subPaths = findAllPaths(neighbor, target, graph, visited, unreachable, localHitCycle, memo); + List> subPaths = findAllPaths(neighbor, target, graph, visited, localHitCycle); for (List subPath : subPaths) { List path = new ArrayList<>(); path.add(start); @@ -119,7 +123,7 @@ public class CallGraphPathFinder { if (superclass != null) { String superMethod = superclass + "." + methodName; if (graph.containsKey(superMethod)) { - List> superPaths = findAllPaths(superMethod, target, graph, visited, unreachable, localHitCycle, memo); + List> superPaths = findAllPaths(superMethod, target, graph, visited, localHitCycle); for (List superPath : superPaths) { List path = new ArrayList<>(); path.add(start); @@ -143,7 +147,7 @@ public class CallGraphPathFinder { for (String impl : impls) { String implMethod = impl + "." + methodName; if (graph.containsKey(implMethod)) { - List> implPaths = findAllPaths(implMethod, target, graph, visited, unreachable, localHitCycle, memo); + List> implPaths = findAllPaths(implMethod, target, graph, visited, localHitCycle); for (List implPath : implPaths) { List path = new ArrayList<>(); path.add(start); @@ -159,13 +163,13 @@ public class CallGraphPathFinder { if (localHitCycle[0]) { parentHitCycle[0] = true; } else if (result.isEmpty()) { - unreachable.add(start); + unreachableCache.put(cacheKey, true); } else { List> toCache = new ArrayList<>(); for (List path : result) { toCache.add(new ArrayList<>(path)); } - memo.put(start, toCache); + memoCache.put(cacheKey, toCache); } return result;