7
This commit is contained in:
@@ -6,11 +6,22 @@ import java.util.List;
|
|||||||
|
|
||||||
public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
||||||
|
|
||||||
|
private final java.util.Map<java.util.Map.Entry<Event, TriggerPoint>, Boolean> matchesCache = new java.util.HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) {
|
public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) {
|
||||||
if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) {
|
if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
java.util.Map.Entry<Event, TriggerPoint> 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 rawTriggerEvent = triggerPoint.getEvent();
|
||||||
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
||||||
|
|||||||
@@ -21,6 +21,21 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
protected final VariableTracer variableTracer;
|
protected final VariableTracer variableTracer;
|
||||||
protected final ConstantExtractor constantExtractor;
|
protected final ConstantExtractor constantExtractor;
|
||||||
protected final ConstructorAnalyzer constructorAnalyzer;
|
protected final ConstructorAnalyzer constructorAnalyzer;
|
||||||
|
private final Map<String, ASTNode> 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
|
@FunctionalInterface
|
||||||
protected interface RhsResolver {
|
protected interface RhsResolver {
|
||||||
@@ -348,10 +363,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTParser exprParser = ASTParser.newParser(AST.JLS17);
|
ASTNode exprNode = parseExpressionString(resolvedValue);
|
||||||
exprParser.setKind(ASTParser.K_EXPRESSION);
|
|
||||||
exprParser.setSource(resolvedValue.toCharArray());
|
|
||||||
ASTNode exprNode = exprParser.createAST(null);
|
|
||||||
|
|
||||||
String varName = null;
|
String varName = null;
|
||||||
String methodName = null;
|
String methodName = null;
|
||||||
@@ -552,10 +564,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
if (extractedFinalTraced[0] != null) {
|
if (extractedFinalTraced[0] != null) {
|
||||||
resolvedValue = extractedFinalTraced[0] + extractedFinalTraced[1];
|
resolvedValue = extractedFinalTraced[0] + extractedFinalTraced[1];
|
||||||
if (extractedFinalTraced[0].contains("new ") && extractedFinalTraced[0].contains("{")) {
|
if (extractedFinalTraced[0].contains("new ") && extractedFinalTraced[0].contains("{")) {
|
||||||
ASTParser p = ASTParser.newParser(AST.JLS17);
|
ASTNode newNode = parseExpressionString(resolvedValue);
|
||||||
p.setKind(ASTParser.K_EXPRESSION);
|
|
||||||
p.setSource(resolvedValue.toCharArray());
|
|
||||||
ASTNode newNode = p.createAST(null);
|
|
||||||
if (newNode instanceof Expression) {
|
if (newNode instanceof Expression) {
|
||||||
List<Expression> traced = variableTracer.traceVariableAll((Expression) newNode);
|
List<Expression> traced = variableTracer.traceVariableAll((Expression) newNode);
|
||||||
for (Expression ex : traced) {
|
for (Expression ex : traced) {
|
||||||
@@ -581,10 +590,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
if (polymorphicEvents.isEmpty() && variableTracer != null) {
|
if (polymorphicEvents.isEmpty() && variableTracer != null) {
|
||||||
String initializer = variableTracer.traceLocalVariable(methodFqn, varName);
|
String initializer = variableTracer.traceLocalVariable(methodFqn, varName);
|
||||||
if (initializer != null && !initializer.equals(varName)) {
|
if (initializer != null && !initializer.equals(varName)) {
|
||||||
ASTParser mapParser = ASTParser.newParser(AST.JLS17);
|
ASTNode mapNode = parseExpressionString(initializer);
|
||||||
mapParser.setKind(ASTParser.K_EXPRESSION);
|
|
||||||
mapParser.setSource(initializer.toCharArray());
|
|
||||||
ASTNode mapNode = mapParser.createAST(null);
|
|
||||||
if (mapNode instanceof Expression) {
|
if (mapNode instanceof Expression) {
|
||||||
List<Expression> mapTraced = variableTracer.traceVariableAll((Expression) mapNode);
|
List<Expression> mapTraced = variableTracer.traceVariableAll((Expression) mapNode);
|
||||||
for (Expression t : mapTraced) {
|
for (Expression t : mapTraced) {
|
||||||
@@ -935,10 +941,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> evaluateGetterOnReceiver(String resolvedValue, String methodSuffix, String entryMethod, List<String> path, Map<String, List<CallEdge>> callGraph) {
|
protected List<String> evaluateGetterOnReceiver(String resolvedValue, String methodSuffix, String entryMethod, List<String> path, Map<String, List<CallEdge>> callGraph) {
|
||||||
ASTParser exprParser = ASTParser.newParser(AST.JLS17);
|
ASTNode exprNode = parseExpressionString(resolvedValue);
|
||||||
exprParser.setKind(ASTParser.K_EXPRESSION);
|
|
||||||
exprParser.setSource(resolvedValue.toCharArray());
|
|
||||||
ASTNode exprNode = exprParser.createAST(null);
|
|
||||||
|
|
||||||
if (exprNode instanceof SuperMethodInvocation smi) {
|
if (exprNode instanceof SuperMethodInvocation smi) {
|
||||||
String getterName = smi.getName().getIdentifier();
|
String getterName = smi.getName().getIdentifier();
|
||||||
@@ -1499,10 +1502,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
if (arg == null) return null;
|
if (arg == null) return null;
|
||||||
String current = arg;
|
String current = arg;
|
||||||
while (current.contains("(") && !current.startsWith("new ")) {
|
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);
|
org.eclipse.jdt.core.dom.ASTNode argNode = parseExpressionString(current);
|
||||||
argParser.setKind(org.eclipse.jdt.core.dom.ASTParser.K_EXPRESSION);
|
|
||||||
argParser.setSource(current.toCharArray());
|
|
||||||
org.eclipse.jdt.core.dom.ASTNode argNode = argParser.createAST(null);
|
|
||||||
if (argNode instanceof org.eclipse.jdt.core.dom.MethodInvocation miArg && !miArg.arguments().isEmpty()) {
|
if (argNode instanceof org.eclipse.jdt.core.dom.MethodInvocation miArg && !miArg.arguments().isEmpty()) {
|
||||||
boolean shouldUnpack = false;
|
boolean shouldUnpack = false;
|
||||||
org.eclipse.jdt.core.dom.Expression recv = miArg.getExpression();
|
org.eclipse.jdt.core.dom.Expression recv = miArg.getExpression();
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ public class CallGraphPathFinder {
|
|||||||
private final CodebaseContext context;
|
private final CodebaseContext context;
|
||||||
private final Map<java.util.Map.Entry<String, String>, Boolean> heuristicCache = new HashMap<>();
|
private final Map<java.util.Map.Entry<String, String>, Boolean> heuristicCache = new HashMap<>();
|
||||||
|
|
||||||
|
private final Map<java.util.Map.Entry<String, String>, List<List<String>>> memoCache = new HashMap<>();
|
||||||
|
private final Map<java.util.Map.Entry<String, String>, Boolean> unreachableCache = new HashMap<>();
|
||||||
|
|
||||||
public CallGraphPathFinder(CodebaseContext context) {
|
public CallGraphPathFinder(CodebaseContext context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
@@ -50,16 +53,17 @@ public class CallGraphPathFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<List<String>> findAllPaths(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited) {
|
public List<List<String>> findAllPaths(String start, String target, Map<String, List<CallEdge>> graph, Set<String> 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<List<String>> findAllPaths(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited, Set<String> unreachable, boolean[] parentHitCycle, Map<String, List<List<String>>> memo) {
|
public List<List<String>> findAllPaths(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited, boolean[] parentHitCycle) {
|
||||||
List<List<String>> result = new ArrayList<>();
|
List<List<String>> result = new ArrayList<>();
|
||||||
if (unreachable.contains(start)) {
|
java.util.Map.Entry<String, String> cacheKey = new java.util.AbstractMap.SimpleImmutableEntry<>(start, target);
|
||||||
|
if (unreachableCache.containsKey(cacheKey)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (memo.containsKey(start)) {
|
if (memoCache.containsKey(cacheKey)) {
|
||||||
List<List<String>> cached = memo.get(start);
|
List<List<String>> cached = memoCache.get(cacheKey);
|
||||||
for (List<String> path : cached) {
|
for (List<String> path : cached) {
|
||||||
boolean hasCycle = false;
|
boolean hasCycle = false;
|
||||||
for (String node : path) {
|
for (String node : path) {
|
||||||
@@ -95,7 +99,7 @@ public class CallGraphPathFinder {
|
|||||||
List<String> path = new ArrayList<>(List.of(start, target));
|
List<String> path = new ArrayList<>(List.of(start, target));
|
||||||
result.add(path);
|
result.add(path);
|
||||||
} else {
|
} else {
|
||||||
List<List<String>> subPaths = findAllPaths(neighbor, target, graph, visited, unreachable, localHitCycle, memo);
|
List<List<String>> subPaths = findAllPaths(neighbor, target, graph, visited, localHitCycle);
|
||||||
for (List<String> subPath : subPaths) {
|
for (List<String> subPath : subPaths) {
|
||||||
List<String> path = new ArrayList<>();
|
List<String> path = new ArrayList<>();
|
||||||
path.add(start);
|
path.add(start);
|
||||||
@@ -119,7 +123,7 @@ public class CallGraphPathFinder {
|
|||||||
if (superclass != null) {
|
if (superclass != null) {
|
||||||
String superMethod = superclass + "." + methodName;
|
String superMethod = superclass + "." + methodName;
|
||||||
if (graph.containsKey(superMethod)) {
|
if (graph.containsKey(superMethod)) {
|
||||||
List<List<String>> superPaths = findAllPaths(superMethod, target, graph, visited, unreachable, localHitCycle, memo);
|
List<List<String>> superPaths = findAllPaths(superMethod, target, graph, visited, localHitCycle);
|
||||||
for (List<String> superPath : superPaths) {
|
for (List<String> superPath : superPaths) {
|
||||||
List<String> path = new ArrayList<>();
|
List<String> path = new ArrayList<>();
|
||||||
path.add(start);
|
path.add(start);
|
||||||
@@ -143,7 +147,7 @@ public class CallGraphPathFinder {
|
|||||||
for (String impl : impls) {
|
for (String impl : impls) {
|
||||||
String implMethod = impl + "." + methodName;
|
String implMethod = impl + "." + methodName;
|
||||||
if (graph.containsKey(implMethod)) {
|
if (graph.containsKey(implMethod)) {
|
||||||
List<List<String>> implPaths = findAllPaths(implMethod, target, graph, visited, unreachable, localHitCycle, memo);
|
List<List<String>> implPaths = findAllPaths(implMethod, target, graph, visited, localHitCycle);
|
||||||
for (List<String> implPath : implPaths) {
|
for (List<String> implPath : implPaths) {
|
||||||
List<String> path = new ArrayList<>();
|
List<String> path = new ArrayList<>();
|
||||||
path.add(start);
|
path.add(start);
|
||||||
@@ -159,13 +163,13 @@ public class CallGraphPathFinder {
|
|||||||
if (localHitCycle[0]) {
|
if (localHitCycle[0]) {
|
||||||
parentHitCycle[0] = true;
|
parentHitCycle[0] = true;
|
||||||
} else if (result.isEmpty()) {
|
} else if (result.isEmpty()) {
|
||||||
unreachable.add(start);
|
unreachableCache.put(cacheKey, true);
|
||||||
} else {
|
} else {
|
||||||
List<List<String>> toCache = new ArrayList<>();
|
List<List<String>> toCache = new ArrayList<>();
|
||||||
for (List<String> path : result) {
|
for (List<String> path : result) {
|
||||||
toCache.add(new ArrayList<>(path));
|
toCache.add(new ArrayList<>(path));
|
||||||
}
|
}
|
||||||
memo.put(start, toCache);
|
memoCache.put(cacheKey, toCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Reference in New Issue
Block a user