diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java index be76a3e..532625b 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/AbstractCallGraphEngine.java @@ -1054,6 +1054,15 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { */ protected org.eclipse.jdt.core.dom.ClassInstanceCreation findReturnedCIC( String className, String methodName, CodebaseContext context) { + return findReturnedCIC(className, methodName, context, new java.util.HashSet<>(), 0); + } + + private org.eclipse.jdt.core.dom.ClassInstanceCreation findReturnedCIC( + String className, String methodName, CodebaseContext context, + java.util.Set visited, int depth) { + if (depth > 50 || !visited.add(className + "#" + methodName)) { + return null; + } org.eclipse.jdt.core.dom.TypeDeclaration td = context.getTypeDeclaration(className); if (td != null) { @@ -1076,7 +1085,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { // Recurse into implementations (handles interfaces / abstract classes) java.util.List impls = context.getImplementations(className); for (String impl : impls) { - org.eclipse.jdt.core.dom.ClassInstanceCreation cic = findReturnedCIC(impl, methodName, context); + org.eclipse.jdt.core.dom.ClassInstanceCreation cic = findReturnedCIC(impl, methodName, context, visited, depth + 1); if (cic != null) return cic; } return null; @@ -1113,11 +1122,20 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { context.findMethodDeclaration(td, getterName, true); if (getter == null || getter.getBody() == null) return java.util.Collections.emptyList(); - java.util.Map fieldValues = buildFieldValuesFromCIC(td, cic, context); - if (fieldValues.isEmpty()) return java.util.Collections.emptyList(); + String getterKey = typeName + "#" + getterName; + if (!visited.add(getterKey)) { + return java.util.Collections.emptyList(); // Cycle detected + } - String result = constantResolver.evaluateMethodBodyWithLocals(getter, fieldValues, context, visited); - return result != null ? java.util.List.of(result) : java.util.Collections.emptyList(); + try { + java.util.Map fieldValues = buildFieldValuesFromCIC(td, cic, context); + if (fieldValues.isEmpty()) return java.util.Collections.emptyList(); + + String result = constantResolver.evaluateMethodBodyWithLocals(getter, fieldValues, context, visited); + return result != null ? java.util.List.of(result) : java.util.Collections.emptyList(); + } finally { + visited.remove(getterKey); + } } private int findConstructorArgumentIndexForLombokField(TypeDeclaration td, String fieldName) { @@ -1594,7 +1612,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { String calledName = mi.getName().getIdentifier(); String targetClass = context.getFqn(td); CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null; - results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, new HashSet<>(visited), cu)); + results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, visited, cu)); } else { String val = constantResolver.resolve(tracedRight, context); if (val != null) results.add(val); @@ -1630,7 +1648,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine { } CompilationUnit cu = td.getRoot() instanceof CompilationUnit ? (CompilationUnit) td.getRoot() : null; - results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, new HashSet<>(visited), cu)); + results.addAll(resolveMethodReturnConstant(targetClass, calledName, 0, visited, cu)); } else { String val = constantResolver.resolve(tracedRight, context); if (val != null) results.add(val);