Generify key type parameter of ControlFlowInfo

Previously only `VariableDescriptor` was key of `ControlFlowInfo`.
Now it's generic parameter `K: Any`, so there is a possibility of using
  different object's inside CFA
This commit is contained in:
Dmitriy Novozhilov
2018-09-24 14:39:27 +03:00
parent efe891bb47
commit 0e8c43f8c0
7 changed files with 79 additions and 74 deletions
@@ -16,41 +16,40 @@
package org.jetbrains.kotlin.cfg package org.jetbrains.kotlin.cfg
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.util.javaslang.ImmutableHashMap import org.jetbrains.kotlin.util.javaslang.ImmutableHashMap
import org.jetbrains.kotlin.util.javaslang.ImmutableMap import org.jetbrains.kotlin.util.javaslang.ImmutableMap
interface ReadOnlyControlFlowInfo<D : Any> { interface ReadOnlyControlFlowInfo<K : Any, D : Any> {
fun getOrNull(variableDescriptor: VariableDescriptor): D? fun getOrNull(key: K): D?
// Only used in tests // Only used in tests
fun asMap(): ImmutableMap<VariableDescriptor, D> fun asMap(): ImmutableMap<K, D>
} }
abstract class ControlFlowInfo<S : ControlFlowInfo<S, D>, D : Any> abstract class ControlFlowInfo<S : ControlFlowInfo<S, K, D>, K : Any, D : Any>
internal constructor( internal constructor(
protected val map: ImmutableMap<VariableDescriptor, D> = ImmutableHashMap.empty() protected val map: ImmutableMap<K, D> = ImmutableHashMap.empty()
) : ImmutableMap<VariableDescriptor, D> by map, ReadOnlyControlFlowInfo<D> { ) : ImmutableMap<K, D> by map, ReadOnlyControlFlowInfo<K, D> {
protected abstract fun copy(newMap: ImmutableMap<VariableDescriptor, D>): S protected abstract fun copy(newMap: ImmutableMap<K, D>): S
override fun put(key: VariableDescriptor, value: D): S = put(key, value, this[key].getOrElse(null as D?)) override fun put(key: K, value: D): S = put(key, value, this[key].getOrElse(null as D?))
/** /**
* This overload exists just for sake of optimizations: in some cases we've just retrieved the old value, * This overload exists just for sake of optimizations: in some cases we've just retrieved the old value,
* so we don't need to scan through the persistent hashmap again * so we don't need to scan through the persistent hashmap again
*/ */
fun put(key: VariableDescriptor, value: D, oldValue: D?): S { fun put(key: K, value: D, oldValue: D?): S {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
// Avoid a copy instance creation if new value is the same // Avoid a copy instance creation if new value is the same
if (value == oldValue) return this as S if (value == oldValue) return this as S
return copy(map.put(key, value)) return copy(map.put(key, value))
} }
override fun getOrNull(variableDescriptor: VariableDescriptor): D? = this[variableDescriptor].getOrElse(null as D?) override fun getOrNull(key: K): D? = this[key].getOrElse(null as D?)
override fun asMap() = this override fun asMap() = this
fun retainAll(predicate: (VariableDescriptor) -> Boolean): S = copy(map.removeAll(map.keySet().filterNot(predicate))) fun retainAll(predicate: (K) -> Boolean): S = copy(map.removeAll(map.keySet().filterNot(predicate)))
override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*, *>)?.map override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*, *, *>)?.map
override fun hashCode() = map.hashCode() override fun hashCode() = map.hashCode()
@@ -1,5 +1,5 @@
/* /*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file. * that can be found in the license/LICENSE.txt file.
*/ */
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cfg.TailRecursionKind.* import org.jetbrains.kotlin.cfg.TailRecursionKind.*
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction
@@ -32,7 +31,6 @@ import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.idea.MainFunctionDetector import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.BindingContext.* import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
@@ -326,8 +324,8 @@ class ControlFlowInformationProvider private constructor(
val reportedDiagnosticMap = hashMapOf<Instruction, DiagnosticFactory<*>>() val reportedDiagnosticMap = hashMapOf<Instruction, DiagnosticFactory<*>>()
pseudocode.traverse(TraversalOrder.FORWARD, initializers) { instruction: Instruction, pseudocode.traverse(TraversalOrder.FORWARD, initializers) { instruction: Instruction,
enterData: ReadOnlyInitControlFlowInfo, enterData: VariableInitReadOnlyControlFlowInfo,
exitData: ReadOnlyInitControlFlowInfo -> exitData: VariableInitReadOnlyControlFlowInfo ->
val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo) val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo)
if (ctxt.variableDescriptor == null) return@traverse if (ctxt.variableDescriptor == null) return@traverse
@@ -603,7 +601,7 @@ class ControlFlowInformationProvider private constructor(
private fun recordInitializedVariables( private fun recordInitializedVariables(
pseudocode: Pseudocode, pseudocode: Pseudocode,
initializersMap: Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> initializersMap: Map<Instruction, Edges<VariableInitReadOnlyControlFlowInfo>>
) { ) {
val initializers = initializersMap[pseudocode.exitInstruction] ?: return val initializers = initializersMap[pseudocode.exitInstruction] ?: return
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false) val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false)
@@ -624,8 +622,8 @@ class ControlFlowInformationProvider private constructor(
val unusedValueExpressions = hashMapOf<KtExpression, Pair<VariableDescriptor, VariableUseContext>>() val unusedValueExpressions = hashMapOf<KtExpression, Pair<VariableDescriptor, VariableUseContext>>()
val usedValueExpressions = hashSetOf<KtExpression>() val usedValueExpressions = hashSetOf<KtExpression>()
pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) { instruction: Instruction, pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) { instruction: Instruction,
enterData: ReadOnlyUseControlFlowInfo, enterData: VariableUsageReadOnlyControlInfo,
_: ReadOnlyUseControlFlowInfo -> _: VariableUsageReadOnlyControlInfo ->
val ctxt = VariableUseContext(instruction, reportedDiagnosticMap) val ctxt = VariableUseContext(instruction, reportedDiagnosticMap)
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false) val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false)
@@ -1071,8 +1069,8 @@ class ControlFlowInformationProvider private constructor(
private inner class VariableInitContext( private inner class VariableInitContext(
instruction: Instruction, instruction: Instruction,
map: MutableMap<Instruction, DiagnosticFactory<*>>, map: MutableMap<Instruction, DiagnosticFactory<*>>,
`in`: ReadOnlyInitControlFlowInfo, `in`: VariableInitReadOnlyControlFlowInfo,
out: ReadOnlyInitControlFlowInfo, out: VariableInitReadOnlyControlFlowInfo,
blockScopeVariableInfo: BlockScopeVariableInfo blockScopeVariableInfo: BlockScopeVariableInfo
) : VariableContext(instruction, map) { ) : VariableContext(instruction, map) {
internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`) internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`)
@@ -1081,7 +1079,7 @@ class ControlFlowInformationProvider private constructor(
private fun initialize( private fun initialize(
variableDescriptor: VariableDescriptor?, variableDescriptor: VariableDescriptor?,
blockScopeVariableInfo: BlockScopeVariableInfo, blockScopeVariableInfo: BlockScopeVariableInfo,
map: ReadOnlyInitControlFlowInfo map: VariableInitReadOnlyControlFlowInfo
): VariableControlFlowState? { ): VariableControlFlowState? {
val state = map.getOrNull(variableDescriptor ?: return null) val state = map.getOrNull(variableDescriptor ?: return null)
if (state != null) return state if (state != null) return state
@@ -56,7 +56,7 @@ fun <D> Pseudocode.traverse(
} }
} }
fun <I : ControlFlowInfo<*, *>> Pseudocode.collectData( fun <I : ControlFlowInfo<*, *, *>> Pseudocode.collectData(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
mergeEdges: (Instruction, Collection<I>) -> Edges<I>, mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
updateEdge: (Instruction, Instruction, I) -> I, updateEdge: (Instruction, Instruction, I) -> I,
@@ -76,7 +76,7 @@ fun <I : ControlFlowInfo<*, *>> Pseudocode.collectData(
return edgesMap return edgesMap
} }
private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph( private fun <I : ControlFlowInfo<*, *, *>> Pseudocode.collectDataFromSubgraph(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
edgesMap: MutableMap<Instruction, Edges<I>>, edgesMap: MutableMap<Instruction, Edges<I>>,
mergeEdges: (Instruction, Collection<I>) -> Edges<I>, mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
@@ -154,7 +154,7 @@ private fun getPreviousIncludingSubGraphInstructions(
return result return result
} }
private fun <I : ControlFlowInfo<*, *>> updateEdgeDataForInstruction( private fun <I : ControlFlowInfo<*, *, *>> updateEdgeDataForInstruction(
instruction: Instruction, instruction: Instruction,
previousValue: Edges<I>?, previousValue: Edges<I>?,
newValue: Edges<I>?, newValue: Edges<I>?,
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.cfg.variable package org.jetbrains.kotlin.cfg.variable
import org.jetbrains.kotlin.cfg.ControlFlowInfo
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
@@ -24,7 +23,7 @@ class PseudocodeVariableDataCollector(
) { ) {
val blockScopeVariableInfo = computeBlockScopeVariableInfo(pseudocode) val blockScopeVariableInfo = computeBlockScopeVariableInfo(pseudocode)
fun <I : ControlFlowInfo<*, *>> collectData( fun <I : VariableUsageControlFlowInfo<*, *>> collectData(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
initialInfo: I, initialInfo: I,
instructionDataMergeStrategy: (Instruction, Collection<I>) -> Edges<I> instructionDataMergeStrategy: (Instruction, Collection<I>) -> Edges<I>
@@ -37,7 +36,7 @@ class PseudocodeVariableDataCollector(
) )
} }
private fun <I : ControlFlowInfo<*, *>> filterOutVariablesOutOfScope( private fun <I : VariableUsageControlFlowInfo<*, *>> filterOutVariablesOutOfScope(
from: Instruction, from: Instruction,
to: Instruction, to: Instruction,
info: I info: I
@@ -45,7 +45,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations = true) getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations = true)
} }
val variableInitializers: Map<Instruction, Edges<ReadOnlyInitVariableControlFlowInfo>> by lazy { val variableInitializers: Map<Instruction, Edges<VariableInitReadOnlyControlFlowInfo>> by lazy {
computeVariableInitializers() computeVariableInitializers()
} }
@@ -133,7 +133,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
// variable initializers // variable initializers
private fun computeVariableInitializers(): Map<Instruction, Edges<ReadOnlyInitVariableControlFlowInfo>> { private fun computeVariableInitializers(): Map<Instruction, Edges<VariableInitReadOnlyControlFlowInfo>> {
val blockScopeVariableInfo = pseudocodeVariableDataCollector.blockScopeVariableInfo val blockScopeVariableInfo = pseudocodeVariableDataCollector.blockScopeVariableInfo
@@ -143,8 +143,8 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
return pseudocodeVariableDataCollector.collectData( return pseudocodeVariableDataCollector.collectData(
TraversalOrder.FORWARD, TraversalOrder.FORWARD,
InitVariableControlFlowInfo() VariableInitControlFlowInfo()
) { instruction: Instruction, incomingEdgesData: Collection<InitVariableControlFlowInfo> -> ) { instruction: Instruction, incomingEdgesData: Collection<VariableInitControlFlowInfo> ->
val enterInstructionData = val enterInstructionData =
mergeIncomingEdgesDataForInitializers( mergeIncomingEdgesDataForInitializers(
@@ -197,8 +197,8 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
private inner class ReadOnlyInitVariableControlFlowInfoImpl( private inner class ReadOnlyInitVariableControlFlowInfoImpl(
val declaredSet: ImmutableSet<VariableDescriptor>, val declaredSet: ImmutableSet<VariableDescriptor>,
val initSet: ImmutableSet<VariableDescriptor>, val initSet: ImmutableSet<VariableDescriptor>,
private val delegate: ReadOnlyInitVariableControlFlowInfo? private val delegate: VariableInitReadOnlyControlFlowInfo?
) : ReadOnlyInitVariableControlFlowInfo { ) : VariableInitReadOnlyControlFlowInfo {
override fun getOrNull(key: VariableDescriptor): VariableControlFlowState? { override fun getOrNull(key: VariableDescriptor): VariableControlFlowState? {
if (key in declaredSet) { if (key in declaredSet) {
return VariableControlFlowState.create(isInitialized = key in initSet, isDeclared = true) return VariableControlFlowState.create(isInitialized = key in initSet, isDeclared = true)
@@ -206,10 +206,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
return delegate?.getOrNull(key) return delegate?.getOrNull(key)
} }
override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitVariableControlFlowInfo): Boolean = override fun checkDefiniteInitializationInWhen(merge: VariableInitReadOnlyControlFlowInfo): Boolean =
delegate?.checkDefiniteInitializationInWhen(merge) ?: false delegate?.checkDefiniteInitializationInWhen(merge) ?: false
fun replaceDelegate(newDelegate: ReadOnlyInitVariableControlFlowInfo): ReadOnlyInitVariableControlFlowInfo = fun replaceDelegate(newDelegate: VariableInitReadOnlyControlFlowInfo): VariableInitReadOnlyControlFlowInfo =
ReadOnlyInitVariableControlFlowInfoImpl(declaredSet, initSet, newDelegate) ReadOnlyInitVariableControlFlowInfoImpl(declaredSet, initSet, newDelegate)
override fun asMap(): ImmutableMap<VariableDescriptor, VariableControlFlowState> { override fun asMap(): ImmutableMap<VariableDescriptor, VariableControlFlowState> {
@@ -243,9 +243,9 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
private fun addVariableInitStateFromCurrentInstructionIfAny( private fun addVariableInitStateFromCurrentInstructionIfAny(
instruction: Instruction, instruction: Instruction,
enterInstructionData: InitVariableControlFlowInfo, enterInstructionData: VariableInitControlFlowInfo,
blockScopeVariableInfo: BlockScopeVariableInfo blockScopeVariableInfo: BlockScopeVariableInfo
): InitVariableControlFlowInfo { ): VariableInitControlFlowInfo {
if (instruction is MagicInstruction) { if (instruction is MagicInstruction) {
if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) { if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
return enterInstructionData.iterator().fold(enterInstructionData) { result, (key, value) -> return enterInstructionData.iterator().fold(enterInstructionData) { result, (key, value) ->
@@ -297,11 +297,11 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
// variable use // variable use
val variableUseStatusData: Map<Instruction, Edges<ReadOnlyUseVariableControlFlowInfo>> val variableUseStatusData: Map<Instruction, Edges<VariableUsageReadOnlyControlInfo>>
get() { get() {
val edgesForTrivialVals = computeUseInfoForTrivialVals() val edgesForTrivialVals = computeUseInfoForTrivialVals()
if (rootVariables.nonTrivialVariables.isEmpty()) { if (rootVariables.nonTrivialVariables.isEmpty()) {
return hashMapOf<Instruction, Edges<ReadOnlyUseVariableControlFlowInfo>>().apply { return hashMapOf<Instruction, Edges<VariableUsageReadOnlyControlInfo>>().apply {
pseudocode.traverse(TraversalOrder.FORWARD) { instruction -> pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
put(instruction, edgesForTrivialVals) put(instruction, edgesForTrivialVals)
} }
@@ -310,13 +310,13 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
return pseudocodeVariableDataCollector.collectData( return pseudocodeVariableDataCollector.collectData(
TraversalOrder.BACKWARD, TraversalOrder.BACKWARD,
UseVariableControlFlowInfo() UsageVariableControlFlowInfo()
) { instruction: Instruction, incomingEdgesData: Collection<UseVariableControlFlowInfo> -> ) { instruction: Instruction, incomingEdgesData: Collection<UsageVariableControlFlowInfo> ->
val enterResult: UseVariableControlFlowInfo = if (incomingEdgesData.size == 1) { val enterResult: UsageVariableControlFlowInfo = if (incomingEdgesData.size == 1) {
incomingEdgesData.single() incomingEdgesData.single()
} else { } else {
incomingEdgesData.fold(UseVariableControlFlowInfo()) { result, edgeData -> incomingEdgesData.fold(UsageVariableControlFlowInfo()) { result, edgeData ->
edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) -> edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) ->
subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor))) subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor)))
} }
@@ -377,14 +377,14 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
private inner class ReadOnlyUseControlFlowInfoImpl( private inner class ReadOnlyUseControlFlowInfoImpl(
val used: Set<VariableDescriptor>, val used: Set<VariableDescriptor>,
val delegate: ReadOnlyUseVariableControlFlowInfo? val delegate: VariableUsageReadOnlyControlInfo?
) : ReadOnlyUseVariableControlFlowInfo { ) : VariableUsageReadOnlyControlInfo {
override fun getOrNull(key: VariableDescriptor): VariableUseState? { override fun getOrNull(key: VariableDescriptor): VariableUseState? {
if (key in used) return VariableUseState.READ if (key in used) return VariableUseState.READ
return delegate?.getOrNull(key) return delegate?.getOrNull(key)
} }
fun replaceDelegate(newDelegate: ReadOnlyUseVariableControlFlowInfo): ReadOnlyUseVariableControlFlowInfo = fun replaceDelegate(newDelegate: VariableUsageReadOnlyControlInfo): VariableUsageReadOnlyControlInfo =
ReadOnlyUseControlFlowInfoImpl(used, newDelegate) ReadOnlyUseControlFlowInfoImpl(used, newDelegate)
override fun asMap(): ImmutableMap<VariableDescriptor, VariableUseState> { override fun asMap(): ImmutableMap<VariableDescriptor, VariableUseState> {
@@ -431,13 +431,13 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
return VariableControlFlowState.create(isInitialized = declaredOutsideThisDeclaration) return VariableControlFlowState.create(isInitialized = declaredOutsideThisDeclaration)
} }
private val EMPTY_INIT_CONTROL_FLOW_INFO = InitVariableControlFlowInfo() private val EMPTY_INIT_CONTROL_FLOW_INFO = VariableInitControlFlowInfo()
private fun mergeIncomingEdgesDataForInitializers( private fun mergeIncomingEdgesDataForInitializers(
instruction: Instruction, instruction: Instruction,
incomingEdgesData: Collection<InitVariableControlFlowInfo>, incomingEdgesData: Collection<VariableInitControlFlowInfo>,
blockScopeVariableInfo: BlockScopeVariableInfo blockScopeVariableInfo: BlockScopeVariableInfo
): InitVariableControlFlowInfo { ): VariableInitControlFlowInfo {
if (incomingEdgesData.size == 1) return incomingEdgesData.single() if (incomingEdgesData.size == 1) return incomingEdgesData.single()
if (incomingEdgesData.isEmpty()) return EMPTY_INIT_CONTROL_FLOW_INFO if (incomingEdgesData.isEmpty()) return EMPTY_INIT_CONTROL_FLOW_INFO
val variablesInScope = linkedSetOf<VariableDescriptor>() val variablesInScope = linkedSetOf<VariableDescriptor>()
@@ -8,26 +8,33 @@ package org.jetbrains.kotlin.cfg.variable
import org.jetbrains.kotlin.cfg.ControlFlowInfo import org.jetbrains.kotlin.cfg.ControlFlowInfo
import org.jetbrains.kotlin.cfg.ReadOnlyControlFlowInfo import org.jetbrains.kotlin.cfg.ReadOnlyControlFlowInfo
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.util.javaslang.ImmutableHashMap
import org.jetbrains.kotlin.util.javaslang.ImmutableMap
import org.jetbrains.kotlin.util.javaslang.component1 import org.jetbrains.kotlin.util.javaslang.component1
import org.jetbrains.kotlin.util.javaslang.component2 import org.jetbrains.kotlin.util.javaslang.component2
interface ReadOnlyInitControlFlowInfo : ReadOnlyControlFlowInfo<VariableControlFlowState> { typealias VariableUsageReadOnlyControlInfo = ReadOnlyControlFlowInfo<VariableDescriptor, VariableUseState>
fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean typealias VariableUsageControlFlowInfo<S, D> = ControlFlowInfo<S, VariableDescriptor, D>
interface VariableInitReadOnlyControlFlowInfo :
ReadOnlyControlFlowInfo<VariableDescriptor, VariableControlFlowState> {
fun checkDefiniteInitializationInWhen(merge: VariableInitReadOnlyControlFlowInfo): Boolean
} }
typealias ReadOnlyUseControlFlowInfo = ReadOnlyControlFlowInfo<VariableUseState> class VariableInitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlFlowState> = ImmutableHashMap.empty()) :
VariableUsageControlFlowInfo<VariableInitControlFlowInfo, VariableControlFlowState>(map),
class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlFlowState> = ImmutableHashMap.empty()) : VariableInitReadOnlyControlFlowInfo {
ControlFlowInfo<InitControlFlowInfo, VariableControlFlowState>(map), ReadOnlyInitControlFlowInfo { override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableControlFlowState>) =
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableControlFlowState>) = InitControlFlowInfo(newMap) VariableInitControlFlowInfo(newMap)
// this = output of EXHAUSTIVE_WHEN_ELSE instruction // this = output of EXHAUSTIVE_WHEN_ELSE instruction
// merge = input of MergeInstruction // merge = input of MergeInstruction
// returns true if definite initialization in when happens here // returns true if definite initialization in when happens here
override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean { override fun checkDefiniteInitializationInWhen(merge: VariableInitReadOnlyControlFlowInfo): Boolean {
for ((key, value) in iterator()) { for ((key, value) in iterator()) {
if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY && if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY &&
merge.getOrNull(key)?.initState == InitState.INITIALIZED) { merge.getOrNull(key)?.initState == InitState.INITIALIZED
) {
return true return true
} }
} }
@@ -35,9 +42,11 @@ class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlF
} }
} }
class UseControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableUseState> = ImmutableHashMap.empty()) : class UsageVariableControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableUseState> = ImmutableHashMap.empty()) :
ControlFlowInfo<UseControlFlowInfo, VariableUseState>(map), ReadOnlyUseControlFlowInfo { VariableUsageControlFlowInfo<UsageVariableControlFlowInfo, VariableUseState>(map),
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableUseState>) = UseControlFlowInfo(newMap) VariableUsageReadOnlyControlInfo {
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableUseState>) =
UsageVariableControlFlowInfo(newMap)
} }
enum class InitState(private val s: String) { enum class InitState(private val s: String) {
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction; import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges; import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges;
import org.jetbrains.kotlin.cfg.variable.PseudocodeVariablesData; import org.jetbrains.kotlin.cfg.variable.PseudocodeVariablesData;
import org.jetbrains.kotlin.cfg.variable.ReadOnlyInitControlFlowInfo; import org.jetbrains.kotlin.cfg.variable.VariableInitReadOnlyControlFlowInfo;
import org.jetbrains.kotlin.cfg.variable.VariableUseState; import org.jetbrains.kotlin.cfg.variable.VariableUseState;
import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
@@ -42,9 +42,9 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest {
@NotNull BindingContext bindingContext @NotNull BindingContext bindingContext
) { ) {
PseudocodeVariablesData pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode.getRootPseudocode(), bindingContext); PseudocodeVariablesData pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode.getRootPseudocode(), bindingContext);
Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> variableInitializers = Map<Instruction, Edges<VariableInitReadOnlyControlFlowInfo>> variableInitializers =
pseudocodeVariablesData.getVariableInitializers(); pseudocodeVariablesData.getVariableInitializers();
Map<Instruction, Edges<ReadOnlyControlFlowInfo<VariableUseState>>> useStatusData = Map<Instruction, Edges<ReadOnlyControlFlowInfo<VariableDescriptor, VariableUseState>>> useStatusData =
pseudocodeVariablesData.getVariableUseStatusData(); pseudocodeVariablesData.getVariableUseStatusData();
String initPrefix = " INIT:"; String initPrefix = " INIT:";
String usePrefix = " USE:"; String usePrefix = " USE:";
@@ -53,16 +53,16 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest {
dumpInstructions(pseudocode, out, (instruction, next, prev) -> { dumpInstructions(pseudocode, out, (instruction, next, prev) -> {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
Edges<ReadOnlyInitControlFlowInfo> initializersEdges = variableInitializers.get(instruction); Edges<VariableInitReadOnlyControlFlowInfo> initializersEdges = variableInitializers.get(instruction);
Edges<ReadOnlyInitControlFlowInfo> previousInitializersEdges = variableInitializers.get(prev); Edges<VariableInitReadOnlyControlFlowInfo> previousInitializersEdges = variableInitializers.get(prev);
String initializersData = ""; String initializersData = "";
if (initializersEdges != null && !initializersEdges.equals(previousInitializersEdges)) { if (initializersEdges != null && !initializersEdges.equals(previousInitializersEdges)) {
initializersData = dumpEdgesData(initPrefix, initializersEdges, pseudocodeVariablesData); initializersData = dumpEdgesData(initPrefix, initializersEdges, pseudocodeVariablesData);
} }
result.append(String.format("%1$-" + initializersColumnWidth + "s", initializersData)); result.append(String.format("%1$-" + initializersColumnWidth + "s", initializersData));
Edges<ReadOnlyControlFlowInfo<VariableUseState>> useStatusEdges = useStatusData.get(instruction); Edges<ReadOnlyControlFlowInfo<VariableDescriptor, VariableUseState>> useStatusEdges = useStatusData.get(instruction);
Edges<ReadOnlyControlFlowInfo<VariableUseState>> nextUseStatusEdges = useStatusData.get(next); Edges<ReadOnlyControlFlowInfo<VariableDescriptor, VariableUseState>> nextUseStatusEdges = useStatusData.get(next);
if (useStatusEdges != null && !useStatusEdges.equals(nextUseStatusEdges)) { if (useStatusEdges != null && !useStatusEdges.equals(nextUseStatusEdges)) {
result.append(dumpEdgesData(usePrefix, useStatusEdges, pseudocodeVariablesData)); result.append(dumpEdgesData(usePrefix, useStatusEdges, pseudocodeVariablesData));
} }
@@ -73,12 +73,12 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest {
private static int countDataColumnWidth( private static int countDataColumnWidth(
@NotNull String prefix, @NotNull String prefix,
@NotNull List<Instruction> instructions, @NotNull List<Instruction> instructions,
@NotNull Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> data, @NotNull Map<Instruction, Edges<VariableInitReadOnlyControlFlowInfo>> data,
@NotNull PseudocodeVariablesData variablesData @NotNull PseudocodeVariablesData variablesData
) { ) {
int maxWidth = 0; int maxWidth = 0;
for (Instruction instruction : instructions) { for (Instruction instruction : instructions) {
Edges<ReadOnlyInitControlFlowInfo> edges = data.get(instruction); Edges<VariableInitReadOnlyControlFlowInfo> edges = data.get(instruction);
if (edges == null) continue; if (edges == null) continue;
int length = dumpEdgesData(prefix, edges, variablesData).length(); int length = dumpEdgesData(prefix, edges, variablesData).length();
if (maxWidth < length) { if (maxWidth < length) {
@@ -90,7 +90,7 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest {
} }
@NotNull @NotNull
private static <S, I extends ReadOnlyControlFlowInfo<S>> String dumpEdgesData( private static <S, I extends ReadOnlyControlFlowInfo<VariableDescriptor, S>> String dumpEdgesData(
String prefix, String prefix,
@NotNull Edges<I> edges, @NotNull Edges<I> edges,
@NotNull PseudocodeVariablesData variablesData @NotNull PseudocodeVariablesData variablesData