update number 2

This commit is contained in:
2026-07-05 14:34:52 +02:00
parent baa33a887c
commit b07b7855a1
11 changed files with 185 additions and 32 deletions

View File

@@ -36,7 +36,7 @@ public class StrictFqnMatchingEngine implements EventMatchingEngine {
List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
for (String pe : polyEvents) {
if (pe.equals(smEventRaw)) {
if (matchEventNames(pe, smEventRaw, triggerPoint.getEventTypeFqn())) {
return true;
}
if (pe.startsWith("<SYMBOLIC: ") && pe.endsWith(".*>")) {
@@ -57,7 +57,32 @@ public class StrictFqnMatchingEngine implements EventMatchingEngine {
return true;
}
return rawTriggerEvent.equals(smEventRaw);
return matchEventNames(rawTriggerEvent, smEventRaw, triggerPoint.getEventTypeFqn());
}
private boolean matchEventNames(String triggerEvent, String smEvent, String eventTypeFqn) {
if (triggerEvent == null || smEvent == null) return false;
if (triggerEvent.equals(smEvent)) return true;
String tConst = triggerEvent.contains(".") ? triggerEvent.substring(triggerEvent.lastIndexOf('.') + 1) : triggerEvent;
String smConst = smEvent.contains(".") ? smEvent.substring(smEvent.lastIndexOf('.') + 1) : smEvent;
if (!tConst.equals(smConst)) return false;
if (eventTypeFqn != null && eventTypeFqn.contains(".") && smEvent.contains(".")) {
String fullTriggerFqn = eventTypeFqn + "." + tConst;
return fullTriggerFqn.equals(smEvent);
}
if (triggerEvent.contains(".") && smEvent.contains(".")) {
String tClass = triggerEvent.substring(0, triggerEvent.lastIndexOf('.'));
String smClass = smEvent.substring(0, smEvent.lastIndexOf('.'));
String tClassSimple = tClass.contains(".") ? tClass.substring(tClass.lastIndexOf('.') + 1) : tClass;
String smClassSimple = smClass.contains(".") ? smClass.substring(smClass.lastIndexOf('.') + 1) : smClass;
return tClassSimple.equals(smClassSimple);
}
return true;
}
private boolean isWildcardVariable(String eventStr) {

View File

@@ -64,6 +64,7 @@ public class TriggerPoint {
}
private static String qualifyEvent(String e, String eventTypeFqn) {
System.out.println("qualifyEvent e=" + e + " eventTypeFqn=" + eventTypeFqn);
if (e == null || eventTypeFqn == null || e.isEmpty()) {
return e;
}
@@ -79,21 +80,23 @@ public class TriggerPoint {
if (!isEnumConstantCandidate(e)) {
return e;
}
String simpleEventType = eventTypeFqn.contains(".") ? eventTypeFqn.substring(eventTypeFqn.lastIndexOf('.') + 1) : eventTypeFqn;
if (e.contains(".")) {
String qualifier = e.substring(0, e.lastIndexOf('.'));
String lastSegment = e.substring(e.lastIndexOf('.') + 1);
if (qualifier.equals(eventTypeFqn)) {
return e;
}
if (eventTypeFqn.endsWith("." + qualifier) || eventTypeFqn.equals(qualifier)) {
return eventTypeFqn + "." + lastSegment;
String simpleQualifier = qualifier.contains(".") ? qualifier.substring(qualifier.lastIndexOf('.') + 1) : qualifier;
if (simpleQualifier.equals(simpleEventType)) {
return simpleEventType + "." + lastSegment;
}
return e;
}
if (eventTypeFqn.startsWith("java.lang.") || eventTypeFqn.equals("int") || eventTypeFqn.equals("long") || eventTypeFqn.equals("char")) {
if (eventTypeFqn.startsWith("java.lang.") || eventTypeFqn.equals("String") || eventTypeFqn.equals("int") || eventTypeFqn.equals("long") || eventTypeFqn.equals("char")) {
return e;
}
return eventTypeFqn + "." + e;
return simpleEventType + "." + e;
}
private static boolean isEnumConstantCandidate(String eventStr) {

View File

@@ -257,6 +257,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
.lineNumber(tp.getLineNumber())
.polymorphicEvents(setterEvents)
.constraint(tp.getConstraint())
.stateTypeFqn(tp.getStateTypeFqn())
.eventTypeFqn(tp.getEventTypeFqn())
.external(tp.isExternal())
.build();
}
}
@@ -278,6 +281,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
.lineNumber(tp.getLineNumber())
.polymorphicEvents(getterEvents)
.constraint(tp.getConstraint())
.stateTypeFqn(tp.getStateTypeFqn())
.eventTypeFqn(tp.getEventTypeFqn())
.external(tp.isExternal())
.build();
}
}
@@ -311,6 +317,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
.lineNumber(tp.getLineNumber())
.polymorphicEvents(polymorphicEvents)
.constraint(tp.getConstraint())
.stateTypeFqn(tp.getStateTypeFqn())
.eventTypeFqn(tp.getEventTypeFqn())
.external(tp.isExternal())
.build();
}
@@ -318,7 +327,6 @@ 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;
@@ -425,7 +433,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
resolvedValue = cic.toString() + "." + methodName + "()";
}
}
System.out.println("Returning early with events: " + polymorphicEvents + " for " + resolvedValue); if (!polymorphicEvents.isEmpty()) {
if (!polymorphicEvents.isEmpty()) {
return TriggerPoint.builder()
.event(resolvedValue)
.className(tp.getClassName())
@@ -437,6 +445,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
.lineNumber(tp.getLineNumber())
.polymorphicEvents(polymorphicEvents)
.constraint(tp.getConstraint())
.stateTypeFqn(tp.getStateTypeFqn())
.eventTypeFqn(tp.getEventTypeFqn())
.external(tp.isExternal())
.build();
}
}
@@ -509,7 +520,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);
System.out.println("RESOLVED VALUES FOR " + methodName + " in " + currentTd.getName() + ": " + values); if (values != null && !values.isEmpty()) {
if (values != null && !values.isEmpty()) {
polymorphicEvents.addAll(values);
}
if (polymorphicEvents.isEmpty() && variableTracer != null) {
@@ -550,7 +561,6 @@ 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;
@@ -688,6 +698,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
polymorphicEvents = polymorphicEvents.stream().distinct().collect(java.util.stream.Collectors.toList());
System.out.println("JSON DEBUG: building final TriggerPoint. resolvedValue=" + resolvedValue + ", event=" + event);
if (!resolvedValue.equals(event) || !polymorphicEvents.isEmpty()) {
return TriggerPoint.builder()
.event(resolvedValue)
@@ -695,8 +706,14 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
.methodName(tp.getMethodName())
.sourceFile(tp.getSourceFile())
.lineNumber(tp.getLineNumber())
.sourceModule(tp.getSourceModule())
.stateMachineId(tp.getStateMachineId())
.sourceState(tp.getSourceState())
.polymorphicEvents(polymorphicEvents)
.constraint(tp.getConstraint())
.stateTypeFqn(tp.getStateTypeFqn())
.eventTypeFqn(tp.getEventTypeFqn())
.external(tp.isExternal())
.build();
}
@@ -789,7 +806,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
val = expr.toString();
}
System.out.println("DEBUG resolveArgument expr=" + expr + ", val=" + val); return postProcessResolvedArgument(expr, val);
return postProcessResolvedArgument(expr, val);
}
protected String postProcessResolvedArgument(Expression originalExpr, String resolvedValue) {

View File

@@ -524,7 +524,24 @@ public class GenericEventDetector {
}
private String[] resolveStateMachineTypeArguments(MethodInvocation node) {
System.out.println("resolveStateMachineTypeArguments CALLED for node: " + node);
Expression receiver = node.getExpression();
System.out.println("initial receiver: " + receiver);
// Trace back the receiver if it's a builder chain
while (receiver instanceof MethodInvocation mi) {
if (mi.getName().getIdentifier().equals("sendEvent")) {
receiver = mi.getExpression();
break;
}
receiver = mi.getExpression();
}
// Also if the node itself is sendEvent, receiver is already correct
if (node.getName().getIdentifier().equals("sendEvent")) {
receiver = node.getExpression();
}
if (receiver == null) return new String[]{null, null};
ITypeBinding binding = receiver.resolveTypeBinding();
@@ -540,16 +557,19 @@ public class GenericEventDetector {
// Fallback: search enclosing class/method for variable declaration
Type declType = findReceiverTypeManually(receiver, node);
System.out.println("findReceiverTypeManually returned: " + declType);
if (declType instanceof ParameterizedType pt) {
List<?> typeArgs = pt.typeArguments();
System.out.println("typeArgs size: " + typeArgs.size());
if (typeArgs.size() >= 2) {
CompilationUnit cu = (CompilationUnit) node.getRoot();
String stateType = resolveTypeToFqn((Type) typeArgs.get(0), cu);
String eventType = resolveTypeToFqn((Type) typeArgs.get(1), cu);
System.out.println("Resolved types: state=" + stateType + " event=" + eventType);
return new String[]{stateType, eventType};
}
}
System.out.println("Returning null, null");
return new String[]{null, null};
}

View File

@@ -167,6 +167,10 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
List<String> allResolved = new ArrayList<>();
if (node.getExpression() == null) {
return Collections.singletonList(baseCalled);
}
if (baseCalled.contains(".")) {
String className = baseCalled.substring(0, baseCalled.lastIndexOf('.'));
String methodName = baseCalled.substring(baseCalled.lastIndexOf('.') + 1);
@@ -180,7 +184,8 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
if (td != null) {
isAbstractOrInterface = td.isInterface() || (td.modifiers() != null && td.modifiers().toString().contains("abstract"));
}
if (!isAbstractOrInterface) {
boolean isImplicitThis = node.getExpression() == null || node.getExpression() instanceof ThisExpression || node.getExpression() instanceof SuperMethodInvocation;
if (!isAbstractOrInterface || isImplicitThis) {
allResolved.add(className + "." + methodName);
}
for (String impl : impls) {
@@ -650,6 +655,12 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
}
}
}
IMethodBinding methodBinding = mi.resolveMethodBinding();
if (methodBinding != null && methodBinding.getReturnType() != null) {
return methodBinding.getReturnType().getErasure().getQualifiedName();
}
return null;
}
}

View File

@@ -459,6 +459,14 @@ public class VariableTracer {
return argValue;
}
String tracedLocal = traceLocalVariable(callerMethod, argValue);
if (tracedLocal != null && !tracedLocal.equals(argValue)) {
if (tracedLocal.contains(".") || tracedLocal.startsWith("new ") || tracedLocal.matches(".*[0-9].*") || tracedLocal.contains("(")) {
return tracedLocal;
}
argValue = tracedLocal;
}
int callerIndex = path.indexOf(callerMethod);
if (callerIndex <= 0) return argValue;

View File

@@ -17,11 +17,11 @@ public class SpringContextScanner extends ASTVisitor {
ITypeBinding typeBinding = node.resolveBinding();
if (typeBinding == null) return true;
if (isSpringComponent(typeBinding)) {
if (isSpringComponent(typeBinding, node)) {
SpringBean bean = SpringBean.builder()
.typeFqn(typeBinding.getQualifiedName())
.assignableTypes(getAssignableTypes(typeBinding))
.isPrimary(hasAnnotation(typeBinding, "org.springframework.context.annotation.Primary"))
.isPrimary(hasPrimary(typeBinding, node))
.qualifiers(extractQualifiers(typeBinding))
.order(extractOrder(typeBinding))
.declaringClassFqn(typeBinding.getQualifiedName())
@@ -73,7 +73,7 @@ public class SpringContextScanner extends ASTVisitor {
SpringBean bean = SpringBean.builder()
.typeFqn(concreteType[0])
.assignableTypes(assignables)
.isPrimary(hasAnnotation(methodBinding, "org.springframework.context.annotation.Primary"))
.isPrimary(hasPrimaryMethod(methodBinding, node))
.qualifiers(extractQualifiers(methodBinding))
.order(extractOrder(methodBinding))
.declaringClassFqn(methodBinding.getDeclaringClass().getQualifiedName())
@@ -97,11 +97,50 @@ public class SpringContextScanner extends ASTVisitor {
return true;
}
private boolean isSpringComponent(ITypeBinding binding) {
return hasMetaAnnotation(binding, "org.springframework.stereotype.Component") ||
hasMetaAnnotation(binding, "org.springframework.web.bind.annotation.RestController");
private boolean isSpringComponent(ITypeBinding binding, TypeDeclaration node) {
if (hasMetaAnnotation(binding, "org.springframework.stereotype.Component") ||
hasMetaAnnotation(binding, "org.springframework.web.bind.annotation.RestController") ||
hasAnnotation(binding, "org.springframework.stereotype.Component") ||
hasAnnotation(binding, "org.springframework.stereotype.Service") ||
hasAnnotation(binding, "org.springframework.stereotype.Repository") ||
hasAnnotation(binding, "org.springframework.stereotype.Controller") ||
hasAnnotation(binding, "org.springframework.web.bind.annotation.RestController") ||
hasAnnotation(binding, "org.springframework.context.annotation.Configuration")) {
return true;
}
for (Object modObj : node.modifiers()) {
if (modObj instanceof Annotation ann) {
String name = ann.getTypeName().getFullyQualifiedName();
if (name.equals("Component") || name.equals("Service") || name.equals("Repository") ||
name.equals("Controller") || name.equals("RestController") || name.equals("Configuration")) {
return true;
}
}
}
return false;
}
private boolean hasPrimary(ITypeBinding binding, TypeDeclaration node) {
if (hasAnnotation(binding, "org.springframework.context.annotation.Primary")) return true;
for (Object modObj : node.modifiers()) {
if (modObj instanceof Annotation ann) {
String name = ann.getTypeName().getFullyQualifiedName();
if (name.equals("Primary")) return true;
}
}
return false;
}
private boolean hasPrimaryMethod(IMethodBinding binding, MethodDeclaration node) {
if (hasAnnotation(binding, "org.springframework.context.annotation.Primary")) return true;
for (Object modObj : node.modifiers()) {
if (modObj instanceof Annotation ann) {
String name = ann.getTypeName().getFullyQualifiedName();
if (name.equals("Primary")) return true;
}
}
return false;
}
private boolean hasMetaAnnotation(ITypeBinding binding, String targetFqn) {
return checkMetaAnnotation(binding.getAnnotations(), targetFqn, new HashSet<>());
}
@@ -202,9 +241,15 @@ public class SpringContextScanner extends ASTVisitor {
private String extractStereotypeValue(ITypeBinding binding) {
for (IAnnotationBinding ann : binding.getAnnotations()) {
// Check if annotation itself is meta-annotated with @Component
if (ann.getAnnotationType() == null) continue;
String fqn = ann.getAnnotationType().getQualifiedName();
if (checkMetaAnnotation(new IAnnotationBinding[]{ann}, "org.springframework.stereotype.Component", new HashSet<>()) ||
"org.springframework.stereotype.Component".equals(ann.getAnnotationType().getQualifiedName())) {
"org.springframework.stereotype.Component".equals(fqn) ||
"org.springframework.stereotype.Service".equals(fqn) ||
"org.springframework.stereotype.Repository".equals(fqn) ||
"org.springframework.stereotype.Controller".equals(fqn) ||
"org.springframework.web.bind.annotation.RestController".equals(fqn) ||
"org.springframework.context.annotation.Configuration".equals(fqn)) {
for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
if ("value".equals(pair.getName()) && pair.getValue() instanceof String) {
return (String) pair.getValue();

View File

@@ -291,7 +291,6 @@ public class CodebaseContext {
}
Set<String> allImpls = new HashSet<>();
collectImplementations(cleanName, allImpls, new HashSet<>());
System.out.println("GET IMPLEMENTATIONS FOR: " + interfaceName + " -> " + allImpls);
return new ArrayList<>(allImpls);
}