From b3704b88ad939bf10a9ab58554f011b86f6bea2e Mon Sep 17 00:00:00 2001 From: Alexey Volkov Date: Tue, 5 Sep 2023 17:14:13 +0300 Subject: [PATCH] Method node builder fixes (#170) * Wrong conversion of some condition instructions with zero comparison from IR to jvm bytecode #167 Wrong conversion of numeric constants from IR to jvm bytecode #166 * Bug with local variables creation #165 --- .../org/jacodb/impl/cfg/MethodNodeBuilder.kt | 18 +++-- .../org/jacodb/testing/cfg/ReverseIRTest.kt | 51 +++++++++++++++ .../kotlin/org/jacodb/testing/ir/ReverseIR.kt | 65 +++++++++++++++++++ 3 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/ReverseIRTest.kt create mode 100644 jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/ir/ReverseIR.kt diff --git a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/MethodNodeBuilder.kt b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/MethodNodeBuilder.kt index a63b4e047..b907950b6 100644 --- a/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/MethodNodeBuilder.kt +++ b/jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/MethodNodeBuilder.kt @@ -265,26 +265,30 @@ class MethodNodeBuilder( is JcRawLtExpr -> Triple(JcRawInt(0), Opcodes.IFLT, Opcodes.IF_ICMPLT) else -> error("Unknown condition expr: $cond") } + val elseBranchTarget: LabelNode currentInsnList.add( when { cond.lhv == zeroValue -> { cond.rhv.accept(this) - JumpInsnNode(zeroCmpOpcode, trueTarget) + elseBranchTarget = trueTarget + JumpInsnNode(zeroCmpOpcode, falseTarget) } cond.rhv == zeroValue -> { cond.lhv.accept(this) + elseBranchTarget = falseTarget JumpInsnNode(zeroCmpOpcode, trueTarget) } else -> { cond.lhv.accept(this) cond.rhv.accept(this) + elseBranchTarget = falseTarget JumpInsnNode(defaultOpcode, trueTarget) } } ) - currentInsnList.add(JumpInsnNode(Opcodes.GOTO, falseTarget)) + currentInsnList.add(JumpInsnNode(Opcodes.GOTO, elseBranchTarget)) updateStackInfo(-stackSize) } @@ -713,7 +717,7 @@ class MethodNodeBuilder( override fun visitJcRawInt(value: JcRawInt) { currentInsnList.add( - when (value.value) { + when (value.value as Comparable) { in -1..5 -> InsnNode(Opcodes.ICONST_0 + value.value) in Byte.MIN_VALUE..Byte.MAX_VALUE -> IntInsnNode(Opcodes.BIPUSH, value.value) in Short.MIN_VALUE..Short.MAX_VALUE -> IntInsnNode(Opcodes.SIPUSH, value.value) @@ -725,8 +729,8 @@ class MethodNodeBuilder( override fun visitJcRawLong(value: JcRawLong) { currentInsnList.add( - when (value.value) { - in 0..1 -> InsnNode(Opcodes.LCONST_0 + value.value.toInt()) + when { + (value.value as Comparable).let { it >= 0 && it <= 1 } -> InsnNode(Opcodes.LCONST_0 + value.value.toInt()) else -> LdcInsnNode(value.value) } ) @@ -735,7 +739,7 @@ class MethodNodeBuilder( override fun visitJcRawFloat(value: JcRawFloat) { currentInsnList.add( - when (value.value) { + when (value.value as Comparable) { 0.0F -> InsnNode(Opcodes.FCONST_0) 1.0F -> InsnNode(Opcodes.FCONST_1) 2.0F -> InsnNode(Opcodes.FCONST_2) @@ -747,7 +751,7 @@ class MethodNodeBuilder( override fun visitJcRawDouble(value: JcRawDouble) { currentInsnList.add( - when (value.value) { + when (value.value as Comparable) { 0.0 -> InsnNode(Opcodes.DCONST_0) 1.0 -> InsnNode(Opcodes.DCONST_1) else -> LdcInsnNode(value.value) diff --git a/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/ReverseIRTest.kt b/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/ReverseIRTest.kt new file mode 100644 index 000000000..a0c5f7696 --- /dev/null +++ b/jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/ReverseIRTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jacodb.testing.cfg + +import org.jacodb.api.ext.findClass +import org.jacodb.testing.WithDB +import org.jacodb.testing.ir.DoubleComparison +import org.jacodb.testing.ir.InvokeMethodWithException +import org.jacodb.testing.ir.WhenExpr +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class ReverseIRTest : BaseInstructionsTest() { + companion object : WithDB() + + @Test + fun comparison() { + val clazz = testAndLoadClass(cp.findClass()) + val m = clazz.declaredMethods.first { it.name == "box" } + assertEquals("OK", m.invoke(null)) + } + + @Test + fun `when`() { + val clazz = testAndLoadClass(cp.findClass()) + val m = clazz.declaredMethods.first { it.name == "box" } + assertEquals("OK", m.invoke(null)) + } + + @Test + fun `local vars`() { + val clazz = testAndLoadClass(cp.findClass()) + val m = clazz.declaredMethods.first { it.name == "box" } + assertEquals("OK", m.invoke(null)) + } + +} \ No newline at end of file diff --git a/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/ir/ReverseIR.kt b/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/ir/ReverseIR.kt new file mode 100644 index 000000000..9d33dfa71 --- /dev/null +++ b/jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/ir/ReverseIR.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jacodb.testing.ir + + +object DoubleComparison { + + @JvmStatic + fun box(): String { + if ((-0.0 as Comparable) >= 0.0) return "fail" + return "OK" + } +} + + +object WhenExpr { + + val x = 1 + + @JvmStatic + fun box() = + when (val y = x) { + in 0..2 -> "OK" + else -> "Fail: $y" + } + +} + +object InvokeMethodWithException { + + class A { + fun lol(a: Int): Int { + return 888/a + } + } + + @JvmStatic + fun box():String { + val method = A::class.java.getMethod("lol", Int::class.java) + var failed = false + try { + method.invoke(null, 0) + } + catch(e: Exception) { + failed = true + } + + return if (!failed) "fail" else "OK" + } + +} \ No newline at end of file