FIR CFA: ensure on API level that graphs have enter & exit
What's going on with script graphs?..
This commit is contained in:
@@ -19,11 +19,6 @@ fun ControlFlowGraph.getNodesInOrder(direction: TraverseDirection): List<CFGNode
|
||||
TraverseDirection.Backward -> nodes.asReversed()
|
||||
}
|
||||
|
||||
fun CFGNode<*>.isEnterNode(direction: TraverseDirection): Boolean = when (direction) {
|
||||
TraverseDirection.Forward -> owner.enterNode == this
|
||||
TraverseDirection.Backward -> owner.exitNode == this
|
||||
}
|
||||
|
||||
val CFGNode<*>.previousCfgNodes: List<CFGNode<*>>
|
||||
get() = previousNodes.filter {
|
||||
val kind = edgeFrom(it).kind
|
||||
|
||||
+5
-4
@@ -231,12 +231,13 @@ abstract class FirDataFlowAnalyzer(
|
||||
// ----------------------------------- Scripts ------------------------------------------
|
||||
|
||||
fun enterScript(script: FirScript) {
|
||||
val res = graphBuilder.enterScript(script)
|
||||
res.mergeIncomingFlow()
|
||||
graphBuilder.enterScript(script).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitScript(script: FirScript) {
|
||||
graphBuilder.exitScript(script)
|
||||
fun exitScript(): ControlFlowGraph {
|
||||
val (node, graph) = graphBuilder.exitScript()
|
||||
node.mergeIncomingFlow()
|
||||
return graph
|
||||
}
|
||||
|
||||
// ----------------------------------- Value parameters (and it's defaults) -----------------------------------
|
||||
|
||||
+68
-65
@@ -113,13 +113,24 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
// ----------------------------------- Utils -----------------------------------
|
||||
|
||||
private fun pushGraph(graph: ControlFlowGraph) {
|
||||
graphs.push(graph)
|
||||
private inline fun <T, E : T, EnterNode, ExitNode> enterGraph(
|
||||
fir: E,
|
||||
name: String,
|
||||
kind: ControlFlowGraph.Kind,
|
||||
nodes: (E) -> Pair<EnterNode, ExitNode>
|
||||
): Pair<EnterNode, ExitNode> where EnterNode : CFGNode<T>, EnterNode : GraphEnterNodeMarker, ExitNode : CFGNode<T>, ExitNode : GraphExitNodeMarker {
|
||||
graphs.push(ControlFlowGraph(fir as? FirDeclaration, name, kind))
|
||||
levelCounter++
|
||||
return nodes(fir).also { (enterNode, exitNode) ->
|
||||
currentGraph.enterNode = enterNode
|
||||
currentGraph.exitNode = exitNode
|
||||
lastNodes.push(enterNode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun popGraph(): ControlFlowGraph {
|
||||
levelCounter--
|
||||
// TODO: count nodes per graph, validate the list after sorting
|
||||
return graphs.pop().also { it.complete() }
|
||||
}
|
||||
|
||||
@@ -137,24 +148,16 @@ class ControlFlowGraphBuilder {
|
||||
val localFunctionNode = runIf(function.symbol.callableId.isLocal && currentGraph.kind.withBody) {
|
||||
createLocalFunctionDeclarationNode(function).also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
pushGraph(ControlFlowGraph(function, name, ControlFlowGraph.Kind.Function))
|
||||
|
||||
val enterNode = createFunctionEnterNode(function).also {
|
||||
lastNodes.push(it)
|
||||
val (enterNode, exitNode) = enterGraph(function, name, ControlFlowGraph.Kind.Function) {
|
||||
createFunctionEnterNode(it) to createFunctionExitNode(it)
|
||||
}
|
||||
|
||||
if (localFunctionNode != null) {
|
||||
addEdge(localFunctionNode, enterNode)
|
||||
} else {
|
||||
enterToLocalClassesMembers.remove(function.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) }
|
||||
}
|
||||
|
||||
createFunctionExitNode(function).also {
|
||||
exitTargetsForReturn.push(it)
|
||||
exitTargetsForTry.push(it)
|
||||
}
|
||||
|
||||
exitTargetsForReturn.push(exitNode)
|
||||
exitTargetsForTry.push(exitNode)
|
||||
return Pair(localFunctionNode, enterNode)
|
||||
}
|
||||
|
||||
@@ -230,15 +233,14 @@ class ControlFlowGraphBuilder {
|
||||
fun enterAnonymousFunction(anonymousFunction: FirAnonymousFunction): FunctionEnterNode {
|
||||
val symbol = anonymousFunction.symbol
|
||||
val flowSourceNode = postponedAnonymousFunctionNodes.getValue(symbol).first
|
||||
pushGraph(ControlFlowGraph(anonymousFunction, "<anonymous>", ControlFlowGraph.Kind.AnonymousFunction))
|
||||
val enterNode = createFunctionEnterNode(anonymousFunction)
|
||||
val exitNode = createFunctionExitNode(anonymousFunction)
|
||||
val (enterNode, exitNode) = enterGraph(anonymousFunction, "<anonymous>", ControlFlowGraph.Kind.AnonymousFunction) {
|
||||
createFunctionEnterNode(it) to createFunctionExitNode(it)
|
||||
}
|
||||
exitTargetsForReturn.push(exitNode)
|
||||
if (!anonymousFunction.invocationKind.isInPlace) {
|
||||
exitTargetsForTry.push(exitNode)
|
||||
}
|
||||
addEdge(flowSourceNode, enterNode)
|
||||
lastNodes.push(enterNode)
|
||||
return enterNode
|
||||
}
|
||||
|
||||
@@ -364,11 +366,12 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
fun enterClass(klass: FirClass, buildGraph: Boolean): Pair<CFGNode<*>?, ClassEnterNode>? {
|
||||
if (!buildGraph) {
|
||||
pushGraph(ControlFlowGraph(null, "STUB_CLASS_GRAPH", ControlFlowGraph.Kind.Stub))
|
||||
graphs.push(ControlFlowGraph(null, "<discarded class graph>", ControlFlowGraph.Kind.ClassInitializer))
|
||||
levelCounter++
|
||||
return null
|
||||
}
|
||||
|
||||
val enterNode = when {
|
||||
val localClassEnterNode = when {
|
||||
// TODO: enum classes cannot be local so this is mostly fine, but it looks hacky. Maybe handle FirEnumEntry?
|
||||
klass is FirAnonymousObject && klass.classKind != ClassKind.ENUM_ENTRY -> createAnonymousObjectEnterNode(klass)
|
||||
// Local classes are only initialized on first use, so they look pretty much like named functions:
|
||||
@@ -382,33 +385,34 @@ class ControlFlowGraphBuilder {
|
||||
is FirRegularClass -> klass.name.asString()
|
||||
else -> throw IllegalArgumentException("Unknown class kind: ${klass::class}")
|
||||
}
|
||||
pushGraph(ControlFlowGraph(klass, name, ControlFlowGraph.Kind.ClassInitializer))
|
||||
|
||||
val graphEnterNode = createClassEnterNode(klass)
|
||||
lastNodes.push(graphEnterNode)
|
||||
if (enterNode != null) {
|
||||
addEdge(enterNode, graphEnterNode)
|
||||
val (enterNode, _) = enterGraph(klass, name, ControlFlowGraph.Kind.ClassInitializer) {
|
||||
createClassEnterNode(it) to createClassExitNode(it)
|
||||
}
|
||||
if (localClassEnterNode != null) {
|
||||
addEdge(localClassEnterNode, enterNode)
|
||||
} else {
|
||||
enterToLocalClassesMembers.remove(klass.symbol)?.let { addEdge(it, graphEnterNode, preferredKind = EdgeKind.DfgForward) }
|
||||
enterToLocalClassesMembers.remove(klass.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) }
|
||||
}
|
||||
|
||||
if (graphEnterNode.previousNodes.isNotEmpty()) {
|
||||
if (enterNode.previousNodes.isNotEmpty()) {
|
||||
for (member in klass.declarations) {
|
||||
if (member is FirFunction || member is FirAnonymousInitializer || member is FirField || member is FirClass || member is FirProperty) {
|
||||
enterToLocalClassesMembers[member.symbol] = graphEnterNode
|
||||
enterToLocalClassesMembers[member.symbol] = enterNode
|
||||
}
|
||||
if (member is FirProperty) {
|
||||
member.getter?.let { enterToLocalClassesMembers[it.symbol] = graphEnterNode }
|
||||
member.setter?.let { enterToLocalClassesMembers[it.symbol] = graphEnterNode }
|
||||
member.getter?.let { enterToLocalClassesMembers[it.symbol] = enterNode }
|
||||
member.setter?.let { enterToLocalClassesMembers[it.symbol] = enterNode }
|
||||
}
|
||||
}
|
||||
}
|
||||
return enterNode to graphEnterNode
|
||||
return localClassEnterNode to enterNode
|
||||
}
|
||||
|
||||
fun exitClass(): Pair<AnonymousObjectExitNode?, ControlFlowGraph>? {
|
||||
if (currentGraph.kind == ControlFlowGraph.Kind.Stub) {
|
||||
popGraph()
|
||||
if (currentGraph.declaration == null) {
|
||||
levelCounter--
|
||||
graphs.pop().also { assert(it.kind == ControlFlowGraph.Kind.ClassInitializer) }
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -468,7 +472,7 @@ class ControlFlowGraphBuilder {
|
||||
prevInitPartNode = partNode
|
||||
}
|
||||
|
||||
val exitNode = createClassExitNode(klass)
|
||||
val exitNode = currentGraph.exitNode as ClassExitNode
|
||||
addEdge(node, exitNode, preferredKind = EdgeKind.CfgForward)
|
||||
if (prevInitPartNode != null) {
|
||||
addEdge(prevInitPartNode, exitNode, preferredKind = EdgeKind.DeadForward, propagateDeadness = false)
|
||||
@@ -500,16 +504,19 @@ class ControlFlowGraphBuilder {
|
||||
}
|
||||
|
||||
fun enterScript(script: FirScript): ScriptEnterNode {
|
||||
pushGraph(ControlFlowGraph(null, "SCRIPT_GRAPH", ControlFlowGraph.Kind.Function))
|
||||
val enterNode = createScriptEnterNode(script)
|
||||
lastNodes.push(enterNode)
|
||||
val (enterNode, exitNode) = enterGraph(script, "SCRIPT_GRAPH", ControlFlowGraph.Kind.Function) {
|
||||
createScriptEnterNode(it) to createScriptExitNode(it)
|
||||
}
|
||||
exitTargetsForTry.push(exitNode)
|
||||
return enterNode
|
||||
}
|
||||
|
||||
fun exitScript(script: FirScript): ScriptExitNode {
|
||||
return createScriptExitNode(script).also {
|
||||
addNewSimpleNodeIfPossible(it)
|
||||
}
|
||||
fun exitScript(): Pair<ScriptExitNode, ControlFlowGraph> {
|
||||
val exitNode = exitTargetsForTry.pop() as ScriptExitNode
|
||||
addNewSimpleNode(exitNode)
|
||||
val graph = popGraph()
|
||||
assert(graph.exitNode == exitNode)
|
||||
return exitNode to graph
|
||||
}
|
||||
|
||||
// ----------------------------------- Value parameters (and it's defaults) -----------------------------------
|
||||
@@ -518,14 +525,14 @@ class ControlFlowGraphBuilder {
|
||||
if (valueParameter.defaultValue == null) return null
|
||||
|
||||
val outerEnterNode = createEnterValueParameterNode(valueParameter)
|
||||
pushGraph(ControlFlowGraph(valueParameter, "default value of ${valueParameter.name}", ControlFlowGraph.Kind.DefaultArgument))
|
||||
val innerExitNode = createExitDefaultArgumentsNode(valueParameter)
|
||||
val innerEnterNode = createEnterDefaultArgumentsNode(valueParameter)
|
||||
addNewSimpleNode(outerEnterNode)
|
||||
addEdge(outerEnterNode, innerEnterNode)
|
||||
lastNodes.push(innerEnterNode)
|
||||
exitTargetsForTry.push(innerExitNode)
|
||||
return outerEnterNode to innerEnterNode
|
||||
val name = "default value of ${valueParameter.name}"
|
||||
val (enterNode, exitNode) = enterGraph(valueParameter, name, ControlFlowGraph.Kind.DefaultArgument) {
|
||||
createEnterDefaultArgumentsNode(it) to createExitDefaultArgumentsNode(it)
|
||||
}
|
||||
addEdge(outerEnterNode, enterNode)
|
||||
exitTargetsForTry.push(exitNode)
|
||||
return outerEnterNode to enterNode
|
||||
}
|
||||
|
||||
fun exitValueParameter(valueParameter: FirValueParameter): Triple<ExitDefaultArgumentsNode, ExitValueParameterNode, ControlFlowGraph>? {
|
||||
@@ -563,13 +570,11 @@ class ControlFlowGraphBuilder {
|
||||
fun enterProperty(property: FirProperty): PropertyInitializerEnterNode? {
|
||||
if (!property.hasInitialization) return null
|
||||
|
||||
pushGraph(ControlFlowGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer))
|
||||
|
||||
val enterNode = createPropertyInitializerEnterNode(property)
|
||||
val exitNode = createPropertyInitializerExitNode(property)
|
||||
val (enterNode, exitNode) = enterGraph(property, "val ${property.name}", ControlFlowGraph.Kind.PropertyInitializer) {
|
||||
createPropertyInitializerEnterNode(it) to createPropertyInitializerExitNode(it)
|
||||
}
|
||||
exitTargetsForTry.push(exitNode)
|
||||
enterToLocalClassesMembers.remove(property.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) }
|
||||
lastNodes.push(enterNode)
|
||||
return enterNode
|
||||
}
|
||||
|
||||
@@ -587,13 +592,11 @@ class ControlFlowGraphBuilder {
|
||||
fun enterField(field: FirField): FieldInitializerEnterNode? {
|
||||
if (field.initializer == null) return null
|
||||
|
||||
pushGraph(ControlFlowGraph(field, "val ${field.name}", ControlFlowGraph.Kind.FieldInitializer))
|
||||
|
||||
val enterNode = createFieldInitializerEnterNode(field)
|
||||
val exitNode = createFieldInitializerExitNode(field)
|
||||
val (enterNode, exitNode) = enterGraph(field, "val ${field.name}", ControlFlowGraph.Kind.FieldInitializer) {
|
||||
createFieldInitializerEnterNode(it) to createFieldInitializerExitNode(it)
|
||||
}
|
||||
exitTargetsForTry.push(exitNode)
|
||||
enterToLocalClassesMembers.remove(field.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) }
|
||||
lastNodes.push(enterNode)
|
||||
return enterNode
|
||||
}
|
||||
|
||||
@@ -1192,7 +1195,8 @@ class ControlFlowGraphBuilder {
|
||||
// Things like annotations and `contract { ... }` use normal call resolution, but aren't real expressions
|
||||
// and are never evaluated. We'll push all nodes created in the process into a stub graph, then throw it away.
|
||||
// TODO: don't waste time creating the nodes in the first place
|
||||
pushGraph(ControlFlowGraph(null, "STUB_GRAPH_FOR_FAKE_EXPRESSION", ControlFlowGraph.Kind.FakeCall))
|
||||
graphs.push(ControlFlowGraph(null, "<compile-time expression graph>", ControlFlowGraph.Kind.FakeCall))
|
||||
levelCounter++
|
||||
return createFakeExpressionEnterNode().also {
|
||||
lastNodes.push(it)
|
||||
exitTargetsForTry.push(it) // technically might create CFG loops, but the graph will never be visited anyway...
|
||||
@@ -1200,9 +1204,10 @@ class ControlFlowGraphBuilder {
|
||||
}
|
||||
|
||||
fun exitFakeExpression() {
|
||||
levelCounter--
|
||||
lastNodes.pop()
|
||||
exitTargetsForTry.pop()
|
||||
popGraph()
|
||||
graphs.pop().also { assert(it.kind == ControlFlowGraph.Kind.FakeCall) }
|
||||
}
|
||||
|
||||
// ----------------------------------- Callable references -----------------------------------
|
||||
@@ -1219,13 +1224,11 @@ class ControlFlowGraphBuilder {
|
||||
|
||||
fun enterInitBlock(initBlock: FirAnonymousInitializer): InitBlockEnterNode {
|
||||
// TODO: questionable moment that we should pass data flow from init to init
|
||||
pushGraph(ControlFlowGraph(initBlock, "init block", ControlFlowGraph.Kind.Function))
|
||||
|
||||
val enterNode = createInitBlockEnterNode(initBlock)
|
||||
val exitNode = createInitBlockExitNode(initBlock)
|
||||
val (enterNode, exitNode) = enterGraph(initBlock, "init block", ControlFlowGraph.Kind.Function) {
|
||||
createInitBlockEnterNode(it) to createInitBlockExitNode(it)
|
||||
}
|
||||
exitTargetsForTry.push(exitNode)
|
||||
enterToLocalClassesMembers.remove(initBlock.symbol)?.let { addEdge(it, enterNode, preferredKind = EdgeKind.DfgForward) }
|
||||
lastNodes.push(enterNode)
|
||||
return enterNode
|
||||
}
|
||||
|
||||
|
||||
+2
-8
@@ -67,17 +67,11 @@ fun ControlFlowGraphBuilder.createFieldInitializerExitNode(fir: FirField): Field
|
||||
fun ControlFlowGraphBuilder.createFieldInitializerEnterNode(fir: FirField): FieldInitializerEnterNode =
|
||||
FieldInitializerEnterNode(currentGraph, fir, levelCounter, createId())
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
fun ControlFlowGraphBuilder.createFunctionEnterNode(fir: FirFunction): FunctionEnterNode =
|
||||
FunctionEnterNode(currentGraph, fir, levelCounter, createId()).also {
|
||||
currentGraph.enterNode = it
|
||||
}
|
||||
FunctionEnterNode(currentGraph, fir, levelCounter, createId())
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
fun ControlFlowGraphBuilder.createFunctionExitNode(fir: FirFunction): FunctionExitNode =
|
||||
FunctionExitNode(currentGraph, fir, levelCounter, createId()).also {
|
||||
currentGraph.exitNode = it
|
||||
}
|
||||
FunctionExitNode(currentGraph, fir, levelCounter, createId())
|
||||
|
||||
fun ControlFlowGraphBuilder.createLocalFunctionDeclarationNode(fir: FirFunction): LocalFunctionDeclarationNode =
|
||||
LocalFunctionDeclarationNode(currentGraph, fir, levelCounter, createId())
|
||||
|
||||
+1
-3
@@ -513,9 +513,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
|
||||
val result = context.withScopesForScript(script, components) {
|
||||
transformDeclarationContent(script, data) as FirScript
|
||||
}
|
||||
|
||||
dataFlowAnalyzer.exitScript(script)
|
||||
|
||||
dataFlowAnalyzer.exitScript() // TODO: FirScript should be a FirControlFlowGraphOwner
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -65,11 +65,6 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
owner.addNode(this)
|
||||
}
|
||||
|
||||
private val _previousNodes: MutableList<CFGNode<*>> = SmartList()
|
||||
private val _followingNodes: MutableList<CFGNode<*>> = SmartList()
|
||||
|
||||
@@ -132,6 +127,8 @@ val CFGNode<*>.lastPreviousNode: CFGNode<*> get() = previousNodes.last()
|
||||
|
||||
interface EnterNodeMarker
|
||||
interface ExitNodeMarker
|
||||
interface GraphEnterNodeMarker : EnterNodeMarker
|
||||
interface GraphExitNodeMarker : ExitNodeMarker
|
||||
|
||||
// a ---> b ---> d
|
||||
// \-> c -/
|
||||
@@ -153,24 +150,25 @@ sealed class CFGNodeWithCfgOwner<out E : FirControlFlowGraphOwner>(owner: Contro
|
||||
// ----------------------------------- Named function -----------------------------------
|
||||
|
||||
class FunctionEnterNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNode<FirFunction>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
GraphEnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFunctionEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionExitNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNode<FirFunction>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
GraphExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFunctionExitNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class LocalFunctionDeclarationNode(owner: ControlFlowGraph, override val fir: FirFunction, level: Int, id: Int) : CFGNodeWithCfgOwner<FirFunction>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitLocalFunctionDeclarationNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------- Default arguments -----------------------------------
|
||||
|
||||
class EnterValueParameterNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNodeWithCfgOwner<FirValueParameter>(owner, level, id) {
|
||||
@@ -179,25 +177,15 @@ class EnterValueParameterNode(owner: ControlFlowGraph, override val fir: FirValu
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class EnterDefaultArgumentsNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNode<FirValueParameter>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
GraphEnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitEnterDefaultArgumentsNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class ExitDefaultArgumentsNode(owner: ControlFlowGraph, override val fir: FirValueParameter, level: Int, id: Int) : CFGNode<FirValueParameter>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
GraphExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitExitDefaultArgumentsNode(this, data)
|
||||
}
|
||||
@@ -245,24 +233,15 @@ class AnonymousFunctionExpressionNode(owner: ControlFlowGraph, override val fir:
|
||||
|
||||
// ----------------------------------- Classes -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class ClassEnterNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int, id: Int) : CFGNode<FirClass>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
GraphEnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitClassEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class ClassExitNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int, id: Int) : CFGNodeWithSubgraphs<FirClass>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
GraphExitNodeMarker {
|
||||
|
||||
lateinit override var subGraphs: List<ControlFlowGraph>
|
||||
|
||||
@@ -297,13 +276,13 @@ class AnonymousObjectExpressionExitNode(owner: ControlFlowGraph, override val fi
|
||||
|
||||
// ----------------------------------- Scripts ------------------------------------------
|
||||
|
||||
class ScriptEnterNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode<FirScript>(owner, level, id) {
|
||||
class ScriptEnterNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode<FirScript>(owner, level, id), GraphEnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitScriptEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class ScriptExitNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode<FirScript>(owner, level, id) {
|
||||
class ScriptExitNode(owner: ControlFlowGraph, override val fir: FirScript, level: Int, id: Int) : CFGNode<FirScript>(owner, level, id), GraphExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitScriptExitNode(this, data)
|
||||
}
|
||||
@@ -319,25 +298,15 @@ class PartOfClassInitializationNode(owner: ControlFlowGraph, override val fir: F
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class PropertyInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
GraphEnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPropertyInitializerEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class PropertyInitializerExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
GraphExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitPropertyInitializerExitNode(this, data)
|
||||
}
|
||||
@@ -353,25 +322,15 @@ class DelegateExpressionExitNode(owner: ControlFlowGraph, override val fir: FirE
|
||||
|
||||
// ----------------------------------- Field -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class FieldInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirField, level: Int, id: Int) : CFGNode<FirField>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
GraphEnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFieldInitializerEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class FieldInitializerExitNode(owner: ControlFlowGraph, override val fir: FirField, level: Int, id: Int) : CFGNode<FirField>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
GraphExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitFieldInitializerExitNode(this, data)
|
||||
}
|
||||
@@ -379,25 +338,15 @@ class FieldInitializerExitNode(owner: ControlFlowGraph, override val fir: FirFie
|
||||
|
||||
// ----------------------------------- Init -----------------------------------
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class InitBlockEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int, id: Int) : CFGNode<FirAnonymousInitializer>(owner, level, id),
|
||||
EnterNodeMarker {
|
||||
init {
|
||||
owner.enterNode = this
|
||||
}
|
||||
|
||||
GraphEnterNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitInitBlockEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(CfgInternals::class)
|
||||
class InitBlockExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int, id: Int) : CFGNode<FirAnonymousInitializer>(owner, level, id),
|
||||
ExitNodeMarker {
|
||||
init {
|
||||
owner.exitNode = this
|
||||
}
|
||||
|
||||
GraphExitNodeMarker {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitInitBlockExitNode(this, data)
|
||||
}
|
||||
|
||||
+6
-38
@@ -10,15 +10,8 @@ import org.jetbrains.kotlin.fir.expressions.FirLoop
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
|
||||
class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val kind: Kind) {
|
||||
private var _nodes: MutableList<CFGNode<*>> = mutableListOf()
|
||||
|
||||
val nodes: List<CFGNode<*>>
|
||||
get() = _nodes
|
||||
|
||||
internal fun addNode(node: CFGNode<*>) {
|
||||
assertState(State.Building)
|
||||
_nodes.add(node)
|
||||
}
|
||||
lateinit var nodes: List<CFGNode<*>>
|
||||
private set
|
||||
|
||||
@set:CfgInternals
|
||||
lateinit var enterNode: CFGNode<*>
|
||||
@@ -29,36 +22,12 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
|
||||
val isSubGraph: Boolean
|
||||
get() = enterNode.previousNodes.isNotEmpty()
|
||||
|
||||
var state: State = State.Building
|
||||
private set
|
||||
|
||||
val subGraphs: List<ControlFlowGraph>
|
||||
get() = _nodes.flatMap { (it as? CFGNodeWithSubgraphs<*>)?.subGraphs ?: emptyList() }
|
||||
get() = nodes.flatMap { (it as? CFGNodeWithSubgraphs<*>)?.subGraphs ?: emptyList() }
|
||||
|
||||
@CfgInternals
|
||||
fun complete() {
|
||||
assertState(State.Building)
|
||||
state = State.Completed
|
||||
if (kind == Kind.Stub) return
|
||||
val sortedNodes = orderNodes()
|
||||
// TODO Fix this
|
||||
// assert(sortedNodes.size == _nodes.size)
|
||||
// for (node in _nodes) {
|
||||
// assert(node in sortedNodes)
|
||||
// }
|
||||
_nodes.clear()
|
||||
_nodes.addAll(sortedNodes)
|
||||
}
|
||||
|
||||
private fun assertState(state: State) {
|
||||
assert(this.state == state) {
|
||||
"This action can not be performed at $this state"
|
||||
}
|
||||
}
|
||||
|
||||
enum class State {
|
||||
Building,
|
||||
Completed;
|
||||
nodes = orderNodes()
|
||||
}
|
||||
|
||||
enum class Kind(val withBody: Boolean) {
|
||||
@@ -70,7 +39,6 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
|
||||
TopLevel(withBody = false),
|
||||
FakeCall(withBody = true),
|
||||
DefaultArgument(withBody = true),
|
||||
Stub(withBody = false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +114,7 @@ enum class EdgeKind(
|
||||
DeadBackward(usedInDfa = false, usedInDeadDfa = false, usedInCfa = true, isBack = true, isDead = true)
|
||||
}
|
||||
|
||||
private fun ControlFlowGraph.orderNodes(): LinkedHashSet<CFGNode<*>> {
|
||||
private fun ControlFlowGraph.orderNodes(): List<CFGNode<*>> {
|
||||
val visitedNodes = linkedSetOf<CFGNode<*>>()
|
||||
/*
|
||||
* [delayedNodes] is needed to accomplish next order contract:
|
||||
@@ -173,5 +141,5 @@ private fun ControlFlowGraph.orderNodes(): LinkedHashSet<CFGNode<*>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
return visitedNodes
|
||||
return visitedNodes.toList()
|
||||
}
|
||||
|
||||
+4
-1
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.FirControlFlowGraphReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.GraphEnterNodeMarker
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.GraphExitNodeMarker
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
import org.jetbrains.kotlin.test.Assertions
|
||||
|
||||
@@ -19,7 +21,8 @@ class FirCfgConsistencyChecker(private val assertions: Assertions) : FirVisitorV
|
||||
|
||||
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) {
|
||||
val graph = (controlFlowGraphReference as? FirControlFlowGraphReferenceImpl)?.controlFlowGraph ?: return
|
||||
assertions.assertEquals(ControlFlowGraph.State.Completed, graph.state)
|
||||
assertions.assertEquals(graph.nodes.single { it is GraphEnterNodeMarker }, graph.enterNode)
|
||||
assertions.assertEquals(graph.nodes.single { it is GraphExitNodeMarker }, graph.exitNode)
|
||||
checkConsistency(graph)
|
||||
checkOrder(graph)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user