Skip to content

Commit c4468eb

Browse files
DaniilStepanovlehvolk
authored andcommitted
Yet another bug with local variables
1 parent e6e4742 commit c4468eb

File tree

5 files changed

+63
-37
lines changed

5 files changed

+63
-37
lines changed

jacodb-core/src/main/kotlin/org/jacodb/impl/cfg/RawInstListBuilder.kt

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,33 @@ private val AbstractInsnNode.isBranchingInst
159159
private val AbstractInsnNode.isTerminateInst
160160
get() = this is InsnNode && (this.opcode == Opcodes.ATHROW || this.opcode in Opcodes.IRETURN..Opcodes.RETURN)
161161

162+
private fun LabelNode.isBetween(labelStart: LabelNode, labelEnd: LabelNode): Boolean {
163+
var curNode: AbstractInsnNode? = this
164+
var left = false
165+
var right = false
166+
while (curNode != null) {
167+
if (curNode == labelStart) {
168+
left = true
169+
break
170+
}
171+
if (curNode == labelEnd && curNode != this) {
172+
return false
173+
}
174+
curNode = curNode.previous
175+
176+
}
177+
if (!left) return false
178+
curNode = this
179+
while (curNode != null) {
180+
if (curNode == labelEnd) {
181+
right = true
182+
break
183+
}
184+
curNode = curNode.next
185+
}
186+
return right
187+
}
188+
162189
private val TryCatchBlockNode.typeOrDefault get() = this.type ?: THROWABLE_CLASS
163190

164191
private val Collection<TryCatchBlockNode>.commonTypeOrDefault
@@ -381,30 +408,6 @@ class RawInstListBuilder(
381408
return currentFrame.locals.getValue(variable)
382409
}
383410

384-
// fun LabelNode.isBetween(labelStart: LabelNode, labelEnd: LabelNode): Boolean {
385-
// var curNode: AbstractInsnNode = labelStart
386-
// while (curNode != labelEnd && curNode != null) {
387-
// if (this == curNode) {
388-
// return true
389-
// }
390-
// curNode = curNode.next
391-
// }
392-
// return this == curNode
393-
// }
394-
//
395-
// val instrLabel = methodNode.instructions.takeWhile { it != insn }.last { it is LabelNode } as LabelNode
396-
// val locals = methodNode.localVariables.filter { it.index == variable }
397-
// locals.map { instrLabel.isBetween(it.start, it.end) }
398-
// locals
399-
400-
401-
// val oldVar = currentFrame.locals[variable]?.let {
402-
// if (expr.typeName.isPrimitive.xor(it.typeName.isPrimitive)) {
403-
// null
404-
// } else {
405-
// it
406-
// }
407-
// }
408411
private fun local(
409412
variable: Int,
410413
expr: JcRawValue,
@@ -919,6 +922,8 @@ class RawInstListBuilder(
919922
else -> {
920923
val assignment = nextRegister(type)
921924
for ((node, frame) in predFrames) {
925+
//TODO! Make anything with that (we should take into account subtyping)
926+
//assigment.isSubtypeOf(frame[variable]!!.typeName)
922927
if (frame != null) {
923928
if (node.isBranchingInst) {
924929
addInstruction(node, JcRawAssignInst(method, assignment, frame[variable]!!), 0)
@@ -1191,17 +1196,26 @@ class RawInstListBuilder(
11911196
}
11921197
}
11931198

1194-
private fun mergeFrames(frames: Map<AbstractInsnNode, Frame>): Frame {
1199+
private fun mergeFrames(frames: Map<AbstractInsnNode, Frame>, curLabel: LabelNode): Frame {
11951200
val frameSet = frames.values
11961201
if (frames.isEmpty()) return currentFrame
11971202
if (frames.size == 1) return frameSet.first()
11981203

11991204
val allLocals = frameSet.flatMap { it.locals.keys }
12001205
val localTypes = allLocals
12011206
.filter { local -> frameSet.all { local in it.locals } }
1202-
.associateWith {
1203-
val types = frameSet.map { frame -> frame[it]!!.typeName }
1204-
types.firstOrNull { it != NULL } ?: NULL
1207+
.associateWith { ind ->
1208+
val types = frameSet.map { frame -> frame[ind]!!.typeName }
1209+
//If we have several variables types for one register we have to search right type in debug info otherwise we cannot guarantee anything
1210+
if (types.toSet().size != 1) {
1211+
methodNode.localVariables
1212+
.firstOrNull { curLabel.isBetween(it.start, it.end) && it.index == ind }
1213+
?.desc
1214+
?.let { TypeNameImpl(it) }
1215+
?: types.firstOrNull { it != NULL } ?: NULL
1216+
} else {
1217+
types.firstOrNull { it != NULL } ?: NULL
1218+
}
12051219
}
12061220
.toSortedMap()
12071221
val newLocals = localTypes.copyLocals(frames).toPersistentMap()
@@ -1227,7 +1241,7 @@ class RawInstListBuilder(
12271241
if (predecessorFrames.size == 1) {
12281242
currentFrame = predecessorFrames.first()
12291243
} else if (predecessors.size == predecessorFrames.size) {
1230-
currentFrame = mergeFrames(predecessors.zip(predecessorFrames).toMap())
1244+
currentFrame = mergeFrames(predecessors.zip(predecessorFrames).toMap(), insnNode)
12311245
}
12321246
val catchEntries = methodNode.tryCatchBlocks.filter { it.handler == insnNode }
12331247

jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/BaseInstructionsTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ abstract class BaseInstructionsTest : BaseTest() {
5555
}
5656

5757

58-
protected fun testClass(klass: JcClassOrInterface, validateLineNumbers: Boolean = true) {
59-
testAndLoadClass(klass, false, validateLineNumbers)
58+
protected fun testClass(klass: JcClassOrInterface, validateLineNumbers: Boolean = true, muteGraphChecker: Boolean = false) {
59+
testAndLoadClass(klass, false, validateLineNumbers, muteGraphChecker)
6060
}
6161

6262
protected fun testAndLoadClass(klass: JcClassOrInterface, muteGraphChecker: Boolean = false): Class<*> {
63-
return testAndLoadClass(klass, true, validateLineNumbers = true)!!
63+
return testAndLoadClass(klass, true, validateLineNumbers = true, muteGraphChecker = muteGraphChecker)!!
6464
}
6565

6666
private fun testAndLoadClass(

jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/IRTest.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,11 @@ class IRTest : BaseInstructionsTest() {
301301
runAlongLib(guavaLib)
302302
}
303303

304-
// todo: make this test green
304+
@Test
305+
fun `get ir of asm`() {
306+
runAlongLib(asmLib, muteGraphChecker = true)
307+
}
308+
305309
@Test
306310
fun `get ir of kotlinx-coroutines`() {
307311
runAlongLib(kotlinxCoroutines, false)
@@ -320,7 +324,7 @@ class IRTest : BaseInstructionsTest() {
320324
}
321325
}
322326

323-
private fun runAlongLib(file: File, validateLineNumbers: Boolean = true) {
327+
private fun runAlongLib(file: File, validateLineNumbers: Boolean = true, muteGraphChecker: Boolean = false) {
324328
println("Run along: ${file.absolutePath}")
325329

326330
val classes = JarLocation(file, isRuntime = false, object : JavaVersion {
@@ -332,7 +336,7 @@ class IRTest : BaseInstructionsTest() {
332336
val clazz = cp.findClass(it.key)
333337
if (!clazz.isAnnotation && !clazz.isInterface) {
334338
println("Testing class: ${it.key}")
335-
testClass(clazz, validateLineNumbers)
339+
testClass(clazz, validateLineNumbers, muteGraphChecker)
336340
}
337341
}
338342
}

jacodb-core/src/test/kotlin/org/jacodb/testing/cfg/KotlinInstructionsTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ class KotlinInstructionsTest: BaseInstructionsTest() {
4848
// fun `kotlin overloading test`() = runKotlinTest(Overloading::class.java.name)
4949

5050
//We have to mute graph checker because of empty catch-es in try/catch blocks
51-
// @Test
52-
// fun `kotlin try catch finally`() = runKotlinTest(TryCatchFinally::class.java.name, muteGraphChecker = true)
51+
@Test
52+
fun `kotlin try catch finally`() = runKotlinTest(TryCatchFinally::class.java.name, muteGraphChecker = true)
5353

5454
@Test
5555
fun `kotlin try catch finally 2`() = runKotlinTest(TryCatchFinally2::class.java.name, muteGraphChecker = true)

jacodb-core/src/testFixtures/kotlin/org/jacodb/testing/LibrariesMixin.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ val guavaLib: File
3333
}
3434
}
3535

36+
val asmLib: File
37+
get() {
38+
val asmUrl = classpath.first { it.contains("/asm/") }
39+
return File(asmUrl).also {
40+
Assertions.assertTrue(it.isFile && it.exists())
41+
}
42+
}
43+
3644
val kotlinxCoroutines: File
3745
get() {
3846
val coroutines = classpath.first { it.contains("kotlinx-coroutines-") }

0 commit comments

Comments
 (0)