FIR CFG: label edges from try-enter through finally block to exit target
This commit is contained in:
committed by
Dmitriy Novozhilov
parent
43852ad7ab
commit
6fc3f7e776
+3
-1
@@ -309,7 +309,8 @@ digraph propertiesAndInitBlocks_kt {
|
|||||||
}
|
}
|
||||||
84 -> {85};
|
84 -> {85};
|
||||||
85 -> {86};
|
85 -> {86};
|
||||||
86 -> {102 91 96 87};
|
86 -> {102 91 87};
|
||||||
|
86 -> {96} [label=onUncaughtException];
|
||||||
87 -> {88};
|
87 -> {88};
|
||||||
88 -> {89};
|
88 -> {89};
|
||||||
89 -> {90};
|
89 -> {90};
|
||||||
@@ -324,6 +325,7 @@ digraph propertiesAndInitBlocks_kt {
|
|||||||
98 -> {99};
|
98 -> {99};
|
||||||
99 -> {100};
|
99 -> {100};
|
||||||
100 -> {101};
|
100 -> {101};
|
||||||
|
100 -> {102} [label=onUncaughtException];
|
||||||
101 -> {102};
|
101 -> {102};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -101,7 +101,8 @@ finally {
|
|||||||
11 -> {12};
|
11 -> {12};
|
||||||
12 -> {13};
|
12 -> {13};
|
||||||
13 -> {14};
|
13 -> {14};
|
||||||
14 -> {30 22 15};
|
14 -> {30 15};
|
||||||
|
14 -> {22} [label=onUncaughtException];
|
||||||
15 -> {16};
|
15 -> {16};
|
||||||
16 -> {17};
|
16 -> {17};
|
||||||
17 -> {31};
|
17 -> {31};
|
||||||
@@ -115,6 +116,7 @@ finally {
|
|||||||
23 -> {24};
|
23 -> {24};
|
||||||
24 -> {25};
|
24 -> {25};
|
||||||
25 -> {26};
|
25 -> {26};
|
||||||
|
25 -> {30} [label=onUncaughtException];
|
||||||
26 -> {27};
|
26 -> {27};
|
||||||
27 -> {30};
|
27 -> {30};
|
||||||
27 -> {28} [style=dotted];
|
27 -> {28} [style=dotted];
|
||||||
@@ -190,7 +192,8 @@ finally {
|
|||||||
36 -> {37};
|
36 -> {37};
|
||||||
37 -> {38};
|
37 -> {38};
|
||||||
38 -> {39};
|
38 -> {39};
|
||||||
39 -> {55 47 40};
|
39 -> {55 40};
|
||||||
|
39 -> {47} [label=onUncaughtException];
|
||||||
40 -> {41};
|
40 -> {41};
|
||||||
41 -> {42};
|
41 -> {42};
|
||||||
42 -> {43 56};
|
42 -> {43 56};
|
||||||
@@ -203,6 +206,7 @@ finally {
|
|||||||
48 -> {49};
|
48 -> {49};
|
||||||
49 -> {50};
|
49 -> {50};
|
||||||
50 -> {51};
|
50 -> {51};
|
||||||
|
50 -> {55} [label=onUncaughtException];
|
||||||
51 -> {52};
|
51 -> {52};
|
||||||
52 -> {55};
|
52 -> {55};
|
||||||
52 -> {53} [style=dotted];
|
52 -> {53} [style=dotted];
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ fun CFGNode<*>.isEnterNode(direction: TraverseDirection): Boolean = when (direct
|
|||||||
|
|
||||||
val CFGNode<*>.previousCfgNodes: List<CFGNode<*>>
|
val CFGNode<*>.previousCfgNodes: List<CFGNode<*>>
|
||||||
get() = previousNodes.filter {
|
get() = previousNodes.filter {
|
||||||
val kind = incomingEdges.getValue(it)
|
val kind = incomingEdges.getValue(it).kind
|
||||||
if (this.isDead) {
|
if (this.isDead) {
|
||||||
kind.usedInCfa
|
kind.usedInCfa
|
||||||
} else {
|
} else {
|
||||||
@@ -39,7 +39,7 @@ val CFGNode<*>.followingCfgNodes: List<CFGNode<*>>
|
|||||||
val nodes = mutableListOf<CFGNode<*>>()
|
val nodes = mutableListOf<CFGNode<*>>()
|
||||||
|
|
||||||
followingNodes.filterTo(nodes) {
|
followingNodes.filterTo(nodes) {
|
||||||
val kind = outgoingEdges.getValue(it)
|
val kind = outgoingEdges.getValue(it).kind
|
||||||
kind.usedInCfa && !kind.isDead
|
kind.usedInCfa && !kind.isDead
|
||||||
}
|
}
|
||||||
(this as? CFGNodeWithCfgOwner<*>)?.subGraphs?.mapTo(nodes) { it.enterNode }
|
(this as? CFGNodeWithCfgOwner<*>)?.subGraphs?.mapTo(nodes) { it.enterNode }
|
||||||
|
|||||||
+2
-2
@@ -1118,9 +1118,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
shouldForkFlow: Boolean = false
|
shouldForkFlow: Boolean = false
|
||||||
): T = this.also { node ->
|
): T = this.also { node ->
|
||||||
val previousFlows = if (node.isDead)
|
val previousFlows = if (node.isDead)
|
||||||
node.previousNodes.mapNotNull { runIf(!node.incomingEdges.getValue(it).isBack) { it.flow } }
|
node.previousNodes.mapNotNull { runIf(!node.incomingEdges.getValue(it).kind.isBack) { it.flow } }
|
||||||
else
|
else
|
||||||
node.previousNodes.mapNotNull { prev -> prev.takeIf { node.incomingEdges.getValue(it).usedInDfa }?.flow }
|
node.previousNodes.mapNotNull { prev -> prev.takeIf { node.incomingEdges.getValue(it).kind.usedInDfa }?.flow }
|
||||||
var flow = logicSystem.joinFlow(previousFlows)
|
var flow = logicSystem.joinFlow(previousFlows)
|
||||||
if (updateReceivers) {
|
if (updateReceivers) {
|
||||||
logicSystem.updateAllReceivers(flow)
|
logicSystem.updateAllReceivers(flow)
|
||||||
|
|||||||
@@ -19,25 +19,46 @@ import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
|||||||
|
|
||||||
sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level: Int, private val id: Int) {
|
sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level: Int, private val id: Int) {
|
||||||
companion object {
|
companion object {
|
||||||
internal fun addEdge(from: CFGNode<*>, to: CFGNode<*>, kind: EdgeKind, propagateDeadness: Boolean) {
|
internal fun addEdge(
|
||||||
|
from: CFGNode<*>,
|
||||||
|
to: CFGNode<*>,
|
||||||
|
kind: EdgeKind,
|
||||||
|
propagateDeadness: Boolean,
|
||||||
|
label: EdgeLabel = NormalPath
|
||||||
|
) {
|
||||||
from._followingNodes += to
|
from._followingNodes += to
|
||||||
to._previousNodes += from
|
to._previousNodes += from
|
||||||
addJustKindEdge(from, to, kind, propagateDeadness, edgeExists = false)
|
addJustKindEdge(from, to, kind, propagateDeadness, edgeExists = false, label = label)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun addJustKindEdge(from: CFGNode<*>, to: CFGNode<*>, kind: EdgeKind, propagateDeadness: Boolean) {
|
internal fun addJustKindEdge(
|
||||||
addJustKindEdge(from, to, kind, propagateDeadness, edgeExists = true)
|
from: CFGNode<*>,
|
||||||
|
to: CFGNode<*>,
|
||||||
|
kind: EdgeKind,
|
||||||
|
propagateDeadness: Boolean,
|
||||||
|
label: EdgeLabel = NormalPath
|
||||||
|
) {
|
||||||
|
addJustKindEdge(from, to, kind, propagateDeadness, edgeExists = true, label = label)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addJustKindEdge(from: CFGNode<*>, to: CFGNode<*>, kind: EdgeKind, propagateDeadness: Boolean, edgeExists: Boolean) {
|
private fun addJustKindEdge(
|
||||||
if (kind != EdgeKind.Forward) {
|
from: CFGNode<*>,
|
||||||
val fromToKind = from._outgoingEdges[to] ?: runIf(edgeExists) { EdgeKind.Forward }
|
to: CFGNode<*>,
|
||||||
|
kind: EdgeKind,
|
||||||
|
propagateDeadness: Boolean,
|
||||||
|
edgeExists: Boolean,
|
||||||
|
label: EdgeLabel = NormalPath
|
||||||
|
) {
|
||||||
|
// It's hard to define label merging, hence overwritten with the latest one.
|
||||||
|
// One day, if we allow multiple edges between nodes with different labels, we won't even need kind merging.
|
||||||
|
if (kind != EdgeKind.Forward || label != NormalPath) {
|
||||||
|
val fromToKind = from._outgoingEdges[to]?.kind ?: runIf(edgeExists) { EdgeKind.Forward }
|
||||||
merge(kind, fromToKind)?.let {
|
merge(kind, fromToKind)?.let {
|
||||||
from._outgoingEdges[to] = it
|
from._outgoingEdges[to] = Edge.create(label, it)
|
||||||
} ?: from._outgoingEdges.remove(to)
|
} ?: from._outgoingEdges.remove(to)
|
||||||
val toFromKind = to._incomingEdges[from] ?: runIf(edgeExists) { EdgeKind.Forward }
|
val toFromKind = to._incomingEdges[from]?.kind ?: runIf(edgeExists) { EdgeKind.Forward }
|
||||||
merge(kind, toFromKind)?.let {
|
merge(kind, toFromKind)?.let {
|
||||||
to._incomingEdges[from] = it
|
to._incomingEdges[from] = Edge.create(label, it)
|
||||||
} ?: to._incomingEdges.remove(from)
|
} ?: to._incomingEdges.remove(from)
|
||||||
}
|
}
|
||||||
if (propagateDeadness && kind == EdgeKind.DeadForward) {
|
if (propagateDeadness && kind == EdgeKind.DeadForward) {
|
||||||
@@ -87,18 +108,18 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
|
|||||||
val previousNodes: List<CFGNode<*>> get() = _previousNodes
|
val previousNodes: List<CFGNode<*>> get() = _previousNodes
|
||||||
val followingNodes: List<CFGNode<*>> get() = _followingNodes
|
val followingNodes: List<CFGNode<*>> get() = _followingNodes
|
||||||
|
|
||||||
private val _incomingEdges = mutableMapOf<CFGNode<*>, EdgeKind>().withDefault { EdgeKind.Forward }
|
private val _incomingEdges = mutableMapOf<CFGNode<*>, Edge>().withDefault { Edge.Normal_Forward }
|
||||||
private val _outgoingEdges = mutableMapOf<CFGNode<*>, EdgeKind>().withDefault { EdgeKind.Forward }
|
private val _outgoingEdges = mutableMapOf<CFGNode<*>, Edge>().withDefault { Edge.Normal_Forward }
|
||||||
|
|
||||||
val incomingEdges: Map<CFGNode<*>, EdgeKind> get() = _incomingEdges
|
val incomingEdges: Map<CFGNode<*>, Edge> get() = _incomingEdges
|
||||||
val outgoingEdges: Map<CFGNode<*>, EdgeKind> get() = _outgoingEdges
|
val outgoingEdges: Map<CFGNode<*>, Edge> get() = _outgoingEdges
|
||||||
|
|
||||||
abstract val fir: E
|
abstract val fir: E
|
||||||
var isDead: Boolean = false
|
var isDead: Boolean = false
|
||||||
protected set
|
protected set
|
||||||
|
|
||||||
internal fun updateDeadStatus() {
|
internal fun updateDeadStatus() {
|
||||||
isDead = incomingEdges.size == previousNodes.size && incomingEdges.values.all { it == EdgeKind.DeadForward }
|
isDead = incomingEdges.size == previousNodes.size && incomingEdges.values.all { it.kind == EdgeKind.DeadForward }
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R
|
abstract fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R
|
||||||
|
|||||||
+54
-1
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||||
|
|
||||||
class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val kind: Kind) {
|
class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val kind: Kind) {
|
||||||
private var _nodes: MutableList<CFGNode<*>> = mutableListOf()
|
private var _nodes: MutableList<CFGNode<*>> = mutableListOf()
|
||||||
@@ -86,6 +87,58 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Edge(
|
||||||
|
val label: EdgeLabel,
|
||||||
|
val kind: EdgeKind,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val Normal_Forward = Edge(NormalPath, EdgeKind.Forward)
|
||||||
|
private val Normal_DeadForward = Edge(NormalPath, EdgeKind.DeadForward)
|
||||||
|
private val Normal_DfgForward = Edge(NormalPath, EdgeKind.DfgForward)
|
||||||
|
private val Normal_CfgForward = Edge(NormalPath, EdgeKind.CfgForward)
|
||||||
|
private val Normal_CfgBackward = Edge(NormalPath, EdgeKind.CfgBackward)
|
||||||
|
private val Normal_DeadBackward = Edge(NormalPath, EdgeKind.DeadBackward)
|
||||||
|
|
||||||
|
fun create(label: EdgeLabel, kind: EdgeKind): Edge =
|
||||||
|
when (label) {
|
||||||
|
NormalPath -> {
|
||||||
|
when (kind) {
|
||||||
|
EdgeKind.Forward -> Normal_Forward
|
||||||
|
EdgeKind.DeadForward -> Normal_DeadForward
|
||||||
|
EdgeKind.DfgForward -> Normal_DfgForward
|
||||||
|
EdgeKind.CfgForward -> Normal_CfgForward
|
||||||
|
EdgeKind.CfgBackward -> Normal_CfgBackward
|
||||||
|
EdgeKind.DeadBackward -> Normal_DeadBackward
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Edge(label, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class EdgeLabel(val label: String?) {
|
||||||
|
open val isNormal: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return label ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object NormalPath : EdgeLabel(label = null) {
|
||||||
|
override val isNormal: Boolean
|
||||||
|
get() = true
|
||||||
|
}
|
||||||
|
|
||||||
|
object UncaughtExceptionPath : EdgeLabel(label = "onUncaughtException")
|
||||||
|
|
||||||
|
// TODO: Label `return`ing edge with this.
|
||||||
|
class ReturnPath(
|
||||||
|
returnTargetSymbol: FirFunctionSymbol<*>
|
||||||
|
) : EdgeLabel(label = "return@${returnTargetSymbol.callableId}")
|
||||||
|
|
||||||
enum class EdgeKind(
|
enum class EdgeKind(
|
||||||
val usedInDfa: Boolean,
|
val usedInDfa: Boolean,
|
||||||
val usedInCfa: Boolean,
|
val usedInCfa: Boolean,
|
||||||
@@ -113,7 +166,7 @@ private fun ControlFlowGraph.orderNodes(): LinkedHashSet<CFGNode<*>> {
|
|||||||
while (stack.isNotEmpty()) {
|
while (stack.isNotEmpty()) {
|
||||||
val node = stack.removeFirst()
|
val node = stack.removeFirst()
|
||||||
val previousNodes = node.previousNodes
|
val previousNodes = node.previousNodes
|
||||||
if (previousNodes.any { it !in visitedNodes && it.owner == this && !node.incomingEdges.getValue(it).isBack }) {
|
if (previousNodes.any { it !in visitedNodes && it.owner == this && !node.incomingEdges.getValue(it).kind.isBack }) {
|
||||||
delayedNodes.add(node)
|
delayedNodes.add(node)
|
||||||
stack.addLast(node)
|
stack.addLast(node)
|
||||||
continue
|
continue
|
||||||
|
|||||||
+12
-7
@@ -777,7 +777,8 @@ class ControlFlowGraphBuilder {
|
|||||||
catchNodeStorages.push(NodeStorage())
|
catchNodeStorages.push(NodeStorage())
|
||||||
val enterTryExpressionNode = createTryExpressionEnterNode(tryExpression)
|
val enterTryExpressionNode = createTryExpressionEnterNode(tryExpression)
|
||||||
addNewSimpleNode(enterTryExpressionNode)
|
addNewSimpleNode(enterTryExpressionNode)
|
||||||
tryExitNodes.push(createTryExpressionExitNode(tryExpression))
|
val tryExitNode = createTryExpressionExitNode(tryExpression)
|
||||||
|
tryExitNodes.push(tryExitNode)
|
||||||
levelCounter++
|
levelCounter++
|
||||||
val enterTryNodeBlock = createTryMainBlockEnterNode(tryExpression)
|
val enterTryNodeBlock = createTryMainBlockEnterNode(tryExpression)
|
||||||
addNewSimpleNode(enterTryNodeBlock)
|
addNewSimpleNode(enterTryNodeBlock)
|
||||||
@@ -793,7 +794,7 @@ class ControlFlowGraphBuilder {
|
|||||||
|
|
||||||
if (tryExpression.finallyBlock != null) {
|
if (tryExpression.finallyBlock != null) {
|
||||||
val finallyEnterNode = createFinallyBlockEnterNode(tryExpression)
|
val finallyEnterNode = createFinallyBlockEnterNode(tryExpression)
|
||||||
addEdge(enterTryNodeBlock, finallyEnterNode)
|
addEdge(enterTryNodeBlock, finallyEnterNode, label = UncaughtExceptionPath)
|
||||||
finallyEnterNodes.push(finallyEnterNode)
|
finallyEnterNodes.push(finallyEnterNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,7 +842,9 @@ class ControlFlowGraphBuilder {
|
|||||||
fun exitFinallyBlock(tryExpression: FirTryExpression): FinallyBlockExitNode {
|
fun exitFinallyBlock(tryExpression: FirTryExpression): FinallyBlockExitNode {
|
||||||
return createFinallyBlockExitNode(tryExpression).also {
|
return createFinallyBlockExitNode(tryExpression).also {
|
||||||
popAndAddEdge(it)
|
popAndAddEdge(it)
|
||||||
addEdge(it, tryExitNodes.top())
|
val tryExitNode = tryExitNodes.top()
|
||||||
|
addEdge(it, tryExitNode)
|
||||||
|
addEdge(it, exitTargetsForTry.top(), label = UncaughtExceptionPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1193,20 +1196,22 @@ class ControlFlowGraphBuilder {
|
|||||||
propagateDeadness: Boolean = true,
|
propagateDeadness: Boolean = true,
|
||||||
isDead: Boolean = false,
|
isDead: Boolean = false,
|
||||||
isBack: Boolean = false,
|
isBack: Boolean = false,
|
||||||
preferredKind: EdgeKind = EdgeKind.Forward
|
preferredKind: EdgeKind = EdgeKind.Forward,
|
||||||
|
label: EdgeLabel = NormalPath
|
||||||
) {
|
) {
|
||||||
val kind = if (isDead || from.isDead || to.isDead) {
|
val kind = if (isDead || from.isDead || to.isDead) {
|
||||||
if (isBack) EdgeKind.DeadBackward else EdgeKind.DeadForward
|
if (isBack) EdgeKind.DeadBackward else EdgeKind.DeadForward
|
||||||
} else preferredKind
|
} else preferredKind
|
||||||
CFGNode.addEdge(from, to, kind, propagateDeadness)
|
CFGNode.addEdge(from, to, kind, propagateDeadness, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addBackEdge(
|
private fun addBackEdge(
|
||||||
from: CFGNode<*>,
|
from: CFGNode<*>,
|
||||||
to: CFGNode<*>,
|
to: CFGNode<*>,
|
||||||
isDead: Boolean = false
|
isDead: Boolean = false,
|
||||||
|
label: EdgeLabel = NormalPath
|
||||||
) {
|
) {
|
||||||
addEdge(from, to, propagateDeadness = false, isDead = isDead, isBack = true, preferredKind = EdgeKind.CfgBackward)
|
addEdge(from, to, propagateDeadness = false, isDead = isDead, isBack = true, preferredKind = EdgeKind.CfgBackward, label = label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Utils -----------------------------------
|
// ----------------------------------- Utils -----------------------------------
|
||||||
|
|||||||
+28
-8
@@ -108,15 +108,35 @@ class FirControlFlowGraphRenderVisitor(
|
|||||||
if (node.followingNodes.isEmpty()) continue
|
if (node.followingNodes.isEmpty()) continue
|
||||||
|
|
||||||
fun renderEdges(kind: EdgeKind) {
|
fun renderEdges(kind: EdgeKind) {
|
||||||
val edges = node.followingNodes.filter { node.outgoingEdges.getValue(it) == kind }
|
val edges = node.followingNodes.filter { node.outgoingEdges.getValue(it).kind == kind }
|
||||||
if (edges.isEmpty()) return
|
if (edges.isEmpty()) return
|
||||||
print(
|
|
||||||
indices.getValue(node),
|
fun renderEdgesWithoutLabel(edges: List<CFGNode<*>>) {
|
||||||
EDGE,
|
print(
|
||||||
edges.joinToString(prefix = "{", postfix = "}", separator = " ") { indices.getValue(it).toString() }
|
indices.getValue(node),
|
||||||
)
|
EDGE,
|
||||||
EDGE_STYLE.getValue(kind).takeIf { it.isNotBlank() }?.let { printWithNoIndent(" $it") }
|
edges.joinToString(prefix = "{", postfix = "}", separator = " ") { indices.getValue(it).toString() }
|
||||||
printlnWithNoIndent(";")
|
)
|
||||||
|
EDGE_STYLE.getValue(kind).takeIf { it.isNotBlank() }?.let { printWithNoIndent(" $it") }
|
||||||
|
printlnWithNoIndent(";")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edges.any { node.outgoingEdges[it]?.label?.label != null }) {
|
||||||
|
val edgeGroups = edges.groupBy { node.outgoingEdges[it]?.label?.label != null }
|
||||||
|
edgeGroups[false]?.let { renderEdgesWithoutLabel(it) }
|
||||||
|
for (edge in edgeGroups[true]!!) {
|
||||||
|
print(
|
||||||
|
indices.getValue(node),
|
||||||
|
EDGE,
|
||||||
|
"{", indices.getValue(edge), "}"
|
||||||
|
)
|
||||||
|
EDGE_STYLE.getValue(kind).takeIf { it.isNotBlank() }?.let { printWithNoIndent(" $it") }
|
||||||
|
print("[label=${node.outgoingEdges[edge]!!.label}]")
|
||||||
|
printlnWithNoIndent(";")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
renderEdgesWithoutLabel(edges)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (kind in EdgeKind.values()) {
|
for (kind in EdgeKind.values()) {
|
||||||
|
|||||||
@@ -326,8 +326,8 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
|
|||||||
private fun checkEdge(from: CFGNode<*>, to: CFGNode<*>) {
|
private fun checkEdge(from: CFGNode<*>, to: CFGNode<*>) {
|
||||||
KtUsefulTestCase.assertContainsElements(from.followingNodes, to)
|
KtUsefulTestCase.assertContainsElements(from.followingNodes, to)
|
||||||
KtUsefulTestCase.assertContainsElements(to.previousNodes, from)
|
KtUsefulTestCase.assertContainsElements(to.previousNodes, from)
|
||||||
val fromKind = from.outgoingEdges.getValue(to)
|
val fromKind = from.outgoingEdges.getValue(to).kind
|
||||||
val toKind = to.incomingEdges.getValue(from)
|
val toKind = to.incomingEdges.getValue(from).kind
|
||||||
TestCase.assertEquals(fromKind, toKind)
|
TestCase.assertEquals(fromKind, toKind)
|
||||||
if (from.isDead && to.isDead) {
|
if (from.isDead && to.isDead) {
|
||||||
KtUsefulTestCase.assertContainsElements(cfgKinds, toKind)
|
KtUsefulTestCase.assertContainsElements(cfgKinds, toKind)
|
||||||
@@ -339,7 +339,7 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
|
|||||||
for (node in graph.nodes) {
|
for (node in graph.nodes) {
|
||||||
for (previousNode in node.previousNodes) {
|
for (previousNode in node.previousNodes) {
|
||||||
if (previousNode.owner != graph) continue
|
if (previousNode.owner != graph) continue
|
||||||
if (!node.incomingEdges.getValue(previousNode).isBack) {
|
if (!node.incomingEdges.getValue(previousNode).kind.isBack) {
|
||||||
assertTrue(previousNode in visited)
|
assertTrue(previousNode in visited)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user