fix on fix
This commit is contained in:
@@ -65,7 +65,16 @@ public class StrictFqnMatchingEngine implements EventMatchingEngine {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (polyEvents.isEmpty() && isWildcardVariable(rawTriggerEvent)) {
|
||||
if (polyEvents.isEmpty() && isDynamicVariable(rawTriggerEvent)) {
|
||||
if (triggerPoint.getEventTypeFqn() != null) {
|
||||
if (smEventRaw.startsWith(triggerPoint.getEventTypeFqn() + ".")) {
|
||||
return true;
|
||||
}
|
||||
if (isStringTypeOrPrimitive(triggerPoint.getEventTypeFqn()) && !smEventRaw.contains(".")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -113,19 +122,29 @@ public class StrictFqnMatchingEngine implements EventMatchingEngine {
|
||||
typeFqn.equals("int") || typeFqn.equals("long") || typeFqn.equals("char");
|
||||
}
|
||||
|
||||
private boolean isWildcardVariable(String eventStr) {
|
||||
if (eventStr == null) return false;
|
||||
private boolean isDynamicVariable(String eventStr) {
|
||||
if (eventStr == null || eventStr.isEmpty()) return false;
|
||||
|
||||
if (eventStr.equals("event") || eventStr.equals("e") ||
|
||||
eventStr.equals("msg") || eventStr.equals("message") ||
|
||||
eventStr.equals("payload")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eventStr.contains(" ? ") && eventStr.contains(" : ")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eventStr.endsWith("()")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eventStr.contains(".")) {
|
||||
String firstPart = eventStr.substring(0, eventStr.indexOf('.'));
|
||||
if (!firstPart.isEmpty() && Character.isLowerCase(firstPart.charAt(0))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Character.isLowerCase(eventStr.charAt(0))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,18 +54,6 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
||||
List<String> startMethods = new ArrayList<>();
|
||||
startMethods.add(startMethod);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration(ep.getClassName());
|
||||
boolean isAbstractOrInterface = false;
|
||||
if (td != null) {
|
||||
isAbstractOrInterface = td.isInterface() || (td.modifiers() != null && td.modifiers().toString().contains("abstract"));
|
||||
}
|
||||
if (isAbstractOrInterface) {
|
||||
List<String> impls = context.getImplementations(ep.getClassName());
|
||||
for (String impl : impls) {
|
||||
startMethods.add(impl + "." + ep.getMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
boolean foundAny = false;
|
||||
for (TriggerPoint tp : triggers) {
|
||||
String targetMethod = tp.getClassName() + "." + tp.getMethodName();
|
||||
@@ -1434,24 +1422,23 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
||||
boolean shouldExpand = true;
|
||||
if (impls.isEmpty()) {
|
||||
shouldExpand = false;
|
||||
} else if (td == null) {
|
||||
// Do not polymorphically expand external generic types (e.g. java.lang.Runnable, Spring Action)
|
||||
// across the entire codebase. This prevents massive false-positive tangling.
|
||||
shouldExpand = false;
|
||||
} else {
|
||||
if (td == null && impls.size() > 3) {
|
||||
shouldExpand = false;
|
||||
}
|
||||
if (impls.size() > 20) {
|
||||
shouldExpand = false;
|
||||
}
|
||||
boolean isImplicitThis = node.getExpression() instanceof ThisExpression;
|
||||
if (td != null && !td.isInterface() && !Modifier.isAbstract(td.getModifiers()) && isImplicitThis) {
|
||||
if (!td.isInterface() && !Modifier.isAbstract(td.getModifiers()) && isImplicitThis) {
|
||||
shouldExpand = false;
|
||||
}
|
||||
}
|
||||
|
||||
allResolved.add(baseCalled);
|
||||
if (shouldExpand) {
|
||||
for (String impl : impls) {
|
||||
allResolved.add(impl + "." + methodName);
|
||||
}
|
||||
} else {
|
||||
allResolved.add(baseCalled);
|
||||
}
|
||||
} else {
|
||||
allResolved.add(baseCalled);
|
||||
|
||||
@@ -77,8 +77,54 @@ public class CallGraphPathFinder {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle inheritance fallback for concrete class methods not in the graph
|
||||
if (result.isEmpty() && start.contains(".")) {
|
||||
String className = start.substring(0, start.lastIndexOf('.'));
|
||||
String methodName = start.substring(start.lastIndexOf('.') + 1);
|
||||
if (className.contains("<")) {
|
||||
className = className.substring(0, className.indexOf('<'));
|
||||
}
|
||||
TypeDeclaration td = context.getTypeDeclaration(className);
|
||||
if (td != null) {
|
||||
String superclass = context.getSuperclassFqn(td);
|
||||
if (superclass != null) {
|
||||
String superMethod = superclass + "." + methodName;
|
||||
if (graph.containsKey(superMethod)) {
|
||||
List<List<String>> superPaths = findAllPaths(superMethod, target, graph, visited);
|
||||
for (List<String> superPath : superPaths) {
|
||||
List<String> path = new ArrayList<>();
|
||||
path.add(start);
|
||||
path.addAll(superPath);
|
||||
result.add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle implementation fallback for interfaces/superclasses (interface -> concrete class)
|
||||
if (result.isEmpty() && start.contains(".")) {
|
||||
String className = start.substring(0, start.lastIndexOf('.'));
|
||||
String methodName = start.substring(start.lastIndexOf('.') + 1);
|
||||
if (className.contains("<")) {
|
||||
className = className.substring(0, className.indexOf('<'));
|
||||
}
|
||||
List<String> impls = context.getImplementations(className);
|
||||
if (impls != null) {
|
||||
for (String impl : impls) {
|
||||
String implMethod = impl + "." + methodName;
|
||||
if (graph.containsKey(implMethod)) {
|
||||
List<List<String>> implPaths = findAllPaths(implMethod, target, graph, visited);
|
||||
for (List<String> implPath : implPaths) {
|
||||
List<String> path = new ArrayList<>();
|
||||
path.add(start);
|
||||
path.addAll(implPath);
|
||||
result.add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visited.remove(start);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
||||
if (receiver instanceof MethodInvocation miReceiver) {
|
||||
String returnType = resolveMethodInvocationReturnType(miReceiver);
|
||||
if (returnType != null) {
|
||||
return resolveToDeclaringMethod(returnType, methodName);
|
||||
return returnType + "." + methodName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
||||
String concreteFqn = injectionAnalyzer.resolveInjectedBeanFqn(varBinding);
|
||||
if (concreteFqn != null) {
|
||||
System.out.println(" -> RESOLVED CONCRETE FQN: " + concreteFqn);
|
||||
return resolveToDeclaringMethod(concreteFqn, methodName);
|
||||
return concreteFqn + "." + methodName;
|
||||
} else {
|
||||
System.out.println(" -> RESOLVE FAILED, RETURNED NULL");
|
||||
}
|
||||
@@ -223,7 +223,7 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
||||
}
|
||||
|
||||
if (typeName != null && !typeName.isEmpty()) {
|
||||
return resolveToDeclaringMethod(typeName, methodName);
|
||||
return typeName + "." + methodName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
||||
if (receiver instanceof SimpleName sn) {
|
||||
String fallbackTypeFqn = resolveReceiverTypeFallback(sn);
|
||||
if (fallbackTypeFqn != null) {
|
||||
return resolveToDeclaringMethod(fallbackTypeFqn, methodName);
|
||||
return fallbackTypeFqn + "." + methodName;
|
||||
}
|
||||
String receiverName = sn.getIdentifier();
|
||||
return receiverName + "." + methodName;
|
||||
@@ -246,21 +246,6 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String resolveToDeclaringMethod(String typeName, String methodName) {
|
||||
if (typeName == null || typeName.isEmpty()) return null;
|
||||
TypeDeclaration td = context.getTypeDeclaration(typeName);
|
||||
if (td != null) {
|
||||
MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
|
||||
if (md != null) {
|
||||
TypeDeclaration declaringTd = findEnclosingType(md);
|
||||
if (declaringTd != null) {
|
||||
return context.getFqn(declaringTd) + "." + methodName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return typeName + "." + methodName;
|
||||
}
|
||||
|
||||
private String resolveReceiverTypeFallback(SimpleName receiverNameNode) {
|
||||
String varName = receiverNameNode.getIdentifier();
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ class TransitionLinkerEnricherTest {
|
||||
enricher.enrich(result, null, null);
|
||||
|
||||
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
|
||||
assertThat(updatedChain.getMatchedTransitions()).isNull();
|
||||
assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -162,4 +162,48 @@ class StrictFqnMatchingEngineTest {
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchDynamicVariableWithMatchingTypeFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder()
|
||||
.event("myCustomEvt") // Not "event", "e", etc., but a custom variable name
|
||||
.eventTypeFqn("com.example.OrderEvents")
|
||||
.build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotMatchDynamicVariableWithMismatchedTypeFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder()
|
||||
.event("myCustomEvt")
|
||||
.eventTypeFqn("com.example.InvoiceEvents") // Mismatched type
|
||||
.build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchDynamicVariableWithUnknownTypeFqn() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder()
|
||||
.event("myCustomEvt")
|
||||
.eventTypeFqn(null) // Unknown type, fallback to true
|
||||
.build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMatchDynamicMethodCallAsVariable() {
|
||||
Event smEvent = Event.of("PAY", "com.example.OrderEvents.PAY");
|
||||
TriggerPoint triggerPoint = TriggerPoint.builder()
|
||||
.event("event.getPayload()") // Dynamic method call
|
||||
.eventTypeFqn("com.example.OrderEvents")
|
||||
.build();
|
||||
|
||||
assertThat(engine.matches(smEvent, triggerPoint)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,14 +182,14 @@ class EnterpriseBugsTest {
|
||||
public class OrderController implements OrderApi {
|
||||
private OrderService service;
|
||||
public void submit() {
|
||||
service.trigger();
|
||||
service.trigger("someEvent");
|
||||
}
|
||||
}
|
||||
""";
|
||||
String serviceSrc = """
|
||||
package com.example;
|
||||
public class OrderService {
|
||||
public void trigger() {}
|
||||
public void trigger(String EVENT) {}
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user