-
Notifications
You must be signed in to change notification settings - Fork 13
Support refinement for conditions of comparisons #389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
2b01a93
58d9302
5ebcb2a
eee0b78
61a9c0e
23f12b4
1d59b43
dc90cb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -1,14 +1,18 @@ | ||||
| package checkers.inference.dataflow; | ||||
|
|
||||
| import org.checkerframework.dataflow.analysis.ConditionalTransferResult; | ||||
| import org.checkerframework.dataflow.analysis.RegularTransferResult; | ||||
| import org.checkerframework.dataflow.analysis.TransferInput; | ||||
| import org.checkerframework.dataflow.analysis.TransferResult; | ||||
| import org.checkerframework.dataflow.cfg.node.AssignmentNode; | ||||
| import org.checkerframework.dataflow.cfg.node.EqualToNode; | ||||
| import org.checkerframework.dataflow.cfg.node.FieldAccessNode; | ||||
| import org.checkerframework.dataflow.cfg.node.LocalVariableNode; | ||||
| import org.checkerframework.dataflow.cfg.node.Node; | ||||
| import org.checkerframework.dataflow.cfg.node.NotEqualNode; | ||||
| import org.checkerframework.dataflow.cfg.node.StringConcatenateAssignmentNode; | ||||
| import org.checkerframework.dataflow.cfg.node.TernaryExpressionNode; | ||||
| import org.checkerframework.dataflow.expression.JavaExpression; | ||||
| import org.checkerframework.framework.flow.CFStore; | ||||
| import org.checkerframework.framework.flow.CFTransfer; | ||||
| import org.checkerframework.framework.flow.CFValue; | ||||
|
|
@@ -21,6 +25,7 @@ | |||
| import java.util.Map; | ||||
| import java.util.logging.Logger; | ||||
|
|
||||
| import javax.lang.model.element.AnnotationMirror; | ||||
| import javax.lang.model.type.TypeKind; | ||||
|
|
||||
| import com.sun.source.tree.Tree; | ||||
|
|
@@ -31,6 +36,8 @@ | |||
| import checkers.inference.SlotManager; | ||||
| import checkers.inference.VariableAnnotator; | ||||
| import checkers.inference.model.AnnotationLocation; | ||||
| import checkers.inference.model.ComparisonVariableSlot; | ||||
| import checkers.inference.model.ConstraintManager; | ||||
| import checkers.inference.model.ExistentialVariableSlot; | ||||
| import checkers.inference.model.RefinementVariableSlot; | ||||
| import checkers.inference.model.Slot; | ||||
|
|
@@ -401,4 +408,101 @@ private TransferResult<CFValue, CFStore> storeDeclaration(Node lhs, | |||
| private boolean isDeclarationWithInitializer(AssignmentNode assignmentNode) { | ||||
| return (assignmentNode.getTree().getKind() == Tree.Kind.VARIABLE); | ||||
| } | ||||
|
|
||||
| /** | ||||
| * Create one {@link ComparisonVariableSlot} for one operand of a comparison for each of the then store | ||||
| * and else store separately. | ||||
| * @param node a CFG node representing one operand of a comparison | ||||
| * @param thenStore the then store in which the value of the given node is updated | ||||
| * @param elseStore the else store in which the value of the given node is updated | ||||
| */ | ||||
| private void createComparisonVariableSlot(Node node, CFStore thenStore, CFStore elseStore) { | ||||
| // Only create refinement comparison slot for variables | ||||
| // TODO: deal with comparison between more complex expressions | ||||
| Node targetNode = node; | ||||
| if (node instanceof AssignmentNode) { | ||||
| AssignmentNode a = (AssignmentNode) node; | ||||
| targetNode = a.getTarget(); | ||||
| } | ||||
| if (!(targetNode instanceof LocalVariableNode) && !(targetNode instanceof FieldAccessNode)) { | ||||
| return; | ||||
| } | ||||
| Tree tree = targetNode.getTree(); | ||||
| ConstraintManager constraintManager = InferenceMain.getInstance().getConstraintManager(); | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think these are the first constraints that are created by
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it works in either ways. I guess putting constraint creation here is just for convenience. I can move it to
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you get a chance to try whether this is nicer in
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not all the constraints are created in
For refinement variables we add the subtype constraints in InferenceVisitor may be because we want to reuse the super method So it makes sense to put the type rule-based constraints in
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Besides, regarding the comparison variable slot, I think the subtype constraints (compareSlot <: declareType) are not precise. It's more precise to create compareSlot <: typeBeforeComparison, i.e. line 443-446 can be removed. |
||||
| SlotManager slotManager = getInferenceAnalysis().getSlotManager(); | ||||
|
|
||||
| AnnotatedTypeMirror atm = typeFactory.getAnnotatedType(tree); | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a problem in getting the declared type using The effect is, A common workaround for this issue in CFI is to use checker-framework-inference/src/checkers/inference/dataflow/InferenceTransfer.java Line 222 in 8a29a0e
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the explanation! Let's try to clean this up with or after #362! |
||||
| Slot slotToRefine = slotManager.getSlot(atm); | ||||
|
|
||||
| // TODO: Understand why there are null slots | ||||
| if (InferenceMain.isHackMode(slotToRefine == null)) { | ||||
| logger.fine("HackMode: slotToRefine is null !"); | ||||
| return; | ||||
| } | ||||
|
|
||||
| if (slotToRefine instanceof RefinementVariableSlot) { | ||||
| slotToRefine = ((RefinementVariableSlot) slotToRefine).getRefined(); | ||||
| assert !(slotToRefine instanceof RefinementVariableSlot); | ||||
| } | ||||
|
|
||||
| AnnotationLocation location = | ||||
| VariableAnnotator.treeToLocation(analysis.getTypeFactory(), tree); | ||||
| // TODO: find out why there are missing location | ||||
| if (InferenceMain.isHackMode(location == AnnotationLocation.MISSING_LOCATION)) { | ||||
| logger.fine("HackMode: create ComparisonVariableSlot on MISSING_LOCATION !"); | ||||
| return; | ||||
| } | ||||
| ComparisonVariableSlot thenSlot = slotManager.createComparisonVariableSlot(location, slotToRefine, true); | ||||
| constraintManager.addSubtypeConstraint(thenSlot, slotToRefine); | ||||
| ComparisonVariableSlot elseSlot = slotManager.createComparisonVariableSlot(location, slotToRefine, false); | ||||
| constraintManager.addSubtypeConstraint(elseSlot, slotToRefine); | ||||
|
|
||||
| if (slotToRefine instanceof VariableSlot) { | ||||
| // ComparisonVariableSlots stores the refined value after comparison, so add it to the "refinedTo" list | ||||
| ((VariableSlot) slotToRefine).getRefinedToSlots().add(thenSlot); | ||||
| ((VariableSlot) slotToRefine).getRefinedToSlots().add(elseSlot); | ||||
| } | ||||
|
|
||||
| AnnotationMirror thenAm = slotManager.getAnnotation(thenSlot); | ||||
| AnnotationMirror elseAm = slotManager.getAnnotation(elseSlot); | ||||
|
|
||||
| // If node is assignment, iterate over lhs; otherwise, just node. | ||||
| JavaExpression receiver; | ||||
| receiver = JavaExpression.fromNode(targetNode); | ||||
| thenStore.clearValue(receiver); | ||||
| thenStore.insertValue(receiver, thenAm); | ||||
| elseStore.clearValue(receiver); | ||||
| elseStore.insertValue(receiver, elseAm); | ||||
| } | ||||
|
|
||||
| @Override | ||||
| public TransferResult<CFValue, CFStore> visitEqualTo( | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These only handle equal and not-equal. Shouldn't other comparisons also be handled?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comparison slots are created only when type refinement can be performed. For non-numeric types (general case in CFI), type refinement is only possible when the comparison is Other kinds of comparisons are only applicable for numeric types and can be defined in specific type system, like |
||||
| EqualToNode n, TransferInput<CFValue, CFStore> in) { | ||||
| TransferResult<CFValue, CFStore> result = super.visitEqualTo(n, in); | ||||
| CFStore thenStore = result.getThenStore(); | ||||
| CFStore elseStore = result.getElseStore(); | ||||
|
|
||||
| createComparisonVariableSlot(n.getLeftOperand(), thenStore, elseStore); | ||||
| createComparisonVariableSlot(n.getRightOperand(), thenStore, elseStore); | ||||
|
|
||||
| CFValue newResultValue = | ||||
| getInferenceAnalysis() | ||||
| .createAbstractValue(typeFactory.getAnnotatedType(n.getTree())); | ||||
| return new ConditionalTransferResult<>(newResultValue, thenStore, elseStore); | ||||
| } | ||||
|
|
||||
| @Override | ||||
| public TransferResult<CFValue, CFStore> visitNotEqual( | ||||
| NotEqualNode n, TransferInput<CFValue, CFStore> in) { | ||||
| TransferResult<CFValue, CFStore> result = super.visitNotEqual(n, in); | ||||
| CFStore thenStore = result.getThenStore(); | ||||
| CFStore elseStore = result.getElseStore(); | ||||
|
|
||||
| createComparisonVariableSlot(n.getLeftOperand(), thenStore, elseStore); | ||||
| createComparisonVariableSlot(n.getRightOperand(), thenStore, elseStore); | ||||
|
|
||||
| CFValue newResultValue = | ||||
| analysis.createAbstractValue(typeFactory.getAnnotatedType(n.getTree())); | ||||
| return new ConditionalTransferResult<>(newResultValue, thenStore, elseStore); | ||||
| } | ||||
| } | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import ostrusted.qual.OsTrusted; | ||
| import ostrusted.qual.OsUntrusted; | ||
|
|
||
| public class Comparison { | ||
|
|
||
| void foo(@OsUntrusted Object o) { | ||
| if (o == null) { | ||
| // Literal null is @OsTrusted. `o` is refined to @OsTrusted through ComparisonConstraint. | ||
| bar(o); | ||
|
d367wang marked this conversation as resolved.
|
||
| } | ||
| } | ||
|
|
||
| @OsTrusted | ||
| Object bar(Object o) { | ||
| // :: fixable-error: (return.type.incompatible) | ||
| return o; | ||
| } | ||
|
|
||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.