FIR CFA: use a common fake node for annotation calls and contracts

The graphs for both will be completely discarded and should never be
visited.
This commit is contained in:
pyos
2022-12-10 20:28:44 +01:00
committed by Dmitriy Novozhilov
parent 4bb7b1ac9f
commit e710edbd77
10 changed files with 35 additions and 117 deletions
@@ -1030,12 +1030,12 @@ abstract class FirDataFlowAnalyzer(
// ----------------------------------- Annotations ----------------------------------- // ----------------------------------- Annotations -----------------------------------
fun enterAnnotation(annotation: FirAnnotation) { fun enterAnnotation() {
graphBuilder.enterAnnotation(annotation).mergeIncomingFlow() graphBuilder.enterFakeExpression().mergeIncomingFlow()
} }
fun exitAnnotation(annotation: FirAnnotation) { fun exitAnnotation() {
graphBuilder.exitAnnotation(annotation).mergeIncomingFlow() graphBuilder.exitFakeExpression()
} }
// ----------------------------------- Init block ----------------------------------- // ----------------------------------- Init block -----------------------------------
@@ -1053,11 +1053,11 @@ abstract class FirDataFlowAnalyzer(
// ----------------------------------- Contract description ----------------------------------- // ----------------------------------- Contract description -----------------------------------
fun enterContractDescription() { fun enterContractDescription() {
graphBuilder.enterContractDescription().mergeIncomingFlow() graphBuilder.enterFakeExpression().mergeIncomingFlow()
} }
fun exitContractDescription() { fun exitContractDescription() {
graphBuilder.exitContractDescription() graphBuilder.exitFakeExpression()
} }
// ----------------------------------- Elvis ----------------------------------- // ----------------------------------- Elvis -----------------------------------
@@ -1186,20 +1186,23 @@ class ControlFlowGraphBuilder {
return node return node
} }
// ----------------------------------- Annotations ----------------------------------- // ----------------------------------- Fake expressions -----------------------------------
fun enterAnnotation(annotation: FirAnnotation): AnnotationEnterNode { fun enterFakeExpression(): FakeExpressionEnterNode {
pushGraph(ControlFlowGraph(null, "STUB_GRAPH_FOR_ANNOTATION_CALL", ControlFlowGraph.Kind.AnnotationCall)) // Things like annotations and `contract { ... }` use normal call resolution, but aren't real expressions
return createAnnotationEnterNode(annotation).also { // 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))
return createFakeExpressionEnterNode().also {
lastNodes.push(it) lastNodes.push(it)
exitTargetsForTry.push(it) // technically might create CFG loops, but the graph will never be visited anyway...
} }
} }
fun exitAnnotation(annotation: FirAnnotation): AnnotationExitNode { fun exitFakeExpression() {
val node = createAnnotationExitNode(annotation) lastNodes.pop()
popAndAddEdge(node) exitTargetsForTry.pop()
popGraph() popGraph()
return node
} }
// ----------------------------------- Callable references ----------------------------------- // ----------------------------------- Callable references -----------------------------------
@@ -1319,23 +1322,6 @@ class ControlFlowGraphBuilder {
return exitNode return exitNode
} }
// ----------------------------------- Contract description -----------------------------------
fun enterContractDescription(): CFGNode<*> {
pushGraph(ControlFlowGraph(null, "contract description", ControlFlowGraph.Kind.AnonymousFunction))
return createContractDescriptionEnterNode().also {
lastNodes.push(it)
exitTargetsForTry.push(it)
}
}
fun exitContractDescription() {
lastNodes.pop()
exitTargetsForTry.pop()
popGraph()
}
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
fun reset() { fun reset() {
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.fir.expressions.*
fun ControlFlowGraphBuilder.createStubNode(): StubNode = StubNode(currentGraph, levelCounter, createId()) fun ControlFlowGraphBuilder.createStubNode(): StubNode = StubNode(currentGraph, levelCounter, createId())
fun ControlFlowGraphBuilder.createContractDescriptionEnterNode(): ContractDescriptionEnterNode = fun ControlFlowGraphBuilder.createFakeExpressionEnterNode(): FakeExpressionEnterNode =
ContractDescriptionEnterNode(currentGraph, levelCounter, createId()) FakeExpressionEnterNode(currentGraph, levelCounter, createId())
fun ControlFlowGraphBuilder.createLoopExitNode(fir: FirLoop): LoopExitNode = LoopExitNode(currentGraph, fir, levelCounter, createId()) fun ControlFlowGraphBuilder.createLoopExitNode(fir: FirLoop): LoopExitNode = LoopExitNode(currentGraph, fir, levelCounter, createId())
@@ -145,12 +145,6 @@ fun ControlFlowGraphBuilder.createStringConcatenationCallNode(fir: FirStringConc
fun ControlFlowGraphBuilder.createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode = fun ControlFlowGraphBuilder.createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode =
VariableAssignmentNode(currentGraph, fir, levelCounter, createId()) VariableAssignmentNode(currentGraph, fir, levelCounter, createId())
fun ControlFlowGraphBuilder.createAnnotationExitNode(fir: FirAnnotation): AnnotationExitNode =
AnnotationExitNode(currentGraph, fir, levelCounter, createId())
fun ControlFlowGraphBuilder.createAnnotationEnterNode(fir: FirAnnotation): AnnotationEnterNode =
AnnotationEnterNode(currentGraph, fir, levelCounter, createId())
fun ControlFlowGraphBuilder.createElvisLhsIsNotNullNode(fir: FirElvisExpression): ElvisLhsIsNotNullNode = fun ControlFlowGraphBuilder.createElvisLhsIsNotNullNode(fir: FirElvisExpression): ElvisLhsIsNotNullNode =
ElvisLhsIsNotNullNode(currentGraph, fir, levelCounter, createId()) ElvisLhsIsNotNullNode(currentGraph, fir, levelCounter, createId())
@@ -1098,9 +1098,9 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.Types) annotationCall.replaceAnnotationResolvePhase(FirAnnotationResolvePhase.Types)
return context.forAnnotation { return context.forAnnotation {
withFirArrayOfCallTransformer { withFirArrayOfCallTransformer {
dataFlowAnalyzer.enterAnnotation(annotationCall) dataFlowAnalyzer.enterAnnotation()
val result = callResolver.resolveAnnotationCall(annotationCall) val result = callResolver.resolveAnnotationCall(annotationCall)
dataFlowAnalyzer.exitAnnotation(result ?: annotationCall) dataFlowAnalyzer.exitAnnotation()
if (result == null) return annotationCall if (result == null) return annotationCall
callCompleter.completeCall(result, noExpectedType) callCompleter.completeCall(result, noExpectedType)
(result.argumentList as FirResolvedArgumentList).let { annotationCall.replaceArgumentMapping((it).toAnnotationArgumentMapping()) } (result.argumentList as FirResolvedArgumentList).let { annotationCall.replaceArgumentMapping((it).toAnnotationArgumentMapping()) }
@@ -143,9 +143,9 @@ private class FirExpressionsResolveTransformerForSpecificAnnotations(
) : FirExpressionsResolveTransformer(transformer) { ) : FirExpressionsResolveTransformer(transformer) {
override fun transformAnnotation(annotation: FirAnnotation, data: ResolutionMode): FirStatement { override fun transformAnnotation(annotation: FirAnnotation, data: ResolutionMode): FirStatement {
dataFlowAnalyzer.enterAnnotation(annotation) dataFlowAnalyzer.enterAnnotation()
annotation.transformChildren(transformer, ResolutionMode.ContextDependent) annotation.transformChildren(transformer, ResolutionMode.ContextDependent)
dataFlowAnalyzer.exitAnnotation(annotation) dataFlowAnalyzer.exitAnnotation()
return annotation return annotation
} }
@@ -747,18 +747,6 @@ class StubNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(
} }
} }
@OptIn(CfgInternals::class)
class ContractDescriptionEnterNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(owner, level, id) {
init {
owner.enterNode = this
}
override val fir: FirStub = FirStub
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitContractDescriptionEnterNode(this, data)
}
}
class VariableDeclarationNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id) { class VariableDeclarationNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id) {
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitVariableDeclarationNode(this, data) return visitor.visitVariableDeclarationNode(this, data)
@@ -813,32 +801,6 @@ class WhenSubjectExpressionExitNode(owner: ControlFlowGraph, override val fir: F
} }
} }
// ----------------------------------- Other -----------------------------------
@OptIn(CfgInternals::class)
class AnnotationEnterNode(owner: ControlFlowGraph, override val fir: FirAnnotation, level: Int, id: Int) : CFGNode<FirAnnotation>(owner, level, id),
EnterNodeMarker {
init {
owner.enterNode = this
}
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitAnnotationEnterNode(this, data)
}
}
@OptIn(CfgInternals::class)
class AnnotationExitNode(owner: ControlFlowGraph, override val fir: FirAnnotation, level: Int, id: Int) : CFGNode<FirAnnotation>(owner, level, id),
ExitNodeMarker {
init {
owner.exitNode = this
}
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitAnnotationExitNode(this, data)
}
}
// ----------------------------------- Stub ----------------------------------- // ----------------------------------- Stub -----------------------------------
object FirStub : FirExpression() { object FirStub : FirExpression() {
@@ -853,6 +815,16 @@ object FirStub : FirExpression() {
override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) } override fun replaceTypeRef(newTypeRef: FirTypeRef) { assert(newTypeRef.isNothing) }
} }
class FakeExpressionEnterNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(owner, level, id) {
init { isDead = true }
override val fir: FirStub = FirStub
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
throw IllegalStateException("fake expressions should not appear in graphs")
}
}
// ----------------------------------- Smart-cast node ----------------------------------- // ----------------------------------- Smart-cast node -----------------------------------
class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: FirSmartCastExpression, level: Int, id: Int) : CFGNode<FirSmartCastExpression>(owner, level, id) { class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: FirSmartCastExpression, level: Int, id: Int) : CFGNode<FirSmartCastExpression>(owner, level, id) {
@@ -90,8 +90,6 @@ fun CFGNode<*>.render(): String =
is FieldInitializerExitNode -> "Exit field" is FieldInitializerExitNode -> "Exit field"
is InitBlockEnterNode -> "Enter init block" is InitBlockEnterNode -> "Enter init block"
is InitBlockExitNode -> "Exit init block" is InitBlockExitNode -> "Exit init block"
is AnnotationEnterNode -> "Enter annotation"
is AnnotationExitNode -> "Exit annotation"
is EnterSafeCallNode -> "Enter safe call" is EnterSafeCallNode -> "Enter safe call"
is ExitSafeCallNode -> "Exit safe call" is ExitSafeCallNode -> "Exit safe call"
@@ -113,7 +111,7 @@ fun CFGNode<*>.render(): String =
is ScriptEnterNode -> "Enter class ${fir.name}" is ScriptEnterNode -> "Enter class ${fir.name}"
is ScriptExitNode -> "Exit class ${fir.name}" is ScriptExitNode -> "Exit class ${fir.name}"
is ContractDescriptionEnterNode -> "Enter contract description" is FakeExpressionEnterNode -> "Enter fake expression"
is EnterValueParameterNode -> "Enter default value of ${fir.name}" is EnterValueParameterNode -> "Enter default value of ${fir.name}"
is EnterDefaultArgumentsNode -> "Enter default value of ${fir.name}" is EnterDefaultArgumentsNode -> "Enter default value of ${fir.name}"
@@ -68,7 +68,7 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k
PropertyInitializer(withBody = true), PropertyInitializer(withBody = true),
FieldInitializer(withBody = true), FieldInitializer(withBody = true),
TopLevel(withBody = false), TopLevel(withBody = false),
AnnotationCall(withBody = true), FakeCall(withBody = true),
DefaultArgument(withBody = true), DefaultArgument(withBody = true),
Stub(withBody = false) Stub(withBody = false)
} }
@@ -347,10 +347,6 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
return visitNode(node, data) return visitNode(node, data)
} }
open fun visitContractDescriptionEnterNode(node: ContractDescriptionEnterNode, data: D): R {
return visitNode(node, data)
}
open fun visitVariableDeclarationNode(node: VariableDeclarationNode, data: D): R { open fun visitVariableDeclarationNode(node: VariableDeclarationNode, data: D): R {
return visitNode(node, data) return visitNode(node, data)
} }
@@ -391,14 +387,6 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
// ----------------------------------- Other ----------------------------------- // ----------------------------------- Other -----------------------------------
open fun visitAnnotationEnterNode(node: AnnotationEnterNode, data: D): R {
return visitNode(node, data)
}
open fun visitAnnotationExitNode(node: AnnotationExitNode, data: D): R {
return visitNode(node, data)
}
open fun visitSmartCastExpressionExitNode(node: SmartCastExpressionExitNode, data: D): R { open fun visitSmartCastExpressionExitNode(node: SmartCastExpressionExitNode, data: D): R {
return visitNode(node, data) return visitNode(node, data)
} }
@@ -291,16 +291,6 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
visitNode(node) visitNode(node)
} }
// ----------------------------------- Other -----------------------------------
open fun visitAnnotationEnterNode(node: AnnotationEnterNode) {
visitNode(node)
}
open fun visitAnnotationExitNode(node: AnnotationExitNode) {
visitNode(node)
}
// --------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------
final override fun visitNode(node: CFGNode<*>, data: Nothing?) { final override fun visitNode(node: CFGNode<*>, data: Nothing?) {
@@ -591,14 +581,4 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
final override fun visitExitSafeCallNode(node: ExitSafeCallNode, data: Nothing?) { final override fun visitExitSafeCallNode(node: ExitSafeCallNode, data: Nothing?) {
visitExitSafeCallNode(node) visitExitSafeCallNode(node)
} }
// ----------------------------------- Other -----------------------------------
final override fun visitAnnotationEnterNode(node: AnnotationEnterNode, data: Nothing?) {
visitAnnotationEnterNode(node)
}
final override fun visitAnnotationExitNode(node: AnnotationExitNode, data: Nothing?) {
visitAnnotationExitNode(node)
}
} }