Java: Implement shared range analysis signatures.

This commit is contained in:
Anders Schack-Mulligen
2023-10-18 15:04:36 +02:00
parent c1c4a5bfcf
commit 36082808d3
6 changed files with 414 additions and 29 deletions

View File

@@ -8,6 +8,7 @@ upgrades: upgrades
dependencies: dependencies:
codeql/dataflow: ${workspace} codeql/dataflow: ${workspace}
codeql/mad: ${workspace} codeql/mad: ${workspace}
codeql/rangeanalysis: ${workspace}
codeql/regex: ${workspace} codeql/regex: ${workspace}
codeql/tutorial: ${workspace} codeql/tutorial: ${workspace}
codeql/typetracking: ${workspace} codeql/typetracking: ${workspace}

View File

@@ -1090,6 +1090,24 @@ class PrimitiveType extends Type, @primitive {
override string getAPrimaryQlClass() { result = "PrimitiveType" } override string getAPrimaryQlClass() { result = "PrimitiveType" }
} }
private int getByteSize(PrimitiveType t) {
t.hasName("boolean") and result = 1
or
t.hasName("byte") and result = 1
or
t.hasName("char") and result = 2
or
t.hasName("short") and result = 2
or
t.hasName("int") and result = 4
or
t.hasName("float") and result = 4
or
t.hasName("long") and result = 8
or
t.hasName("double") and result = 8
}
/** The type of the `null` literal. */ /** The type of the `null` literal. */
class NullType extends Type, @primitive { class NullType extends Type, @primitive {
NullType() { this.hasName("<nulltype>") } NullType() { this.hasName("<nulltype>") }
@@ -1282,6 +1300,12 @@ class IntegralType extends Type {
name = ["byte", "char", "short", "int", "long"] name = ["byte", "char", "short", "int", "long"]
) )
} }
/** Gets the size in bytes of this numeric type. */
final int getByteSize() {
result = getByteSize(this) or
result = getByteSize(this.(BoxedType).getPrimitiveType())
}
} }
/** A boolean type, which may be either a primitive or a boxed type. */ /** A boolean type, which may be either a primitive or a boxed type. */

View File

@@ -25,16 +25,8 @@ abstract class Bound extends TBound {
/** Gets an expression that equals this bound. */ /** Gets an expression that equals this bound. */
Expr getExpr() { result = this.getExpr(0) } Expr getExpr() { result = this.getExpr(0) }
/** /** Gets the location of this bound. */
* Holds if this element is at the specified location. abstract Location getLocation();
* The location spans column `sc` of line `sl` to
* column `ec` of line `el` in file `path`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
} }
/** /**
@@ -45,6 +37,8 @@ class ZeroBound extends Bound, TBoundZero {
override string toString() { result = "0" } override string toString() { result = "0" }
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta } override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
} }
/** /**
@@ -58,9 +52,7 @@ class SsaBound extends Bound, TBoundSsa {
override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 } override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 }
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { override Location getLocation() { result = this.getSsa().getLocation() }
this.getSsa().getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
} }
/** /**
@@ -72,7 +64,5 @@ class ExprBound extends Bound, TBoundExpr {
override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 } override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 }
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { override Location getLocation() { result = this.getExpr().getLocation() }
this.getExpr().getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
} }

View File

@@ -75,6 +75,354 @@ private import semmle.code.java.Reflection
private import semmle.code.java.Collections private import semmle.code.java.Collections
private import semmle.code.java.Maps private import semmle.code.java.Maps
import Bound import Bound
private import codeql.rangeanalysis.RangeAnalysis
module Sem implements Semantic {
private import java as J
private import SSA as SSA
private import RangeUtils as RU
private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon as SSAReadPos
private import semmle.code.java.controlflow.internal.GuardsLogic as GL
class Expr = J::Expr;
class ConstantIntegerExpr = RU::ConstantIntegerExpr;
abstract class BinaryExpr extends Expr {
Expr getLeftOperand() {
result = this.(J::BinaryExpr).getLeftOperand() or result = this.(J::AssignOp).getDest()
}
Expr getRightOperand() {
result = this.(J::BinaryExpr).getRightOperand() or result = this.(J::AssignOp).getRhs()
}
final Expr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() }
final predicate hasOperands(Expr e1, Expr e2) {
this.getLeftOperand() = e1 and this.getRightOperand() = e2
or
this.getLeftOperand() = e2 and this.getRightOperand() = e1
}
}
class AddExpr extends BinaryExpr {
AddExpr() { this instanceof J::AddExpr or this instanceof J::AssignAddExpr }
}
class SubExpr extends BinaryExpr {
SubExpr() { this instanceof J::SubExpr or this instanceof J::AssignSubExpr }
}
class MulExpr extends BinaryExpr {
MulExpr() { this instanceof J::MulExpr or this instanceof J::AssignMulExpr }
}
class DivExpr extends BinaryExpr {
DivExpr() { this instanceof J::DivExpr or this instanceof J::AssignDivExpr }
}
class RemExpr extends BinaryExpr {
RemExpr() { this instanceof J::RemExpr or this instanceof J::AssignRemExpr }
}
class BitAndExpr extends BinaryExpr {
BitAndExpr() { this instanceof J::AndBitwiseExpr or this instanceof J::AssignAndExpr }
}
class BitOrExpr extends BinaryExpr {
BitOrExpr() { this instanceof J::OrBitwiseExpr or this instanceof J::AssignOrExpr }
}
class ShiftLeftExpr extends BinaryExpr {
ShiftLeftExpr() { this instanceof J::LeftShiftExpr or this instanceof J::AssignLeftShiftExpr }
}
class ShiftRightExpr extends BinaryExpr {
ShiftRightExpr() {
this instanceof J::RightShiftExpr or this instanceof J::AssignRightShiftExpr
}
}
class ShiftRightUnsignedExpr extends BinaryExpr {
ShiftRightUnsignedExpr() {
this instanceof J::UnsignedRightShiftExpr or this instanceof J::AssignUnsignedRightShiftExpr
}
}
class RelationalExpr = J::ComparisonExpr;
abstract class UnaryExpr extends Expr {
abstract Expr getOperand();
}
class ConvertExpr extends UnaryExpr instanceof CastingExpr {
override Expr getOperand() { result = super.getExpr() }
}
class BoxExpr extends UnaryExpr {
BoxExpr() { none() }
override Expr getOperand() { none() }
}
class UnboxExpr extends UnaryExpr {
UnboxExpr() { none() }
override Expr getOperand() { none() }
}
class NegateExpr extends UnaryExpr instanceof MinusExpr {
override Expr getOperand() { result = super.getExpr() }
}
// TODO: Implement once utils are properly shared
class AddOneExpr extends UnaryExpr {
AddOneExpr() { none() }
override Expr getOperand() { none() }
}
// TODO: Implement once utils are properly shared
class SubOneExpr extends UnaryExpr {
SubOneExpr() { none() }
override Expr getOperand() { none() }
}
class ConditionalExpr = J::ConditionalExpr;
class BasicBlock = J::BasicBlock;
class Guard extends GL::Guard {
Expr asExpr() { result = this }
}
predicate implies_v2(Guard g1, boolean b1, Guard g2, boolean b2) {
GL::implies_v2(g1, b1, g2, b2)
}
predicate guardDirectlyControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
RU::guardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
}
class Type = J::Type;
class IntegerType extends J::IntegralType {
predicate isSigned() { not this instanceof CharacterType }
}
class FloatingPointType extends Type {
FloatingPointType() { none() }
}
class AddressType extends Type {
AddressType() { none() }
}
final private class FinalSsaVariable = SSA::SsaVariable;
class SsaVariable extends FinalSsaVariable {
Expr getAUse() { result = super.getAUse() }
}
class SsaPhiNode extends SsaVariable instanceof SSA::SsaPhiNode { }
class SsaExplicitUpdate extends SsaVariable instanceof SSA::SsaExplicitUpdate { }
final private class FinalSsaReadPosition = SSAReadPos::SsaReadPosition;
class SsaReadPosition extends FinalSsaReadPosition {
predicate hasReadOfVar(SsaVariable v) { super.hasReadOfVar(v) }
}
class SsaReadPositionPhiInputEdge extends SsaReadPosition instanceof SSAReadPos::SsaReadPositionPhiInputEdge
{
predicate phiInput(SsaPhiNode phi, SsaVariable inp) { super.phiInput(phi, inp) }
}
class SsaReadPositionBlock extends SsaReadPosition instanceof SSAReadPos::SsaReadPositionBlock {
BasicBlock getBlock() { result = super.getBlock() }
}
predicate backEdge(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge) {
RU::backEdge(phi, inp, edge)
}
predicate conversionCannotOverflow = safeCast/2;
}
module SignInp implements SignAnalysisSig<Sem> {
private import SignAnalysis
private import internal.rangeanalysis.Sign
predicate semPositive = positive/1;
predicate semNegative = negative/1;
predicate semStrictlyPositive = strictlyPositive/1;
predicate semStrictlyNegative = strictlyNegative/1;
predicate semMayBePositive(Sem::Expr e) { exprSign(e) = TPos() }
predicate semMayBeNegative(Sem::Expr e) { exprSign(e) = TNeg() }
}
module Modulus implements ModulusAnalysisSig<Sem> {
class ModBound = Bound;
predicate semExprModulus = exprModulus/4;
}
module IntDelta implements DeltaSig {
class Delta = int;
bindingset[d]
bindingset[result]
float toFloat(Delta d) { result = d }
bindingset[d]
bindingset[result]
int toInt(Delta d) { result = d }
bindingset[n]
bindingset[result]
Delta fromInt(int n) { result = n }
bindingset[f]
Delta fromFloat(float f) { result = f }
}
module JavaLangImpl implements LangSig<Sem, IntDelta> {
predicate ignoreSsaReadCopy(Sem::Expr e) { none() }
/**
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
*/
predicate hasConstantBound(Sem::Expr e, int bound, boolean upper) {
(
e.(MethodCall).getMethod() instanceof StringLengthMethod or
e.(MethodCall).getMethod() instanceof CollectionSizeMethod or
e.(MethodCall).getMethod() instanceof MapSizeMethod or
e.(FieldRead).getField() instanceof ArrayLengthField
) and
bound = 0 and
upper = false
or
exists(Method read |
e.(MethodCall).getMethod().overrides*(read) and
read.getDeclaringType() instanceof TypeInputStream and
read.hasName("read") and
read.getNumberOfParameters() = 0
|
upper = true and bound = 255
or
upper = false and bound = -1
)
}
/**
* Holds if `e2 >= e1 + delta` (if `upper = false`) or `e2 <= e1 + delta` (if `upper = true`).
*/
predicate hasBound(Sem::Expr e2, Sem::Expr e1, int delta, boolean upper) {
exists(RandomDataSource rds |
e2 = rds.getOutput() and
(
e1 = rds.getUpperBoundExpr() and
delta = -1 and
upper = true
or
e1 = rds.getLowerBoundExpr() and
delta = 0 and
upper = false
)
)
or
exists(MethodCall ma, Method m |
e2 = ma and
ma.getMethod() = m and
(
m.hasName("max") and upper = false
or
m.hasName("min") and upper = true
) and
m.getDeclaringType().hasQualifiedName("java.lang", "Math") and
e1 = ma.getAnArgument() and
delta = 0
)
}
predicate ignoreExprBound(Sem::Expr e) { none() }
predicate ignoreZeroLowerBound(Sem::Expr e) { none() }
predicate ignoreSsaReadArithmeticExpr(Sem::Expr e) { none() }
predicate ignoreSsaReadAssignment(Sem::SsaVariable v) { none() }
Sem::Expr specificSsaRead(Sem::SsaVariable v, int delta) { none() }
predicate additionalValueFlowStep(Sem::Expr dest, Sem::Expr src, int delta) { none() }
Sem::Type getAlternateType(Sem::Expr e) { none() }
Sem::Type getAlternateTypeForSsaVariable(Sem::SsaVariable var) { none() }
predicate javaCompatibility() { any() }
}
module Utils implements UtilSig<Sem, IntDelta> {
private import RangeUtils as RU
private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon as SSAReadPos
Sem::Expr semSsaRead(Sem::SsaVariable v, int delta) { result = RU::ssaRead(v, delta) }
Sem::Guard semEqFlowCond(
Sem::SsaVariable v, Sem::Expr e, int delta, boolean isEq, boolean testIsTrue
) {
result = RU::eqFlowCond(v, e, delta, isEq, testIsTrue)
}
predicate semSsaUpdateStep(Sem::SsaExplicitUpdate v, Sem::Expr e, int delta) {
RU::ssaUpdateStep(v, e, delta)
}
predicate semValueFlowStep = RU::valueFlowStep/3;
Sem::Type getTrackedTypeForSsaVariable(Sem::SsaVariable var) {
result = var.getSourceVariable().getType()
}
Sem::Type getTrackedType(Sem::Expr e) { result = e.getType() }
predicate rankedPhiInput(
Sem::SsaPhiNode phi, Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge, int r
) {
SSAReadPos::rankedPhiInput(phi, inp, edge, r)
}
predicate maxPhiInputRank(Sem::SsaPhiNode phi, int rix) { SSAReadPos::maxPhiInputRank(phi, rix) }
}
module Bounds implements BoundSig<Location, Sem, IntDelta> {
class SemBound = Bound;
class SemZeroBound = ZeroBound;
class SemSsaBound extends SsaBound {
Sem::SsaVariable getAVariable() { result = super.getSsa() }
}
}
module Overflow implements OverflowSig<Sem, IntDelta> {
predicate semExprDoesNotOverflow(boolean positively, Sem::Expr expr) {
positively = [true, false] and exists(expr)
}
}
module Range =
RangeStage<Location, Sem, IntDelta, Bounds, Overflow, JavaLangImpl, SignInp, Modulus, Utils>;
cached cached
private module RangeAnalysisCache { private module RangeAnalysisCache {
@@ -138,7 +486,8 @@ private predicate boundCondition(
sub.getLeftOperand() = ssaRead(v, d) and sub.getLeftOperand() = ssaRead(v, d) and
sub.getRightOperand() = e and sub.getRightOperand() = e and
upper = true and upper = true and
delta = d + c.getIntValue() delta = d + c.getIntValue() and
okInt(d.(float) + c.getIntValue().(float))
or or
// (v - d) - e > c // (v - d) - e > c
comp.getGreaterOperand() = sub and comp.getGreaterOperand() = sub and
@@ -146,7 +495,8 @@ private predicate boundCondition(
sub.getLeftOperand() = ssaRead(v, d) and sub.getLeftOperand() = ssaRead(v, d) and
sub.getRightOperand() = e and sub.getRightOperand() = e and
upper = false and upper = false and
delta = d + c.getIntValue() delta = d + c.getIntValue() and
okInt(d.(float) + c.getIntValue().(float))
or or
// e - (v - d) < c // e - (v - d) < c
comp.getLesserOperand() = sub and comp.getLesserOperand() = sub and
@@ -154,7 +504,8 @@ private predicate boundCondition(
sub.getLeftOperand() = e and sub.getLeftOperand() = e and
sub.getRightOperand() = ssaRead(v, d) and sub.getRightOperand() = ssaRead(v, d) and
upper = false and upper = false and
delta = d - c.getIntValue() delta = d - c.getIntValue() and
okInt(d.(float) - c.getIntValue().(float))
or or
// e - (v - d) > c // e - (v - d) > c
comp.getGreaterOperand() = sub and comp.getGreaterOperand() = sub and
@@ -162,7 +513,8 @@ private predicate boundCondition(
sub.getLeftOperand() = e and sub.getLeftOperand() = e and
sub.getRightOperand() = ssaRead(v, d) and sub.getRightOperand() = ssaRead(v, d) and
upper = true and upper = true and
delta = d - c.getIntValue() delta = d - c.getIntValue() and
okInt(d.(float) - c.getIntValue().(float))
) )
} }
@@ -245,7 +597,8 @@ private Guard boundFlowCond(SsaVariable v, Expr e, int delta, boolean upper, boo
or or
resultIsStrict = false and d3 = 0 resultIsStrict = false and d3 = 0
) and ) and
delta = d1 + d2 + d3 delta = d1 + d2 + d3 and
okInt(d1.(float) + d2.(float) + d3.(float))
) )
or or
exists(boolean testIsTrue0 | exists(boolean testIsTrue0 |
@@ -260,7 +613,8 @@ private Guard boundFlowCond(SsaVariable v, Expr e, int delta, boolean upper, boo
exists(SsaVariable v2, int d | exists(SsaVariable v2, int d |
// equality needs to control guard // equality needs to control guard
result.getBasicBlock() = eqSsaCondDirectlyControls(v, v2, d) and result.getBasicBlock() = eqSsaCondDirectlyControls(v, v2, d) and
result = boundFlowCond(v2, e, delta - d, upper, testIsTrue) result = boundFlowCond(v2, e, delta - d, upper, testIsTrue) and
okInt((delta - d).(float) + d.(float))
) )
} }
@@ -272,7 +626,8 @@ private BasicBlock eqSsaCondDirectlyControls(SsaVariable v1, SsaVariable v2, int
exists(Guard guardEq, int d1, int d2, boolean eqIsTrue | exists(Guard guardEq, int d1, int d2, boolean eqIsTrue |
guardEq = eqFlowCond(v1, ssaRead(v2, d1), d2, true, eqIsTrue) and guardEq = eqFlowCond(v1, ssaRead(v2, d1), d2, true, eqIsTrue) and
delta = d2 - d1 and delta = d2 - d1 and
guardEq.directlyControls(result, eqIsTrue) guardEq.directlyControls(result, eqIsTrue) and
okInt(d2.(float) - d1.(float))
) )
} }
@@ -530,6 +885,7 @@ private predicate boundFlowStepMul(Expr e2, Expr e1, int factor) {
* therefore only valid for non-negative numbers. * therefore only valid for non-negative numbers.
*/ */
private predicate boundFlowStepDiv(Expr e2, Expr e1, int factor) { private predicate boundFlowStepDiv(Expr e2, Expr e1, int factor) {
e2.getType() instanceof IntegralType and
exists(ConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 | exists(ConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
exists(DivExpr e | exists(DivExpr e |
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = k e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = k
@@ -570,7 +926,8 @@ private predicate boundedSsa(
// upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta // upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta
// upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta // upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta
delta = d1 + d2 and delta = d1 + d2 and
(if r1 instanceof NoReason then reason = r2 else reason = r1) (if r1 instanceof NoReason then reason = r2 else reason = r1) and
okInt(d1.(float) + d2.(float))
) )
or or
exists(int d, Reason r1, Reason r2 | exists(int d, Reason r1, Reason r2 |
@@ -800,6 +1157,9 @@ private predicate boundedCastExpr(
bounded(cast.getExpr(), b, delta, upper, fromBackEdge, origdelta, reason) bounded(cast.getExpr(), b, delta, upper, fromBackEdge, origdelta, reason)
} }
bindingset[f]
private predicate okInt(float f) { -2.pow(31) <= f and f <= 2.pow(31) - 1 }
/** /**
* Holds if `b + delta` is a valid bound for `e`. * Holds if `b + delta` is a valid bound for `e`.
* - `upper = true` : `e <= b + delta` * - `upper = true` : `e <= b + delta`
@@ -833,7 +1193,8 @@ private predicate bounded(
bounded(mid, b, d2, upper, fromBackEdge, origdelta, reason) and bounded(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
// upper = true: e <= mid + d1 <= b + d1 + d2 = b + delta // upper = true: e <= mid + d1 <= b + d1 + d2 = b + delta
// upper = false: e >= mid + d1 >= b + d1 + d2 = b + delta // upper = false: e >= mid + d1 >= b + d1 + d2 = b + delta
delta = d1 + d2 delta = d1 + d2 and
okInt(d1.(float) + d2.(float))
) )
or or
exists(SsaPhiNode phi | exists(SsaPhiNode phi |
@@ -846,7 +1207,8 @@ private predicate bounded(
not e instanceof ConstantIntegerExpr and not e instanceof ConstantIntegerExpr and
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
b instanceof ZeroBound and b instanceof ZeroBound and
delta = d * factor delta = d * factor and
okInt(d.(float) * factor.(float))
) )
or or
exists(Expr mid, int factor, int d | exists(Expr mid, int factor, int d |
@@ -889,7 +1251,8 @@ private predicate bounded(
boundedAddition(e, upper, b1, true, d1, fbe1, od1, r1) and boundedAddition(e, upper, b1, true, d1, fbe1, od1, r1) and
boundedAddition(e, upper, b2, false, d2, fbe2, od2, r2) and boundedAddition(e, upper, b2, false, d2, fbe2, od2, r2) and
delta = d1 + d2 and delta = d1 + d2 and
fromBackEdge = fbe1.booleanOr(fbe2) fromBackEdge = fbe1.booleanOr(fbe2) and
okInt(d1.(float) + d2.(float))
| |
b = b1 and origdelta = od1 and reason = r1 and b2 instanceof ZeroBound b = b1 and origdelta = od1 and reason = r1 and b2 instanceof ZeroBound
or or

View File

@@ -145,6 +145,9 @@ class ConstantStringExpr extends Expr {
string getStringValue() { constantStringExpr(this, result) } string getStringValue() { constantStringExpr(this, result) }
} }
bindingset[f]
private predicate okInt(float f) { -2.pow(31) <= f and f <= 2.pow(31) - 1 }
/** /**
* Gets an expression that equals `v - d`. * Gets an expression that equals `v - d`.
*/ */
@@ -153,14 +156,16 @@ Expr ssaRead(SsaVariable v, int delta) {
or or
exists(int d1, ConstantIntegerExpr c | exists(int d1, ConstantIntegerExpr c |
result.(AddExpr).hasOperands(ssaRead(v, d1), c) and result.(AddExpr).hasOperands(ssaRead(v, d1), c) and
delta = d1 - c.getIntValue() delta = d1 - c.getIntValue() and
okInt(d1.(float) - c.getIntValue().(float))
) )
or or
exists(SubExpr sub, int d1, ConstantIntegerExpr c | exists(SubExpr sub, int d1, ConstantIntegerExpr c |
result = sub and result = sub and
sub.getLeftOperand() = ssaRead(v, d1) and sub.getLeftOperand() = ssaRead(v, d1) and
sub.getRightOperand() = c and sub.getRightOperand() = c and
delta = d1 + c.getIntValue() delta = d1 + c.getIntValue() and
okInt(d1.(float) + c.getIntValue().(float))
) )
or or
v.(SsaExplicitUpdate).getDefiningExpr().(PreIncExpr) = result and delta = 0 v.(SsaExplicitUpdate).getDefiningExpr().(PreIncExpr) = result and delta = 0

View File

@@ -10,6 +10,8 @@ class SsaVariable = Ssa::SsaVariable;
class Expr = J::Expr; class Expr = J::Expr;
class Location = J::Location;
class IntegralType = J::IntegralType; class IntegralType = J::IntegralType;
class ConstantIntegerExpr = RU::ConstantIntegerExpr; class ConstantIntegerExpr = RU::ConstantIntegerExpr;