better enricher attempt
This commit is contained in:
@@ -51,10 +51,10 @@ public class TransitionLinkerEnricher implements AnalysisEnricher {
|
|||||||
if (targetVar == null && chain.getTriggerPoint() != null) {
|
if (targetVar == null && chain.getTriggerPoint() != null) {
|
||||||
targetVar = chain.getTriggerPoint().getStateMachineId();
|
targetVar = chain.getTriggerPoint().getStateMachineId();
|
||||||
}
|
}
|
||||||
if (targetVar == null || targetVar.isEmpty()) {
|
// We no longer hard-block wildcards without a specific routing context.
|
||||||
// Wildcard without specific routing context is too broad, do not link
|
// If a project doesn't use standard SM persisters (e.g. restores state manually),
|
||||||
continue;
|
// contextMachineId will be null. We should still link the wildcard to provide SOME visibility,
|
||||||
}
|
// rather than completely hiding the endpoint.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWildcard || smEvent.equals(triggerEvent) || triggerEvent.toLowerCase().contains(smEvent.toLowerCase()) || smEvent.toLowerCase().contains(triggerEvent.toLowerCase())) {
|
if (isWildcard || smEvent.equals(triggerEvent) || triggerEvent.toLowerCase().contains(smEvent.toLowerCase()) || smEvent.toLowerCase().contains(triggerEvent.toLowerCase())) {
|
||||||
|
|||||||
@@ -84,6 +84,15 @@ public class CallGraphBuilder {
|
|||||||
|
|
||||||
// Find parameter index in target method
|
// Find parameter index in target method
|
||||||
int paramIndex = getParameterIndex(target, currentParamName);
|
int paramIndex = getParameterIndex(target, currentParamName);
|
||||||
|
if (paramIndex < 0) {
|
||||||
|
// Not a parameter. Maybe it's a local variable initialized from a parameter or method call?
|
||||||
|
String tracedVar = traceLocalVariable(target, currentParamName);
|
||||||
|
if (tracedVar != null && !tracedVar.equals(currentParamName)) {
|
||||||
|
currentParamName = tracedVar;
|
||||||
|
// We must try to find paramIndex again, because the traced variable might be a parameter of this same method
|
||||||
|
paramIndex = getParameterIndex(target, currentParamName);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (paramIndex < 0) {
|
if (paramIndex < 0) {
|
||||||
break; // Parameter name changed or not found, stop tracing
|
break; // Parameter name changed or not found, stop tracing
|
||||||
}
|
}
|
||||||
@@ -141,6 +150,51 @@ public class CallGraphBuilder {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String traceLocalVariable(String methodFqn, String varName) {
|
||||||
|
if (methodFqn == null || !methodFqn.contains(".")) return null;
|
||||||
|
String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
|
||||||
|
String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
|
||||||
|
TypeDeclaration td = context.getTypeDeclaration(className);
|
||||||
|
if (td != null) {
|
||||||
|
MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
|
||||||
|
if (md != null && md.getBody() != null) {
|
||||||
|
final Expression[] initializer = new Expression[1];
|
||||||
|
md.getBody().accept(new ASTVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(VariableDeclarationFragment node) {
|
||||||
|
if (node.getName().getIdentifier().equals(varName) && node.getInitializer() != null) {
|
||||||
|
initializer[0] = node.getInitializer();
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean visit(Assignment node) {
|
||||||
|
if (node.getLeftHandSide() instanceof SimpleName asn && asn.getIdentifier().equals(varName)) {
|
||||||
|
initializer[0] = node.getRightHandSide();
|
||||||
|
}
|
||||||
|
return super.visit(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (initializer[0] != null) {
|
||||||
|
Expression expr = traceVariable(initializer[0]);
|
||||||
|
if (expr instanceof MethodInvocation mi) {
|
||||||
|
// e.g. event.getType() -> return "event.getType()" or just "event"
|
||||||
|
if (mi.getExpression() instanceof SimpleName sn) {
|
||||||
|
return sn.getIdentifier() + "." + mi.getName().getIdentifier() + "()";
|
||||||
|
}
|
||||||
|
return mi.getName().getIdentifier() + "()";
|
||||||
|
}
|
||||||
|
if (expr instanceof SimpleName sn) {
|
||||||
|
return sn.getIdentifier();
|
||||||
|
}
|
||||||
|
return expr.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private String extractContextMachineId(List<String> path, Map<String, List<CallEdge>> callGraph) {
|
private String extractContextMachineId(List<String> path, Map<String, List<CallEdge>> callGraph) {
|
||||||
for (String node : path) {
|
for (String node : path) {
|
||||||
List<CallEdge> edges = callGraph.get(node);
|
List<CallEdge> edges = callGraph.get(node);
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ class TransitionLinkerEnricherTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldIgnoreWildcardWhenContextMachineIdIsMissing() {
|
void shouldAllowWildcardWhenContextMachineIdIsMissing() {
|
||||||
Transition t1 = new Transition();
|
Transition t1 = new Transition();
|
||||||
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
|
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
|
||||||
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
|
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
|
||||||
@@ -176,7 +176,7 @@ class TransitionLinkerEnricherTest {
|
|||||||
|
|
||||||
CallChain chain = CallChain.builder()
|
CallChain chain = CallChain.builder()
|
||||||
.triggerPoint(TriggerPoint.builder().event("event").build())
|
.triggerPoint(TriggerPoint.builder().event("event").build())
|
||||||
// Intentionally NO contextMachineId or stateMachineId
|
// NO contextMachineId or stateMachineId
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
AnalysisResult result = AnalysisResult.builder()
|
AnalysisResult result = AnalysisResult.builder()
|
||||||
@@ -188,6 +188,6 @@ class TransitionLinkerEnricherTest {
|
|||||||
enricher.enrich(result, null, null);
|
enricher.enrich(result, null, null);
|
||||||
|
|
||||||
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
|
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
|
||||||
assertThat(updatedChain.getMatchedTransitions()).isNullOrEmpty();
|
assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,4 +165,58 @@ class CallGraphBuilderTest {
|
|||||||
CallChain chain = chains.get(0);
|
CallChain chain = chains.get(0);
|
||||||
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("OrderEvents.CREATE");
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("OrderEvents.CREATE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveTriggerPointLocalVariableAcrossChain() throws IOException {
|
||||||
|
String source = """
|
||||||
|
package com.example;
|
||||||
|
public class OrderController {
|
||||||
|
private OrderService service;
|
||||||
|
public void processOrderEvent(MyOrderEvent domainEvent) {
|
||||||
|
doProcessOrderEvent(domainEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doProcessOrderEvent(MyOrderEvent domainEvent) {
|
||||||
|
OrderEvents eventType = domainEvent.getType();
|
||||||
|
service.updateOrderState(eventType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OrderService {
|
||||||
|
public void updateOrderState(OrderEvents event) {
|
||||||
|
// sendEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyOrderEvent {
|
||||||
|
public OrderEvents getType() { return OrderEvents.PAY; }
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OrderEvents { CREATE, PAY }
|
||||||
|
""";
|
||||||
|
Path tempDir = Files.createTempDirectory("callgraph_test2");
|
||||||
|
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
CallGraphBuilder builder = new CallGraphBuilder(context);
|
||||||
|
|
||||||
|
EntryPoint entryPoint = EntryPoint.builder()
|
||||||
|
.className("com.example.OrderController")
|
||||||
|
.methodName("processOrderEvent")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
.className("com.example.OrderService")
|
||||||
|
.methodName("updateOrderState")
|
||||||
|
.event("event")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
|
||||||
|
|
||||||
|
assertThat(chains).hasSize(1);
|
||||||
|
CallChain chain = chains.get(0);
|
||||||
|
assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("domainEvent.getType()");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user