diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/Flow.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/Flow.kt index e7e47794bc1..0482796a6de 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/Flow.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/Flow.kt @@ -38,7 +38,8 @@ class Flow( to: DataFlowVariable, transform: ((UnapprovedFirDataFlowInfo) -> UnapprovedFirDataFlowInfo)? = null ): Flow { - if (isFrozen) copyForBuilding().copyNotApprovedFacts(from, to) + if (isFrozen) + return copyForBuilding().copyNotApprovedFacts(from, to, transform) var facts = if (from.isSynthetic) { notApprovedFacts.removeAll(from) } else { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt index f9d53838299..fa1eb141714 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphRenderer.kt @@ -22,8 +22,11 @@ import org.jetbrains.kotlin.utils.DFS private const val INDENT = " " private const val DEAD = "[DEAD]" -fun ControlFlowGraph.renderToStringBuilder(builder: StringBuilder) { - val sortedNodes: List> = DFS.topologicalOrder( + +fun List>.indicesMap(): Map, Int> = mapIndexed { i, node -> node to i }.toMap() + +fun ControlFlowGraph.sortNodes(): List> { + return DFS.topologicalOrder( nodes ) { val result = if (it !is WhenBranchConditionExitNode || it.followingNodes.size < 2) { @@ -33,9 +36,12 @@ fun ControlFlowGraph.renderToStringBuilder(builder: StringBuilder) { } result } +} +fun ControlFlowGraph.renderToStringBuilder(builder: StringBuilder) { + val sortedNodes = sortNodes() - val indices = sortedNodes.mapIndexed { i, node -> node to i }.toMap() + val indices = sortedNodes.indicesMap() val notVisited = sortedNodes.toMutableSet() val maxLineNumberSize = sortedNodes.size.toString().length diff --git a/compiler/fir/resolve/testData/resolve/cfg/binaryOperations.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/binaryOperations.cfg.dot new file mode 100644 index 00000000000..c5ec7519a70 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/binaryOperations.cfg.dot @@ -0,0 +1,200 @@ +digraph binaryOperations_kt { +subgraph test_1 { + 0 [shape=box label="Enter function test_1"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Enter when"]; + 3 [shape=box label="Enter when branch condition "]; + 4 [shape=box label="Enter ||"]; + 5 [shape=box label="Access variable R|/b1|"]; + 6 [shape=box label="Exit left part of ||"]; + 7 [shape=box label="Access variable R|/b2|"]; + 8 [shape=box label="Exit ||"]; + 9 [shape=box label="Exit when branch condition"]; + 10 [shape=box label="Enter block"]; + 11 [shape=box label="Const: Int(1)"]; + 12 [shape=box label="Exit block"]; + 13 [shape=box label="Exit when branch result"]; + 14 [shape=box label="Enter when branch condition else"]; + 15 [shape=box label="Exit when branch condition"]; + 16 [shape=box label="Enter block"]; + 17 [shape=box label="Exit block"]; + 18 [shape=box label="Exit when branch result"]; + 19 [shape=box label="Exit when"]; + 20 [shape=box label="Exit block"]; + 21 [shape=box label="Exit function test_1"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; + 4 -> {5 8}; + 5 -> {8 6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {10 14}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {19}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; +} + +subgraph test_2 { + 22 [shape=box label="Enter function test_2"]; + 23 [shape=box label="Enter block"]; + 24 [shape=box label="Enter when"]; + 25 [shape=box label="Enter when branch condition "]; + 26 [shape=box label="Enter &&"]; + 27 [shape=box label="Access variable R|/b1|"]; + 28 [shape=box label="Access variable R|/b2|"]; + 29 [shape=box label="Exit &&"]; + 30 [shape=box label="Exit when branch condition"]; + 31 [shape=box label="Enter block"]; + 32 [shape=box label="Const: Int(1)"]; + 33 [shape=box label="Exit block"]; + 34 [shape=box label="Exit when branch result"]; + 35 [shape=box label="Enter when branch condition else"]; + 36 [shape=box label="Exit when branch condition"]; + 37 [shape=box label="Enter block"]; + 38 [shape=box label="Exit block"]; + 39 [shape=box label="Exit when branch result"]; + 40 [shape=box label="Exit when"]; + 41 [shape=box label="Exit block"]; + 42 [shape=box label="Exit function test_2"]; + + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {29 28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {31 35}; + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {40}; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; +} + +subgraph test_3 { + 43 [shape=box label="Enter function test_3"]; + 44 [shape=box label="Enter block"]; + 45 [shape=box label="Enter when"]; + 46 [shape=box label="Enter when branch condition "]; + 47 [shape=box label="Enter ||"]; + 48 [shape=box label="Enter &&"]; + 49 [shape=box label="Access variable R|/b1|"]; + 50 [shape=box label="Access variable R|/b2|"]; + 51 [shape=box label="Exit &&"]; + 52 [shape=box label="Exit left part of ||"]; + 53 [shape=box label="Access variable R|/b3|"]; + 54 [shape=box label="Exit ||"]; + 55 [shape=box label="Exit when branch condition"]; + 56 [shape=box label="Enter block"]; + 57 [shape=box label="Const: Int(1)"]; + 58 [shape=box label="Exit block"]; + 59 [shape=box label="Exit when branch result"]; + 60 [shape=box label="Enter when branch condition else"]; + 61 [shape=box label="Exit when branch condition"]; + 62 [shape=box label="Enter block"]; + 63 [shape=box label="Exit block"]; + 64 [shape=box label="Exit when branch result"]; + 65 [shape=box label="Exit when"]; + 66 [shape=box label="Exit block"]; + 67 [shape=box label="Exit function test_3"]; + + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {47}; + 47 -> {48 54}; + 48 -> {49}; + 49 -> {51 50}; + 50 -> {51}; + 51 -> {54 52}; + 52 -> {53}; + 53 -> {54}; + 54 -> {55}; + 55 -> {56 60}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {65}; + 60 -> {61}; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; +} + +subgraph test_4 { + 68 [shape=box label="Enter function test_4"]; + 69 [shape=box label="Enter block"]; + 70 [shape=box label="Enter when"]; + 71 [shape=box label="Enter when branch condition "]; + 72 [shape=box label="Enter ||"]; + 73 [shape=box label="Access variable R|/b1|"]; + 74 [shape=box label="Exit left part of ||"]; + 75 [shape=box label="Enter &&"]; + 76 [shape=box label="Access variable R|/b2|"]; + 77 [shape=box label="Access variable R|/b3|"]; + 78 [shape=box label="Exit &&"]; + 79 [shape=box label="Exit ||"]; + 80 [shape=box label="Exit when branch condition"]; + 81 [shape=box label="Enter block"]; + 82 [shape=box label="Const: Int(1)"]; + 83 [shape=box label="Exit block"]; + 84 [shape=box label="Exit when branch result"]; + 85 [shape=box label="Enter when branch condition else"]; + 86 [shape=box label="Exit when branch condition"]; + 87 [shape=box label="Enter block"]; + 88 [shape=box label="Exit block"]; + 89 [shape=box label="Exit when branch result"]; + 90 [shape=box label="Exit when"]; + 91 [shape=box label="Exit block"]; + 92 [shape=box label="Exit function test_4"]; + + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 72 -> {73 79}; + 73 -> {79 74}; + 74 -> {75}; + 75 -> {76}; + 76 -> {78 77}; + 77 -> {78}; + 78 -> {79}; + 79 -> {80}; + 80 -> {81 85}; + 81 -> {82}; + 82 -> {83}; + 83 -> {84}; + 84 -> {90}; + 85 -> {86}; + 86 -> {87}; + 87 -> {88}; + 88 -> {89}; + 89 -> {90}; + 90 -> {91}; + 91 -> {92}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/complex.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/complex.cfg.dot new file mode 100644 index 00000000000..d0be423887b --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/complex.cfg.dot @@ -0,0 +1,94 @@ +digraph complex_kt { +subgraph fetchPluginReleaseDate { + 0 [shape=box label="Enter function fetchPluginReleaseDate"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Const: String(https://plugins.jetbrains.com/api/plugins/)"]; + 3 [shape=box label="Access variable R|/pluginId|"]; + 4 [shape=box label="Access variable #"]; + 5 [shape=box label="Const: String(/updates?version=)"]; + 6 [shape=box label="Access variable R|/version|"]; + 7 [shape=box label="Variable declaration: lval url: R|kotlin/String|"]; + 8 [shape=box label="Try expression enter"]; + 9 [shape=box label="Try main block enter"]; + 10 [shape=box label="Enter block"]; + 11 [shape=box label="Access variable #"]; + 12 [shape=box label="Access variable R|/url|"]; + 13 [shape=box label="Function call: #.#(R|/url|)"]; + 14 [shape=box label="Function call: #.#(R|/url|).#( = connect@fun .(): { + GsonBuilder#().create#().fromJson#(it#.inputStream#.reader#(), (Array#()).java#) +} +)"]; + 15 [shape=box label="Exit block"]; + 16 [shape=box label="Try main block exit"]; + 17 [shape=box label="Catch enter"]; + 18 [shape=box label="Enter block"]; + 19 [shape=box label="Const: String(Can't parse json response)"]; + 20 [shape=box label="Access variable R|/syntaxException|"]; + 21 [shape=box label="Function call: #(String(Can't parse json response), R|/syntaxException|)"]; + 22 [shape=box label="Throw: throw #(String(Can't parse json response), R|/syntaxException|)"]; + 23 [shape=box label="Stub[DEAD]"]; + 24 [shape=box label="Exit block[DEAD]"]; + 25 [shape=box label="Catch exit[DEAD]"]; + 26 [shape=box label="Catch enter"]; + 27 [shape=box label="Enter block"]; + 28 [shape=box label="Access variable R|/ioException|"]; + 29 [shape=box label="Function call: #(R|/ioException|)"]; + 30 [shape=box label="Throw: throw #(R|/ioException|)"]; + 31 [shape=box label="Stub[DEAD]"]; + 32 [shape=box label="Exit block[DEAD]"]; + 33 [shape=box label="Catch exit[DEAD]"]; + 34 [shape=box label="Try expression exit"]; + 35 [shape=box label="Variable declaration: lval pluginDTOs: R|kotlin/Array|"]; + 36 [shape=box label="Exit block"]; + 37 [shape=box label="Enter annotation"]; + 38 [shape=box label="Access variable #"]; + 39 [shape=box label="Access variable #"]; + 40 [shape=box label="Exit annotation"]; + 41 [shape=box label="Exit function fetchPluginReleaseDate"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {41 26 17 10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {34}; + 17 -> {41 18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {41}; + 22 -> {23} [style=dotted]; + 23 -> {24} [style=dotted]; + 24 -> {25} [style=dotted]; + 25 -> {34} [style=dotted]; + 26 -> {41 27}; + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {41}; + 30 -> {31} [style=dotted]; + 31 -> {32} [style=dotted]; + 32 -> {33} [style=dotted]; + 33 -> {34} [style=dotted]; + 34 -> {35}; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/complex.cfg.txt b/compiler/fir/resolve/testData/resolve/cfg/complex.cfg.txt index 2f13e13e782..b45bccefb9d 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/complex.cfg.txt +++ b/compiler/fir/resolve/testData/resolve/cfg/complex.cfg.txt @@ -1,21 +1,46 @@ -0: Enter function "foo" -> 1 + 0: Enter function "fetchPluginReleaseDate" -> 1 1: Enter block -> 2 | <- 0 - 2: Try expression enter -> 3 | <- 1 - 3: Try main block enter -> 4, 8, 20 | <- 2 - 4: Enter block -> 5 | <- 3 - 5: Const: Int(1) -> 6 | <- 4 - 6: Exit block -> 7 | <- 5 - 7: Try main block exit -> 14 | <- 6 - 8: Catch enter -> 9, 20 | <- 3 - 9: Enter block -> 10 | <- 8 -10: Jump: ^foo Unit -> 11[DEAD], 20 | <- 9 -11: Stub[DEAD] -> 12 | <- 10 -12: Exit block[DEAD] -> 13 | <- 11 -13: Catch exit[DEAD] -> 14 | <- 12 -14: Try expression exit -> 15 | <- 7, 13[DEAD] -15: Variable declaration: lval x: R|kotlin/Int| -> 16 | <- 14 -16: Access variable R|/x| -> 17 | <- 15 -17: Const: Int(1) -> 18 | <- 16 -18: Function call: R|/x|.R|kotlin/Int.plus|(Int(1)) -> 19 | <- 17 -19: Exit block -> 20 | <- 18 -20: Exit function "foo" -> | <- 3, 8, 10, 19 + 2: Const: String(https://plugins.jetbrains.com/api/plugins/) -> 3 | <- 1 + 3: Access variable R|/pluginId| -> 4 | <- 2 + 4: Access variable # -> 5 | <- 3 + 5: Const: String(/updates?version=) -> 6 | <- 4 + 6: Access variable R|/version| -> 7 | <- 5 + 7: Variable declaration: lval url: R|kotlin/String| -> 8 | <- 6 + 8: Try expression enter -> 9 | <- 7 + 9: Try main block enter -> 10, 17, 26, 41 | <- 8 +10: Enter block -> 11 | <- 9 +11: Access variable # -> 12 | <- 10 +12: Access variable R|/url| -> 13 | <- 11 +13: Function call: #.#(R|/url|) -> 14 | <- 12 +14: Function call: #.#(R|/url|).#( = connect@fun .(): { + GsonBuilder#().create#().fromJson#(it#.inputStream#.reader#(), (Array#()).java#) +} +) -> 15 | <- 13 +15: Exit block -> 16 | <- 14 +16: Try main block exit -> 34 | <- 15 +17: Catch enter -> 18, 41 | <- 9 +18: Enter block -> 19 | <- 17 +19: Const: String(Can't parse json response) -> 20 | <- 18 +20: Access variable R|/syntaxException| -> 21 | <- 19 +21: Function call: #(String(Can't parse json response), R|/syntaxException|) -> 22 | <- 20 +22: Throw: throw #(String(Can't parse json response), R|/syntaxException|) -> 23[DEAD], 41 | <- 21 +23: Stub[DEAD] -> 24 | <- 22 +24: Exit block[DEAD] -> 25 | <- 23 +25: Catch exit[DEAD] -> 34 | <- 24 +26: Catch enter -> 27, 41 | <- 9 +27: Enter block -> 28 | <- 26 +28: Access variable R|/ioException| -> 29 | <- 27 +29: Function call: #(R|/ioException|) -> 30 | <- 28 +30: Throw: throw #(R|/ioException|) -> 31[DEAD], 41 | <- 29 +31: Stub[DEAD] -> 32 | <- 30 +32: Exit block[DEAD] -> 33 | <- 31 +33: Catch exit[DEAD] -> 34 | <- 32 +34: Try expression exit -> 35 | <- 16, 25[DEAD], 33[DEAD] +35: Variable declaration: lval pluginDTOs: R|kotlin/Array| -> 36 | <- 34 +36: Exit block -> 37 | <- 35 +37: Enter annotation -> 38 | <- 36 +38: Access variable # -> 39 | <- 37 +39: Access variable # -> 40 | <- 38 +40: Exit annotation -> 41 | <- 39 +41: Exit function "fetchPluginReleaseDate" -> | <- 9, 17, 22, 26, 30, 40 + diff --git a/compiler/fir/resolve/testData/resolve/cfg/complex.txt b/compiler/fir/resolve/testData/resolve/cfg/complex.txt new file mode 100644 index 00000000000..157fb98cfef --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/complex.txt @@ -0,0 +1,17 @@ +FILE: complex.kt + @R|kotlin/jvm/Throws|((#), (#)) public final fun fetchPluginReleaseDate(pluginId: R|class error: Symbol not found, for `PluginId`|, version: R|kotlin/String|, channel: R|kotlin/String?|): R|class error: Symbol not found, for `LocalDate?`| { + lval url: R|kotlin/String| = (String(https://plugins.jetbrains.com/api/plugins/), R|/pluginId|.#, String(/updates?version=), R|/version|) + lval pluginDTOs: R|kotlin/Array| = try { + #.#(R|/url|).#( = connect@fun .(): { + GsonBuilder#().create#().fromJson#(it#.inputStream#.reader#(), (Array#()).java#) + } + ) + } + catch (ioException: R|class error: Symbol not found, for `JsonIOException`|) { + throw #(R|/ioException|) + } + catch (syntaxException: R|class error: Symbol not found, for `JsonSyntaxException`|) { + throw #(String(Can't parse json response), R|/syntaxException|) + } + + } diff --git a/compiler/fir/resolve/testData/resolve/cfg/initBlock.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/initBlock.cfg.dot new file mode 100644 index 00000000000..938afe430e0 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/initBlock.cfg.dot @@ -0,0 +1,2 @@ +digraph initBlock_kt { +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/jumps.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/jumps.cfg.dot new file mode 100644 index 00000000000..35eee66e162 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/jumps.cfg.dot @@ -0,0 +1,192 @@ +digraph jumps_kt { +subgraph test_1 { + 0 [shape=box label="Enter function test_1"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Enter when"]; + 3 [shape=box label="Enter when branch condition "]; + 4 [shape=box label="Access variable R|/x|"]; + 5 [shape=box label="Const: Null(null)"]; + 6 [shape=box label="Operator =="]; + 7 [shape=box label="Exit when branch condition"]; + 8 [shape=box label="Enter block"]; + 9 [shape=box label="Function call: R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 10 [shape=box label="Throw: throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()"]; + 11 [shape=box label="Stub[DEAD]"]; + 12 [shape=box label="Exit block[DEAD]"]; + 13 [shape=box label="Exit when branch result[DEAD]"]; + 14 [shape=box label="Enter when branch condition else"]; + 15 [shape=box label="Exit when branch condition"]; + 16 [shape=box label="Enter block"]; + 17 [shape=box label="Access variable R|/x|"]; + 18 [shape=box label="Exit block"]; + 19 [shape=box label="Exit when branch result"]; + 20 [shape=box label="Exit when"]; + 21 [shape=box label="Variable declaration: lval y: R|kotlin/Int|"]; + 22 [shape=box label="Access variable R|/y|"]; + 23 [shape=box label="Function call: R|/y|.R|kotlin/Int.inc|()"]; + 24 [shape=box label="Exit block"]; + 25 [shape=box label="Exit function test_1"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8 14}; + 8 -> {9}; + 9 -> {10}; + 10 -> {25}; + 10 -> {11} [style=dotted]; + 11 -> {12} [style=dotted]; + 12 -> {13} [style=dotted]; + 13 -> {20} [style=dotted]; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; +} + +subgraph test_2 { + 26 [shape=box label="Enter function test_2"]; + 27 [shape=box label="Enter block"]; + 28 [shape=box label="Enter when"]; + 29 [shape=box label="Enter when branch condition "]; + 30 [shape=box label="Access variable R|/x|"]; + 31 [shape=box label="Const: Null(null)"]; + 32 [shape=box label="Operator =="]; + 33 [shape=box label="Exit when branch condition"]; + 34 [shape=box label="Enter block"]; + 35 [shape=box label="Access variable R|/x|"]; + 36 [shape=box label="Exit block"]; + 37 [shape=box label="Exit when branch result"]; + 38 [shape=box label="Enter when branch condition else"]; + 39 [shape=box label="Exit when branch condition"]; + 40 [shape=box label="Enter block"]; + 41 [shape=box label="Access variable R|/x|"]; + 42 [shape=box label="Exit block"]; + 43 [shape=box label="Exit when branch result"]; + 44 [shape=box label="Exit when"]; + 45 [shape=box label="Variable declaration: lval y: R|kotlin/Int?|"]; + 46 [shape=box label="Access variable R|/y|"]; + 47 [shape=box label="Function call: R|/y|.#()"]; + 48 [shape=box label="Exit block"]; + 49 [shape=box label="Exit function test_2"]; + + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {31}; + 31 -> {32}; + 32 -> {33}; + 33 -> {34 38}; + 34 -> {35}; + 35 -> {36}; + 36 -> {37}; + 37 -> {44}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {47}; + 47 -> {48}; + 48 -> {49}; +} + +subgraph test_3 { + 50 [shape=box label="Enter function test_3"]; + 51 [shape=box label="Enter block"]; + 52 [shape=box label="Enter while loop"]; + 53 [shape=box label="Enter loop condition"]; + 54 [shape=box label="Const: Boolean(true)"]; + 55 [shape=box label="Exit loop condition"]; + 56 [shape=box label="Enter loop block"]; + 57 [shape=box label="Enter block"]; + 58 [shape=box label="Access variable R|/x|"]; + 59 [shape=box label="Type operator: x as Int"]; + 60 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 61 [shape=box label="Stub[DEAD]"]; + 62 [shape=box label="Exit block[DEAD]"]; + 63 [shape=box label="Exit loop block[DEAD]"]; + 64 [shape=box label="Exit whileloop"]; + 65 [shape=box label="Access variable R|/x|"]; + 66 [shape=box label="Function call: R|/x|.#()"]; + 67 [shape=box label="Exit block"]; + 68 [shape=box label="Exit function test_3"]; + + 50 -> {51}; + 51 -> {52}; + 52 -> {53}; + 53 -> {54}; + 54 -> {55}; + 55 -> {64 56}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; + 60 -> {64}; + 60 -> {61} [style=dotted]; + 61 -> {62} [style=dotted]; + 62 -> {63} [style=dotted]; + 63 -> {53} [style=dotted]; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; + 67 -> {68}; +} + +subgraph test_4 { + 69 [shape=box label="Enter function test_4"]; + 70 [shape=box label="Enter block"]; + 71 [shape=box label="Enter do-while loop"]; + 72 [shape=box label="Enter loop block"]; + 73 [shape=box label="Enter block"]; + 74 [shape=box label="Access variable R|/x|"]; + 75 [shape=box label="Type operator: x as int"]; + 76 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 77 [shape=box label="Stub[DEAD]"]; + 78 [shape=box label="Exit block[DEAD]"]; + 79 [shape=box label="Exit loop block[DEAD]"]; + 80 [shape=box label="Enter loop condition[DEAD]"]; + 81 [shape=box label="Const: Boolean(true)[DEAD]"]; + 82 [shape=box label="Exit loop condition[DEAD]"]; + 83 [shape=box label="Exit do-whileloop[DEAD]"]; + 84 [shape=box label="Access variable R|/x|[DEAD]"]; + 85 [shape=box label="Function call: R|/x|.#()[DEAD]"]; + 86 [shape=box label="Exit block[DEAD]"]; + 87 [shape=box label="Exit function test_4[DEAD]"]; + + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 74 -> {75}; + 75 -> {76}; + 76 -> {83 77} [style=dotted]; + 77 -> {78} [style=dotted]; + 78 -> {79} [style=dotted]; + 79 -> {80} [style=dotted]; + 80 -> {81} [style=dotted]; + 81 -> {82} [style=dotted]; + 82 -> {72 83} [style=dotted]; + 83 -> {84} [style=dotted]; + 84 -> {85} [style=dotted]; + 85 -> {86} [style=dotted]; + 86 -> {87} [style=dotted]; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/jumps.cfg.txt b/compiler/fir/resolve/testData/resolve/cfg/jumps.cfg.txt index e06fffa33a9..d2803768826 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/jumps.cfg.txt +++ b/compiler/fir/resolve/testData/resolve/cfg/jumps.cfg.txt @@ -19,9 +19,9 @@ 18: Exit block -> 19 | <- 17 19: Exit when branch result -> 20 | <- 18 20: Exit when -> 21 | <- 13[DEAD], 19 -21: Variable declaration: lval y: R|kotlin/Int?| -> 22 | <- 20 +21: Variable declaration: lval y: R|kotlin/Int| -> 22 | <- 20 22: Access variable R|/y| -> 23 | <- 21 -23: Function call: R|/y|.#() -> 24 | <- 22 +23: Function call: R|/y|.R|kotlin/Int.inc|() -> 24 | <- 22 24: Exit block -> 25 | <- 23 25: Exit function "test_1" -> | <- 10, 24 diff --git a/compiler/fir/resolve/testData/resolve/cfg/jumps.txt b/compiler/fir/resolve/testData/resolve/cfg/jumps.txt index 01a60bbdbe0..52048d3559b 100644 --- a/compiler/fir/resolve/testData/resolve/cfg/jumps.txt +++ b/compiler/fir/resolve/testData/resolve/cfg/jumps.txt @@ -1,6 +1,6 @@ FILE: jumps.kt public final fun test_1(x: R|kotlin/Int?|): R|kotlin/Unit| { - lval y: R|kotlin/Int?| = when () { + lval y: R|kotlin/Int| = when () { ==(R|/x|, Null(null)) -> { throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() } @@ -9,7 +9,7 @@ FILE: jumps.kt } } - R|/y|.#() + R|/y|.R|kotlin/Int.inc|() } public final fun test_2(x: R|kotlin/Int?|): R|kotlin/Unit| { lval y: R|kotlin/Int?| = when () { diff --git a/compiler/fir/resolve/testData/resolve/cfg/loops.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/loops.cfg.dot new file mode 100644 index 00000000000..33fd3b89a64 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/loops.cfg.dot @@ -0,0 +1,141 @@ +digraph loops_kt { +subgraph testWhile { + 0 [shape=box label="Enter function testWhile"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Enter while loop"]; + 3 [shape=box label="Enter loop condition"]; + 4 [shape=box label="Access variable R|/b|"]; + 5 [shape=box label="Exit loop condition"]; + 6 [shape=box label="Enter loop block"]; + 7 [shape=box label="Enter block"]; + 8 [shape=box label="Access variable R|/x|"]; + 9 [shape=box label="Type operator: x is String"]; + 10 [shape=box label="Variable declaration: lval y: R|kotlin/Boolean|"]; + 11 [shape=box label="Exit block"]; + 12 [shape=box label="Exit loop block"]; + 13 [shape=box label="Exit whileloop"]; + 14 [shape=box label="Access variable R|/x|"]; + 15 [shape=box label="Type operator: x is String"]; + 16 [shape=box label="Exit block"]; + 17 [shape=box label="Exit function testWhile"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {13 6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {3}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; +} + +subgraph testDoWhile { + 18 [shape=box label="Enter function testDoWhile"]; + 19 [shape=box label="Enter block"]; + 20 [shape=box label="Enter do-while loop"]; + 21 [shape=box label="Enter loop block"]; + 22 [shape=box label="Enter block"]; + 23 [shape=box label="Access variable R|/x|"]; + 24 [shape=box label="Type operator: x is String"]; + 25 [shape=box label="Variable declaration: lval y: R|kotlin/Boolean|"]; + 26 [shape=box label="Exit block"]; + 27 [shape=box label="Exit loop block"]; + 28 [shape=box label="Enter loop condition"]; + 29 [shape=box label="Access variable R|/b|"]; + 30 [shape=box label="Exit loop condition"]; + 31 [shape=box label="Exit do-whileloop"]; + 32 [shape=box label="Access variable R|/x|"]; + 33 [shape=box label="Type operator: x is String"]; + 34 [shape=box label="Exit block"]; + 35 [shape=box label="Exit function testDoWhile"]; + + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {21 31}; + 31 -> {32}; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; +} + +subgraph testFor { + 36 [shape=box label="Enter function testFor"]; + 37 [shape=box label="Enter block"]; + 38 [shape=box label="Const: Int(0)"]; + 39 [shape=box label="Const: Int(5)"]; + 40 [shape=box label="Function call: Int(0).R|kotlin/Int.rangeTo|(Int(5))"]; + 41 [shape=box label="Variable declaration: lval : R|kotlin/ranges/IntRange|"]; + 42 [shape=box label="Access variable R|/|"]; + 43 [shape=box label="Function call: R|/|.R|kotlin/ranges/IntProgression.iterator|()"]; + 44 [shape=box label="Variable declaration: lval : R|kotlin/collections/IntIterator|"]; + 45 [shape=box label="Enter while loop"]; + 46 [shape=box label="Enter loop condition"]; + 47 [shape=box label="Access variable R|/|"]; + 48 [shape=box label="Function call: R|/|.R|FakeOverride|()"]; + 49 [shape=box label="Exit loop condition"]; + 50 [shape=box label="Enter loop block"]; + 51 [shape=box label="Enter block"]; + 52 [shape=box label="Access variable R|/|"]; + 53 [shape=box label="Function call: R|/|.R|kotlin/collections/IntIterator.next|()"]; + 54 [shape=box label="Variable declaration: lval i: R|kotlin/Int|"]; + 55 [shape=box label="Access variable R|/x|"]; + 56 [shape=box label="Type operator: x is String"]; + 57 [shape=box label="Variable declaration: lval y: R|kotlin/Boolean|"]; + 58 [shape=box label="Exit block"]; + 59 [shape=box label="Exit loop block"]; + 60 [shape=box label="Exit whileloop"]; + 61 [shape=box label="Access variable R|/x|"]; + 62 [shape=box label="Type operator: x is String"]; + 63 [shape=box label="Exit block"]; + 64 [shape=box label="Exit function testFor"]; + + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {47}; + 47 -> {48}; + 48 -> {49}; + 49 -> {60 50}; + 50 -> {51}; + 51 -> {52}; + 52 -> {53}; + 53 -> {54}; + 54 -> {55}; + 55 -> {56}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {46}; + 60 -> {61}; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/propertiesAndInitBlocks.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/propertiesAndInitBlocks.cfg.dot new file mode 100644 index 00000000000..c1dd22a635d --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/propertiesAndInitBlocks.cfg.dot @@ -0,0 +1,216 @@ +digraph propertiesAndInitBlocks_kt { +subgraph run { + 0 [shape=box label="Enter function run"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Function call: R|/block|.R|FakeOverride|()"]; + 3 [shape=box label="Exit block"]; + 4 [shape=box label="Exit function run"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; +} + +subgraph val_x1 { + 5 [shape=box label="Enter property"]; + 6 [shape=box label="Const: Int(1)"]; + 7 [shape=box label="Exit property"]; + + 5 -> {6}; + 6 -> {7}; +} + +subgraph { + 8 [shape=box label="Enter function getter"]; + 9 [shape=box label="Enter block"]; + 10 [shape=box label="Const: Int(1)"]; + 11 [shape=box label="Jump: ^ Int(1)"]; + 12 [shape=box label="Stub[DEAD]"]; + 13 [shape=box label="Exit block[DEAD]"]; + 14 [shape=box label="Exit function getter[DEAD]"]; + + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {14 12} [style=dotted]; + 12 -> {13} [style=dotted]; + 13 -> {14} [style=dotted]; +} + +subgraph { + 15 [shape=box label="Enter function setter"]; + 16 [shape=box label="Enter block"]; + 17 [shape=box label="Const: Int(1)"]; + 18 [shape=box label="Assignmenet: F|/x2|"]; + 19 [shape=box label="Exit block"]; + 20 [shape=box label="Exit function setter"]; + + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; +} + +subgraph val_x2 { + 21 [shape=box label="Enter property"]; + 22 [shape=box label="Const: Int(1)"]; + 23 [shape=box label="Exit property"]; + + 21 -> {22}; + 22 -> {23}; +} + +subgraph { + 24 [shape=box label="Enter init block"]; + 25 [shape=box label="Enter block"]; + 26 [shape=box label="Function call: #()"]; + 27 [shape=box label="Throw: throw #()"]; + 28 [shape=box label="Stub[DEAD]"]; + 29 [shape=box label="Const: Int(1)[DEAD]"]; + 30 [shape=box label="Exit block[DEAD]"]; + 31 [shape=box label="Exit init block[DEAD]"]; + 32 [shape=box label="Enter function anonymousFunction"]; + 33 [shape=box label="Enter block"]; + 34 [shape=box label="Function call: #()"]; + 35 [shape=box label="Throw: throw #()"]; + 36 [shape=box label="Stub[DEAD]"]; + 37 [shape=box label="Exit block[DEAD]"]; + 38 [shape=box label="Exit function anonymousFunction[DEAD]"]; + + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {31 28} [style=dotted]; + 28 -> {29} [style=dotted]; + 29 -> {30} [style=dotted]; + 30 -> {31} [style=dotted]; + 32 -> {33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {38 36} [style=dotted]; + 36 -> {37} [style=dotted]; + 37 -> {38} [style=dotted]; +} + +subgraph foo { + 39 [shape=box label="Enter function foo"]; + 40 [shape=box label="Enter block"]; + 41 [shape=box label="Const: Int(1)"]; + 42 [shape=box label="Const: Int(1)"]; + 43 [shape=box label="Function call: Int(1).R|kotlin/Int.plus|(Int(1))"]; + 44 [shape=box label="Variable declaration: lval c: R|kotlin/Int|"]; + 45 [shape=box label="Function call: #()"]; + 46 [shape=box label="Throw: throw #()"]; + 47 [shape=box label="Stub[DEAD]"]; + 48 [shape=box label="Exit block[DEAD]"]; + 49 [shape=box label="Exit function foo[DEAD]"]; + + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {49 47} [style=dotted]; + 47 -> {48} [style=dotted]; + 48 -> {49} [style=dotted]; +} + +subgraph { + 50 [shape=box label="Enter init block"]; + 51 [shape=box label="Enter block"]; + 52 [shape=box label="Function call: #()"]; + 53 [shape=box label="Throw: throw #()"]; + 54 [shape=box label="Stub[DEAD]"]; + 55 [shape=box label="Exit block[DEAD]"]; + 56 [shape=box label="Exit init block[DEAD]"]; + 57 [shape=box label="Enter function getter"]; + 58 [shape=box label="Enter block"]; + 59 [shape=box label="Exit block"]; + 60 [shape=box label="Exit function getter"]; + + 50 -> {51}; + 51 -> {52}; + 52 -> {53}; + 53 -> {56 54} [style=dotted]; + 54 -> {55} [style=dotted]; + 55 -> {56} [style=dotted]; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; +} + +subgraph val_x3 { + 61 [shape=box label="Enter property"]; + 62 [shape=box label="Function call: R|/run|( = run@fun (): R|kotlin/Unit| { + local final fun foo(): R|kotlin/Unit| { + lval c: R|kotlin/Int| = Int(1).R|kotlin/Int.plus|(Int(1)) + throw #() + } + + local final class LocalClass : R|kotlin/Any| { + public constructor(): R|LocalClass| { + super() + } + + init { + throw #() + Int(1) + } + + } + + throw #() +} +)"]; + 63 [shape=box label="Exit property"]; + + 61 -> {62}; + 62 -> {63}; +} + +subgraph val_x4 { + 64 [shape=box label="Enter property"]; + 65 [shape=box label="Try expression enter"]; + 66 [shape=box label="Try main block enter"]; + 67 [shape=box label="Enter block"]; + 68 [shape=box label="Const: Int(1)"]; + 69 [shape=box label="Exit block"]; + 70 [shape=box label="Try main block exit"]; + 71 [shape=box label="Enter finally"]; + 72 [shape=box label="Enter block"]; + 73 [shape=box label="Const: Int(0)"]; + 74 [shape=box label="Exit block"]; + 75 [shape=box label="Exit finally"]; + 76 [shape=box label="Catch enter"]; + 77 [shape=box label="Enter block"]; + 78 [shape=box label="Const: Int(2)"]; + 79 [shape=box label="Exit block"]; + 80 [shape=box label="Catch exit"]; + 81 [shape=box label="Try expression exit"]; + 82 [shape=box label="Exit property"]; + + 64 -> {65}; + 65 -> {66}; + 66 -> {82 76 71 67}; + 67 -> {68}; + 68 -> {69}; + 69 -> {70}; + 70 -> {81}; + 71 -> {72}; + 72 -> {73}; + 73 -> {74}; + 74 -> {75}; + 75 -> {81}; + 76 -> {82 77}; + 77 -> {78}; + 78 -> {79}; + 79 -> {80}; + 80 -> {81}; + 81 -> {82}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/simple.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/simple.cfg.dot new file mode 100644 index 00000000000..a2ce1eefec7 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/simple.cfg.dot @@ -0,0 +1,38 @@ +digraph simple_kt { +subgraph foo { + 0 [shape=box label="Enter function foo"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Exit block"]; + 3 [shape=box label="Exit function foo"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; +} + +subgraph test { + 4 [shape=box label="Enter function test"]; + 5 [shape=box label="Enter block"]; + 6 [shape=box label="Const: Int(1)"]; + 7 [shape=box label="Variable declaration: lval x: R|kotlin/Int|"]; + 8 [shape=box label="Access variable R|/x|"]; + 9 [shape=box label="Const: Int(1)"]; + 10 [shape=box label="Function call: R|/x|.R|kotlin/Int.plus|(Int(1))"]; + 11 [shape=box label="Variable declaration: lval y: R|kotlin/Int|"]; + 12 [shape=box label="Function call: R|/foo|()"]; + 13 [shape=box label="Exit block"]; + 14 [shape=box label="Exit function test"]; + + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/tryCatch.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/tryCatch.cfg.dot new file mode 100644 index 00000000000..6c0e64362c0 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/tryCatch.cfg.dot @@ -0,0 +1,233 @@ +digraph tryCatch_kt { +subgraph test_1 { + 0 [shape=box label="Enter function test_1"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Try expression enter"]; + 3 [shape=box label="Try main block enter"]; + 4 [shape=box label="Enter block"]; + 5 [shape=box label="Const: Int(1)"]; + 6 [shape=box label="Variable declaration: lval x: R|kotlin/Int|"]; + 7 [shape=box label="Exit block"]; + 8 [shape=box label="Try main block exit"]; + 9 [shape=box label="Catch enter"]; + 10 [shape=box label="Enter block"]; + 11 [shape=box label="Const: Int(3)"]; + 12 [shape=box label="Variable declaration: lval z: R|kotlin/Int|"]; + 13 [shape=box label="Exit block"]; + 14 [shape=box label="Catch exit"]; + 15 [shape=box label="Catch enter"]; + 16 [shape=box label="Enter block"]; + 17 [shape=box label="Const: Int(2)"]; + 18 [shape=box label="Variable declaration: lval y: R|kotlin/Int|"]; + 19 [shape=box label="Exit block"]; + 20 [shape=box label="Catch exit"]; + 21 [shape=box label="Try expression exit"]; + 22 [shape=box label="Exit block"]; + 23 [shape=box label="Exit function test_1"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {23 15 9 4}; + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8}; + 8 -> {21}; + 9 -> {23 10}; + 10 -> {11}; + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + 14 -> {21}; + 15 -> {23 16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {23}; +} + +subgraph test_2 { + 24 [shape=box label="Enter function test_2"]; + 25 [shape=box label="Enter block"]; + 26 [shape=box label="Try expression enter"]; + 27 [shape=box label="Try main block enter"]; + 28 [shape=box label="Enter block"]; + 29 [shape=box label="Const: Int(1)"]; + 30 [shape=box label="Exit block"]; + 31 [shape=box label="Try main block exit"]; + 32 [shape=box label="Catch enter"]; + 33 [shape=box label="Enter block"]; + 34 [shape=box label="Const: Int(2)"]; + 35 [shape=box label="Exit block"]; + 36 [shape=box label="Catch exit"]; + 37 [shape=box label="Try expression exit"]; + 38 [shape=box label="Variable declaration: lval x: R|kotlin/Int|"]; + 39 [shape=box label="Exit block"]; + 40 [shape=box label="Exit function test_2"]; + + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {40 32 28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {31}; + 31 -> {37}; + 32 -> {40 33}; + 33 -> {34}; + 34 -> {35}; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; +} + +subgraph test_3 { + 41 [shape=box label="Enter function test_3"]; + 42 [shape=box label="Enter block"]; + 43 [shape=box label="Enter while loop"]; + 44 [shape=box label="Enter loop condition"]; + 45 [shape=box label="Const: Boolean(true)"]; + 46 [shape=box label="Exit loop condition"]; + 47 [shape=box label="Enter loop block"]; + 48 [shape=box label="Enter block"]; + 49 [shape=box label="Try expression enter"]; + 50 [shape=box label="Try main block enter"]; + 51 [shape=box label="Enter block"]; + 52 [shape=box label="Enter when"]; + 53 [shape=box label="Enter when branch condition "]; + 54 [shape=box label="Access variable R|/b|"]; + 55 [shape=box label="Exit when branch condition"]; + 56 [shape=box label="Enter block"]; + 57 [shape=box label="Jump: ^test_3 Unit"]; + 58 [shape=box label="Stub[DEAD]"]; + 59 [shape=box label="Exit block[DEAD]"]; + 60 [shape=box label="Exit when branch result[DEAD]"]; + 61 [shape=box label="Enter when branch condition else"]; + 62 [shape=box label="Exit when branch condition"]; + 63 [shape=box label="Enter block"]; + 64 [shape=box label="Exit block"]; + 65 [shape=box label="Exit when branch result"]; + 66 [shape=box label="Exit when"]; + 67 [shape=box label="Const: Int(1)"]; + 68 [shape=box label="Variable declaration: lval x: R|kotlin/Int|"]; + 69 [shape=box label="Enter when"]; + 70 [shape=box label="Enter when branch condition "]; + 71 [shape=box label="Access variable R|/b|"]; + 72 [shape=box label="Function call: R|/b|.R|kotlin/Boolean.not|()"]; + 73 [shape=box label="Exit when branch condition"]; + 74 [shape=box label="Enter block"]; + 75 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 76 [shape=box label="Stub[DEAD]"]; + 77 [shape=box label="Exit block[DEAD]"]; + 78 [shape=box label="Exit when branch result[DEAD]"]; + 79 [shape=box label="Enter when branch condition else"]; + 80 [shape=box label="Exit when branch condition"]; + 81 [shape=box label="Enter block"]; + 82 [shape=box label="Exit block"]; + 83 [shape=box label="Exit when branch result"]; + 84 [shape=box label="Exit when"]; + 85 [shape=box label="Exit block"]; + 86 [shape=box label="Try main block exit"]; + 87 [shape=box label="Catch enter"]; + 88 [shape=box label="Enter block"]; + 89 [shape=box label="Jump: break@@@[Boolean(true)] "]; + 90 [shape=box label="Stub[DEAD]"]; + 91 [shape=box label="Exit block[DEAD]"]; + 92 [shape=box label="Catch exit[DEAD]"]; + 93 [shape=box label="Catch enter"]; + 94 [shape=box label="Enter block"]; + 95 [shape=box label="Jump: continue@@@[Boolean(true)] "]; + 96 [shape=box label="Stub[DEAD]"]; + 97 [shape=box label="Exit block[DEAD]"]; + 98 [shape=box label="Catch exit[DEAD]"]; + 99 [shape=box label="Try expression exit"]; + 100 [shape=box label="Const: Int(2)"]; + 101 [shape=box label="Variable declaration: lval y: R|kotlin/Int|"]; + 102 [shape=box label="Exit block"]; + 103 [shape=box label="Exit loop block"]; + 104 [shape=box label="Exit whileloop"]; + 105 [shape=box label="Const: Int(3)"]; + 106 [shape=box label="Variable declaration: lval z: R|kotlin/Int|"]; + 107 [shape=box label="Exit block"]; + 108 [shape=box label="Exit function test_3"]; + + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; + 44 -> {45}; + 45 -> {46}; + 46 -> {104 47}; + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {108 93 87 51}; + 51 -> {52}; + 52 -> {53}; + 53 -> {54}; + 54 -> {55}; + 55 -> {56 61}; + 56 -> {57}; + 57 -> {108}; + 57 -> {58} [style=dotted]; + 58 -> {59} [style=dotted]; + 59 -> {60} [style=dotted]; + 60 -> {66} [style=dotted]; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; + 67 -> {68}; + 68 -> {69}; + 69 -> {70}; + 70 -> {71}; + 71 -> {72}; + 72 -> {73}; + 73 -> {74 79}; + 74 -> {75}; + 75 -> {104}; + 75 -> {76} [style=dotted]; + 76 -> {77} [style=dotted]; + 77 -> {78} [style=dotted]; + 78 -> {84} [style=dotted]; + 79 -> {80}; + 80 -> {81}; + 81 -> {82}; + 82 -> {83}; + 83 -> {84}; + 84 -> {85}; + 85 -> {86}; + 86 -> {99}; + 87 -> {108 88}; + 88 -> {89}; + 89 -> {104}; + 89 -> {90} [style=dotted]; + 90 -> {91} [style=dotted]; + 91 -> {92} [style=dotted]; + 92 -> {99} [style=dotted]; + 93 -> {108 94}; + 94 -> {95}; + 95 -> {44}; + 95 -> {96} [style=dotted]; + 96 -> {97} [style=dotted]; + 97 -> {98} [style=dotted]; + 98 -> {99} [style=dotted]; + 99 -> {100}; + 100 -> {101}; + 101 -> {102}; + 102 -> {103}; + 103 -> {44}; + 104 -> {105}; + 105 -> {106}; + 106 -> {107}; + 107 -> {108}; +} + +} diff --git a/compiler/fir/resolve/testData/resolve/cfg/when.cfg.dot b/compiler/fir/resolve/testData/resolve/cfg/when.cfg.dot new file mode 100644 index 00000000000..9ddee9505b2 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/cfg/when.cfg.dot @@ -0,0 +1,147 @@ +digraph when_kt { +subgraph test_1 { + 0 [shape=box label="Enter function test_1"]; + 1 [shape=box label="Enter block"]; + 2 [shape=box label="Enter when"]; + 3 [shape=box label="Enter when branch condition "]; + 4 [shape=box label="Access variable R|/x|"]; + 5 [shape=box label="Const: Int(1)"]; + 6 [shape=box label="Operator =="]; + 7 [shape=box label="Exit when branch condition"]; + 8 [shape=box label="Enter block"]; + 9 [shape=box label="Const: Int(10)"]; + 10 [shape=box label="Exit block"]; + 11 [shape=box label="Exit when branch result"]; + 12 [shape=box label="Enter when branch condition "]; + 13 [shape=box label="Access variable R|/x|"]; + 14 [shape=box label="Const: Int(2)"]; + 15 [shape=box label="Function call: R|/x|.R|kotlin/Int.rem|(Int(2))"]; + 16 [shape=box label="Const: Int(0)"]; + 17 [shape=box label="Operator =="]; + 18 [shape=box label="Exit when branch condition"]; + 19 [shape=box label="Enter block"]; + 20 [shape=box label="Const: Int(20)"]; + 21 [shape=box label="Exit block"]; + 22 [shape=box label="Exit when branch result"]; + 23 [shape=box label="Enter when branch condition "]; + 24 [shape=box label="Const: Int(1)"]; + 25 [shape=box label="Const: Int(1)"]; + 26 [shape=box label="Function call: Int(1).R|kotlin/Int.minus|(Int(1))"]; + 27 [shape=box label="Const: Int(0)"]; + 28 [shape=box label="Operator =="]; + 29 [shape=box label="Exit when branch condition"]; + 30 [shape=box label="Enter block"]; + 31 [shape=box label="Jump: ^test_1 Unit"]; + 32 [shape=box label="Stub[DEAD]"]; + 33 [shape=box label="Exit block[DEAD]"]; + 34 [shape=box label="Exit when branch result[DEAD]"]; + 35 [shape=box label="Enter when branch condition else"]; + 36 [shape=box label="Exit when branch condition"]; + 37 [shape=box label="Enter block"]; + 38 [shape=box label="Const: Int(5)"]; + 39 [shape=box label="Exit block"]; + 40 [shape=box label="Exit when branch result"]; + 41 [shape=box label="Exit when"]; + 42 [shape=box label="Variable declaration: lval y: R|kotlin/Int|"]; + 43 [shape=box label="Exit block"]; + 44 [shape=box label="Exit function test_1"]; + + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + 3 -> {4}; + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {8 12}; + 8 -> {9}; + 9 -> {10}; + 10 -> {11}; + 11 -> {41}; + 12 -> {13}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18}; + 18 -> {19 23}; + 19 -> {20}; + 20 -> {21}; + 21 -> {22}; + 22 -> {41}; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + 26 -> {27}; + 27 -> {28}; + 28 -> {29}; + 29 -> {30 35}; + 30 -> {31}; + 31 -> {44}; + 31 -> {32} [style=dotted]; + 32 -> {33} [style=dotted]; + 33 -> {34} [style=dotted]; + 34 -> {41} [style=dotted]; + 35 -> {36}; + 36 -> {37}; + 37 -> {38}; + 38 -> {39}; + 39 -> {40}; + 40 -> {41}; + 41 -> {42}; + 42 -> {43}; + 43 -> {44}; +} + +subgraph test_2 { + 45 [shape=box label="Enter function test_2"]; + 46 [shape=box label="Enter block"]; + 47 [shape=box label="Enter when"]; + 48 [shape=box label="Enter when branch condition "]; + 49 [shape=box label="Enter &&"]; + 50 [shape=box label="Access variable R|/x|"]; + 51 [shape=box label="Type operator: x is A"]; + 52 [shape=box label="Access variable R|/x|"]; + 53 [shape=box label="Type operator: x is B"]; + 54 [shape=box label="Exit &&"]; + 55 [shape=box label="Exit when branch condition"]; + 56 [shape=box label="Enter block"]; + 57 [shape=box label="Access variable R|/x|"]; + 58 [shape=box label="Type operator: x is A"]; + 59 [shape=box label="Exit block"]; + 60 [shape=box label="Exit when branch result"]; + 61 [shape=box label="Enter when branch condition else"]; + 62 [shape=box label="Exit when branch condition"]; + 63 [shape=box label="Enter block"]; + 64 [shape=box label="Exit block"]; + 65 [shape=box label="Exit when branch result"]; + 66 [shape=box label="Exit when"]; + 67 [shape=box label="Exit block"]; + 68 [shape=box label="Exit function test_2"]; + + 45 -> {46}; + 46 -> {47}; + 47 -> {48}; + 48 -> {49}; + 49 -> {50}; + 50 -> {51}; + 51 -> {54 52}; + 52 -> {53}; + 53 -> {54}; + 54 -> {55}; + 55 -> {56 61}; + 56 -> {57}; + 57 -> {58}; + 58 -> {59}; + 59 -> {60}; + 60 -> {66}; + 61 -> {62}; + 62 -> {63}; + 63 -> {64}; + 64 -> {65}; + 65 -> {66}; + 66 -> {67}; + 67 -> {68}; +} + +} diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirCfgBuildingTest.kt b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirCfgBuildingTest.kt index c4e689e0e9e..b73a694b8b8 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirCfgBuildingTest.kt +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirCfgBuildingTest.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.fir import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.renderToStringBuilder +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.KotlinTestUtils @@ -25,18 +25,83 @@ abstract class AbstractFirCfgBuildingTest : AbstractFirResolveTestCase() { } fun checkCfg(path: String, firFiles: List) { - val firFileDump = StringBuilder().also { firFiles.first().accept(FirControlFlowGraphRenderVisitor(it), null) }.toString() - val expectedPath = path.replace(".kt", ".cfg.txt") - KotlinTestUtils.assertEqualsToFile(File(expectedPath), firFileDump) + val simpleBuilder = StringBuilder() + val dotBuilder = StringBuilder() + + firFiles.first().accept(FirControlFlowGraphRenderVisitor(simpleBuilder, dotBuilder), null) + + val dotCfgDump = dotBuilder.toString() + val dotExpectedPath = path.replace(".kt", ".cfg.dot") + KotlinTestUtils.assertEqualsToFile(File(dotExpectedPath), dotCfgDump) } - private class FirControlFlowGraphRenderVisitor(private val builder: StringBuilder) : FirVisitorVoid() { + private class FirControlFlowGraphRenderVisitor( + private val simpleBuilder: StringBuilder, + private val dotBuilder: StringBuilder + ) : FirVisitorVoid() { + private var indexOffset = 0 + + override fun visitFile(file: FirFile) { + dotBuilder.appendln("digraph ${file.name.replace(".", "_")} {") + super.visitFile(file) + dotBuilder.appendln("}") + } + override fun visitElement(element: FirElement) { element.acceptChildren(this) } override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) { - (controlFlowGraphReference as? FirControlFlowGraphReferenceImpl)?.controlFlowGraph?.renderToStringBuilder(builder) + val controlFlowGraph = (controlFlowGraphReference as? FirControlFlowGraphReferenceImpl)?.controlFlowGraph ?: return + controlFlowGraph.renderToStringBuilder(simpleBuilder) + indexOffset = controlFlowGraph.dotRenderToStringBuilder(dotBuilder, indexOffset) + dotBuilder.appendln() } } +} + +private const val EDGE = " -> " +private const val INDENT = " " + +fun ControlFlowGraph.dotRenderToStringBuilder(builder: StringBuilder, indexOffset: Int): Int { + with(builder) { + val sortedNodes = sortNodes() + val indices = sortedNodes.indicesMap().mapValues { (_, index) -> index + indexOffset } + appendln("subgraph ${name.replace(" ", "_")} {") + + fun CFGNode<*>.splitEdges(): Pair>, List>> = + if (isDead) emptyList>() to followingNodes + else followingNodes.filter { !it.isDead } to followingNodes.filter { it.isDead } + + sortedNodes.forEach { + append(INDENT) + append(indices.getValue(it)) + appendln(" [shape=box label=\"${it.render().replace("\"", "")}\"];") + } + appendln() + + sortedNodes.forEachIndexed { i, node -> + if (node.followingNodes.isEmpty()) return@forEachIndexed + val (aliveEdges, deadEdges) = node.splitEdges() + + fun renderEdges(edges: List>, isDead: Boolean) { + if (edges.isEmpty()) return + append(INDENT) + append(i + indexOffset) + append(EDGE) + append(edges.joinToString(prefix = "{", postfix = "}", separator = " ") { indices.getValue(it).toString() }) + if (isDead) { + append(" [style=dotted]") + } + appendln(";") + } + + renderEdges(aliveEdges, false) + renderEdges(deadEdges, true) + } + + appendln("}") + + return indexOffset + sortedNodes.size + } } \ No newline at end of file