event maching heuristic update 4

This commit is contained in:
2026-06-21 15:36:36 +02:00
parent 2720296d14
commit 23d79d1930
7 changed files with 603 additions and 68 deletions

View File

@@ -17,6 +17,19 @@ public class ConstantResolver {
private String resolveInternal(Expression expr, CodebaseContext context, Set<String> visited) {
if (expr == null) return null;
// Unwrap common wrappers
if (expr instanceof CastExpression ce) {
return resolveInternal(ce.getExpression(), context, visited);
}
if (expr instanceof ParenthesizedExpression pe) {
return resolveInternal(pe.getExpression(), context, visited);
}
if (expr instanceof MethodInvocation mi && (mi.getName().getIdentifier().equals("requireNonNull") || mi.getName().getIdentifier().equals("notNull"))) {
if (!mi.arguments().isEmpty()) {
return resolveInternal((Expression) mi.arguments().get(0), context, visited);
}
}
// 1. Literal?
if (expr instanceof StringLiteral sl) {
return sl.getLiteralValue();

View File

@@ -126,7 +126,10 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
org.eclipse.jdt.core.dom.Expression localSetterExpr = traceLocalSetter(entryMethod, currentParamName, localMethodName);
if (localSetterExpr != null) {
List<String> setterEvents = new ArrayList<>();
extractConstantsFromExpression(localSetterExpr, setterEvents);
List<org.eclipse.jdt.core.dom.Expression> tracedSetters = traceVariableAll(localSetterExpr);
for (org.eclipse.jdt.core.dom.Expression tracedSetter : tracedSetters) {
extractConstantsFromExpression(tracedSetter, setterEvents);
}
if (!setterEvents.isEmpty()) {
return TriggerPoint.builder()
.event(tp.getEvent())
@@ -240,7 +243,10 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
org.eclipse.jdt.core.dom.Expression localSetterExpr = traceLocalSetter(entryMethod, varName, methodName);
System.out.println("localSetterExpr = " + (localSetterExpr != null ? localSetterExpr.toString() : "null"));
if (localSetterExpr != null) {
extractConstantsFromExpression(localSetterExpr, polymorphicEvents);
List<org.eclipse.jdt.core.dom.Expression> tracedSetters = traceVariableAll(localSetterExpr);
for (org.eclipse.jdt.core.dom.Expression tracedSetter : tracedSetters) {
extractConstantsFromExpression(tracedSetter, polymorphicEvents);
}
System.out.println("Extracted from setter: " + polymorphicEvents);
if (!polymorphicEvents.isEmpty()) {
return TriggerPoint.builder()
@@ -496,30 +502,33 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
}
}
if (!handled) {
extractConstantsFromExpression(retExpr, constants);
String val = constantResolver.resolve(retExpr, context);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
for (String eVal : val.substring(9).split(",")) {
String parsed = parseEnumSetElement(eVal);
if (!constants.contains(parsed)) constants.add(parsed);
List<Expression> tracedRetExprs = traceVariableAll(retExpr);
for (Expression tracedRetExpr : tracedRetExprs) {
extractConstantsFromExpression(tracedRetExpr, constants);
String val = constantResolver.resolve(tracedRetExpr, context);
if (val != null) {
if (val.startsWith("ENUM_SET:")) {
for (String eVal : val.substring(9).split(",")) {
String parsed = parseEnumSetElement(eVal);
if (!constants.contains(parsed)) constants.add(parsed);
}
} else {
if (!constants.contains(val)) constants.add(val);
}
} else if (tracedRetExpr instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
List<String> consts = traceFieldInConstructors(td, sn.getIdentifier(), context, visited);
if (!consts.isEmpty()) {
constants.addAll(consts);
} else {
constants.add(sn.toString());
}
} else if (tracedRetExpr instanceof org.eclipse.jdt.core.dom.FieldAccess fa) {
List<String> consts = traceFieldInConstructors(td, fa.getName().getIdentifier(), context, visited);
if (!consts.isEmpty()) {
constants.addAll(consts);
} else {
constants.add(fa.getName().getIdentifier());
}
} else {
if (!constants.contains(val)) constants.add(val);
}
} else if (retExpr instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
List<String> consts = traceFieldInConstructors(td, sn.getIdentifier(), context, visited);
if (!consts.isEmpty()) {
constants.addAll(consts);
} else {
constants.add(sn.toString());
}
} else if (retExpr instanceof org.eclipse.jdt.core.dom.FieldAccess fa) {
List<String> consts = traceFieldInConstructors(td, fa.getName().getIdentifier(), context, visited);
if (!consts.isEmpty()) {
constants.addAll(consts);
} else {
constants.add(fa.getName().getIdentifier());
}
}
}
@@ -542,9 +551,11 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
for (MethodDeclaration md : td.getMethods()) {
if (md.getBody() != null) {
md.getBody().accept(new ASTVisitor() {
@Override
public boolean visit(ReturnStatement node) {
extractConstantsFromExpression(node.getExpression(), resolvedConstants);
List<org.eclipse.jdt.core.dom.Expression> tracedReturns = traceVariableAll(node.getExpression());
for (org.eclipse.jdt.core.dom.Expression tracedRet : tracedReturns) {
extractConstantsFromExpression(tracedRet, resolvedConstants);
}
return super.visit(node);
}
});
@@ -567,6 +578,8 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
extractConstantsFromExpression(pe.getExpression(), constants);
} else if (expr instanceof org.eclipse.jdt.core.dom.CastExpression ce) {
extractConstantsFromExpression(ce.getExpression(), constants);
} else if (expr instanceof org.eclipse.jdt.core.dom.Assignment assignment) {
extractConstantsFromExpression(assignment.getRightHandSide(), constants);
} else if (expr instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
se.accept(new org.eclipse.jdt.core.dom.ASTVisitor() {
@Override
@@ -1135,33 +1148,43 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
return (TypeDeclaration) parent;
}
private Expression traceVariable(Expression expr) {
List<Expression> all = traceVariableAll(expr);
return all.isEmpty() ? expr : all.get(all.size() - 1);
}
private List<Expression> traceVariableAll(Expression expr) {
List<Expression> results = new ArrayList<>();
System.out.println("traceVariableAll called with: " + expr);
if (expr instanceof SimpleName sn) {
String varName = sn.getIdentifier();
MethodDeclaration enclosingMethod = findEnclosingMethod(expr);
System.out.println("traceVariableAll SimpleName=" + varName + " enclosingMethod=" + (enclosingMethod != null ? enclosingMethod.getName() : "null"));
if (enclosingMethod != null) {
final Expression[] initializer = new Expression[1];
enclosingMethod.accept(new ASTVisitor() {
@Override
public boolean visit(VariableDeclarationFragment node) {
if (node.getName().getIdentifier().equals(varName) && node.getInitializer() != null) {
initializer[0] = node.getInitializer();
System.out.println("traceVariableAll found init: " + node.getInitializer());
results.addAll(traceVariableAll(node.getInitializer()));
}
return super.visit(node);
}
@Override
public boolean visit(Assignment node) {
if (node.getLeftHandSide() instanceof SimpleName asn && asn.getIdentifier().equals(varName)) {
initializer[0] = node.getRightHandSide();
System.out.println("traceVariableAll found assign: " + node.getRightHandSide());
results.addAll(traceVariableAll(node.getRightHandSide()));
}
return super.visit(node);
}
});
if (initializer[0] != null) {
return traceVariable(initializer[0]);
if (!results.isEmpty()) {
return results;
}
}
}
return expr;
results.add(expr);
return results;
}
private List<String> traceFieldInConstructors(TypeDeclaration td, String fieldName, CodebaseContext context, Set<String> visited) {
@@ -1173,14 +1196,17 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
org.eclipse.jdt.core.dom.VariableDeclarationFragment frag = (org.eclipse.jdt.core.dom.VariableDeclarationFragment) fragObj;
if (frag.getName().getIdentifier().equals(fieldName) && frag.getInitializer() != null) {
Expression right = frag.getInitializer();
if (right instanceof MethodInvocation mi) {
String calledName = mi.getName().getIdentifier();
String targetClass = context.getFqn(td);
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, new HashSet<>(visited), cu));
} else {
String val = constantResolver.resolve(right, context);
if (val != null) results.add(val);
List<Expression> tracedRights = traceVariableAll(right);
for (Expression tracedRight : tracedRights) {
if (tracedRight instanceof MethodInvocation mi) {
String calledName = mi.getName().getIdentifier();
String targetClass = context.getFqn(td);
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, new HashSet<>(visited), cu));
} else {
String val = constantResolver.resolve(tracedRight, context);
if (val != null) results.add(val);
}
}
}
}
@@ -1194,26 +1220,29 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
(left instanceof FieldAccess && ((FieldAccess) left).getName().getIdentifier().equals(fieldName))) {
Expression right = node.getRightHandSide();
if (right instanceof MethodInvocation mi) {
String calledName = mi.getName().getIdentifier();
String targetClass = context.getFqn(td);
if (mi.getExpression() != null && mi.resolveMethodBinding() != null && mi.resolveMethodBinding().getDeclaringClass() != null) {
targetClass = mi.resolveMethodBinding().getDeclaringClass().getQualifiedName();
} else if (mi.getExpression() instanceof SimpleName) {
String exprName = ((SimpleName) mi.getExpression()).getIdentifier();
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
TypeDeclaration targetTd = context.resolveStaticImport(exprName, cu);
if (targetTd != null) {
targetClass = context.getFqn(targetTd);
List<Expression> tracedRights = traceVariableAll(right);
for (Expression tracedRight : tracedRights) {
if (tracedRight instanceof MethodInvocation mi) {
String calledName = mi.getName().getIdentifier();
String targetClass = context.getFqn(td);
if (mi.getExpression() != null && mi.resolveMethodBinding() != null && mi.resolveMethodBinding().getDeclaringClass() != null) {
targetClass = mi.resolveMethodBinding().getDeclaringClass().getQualifiedName();
} else if (mi.getExpression() instanceof SimpleName) {
String exprName = ((SimpleName) mi.getExpression()).getIdentifier();
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
TypeDeclaration targetTd = context.resolveStaticImport(exprName, cu);
if (targetTd != null) {
targetClass = context.getFqn(targetTd);
}
}
}
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, new HashSet<>(visited), cu));
} else {
String val = constantResolver.resolve(right, context);
if (val != null) results.add(val);
CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null;
results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, new HashSet<>(visited), cu));
} else {
String val = constantResolver.resolve(tracedRight, context);
if (val != null) results.add(val);
}
}
}
return super.visit(node);
@@ -1303,10 +1332,11 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
public boolean visit(Assignment asn) {
Expression left = asn.getLeftHandSide();
if ((left instanceof SimpleName sn && sn.getIdentifier().equals(fieldName)) ||
(left instanceof FieldAccess fa && fa.getName().getIdentifier().equals(fieldName))) {
(left instanceof FieldAccess fa && fa.getName().getIdentifier().equals(fieldName)) ||
(left instanceof SuperFieldAccess sfa && sfa.getName().getIdentifier().equals(fieldName))) {
Expression right = asn.getRightHandSide();
if (right instanceof SimpleName snRight) {
String rightName = snRight.getIdentifier();
String rightName = extractVariableName(right);
if (rightName != null) {
for (int i = 0; i < constructorMd.parameters().size(); i++) {
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
if (svd.getName().getIdentifier().equals(rightName)) {
@@ -1319,15 +1349,22 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
}
@Override
public boolean visit(ConstructorInvocation node) {
org.eclipse.jdt.core.dom.IMethodBinding resolvedBinding = node.resolveConstructorBinding();
TypeDeclaration enclosingTd = findEnclosingType(node);
if (enclosingTd != null) {
for (MethodDeclaration otherMd : enclosingTd.getMethods()) {
if (otherMd.isConstructor() && otherMd != constructorMd && otherMd.parameters().size() == node.arguments().size()) {
boolean matches = false;
if (resolvedBinding != null && otherMd.resolveBinding() != null) {
matches = resolvedBinding.isEqualTo(otherMd.resolveBinding()) || resolvedBinding.getKey().equals(otherMd.resolveBinding().getKey());
} else {
matches = otherMd.isConstructor() && otherMd != constructorMd && otherMd.parameters().size() == node.arguments().size();
}
if (matches) {
int targetIdx = findAssignedParameterIndex(otherMd, fieldName, context, visited);
if (targetIdx >= 0 && targetIdx < node.arguments().size()) {
Expression arg = (Expression) node.arguments().get(targetIdx);
if (arg instanceof SimpleName snArg) {
String argName = snArg.getIdentifier();
String argName = extractVariableName(arg);
if (argName != null) {
for (int i = 0; i < constructorMd.parameters().size(); i++) {
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
if (svd.getName().getIdentifier().equals(argName)) {
@@ -1343,6 +1380,7 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
}
@Override
public boolean visit(SuperConstructorInvocation node) {
org.eclipse.jdt.core.dom.IMethodBinding resolvedBinding = node.resolveConstructorBinding();
TypeDeclaration enclosingTd = findEnclosingType(node);
if (enclosingTd != null) {
String superFqn = context.getSuperclassFqn(enclosingTd);
@@ -1350,12 +1388,18 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
if (superTd != null) {
for (MethodDeclaration otherMd : superTd.getMethods()) {
if (otherMd.isConstructor() && otherMd.parameters().size() == node.arguments().size()) {
boolean matches = false;
if (resolvedBinding != null && otherMd.resolveBinding() != null) {
matches = resolvedBinding.isEqualTo(otherMd.resolveBinding()) || resolvedBinding.getKey().equals(otherMd.resolveBinding().getKey());
} else {
matches = otherMd.isConstructor() && otherMd.parameters().size() == node.arguments().size();
}
if (matches) {
int targetIdx = findAssignedParameterIndex(otherMd, fieldName, context, visited);
if (targetIdx >= 0 && targetIdx < node.arguments().size()) {
Expression arg = (Expression) node.arguments().get(targetIdx);
if (arg instanceof SimpleName snArg) {
String argName = snArg.getIdentifier();
String argName = extractVariableName(arg);
if (argName != null) {
for (int i = 0; i < constructorMd.parameters().size(); i++) {
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
if (svd.getName().getIdentifier().equals(argName)) {
@@ -1374,4 +1418,16 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
});
return foundIdx[0];
}
private String extractVariableName(Expression expr) {
if (expr instanceof SimpleName sn) return sn.getIdentifier();
if (expr instanceof FieldAccess fa) return fa.getName().getIdentifier();
if (expr instanceof SuperFieldAccess sfa) return sfa.getName().getIdentifier();
if (expr instanceof CastExpression ce) return extractVariableName(ce.getExpression());
if (expr instanceof ParenthesizedExpression pe) return extractVariableName(pe.getExpression());
if (expr instanceof MethodInvocation mi && (mi.getName().getIdentifier().equals("requireNonNull") || mi.getName().getIdentifier().equals("notNull"))) {
if (!mi.arguments().isEmpty()) return extractVariableName((Expression)mi.arguments().get(0));
}
return null;
}
}