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