Skip to content

Commit 8b5b6b0

Browse files
committed
SONARJAVA-1389 SE: Use persistent collections and refactor visibility of ProgramState internals
1 parent 3e96724 commit 8b5b6b0

5 files changed

Lines changed: 182 additions & 121 deletions

File tree

java-squid/src/main/java/org/sonar/java/se/CheckerDispatcher.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ public ProgramState getState() {
8585

8686
@Override
8787
public boolean isNull(SymbolicValue val) {
88+
if(val == null) {
89+
return false;
90+
}
8891
return explodedGraphWalker.constraintManager.isNull(getState(), val);
8992
}
9093

java-squid/src/main/java/org/sonar/java/se/ConstraintManager.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,15 @@
2121

2222
import org.sonar.plugins.java.api.tree.LiteralTree;
2323
import org.sonar.plugins.java.api.tree.Tree;
24-
import org.sonar.plugins.java.api.tree.VariableTree;
2524

2625
import java.util.List;
2726

2827
public class ConstraintManager {
2928

30-
private int counter = ProgramState.EMPTY_STATE.constraints.size();
29+
private int counter = ProgramState.EMPTY_STATE.constraintsSize();
3130

3231
public SymbolicValue createSymbolicValue(Tree syntaxNode) {
3332
SymbolicValue result;
34-
counter++;
3533
switch (syntaxNode.kind()) {
3634
case EQUAL_TO:
3735
result = new SymbolicValue.EqualToSymbolicValue(counter);
@@ -57,13 +55,10 @@ public SymbolicValue createSymbolicValue(Tree syntaxNode) {
5755
default:
5856
result = new SymbolicValue.ObjectSymbolicValue(counter);
5957
}
58+
counter++;
6059
return result;
6160
}
6261

63-
public SymbolicValue supersedeSymbolicValue(VariableTree variable) {
64-
return createSymbolicValue(variable);
65-
}
66-
6762
public SymbolicValue evalLiteral(LiteralTree syntaxNode) {
6863
if (syntaxNode.is(Tree.Kind.NULL_LITERAL)) {
6964
return SymbolicValue.NULL_LITERAL;
@@ -78,11 +73,12 @@ public SymbolicValue evalLiteral(LiteralTree syntaxNode) {
7873
}
7974

8075
public boolean isNull(ProgramState ps, SymbolicValue val) {
81-
return NullConstraint.NULL.equals(ps.constraints.get(val));
76+
return NullConstraint.NULL.equals(ps.getConstraint(val));
8277
}
8378

8479
public Pair<List<ProgramState>, List<ProgramState>> assumeDual(ProgramState programState) {
85-
Pair<ProgramState, List<SymbolicValue>> unstack = ProgramState.unstack(programState, 1);
80+
81+
Pair<ProgramState, List<SymbolicValue>> unstack = programState.unstackValue(1);
8682
SymbolicValue sv = unstack.b.get(0);
8783
final List<ProgramState> falseConstraint = sv.setConstraint(unstack.a, BooleanConstraint.FALSE);
8884
final List<ProgramState> trueConstraint = sv.setConstraint(unstack.a, BooleanConstraint.TRUE);

java-squid/src/main/java/org/sonar/java/se/ExplodedGraphWalker.java

Lines changed: 45 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import com.google.common.collect.ImmutableSet;
2626
import com.google.common.collect.Iterables;
2727
import com.google.common.collect.Lists;
28-
import com.google.common.collect.Maps;
2928
import org.slf4j.Logger;
3029
import org.slf4j.LoggerFactory;
3130
import org.sonar.java.cfg.CFG;
@@ -59,11 +58,11 @@
5958
import org.sonar.plugins.java.api.tree.WhileStatementTree;
6059

6160
import javax.annotation.Nullable;
61+
6262
import java.util.ArrayList;
6363
import java.util.Deque;
6464
import java.util.LinkedList;
6565
import java.util.List;
66-
import java.util.Map;
6766
import java.util.Set;
6867

6968
public class ExplodedGraphWalker extends BaseTreeVisitor {
@@ -176,7 +175,7 @@ private Iterable<ProgramState> startingStates(MethodTree tree, ProgramState ps)
176175
startingStates = Iterables.transform(startingStates, new Function<ProgramState, ProgramState>() {
177176
@Override
178177
public ProgramState apply(ProgramState input) {
179-
return ProgramState.put(input, variableTree.symbol(), sv);
178+
return input.put(variableTree.symbol(), sv);
180179
}
181180
});
182181

@@ -245,14 +244,14 @@ private void handleBranch(CFG.Block programPosition, Tree condition, boolean che
245244
Pair<List<ProgramState>, List<ProgramState>> pair = constraintManager.assumeDual(programState);
246245
for (ProgramState state : pair.a) {
247246
// enqueue false-branch, if feasible
248-
ProgramState ps = ProgramState.stackValue(state, SymbolicValue.FALSE_LITERAL);
247+
ProgramState ps = state.stackValue(SymbolicValue.FALSE_LITERAL);
249248
enqueue(new ExplodedGraph.ProgramPoint(programPosition.falseBlock(), 0), ps);
250249
if (checkPath) {
251250
alwaysTrueOrFalseChecker.evaluatedToFalse(condition);
252251
}
253252
}
254253
for (ProgramState state : pair.b) {
255-
ProgramState ps = ProgramState.stackValue(state, SymbolicValue.TRUE_LITERAL);
254+
ProgramState ps = state.stackValue(SymbolicValue.TRUE_LITERAL);
256255
// enqueue true-branch, if feasible
257256
enqueue(new ExplodedGraph.ProgramPoint(programPosition.trueBlock(), 0), ps);
258257
if (checkPath) {
@@ -272,9 +271,10 @@ private void visit(Tree tree, @Nullable Tree terminator) {
272271
MethodInvocationTree mit = (MethodInvocationTree) tree;
273272
setSymbolicValueOnFields(mit);
274273
// unstack arguments and method identifier
275-
programState = ProgramState.unstack(programState, mit.arguments().size() + 1).a;
274+
275+
programState = programState.unstackValue(mit.arguments().size() + 1).a;
276276
logState(mit);
277-
programState = ProgramState.stackValue(programState, constraintManager.createSymbolicValue(mit));
277+
programState = programState.stackValue(constraintManager.createSymbolicValue(mit));
278278
break;
279279
case LABELED_STATEMENT:
280280
case SWITCH_STATEMENT:
@@ -294,53 +294,59 @@ private void visit(Tree tree, @Nullable Tree terminator) {
294294
sv = SymbolicValue.NULL_LITERAL;
295295
}
296296
if (sv != null) {
297-
programState = ProgramState.put(programState, variableTree.symbol(), sv);
297+
programState = programState.put(variableTree.symbol(), sv);
298298
}
299299
} else {
300-
Pair<ProgramState, List<SymbolicValue>> unstack = ProgramState.unstack(programState, 1);
300+
301+
Pair<ProgramState, List<SymbolicValue>> unstack = programState.unstackValue(1);
301302
programState = unstack.a;
302-
programState = ProgramState.put(programState, variableTree.symbol(), unstack.b.get(0));
303+
programState = programState.put(variableTree.symbol(), unstack.b.get(0));
303304
}
304305
break;
305306
case TYPE_CAST:
306307
TypeCastTree typeCast = (TypeCastTree) tree;
307308
Type type = typeCast.type().symbolType();
308309
if (type.isPrimitive()) {
309-
Pair<ProgramState, List<SymbolicValue>> unstack = ProgramState.unstack(programState, 1);
310+
311+
Pair<ProgramState, List<SymbolicValue>> unstack = programState.unstackValue(1);
310312
programState = unstack.a;
311-
programState = ProgramState.stackValue(programState, constraintManager.createSymbolicValue(typeCast.expression()));
313+
programState = programState.stackValue(constraintManager.createSymbolicValue(typeCast.expression()));
312314
}
313315
break;
314316
case ASSIGNMENT:
315317
ExpressionTree variable = ((AssignmentExpressionTree) tree).variable();
316318
if (variable.is(Tree.Kind.IDENTIFIER)) {
317319
// FIXME restricted to identifiers for now.
318-
Pair<ProgramState, List<SymbolicValue>> unstack = ProgramState.unstack(programState, 2);
320+
321+
Pair<ProgramState, List<SymbolicValue>> unstack = programState.unstackValue(2);
319322
SymbolicValue value = unstack.b.get(1);
320323
programState = unstack.a;
321-
programState = ProgramState.put(programState, ((IdentifierTree) variable).symbol(), value);
322-
programState = ProgramState.stackValue(programState, value);
324+
programState = programState.put(((IdentifierTree) variable).symbol(), value);
325+
programState = programState.stackValue(value);
323326
}
324327
break;
325328
case ARRAY_ACCESS_EXPRESSION:
326329
ArrayAccessExpressionTree arrayAccessExpressionTree = (ArrayAccessExpressionTree) tree;
327330
// unstack expression and dimension
328-
Pair<ProgramState, List<SymbolicValue>> unstack = ProgramState.unstack(programState, 2);
331+
332+
Pair<ProgramState, List<SymbolicValue>> unstack = programState.unstackValue(2);
329333
programState = unstack.a;
330-
programState = ProgramState.stackValue(programState, constraintManager.createSymbolicValue(arrayAccessExpressionTree));
334+
programState = programState.stackValue(constraintManager.createSymbolicValue(arrayAccessExpressionTree));
331335
break;
332336
case NEW_ARRAY:
333337
NewArrayTree newArrayTree = (NewArrayTree) tree;
334-
programState = ProgramState.unstack(programState, newArrayTree.initializers().size()).a;
338+
339+
programState = programState.unstackValue(newArrayTree.initializers().size()).a;
335340
SymbolicValue svNewArray = constraintManager.createSymbolicValue(newArrayTree);
336-
programState = ProgramState.stackValue(programState, svNewArray);
341+
programState = programState.stackValue(svNewArray);
337342
programState = svNewArray.setSingleConstraint(programState, NullConstraint.NOT_NULL);
338343
break;
339344
case NEW_CLASS:
340345
NewClassTree newClassTree = (NewClassTree) tree;
341-
programState = ProgramState.unstack(programState, newClassTree.arguments().size()).a;
346+
347+
programState = programState.unstackValue(newClassTree.arguments().size()).a;
342348
SymbolicValue svNewClass = constraintManager.createSymbolicValue(newClassTree);
343-
programState = ProgramState.stackValue(programState, svNewClass);
349+
programState = programState.stackValue(svNewClass);
344350
programState = svNewClass.setSingleConstraint(programState, NullConstraint.NOT_NULL);
345351
break;
346352
case MULTIPLY:
@@ -361,11 +367,12 @@ private void visit(Tree tree, @Nullable Tree terminator) {
361367
case EQUAL_TO:
362368
case NOT_EQUAL_TO:
363369
// Consume two and produce one SV.
364-
Pair<ProgramState, List<SymbolicValue>> unstackBinary = ProgramState.unstack(programState, 2);
370+
371+
Pair<ProgramState, List<SymbolicValue>> unstackBinary = programState.unstackValue(2);
365372
programState = unstackBinary.a;
366373
SymbolicValue symbolicValue = constraintManager.createSymbolicValue(tree);
367374
symbolicValue.computedFrom(unstackBinary.b);
368-
programState = ProgramState.stackValue(programState, symbolicValue);
375+
programState = programState.stackValue(symbolicValue);
369376
break;
370377
case POSTFIX_INCREMENT:
371378
case POSTFIX_DECREMENT:
@@ -377,29 +384,31 @@ private void visit(Tree tree, @Nullable Tree terminator) {
377384
case LOGICAL_COMPLEMENT:
378385
case INSTANCE_OF:
379386
// consume one and produce one
380-
Pair<ProgramState, List<SymbolicValue>> unstackUnary = ProgramState.unstack(programState, 1);
387+
388+
Pair<ProgramState, List<SymbolicValue>> unstackUnary = programState.unstackValue(1);
381389
programState = unstackUnary.a;
382390
SymbolicValue unarySymbolicValue = constraintManager.createSymbolicValue(tree);
383391
unarySymbolicValue.computedFrom(unstackUnary.b);
384-
programState = ProgramState.stackValue(programState, unarySymbolicValue);
392+
programState = programState.stackValue(unarySymbolicValue);
385393
break;
386394
case IDENTIFIER:
387395
Symbol symbol = ((IdentifierTree) tree).symbol();
388-
SymbolicValue value = programState.values.get(symbol);
396+
SymbolicValue value = programState.getValue(symbol);
389397
if (value == null) {
390398
value = constraintManager.createSymbolicValue(tree);
391-
programState = ProgramState.put(programState, symbol, value);
399+
programState = programState.put(symbol, value);
392400
}
393-
programState = ProgramState.stackValue(programState, value);
401+
programState = programState.stackValue(value);
394402
break;
395403
case MEMBER_SELECT:
396404
MemberSelectExpressionTree mse = (MemberSelectExpressionTree) tree;
397405
if (!"class".equals(mse.identifier().name())) {
398-
Pair<ProgramState, List<SymbolicValue>> unstackMSE = ProgramState.unstack(programState, 1);
406+
407+
Pair<ProgramState, List<SymbolicValue>> unstackMSE = programState.unstackValue(1);
399408
programState = unstackMSE.a;
400409
}
401410
SymbolicValue mseValue = constraintManager.createSymbolicValue(tree);
402-
programState = ProgramState.stackValue(programState, mseValue);
411+
programState = programState.stackValue(mseValue);
403412
break;
404413
case INT_LITERAL:
405414
case LONG_LITERAL:
@@ -410,11 +419,11 @@ private void visit(Tree tree, @Nullable Tree terminator) {
410419
case STRING_LITERAL:
411420
case NULL_LITERAL:
412421
SymbolicValue val = constraintManager.evalLiteral((LiteralTree) tree);
413-
programState = ProgramState.stackValue(programState, val);
422+
programState = programState.stackValue(val);
414423
break;
415424
case LAMBDA_EXPRESSION:
416425
case METHOD_REFERENCE:
417-
programState = ProgramState.stackValue(programState, constraintManager.createSymbolicValue(tree));
426+
programState = programState.stackValue(constraintManager.createSymbolicValue(tree));
418427
break;
419428
default:
420429
}
@@ -425,7 +434,7 @@ private void visit(Tree tree, @Nullable Tree terminator) {
425434

426435
public void clearStack(Tree tree) {
427436
if (tree.parent().is(Tree.Kind.EXPRESSION_STATEMENT)) {
428-
programState = ProgramState.unstack(programState, programState.stack.size()).a;
437+
programState = programState.clearStack();
429438
}
430439
}
431440

@@ -451,26 +460,7 @@ private static boolean isLocalMethodInvocation(MethodInvocationTree tree) {
451460
}
452461

453462
private void resetFieldValues() {
454-
boolean changed = false;
455-
Map<Symbol, SymbolicValue> values = Maps.newHashMap(programState.values);
456-
for (Map.Entry<Symbol, SymbolicValue> entry : values.entrySet()) {
457-
Symbol symbol = entry.getKey();
458-
if (isField(symbol)) {
459-
VariableTree variable = ((Symbol.VariableSymbol) symbol).declaration();
460-
if (variable != null) {
461-
changed = true;
462-
SymbolicValue nonNullValue = constraintManager.supersedeSymbolicValue(variable);
463-
values.put(symbol, nonNullValue);
464-
}
465-
}
466-
}
467-
if (changed) {
468-
programState = new ProgramState(values, programState.constraints, programState.visitedPoints, programState.stack);
469-
}
470-
}
471-
472-
private static boolean isField(Symbol symbol) {
473-
return symbol.isVariableSymbol() && !symbol.owner().isMethodSymbol();
463+
programState = programState.resetFieldValues(constraintManager);
474464
}
475465

476466
private void logState(MethodInvocationTree mit) {
@@ -495,8 +485,7 @@ public void enqueue(ExplodedGraph.ProgramPoint programPoint, ProgramState progra
495485
throw new ExplodedGraphTooBigException("Program state constraints are too big : stopping Symbolic Execution for method "
496486
+ methodTree.simpleName().name() + "in class " + methodTree.symbol().owner().name());
497487
}
498-
ExplodedGraph.Node cachedNode = explodedGraph.getNode(programPoint,
499-
new ProgramState(programState.values, programState.constraints, programState.visitedPoints.put(programPoint, nbOfExecution+1), programState.stack));
488+
ExplodedGraph.Node cachedNode = explodedGraph.getNode(programPoint, programState.visitingPoint(programPoint));
500489
if (!cachedNode.isNew) {
501490
// has been enqueued earlier
502491
return;
@@ -506,7 +495,7 @@ public void enqueue(ExplodedGraph.ProgramPoint programPoint, ProgramState progra
506495

507496
private boolean isExplodedGraphTooBig(ProgramState programState) {
508497
// Arbitrary formula to avoid out of memory errors.
509-
return steps + workList.size() > MAX_STEPS / 2 && programState.constraints.size() > 75;
498+
return steps + workList.size() > MAX_STEPS / 2 && programState.constraintsSize() > 75;
510499
}
511500

512501
}

0 commit comments

Comments
 (0)