stage 2 update loop
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user