import java
import semmle.code.java.arithmetic.Overflow
import semmle.code.java.dataflow.SSA
import semmle.code.java.controlflow.Guards
import semmle.code.java.dataflow.RangeAnalysis

class NumericNarrowingCastExpr extends CastExpr {
  NumericNarrowingCastExpr() {
    exists(NumericType sourceType, NumericType targetType |
      sourceType = getExpr().getType() and targetType = getType()
    |
      not targetType.(NumType).widerThanOrEqualTo(sourceType.(NumType))
    )
  }
}

class RightShiftOp extends Expr {
  RightShiftOp() {
    this instanceof RShiftExpr or
    this instanceof URShiftExpr or
    this instanceof AssignRShiftExpr or
    this instanceof AssignURShiftExpr
  }

  private Expr getLhs() {
    this.(BinaryExpr).getLeftOperand() = result or
    this.(Assignment).getDest() = result
  }

  Variable getShiftedVariable() {
    getLhs() = result.getAnAccess() or
    getLhs().getProperExpr().(AndBitwiseExpr).getAnOperand() = result.getAnAccess()
  }
}

predicate boundedRead(RValue read) {
  exists(SsaVariable v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
    read = v.getAUse() and
    cb.controls(read.getBasicBlock(), testIsTrue) and
    cb.getCondition() = comp
  |
    comp.getLesserOperand() = v.getAUse() and testIsTrue = true
    or
    comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
  )
}

predicate castCheck(RValue read) {
  exists(EqualityTest eq, CastExpr cast |
    cast.getExpr() = read and
    eq.hasOperands(cast, read.getVariable().getAnAccess())
  )
}

class SmallType extends Type {
  SmallType() {
    this instanceof BooleanType or
    this.(PrimitiveType).hasName("byte") or
    this.(BoxedType).getPrimitiveType().hasName("byte")
  }
}

predicate smallExpr(Expr e) {
  exists(int low, int high |
    bounded(e, any(ZeroBound zb), low, false, _) and
    bounded(e, any(ZeroBound zb), high, true, _) and
    high - low < 256
  )
}
