CFG: fix label binding for last instruction of finally block

So #KT-18698 Fixed
This commit is contained in:
Mikhail Glukhikh
2017-07-12 13:15:38 +03:00
committed by Mikhail Glukhikh
parent ab0e734e7d
commit c0e1b3ef2d
6 changed files with 254 additions and 1 deletions
@@ -395,7 +395,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
internalSinkInstruction = copy
}
}
if (finishIndex < mutableInstructionList.size) {
if (finishIndex < originalPseudocode.mutableInstructionList.size) {
repeatLabelsBindingForInstruction(originalPseudocode.mutableInstructionList[finishIndex],
originalToCopy,
originalLabelsForInstruction)
@@ -0,0 +1,151 @@
== s ==
val s = mutableListOf<String>()
---------------------
L0:
1 <START>
v(val s = mutableListOf<String>())
mark(mutableListOf<String>())
magic[UNRESOLVED_CALL](mutableListOf<String>()|!<v0>) -> <v1>
w(s|<v1>)
L1:
<END> NEXT:[<SINK>]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================
== test ==
fun test(name: String?, flag: Boolean): Boolean {
try {
name?.let {
if (flag) {
s.add(it)
}
else {
s.remove(it)
}
return true
}
return false
} finally {
name?.hashCode()
}
}
---------------------
L0:
1 <START>
v(name: String?)
magic[FAKE_INITIALIZER](name: String?) -> <v0>
w(name|<v0>)
v(flag: Boolean)
magic[FAKE_INITIALIZER](flag: Boolean) -> <v1>
w(flag|<v1>)
2 mark({ try { name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } finally { name?.hashCode() } })
mark(try { name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } finally { name?.hashCode() })
jmp?(L2) NEXT:[mark({ name?.hashCode() }), mark({ name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false })]
3 mark({ name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false })
mark(name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true })
jf(L3) NEXT:[jmp?(L2), r(name) -> <v2>]
r(name) -> <v2>
mark({ if (flag) { s.add(it) } else { s.remove(it) } return true })
jmp?(L4) NEXT:[r({ if (flag) { s.add(it) } else { s.remove(it) } return true }) -> <v3>, d({ if (flag) { s.add(it) } else { s.remove(it) } return true })]
d({ if (flag) { s.add(it) } else { s.remove(it) } return true }) NEXT:[<SINK>]
L4 [after local declaration]:
r({ if (flag) { s.add(it) } else { s.remove(it) } return true }) -> <v3> PREV:[jmp?(L4)]
mark(let { if (flag) { s.add(it) } else { s.remove(it) } return true })
call(let { if (flag) { s.add(it) } else { s.remove(it) } return true }, let|<v2>, <v3>)
L3 [result of call]:
jmp?(L2) NEXT:[mark({ name?.hashCode() }), r(false) -> <v5>] PREV:[jf(L3), call(let { if (flag) { s.add(it) } else { s.remove(it) } return true }, let|<v2>, <v3>)]
r(false) -> <v5>
6 mark({ name?.hashCode() })
mark(name?.hashCode())
jf(L12) NEXT:[ret(*|<v5>) L1, r(name) -> <v11>]
r(name) -> <v11>
mark(hashCode())
call(hashCode(), hashCode|<v11>) -> <v12>
L12 [copy of L10, result of call]:
3 ret(*|<v5>) L1 NEXT:[<END>] PREV:[jf(L12), call(hashCode(), hashCode|<v11>) -> <v12>]
- 2 jmp?(L2) NEXT:[mark({ name?.hashCode() }), jmp(L13)] PREV:[]
- jmp(L13) NEXT:[mark({ name?.hashCode() })] PREV:[]
L2 [onExceptionToFinallyBlock]:
6 mark({ name?.hashCode() }) PREV:[jmp?(L2), jmp?(L2)]
mark(name?.hashCode())
jf(L14) NEXT:[jmp(error), r(name) -> <v11>]
r(name) -> <v11>
mark(hashCode())
call(hashCode(), hashCode|<v11>) -> <v12>
L14 [copy of L10, result of call]:
2 jmp(error) NEXT:[<ERROR>] PREV:[jf(L14), call(hashCode(), hashCode|<v11>) -> <v12>]
L13 [skipFinallyToErrorBlock]:
- 6 mark({ name?.hashCode() }) PREV:[]
- mark(name?.hashCode()) PREV:[]
- jf(L15) NEXT:[merge(try { name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } finally { name?.hashCode() }|!<v6>) -> <v7>, r(name) -> <v11>] PREV:[]
- r(name) -> <v11> PREV:[]
- mark(hashCode()) PREV:[]
- call(hashCode(), hashCode|<v11>) -> <v12> PREV:[]
L15 [copy of L10, result of call]:
- 2 merge(try { name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } finally { name?.hashCode() }|!<v6>) -> <v7> PREV:[]
L1:
1 <END> NEXT:[<SINK>] PREV:[ret(*|<v10>) L1, ret(*|<v5>) L1]
error:
<ERROR> PREV:[jmp(error)]
sink:
<SINK> PREV:[<ERROR>, <END>, d({ if (flag) { s.add(it) } else { s.remove(it) } return true })]
=====================
== anonymous_0 ==
{
if (flag) {
s.add(it)
}
else {
s.remove(it)
}
return true
}
---------------------
L5:
4 <START>
5 mark(if (flag) { s.add(it) } else { s.remove(it) } return true)
mark(if (flag) { s.add(it) } else { s.remove(it) })
r(flag) -> <v0>
jf(L7|<v0>) NEXT:[mark({ s.remove(it) }), mark({ s.add(it) })]
6 mark({ s.add(it) })
mark(s.add(it))
r(s) -> <v1>
r(it) -> <v2>
magic[VALUE_CONSUMER](it|<v2>) -> <v3>
mark(add(it))
call(add(it), <ERROR FUNCTION>|<v1>) -> <v4>
5 jmp(L8) NEXT:[merge(if (flag) { s.add(it) } else { s.remove(it) }|<v4>, <v8>) -> <v9>]
L7 [else branch]:
6 mark({ s.remove(it) }) PREV:[jf(L7|<v0>)]
mark(s.remove(it))
r(s) -> <v5>
r(it) -> <v6>
magic[VALUE_CONSUMER](it|<v6>) -> <v7>
mark(remove(it))
call(remove(it), <ERROR FUNCTION>|<v5>) -> <v8>
L8 ['if' expression result]:
5 merge(if (flag) { s.add(it) } else { s.remove(it) }|<v4>, <v8>) -> <v9> PREV:[jmp(L8), call(remove(it), <ERROR FUNCTION>|<v5>) -> <v8>]
r(true) -> <v10>
L9 [start finally]:
6 mark({ name?.hashCode() })
mark(name?.hashCode())
jf(L10) NEXT:[ret(*|<v10>) L1, r(name) -> <v11>]
r(name) -> <v11>
mark(hashCode())
call(hashCode(), hashCode|<v11>) -> <v12>
L10 [result of call]:
L11 [finish finally]:
5 ret(*|<v10>) L1 NEXT:[<END>] PREV:[jf(L10), call(hashCode(), hashCode|<v11>) -> <v12>]
- 4 ret(*|!<v13>) L6 PREV:[]
L6:
<END> NEXT:[<SINK>] PREV:[]
error:
<ERROR> PREV:[]
sink:
<SINK> PREV:[<ERROR>, <END>]
=====================
@@ -0,0 +1,22 @@
// See KT-18698
val s = mutableListOf<String>()
fun test(name: String?, flag: Boolean): Boolean {
try {
name?.let {
if (flag) {
s.add(it)
}
else {
s.remove(it)
}
return true
}
return false
} finally {
name?.hashCode()
}
}
@@ -0,0 +1,68 @@
== s ==
val s = mutableListOf<String>()
---------------------
mutableListOf !<v0>: *
mutableListOf<String>() <v1>: {<: [ERROR : Type for mutableListOf<String>()]} NEW: magic[UNRESOLVED_CALL](mutableListOf<String>()|!<v0>) -> <v1>
=====================
== test ==
fun test(name: String?, flag: Boolean): Boolean {
try {
name?.let {
if (flag) {
s.add(it)
}
else {
s.remove(it)
}
return true
}
return false
} finally {
name?.hashCode()
}
}
---------------------
<v0>: {<: String?} NEW: magic[FAKE_INITIALIZER](name: String?) -> <v0>
<v1>: Boolean NEW: magic[FAKE_INITIALIZER](flag: Boolean) -> <v1>
name <v2>: String NEW: r(name) -> <v2>
{ if (flag) { s.add(it) } else { s.remove(it) } return true } <v3>: {<: (String) -> Nothing} NEW: r({ if (flag) { s.add(it) } else { s.remove(it) } return true }) -> <v3>
let { if (flag) { s.add(it) } else { s.remove(it) } return true } !<v4>: *
name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } !<v4>: * COPY
false <v5>: Boolean NEW: r(false) -> <v5>
return false !<v6>: *
{ name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } !<v6>: * COPY
try { name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } finally { name?.hashCode() } <v7>: * NEW: merge(try { name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } finally { name?.hashCode() }|!<v6>) -> <v7>
{ try { name?.let { if (flag) { s.add(it) } else { s.remove(it) } return true } return false } finally { name?.hashCode() } } <v7>: * COPY
=====================
== anonymous_0 ==
{
if (flag) {
s.add(it)
}
else {
s.remove(it)
}
return true
}
---------------------
<v3>: * NEW: magic[VALUE_CONSUMER](it|<v2>) -> <v3>
<v7>: * NEW: magic[VALUE_CONSUMER](it|<v6>) -> <v7>
flag <v0>: Boolean NEW: r(flag) -> <v0>
s <v1>: * NEW: r(s) -> <v1>
it <v2>: * NEW: r(it) -> <v2>
add(it) <v4>: * NEW: call(add(it), <ERROR FUNCTION>|<v1>) -> <v4>
s.add(it) <v4>: * COPY
{ s.add(it) } <v4>: * COPY
s <v5>: * NEW: r(s) -> <v5>
it <v6>: * NEW: r(it) -> <v6>
remove(it) <v8>: * NEW: call(remove(it), <ERROR FUNCTION>|<v5>) -> <v8>
s.remove(it) <v8>: * COPY
{ s.remove(it) } <v8>: * COPY
if (flag) { s.add(it) } else { s.remove(it) } <v9>: * NEW: merge(if (flag) { s.add(it) } else { s.remove(it) }|<v4>, <v8>) -> <v9>
true <v10>: Boolean NEW: r(true) -> <v10>
return true !<v13>: *
if (flag) { s.add(it) } else { s.remove(it) } return true !<v13>: * COPY
=====================
@@ -233,6 +233,12 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest {
doTest(fileName);
}
@TestMetadata("incorrectIndex.kt")
public void testIncorrectIndex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/incorrectIndex.kt");
doTest(fileName);
}
@TestMetadata("InfiniteLoops.kt")
public void testInfiniteLoops() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/InfiniteLoops.kt");
@@ -235,6 +235,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest {
doTest(fileName);
}
@TestMetadata("incorrectIndex.kt")
public void testIncorrectIndex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/incorrectIndex.kt");
doTest(fileName);
}
@TestMetadata("InfiniteLoops.kt")
public void testInfiniteLoops() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/InfiniteLoops.kt");