more tests fixes
This commit is contained in:
@@ -93,9 +93,9 @@ public class ConstantResolver {
|
|||||||
|
|
||||||
// Fallback for unresolved AST nodes
|
// Fallback for unresolved AST nodes
|
||||||
if (mi.getExpression() instanceof QualifiedName qn) {
|
if (mi.getExpression() instanceof QualifiedName qn) {
|
||||||
return qn.getName().getIdentifier();
|
System.out.println("RETURNING qn name: " + qn.getName().getIdentifier()); return qn.getName().getIdentifier();
|
||||||
} else if (mi.getExpression() instanceof SimpleName sn) {
|
} else if (mi.getExpression() instanceof SimpleName sn) {
|
||||||
return sn.getIdentifier();
|
System.out.println("RETURNING sn name: " + sn.getIdentifier()); return sn.getIdentifier();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +418,7 @@ public class ConstantResolver {
|
|||||||
if (paramValues.containsKey(name)) return paramValues.get(name);
|
if (paramValues.containsKey(name)) return paramValues.get(name);
|
||||||
return null;
|
return null;
|
||||||
} else if (expr instanceof QualifiedName qn) {
|
} else if (expr instanceof QualifiedName qn) {
|
||||||
return qn.getName().getIdentifier();
|
return null;
|
||||||
} else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
} else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
||||||
return sl.getLiteralValue();
|
return sl.getLiteralValue();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
exprParser.setKind(ASTParser.K_EXPRESSION);
|
exprParser.setKind(ASTParser.K_EXPRESSION);
|
||||||
exprParser.setSource(resolvedValue.toCharArray());
|
exprParser.setSource(resolvedValue.toCharArray());
|
||||||
ASTNode exprNode = exprParser.createAST(null);
|
ASTNode exprNode = exprParser.createAST(null);
|
||||||
|
System.out.println("DEBUG resolvedValue=" + resolvedValue + ", exprNode=" + exprNode.getClass().getSimpleName());
|
||||||
|
|
||||||
String varName = null;
|
String varName = null;
|
||||||
String methodName = null;
|
String methodName = null;
|
||||||
@@ -434,7 +435,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
TypeDeclaration currentTd = context.getTypeDeclaration(methodFqn.substring(0, methodFqn.lastIndexOf('.')));
|
TypeDeclaration currentTd = context.getTypeDeclaration(methodFqn.substring(0, methodFqn.lastIndexOf('.')));
|
||||||
CompilationUnit cu = currentTd != null && currentTd.getRoot() instanceof CompilationUnit ? (CompilationUnit) currentTd.getRoot() : null;
|
CompilationUnit cu = currentTd != null && currentTd.getRoot() instanceof CompilationUnit ? (CompilationUnit) currentTd.getRoot() : null;
|
||||||
List<String> values = constantExtractor.resolveMethodReturnConstant(declaredType, methodName, 0, new HashSet<>(), cu);
|
List<String> values = constantExtractor.resolveMethodReturnConstant(declaredType, methodName, 0, new HashSet<>(), cu);
|
||||||
if (values != null && !values.isEmpty()) {
|
System.out.println("RESOLVED VALUES FOR " + methodName + " in " + currentTd.getName() + ": " + values); if (values != null && !values.isEmpty()) {
|
||||||
polymorphicEvents.addAll(values);
|
polymorphicEvents.addAll(values);
|
||||||
}
|
}
|
||||||
if (polymorphicEvents.isEmpty() && variableTracer != null) {
|
if (polymorphicEvents.isEmpty() && variableTracer != null) {
|
||||||
@@ -475,6 +476,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
if (currentTd != null && context.findMethodDeclaration(currentTd, methodName, true) != null) {
|
if (currentTd != null && context.findMethodDeclaration(currentTd, methodName, true) != null) {
|
||||||
CompilationUnit cu = currentTd.getRoot() instanceof CompilationUnit ? (CompilationUnit) currentTd.getRoot() : null;
|
CompilationUnit cu = currentTd.getRoot() instanceof CompilationUnit ? (CompilationUnit) currentTd.getRoot() : null;
|
||||||
List<String> values = constantExtractor.resolveMethodReturnConstant(context.getFqn(currentTd), methodName, 0, new HashSet<>(), cu);
|
List<String> values = constantExtractor.resolveMethodReturnConstant(context.getFqn(currentTd), methodName, 0, new HashSet<>(), cu);
|
||||||
|
System.out.println("DEBUG RESOLVED VALUES FOR " + methodName + " in " + currentTd.getName() + " -> " + values);
|
||||||
if (values != null && !values.isEmpty()) {
|
if (values != null && !values.isEmpty()) {
|
||||||
polymorphicEvents.addAll(values);
|
polymorphicEvents.addAll(values);
|
||||||
sourceMethod = methodFqn;
|
sourceMethod = methodFqn;
|
||||||
@@ -669,7 +671,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
val = expr.toString();
|
val = expr.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return postProcessResolvedArgument(expr, val);
|
System.out.println("DEBUG resolveArgument expr=" + expr + ", val=" + val); return postProcessResolvedArgument(expr, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String postProcessResolvedArgument(Expression originalExpr, String resolvedValue) {
|
protected String postProcessResolvedArgument(Expression originalExpr, String resolvedValue) {
|
||||||
@@ -690,6 +692,27 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String resolveMethodInTypeHierarchy(TypeDeclaration td, String methodName) {
|
||||||
|
String resolved = resolveMethodInType(td, methodName);
|
||||||
|
if (resolved != null) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
String superFqn = context.getSuperclassFqn(td);
|
||||||
|
while (superFqn != null) {
|
||||||
|
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
||||||
|
if (superTd != null) {
|
||||||
|
resolved = resolveMethodInType(superTd, methodName);
|
||||||
|
if (resolved != null) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
superFqn = context.getSuperclassFqn(superTd);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected Expression traceVariable(Expression expr) {
|
protected Expression traceVariable(Expression expr) {
|
||||||
return variableTracer.traceVariable(expr);
|
return variableTracer.traceVariable(expr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,8 @@ public class ConstructorAnalyzer {
|
|||||||
TypeDeclaration td,
|
TypeDeclaration td,
|
||||||
ClassInstanceCreation cic,
|
ClassInstanceCreation cic,
|
||||||
CodebaseContext context,
|
CodebaseContext context,
|
||||||
RhsResolver rhsResolver) {
|
RhsResolver rhsResolver,
|
||||||
|
Map<String, String> initialLocals) {
|
||||||
|
|
||||||
String actualTypeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
String actualTypeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
|
||||||
TypeDeclaration actualTd = context.getTypeDeclaration(actualTypeName);
|
TypeDeclaration actualTd = context.getTypeDeclaration(actualTypeName);
|
||||||
@@ -153,15 +154,23 @@ public class ConstructorAnalyzer {
|
|||||||
|
|
||||||
Map<String, String> fieldValues = new HashMap<>();
|
Map<String, String> fieldValues = new HashMap<>();
|
||||||
Set<String> visitedCtors = new HashSet<>();
|
Set<String> visitedCtors = new HashSet<>();
|
||||||
buildFieldValuesFromCICRecursive(actualTd, cic, context, fieldValues, visitedCtors, rhsResolver);
|
buildFieldValuesFromCICRecursive(actualTd, cic, context, fieldValues, visitedCtors, rhsResolver, initialLocals);
|
||||||
return fieldValues;
|
return fieldValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, String> buildFieldValuesFromCIC(
|
||||||
|
TypeDeclaration td,
|
||||||
|
ClassInstanceCreation cic,
|
||||||
|
CodebaseContext context,
|
||||||
|
RhsResolver rhsResolver) {
|
||||||
|
return buildFieldValuesFromCIC(td, cic, context, rhsResolver, null);
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, String> buildFieldValuesFromCIC(
|
public Map<String, String> buildFieldValuesFromCIC(
|
||||||
TypeDeclaration td,
|
TypeDeclaration td,
|
||||||
ClassInstanceCreation cic,
|
ClassInstanceCreation cic,
|
||||||
CodebaseContext context) {
|
CodebaseContext context) {
|
||||||
return buildFieldValuesFromCIC(td, cic, context, null);
|
return buildFieldValuesFromCIC(td, cic, context, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildFieldValuesFromCICRecursive(
|
private void buildFieldValuesFromCICRecursive(
|
||||||
@@ -170,7 +179,8 @@ public class ConstructorAnalyzer {
|
|||||||
CodebaseContext context,
|
CodebaseContext context,
|
||||||
Map<String, String> fieldValues,
|
Map<String, String> fieldValues,
|
||||||
Set<String> visitedCtors,
|
Set<String> visitedCtors,
|
||||||
RhsResolver rhsResolver) {
|
RhsResolver rhsResolver,
|
||||||
|
Map<String, String> initialLocals) {
|
||||||
|
|
||||||
String tdKey = context.getFqn(td);
|
String tdKey = context.getFqn(td);
|
||||||
if (!visitedCtors.add(tdKey)) return;
|
if (!visitedCtors.add(tdKey)) return;
|
||||||
@@ -190,7 +200,13 @@ public class ConstructorAnalyzer {
|
|||||||
if (!consts.isEmpty()) {
|
if (!consts.isEmpty()) {
|
||||||
paramValues.put(pName, consts.get(0));
|
paramValues.put(pName, consts.get(0));
|
||||||
} else {
|
} else {
|
||||||
String resolved = constantResolver.resolve(argExpr, context);
|
String resolved = null;
|
||||||
|
if (argExpr instanceof SimpleName sn && initialLocals != null && initialLocals.containsKey(sn.getIdentifier())) {
|
||||||
|
resolved = initialLocals.get(sn.getIdentifier());
|
||||||
|
}
|
||||||
|
if (resolved == null) {
|
||||||
|
resolved = constantResolver.resolve(argExpr, context);
|
||||||
|
}
|
||||||
if (resolved != null) paramValues.put(pName, resolved);
|
if (resolved != null) paramValues.put(pName, resolved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,6 +256,12 @@ public class ConstructorAnalyzer {
|
|||||||
}
|
}
|
||||||
return super.visit(sci);
|
return super.visit(sci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(ConstructorInvocation ci) {
|
||||||
|
processSuperConstructorArgs(td, ci.arguments(), paramValues, context, fieldValues, visitedCtors, rhsResolver);
|
||||||
|
return super.visit(ci);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -330,6 +352,12 @@ public class ConstructorAnalyzer {
|
|||||||
}
|
}
|
||||||
return super.visit(sci);
|
return super.visit(sci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(ConstructorInvocation ci) {
|
||||||
|
processSuperConstructorArgs(superTd, ci.arguments(), superParamValues, context, fieldValues, visitedCtors, rhsResolver);
|
||||||
|
return super.visit(ci);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,23 +179,9 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
TypeDeclaration td = context.getTypeDeclaration(className);
|
TypeDeclaration td = context.getTypeDeclaration(className);
|
||||||
if (td == null) return null;
|
if (td == null) return null;
|
||||||
|
|
||||||
MethodDeclaration md = context.findMethodDeclaration(td, methodName, false);
|
String resolvedMethodFqn = resolveMethodInTypeHierarchy(td, methodName);
|
||||||
if (md != null) {
|
if (resolvedMethodFqn != null) {
|
||||||
return className;
|
return resolvedMethodFqn.substring(0, resolvedMethodFqn.lastIndexOf('.'));
|
||||||
}
|
|
||||||
|
|
||||||
String superFqn = context.getSuperclassFqn(td);
|
|
||||||
while (superFqn != null) {
|
|
||||||
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
|
|
||||||
if (superTd != null) {
|
|
||||||
MethodDeclaration superMd = context.findMethodDeclaration(superTd, methodName, false);
|
|
||||||
if (superMd != null) {
|
|
||||||
return superFqn;
|
|
||||||
}
|
|
||||||
superFqn = context.getSuperclassFqn(superTd);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -207,7 +193,7 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
if (receiver == null) {
|
if (receiver == null) {
|
||||||
TypeDeclaration td = findEnclosingType(node);
|
TypeDeclaration td = findEnclosingType(node);
|
||||||
if (td != null) {
|
if (td != null) {
|
||||||
return resolveMethodInType(td, methodName);
|
return resolveMethodInTypeHierarchy(td, methodName);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -233,12 +219,31 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
return receiverName + "." + methodName;
|
return receiverName + "." + methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (receiver instanceof QualifiedName qn) {
|
||||||
|
String fqn = qn.getFullyQualifiedName();
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(fqn);
|
||||||
|
if (td != null) {
|
||||||
|
return fqn + "." + methodName;
|
||||||
|
}
|
||||||
|
return fqn + "." + methodName;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String resolveReceiverTypeFallback(SimpleName receiverNameNode) {
|
protected String resolveReceiverTypeFallback(SimpleName receiverNameNode) {
|
||||||
String varName = receiverNameNode.getIdentifier();
|
String varName = receiverNameNode.getIdentifier();
|
||||||
|
|
||||||
|
CompilationUnit cu = receiverNameNode.getRoot() instanceof CompilationUnit ? (CompilationUnit) receiverNameNode.getRoot() : null;
|
||||||
|
TypeDeclaration staticTd = context.getTypeDeclaration(varName, cu);
|
||||||
|
if (staticTd != null) {
|
||||||
|
return context.getFqn(staticTd);
|
||||||
|
}
|
||||||
|
TypeDeclaration fallbackStaticTd = context.getTypeDeclaration(varName);
|
||||||
|
if (fallbackStaticTd != null) {
|
||||||
|
return context.getFqn(fallbackStaticTd);
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Check local variables in enclosing method
|
// 1. Check local variables in enclosing method
|
||||||
MethodDeclaration enclosingMethod = findEnclosingMethod(receiverNameNode);
|
MethodDeclaration enclosingMethod = findEnclosingMethod(receiverNameNode);
|
||||||
if (enclosingMethod != null) {
|
if (enclosingMethod != null) {
|
||||||
@@ -357,12 +362,17 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
|
|
||||||
// Obtain the ClassInstanceCreation that created the object
|
// Obtain the ClassInstanceCreation that created the object
|
||||||
org.eclipse.jdt.core.dom.ClassInstanceCreation cic = null;
|
org.eclipse.jdt.core.dom.ClassInstanceCreation cic = null;
|
||||||
|
String receiverType = null;
|
||||||
if (initExpr[0] instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation directCic) {
|
if (initExpr[0] instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation directCic) {
|
||||||
cic = directCic;
|
cic = directCic;
|
||||||
} else if (initExpr[0] instanceof MethodInvocation factoryMi
|
} else if (initExpr[0] instanceof MethodInvocation factoryMi
|
||||||
&& factoryMi.getExpression() instanceof SimpleName receiverSn) {
|
&& (factoryMi.getExpression() instanceof SimpleName || factoryMi.getExpression() instanceof QualifiedName)) {
|
||||||
// e.g. "mapper.toMsg(dto)" → find the CIC returned by the factory method
|
// e.g. "mapper.toMsg(dto)" or "com.example.Factory.toMsg()"
|
||||||
String receiverType = resolveReceiverTypeFallback(receiverSn);
|
if (factoryMi.getExpression() instanceof SimpleName receiverSn) {
|
||||||
|
receiverType = resolveReceiverTypeFallback(receiverSn);
|
||||||
|
} else if (factoryMi.getExpression() instanceof QualifiedName qn) {
|
||||||
|
receiverType = qn.getFullyQualifiedName();
|
||||||
|
}
|
||||||
if (receiverType != null) {
|
if (receiverType != null) {
|
||||||
cic = findReturnedCIC(receiverType, factoryMi.getName().getIdentifier(), context);
|
cic = findReturnedCIC(receiverType, factoryMi.getName().getIdentifier(), context);
|
||||||
}
|
}
|
||||||
@@ -392,7 +402,22 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
if (varTypeTd == null) return null;
|
if (varTypeTd == null) return null;
|
||||||
|
|
||||||
// Evaluate the getter body with field values substituted from the CIC's constructor args
|
// Evaluate the getter body with field values substituted from the CIC's constructor args
|
||||||
java.util.Map<String, String> fieldValues = buildFieldValuesFromCIC(varTypeTd, cic, context, this::evaluateMethodCallInConstructor);
|
java.util.Map<String, String> initialLocals = new java.util.HashMap<>();
|
||||||
|
if (initExpr[0] instanceof MethodInvocation factoryMi) {
|
||||||
|
MethodDeclaration md = context.findMethodDeclaration(context.getTypeDeclaration(receiverType != null ? receiverType : varTypeName[0]), factoryMi.getName().getIdentifier(), true);
|
||||||
|
if (md != null) {
|
||||||
|
for (int i = 0; i < md.parameters().size() && i < factoryMi.arguments().size(); i++) {
|
||||||
|
SingleVariableDeclaration param = (SingleVariableDeclaration) md.parameters().get(i);
|
||||||
|
Expression arg = (Expression) factoryMi.arguments().get(i);
|
||||||
|
String resolvedVal = constantResolver.resolve(arg, context);
|
||||||
|
if (resolvedVal != null) {
|
||||||
|
initialLocals.put(param.getName().getIdentifier(), resolvedVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java.util.Map<String, String> fieldValues = constructorAnalyzer.buildFieldValuesFromCIC(varTypeTd, cic, context, this::evaluateMethodCallInConstructor, initialLocals);
|
||||||
if (fieldValues.isEmpty()) return null;
|
if (fieldValues.isEmpty()) return null;
|
||||||
|
|
||||||
// Track setter calls on the variable between its initialization and the getter call
|
// Track setter calls on the variable between its initialization and the getter call
|
||||||
|
|||||||
Reference in New Issue
Block a user