CFG: fix label binding for last instruction of finally block
So #KT-18698 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
ab0e734e7d
commit
c0e1b3ef2d
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user