event maching heuristic update 3
This commit is contained in:
@@ -461,6 +461,25 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
TypeDeclaration targetTd = contextCu != null ? context.getTypeDeclaration(cName, contextCu) : context.getTypeDeclaration(cName);
|
TypeDeclaration targetTd = contextCu != null ? context.getTypeDeclaration(cName, contextCu) : context.getTypeDeclaration(cName);
|
||||||
if (targetTd == null) targetTd = context.getTypeDeclaration(cName);
|
if (targetTd == null) targetTd = context.getTypeDeclaration(cName);
|
||||||
|
|
||||||
|
if (targetTd != null && context.findMethodDeclaration(targetTd, mName, true) != null) {
|
||||||
|
List<String> delegationResult = resolveMethodReturnConstant(cName, mName, depth + 1, visited, contextCu);
|
||||||
|
constants.addAll(delegationResult);
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (retExpr instanceof org.eclipse.jdt.core.dom.SuperMethodInvocation smi) {
|
||||||
|
String superFqn = context.getSuperclassFqn(td);
|
||||||
|
if (superFqn != null) {
|
||||||
|
String called = superFqn + "." + smi.getName().getIdentifier();
|
||||||
|
if (visited.contains(called)) {
|
||||||
|
handled = true;
|
||||||
|
} else {
|
||||||
|
String cName = superFqn;
|
||||||
|
String mName = smi.getName().getIdentifier();
|
||||||
|
TypeDeclaration targetTd = contextCu != null ? context.getTypeDeclaration(cName, contextCu) : context.getTypeDeclaration(cName);
|
||||||
|
if (targetTd == null) targetTd = context.getTypeDeclaration(cName);
|
||||||
|
|
||||||
if (targetTd != null && context.findMethodDeclaration(targetTd, mName, true) != null) {
|
if (targetTd != null && context.findMethodDeclaration(targetTd, mName, true) != null) {
|
||||||
List<String> delegationResult = resolveMethodReturnConstant(cName, mName, depth + 1, visited, contextCu);
|
List<String> delegationResult = resolveMethodReturnConstant(cName, mName, depth + 1, visited, contextCu);
|
||||||
constants.addAll(delegationResult);
|
constants.addAll(delegationResult);
|
||||||
@@ -488,6 +507,13 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
} else {
|
} else {
|
||||||
constants.add(sn.toString());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1168,35 +1194,18 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(ConstructorInvocation node) {
|
public boolean visit(ConstructorInvocation node) {
|
||||||
for (Object argObj : node.arguments()) {
|
processConstructorInvocationArgs(node.arguments(), findEnclosingType(node), node, results, fieldName, context, visited);
|
||||||
Expression arg = (Expression) argObj;
|
|
||||||
String val = constantResolver.resolve(arg, context);
|
|
||||||
if (val != null) {
|
|
||||||
if (val.startsWith("ENUM_SET:")) {
|
|
||||||
for (String eVal : val.substring(9).split(",")) {
|
|
||||||
results.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results.add(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(node);
|
return super.visit(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(SuperConstructorInvocation node) {
|
public boolean visit(SuperConstructorInvocation node) {
|
||||||
for (Object argObj : node.arguments()) {
|
TypeDeclaration enclosingTd = findEnclosingType(node);
|
||||||
Expression arg = (Expression) argObj;
|
if (enclosingTd != null) {
|
||||||
String val = constantResolver.resolve(arg, context);
|
String superFqn = context.getSuperclassFqn(enclosingTd);
|
||||||
if (val != null) {
|
if (superFqn != null) {
|
||||||
if (val.startsWith("ENUM_SET:")) {
|
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
||||||
for (String eVal : val.substring(9).split(",")) {
|
processConstructorInvocationArgs(node.arguments(), superTd, node, results, fieldName, context, visited);
|
||||||
results.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results.add(val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(node);
|
return super.visit(node);
|
||||||
@@ -1220,4 +1229,123 @@ public class HeuristicCallGraphEngine implements CallGraphEngine {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processConstructorInvocationArgs(List<?> arguments, TypeDeclaration targetTd, ASTNode callNode, List<String> results, String fieldName, CodebaseContext context, Set<String> visited) {
|
||||||
|
if (targetTd == null) return;
|
||||||
|
MethodDeclaration callerMd = findEnclosingMethod(callNode);
|
||||||
|
|
||||||
|
org.eclipse.jdt.core.dom.IMethodBinding resolvedBinding = null;
|
||||||
|
if (callNode instanceof ConstructorInvocation ci) {
|
||||||
|
resolvedBinding = ci.resolveConstructorBinding();
|
||||||
|
} else if (callNode instanceof SuperConstructorInvocation sci) {
|
||||||
|
resolvedBinding = sci.resolveConstructorBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MethodDeclaration otherMd : targetTd.getMethods()) {
|
||||||
|
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() == arguments.size() && otherMd != callerMd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
int targetIdx = findAssignedParameterIndex(otherMd, fieldName, context, new java.util.HashSet<>());
|
||||||
|
if (targetIdx >= 0 && targetIdx < arguments.size()) {
|
||||||
|
Expression arg = (Expression) arguments.get(targetIdx);
|
||||||
|
String val = constantResolver.resolve(arg, context);
|
||||||
|
if (val != null) {
|
||||||
|
if (val.startsWith("ENUM_SET:")) {
|
||||||
|
for (String eVal : val.substring(9).split(",")) {
|
||||||
|
results.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results.add(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findAssignedParameterIndex(MethodDeclaration constructorMd, String fieldName, CodebaseContext context, java.util.Set<MethodDeclaration> visited) {
|
||||||
|
if (constructorMd == null || constructorMd.getBody() == null) return -1;
|
||||||
|
if (!visited.add(constructorMd)) return -1;
|
||||||
|
|
||||||
|
final int[] foundIdx = {-1};
|
||||||
|
constructorMd.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
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))) {
|
||||||
|
Expression right = asn.getRightHandSide();
|
||||||
|
if (right instanceof SimpleName snRight) {
|
||||||
|
String rightName = snRight.getIdentifier();
|
||||||
|
for (int i = 0; i < constructorMd.parameters().size(); i++) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
|
||||||
|
if (svd.getName().getIdentifier().equals(rightName)) {
|
||||||
|
foundIdx[0] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(asn);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(ConstructorInvocation node) {
|
||||||
|
TypeDeclaration enclosingTd = findEnclosingType(node);
|
||||||
|
if (enclosingTd != null) {
|
||||||
|
for (MethodDeclaration otherMd : enclosingTd.getMethods()) {
|
||||||
|
if (otherMd.isConstructor() && otherMd != constructorMd && otherMd.parameters().size() == node.arguments().size()) {
|
||||||
|
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();
|
||||||
|
for (int i = 0; i < constructorMd.parameters().size(); i++) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
|
||||||
|
if (svd.getName().getIdentifier().equals(argName)) {
|
||||||
|
foundIdx[0] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(SuperConstructorInvocation node) {
|
||||||
|
TypeDeclaration enclosingTd = findEnclosingType(node);
|
||||||
|
if (enclosingTd != null) {
|
||||||
|
String superFqn = context.getSuperclassFqn(enclosingTd);
|
||||||
|
if (superFqn != null) {
|
||||||
|
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
||||||
|
if (superTd != null) {
|
||||||
|
for (MethodDeclaration otherMd : superTd.getMethods()) {
|
||||||
|
if (otherMd.isConstructor() && otherMd.parameters().size() == node.arguments().size()) {
|
||||||
|
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();
|
||||||
|
for (int i = 0; i < constructorMd.parameters().size(); i++) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
|
||||||
|
if (svd.getName().getIdentifier().equals(argName)) {
|
||||||
|
foundIdx[0] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return foundIdx[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -418,6 +418,25 @@ public class JdtCallGraphEngine implements CallGraphEngine {
|
|||||||
TypeDeclaration targetTd = contextCu != null ? context.getTypeDeclaration(cName, contextCu) : context.getTypeDeclaration(cName);
|
TypeDeclaration targetTd = contextCu != null ? context.getTypeDeclaration(cName, contextCu) : context.getTypeDeclaration(cName);
|
||||||
if (targetTd == null) targetTd = context.getTypeDeclaration(cName);
|
if (targetTd == null) targetTd = context.getTypeDeclaration(cName);
|
||||||
|
|
||||||
|
if (targetTd != null && context.findMethodDeclaration(targetTd, mName, true) != null) {
|
||||||
|
List<String> delegationResult = resolveMethodReturnConstant(cName, mName, depth + 1, visited, contextCu);
|
||||||
|
constants.addAll(delegationResult);
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (retExpr instanceof org.eclipse.jdt.core.dom.SuperMethodInvocation smi) {
|
||||||
|
String superFqn = context.getSuperclassFqn(td);
|
||||||
|
if (superFqn != null) {
|
||||||
|
String called = superFqn + "." + smi.getName().getIdentifier();
|
||||||
|
if (visited.contains(called)) {
|
||||||
|
handled = true;
|
||||||
|
} else {
|
||||||
|
String cName = superFqn;
|
||||||
|
String mName = smi.getName().getIdentifier();
|
||||||
|
TypeDeclaration targetTd = contextCu != null ? context.getTypeDeclaration(cName, contextCu) : context.getTypeDeclaration(cName);
|
||||||
|
if (targetTd == null) targetTd = context.getTypeDeclaration(cName);
|
||||||
|
|
||||||
if (targetTd != null && context.findMethodDeclaration(targetTd, mName, true) != null) {
|
if (targetTd != null && context.findMethodDeclaration(targetTd, mName, true) != null) {
|
||||||
List<String> delegationResult = resolveMethodReturnConstant(cName, mName, depth + 1, visited, contextCu);
|
List<String> delegationResult = resolveMethodReturnConstant(cName, mName, depth + 1, visited, contextCu);
|
||||||
constants.addAll(delegationResult);
|
constants.addAll(delegationResult);
|
||||||
@@ -451,6 +470,13 @@ public class JdtCallGraphEngine implements CallGraphEngine {
|
|||||||
} else {
|
} else {
|
||||||
constants.add(sn.toString());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1135,35 +1161,18 @@ public class JdtCallGraphEngine implements CallGraphEngine {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(ConstructorInvocation node) {
|
public boolean visit(ConstructorInvocation node) {
|
||||||
for (Object argObj : node.arguments()) {
|
processConstructorInvocationArgs(node.arguments(), findEnclosingType(node), node, results, fieldName, context, visited);
|
||||||
Expression arg = (Expression) argObj;
|
|
||||||
String val = constantResolver.resolve(arg, context);
|
|
||||||
if (val != null) {
|
|
||||||
if (val.startsWith("ENUM_SET:")) {
|
|
||||||
for (String eVal : val.substring(9).split(",")) {
|
|
||||||
results.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results.add(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(node);
|
return super.visit(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(SuperConstructorInvocation node) {
|
public boolean visit(SuperConstructorInvocation node) {
|
||||||
for (Object argObj : node.arguments()) {
|
TypeDeclaration enclosingTd = findEnclosingType(node);
|
||||||
Expression arg = (Expression) argObj;
|
if (enclosingTd != null) {
|
||||||
String val = constantResolver.resolve(arg, context);
|
String superFqn = context.getSuperclassFqn(enclosingTd);
|
||||||
if (val != null) {
|
if (superFqn != null) {
|
||||||
if (val.startsWith("ENUM_SET:")) {
|
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
||||||
for (String eVal : val.substring(9).split(",")) {
|
processConstructorInvocationArgs(node.arguments(), superTd, node, results, fieldName, context, visited);
|
||||||
results.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
results.add(val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(node);
|
return super.visit(node);
|
||||||
@@ -1187,4 +1196,123 @@ public class JdtCallGraphEngine implements CallGraphEngine {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processConstructorInvocationArgs(List<?> arguments, TypeDeclaration targetTd, ASTNode callNode, List<String> results, String fieldName, CodebaseContext context, Set<String> visited) {
|
||||||
|
if (targetTd == null) return;
|
||||||
|
MethodDeclaration callerMd = findEnclosingMethod(callNode);
|
||||||
|
|
||||||
|
org.eclipse.jdt.core.dom.IMethodBinding resolvedBinding = null;
|
||||||
|
if (callNode instanceof ConstructorInvocation ci) {
|
||||||
|
resolvedBinding = ci.resolveConstructorBinding();
|
||||||
|
} else if (callNode instanceof SuperConstructorInvocation sci) {
|
||||||
|
resolvedBinding = sci.resolveConstructorBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MethodDeclaration otherMd : targetTd.getMethods()) {
|
||||||
|
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() == arguments.size() && otherMd != callerMd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
int targetIdx = findAssignedParameterIndex(otherMd, fieldName, context, new java.util.HashSet<>());
|
||||||
|
if (targetIdx >= 0 && targetIdx < arguments.size()) {
|
||||||
|
Expression arg = (Expression) arguments.get(targetIdx);
|
||||||
|
String val = constantResolver.resolve(arg, context);
|
||||||
|
if (val != null) {
|
||||||
|
if (val.startsWith("ENUM_SET:")) {
|
||||||
|
for (String eVal : val.substring(9).split(",")) {
|
||||||
|
results.add(eVal.substring(eVal.lastIndexOf('.') + 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results.add(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findAssignedParameterIndex(MethodDeclaration constructorMd, String fieldName, CodebaseContext context, java.util.Set<MethodDeclaration> visited) {
|
||||||
|
if (constructorMd == null || constructorMd.getBody() == null) return -1;
|
||||||
|
if (!visited.add(constructorMd)) return -1;
|
||||||
|
|
||||||
|
final int[] foundIdx = {-1};
|
||||||
|
constructorMd.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
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))) {
|
||||||
|
Expression right = asn.getRightHandSide();
|
||||||
|
if (right instanceof SimpleName snRight) {
|
||||||
|
String rightName = snRight.getIdentifier();
|
||||||
|
for (int i = 0; i < constructorMd.parameters().size(); i++) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
|
||||||
|
if (svd.getName().getIdentifier().equals(rightName)) {
|
||||||
|
foundIdx[0] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(asn);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(ConstructorInvocation node) {
|
||||||
|
TypeDeclaration enclosingTd = findEnclosingType(node);
|
||||||
|
if (enclosingTd != null) {
|
||||||
|
for (MethodDeclaration otherMd : enclosingTd.getMethods()) {
|
||||||
|
if (otherMd.isConstructor() && otherMd != constructorMd && otherMd.parameters().size() == node.arguments().size()) {
|
||||||
|
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();
|
||||||
|
for (int i = 0; i < constructorMd.parameters().size(); i++) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
|
||||||
|
if (svd.getName().getIdentifier().equals(argName)) {
|
||||||
|
foundIdx[0] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(SuperConstructorInvocation node) {
|
||||||
|
TypeDeclaration enclosingTd = findEnclosingType(node);
|
||||||
|
if (enclosingTd != null) {
|
||||||
|
String superFqn = context.getSuperclassFqn(enclosingTd);
|
||||||
|
if (superFqn != null) {
|
||||||
|
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
||||||
|
if (superTd != null) {
|
||||||
|
for (MethodDeclaration otherMd : superTd.getMethods()) {
|
||||||
|
if (otherMd.isConstructor() && otherMd.parameters().size() == node.arguments().size()) {
|
||||||
|
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();
|
||||||
|
for (int i = 0; i < constructorMd.parameters().size(); i++) {
|
||||||
|
SingleVariableDeclaration svd = (SingleVariableDeclaration) constructorMd.parameters().get(i);
|
||||||
|
if (svd.getName().getIdentifier().equals(argName)) {
|
||||||
|
foundIdx[0] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return foundIdx[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class ConstructorInvocationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotBlindlyAddAllConstructorArguments(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RichOrderEvent {
|
||||||
|
private OrderEvents type;
|
||||||
|
private String irrelevantInfo;
|
||||||
|
|
||||||
|
public RichOrderEvent() {
|
||||||
|
this(OrderEvents.PAY, "DEFAULT_INFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RichOrderEvent(OrderEvents type, String info) {
|
||||||
|
this.type = type;
|
||||||
|
this.irrelevantInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderEvents getType() { return type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
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())
|
||||||
|
.contains("OrderEvents.PAY")
|
||||||
|
.doesNotContain("DEFAULT_INFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleOverloadedConstructorsGracefully(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RichOrderEvent {
|
||||||
|
private OrderEvents type;
|
||||||
|
private String irrelevantInfo;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
public RichOrderEvent() {
|
||||||
|
this(OrderEvents.PAY, "DEFAULT_INFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded constructor 1
|
||||||
|
public RichOrderEvent(OrderEvents type, String info) {
|
||||||
|
this.type = type;
|
||||||
|
this.irrelevantInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overloaded constructor 2 (same number of arguments!)
|
||||||
|
public RichOrderEvent(String info, int count) {
|
||||||
|
this.irrelevantInfo = info;
|
||||||
|
this.count = count;
|
||||||
|
// type is not set here
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderEvents getType() { return type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
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())
|
||||||
|
.contains("OrderEvents.PAY")
|
||||||
|
.doesNotContain("DEFAULT_INFO")
|
||||||
|
.doesNotContain("count");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleChainedConstructorDelegationGracefully(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseEvent {
|
||||||
|
protected OrderEvents type;
|
||||||
|
public BaseEvent(OrderEvents type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RichOrderEvent extends BaseEvent {
|
||||||
|
public RichOrderEvent() {
|
||||||
|
this(OrderEvents.PAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RichOrderEvent(OrderEvents type) {
|
||||||
|
this(type, "DEFAULT_INFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RichOrderEvent(OrderEvents type, String info) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderEvents getType() { return type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
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())
|
||||||
|
.contains("OrderEvents.PAY")
|
||||||
|
.doesNotContain("DEFAULT_INFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleFieldAccessGracefully(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(RichOrderEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RichOrderEvent {
|
||||||
|
private OrderEvents type;
|
||||||
|
|
||||||
|
public RichOrderEvent() {
|
||||||
|
this(OrderEvents.PAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RichOrderEvent(OrderEvents type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return this.type; // Specifically testing FieldAccess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
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())
|
||||||
|
.contains("OrderEvents.PAY");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.service;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class SuperMethodInvocationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleSuperMethodInvocation(@TempDir Path tempDir) throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(DerivedEvent domainEvent) {
|
||||||
|
service.updateOrderState(domainEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseEvent {
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return OrderEvents.PAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DerivedEvent extends BaseEvent {
|
||||||
|
public OrderEvents getType() {
|
||||||
|
return super.getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { PAY }
|
||||||
|
""";
|
||||||
|
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())
|
||||||
|
.contains("OrderEvents.PAY");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user