Compare commits
1 Commits
fc267c43c6
...
29e391c472
| Author | SHA1 | Date | |
|---|---|---|---|
| 29e391c472 |
@@ -12,24 +12,16 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
String rawTriggerEvent = triggerPoint.getEvent();
|
||||
String triggerEvent = simplify(triggerPoint.getEvent());
|
||||
String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
|
||||
String smEvent = simplify(smEventRaw);
|
||||
|
||||
boolean isWildcard = isWildcardVariable(rawTriggerEvent);
|
||||
boolean isWildcard = isWildcardVariable(triggerEvent);
|
||||
|
||||
List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
|
||||
|
||||
boolean hasPolyMatch = false;
|
||||
for (String pe : polyEvents) {
|
||||
if (pe.contains(".") && smEventRaw.contains(".")) {
|
||||
if (smEventRaw.equals(pe) || smEventRaw.endsWith("." + pe)) {
|
||||
hasPolyMatch = true;
|
||||
break;
|
||||
}
|
||||
continue; // Stricter matching: do not fallback if both have qualifiers but don't match
|
||||
}
|
||||
|
||||
String simplePe = pe;
|
||||
if (pe.contains(".")) {
|
||||
simplePe = pe.substring(pe.lastIndexOf('.') + 1);
|
||||
@@ -43,18 +35,7 @@ public class HeuristicEventMatchingEngine implements EventMatchingEngine {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPolyMatch) return true;
|
||||
if (polyEvents.isEmpty() && isWildcard) return true;
|
||||
|
||||
if (rawTriggerEvent.contains(".") && smEventRaw.contains(".")) {
|
||||
if (smEventRaw.equals(rawTriggerEvent) || smEventRaw.endsWith("." + rawTriggerEvent)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String triggerEvent = simplify(rawTriggerEvent);
|
||||
return smEvent.equals(triggerEvent);
|
||||
return hasPolyMatch || smEvent.equals(triggerEvent) || (polyEvents.isEmpty() && isWildcard);
|
||||
}
|
||||
|
||||
private boolean isWildcardVariable(String eventStr) {
|
||||
|
||||
@@ -129,29 +129,6 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
String entryMethod = path.get(0);
|
||||
int entryParamIndex = getParameterIndex(entryMethod, currentParamName);
|
||||
if (entryParamIndex < 0) {
|
||||
// Intercept local setter before falling back to initializer
|
||||
if (methodSuffix.startsWith(".get") || methodSuffix.equals(".type") || methodSuffix.equals(".event") || methodSuffix.equals(".type()") || methodSuffix.equals(".event()")) {
|
||||
String localMethodName = methodSuffix.substring(1).replace("()", "");
|
||||
org.eclipse.jdt.core.dom.Expression localSetterExpr = traceLocalSetter(entryMethod, currentParamName, localMethodName);
|
||||
if (localSetterExpr != null) {
|
||||
List<String> setterEvents = new ArrayList<>();
|
||||
extractConstantsFromExpression(localSetterExpr, setterEvents);
|
||||
if (!setterEvents.isEmpty()) {
|
||||
return TriggerPoint.builder()
|
||||
.event(tp.getEvent())
|
||||
.className(tp.getClassName())
|
||||
.methodName(tp.getMethodName())
|
||||
.sourceFile(tp.getSourceFile())
|
||||
.sourceModule(tp.getSourceModule())
|
||||
.stateMachineId(tp.getStateMachineId())
|
||||
.sourceState(tp.getSourceState())
|
||||
.lineNumber(tp.getLineNumber())
|
||||
.polymorphicEvents(setterEvents)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String tracedVar = traceLocalVariable(entryMethod, currentParamName);
|
||||
if (tracedVar != null && !tracedVar.equals(currentParamName)) {
|
||||
int dotIdx = tracedVar.indexOf('.');
|
||||
@@ -171,7 +148,6 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
exprParser.setSource(resolvedValue.toCharArray());
|
||||
exprParser.setKind(org.eclipse.jdt.core.dom.ASTParser.K_EXPRESSION);
|
||||
org.eclipse.jdt.core.dom.ASTNode exprNode = exprParser.createAST(null);
|
||||
System.out.println("resolvedValue = " + resolvedValue + " exprNode=" + exprNode.getClass().getSimpleName());
|
||||
|
||||
String varName = null;
|
||||
String methodName = null;
|
||||
@@ -228,29 +204,6 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
}
|
||||
|
||||
if (methodName != null) {
|
||||
System.out.println("Checking local setter for: varName=" + varName + " methodName=" + methodName + " entryMethod=" + entryMethod);
|
||||
if (varName != null && !varName.isEmpty() && Character.isLowerCase(varName.charAt(0)) && !methodName.equals("VariableReference")) {
|
||||
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);
|
||||
System.out.println("Extracted from setter: " + polymorphicEvents);
|
||||
if (!polymorphicEvents.isEmpty()) {
|
||||
return TriggerPoint.builder()
|
||||
.event(tp.getEvent())
|
||||
.className(tp.getClassName())
|
||||
.methodName(tp.getMethodName())
|
||||
.sourceFile(tp.getSourceFile())
|
||||
.sourceModule(tp.getSourceModule())
|
||||
.stateMachineId(tp.getStateMachineId())
|
||||
.sourceState(tp.getSourceState())
|
||||
.lineNumber(tp.getLineNumber())
|
||||
.polymorphicEvents(polymorphicEvents)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (varName != null && declaredType == null) {
|
||||
for (String methodFqn : path) {
|
||||
declaredType = getVariableDeclaredType(methodFqn, varName);
|
||||
@@ -470,18 +423,20 @@ 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 = eVal.substring(eVal.lastIndexOf('.') + 1);
|
||||
if (!constants.contains(parsed)) constants.add(parsed);
|
||||
constants.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
||||
}
|
||||
} else {
|
||||
if (!constants.contains(val)) constants.add(val);
|
||||
constants.add(val);
|
||||
}
|
||||
} else if (retExpr instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
|
||||
} else if (retExpr instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) {
|
||||
constants.add(click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType()));
|
||||
} else if (retExpr instanceof QualifiedName qn) {
|
||||
constants.add(qn.toString());
|
||||
} else if (retExpr instanceof SimpleName sn) {
|
||||
List<String> consts = traceFieldInConstructors(td, sn.getIdentifier(), context, visited);
|
||||
if (!consts.isEmpty()) {
|
||||
constants.addAll(consts);
|
||||
@@ -511,7 +466,11 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
md.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(ReturnStatement node) {
|
||||
extractConstantsFromExpression(node.getExpression(), resolvedConstants);
|
||||
if (node.getExpression() instanceof QualifiedName qn) {
|
||||
resolvedConstants.add(qn.getName().getIdentifier());
|
||||
} else if (node.getExpression() instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
||||
resolvedConstants.add(sl.getLiteralValue());
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
@@ -521,100 +480,6 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void extractConstantsFromExpression(org.eclipse.jdt.core.dom.Expression expr, List<String> constants) {
|
||||
System.out.println("EXTRACT CONST: " + (expr != null ? expr.getClass().getSimpleName() + " " + expr.toString() : "null"));
|
||||
if (expr instanceof QualifiedName qn) {
|
||||
constants.add(qn.toString());
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
||||
constants.add(sl.getLiteralValue());
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.ConditionalExpression ce) {
|
||||
extractConstantsFromExpression(ce.getThenExpression(), constants);
|
||||
extractConstantsFromExpression(ce.getElseExpression(), constants);
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.ParenthesizedExpression pe) {
|
||||
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.SwitchExpression se) {
|
||||
se.accept(new org.eclipse.jdt.core.dom.ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.YieldStatement ys) {
|
||||
extractConstantsFromExpression(ys.getExpression(), constants);
|
||||
return super.visit(ys);
|
||||
}
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.ExpressionStatement es) {
|
||||
extractConstantsFromExpression(es.getExpression(), constants);
|
||||
return super.visit(es);
|
||||
}
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.ReturnStatement rs) {
|
||||
extractConstantsFromExpression(rs.getExpression(), constants);
|
||||
return super.visit(rs);
|
||||
}
|
||||
});
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.MethodInvocation mi) {
|
||||
String methodName = mi.getName().getIdentifier();
|
||||
|
||||
// 1. Local setter tracking
|
||||
if ((methodName.startsWith("get") || methodName.equals("type") || methodName.equals("event")) && mi.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
|
||||
String varName = sn.getIdentifier();
|
||||
String propName = methodName.startsWith("get") ? methodName.substring(3) : methodName;
|
||||
|
||||
org.eclipse.jdt.core.dom.Block block = findEnclosingBlock(mi);
|
||||
if (block != null) {
|
||||
for (Object stmtObj : block.statements()) {
|
||||
if (stmtObj == mi.getParent() || stmtObj == mi) break;
|
||||
if (stmtObj instanceof org.eclipse.jdt.core.dom.ExpressionStatement es) {
|
||||
if (es.getExpression() instanceof org.eclipse.jdt.core.dom.MethodInvocation setterMi) {
|
||||
if (setterMi.getName().getIdentifier().equalsIgnoreCase("set" + propName) || setterMi.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
if (setterMi.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName setterSn && setterSn.getIdentifier().equals(varName)) {
|
||||
if (!setterMi.arguments().isEmpty()) {
|
||||
extractConstantsFromExpression((org.eclipse.jdt.core.dom.Expression) setterMi.arguments().get(0), constants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (es.getExpression() instanceof org.eclipse.jdt.core.dom.Assignment assignment) {
|
||||
if (assignment.getLeftHandSide() instanceof org.eclipse.jdt.core.dom.FieldAccess fa) {
|
||||
if (fa.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName faSn && faSn.getIdentifier().equals(varName)) {
|
||||
if (fa.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
extractConstantsFromExpression(assignment.getRightHandSide(), constants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (assignment.getLeftHandSide() instanceof org.eclipse.jdt.core.dom.QualifiedName qqn) {
|
||||
if (qqn.getQualifier().getFullyQualifiedName().equals(varName)) {
|
||||
if (qqn.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
extractConstantsFromExpression(assignment.getRightHandSide(), constants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Delegate to method return analysis
|
||||
TypeDeclaration td = findEnclosingType(mi);
|
||||
System.out.println("DELEGATING MethodInvocation: " + methodName + " td=" + (td != null ? td.getName() : "null") + " fqn=" + (td != null ? context.getFqn(td) : "null"));
|
||||
if (td != null && (mi.getExpression() == null || mi.getExpression() instanceof org.eclipse.jdt.core.dom.ThisExpression)) {
|
||||
List<String> values = resolveMethodReturnConstant(context.getFqn(td), methodName, 0, new java.util.HashSet<>(), null);
|
||||
System.out.println("RESOLVED MethodInvocation values: " + values);
|
||||
if (values != null) constants.addAll(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private 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)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return (org.eclipse.jdt.core.dom.Block) parent;
|
||||
}
|
||||
|
||||
private String traceLocalVariable(String methodFqn, String varName) {
|
||||
if (methodFqn == null || !methodFqn.contains(".")) return null;
|
||||
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||
@@ -667,52 +532,6 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
||||
return null;
|
||||
}
|
||||
|
||||
private org.eclipse.jdt.core.dom.Expression traceLocalSetter(String methodFqn, String varName, String getterName) {
|
||||
if (methodFqn == null || !methodFqn.contains(".")) return null;
|
||||
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||
String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
|
||||
org.eclipse.jdt.core.dom.TypeDeclaration td = context.getTypeDeclaration(className);
|
||||
if (td != null) {
|
||||
org.eclipse.jdt.core.dom.MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
|
||||
if (md != null && md.getBody() != null) {
|
||||
final org.eclipse.jdt.core.dom.Expression[] setterArg = new org.eclipse.jdt.core.dom.Expression[1];
|
||||
String propName = getterName.startsWith("get") ? getterName.substring(3) : getterName;
|
||||
md.getBody().accept(new org.eclipse.jdt.core.dom.ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.MethodInvocation node) {
|
||||
if (node.getName().getIdentifier().equalsIgnoreCase("set" + propName) || node.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
if (node.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName sn && sn.getIdentifier().equals(varName)) {
|
||||
if (!node.arguments().isEmpty()) {
|
||||
setterArg[0] = (org.eclipse.jdt.core.dom.Expression) node.arguments().get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.Assignment node) {
|
||||
if (node.getLeftHandSide() instanceof org.eclipse.jdt.core.dom.FieldAccess fa) {
|
||||
if (fa.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName faSn && faSn.getIdentifier().equals(varName)) {
|
||||
if (fa.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
setterArg[0] = node.getRightHandSide();
|
||||
}
|
||||
}
|
||||
} else if (node.getLeftHandSide() instanceof org.eclipse.jdt.core.dom.QualifiedName qqn) {
|
||||
if (qqn.getQualifier().getFullyQualifiedName().equals(varName)) {
|
||||
if (qqn.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
setterArg[0] = node.getRightHandSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
return setterArg[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Expression unwrapMethodInvocation(MethodInvocation mi, int depth) {
|
||||
if (depth > 5) return mi;
|
||||
if (!mi.arguments().isEmpty()) {
|
||||
|
||||
@@ -427,19 +427,15 @@ public class JdtCallGraphEngine 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 = eVal.substring(eVal.lastIndexOf('.') + 1);
|
||||
if (!constants.contains(parsed)) constants.add(parsed);
|
||||
constants.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
||||
}
|
||||
} else {
|
||||
if (!constants.contains(val)) constants.add(val);
|
||||
constants.add(val);
|
||||
}
|
||||
} else if (retExpr instanceof org.eclipse.jdt.core.dom.ConditionalExpression ce) {
|
||||
extractConstantsFromExpression(ce, constants);
|
||||
} else if (retExpr instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic) {
|
||||
constants.add(click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType()));
|
||||
} else if (retExpr instanceof QualifiedName qn) {
|
||||
@@ -474,7 +470,11 @@ public class JdtCallGraphEngine implements CallGraphEngine {
|
||||
md.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(ReturnStatement node) {
|
||||
extractConstantsFromExpression(node.getExpression(), resolvedConstants);
|
||||
if (node.getExpression() instanceof QualifiedName qn) {
|
||||
resolvedConstants.add(qn.getName().getIdentifier());
|
||||
} else if (node.getExpression() instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
||||
resolvedConstants.add(sl.getLiteralValue());
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
@@ -484,97 +484,6 @@ public class JdtCallGraphEngine implements CallGraphEngine {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void extractConstantsFromExpression(org.eclipse.jdt.core.dom.Expression expr, List<String> constants) {
|
||||
if (expr instanceof QualifiedName qn) {
|
||||
constants.add(qn.toString());
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
||||
constants.add(sl.getLiteralValue());
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.ConditionalExpression ce) {
|
||||
extractConstantsFromExpression(ce.getThenExpression(), constants);
|
||||
extractConstantsFromExpression(ce.getElseExpression(), constants);
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.ParenthesizedExpression pe) {
|
||||
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.SwitchExpression se) {
|
||||
se.accept(new org.eclipse.jdt.core.dom.ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.YieldStatement ys) {
|
||||
extractConstantsFromExpression(ys.getExpression(), constants);
|
||||
return super.visit(ys);
|
||||
}
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.ExpressionStatement es) {
|
||||
extractConstantsFromExpression(es.getExpression(), constants);
|
||||
return super.visit(es);
|
||||
}
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.ReturnStatement rs) {
|
||||
extractConstantsFromExpression(rs.getExpression(), constants);
|
||||
return super.visit(rs);
|
||||
}
|
||||
});
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.MethodInvocation mi) {
|
||||
String methodName = mi.getName().getIdentifier();
|
||||
|
||||
// 1. Local setter tracking
|
||||
if ((methodName.startsWith("get") || methodName.equals("type") || methodName.equals("event")) && mi.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName sn) {
|
||||
String varName = sn.getIdentifier();
|
||||
String propName = methodName.startsWith("get") ? methodName.substring(3) : methodName;
|
||||
|
||||
org.eclipse.jdt.core.dom.Block block = findEnclosingBlock(mi);
|
||||
if (block != null) {
|
||||
for (Object stmtObj : block.statements()) {
|
||||
if (stmtObj == mi.getParent() || stmtObj == mi) break;
|
||||
if (stmtObj instanceof org.eclipse.jdt.core.dom.ExpressionStatement es) {
|
||||
if (es.getExpression() instanceof org.eclipse.jdt.core.dom.MethodInvocation setterMi) {
|
||||
if (setterMi.getName().getIdentifier().equalsIgnoreCase("set" + propName) || setterMi.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
if (setterMi.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName setterSn && setterSn.getIdentifier().equals(varName)) {
|
||||
if (!setterMi.arguments().isEmpty()) {
|
||||
extractConstantsFromExpression((org.eclipse.jdt.core.dom.Expression) setterMi.arguments().get(0), constants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (es.getExpression() instanceof org.eclipse.jdt.core.dom.Assignment assignment) {
|
||||
if (assignment.getLeftHandSide() instanceof org.eclipse.jdt.core.dom.FieldAccess fa) {
|
||||
if (fa.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName faSn && faSn.getIdentifier().equals(varName)) {
|
||||
if (fa.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
extractConstantsFromExpression(assignment.getRightHandSide(), constants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (assignment.getLeftHandSide() instanceof org.eclipse.jdt.core.dom.QualifiedName qqn) {
|
||||
if (qqn.getQualifier().getFullyQualifiedName().equals(varName)) {
|
||||
if (qqn.getName().getIdentifier().equalsIgnoreCase(propName)) {
|
||||
extractConstantsFromExpression(assignment.getRightHandSide(), constants);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Delegate to method return analysis
|
||||
TypeDeclaration td = findEnclosingType(mi);
|
||||
if (td != null && (mi.getExpression() == null || mi.getExpression() instanceof org.eclipse.jdt.core.dom.ThisExpression)) {
|
||||
List<String> values = resolveMethodReturnConstant(context.getFqn(td), methodName, 0, new java.util.HashSet<>(), null);
|
||||
if (values != null) constants.addAll(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private 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)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return (org.eclipse.jdt.core.dom.Block) parent;
|
||||
}
|
||||
|
||||
private String traceLocalVariable(String methodFqn, String varName) {
|
||||
if (methodFqn == null || !methodFqn.contains(".")) return null;
|
||||
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.enricher.matching;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||
import click.kamil.springstatemachineexporter.model.Event;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class HeuristicEventMatchingEngineTest {
|
||||
|
||||
private final HeuristicEventMatchingEngine engine = new HeuristicEventMatchingEngine();
|
||||
|
||||
@Test
|
||||
void shouldMatchExactFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("com.example.OrderEvents.PAY").build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchEnumQualifierToFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("OrderEvents.PAY").build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPreventCrossEnumContamination() {
|
||||
Event smEvent = Event.of("PAY", "com.example.InvoiceEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("OrderEvents.PAY").build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPreventCrossEnumContaminationWithPolymorphicEvents() {
|
||||
Event smEvent = Event.of("PAY", "com.example.InvoiceEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("getType()").polymorphicEvents(List.of("OrderEvents.PAY")).build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchPolymorphicEnumQualifierToFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("getType()").polymorphicEvents(List.of("OrderEvents.PAY")).build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchRawStringToFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("PAY").build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchRawStringToRawString() {
|
||||
Event smEvent = Event.of("PAY", "PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("PAY").build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchPolymorphicRawStringToFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder().event("getType()").polymorphicEvents(List.of("PAY")).build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -18,67 +18,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@Tag("heuristic_guess_paths_and_scrape_regex_replace_with_data_flow_analysis")
|
||||
class HeuristicCallGraphEngineTest {
|
||||
|
||||
@Test
|
||||
void shouldResolveLocalSetterAndSwitchExpressionPolymorphism(@TempDir Path tempDir) throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
public class OrderService {
|
||||
public void updateOrderState(String vv) {
|
||||
StateMachine sm = new StateMachine();
|
||||
MysteryPayload a = new MysteryPayload();
|
||||
a.setType(my_func(vv));
|
||||
sm.sendEvent(a.getType());
|
||||
}
|
||||
|
||||
private OrderEvents my_func(String q) {
|
||||
return switch (q) {
|
||||
case "a" -> OrderEvents.A8;
|
||||
case "b" -> OrderEvents.A133;
|
||||
default -> throw new IllegalArgumentException("Unknown");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum OrderEvents { A8, A133 }
|
||||
|
||||
class MysteryPayload {
|
||||
private OrderEvents type;
|
||||
public void setType(OrderEvents type) { this.type = type; }
|
||||
public OrderEvents getType() { return type; }
|
||||
}
|
||||
|
||||
class StateMachine {
|
||||
public void sendEvent(OrderEvents event) {}
|
||||
}
|
||||
""";
|
||||
Files.writeString(tempDir.resolve("OrderService.java"), source);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
HeuristicCallGraphEngine builder = new HeuristicCallGraphEngine(context);
|
||||
|
||||
EntryPoint entryPoint = EntryPoint.builder()
|
||||
.className("com.example.OrderService")
|
||||
.methodName("updateOrderState")
|
||||
.build();
|
||||
|
||||
TriggerPoint trigger = TriggerPoint.builder()
|
||||
.className("com.example.StateMachine")
|
||||
.methodName("sendEvent")
|
||||
.event("event")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
|
||||
|
||||
assertThat(chains).hasSize(1);
|
||||
CallChain chain = chains.get(0);
|
||||
|
||||
System.out.println("Resolved poly events: " + chain.getTriggerPoint().getPolymorphicEvents());
|
||||
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
|
||||
.containsExactlyInAnyOrder("OrderEvents.A8", "OrderEvents.A133");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindCallChainWithLambdaMethodReference(@TempDir Path tempDir) throws IOException {
|
||||
String source = """
|
||||
@@ -353,63 +292,6 @@ class HeuristicCallGraphEngineTest {
|
||||
.containsExactlyInAnyOrder("OrderEvents.CANCELLED", "OrderEvents.RECEIVED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResolveTernaryAndStringPolymorphicReturns() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
public class OrderController {
|
||||
private OrderService service;
|
||||
public void processOrderEvent() {
|
||||
service.updateOrderState(new MysteryPayload().getType());
|
||||
}
|
||||
}
|
||||
|
||||
class OrderService {
|
||||
public void updateOrderState(Object event) {
|
||||
// sendEvent
|
||||
}
|
||||
}
|
||||
|
||||
class MysteryPayload {
|
||||
public Object getType() {
|
||||
int a = 5;
|
||||
if (a > 10) {
|
||||
return "RAW_STRING_EVENT";
|
||||
}
|
||||
return a > 5 ? OrderEvents.ABCD : OrderEvents.PAY;
|
||||
}
|
||||
}
|
||||
|
||||
enum OrderEvents { ABCD, PAY }
|
||||
""";
|
||||
Path tempDir = Files.createTempDirectory("callgraph_test_ternary_string");
|
||||
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("processOrderEvent")
|
||||
.build();
|
||||
|
||||
TriggerPoint trigger = TriggerPoint.builder()
|
||||
.className("com.example.OrderService")
|
||||
.methodName("updateOrderState")
|
||||
.event("event")
|
||||
.build();
|
||||
|
||||
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
|
||||
|
||||
assertThat(chains).hasSize(1);
|
||||
CallChain chain = chains.get(0);
|
||||
assertThat(chain.getTriggerPoint().getPolymorphicEvents())
|
||||
.as("Resolved event was: " + chain.getTriggerPoint().getEvent())
|
||||
.containsExactlyInAnyOrder("OrderEvents.ABCD", "OrderEvents.PAY", "RAW_STRING_EVENT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUnwrapDeepMethodWrappers() throws IOException {
|
||||
String source = """
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "OrderEvents.PAY" ]
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -222,7 +222,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "OrderEvents.FULFILL" ]
|
||||
"polymorphicEvents" : [ "FULFILL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -253,7 +253,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "OrderEvents.CANCEL" ]
|
||||
"polymorphicEvents" : [ "CANCEL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -284,7 +284,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 17,
|
||||
"polymorphicEvents" : [ "OrderEvents.PAY" ]
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -315,7 +315,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "OrderEvents.PAY" ]
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -346,7 +346,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "OrderEvents.PAY" ]
|
||||
"polymorphicEvents" : [ "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -451,7 +451,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "OrderEvents.FULFILL" ]
|
||||
"polymorphicEvents" : [ "FULFILL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -482,7 +482,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 13,
|
||||
"polymorphicEvents" : [ "OrderEvents.CANCEL" ]
|
||||
"polymorphicEvents" : [ "CANCEL" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -513,7 +513,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 21,
|
||||
"polymorphicEvents" : [ "OrderEvents.ABCD" ]
|
||||
"polymorphicEvents" : [ "ABCD" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
@@ -544,7 +544,7 @@
|
||||
"stateMachineId" : null,
|
||||
"sourceState" : null,
|
||||
"lineNumber" : 21,
|
||||
"polymorphicEvents" : [ "OrderEvents.ABCD", "OrderEvents.PAY" ]
|
||||
"polymorphicEvents" : [ "ABCD", "PAY" ]
|
||||
},
|
||||
"contextMachineId" : null,
|
||||
"matchedTransitions" : [ {
|
||||
|
||||
@@ -4,6 +4,10 @@ public class MysteryPayload implements CustomCodeEventInterface {
|
||||
@Override
|
||||
public OrderEvents resolveEventCode() {
|
||||
int a = (int) (Math.random() * 10);
|
||||
return a > 5 ? OrderEvents.ABCD : OrderEvents.PAY;
|
||||
if (a > 5) {
|
||||
return OrderEvents.ABCD;
|
||||
} else {
|
||||
return OrderEvents.PAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user