|
16 | 16 | import liquidjava.utils.constants.Ops; |
17 | 17 | import liquidjava.utils.constants.Types; |
18 | 18 | import liquidjava.rj_language.Predicate; |
| 19 | +import liquidjava.rj_language.ast.BinaryExpression; |
| 20 | +import liquidjava.rj_language.ast.Expression; |
| 21 | +import liquidjava.rj_language.ast.GroupExpression; |
19 | 22 | import org.apache.commons.lang3.NotImplementedException; |
20 | 23 | import spoon.reflect.code.BinaryOperatorKind; |
21 | 24 | import spoon.reflect.code.CtAssignment; |
| 25 | +import spoon.reflect.code.CtConditional; |
22 | 26 | import spoon.reflect.code.CtBinaryOperator; |
23 | 27 | import spoon.reflect.code.CtExpression; |
24 | 28 | import spoon.reflect.code.CtFieldRead; |
25 | 29 | import spoon.reflect.code.CtIf; |
26 | 30 | import spoon.reflect.code.CtInvocation; |
27 | 31 | import spoon.reflect.code.CtLiteral; |
28 | 32 | import spoon.reflect.code.CtLocalVariable; |
| 33 | +import spoon.reflect.code.CtOperatorAssignment; |
29 | 34 | import spoon.reflect.code.CtReturn; |
30 | 35 | import spoon.reflect.code.CtUnaryOperator; |
31 | 36 | import spoon.reflect.code.CtVariableRead; |
@@ -94,6 +99,18 @@ public <T> void getBinaryOpRefinements(CtBinaryOperator<T> operator) throws LJEr |
94 | 99 | // TODO ADD TYPES |
95 | 100 | } |
96 | 101 |
|
| 102 | + /** |
| 103 | + * Builds the refinement for a operator assignment. Java operator assignments such as {@code x += y} are modeled as |
| 104 | + * {@code x = x + y}; the returned predicate refines the assigned value as {@code _ == current(x) <op> rhs}. |
| 105 | + */ |
| 106 | + public Predicate getOperatorAssignmentRefinement(String assignedName, CtOperatorAssignment<?, ?> assignment) |
| 107 | + throws LJError { |
| 108 | + Predicate left = getCurrentVariableValue(assignedName); |
| 109 | + Predicate right = getOperatorAssignmentRefinement(assignment.getAssignment()); |
| 110 | + Predicate operation = Predicate.createOperation(left, getOperatorFromKind(assignment.getKind()), right); |
| 111 | + return Predicate.createEquals(Predicate.createVar(Keys.WILDCARD), operation); |
| 112 | + } |
| 113 | + |
97 | 114 | /** |
98 | 115 | * Finds and adds refinement metadata to the unary operation |
99 | 116 | * |
@@ -280,6 +297,91 @@ private Predicate getOperationRefinementFromExternalLib(CtInvocation<?> inv) thr |
280 | 297 | return new Predicate(); |
281 | 298 | } |
282 | 299 |
|
| 300 | + /** |
| 301 | + * Returns the latest symbolic value for a variable |
| 302 | + */ |
| 303 | + private Predicate getCurrentVariableValue(String name) { |
| 304 | + Optional<VariableInstance> variableInstance = rtc.getContext().getLastVariableInstance(name); |
| 305 | + return Predicate.createVar(variableInstance.map(VariableInstance::getName).orElse(name)); |
| 306 | + } |
| 307 | + |
| 308 | + /** |
| 309 | + * Converts a operator assignment into an arithmetic predicate operand |
| 310 | + */ |
| 311 | + private Predicate getOperatorAssignmentRefinement(CtExpression<?> element) throws LJError { |
| 312 | + if (element instanceof CtVariableRead<?> variableRead) { |
| 313 | + String name = variableRead.getVariable().getSimpleName(); |
| 314 | + if (variableRead instanceof CtFieldRead<?>) |
| 315 | + name = String.format(Formats.THIS, name); |
| 316 | + return getCurrentVariableValue(name); |
| 317 | + } else if (element instanceof CtBinaryOperator<?> binaryOperator) { |
| 318 | + Predicate left = getOperatorAssignmentRefinement(binaryOperator.getLeftHandOperand()); |
| 319 | + Predicate right = getOperatorAssignmentRefinement(binaryOperator.getRightHandOperand()); |
| 320 | + return Predicate.createOperation(left, getOperatorFromKind(binaryOperator.getKind()), right); |
| 321 | + } else if (element instanceof CtConditional<?> conditional) { |
| 322 | + Predicate condition = getConditionRefinement(conditional.getCondition()); |
| 323 | + Predicate thenExpression = getOperatorAssignmentRefinement(conditional.getThenExpression()); |
| 324 | + Predicate elseExpression = getOperatorAssignmentRefinement(conditional.getElseExpression()); |
| 325 | + return Predicate.createITE(condition, thenExpression, elseExpression); |
| 326 | + } else if (element instanceof CtLiteral<?> literal) { |
| 327 | + if (literal.getValue() == null) |
| 328 | + throw new CustomError("Null literals are not supported", literal.getPosition()); |
| 329 | + return new Predicate(literal.getValue().toString(), element); |
| 330 | + } else if (element instanceof CtInvocation<?>) { |
| 331 | + VariableInstance invocationValue = (VariableInstance) element.getMetadata(Keys.TARGET); |
| 332 | + if (invocationValue != null) |
| 333 | + return Predicate.createVar(invocationValue.getName()); |
| 334 | + } |
| 335 | + return valueFromRefinement(element, rtc.getRefinement(element)); |
| 336 | + } |
| 337 | + |
| 338 | + private Predicate getConditionRefinement(CtExpression<Boolean> condition) throws LJError { |
| 339 | + Predicate refinement = rtc.getRefinement(condition); |
| 340 | + Optional<Predicate> value = unwrapWildcardEquality(refinement); |
| 341 | + if (value.isPresent()) |
| 342 | + return value.get(); |
| 343 | + return refinement; |
| 344 | + } |
| 345 | + |
| 346 | + private Optional<Predicate> unwrapWildcardEquality(Predicate refinement) { |
| 347 | + Expression expression = unwrapGroupExpression(refinement.getExpression()); |
| 348 | + if (expression instanceof BinaryExpression binaryExpression && Ops.EQ.equals(binaryExpression.getOperator()) |
| 349 | + && Keys.WILDCARD.equals(binaryExpression.getFirstOperand().toString())) { |
| 350 | + return Optional.of(new Predicate(binaryExpression.getSecondOperand())); |
| 351 | + } |
| 352 | + return Optional.empty(); |
| 353 | + } |
| 354 | + |
| 355 | + private Predicate valueFromRefinement(CtExpression<?> element, Predicate refinement) { |
| 356 | + if (refinement == null) |
| 357 | + return createFreshValue(element, new Predicate()); |
| 358 | + |
| 359 | + Optional<Predicate> value = unwrapWildcardEquality(refinement); |
| 360 | + if (value.isPresent()) |
| 361 | + return value.get(); |
| 362 | + |
| 363 | + Expression expression = unwrapGroupExpression(refinement.getExpression()); |
| 364 | + boolean hasWildcard = refinement.getVariableNames().contains(Keys.WILDCARD); |
| 365 | + if (!hasWildcard && !expression.isBooleanExpression()) |
| 366 | + return new Predicate(expression); |
| 367 | + |
| 368 | + Predicate constraint = hasWildcard ? refinement : new Predicate(); |
| 369 | + return createFreshValue(element, constraint); |
| 370 | + } |
| 371 | + |
| 372 | + private Predicate createFreshValue(CtExpression<?> element, Predicate refinement) { |
| 373 | + String newName = String.format(Formats.FRESH, rtc.getContext().getCounter()); |
| 374 | + Predicate freshRefinement = refinement.substituteVariable(Keys.WILDCARD, newName); |
| 375 | + rtc.getContext().addVarToContext(newName, element.getType(), freshRefinement, element); |
| 376 | + return Predicate.createVar(newName); |
| 377 | + } |
| 378 | + |
| 379 | + private Expression unwrapGroupExpression(Expression expression) { |
| 380 | + while (expression instanceof GroupExpression groupExpression) |
| 381 | + expression = groupExpression.getExpression(); |
| 382 | + return expression; |
| 383 | + } |
| 384 | + |
283 | 385 | /** |
284 | 386 | * Retrieves the refinements for the variable write inside unary operation |
285 | 387 | * |
|
0 commit comments