Skip to content

Commit 3c58089

Browse files
authored
Merge pull request github#11712 from aschackmull/java/constant-guards
Java: Apply deadcode guard to data flow nodes.
2 parents a4c3ea2 + b4607d3 commit 3c58089

File tree

11 files changed

+251
-143
lines changed

11 files changed

+251
-143
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The data flow library now disregards flow through code that is dead based on some basic constant propagation, for example, guards like `if (1+1>3)`.
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* Provdides a module to calculate constant integer and boolean values.
3+
*/
4+
5+
import java
6+
7+
signature boolean getBoolValSig(Expr e);
8+
9+
signature int getIntValSig(Expr e);
10+
11+
/**
12+
* Given predicates defining boolean and integer constants, this module
13+
* calculates additional boolean and integer constants using only the rules that
14+
* apply to compile-time constants.
15+
*
16+
* The input and output predicates are expected to be mutually recursive.
17+
*/
18+
module CalculateConstants<getBoolValSig/1 getBoolVal, getIntValSig/1 getIntVal> {
19+
/** Gets the value of a constant boolean expression. */
20+
pragma[assume_small_delta]
21+
boolean calculateBooleanValue(Expr e) {
22+
// No casts relevant to booleans.
23+
// `!` is the only unary operator that evaluates to a boolean.
24+
result = getBoolVal(e.(LogNotExpr).getExpr()).booleanNot()
25+
or
26+
// Handle binary expressions that have integer operands and a boolean result.
27+
exists(BinaryExpr b, int left, int right |
28+
b = e and
29+
left = getIntVal(b.getLeftOperand()) and
30+
right = getIntVal(b.getRightOperand())
31+
|
32+
(
33+
b instanceof LTExpr and
34+
if left < right then result = true else result = false
35+
)
36+
or
37+
(
38+
b instanceof LEExpr and
39+
if left <= right then result = true else result = false
40+
)
41+
or
42+
(
43+
b instanceof GTExpr and
44+
if left > right then result = true else result = false
45+
)
46+
or
47+
(
48+
b instanceof GEExpr and
49+
if left >= right then result = true else result = false
50+
)
51+
or
52+
(
53+
b instanceof ValueOrReferenceEqualsExpr and
54+
if left = right then result = true else result = false
55+
)
56+
or
57+
(
58+
b instanceof ValueOrReferenceNotEqualsExpr and
59+
if left != right then result = true else result = false
60+
)
61+
)
62+
or
63+
// Handle binary expressions that have boolean operands and a boolean result.
64+
exists(BinaryExpr b, boolean left, boolean right |
65+
b = e and
66+
left = getBoolVal(b.getLeftOperand()) and
67+
right = getBoolVal(b.getRightOperand())
68+
|
69+
(
70+
b instanceof ValueOrReferenceEqualsExpr and
71+
if left = right then result = true else result = false
72+
)
73+
or
74+
(
75+
b instanceof ValueOrReferenceNotEqualsExpr and
76+
if left != right then result = true else result = false
77+
)
78+
or
79+
(b instanceof AndBitwiseExpr or b instanceof AndLogicalExpr) and
80+
result = left.booleanAnd(right)
81+
or
82+
(b instanceof OrBitwiseExpr or b instanceof OrLogicalExpr) and
83+
result = left.booleanOr(right)
84+
or
85+
b instanceof XorBitwiseExpr and result = left.booleanXor(right)
86+
)
87+
or
88+
// Ternary expressions, where the `true` and `false` expressions are boolean constants.
89+
exists(ConditionalExpr ce, boolean condition |
90+
ce = e and
91+
condition = getBoolVal(ce.getCondition()) and
92+
result = getBoolVal(ce.getBranchExpr(condition))
93+
)
94+
or
95+
// If a `Variable` is final, its value is its initializer, if it exists.
96+
exists(Variable v | e = v.getAnAccess() and v.isFinal() |
97+
result = getBoolVal(v.getInitializer())
98+
)
99+
}
100+
101+
/** Gets the value of a constant integer expression. */
102+
pragma[assume_small_delta]
103+
int calculateIntValue(Expr e) {
104+
exists(IntegralType t | e.getType() = t | t.getName().toLowerCase() != "long") and
105+
(
106+
exists(CastingExpr cast, int val | cast = e and val = getIntVal(cast.getExpr()) |
107+
if cast.getType().hasName("byte")
108+
then result = (val + 128).bitAnd(255) - 128
109+
else
110+
if cast.getType().hasName("short")
111+
then result = (val + 32768).bitAnd(65535) - 32768
112+
else
113+
if cast.getType().hasName("char")
114+
then result = val.bitAnd(65535)
115+
else result = val
116+
)
117+
or
118+
result = getIntVal(e.(PlusExpr).getExpr())
119+
or
120+
result = -getIntVal(e.(MinusExpr).getExpr())
121+
or
122+
result = getIntVal(e.(BitNotExpr).getExpr()).bitNot()
123+
or
124+
// No `int` value for `LogNotExpr`.
125+
exists(BinaryExpr b, int v1, int v2 |
126+
b = e and
127+
v1 = getIntVal(b.getLeftOperand()) and
128+
v2 = getIntVal(b.getRightOperand())
129+
|
130+
b instanceof MulExpr and result = v1 * v2
131+
or
132+
b instanceof DivExpr and result = v1 / v2
133+
or
134+
b instanceof RemExpr and result = v1 % v2
135+
or
136+
b instanceof AddExpr and result = v1 + v2
137+
or
138+
b instanceof SubExpr and result = v1 - v2
139+
or
140+
b instanceof LeftShiftExpr and result = v1.bitShiftLeft(v2)
141+
or
142+
b instanceof RightShiftExpr and result = v1.bitShiftRightSigned(v2)
143+
or
144+
b instanceof UnsignedRightShiftExpr and result = v1.bitShiftRight(v2)
145+
or
146+
b instanceof AndBitwiseExpr and result = v1.bitAnd(v2)
147+
or
148+
b instanceof OrBitwiseExpr and result = v1.bitOr(v2)
149+
or
150+
b instanceof XorBitwiseExpr and result = v1.bitXor(v2)
151+
// No `int` value for `AndLogicalExpr` or `OrLogicalExpr`.
152+
// No `int` value for `LTExpr`, `GTExpr`, `LEExpr`, `GEExpr`, `ValueOrReferenceEqualsExpr` or `ValueOrReferenceNotEqualsExpr`.
153+
)
154+
or
155+
// Ternary conditional, with constant condition.
156+
exists(ConditionalExpr ce, boolean condition |
157+
ce = e and
158+
condition = getBoolVal(ce.getCondition()) and
159+
result = getIntVal(ce.getBranchExpr(condition))
160+
)
161+
or
162+
// If a `Variable` is final, its value is its initializer, if it exists.
163+
exists(Variable v | e = v.getAnAccess() and v.isFinal() |
164+
result = getIntVal(v.getInitializer())
165+
)
166+
)
167+
}
168+
}

java/ql/lib/semmle/code/java/Expr.qll

Lines changed: 14 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java
66
private import semmle.code.java.frameworks.android.Compose
7+
private import semmle.code.java.Constants
78

89
/** A common super-class that represents all kinds of expressions. */
910
class Expr extends ExprParent, @expr {
@@ -130,6 +131,7 @@ private predicate primitiveOrString(Type t) {
130131
* See JLS v8, section 15.28 (Constant Expressions).
131132
*/
132133
class CompileTimeConstantExpr extends Expr {
134+
pragma[assume_small_delta]
133135
CompileTimeConstantExpr() {
134136
primitiveOrString(this.getType()) and
135137
(
@@ -179,6 +181,7 @@ class CompileTimeConstantExpr extends Expr {
179181
/**
180182
* Gets the string value of this expression, where possible.
181183
*/
184+
pragma[assume_small_delta]
182185
pragma[nomagic]
183186
string getStringValue() {
184187
result = this.(StringLiteral).getValue()
@@ -204,75 +207,13 @@ class CompileTimeConstantExpr extends Expr {
204207
/**
205208
* Gets the boolean value of this expression, where possible.
206209
*/
210+
pragma[assume_small_delta]
211+
pragma[nomagic]
207212
boolean getBooleanValue() {
208213
// Literal value.
209214
result = this.(BooleanLiteral).getBooleanValue()
210215
or
211-
// No casts relevant to booleans.
212-
// `!` is the only unary operator that evaluates to a boolean.
213-
result = this.(LogNotExpr).getExpr().(CompileTimeConstantExpr).getBooleanValue().booleanNot()
214-
or
215-
// Handle binary expressions that have integer operands and a boolean result.
216-
exists(BinaryExpr b, int left, int right |
217-
b = this and
218-
left = b.getLeftOperand().(CompileTimeConstantExpr).getIntValue() and
219-
right = b.getRightOperand().(CompileTimeConstantExpr).getIntValue()
220-
|
221-
(
222-
b instanceof LTExpr and
223-
if left < right then result = true else result = false
224-
)
225-
or
226-
(
227-
b instanceof LEExpr and
228-
if left <= right then result = true else result = false
229-
)
230-
or
231-
(
232-
b instanceof GTExpr and
233-
if left > right then result = true else result = false
234-
)
235-
or
236-
(
237-
b instanceof GEExpr and
238-
if left >= right then result = true else result = false
239-
)
240-
or
241-
(
242-
b instanceof ValueOrReferenceEqualsExpr and
243-
if left = right then result = true else result = false
244-
)
245-
or
246-
(
247-
b instanceof ValueOrReferenceNotEqualsExpr and
248-
if left != right then result = true else result = false
249-
)
250-
)
251-
or
252-
// Handle binary expressions that have boolean operands and a boolean result.
253-
exists(BinaryExpr b, boolean left, boolean right |
254-
b = this and
255-
left = b.getLeftOperand().(CompileTimeConstantExpr).getBooleanValue() and
256-
right = b.getRightOperand().(CompileTimeConstantExpr).getBooleanValue()
257-
|
258-
(
259-
b instanceof ValueOrReferenceEqualsExpr and
260-
if left = right then result = true else result = false
261-
)
262-
or
263-
(
264-
b instanceof ValueOrReferenceNotEqualsExpr and
265-
if left != right then result = true else result = false
266-
)
267-
or
268-
(b instanceof AndBitwiseExpr or b instanceof AndLogicalExpr) and
269-
result = left.booleanAnd(right)
270-
or
271-
(b instanceof OrBitwiseExpr or b instanceof OrLogicalExpr) and
272-
result = left.booleanOr(right)
273-
or
274-
b instanceof XorBitwiseExpr and result = left.booleanXor(right)
275-
)
216+
result = CalcCompileTimeConstants::calculateBooleanValue(this)
276217
or
277218
// Handle binary expressions that have `String` operands and a boolean result.
278219
exists(BinaryExpr b, string left, string right |
@@ -300,18 +241,6 @@ class CompileTimeConstantExpr extends Expr {
300241
)
301242
or
302243
// Note: no `getFloatValue()`, so we cannot support binary expressions with float or double operands.
303-
// Ternary expressions, where the `true` and `false` expressions are boolean compile-time constants.
304-
exists(ConditionalExpr ce, boolean condition |
305-
ce = this and
306-
condition = ce.getCondition().(CompileTimeConstantExpr).getBooleanValue() and
307-
result = ce.getBranchExpr(condition).(CompileTimeConstantExpr).getBooleanValue()
308-
)
309-
or
310-
// Simple or qualified names where the variable is final and the initializer is a constant.
311-
exists(Variable v | this = v.getAnAccess() |
312-
result = v.getInitializer().(CompileTimeConstantExpr).getBooleanValue()
313-
)
314-
or
315244
result = this.(LiveLiteral).getValue().getBooleanValue()
316245
}
317246

@@ -329,75 +258,20 @@ class CompileTimeConstantExpr extends Expr {
329258
result = this.(IntegerLiteral).getIntValue()
330259
or
331260
result = this.(CharacterLiteral).getCodePointValue()
332-
or
333-
exists(CastingExpr cast, int val |
334-
cast = this and val = cast.getExpr().(CompileTimeConstantExpr).getIntValue()
335-
|
336-
if cast.getType().hasName("byte")
337-
then result = (val + 128).bitAnd(255) - 128
338-
else
339-
if cast.getType().hasName("short")
340-
then result = (val + 32768).bitAnd(65535) - 32768
341-
else
342-
if cast.getType().hasName("char")
343-
then result = val.bitAnd(65535)
344-
else result = val
345-
)
346-
or
347-
result = this.(PlusExpr).getExpr().(CompileTimeConstantExpr).getIntValue()
348-
or
349-
result = -this.(MinusExpr).getExpr().(CompileTimeConstantExpr).getIntValue()
350-
or
351-
result = this.(BitNotExpr).getExpr().(CompileTimeConstantExpr).getIntValue().bitNot()
352-
or
353-
// No `int` value for `LogNotExpr`.
354-
exists(BinaryExpr b, int v1, int v2 |
355-
b = this and
356-
v1 = b.getLeftOperand().(CompileTimeConstantExpr).getIntValue() and
357-
v2 = b.getRightOperand().(CompileTimeConstantExpr).getIntValue()
358-
|
359-
b instanceof MulExpr and result = v1 * v2
360-
or
361-
b instanceof DivExpr and result = v1 / v2
362-
or
363-
b instanceof RemExpr and result = v1 % v2
364-
or
365-
b instanceof AddExpr and result = v1 + v2
366-
or
367-
b instanceof SubExpr and result = v1 - v2
368-
or
369-
b instanceof LeftShiftExpr and result = v1.bitShiftLeft(v2)
370-
or
371-
b instanceof RightShiftExpr and result = v1.bitShiftRightSigned(v2)
372-
or
373-
b instanceof UnsignedRightShiftExpr and result = v1.bitShiftRight(v2)
374-
or
375-
b instanceof AndBitwiseExpr and result = v1.bitAnd(v2)
376-
or
377-
b instanceof OrBitwiseExpr and result = v1.bitOr(v2)
378-
or
379-
b instanceof XorBitwiseExpr and result = v1.bitXor(v2)
380-
// No `int` value for `AndLogicalExpr` or `OrLogicalExpr`.
381-
// No `int` value for `LTExpr`, `GTExpr`, `LEExpr`, `GEExpr`, `ValueOrReferenceEqualsExpr` or `ValueOrReferenceNotEqualsExpr`.
382-
)
383-
or
384-
// Ternary conditional, with compile-time constant condition.
385-
exists(ConditionalExpr ce, boolean condition |
386-
ce = this and
387-
condition = ce.getCondition().(CompileTimeConstantExpr).getBooleanValue() and
388-
result = ce.getBranchExpr(condition).(CompileTimeConstantExpr).getIntValue()
389-
)
390-
or
391-
// If a `Variable` is a `CompileTimeConstantExpr`, its value is its initializer.
392-
exists(Variable v | this = v.getAnAccess() |
393-
result = v.getInitializer().(CompileTimeConstantExpr).getIntValue()
394-
)
395261
)
396262
or
263+
result = CalcCompileTimeConstants::calculateIntValue(this)
264+
or
397265
result = this.(LiveLiteral).getValue().getIntValue()
398266
}
399267
}
400268

269+
private boolean getBoolValue(Expr e) { result = e.(CompileTimeConstantExpr).getBooleanValue() }
270+
271+
private int getIntValue(Expr e) { result = e.(CompileTimeConstantExpr).getIntValue() }
272+
273+
private module CalcCompileTimeConstants = CalculateConstants<getBoolValue/1, getIntValue/1>;
274+
401275
/** An expression parent is an element that may have an expression as its child. */
402276
class ExprParent extends @exprparent, Top { }
403277

0 commit comments

Comments
 (0)