te21
This commit is contained in:
@@ -1652,6 +1652,81 @@ import java.util.*;
|
|||||||
return eVal.contains(".") ? eVal.substring(eVal.lastIndexOf('.', eVal.lastIndexOf('.') - 1) + 1) : eVal;
|
return eVal.contains(".") ? eVal.substring(eVal.lastIndexOf('.', eVal.lastIndexOf('.') - 1) + 1) : eVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a single argument expression to a string value.
|
||||||
|
* Handles lambdas, method references, and constructor argument unwrapping.
|
||||||
|
* Subclasses can override {@link #postProcessResolvedArgument(Expression, String)} for custom behavior.
|
||||||
|
*/
|
||||||
|
protected String resolveArgument(org.eclipse.jdt.core.dom.Expression expr) {
|
||||||
|
// Extract from lambda
|
||||||
|
if (expr instanceof org.eclipse.jdt.core.dom.LambdaExpression le) {
|
||||||
|
org.eclipse.jdt.core.dom.ASTNode body = le.getBody();
|
||||||
|
if (body instanceof org.eclipse.jdt.core.dom.Expression bodyExpr) {
|
||||||
|
expr = bodyExpr;
|
||||||
|
} else if (body instanceof org.eclipse.jdt.core.dom.Block block) {
|
||||||
|
for (Object stmtObj : block.statements()) {
|
||||||
|
if (stmtObj instanceof org.eclipse.jdt.core.dom.ReturnStatement rs && rs.getExpression() != null) {
|
||||||
|
expr = rs.getExpression();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract from method reference
|
||||||
|
if (expr instanceof org.eclipse.jdt.core.dom.ExpressionMethodReference emr) {
|
||||||
|
org.eclipse.jdt.core.dom.TypeDeclaration td = findEnclosingType(emr);
|
||||||
|
if (td != null) {
|
||||||
|
org.eclipse.jdt.core.dom.MethodDeclaration md = context.findMethodDeclaration(td, emr.getName().getIdentifier(), true);
|
||||||
|
if (md != null && md.getBody() != null) {
|
||||||
|
for (Object stmtObj : md.getBody().statements()) {
|
||||||
|
if (stmtObj instanceof org.eclipse.jdt.core.dom.ReturnStatement rs && rs.getExpression() != null) {
|
||||||
|
expr = rs.getExpression();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap constructor first argument if it's a constant (e.g., new CustomMessage(OrderEvent.PROCESS))
|
||||||
|
if (expr instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation cic && !cic.arguments().isEmpty()) {
|
||||||
|
org.eclipse.jdt.core.dom.Expression firstArg = (org.eclipse.jdt.core.dom.Expression) cic.arguments().get(0);
|
||||||
|
String resolved = constantResolver.resolve(firstArg, context);
|
||||||
|
if (resolved != null) {
|
||||||
|
expr = firstArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String val = constantResolver.resolve(expr, context);
|
||||||
|
if (val == null) {
|
||||||
|
val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow subclasses to post-process (e.g., preserve getters for later substitution)
|
||||||
|
return postProcessResolvedArgument(expr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for subclasses to customize argument resolution.
|
||||||
|
* Default implementation returns the value as-is.
|
||||||
|
*/
|
||||||
|
protected String postProcessResolvedArgument(org.eclipse.jdt.core.dom.Expression originalExpr, String resolvedValue) {
|
||||||
|
return resolvedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a list of argument expressions.
|
||||||
|
* Default implementation calls {@link #resolveArgument(Expression)} for each.
|
||||||
|
*/
|
||||||
|
protected List<String> resolveArguments(List<?> astArguments) {
|
||||||
|
List<String> args = new ArrayList<>();
|
||||||
|
for (Object argObj : astArguments) {
|
||||||
|
args.add(resolveArgument((org.eclipse.jdt.core.dom.Expression) argObj));
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract Map<String, List<CallEdge>> buildCallGraph();
|
protected abstract Map<String, List<CallEdge>> buildCallGraph();
|
||||||
protected abstract String resolveCalledMethod(MethodInvocation node);
|
protected abstract String resolveCalledMethod(MethodInvocation node);
|
||||||
protected List<String> findPath(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited) {
|
protected List<String> findPath(String start, String target, Map<String, List<CallEdge>> graph, Set<String> visited) {
|
||||||
|
|||||||
@@ -122,80 +122,31 @@ public class HeuristicCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> resolveArguments(List<?> astArguments) {
|
@Override
|
||||||
List<String> args = new ArrayList<>();
|
protected String postProcessResolvedArgument(org.eclipse.jdt.core.dom.Expression originalExpr, String resolvedValue) {
|
||||||
for (Object argObj : astArguments) {
|
// Preserve getter calls on 'this' or 'super' for later resolution in trigger parameter tracing
|
||||||
Expression expr = (Expression) argObj;
|
// where we can substitute the actual receiver from the call edge
|
||||||
|
boolean isThisOrSuperGetter = originalExpr instanceof org.eclipse.jdt.core.dom.MethodInvocation mi
|
||||||
// Extract from lambda
|
&& (mi.getExpression() instanceof org.eclipse.jdt.core.dom.ThisExpression || mi.getExpression() instanceof org.eclipse.jdt.core.dom.SuperMethodInvocation)
|
||||||
if (expr instanceof LambdaExpression le) {
|
&& mi.arguments().isEmpty();
|
||||||
ASTNode body = le.getBody();
|
|
||||||
if (body instanceof Expression bodyExpr) {
|
|
||||||
expr = bodyExpr;
|
|
||||||
} else if (body instanceof Block block) {
|
|
||||||
for (Object stmtObj : block.statements()) {
|
|
||||||
if (stmtObj instanceof ReturnStatement rs && rs.getExpression() != null) {
|
|
||||||
expr = rs.getExpression();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expr instanceof ExpressionMethodReference emr) {
|
if (isThisOrSuperGetter) {
|
||||||
TypeDeclaration td = findEnclosingType(expr);
|
return originalExpr.toString(); // Preserve "this.getTransitionType()" for later substitution
|
||||||
if (td != null) {
|
|
||||||
MethodDeclaration md = context.findMethodDeclaration(td, emr.getName().getIdentifier(), true);
|
|
||||||
if (md != null && md.getBody() != null) {
|
|
||||||
for (Object stmtObj : md.getBody().statements()) {
|
|
||||||
if (stmtObj instanceof ReturnStatement rs && rs.getExpression() != null) {
|
|
||||||
expr = rs.getExpression();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removed eager tracing here to allow dynamic tracing in resolveTriggerPointParameters
|
|
||||||
|
|
||||||
if (expr instanceof ClassInstanceCreation cic && !cic.arguments().isEmpty()) {
|
|
||||||
Expression firstArg = (Expression) cic.arguments().get(0);
|
|
||||||
String resolved = constantResolver.resolve(firstArg, context);
|
|
||||||
if (resolved != null) {
|
|
||||||
expr = firstArg; // Only unwrap if it's actually a constant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String val = null;
|
|
||||||
|
|
||||||
// Preserve getter calls on 'this' or 'super' for later resolution in trigger parameter tracing
|
|
||||||
// where we can substitute the actual receiver from the call edge
|
|
||||||
boolean isThisOrSuperGetter = expr instanceof MethodInvocation mi
|
|
||||||
&& (mi.getExpression() instanceof ThisExpression || mi.getExpression() instanceof SuperMethodInvocation)
|
|
||||||
&& mi.arguments().isEmpty();
|
|
||||||
|
|
||||||
if (!isThisOrSuperGetter) {
|
|
||||||
// Narrow resolution for "localVar.getX()" patterns where localVar is initialized
|
|
||||||
// from a ClassInstanceCreation (directly or via a factory method). This avoids
|
|
||||||
// the broad ENUM_SET fallback when the getter transforms one enum to another.
|
|
||||||
if (expr instanceof MethodInvocation getterMi
|
|
||||||
&& getterMi.getExpression() instanceof SimpleName instanceSn
|
|
||||||
&& getterMi.arguments().isEmpty()) {
|
|
||||||
val = tryNarrowGetterViaLocalVarChain(getterMi, instanceSn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val == null) val = constantResolver.resolve(expr, context);
|
|
||||||
if (val == null) {
|
|
||||||
val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val = expr.toString(); // Preserve "this.getTransitionType()" for later substitution
|
|
||||||
}
|
|
||||||
args.add(val);
|
|
||||||
}
|
}
|
||||||
System.out.println("DEBUG resolveArguments: " + args);
|
|
||||||
return args;
|
// Narrow resolution for "localVar.getX()" patterns where localVar is initialized
|
||||||
|
// from a ClassInstanceCreation (directly or via a factory method). This avoids
|
||||||
|
// the broad ENUM_SET fallback when the getter transforms one enum to another.
|
||||||
|
if (originalExpr instanceof org.eclipse.jdt.core.dom.MethodInvocation getterMi
|
||||||
|
&& getterMi.getExpression() instanceof org.eclipse.jdt.core.dom.SimpleName instanceSn
|
||||||
|
&& getterMi.arguments().isEmpty()) {
|
||||||
|
String narrowed = tryNarrowGetterViaLocalVarChain(getterMi, instanceSn);
|
||||||
|
if (narrowed != null) {
|
||||||
|
return narrowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> resolveCalledMethodsPolymorphic(MethodInvocation node) {
|
protected List<String> resolveCalledMethodsPolymorphic(MethodInvocation node) {
|
||||||
|
|||||||
@@ -122,67 +122,25 @@ public class JdtCallGraphEngine extends AbstractCallGraphEngine {
|
|||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> resolveArguments(List<?> astArguments) {
|
@Override
|
||||||
List<String> args = new ArrayList<>();
|
protected String resolveArgument(org.eclipse.jdt.core.dom.Expression expr) {
|
||||||
for (Object argObj : astArguments) {
|
// Extract from constructor args (e.g., new CustomMessage(OrderEvent.PROCESS, ...))
|
||||||
Expression expr = (Expression) argObj;
|
org.eclipse.jdt.core.dom.Expression unwrappedBuilder = unwrapMessageBuilder(expr);
|
||||||
|
if (unwrappedBuilder != expr) {
|
||||||
// Extract from lambda
|
expr = unwrappedBuilder;
|
||||||
if (expr instanceof LambdaExpression le) {
|
|
||||||
ASTNode body = le.getBody();
|
|
||||||
if (body instanceof Expression bodyExpr) {
|
|
||||||
expr = bodyExpr;
|
|
||||||
} else if (body instanceof Block block) {
|
|
||||||
for (Object stmtObj : block.statements()) {
|
|
||||||
if (stmtObj instanceof ReturnStatement rs && rs.getExpression() != null) {
|
|
||||||
expr = rs.getExpression();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expr instanceof ExpressionMethodReference emr) {
|
|
||||||
TypeDeclaration td = findEnclosingType(expr);
|
|
||||||
if (td != null) {
|
|
||||||
MethodDeclaration md = context.findMethodDeclaration(td, emr.getName().getIdentifier(), true);
|
|
||||||
if (md != null && md.getBody() != null) {
|
|
||||||
for (Object stmtObj : md.getBody().statements()) {
|
|
||||||
if (stmtObj instanceof ReturnStatement rs && rs.getExpression() != null) {
|
|
||||||
expr = rs.getExpression();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract from constructor args (e.g., new CustomMessage(OrderEvent.PROCESS, ...))
|
|
||||||
Expression unwrappedBuilder = unwrapMessageBuilder(expr);
|
|
||||||
if (unwrappedBuilder != expr) {
|
|
||||||
expr = unwrappedBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression tracedExpr = traceVariable(expr);
|
|
||||||
if (tracedExpr instanceof QualifiedName || tracedExpr instanceof ClassInstanceCreation || tracedExpr instanceof StringLiteral || tracedExpr instanceof NumberLiteral) {
|
|
||||||
expr = tracedExpr; // Only accept trace if it resolves to a pure constant/primitive/constructor
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expr instanceof ClassInstanceCreation cic && !cic.arguments().isEmpty()) {
|
|
||||||
Expression firstArg = (Expression) cic.arguments().get(0);
|
|
||||||
String resolved = constantResolver.resolve(firstArg, context);
|
|
||||||
if (resolved != null) {
|
|
||||||
expr = firstArg; // Only unwrap if it's actually a constant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String val = constantResolver.resolve(expr, context);
|
|
||||||
if (val == null) {
|
|
||||||
val = expr.toString(); // Fallback to raw string (handles Enums, 'new Class()', etc)
|
|
||||||
}
|
|
||||||
args.add(val);
|
|
||||||
}
|
}
|
||||||
return args;
|
|
||||||
|
// Trace variable to resolve local variable references
|
||||||
|
org.eclipse.jdt.core.dom.Expression tracedExpr = traceVariable(expr);
|
||||||
|
if (tracedExpr instanceof org.eclipse.jdt.core.dom.QualifiedName
|
||||||
|
|| tracedExpr instanceof org.eclipse.jdt.core.dom.ClassInstanceCreation
|
||||||
|
|| tracedExpr instanceof org.eclipse.jdt.core.dom.StringLiteral
|
||||||
|
|| tracedExpr instanceof org.eclipse.jdt.core.dom.NumberLiteral) {
|
||||||
|
expr = tracedExpr; // Only accept trace if it resolves to a pure constant/primitive/constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate to base class for lambda, method reference, and constructor unwrapping
|
||||||
|
return super.resolveArgument(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> resolveCalledMethodsPolymorphic(MethodInvocation node) {
|
private List<String> resolveCalledMethodsPolymorphic(MethodInvocation node) {
|
||||||
|
|||||||
Reference in New Issue
Block a user