stage 2 update loop

This commit is contained in:
2026-06-27 19:33:44 +02:00
parent 642f6f8926
commit 8d66af6d24
5 changed files with 365 additions and 14 deletions

View File

@@ -76,13 +76,16 @@ public class CfgBuilder {
}
if (node instanceof DoStatement doStmt) {
List<ControlFlowGraph.CfgNode> bodyExits = buildFlow(doStmt.getBody(), sources, cfg);
ControlFlowGraph.CfgNode condNode = new ControlFlowGraph.CfgNode(doStmt.getExpression(), ControlFlowGraph.CfgNodeType.STATEMENT);
cfg.addNode(condNode);
List<ControlFlowGraph.CfgNode> bodySources = new ArrayList<>(sources);
bodySources.add(condNode);
List<ControlFlowGraph.CfgNode> bodyExits = buildFlow(doStmt.getBody(), bodySources, cfg);
for (ControlFlowGraph.CfgNode exit : bodyExits) {
exit.addSuccessor(condNode);
}
condNode.addSuccessor(condNode); // Loop back
return List.of(condNode);
}

View File

@@ -33,13 +33,57 @@ public class JdtDataFlowModel implements DataFlowModel {
@Override
public List<Expression> getReachingDefinitions(Expression expr) {
return getReachingDefinitions(expr, new HashSet<>());
return getReachingDefinitions(expr, new HashSet<>(), new HashMap<>(), 0);
}
private List<Expression> getReachingDefinitions(Expression expr, Set<ASTNode> visited) {
if (expr == null || !visited.add(expr)) return List.of();
private List<Expression> getReachingDefinitions(
Expression expr,
Set<ASTNode> visited,
Map<IVariableBinding, Expression> paramBindings,
int depth) {
if (expr == null || depth > 50 || !visited.add(expr)) {
return List.of();
}
// 1. Handle Ternary / Conditional Expression
if (expr instanceof ConditionalExpression ce) {
String condVal = resolveValue(ce.getExpression(), context);
if ("true".equals(condVal)) {
List<Expression> resolved = getReachingDefinitions(ce.getThenExpression(), visited, paramBindings, depth + 1);
visited.remove(expr);
return resolved;
} else if ("false".equals(condVal)) {
List<Expression> resolved = getReachingDefinitions(ce.getElseExpression(), visited, paramBindings, depth + 1);
visited.remove(expr);
return resolved;
} else {
visited.remove(expr);
return List.of(ce);
}
}
// 2. Handle Field constant / field initializer propagation
IVariableBinding vb = getVariableBinding(expr);
if (vb != null && vb.isField()) {
Expression fieldInit = findFieldInitializer(vb, expr);
if (fieldInit != null) {
List<Expression> resolved = getReachingDefinitions(fieldInit, visited, paramBindings, depth + 1);
visited.remove(expr);
return resolved;
}
}
// 3. Handle Parameter mapping for SimpleName
if (expr instanceof SimpleName sn) {
IBinding b = sn.resolveBinding();
if (b instanceof IVariableBinding paramVb && paramBindings.containsKey(paramVb)) {
Expression argExpr = paramBindings.get(paramVb);
List<Expression> resolved = getReachingDefinitions(argExpr, visited, paramBindings, depth + 1);
visited.remove(expr);
return resolved;
}
// Fallback: Local Reaching Definitions
MethodDeclaration md = findEnclosingMethod(sn);
ReachingDefinitions rd = getReachingDefinitionsForMethod(md);
if (rd != null) {
@@ -49,7 +93,7 @@ public class JdtDataFlowModel implements DataFlowModel {
for (ASTNode def : defs) {
Expression defExpr = ReachingDefinitions.getDefinitionExpression(def);
if (defExpr != null) {
exprs.addAll(getReachingDefinitions(defExpr, visited));
exprs.addAll(getReachingDefinitions(defExpr, visited, paramBindings, depth + 1));
} else {
exprs.add(expr);
}
@@ -59,11 +103,138 @@ public class JdtDataFlowModel implements DataFlowModel {
}
}
}
// 4. Handle Method Invocations (Static Factory methods / Transforming Getters)
if (expr instanceof MethodInvocation mi) {
IMethodBinding mb = mi.resolveMethodBinding();
if (mb != null) {
MethodDeclaration md = findMethodDeclaration(mb);
if (md != null && md.getBody() != null) {
Map<IVariableBinding, Expression> newParamBindings = new HashMap<>(paramBindings);
List<?> parameters = md.parameters();
List<?> arguments = mi.arguments();
int count = Math.min(parameters.size(), arguments.size());
for (int i = 0; i < count; i++) {
SingleVariableDeclaration svd = (SingleVariableDeclaration) parameters.get(i);
IVariableBinding paramVb = svd.resolveBinding();
if (paramVb != null) {
newParamBindings.put(paramVb, (Expression) arguments.get(i));
}
}
List<ReturnStatement> returns = findReturnStatements(md.getBody());
if (!returns.isEmpty()) {
List<Expression> results = new ArrayList<>();
for (ReturnStatement rs : returns) {
if (rs.getExpression() != null) {
results.addAll(getReachingDefinitions(rs.getExpression(), visited, newParamBindings, depth + 1));
}
}
visited.remove(expr);
return results;
}
}
}
}
visited.remove(expr);
return List.of(expr);
}
private IVariableBinding getVariableBinding(Expression expr) {
if (expr instanceof SimpleName sn) {
IBinding b = sn.resolveBinding();
if (b instanceof IVariableBinding varBinding) return varBinding;
} else if (expr instanceof QualifiedName qn) {
IBinding b = qn.resolveBinding();
if (b instanceof IVariableBinding varBinding) return varBinding;
} else if (expr instanceof FieldAccess fa) {
return fa.resolveFieldBinding();
} else if (expr instanceof SuperFieldAccess sfa) {
return sfa.resolveFieldBinding();
}
return null;
}
private Expression findFieldInitializer(IVariableBinding vb, ASTNode contextNode) {
if (vb == null) return null;
ITypeBinding declaringClass = vb.getDeclaringClass();
if (declaringClass == null) return null;
String classFqn = declaringClass.getErasure().getQualifiedName();
if (classFqn == null || classFqn.isEmpty()) return null;
TypeDeclaration td = context.getTypeDeclaration(classFqn);
if (td == null) {
CompilationUnit cu = null;
ASTNode root = contextNode.getRoot();
if (root instanceof CompilationUnit) {
cu = (CompilationUnit) root;
}
td = context.getTypeDeclaration(classFqn, cu);
}
if (td != null) {
for (FieldDeclaration fd : td.getFields()) {
for (Object fragObj : fd.fragments()) {
if (fragObj instanceof VariableDeclarationFragment fragment) {
if (fragment.getName().getIdentifier().equals(vb.getName())) {
return fragment.getInitializer();
}
}
}
}
}
return null;
}
private MethodDeclaration findMethodDeclaration(IMethodBinding mb) {
if (mb == null) return null;
ITypeBinding declaringClass = mb.getDeclaringClass();
if (declaringClass == null) return null;
String classFqn = declaringClass.getErasure().getQualifiedName();
if (classFqn == null || classFqn.isEmpty()) return null;
TypeDeclaration td = context.getTypeDeclaration(classFqn);
if (td != null) {
for (MethodDeclaration md : td.getMethods()) {
IMethodBinding candidateMb = md.resolveBinding();
if (candidateMb != null && candidateMb.getKey().equals(mb.getKey())) {
return md;
}
}
for (MethodDeclaration md : td.getMethods()) {
if (md.getName().getIdentifier().equals(mb.getName()) && md.parameters().size() == mb.getParameterTypes().length) {
return md;
}
}
}
return null;
}
private List<ReturnStatement> findReturnStatements(ASTNode node) {
List<ReturnStatement> returns = new ArrayList<>();
node.accept(new ASTVisitor() {
@Override
public boolean visit(ReturnStatement rs) {
returns.add(rs);
return super.visit(rs);
}
@Override
public boolean visit(LambdaExpression le) {
return false;
}
@Override
public boolean visit(AnonymousClassDeclaration acd) {
return false;
}
@Override
public boolean visit(TypeDeclarationStatement tds) {
return false;
}
});
return returns;
}
@Override
public String resolveValue(Expression expr, CodebaseContext context) {
if (expr == null) return null;

View File

@@ -38,7 +38,10 @@ public class ReachingDefinitions {
}
boolean changed = true;
while (changed) {
int iterations = 0;
int maxIterations = 5000;
while (changed && iterations < maxIterations) {
iterations++;
changed = false;
for (ControlFlowGraph.CfgNode node : cfgNodes) {
Set<ASTNode> newIn = new HashSet<>();
@@ -67,6 +70,10 @@ public class ReachingDefinitions {
}
}
}
if (iterations >= maxIterations) {
// Log warning or print to stderr
System.err.println("Warning: ReachingDefinitions analysis reached max iterations (" + maxIterations + ") and terminated early.");
}
}
public Set<ASTNode> getReachingDefinitions(ASTNode usageNode, String varName) {