From 131ccbeda00f13653d4ce9c29cd645f7bcb20dc0 Mon Sep 17 00:00:00 2001 From: Kamil Patryk Kozakowski Date: Mon, 22 Jun 2026 18:51:20 +0200 Subject: [PATCH] forward analysis 2 --- .../analysis/resolver/ConstantResolver.java | 66 +++++++++++++++++-- .../resolver/ConstantResolverTest.java | 30 +++++++++ 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java index b86e233..355bf9c 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolver.java @@ -64,6 +64,9 @@ public class ConstantResolver { String val = resolveManual(qn, context, visited); return val != null ? val : qn.toString(); } + if (expr instanceof FieldAccess fa) { + return resolveManual(fa.getName(), context, visited); + } if (expr instanceof SimpleName sn) { return resolveManual(sn, context, visited); } @@ -129,25 +132,50 @@ public class ConstantResolver { private String evaluateMethodOutput(MethodInvocation mi, MethodDeclaration md, CodebaseContext context, Set visited) { if (md.getBody() == null || mi.arguments().size() != md.parameters().size()) return null; - java.util.Map paramValues = new java.util.HashMap<>(); + java.util.Map localVars = new java.util.HashMap<>(); for (int i = 0; i < mi.arguments().size(); i++) { Expression arg = (Expression) mi.arguments().get(i); String val = resolveInternal(arg, context, visited); if (val != null) { org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) md.parameters().get(i); - paramValues.put(param.getName().getIdentifier(), val); + localVars.put(param.getName().getIdentifier(), val); } } - if (paramValues.isEmpty()) return null; + if (localVars.isEmpty()) return null; for (Object stmtObj : md.getBody().statements()) { - if (stmtObj instanceof org.eclipse.jdt.core.dom.SwitchStatement ss) { - String result = evaluateSwitchStatement(ss, paramValues, context, visited); + if (stmtObj instanceof org.eclipse.jdt.core.dom.ExpressionStatement es) { + if (es.getExpression() instanceof Assignment assignment) { + Expression lhs = assignment.getLeftHandSide(); + String varName = null; + if (lhs instanceof SimpleName sn) { + varName = sn.getIdentifier(); + } else if (lhs instanceof FieldAccess fa) { + varName = "this." + fa.getName().getIdentifier(); + } + + String rhsVal = resolveExpressionWithParams(assignment.getRightHandSide(), localVars); + if (rhsVal == null) rhsVal = resolveInternal(assignment.getRightHandSide(), context, visited); + + if (varName != null && rhsVal != null) { + localVars.put(varName, rhsVal); + if (varName.startsWith("this.")) { + localVars.put(varName.substring(5), rhsVal); // alias without 'this.' + } else { + localVars.put("this." + varName, rhsVal); // alias with 'this.' + } + } + } + } else if (stmtObj instanceof org.eclipse.jdt.core.dom.SwitchStatement ss) { + String result = evaluateSwitchStatement(ss, localVars, context, visited); if (result != null) return result; } else if (stmtObj instanceof org.eclipse.jdt.core.dom.ReturnStatement rs) { if (rs.getExpression() instanceof org.eclipse.jdt.core.dom.SwitchExpression se) { - String result = evaluateSwitchExpression(se, paramValues, context, visited); + String result = evaluateSwitchExpression(se, localVars, context, visited); + if (result != null) return result; + } else { + String result = resolveExpressionWithParams(rs.getExpression(), localVars); if (result != null) return result; } } @@ -224,6 +252,10 @@ public class ConstantResolver { String name = sn.getIdentifier(); if (paramValues.containsKey(name)) return paramValues.get(name); return name; + } else if (expr instanceof FieldAccess fa) { + String name = "this." + fa.getName().getIdentifier(); + if (paramValues.containsKey(name)) return paramValues.get(name); + return null; } else if (expr instanceof QualifiedName qn) { return qn.getName().getIdentifier(); } else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) { @@ -311,6 +343,28 @@ public class ConstantResolver { } } } + + // Check constructors for assignments + for (MethodDeclaration md : td.getMethods()) { + if (md.isConstructor() && md.getBody() != null) { + for (Object stmtObj : md.getBody().statements()) { + if (stmtObj instanceof ExpressionStatement es) { + if (es.getExpression() instanceof Assignment assignment) { + Expression lhs = assignment.getLeftHandSide(); + boolean matches = false; + if (lhs instanceof SimpleName snLhs && snLhs.getIdentifier().equals(fieldName)) { + matches = true; + } else if (lhs instanceof FieldAccess fa && fa.getName().getIdentifier().equals(fieldName) && fa.getExpression() instanceof ThisExpression) { + matches = true; + } + if (matches) { + return resolveInternal(assignment.getRightHandSide(), context, visited); + } + } + } + } + } + } } finally { visited.remove(fieldId); } diff --git a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolverTest.java b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolverTest.java index 31f6f5f..3e9da56 100644 --- a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolverTest.java +++ b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/resolver/ConstantResolverTest.java @@ -152,4 +152,34 @@ public class ConstantResolverTest { String result = resolver.resolve(returnExpr, context); assertThat(result).isEqualTo("${app.path}"); } + + @Test + void testConstructorAssignmentAndFieldAccess(@TempDir Path tempDir) throws IOException { + Path dir = tempDir.resolve("com/example"); + Files.createDirectories(dir); + Files.writeString(dir.resolve("EventDto.java"), + "package com.example;\n" + + "public class EventDto {\n" + + " private String eventType;\n" + + " public EventDto() {\n" + + " this.eventType = \"PAYMENT_SUCCESS\";\n" + + " }\n" + + " public String getEventType() {\n" + + " return this.eventType;\n" + + " }\n" + + "}"); + + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + + TypeDeclaration td = context.getTypeDeclaration("com.example.EventDto"); + MethodDeclaration method = td.getMethods()[1]; // getEventType + ReturnStatement rs = (ReturnStatement) method.getBody().statements().get(0); + Expression returnExpr = rs.getExpression(); // this.eventType + + ConstantResolver resolver = new ConstantResolver(); + String result = resolver.resolve(returnExpr, context); + + assertThat(result).isEqualTo("PAYMENT_SUCCESS"); + } }