[FIR] Reduce coping of flow and inline default value of it

This commit is contained in:
Dmitriy Novozhilov
2019-09-02 11:35:51 +03:00
parent c2180b9361
commit 546bbceeea
2 changed files with 85 additions and 99 deletions
@@ -37,7 +37,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
private val graphBuilder = ControlFlowGraphBuilder() private val graphBuilder = ControlFlowGraphBuilder()
private val logicSystem = LogicSystem(context) private val logicSystem = LogicSystem(context)
private val variableStorage = DataFlowVariableStorage() private val variableStorage = DataFlowVariableStorage()
private val edges = mutableMapOf<CFGNode<*>, Flow>().withDefault { Flow.EMPTY } private val edges = mutableMapOf<CFGNode<*>, Flow>()
/* /*
* If there is no types from smartcasts function returns null * If there is no types from smartcasts function returns null
@@ -92,7 +92,6 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
if (previousNode != null) { if (previousNode != null) {
node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqTrue, node.flow) node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqTrue, node.flow)
} }
node.flow.freeze()
} }
fun exitBlock(block: FirBlock) { fun exitBlock(block: FirBlock) {
@@ -111,89 +110,81 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) { fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false) val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false)
try { if (typeOperatorCall.operation !in FirOperation.TYPES) return
if (typeOperatorCall.operation !in FirOperation.TYPES) return val symbol: FirCallableSymbol<*> = typeOperatorCall.argument.getResolvedSymbol() ?: return
val symbol: FirCallableSymbol<*> = typeOperatorCall.argument.getResolvedSymbol() ?: return
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return
val varVariable = getRealVariable(symbol) val varVariable = getRealVariable(symbol)
var flow = node.flow var flow = node.flow
when (typeOperatorCall.operation) { when (typeOperatorCall.operation) {
FirOperation.IS, FirOperation.NOT_IS -> { FirOperation.IS, FirOperation.NOT_IS -> {
val expressionVariable = getSyntheticVariable(typeOperatorCall) val expressionVariable = getSyntheticVariable(typeOperatorCall)
val trueInfo = FirDataFlowInfo(setOf(type), emptySet()) val trueInfo = FirDataFlowInfo(setOf(type), emptySet())
val falseInfo = FirDataFlowInfo(emptySet(), setOf(type)) val falseInfo = FirDataFlowInfo(emptySet(), setOf(type))
fun chooseInfo(trueBranch: Boolean) = fun chooseInfo(trueBranch: Boolean) =
if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( UnapprovedFirDataFlowInfo(
EqTrue, varVariable, chooseInfo(true) EqTrue, varVariable, chooseInfo(true)
)
) )
)
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( UnapprovedFirDataFlowInfo(
EqFalse, varVariable, chooseInfo(false) EqFalse, varVariable, chooseInfo(false)
)
) )
} )
FirOperation.AS -> {
flow = flow.addApprovedFact(varVariable, FirDataFlowInfo(setOf(type), emptySet()))
}
FirOperation.SAFE_AS -> {
val expressionVariable = getSyntheticVariable(typeOperatorCall)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet())
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type))
)
)
}
else -> throw IllegalStateException()
} }
node.flow = flow FirOperation.AS -> {
} finally { flow = flow.addApprovedFact(varVariable, FirDataFlowInfo(setOf(type), emptySet()))
node.flow.freeze() }
FirOperation.SAFE_AS -> {
val expressionVariable = getSyntheticVariable(typeOperatorCall)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet())
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type))
)
)
}
else -> throw IllegalStateException()
} }
node.flow = flow
} }
fun exitOperatorCall(operatorCall: FirOperatorCall) { fun exitOperatorCall(operatorCall: FirOperatorCall) {
val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false) val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false)
try { when (val operation = operatorCall.operation) {
when (val operation = operatorCall.operation) { FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> {
FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> { val leftOperand = operatorCall.arguments[0]
val leftOperand = operatorCall.arguments[0] val rightOperand = operatorCall.arguments[1]
val rightOperand = operatorCall.arguments[1]
val leftConst = leftOperand as? FirConstExpression<*> val leftConst = leftOperand as? FirConstExpression<*>
val rightConst = rightOperand as? FirConstExpression<*> val rightConst = rightOperand as? FirConstExpression<*>
when { when {
leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation) leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation)
rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation) rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation)
leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation) leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation)
rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation) rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation)
operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return
else -> processEq(node, leftOperand, rightOperand, operation) else -> processEq(node, leftOperand, rightOperand, operation)
}
} }
} }
} finally {
node.flow.freeze()
} }
} }
@@ -327,7 +318,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
fun exitWhenBranchCondition(whenBranch: FirWhenBranch) { fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow() val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow(true)
val conditionVariable = getVariable(whenBranch.condition) val conditionVariable = getVariable(whenBranch.condition)
node.variable = conditionVariable node.variable = conditionVariable
@@ -336,7 +327,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun exitWhenBranchResult(whenBranch: FirWhenBranch) { fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false) val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false)
val conditionVariable = getVariable(whenBranch.condition) val conditionVariable = getVariable(whenBranch.condition)
node.flow = node.flow.removeSyntheticVariable(conditionVariable) node.flow = node.flow.removeSyntheticVariable(conditionVariable).apply { freeze() }
} }
fun exitWhenExpression(whenExpression: FirWhenExpression) { fun exitWhenExpression(whenExpression: FirWhenExpression) {
@@ -448,31 +439,26 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun exitVariableDeclaration(variable: FirVariable<*>) { fun exitVariableDeclaration(variable: FirVariable<*>) {
val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false) val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false)
try { val initializer = variable.initializer ?: return
val initializer = variable.initializer ?: return
/* /*
* That part is needed for cases like that: * That part is needed for cases like that:
* *
* val b = x is String * val b = x is String
* ... * ...
* if (b) { * if (b) {
* x.length * x.length
* } * }
*/ */
variableStorage[initializer]?.let { initializerVariable -> variableStorage[initializer]?.let { initializerVariable ->
assert(initializerVariable.isSynthetic) assert(initializerVariable.isSynthetic)
val realVariable = getRealVariable(variable.symbol) val realVariable = getRealVariable(variable.symbol)
node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable) node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable)
} }
initializer.resolvedSymbol?.let { initializerSymbol: FirBasedSymbol<*> -> initializer.resolvedSymbol?.let { initializerSymbol: FirBasedSymbol<*> ->
val rhsVariable = variableStorage[initializerSymbol]?.takeIf { !it.isSynthetic } ?: return val rhsVariable = variableStorage[initializerSymbol]?.takeIf { !it.isSynthetic } ?: return
variableStorage.createAliasVariable(variable.symbol, rhsVariable) variableStorage.createAliasVariable(variable.symbol, rhsVariable)
}
} finally {
node.flow.freeze()
} }
} }
@@ -495,7 +481,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) { fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression) val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression)
leftNode.passFlow() leftNode.passFlow(true)
rightNode.passFlow(false) rightNode.passFlow(false)
val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir) val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir)
rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqTrue, rightNode.flow).also { it.freeze() } rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqTrue, rightNode.flow).also { it.freeze() }
@@ -531,7 +517,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
} }
} }
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() } node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable)
} }
fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
@@ -540,7 +526,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) { fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression) val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression)
leftNode.passFlow() leftNode.passFlow(true)
rightNode.passFlow(false) rightNode.passFlow(false)
val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir) val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir)
rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqFalse, rightNode.flow).also { it.freeze() } rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqFalse, rightNode.flow).also { it.freeze() }
@@ -571,13 +557,13 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
} }
} }
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() } node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable)
} }
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = getVariable(node.previousNodes.first().fir) val booleanExpressionVariable = getVariable(node.previousNodes.first().fir)
val variable = getVariable(functionCall) val variable = getVariable(functionCall)
node.flow = node.flow.copyNotApprovedFacts(booleanExpressionVariable, variable) { it.invert() }.also { it.freeze() } node.flow = node.flow.copyNotApprovedFacts(booleanExpressionVariable, variable) { it.invert() }
} }
// ----------------------------------- Annotations ----------------------------------- // ----------------------------------- Annotations -----------------------------------
@@ -609,12 +595,12 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
getVariable(leftOperand) to getVariable(rightOperand) getVariable(leftOperand) to getVariable(rightOperand)
private var CFGNode<*>.flow: Flow private var CFGNode<*>.flow: Flow
get() = edges.getValue(this) get() = edges[this] ?: Flow.EMPTY
set(value) { set(value) {
edges[this] = value edges[this] = value
} }
private fun <T : CFGNode<*>> T.passFlow(shouldFreeze: Boolean = true): T = this.also { node -> private fun <T : CFGNode<*>> T.passFlow(shouldFreeze: Boolean = false): T = this.also { node ->
node.flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }).also { node.flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }).also {
if (shouldFreeze) it.freeze() if (shouldFreeze) it.freeze()
} }
@@ -12,7 +12,7 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
fun or(storages: Collection<Flow>): Flow { fun or(storages: Collection<Flow>): Flow {
storages.singleOrNull()?.let { storages.singleOrNull()?.let {
return it.copy() return it
} }
val approvedFacts = mutableMapOf<DataFlowVariable, FirDataFlowInfo>().apply { val approvedFacts = mutableMapOf<DataFlowVariable, FirDataFlowInfo>().apply {
storages.map { it.approvedFacts.keys } storages.map { it.approvedFacts.keys }