Skip to content

Commit

Permalink
Support for <<, >> and |, &, ^ in ValueEvaluator and `Bin…
Browse files Browse the repository at this point in the history
…aryOperation` (#1333)

Support for `<<`, `>>` and `|` in `ValueEvaluator` and `BinaryOperation`
  • Loading branch information
oxisto authored Oct 18, 2023
1 parent 8f5cd45 commit a1d8f2f
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ open class ValueEvaluator(
"/=" -> handleDiv(lhsValue, rhsValue, expr)
"*",
"*=" -> handleTimes(lhsValue, rhsValue, expr)
"<<" -> handleShiftLeft(lhsValue, rhsValue, expr)
">>" -> handleShiftRight(lhsValue, rhsValue, expr)
"&" -> handleBitwiseAnd(lhsValue, rhsValue, expr)
"|" -> handleBitwiseOr(lhsValue, rhsValue, expr)
"^" -> handleBitwiseXor(lhsValue, rhsValue, expr)
">" -> handleGreater(lhsValue, rhsValue, expr)
">=" -> handleGEq(lhsValue, rhsValue, expr)
"<" -> handleLess(lhsValue, rhsValue, expr)
Expand Down Expand Up @@ -202,6 +207,51 @@ open class ValueEvaluator(
}
}

private fun handleShiftLeft(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? {
return when {
// right side must always be an int
lhsValue is Int && rhsValue is Int -> lhsValue shl rhsValue
lhsValue is Long && rhsValue is Int -> lhsValue shl rhsValue
else -> cannotEvaluate(expr, this)
}
}

private fun handleShiftRight(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? {
return when {
// right side must always be an int
lhsValue is Int && rhsValue is Int -> lhsValue shr rhsValue
lhsValue is Long && rhsValue is Int -> lhsValue shr rhsValue
else -> cannotEvaluate(expr, this)
}
}

private fun handleBitwiseAnd(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? {
return when {
// left and right must be equal and only long and int are supported
lhsValue is Int && rhsValue is Int -> lhsValue and rhsValue
lhsValue is Long && rhsValue is Long -> lhsValue and rhsValue
else -> cannotEvaluate(expr, this)
}
}

private fun handleBitwiseOr(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? {
return when {
// left and right must be equal and only long and int are supported
lhsValue is Int && rhsValue is Int -> lhsValue or rhsValue
lhsValue is Long && rhsValue is Long -> lhsValue or rhsValue
else -> cannotEvaluate(expr, this)
}
}

private fun handleBitwiseXor(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? {
return when {
// left and right must be equal and only long and int are supported
lhsValue is Int && rhsValue is Int -> lhsValue xor rhsValue
lhsValue is Long && rhsValue is Long -> lhsValue xor rhsValue
else -> cannotEvaluate(expr, this)
}
}

private fun handleGreater(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? {
return if (lhsValue is Number && rhsValue is Number) {
lhsValue.compareTo(rhsValue) > 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,106 @@ class ValueEvaluatorTest {
}
}

@Test
fun testHandleShiftLeft() {
with(TestHandler(TestLanguageFrontend())) {
val binOp = newBinaryOperator("<<")
// Int.plus
binOp.lhs = newLiteral(3, primitiveType("int"))
binOp.rhs = newLiteral(2, primitiveType("int"))
assertEquals(12, ValueEvaluator().evaluate(binOp))

// Long.plus
binOp.lhs = newLiteral(3L, primitiveType("long"))
binOp.rhs = newLiteral(2, primitiveType("int"))
assertEquals(12L, ValueEvaluator().evaluate(binOp))

binOp.lhs = newLiteral("Hello", primitiveType("string"))
binOp.rhs = newLiteral(" world", primitiveType("string"))
assertEquals("{<<}", ValueEvaluator().evaluate(binOp))
}
}

@Test
fun testHandleShiftRight() {
with(TestHandler(TestLanguageFrontend())) {
val binOp = newBinaryOperator(">>")
// Int.plus
binOp.lhs = newLiteral(3, primitiveType("int"))
binOp.rhs = newLiteral(2, primitiveType("int"))
assertEquals(0, ValueEvaluator().evaluate(binOp))

// Long.plus
binOp.lhs = newLiteral(3L, primitiveType("long"))
binOp.rhs = newLiteral(2, primitiveType("int"))
assertEquals(0L, ValueEvaluator().evaluate(binOp))

binOp.lhs = newLiteral("Hello", primitiveType("string"))
binOp.rhs = newLiteral(" world", primitiveType("string"))
assertEquals("{>>}", ValueEvaluator().evaluate(binOp))
}
}

@Test
fun testHandleBitwiseAnd() {
with(TestHandler(TestLanguageFrontend())) {
val binOp = newBinaryOperator("&")
// Int.plus
binOp.lhs = newLiteral(3, primitiveType("int"))
binOp.rhs = newLiteral(2, primitiveType("int"))
assertEquals(2, ValueEvaluator().evaluate(binOp))

// Long.plus
binOp.lhs = newLiteral(3L, primitiveType("long"))
binOp.rhs = newLiteral(2L, primitiveType("long"))
assertEquals(2L, ValueEvaluator().evaluate(binOp))

binOp.lhs = newLiteral("Hello", primitiveType("string"))
binOp.rhs = newLiteral(" world", primitiveType("string"))
assertEquals("{&}", ValueEvaluator().evaluate(binOp))
}
}

@Test
fun testHandleBitwiseOr() {
with(TestHandler(TestLanguageFrontend())) {
val binOp = newBinaryOperator("|")
// Int.plus
binOp.lhs = newLiteral(3, primitiveType("int"))
binOp.rhs = newLiteral(2, primitiveType("int"))
assertEquals(3, ValueEvaluator().evaluate(binOp))

// Long.plus
binOp.lhs = newLiteral(3L, primitiveType("long"))
binOp.rhs = newLiteral(2L, primitiveType("long"))
assertEquals(3L, ValueEvaluator().evaluate(binOp))

binOp.lhs = newLiteral("Hello", primitiveType("string"))
binOp.rhs = newLiteral(" world", primitiveType("string"))
assertEquals("{|}", ValueEvaluator().evaluate(binOp))
}
}

@Test
fun testHandleBitwiseXor() {
with(TestHandler(TestLanguageFrontend())) {
val binOp = newBinaryOperator("^")
// Int.plus
binOp.lhs = newLiteral(3, primitiveType("int"))
binOp.rhs = newLiteral(2, primitiveType("int"))
assertEquals(1, ValueEvaluator().evaluate(binOp))

// Long.plus
binOp.lhs = newLiteral(3L, primitiveType("long"))
binOp.rhs = newLiteral(2L, primitiveType("long"))
assertEquals(1L, ValueEvaluator().evaluate(binOp))

binOp.lhs = newLiteral("Hello", primitiveType("string"))
binOp.rhs = newLiteral(" world", primitiveType("string"))
assertEquals("{^}", ValueEvaluator().evaluate(binOp))
}
}

@Test
fun testHandleUnary() {
with(TestHandler(TestLanguageFrontend())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ abstract class Language<T : LanguageFrontend<*, *>> : Node() {
"-",
"*",
"/" -> arithmeticOpTypePropagation(operation.lhs.type, operation.rhs.type)
"&",
"|",
"^",
"<<",
">>" ->
if (operation.lhs.type.isPrimitive && operation.rhs.type.isPrimitive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,16 @@ class DeclarationTest {
with(tu) {
val values =
mapOf(
"zeroShift" to Pair(0, objectType("int")),
"zeroAnd" to Pair(0, objectType("int")),
"one" to Pair(1, objectType("p.custom")),
"oneAsWell" to Pair(1, objectType("p.custom")),
"oneShift" to Pair(1, primitiveType("int")),
"two" to Pair(2, primitiveType("int")),
"twoShift" to Pair(2, primitiveType("int")),
"three" to Pair(3, primitiveType("int")),
"threeOr" to Pair(3, primitiveType("int")),
"threeXor" to Pair(3, primitiveType("int")),
"four" to Pair(4, primitiveType("int")),
"tenAsWell" to Pair(10, primitiveType("int")),
"five" to Pair(5, primitiveType("int")),
Expand Down
9 changes: 9 additions & 0 deletions cpg-language-go/src/test/resources/golang/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ const (
fiveAsWell = 5 + iota*100
onehundredandfive
)

const (
oneShift = 1 << iota
twoShift
zeroShift = 1 >> iota
zeroAnd = oneShift & twoShift
threeOr = oneShift | twoShift
threeXor = oneShift ^ twoShift
)

0 comments on commit a1d8f2f

Please sign in to comment.