from ai
This commit is contained in:
@@ -27,6 +27,7 @@ public class TriggerPoint {
|
||||
private final java.util.List<String> polymorphicEvents; // NEW: stores concrete events resolved via deep polymorphism
|
||||
private final boolean external;
|
||||
private final String constraint;
|
||||
private final boolean ambiguous;
|
||||
|
||||
public TriggerPoint(
|
||||
String event,
|
||||
@@ -41,7 +42,8 @@ public class TriggerPoint {
|
||||
String eventTypeFqn,
|
||||
java.util.List<String> polymorphicEvents,
|
||||
boolean external,
|
||||
String constraint) {
|
||||
String constraint,
|
||||
boolean ambiguous) {
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
this.sourceFile = sourceFile;
|
||||
@@ -61,6 +63,7 @@ public class TriggerPoint {
|
||||
}
|
||||
this.external = external;
|
||||
this.constraint = constraint;
|
||||
this.ambiguous = ambiguous;
|
||||
}
|
||||
|
||||
private static String qualifyEvent(String e, String eventTypeFqn) {
|
||||
|
||||
@@ -239,9 +239,8 @@ public class ConstantResolver {
|
||||
}
|
||||
|
||||
try {
|
||||
java.util.Map<String, String> localVars = new java.util.HashMap<>(prebuiltLocals);
|
||||
java.util.Set<String> declaredLocals = new java.util.HashSet<>(prebuiltLocals.keySet());
|
||||
return evaluateBody(md.getBody(), localVars, declaredLocals, context, visited);
|
||||
return evaluateBody(md.getBody(), prebuiltLocals, declaredLocals, context, visited);
|
||||
} finally {
|
||||
visited.remove(methodFqn);
|
||||
}
|
||||
@@ -267,7 +266,7 @@ public class ConstantResolver {
|
||||
String varName = fragment.getName().getIdentifier();
|
||||
declaredLocals.add(varName);
|
||||
if (fragment.getInitializer() != null) {
|
||||
String rhsVal = resolveExpressionWithParams(fragment.getInitializer(), localVars);
|
||||
String rhsVal = resolveExpressionWithParams(fragment.getInitializer(), localVars, context);
|
||||
if (rhsVal == null) rhsVal = resolveInternal(fragment.getInitializer(), context, visited);
|
||||
if (rhsVal != null) localVars.put(varName, rhsVal);
|
||||
}
|
||||
@@ -276,6 +275,43 @@ public class ConstantResolver {
|
||||
return super.visit(vds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(org.eclipse.jdt.core.dom.MethodInvocation node) {
|
||||
MethodDeclaration sideEffectMd = findInvokedMethod(node, context);
|
||||
if (sideEffectMd != null && sideEffectMd.getBody() != null) {
|
||||
TypeDeclaration currentTd = findEnclosingType(sideEffectMd);
|
||||
if (sideEffectMd != null && sideEffectMd.getBody() != null) {
|
||||
java.util.Map<String, String> inlineLocals = new java.util.HashMap<>();
|
||||
for (java.util.Map.Entry<String, String> entry : localVars.entrySet()) {
|
||||
if (entry.getKey().startsWith("this.")) {
|
||||
inlineLocals.put(entry.getKey(), entry.getValue());
|
||||
} else if (context.hasField(currentTd, entry.getKey())) {
|
||||
inlineLocals.put(entry.getKey(), entry.getValue());
|
||||
inlineLocals.put("this." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < node.arguments().size() && i < sideEffectMd.parameters().size(); i++) {
|
||||
Expression arg = (Expression) node.arguments().get(i);
|
||||
String val = resolveExpressionWithParams(arg, localVars, context);
|
||||
if (val == null) val = resolveInternal(arg, context, visited);
|
||||
if (val != null) {
|
||||
org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) sideEffectMd.parameters().get(i);
|
||||
inlineLocals.put(param.getName().getIdentifier(), val);
|
||||
}
|
||||
}
|
||||
evaluateMethodBodyWithLocals(sideEffectMd, inlineLocals, context, visited);
|
||||
for (java.util.Map.Entry<String, String> entry : inlineLocals.entrySet()) {
|
||||
if (entry.getKey().startsWith("this.")) {
|
||||
localVars.put(entry.getKey(), entry.getValue());
|
||||
String bareName = entry.getKey().substring(5);
|
||||
if (!declaredLocals.contains(bareName)) localVars.put(bareName, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(Assignment assignment) {
|
||||
Expression lhs = assignment.getLeftHandSide();
|
||||
@@ -285,7 +321,7 @@ public class ConstantResolver {
|
||||
} else if (lhs instanceof FieldAccess fa) {
|
||||
varName = "this." + fa.getName().getIdentifier();
|
||||
}
|
||||
String rhsVal = resolveExpressionWithParams(assignment.getRightHandSide(), localVars);
|
||||
String rhsVal = resolveExpressionWithParams(assignment.getRightHandSide(), localVars, context);
|
||||
if (rhsVal == null) rhsVal = resolveInternal(assignment.getRightHandSide(), context, visited);
|
||||
if (varName != null && rhsVal != null) {
|
||||
if (varName.startsWith("this.")) {
|
||||
@@ -316,7 +352,7 @@ public class ConstantResolver {
|
||||
String result = evaluateSwitchExpression(se, localVars, context, visited);
|
||||
if (result != null) finalResult[0] = result;
|
||||
} else if (rs.getExpression() != null) {
|
||||
String result = resolveExpressionWithParams(rs.getExpression(), localVars);
|
||||
String result = resolveExpressionWithParams(rs.getExpression(), localVars, context);
|
||||
if (result == null) result = resolveInternal(rs.getExpression(), context, visited);
|
||||
if (result != null) finalResult[0] = result;
|
||||
}
|
||||
@@ -329,7 +365,7 @@ public class ConstantResolver {
|
||||
}
|
||||
|
||||
private String evaluateSwitchStatement(org.eclipse.jdt.core.dom.SwitchStatement ss, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) {
|
||||
String switchVar = resolveExpressionWithParams(ss.getExpression(), paramValues);
|
||||
String switchVar = resolveExpressionWithParams(ss.getExpression(), paramValues, context);
|
||||
if (switchVar == null) {
|
||||
Set<String> values = new LinkedHashSet<>();
|
||||
for (Object stmtObj : ss.statements()) {
|
||||
@@ -381,7 +417,7 @@ public class ConstantResolver {
|
||||
matchingCase = true;
|
||||
} else {
|
||||
for (Object exprObj : sc.expressions()) {
|
||||
String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues);
|
||||
String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues, context);
|
||||
// Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback
|
||||
if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) {
|
||||
caseVal = caseSn.getIdentifier();
|
||||
@@ -416,7 +452,7 @@ public class ConstantResolver {
|
||||
}
|
||||
|
||||
private String evaluateSwitchExpression(org.eclipse.jdt.core.dom.SwitchExpression se, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) {
|
||||
String switchVar = resolveExpressionWithParams(se.getExpression(), paramValues);
|
||||
String switchVar = resolveExpressionWithParams(se.getExpression(), paramValues, context);
|
||||
if (switchVar == null) {
|
||||
Set<String> values = new LinkedHashSet<>();
|
||||
for (Object stmtObj : se.statements()) {
|
||||
@@ -468,7 +504,7 @@ public class ConstantResolver {
|
||||
matchingCase = true;
|
||||
} else {
|
||||
for (Object exprObj : sc.expressions()) {
|
||||
String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues);
|
||||
String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues, context);
|
||||
// Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback
|
||||
if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) {
|
||||
caseVal = caseSn.getIdentifier();
|
||||
@@ -502,7 +538,7 @@ public class ConstantResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String resolveExpressionWithParams(Expression expr, java.util.Map<String, String> paramValues) {
|
||||
private String resolveExpressionWithParams(Expression expr, java.util.Map<String, String> paramValues, CodebaseContext context) {
|
||||
if (expr instanceof SimpleName sn) {
|
||||
String name = sn.getIdentifier();
|
||||
if (paramValues.containsKey(name)) return paramValues.get(name);
|
||||
@@ -515,7 +551,65 @@ public class ConstantResolver {
|
||||
return null;
|
||||
} else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
|
||||
return sl.getLiteralValue();
|
||||
}
|
||||
} else if (expr instanceof MethodInvocation mi) {
|
||||
if ("valueOf".equals(mi.getName().getIdentifier())) {
|
||||
Expression arg = null;
|
||||
if (mi.arguments().size() == 1) {
|
||||
arg = (Expression) mi.arguments().get(0);
|
||||
} else if (mi.arguments().size() == 2) {
|
||||
arg = (Expression) mi.arguments().get(1);
|
||||
}
|
||||
if (arg != null) {
|
||||
String resolvedArg = resolveExpressionWithParams(arg, paramValues, context);
|
||||
if (resolvedArg != null && resolvedArg.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
|
||||
String enumFqn = null;
|
||||
if (mi.arguments().size() == 2) {
|
||||
enumFqn = resolveEnumFqn((Expression) mi.arguments().get(0), context);
|
||||
} else if (mi.arguments().size() == 1 && mi.getExpression() != null) {
|
||||
enumFqn = resolveEnumFqn(mi.getExpression(), context);
|
||||
}
|
||||
if (enumFqn != null) {
|
||||
return enumFqn + "." + resolvedArg;
|
||||
} else {
|
||||
// If we can't resolve the exact enum type here, we can just return the bare constant
|
||||
return resolvedArg;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MethodDeclaration sideEffectMd = findInvokedMethod(mi, context);
|
||||
if (sideEffectMd != null && sideEffectMd.getBody() != null) {
|
||||
TypeDeclaration currentTd = findEnclosingType(sideEffectMd);
|
||||
java.util.Map<String, String> inlineLocals = new java.util.HashMap<>();
|
||||
for (java.util.Map.Entry<String, String> entry : paramValues.entrySet()) {
|
||||
if (entry.getKey().startsWith("this.")) {
|
||||
inlineLocals.put(entry.getKey(), entry.getValue());
|
||||
} else if (context.hasField(currentTd, entry.getKey())) {
|
||||
inlineLocals.put(entry.getKey(), entry.getValue());
|
||||
inlineLocals.put("this." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < mi.arguments().size() && i < sideEffectMd.parameters().size(); i++) {
|
||||
Expression arg = (Expression) mi.arguments().get(i);
|
||||
String val = resolveExpressionWithParams(arg, paramValues, context);
|
||||
if (val == null) val = resolveInternal(arg, context, new java.util.HashSet<>());
|
||||
if (val != null) {
|
||||
org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) sideEffectMd.parameters().get(i);
|
||||
inlineLocals.put(param.getName().getIdentifier(), val);
|
||||
}
|
||||
}
|
||||
String ret = evaluateMethodBodyWithLocals(sideEffectMd, inlineLocals, context, new java.util.HashSet<>());
|
||||
for (java.util.Map.Entry<String, String> entry : inlineLocals.entrySet()) {
|
||||
if (entry.getKey().startsWith("this.")) {
|
||||
paramValues.put(entry.getKey(), entry.getValue());
|
||||
String bareName = entry.getKey().substring(5);
|
||||
paramValues.put(bareName, entry.getValue());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -685,11 +779,29 @@ public class ConstantResolver {
|
||||
}
|
||||
|
||||
private TypeDeclaration findEnclosingType(ASTNode node) {
|
||||
ASTNode parent = node.getParent();
|
||||
while (parent != null && !(parent instanceof TypeDeclaration)) {
|
||||
parent = parent.getParent();
|
||||
ASTNode current = node;
|
||||
while (current != null && !(current instanceof TypeDeclaration)) {
|
||||
current = current.getParent();
|
||||
}
|
||||
return (TypeDeclaration) parent;
|
||||
return (TypeDeclaration) current;
|
||||
}
|
||||
|
||||
private MethodDeclaration findInvokedMethod(org.eclipse.jdt.core.dom.MethodInvocation mi, CodebaseContext context) {
|
||||
org.eclipse.jdt.core.dom.IMethodBinding methodBinding = mi.resolveMethodBinding();
|
||||
if (methodBinding != null) {
|
||||
org.eclipse.jdt.core.dom.ITypeBinding declaringClass = methodBinding.getDeclaringClass();
|
||||
if (declaringClass != null) {
|
||||
TypeDeclaration targetTd = context.getTypeDeclaration(declaringClass.getErasure().getQualifiedName());
|
||||
if (targetTd != null) {
|
||||
return context.findMethodDeclaration(targetTd, mi.getName().getIdentifier(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
TypeDeclaration currentTd = findEnclosingType(mi);
|
||||
if (currentTd != null && (mi.getExpression() == null || mi.getExpression() instanceof org.eclipse.jdt.core.dom.ThisExpression)) {
|
||||
return context.findMethodDeclaration(currentTd, mi.getName().getIdentifier(), true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MethodDeclaration findEnclosingMethod(ASTNode node) {
|
||||
|
||||
@@ -137,11 +137,11 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
||||
String currentParamName = event;
|
||||
String resolvedValue = event;
|
||||
String methodSuffix = "";
|
||||
|
||||
String[] extractedEntry = extractMethodSuffix(currentParamName, methodSuffix);
|
||||
currentParamName = extractedEntry[0];
|
||||
methodSuffix = extractedEntry[1];
|
||||
if (finalParamNameRef != null) finalParamNameRef[0] = currentParamName;
|
||||
String[] extractedEntry = extractMethodSuffix(currentParamName, methodSuffix);
|
||||
currentParamName = extractedEntry[0];
|
||||
methodSuffix = extractedEntry[1];
|
||||
boolean isAmbiguous = false;
|
||||
if (finalParamNameRef != null) finalParamNameRef[0] = currentParamName;
|
||||
|
||||
for (int i = path.size() - 1; i > 0; i--) {
|
||||
String target = path.get(i);
|
||||
@@ -410,6 +410,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
||||
.stateTypeFqn(tp.getStateTypeFqn())
|
||||
.eventTypeFqn(tp.getEventTypeFqn())
|
||||
.external(tp.isExternal())
|
||||
.ambiguous(isAmbiguous)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -515,6 +516,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
||||
.stateTypeFqn(tp.getStateTypeFqn())
|
||||
.eventTypeFqn(tp.getEventTypeFqn())
|
||||
.external(tp.isExternal())
|
||||
.ambiguous(isAmbiguous)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -638,6 +640,7 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
||||
for (String ev : enumValues) {
|
||||
polymorphicEvents.add(constantExtractor.parseEnumSetElement(ev));
|
||||
}
|
||||
isAmbiguous = true;
|
||||
} else {
|
||||
List<String> typesToInspect = new ArrayList<>();
|
||||
if ("inline-instantiation".equals(sourceMethod)) {
|
||||
|
||||
@@ -277,37 +277,17 @@ public class ConstructorAnalyzer {
|
||||
}
|
||||
// Evaluate constructor body regardless of whether parameters could be resolved as constants
|
||||
|
||||
ctor.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(Assignment node) {
|
||||
Expression lhs = node.getLeftHandSide();
|
||||
Expression rhs = node.getRightHandSide();
|
||||
|
||||
String fieldName = null;
|
||||
if (lhs instanceof FieldAccess fa
|
||||
&& fa.getExpression() instanceof ThisExpression) {
|
||||
fieldName = fa.getName().getIdentifier();
|
||||
} else if (lhs instanceof SimpleName sn
|
||||
&& !paramValues.containsKey(sn.getIdentifier())) {
|
||||
fieldName = sn.getIdentifier();
|
||||
}
|
||||
|
||||
if (fieldName != null) {
|
||||
String paramVal = null;
|
||||
if (rhs instanceof SimpleName snRhs) {
|
||||
paramVal = paramValues.get(snRhs.getIdentifier());
|
||||
} else if (rhsResolver != null) {
|
||||
paramVal = rhsResolver.resolve(rhs, paramValues, td);
|
||||
}
|
||||
|
||||
if (paramVal != null) {
|
||||
fieldValues.put(fieldName, paramVal);
|
||||
fieldValues.put("this." + fieldName, paramVal);
|
||||
}
|
||||
}
|
||||
return super.visit(node);
|
||||
java.util.Map<String, String> localVars = new java.util.HashMap<>(paramValues);
|
||||
constantResolver.evaluateMethodBodyWithLocals(ctor, localVars, context, new java.util.HashSet<>());
|
||||
for (java.util.Map.Entry<String, String> entry : localVars.entrySet()) {
|
||||
if (entry.getKey().startsWith("this.")) {
|
||||
fieldValues.put(entry.getKey(), entry.getValue());
|
||||
fieldValues.put(entry.getKey().substring(5), entry.getValue());
|
||||
} else if (context.hasField(td, entry.getKey())) {
|
||||
fieldValues.put(entry.getKey(), entry.getValue());
|
||||
fieldValues.put("this." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ctor.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
@@ -399,37 +379,17 @@ public class ConstructorAnalyzer {
|
||||
}
|
||||
if (superParamValues.isEmpty()) continue;
|
||||
|
||||
superCtor.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(Assignment node) {
|
||||
Expression lhs = node.getLeftHandSide();
|
||||
Expression rhs = node.getRightHandSide();
|
||||
|
||||
String fieldName = null;
|
||||
if (lhs instanceof FieldAccess fa
|
||||
&& fa.getExpression() instanceof ThisExpression) {
|
||||
fieldName = fa.getName().getIdentifier();
|
||||
} else if (lhs instanceof SimpleName sn
|
||||
&& !superParamValues.containsKey(sn.getIdentifier())) {
|
||||
fieldName = sn.getIdentifier();
|
||||
}
|
||||
|
||||
if (fieldName != null) {
|
||||
String paramVal = null;
|
||||
if (rhs instanceof SimpleName snRhs) {
|
||||
paramVal = superParamValues.get(snRhs.getIdentifier());
|
||||
} else if (rhsResolver != null) {
|
||||
paramVal = rhsResolver.resolve(rhs, superParamValues, superTd);
|
||||
}
|
||||
|
||||
if (paramVal != null) {
|
||||
fieldValues.put(fieldName, paramVal);
|
||||
fieldValues.put("this." + fieldName, paramVal);
|
||||
}
|
||||
}
|
||||
return super.visit(node);
|
||||
java.util.Map<String, String> localVars = new java.util.HashMap<>(superParamValues);
|
||||
constantResolver.evaluateMethodBodyWithLocals(superCtor, localVars, context, new java.util.HashSet<>());
|
||||
for (java.util.Map.Entry<String, String> entry : localVars.entrySet()) {
|
||||
if (entry.getKey().startsWith("this.")) {
|
||||
fieldValues.put(entry.getKey(), entry.getValue());
|
||||
fieldValues.put(entry.getKey().substring(5), entry.getValue());
|
||||
} else if (context.hasField(superTd, entry.getKey())) {
|
||||
fieldValues.put(entry.getKey(), entry.getValue());
|
||||
fieldValues.put("this." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
superCtor.getBody().accept(new ASTVisitor() {
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user