ControlFlowInfo introduced to store variable states, related refactoring

This commit is contained in:
Mikhail Glukhikh
2015-12-25 13:57:58 +03:00
committed by Mikhail Glukhikh
parent 0f3997c6ca
commit 52c3fb03a2
6 changed files with 144 additions and 134 deletions
@@ -16,6 +16,35 @@
package org.jetbrains.kotlin.cfg package org.jetbrains.kotlin.cfg
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import java.util.*
open class ControlFlowInfo<D> internal constructor(protected val map: MutableMap<VariableDescriptor, D> = hashMapOf()) :
MutableMap<VariableDescriptor, D> by map {
open fun copy() = ControlFlowInfo(HashMap(map))
fun retainAll(predicate: (VariableDescriptor) -> Boolean): ControlFlowInfo<D> {
map.keys.retainAll(predicate)
return this
}
override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*>)?.map
override fun hashCode() = map.hashCode()
override fun toString() = map.toString()
}
class InitControlFlowInfo(map: MutableMap<VariableDescriptor, VariableControlFlowState> = hashMapOf()) :
ControlFlowInfo<VariableControlFlowState>(map) {
override fun copy() = InitControlFlowInfo(HashMap(map))
}
class UseControlFlowInfo(map: MutableMap<VariableDescriptor, VariableUseState> = hashMapOf()) :
ControlFlowInfo<VariableUseState>(map) {
override fun copy() = UseControlFlowInfo(HashMap(map))
}
enum class InitState(private val s: String) { enum class InitState(private val s: String) {
// Definitely initialized // Definitely initialized
INITIALIZED("I"), INITIALIZED("I"),
@@ -301,7 +301,7 @@ public class ControlFlowInformationProvider {
final boolean processClassOrObject = subroutine instanceof KtClassOrObject; final boolean processClassOrObject = subroutine instanceof KtClassOrObject;
PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData(); PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializers = Map<Instruction, Edges<InitControlFlowInfo>> initializers =
pseudocodeVariablesData.getVariableInitializers(); pseudocodeVariablesData.getVariableInitializers();
final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true); final Set<VariableDescriptor> declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true);
final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariablesData.getLexicalScopeVariableInfo(); final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariablesData.getLexicalScopeVariableInfo();
@@ -350,8 +350,7 @@ public class ControlFlowInformationProvider {
public void recordInitializedVariables() { public void recordInitializedVariables() {
PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData(); PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode(); Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode();
Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializers = Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
pseudocodeVariablesData.getVariableInitializers();
recordInitializedVariables(pseudocode, initializers); recordInitializedVariables(pseudocode, initializers);
for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) { for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) {
recordInitializedVariables(instruction.getBody(), initializers); recordInitializedVariables(instruction.getBody(), initializers);
@@ -517,9 +516,9 @@ public class ControlFlowInformationProvider {
private void recordInitializedVariables( private void recordInitializedVariables(
@NotNull Pseudocode pseudocode, @NotNull Pseudocode pseudocode,
@NotNull Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> initializersMap @NotNull Map<Instruction, Edges<InitControlFlowInfo>> initializersMap
) { ) {
Edges<Map<VariableDescriptor, VariableControlFlowState>> initializers = initializersMap.get(pseudocode.getExitInstruction()); Edges<InitControlFlowInfo> initializers = initializersMap.get(pseudocode.getExitInstruction());
if (initializers == null) return; if (initializers == null) return;
Set<VariableDescriptor> declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false); Set<VariableDescriptor> declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false);
for (VariableDescriptor variable : declaredVariables) { for (VariableDescriptor variable : declaredVariables) {
@@ -536,8 +535,7 @@ public class ControlFlowInformationProvider {
public void markUnusedVariables() { public void markUnusedVariables() {
final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData(); final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData();
Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> variableStatusData = Map<Instruction, Edges<UseControlFlowInfo>> variableStatusData = pseudocodeVariablesData.getVariableUseStatusData();
pseudocodeVariablesData.getVariableUseStatusData();
final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap(); final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy = InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>> variableStatusAnalyzeStrategy =
new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>>() { new InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableUseState>>() {
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.cfg.pseudocodeTraverser package org.jetbrains.kotlin.cfg.pseudocodeTraverser
import org.jetbrains.kotlin.cfg.ControlFlowInfo
import org.jetbrains.kotlin.cfg.pseudocode.* import org.jetbrains.kotlin.cfg.pseudocode.*
import java.util.* import java.util.*
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
@@ -54,16 +55,16 @@ public fun <D> Pseudocode.traverse(
} }
} }
public fun <D> Pseudocode.collectData( public fun <I : ControlFlowInfo<*>> Pseudocode.collectData(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
mergeDataWithLocalDeclarations: Boolean, mergeDataWithLocalDeclarations: Boolean,
mergeEdges: (Instruction, Collection<D>) -> Edges<D>, mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
updateEdge: (Instruction, Instruction, D) -> D, updateEdge: (Instruction, Instruction, I) -> I,
initialDataValue: D initialInfo: I
): Map<Instruction, Edges<D>> { ): Map<Instruction, Edges<I>> {
val edgesMap = LinkedHashMap<Instruction, Edges<D>>() val edgesMap = LinkedHashMap<Instruction, Edges<I>>()
initializeEdgesMap(edgesMap, initialDataValue) initializeEdgesMap(edgesMap, initialInfo)
edgesMap.put(getStartInstruction(traversalOrder), Edges(initialDataValue, initialDataValue)) edgesMap.put(getStartInstruction(traversalOrder), Edges(initialInfo, initialInfo))
val changed = BooleanArray(1) val changed = BooleanArray(1)
changed[0] = true changed[0] = true
@@ -76,26 +77,26 @@ public fun <D> Pseudocode.collectData(
return edgesMap return edgesMap
} }
private fun <D> Pseudocode.initializeEdgesMap( private fun <I> Pseudocode.initializeEdgesMap(
edgesMap: MutableMap<Instruction, Edges<D>>, edgesMap: MutableMap<Instruction, Edges<I>>,
initialDataValue: D initialInfo: I
) { ) {
val instructions = getInstructions() val instructions = getInstructions()
val initialEdge = Edges(initialDataValue, initialDataValue) val initialEdge = Edges(initialInfo, initialInfo)
for (instruction in instructions) { for (instruction in instructions) {
edgesMap.put(instruction, initialEdge) edgesMap.put(instruction, initialEdge)
if (instruction is LocalFunctionDeclarationInstruction) { if (instruction is LocalFunctionDeclarationInstruction) {
instruction.body.initializeEdgesMap(edgesMap, initialDataValue) instruction.body.initializeEdgesMap(edgesMap, initialInfo)
} }
} }
} }
private fun <D> Pseudocode.collectDataFromSubgraph( private fun <I : ControlFlowInfo<*>> Pseudocode.collectDataFromSubgraph(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
mergeDataWithLocalDeclarations: Boolean, mergeDataWithLocalDeclarations: Boolean,
edgesMap: MutableMap<Instruction, Edges<D>>, edgesMap: MutableMap<Instruction, Edges<I>>,
mergeEdges: (Instruction, Collection<D>) -> Edges<D>, mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
updateEdge: (Instruction, Instruction, D) -> D, updateEdge: (Instruction, Instruction, I) -> I,
previousSubGraphInstructions: Collection<Instruction>, previousSubGraphInstructions: Collection<Instruction>,
changed: BooleanArray, changed: BooleanArray,
isLocal: Boolean isLocal: Boolean
@@ -120,8 +121,8 @@ private fun <D> Pseudocode.collectDataFromSubgraph(
val previousInstructions = getPreviousIncludingSubGraphInstructions() val previousInstructions = getPreviousIncludingSubGraphInstructions()
fun updateEdgeDataForInstruction( fun updateEdgeDataForInstruction(
previousValue: Edges<D>?, previousValue: Edges<I>?,
newValue: Edges<D>? newValue: Edges<I>?
) { ) {
if (previousValue != newValue && newValue != null) { if (previousValue != newValue && newValue != null) {
changed[0] = true changed[0] = true
@@ -151,7 +152,7 @@ private fun <D> Pseudocode.collectDataFromSubgraph(
} }
val previousDataValue = edgesMap.get(instruction) val previousDataValue = edgesMap.get(instruction)
val incomingEdgesData = HashSet<D>() val incomingEdgesData = HashSet<I>()
for (previousInstruction in previousInstructions) { for (previousInstruction in previousInstructions) {
val previousData = edgesMap.get(previousInstruction) val previousData = edgesMap.get(previousInstruction)
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import java.util.ArrayList import java.util.ArrayList
import java.util.Collections
import java.util.HashMap import java.util.HashMap
public class PseudocodeVariableDataCollector( public class PseudocodeVariableDataCollector(
@@ -36,40 +35,38 @@ public class PseudocodeVariableDataCollector(
) { ) {
val lexicalScopeVariableInfo = computeLexicalScopeVariableInfo(pseudocode) val lexicalScopeVariableInfo = computeLexicalScopeVariableInfo(pseudocode)
@Suppress("UNCHECKED_CAST") public fun <I : ControlFlowInfo<*>> collectData(
public fun <D> collectData(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
mergeDataWithLocalDeclarations: Boolean, mergeDataWithLocalDeclarations: Boolean,
instructionDataMergeStrategy: InstructionDataMergeStrategy<D> initialInfo: I,
): MutableMap<Instruction, Edges<MutableMap<VariableDescriptor, D>>> { instructionDataMergeStrategy: (Instruction, Collection<I>) -> Edges<I>
val result = pseudocode.collectData( ): Map<Instruction, Edges<I>> {
return pseudocode.collectData(
traversalOrder, mergeDataWithLocalDeclarations, traversalOrder, mergeDataWithLocalDeclarations,
//see KT-4605 instructionDataMergeStrategy,
instructionDataMergeStrategy as { from, to, info -> filterOutVariablesOutOfScope(from, to, info) },
(Instruction, Collection<Map<VariableDescriptor, D>>) -> Edges<Map<VariableDescriptor, D>>, initialInfo
{ from, to, data -> filterOutVariablesOutOfScope(from, to, data)}, )
Collections.emptyMap<VariableDescriptor, D>())
//see KT-4605
return result as MutableMap<Instruction, Edges<MutableMap<VariableDescriptor, D>>>
} }
private fun <D> filterOutVariablesOutOfScope( private fun <I : ControlFlowInfo<*>> filterOutVariablesOutOfScope(
from: Instruction, from: Instruction,
to: Instruction, to: Instruction,
data: Map<VariableDescriptor, D> info: I
): Map<VariableDescriptor, D> { ): I {
// If an edge goes from deeper lexical scope to a less deep one, this means that it points outside of the deeper scope. // If an edge goes from deeper lexical scope to a less deep one, this means that it points outside of the deeper scope.
val toDepth = to.lexicalScope.depth val toDepth = to.lexicalScope.depth
if (toDepth >= from.lexicalScope.depth) return data if (toDepth >= from.lexicalScope.depth) return info
// Variables declared in an inner (deeper) scope can't be accessed from an outer scope. // Variables declared in an inner (deeper) scope can't be accessed from an outer scope.
// Thus they can be filtered out upon leaving the inner scope. // Thus they can be filtered out upon leaving the inner scope.
return data.filterKeys { variable -> @Suppress("UNCHECKED_CAST")
return info.copy().retainAll { variable ->
val lexicalScope = lexicalScopeVariableInfo.declaredIn[variable] val lexicalScope = lexicalScopeVariableInfo.declaredIn[variable]
// '-1' for variables declared outside this pseudocode // '-1' for variables declared outside this pseudocode
val depth = lexicalScope?.depth ?: -1 val depth = lexicalScope?.depth ?: -1
depth <= toDepth depth <= toDepth
} } as I
} }
fun computeLexicalScopeVariableInfo(pseudocode: Pseudocode): LexicalScopeVariableInfo { fun computeLexicalScopeVariableInfo(pseudocode: Pseudocode): LexicalScopeVariableInfo {
@@ -96,10 +93,6 @@ public class PseudocodeVariableDataCollector(
} }
} }
//todo may be a type alias
interface InstructionDataMergeStrategy<D> :
(Instruction, Collection<MutableMap<VariableDescriptor, D>>) -> Edges<MutableMap<VariableDescriptor, D>>
public interface LexicalScopeVariableInfo { public interface LexicalScopeVariableInfo {
val declaredIn : Map<VariableDescriptor, LexicalScope> val declaredIn : Map<VariableDescriptor, LexicalScope>
val scopeVariables : Map<LexicalScope, Collection<VariableDescriptor>> val scopeVariables : Map<LexicalScope, Collection<VariableDescriptor>>
@@ -21,10 +21,7 @@ import com.google.common.collect.Sets
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.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder
@@ -38,7 +35,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
private val declaredVariablesForDeclaration = Maps.newHashMap<Pseudocode, Set<VariableDescriptor>>() private val declaredVariablesForDeclaration = Maps.newHashMap<Pseudocode, Set<VariableDescriptor>>()
val variableInitializers: MutableMap<Instruction, Edges<MutableMap<VariableDescriptor, VariableControlFlowState>>> by lazy { val variableInitializers: Map<Instruction, Edges<InitControlFlowInfo>> by lazy {
computeVariableInitializers() computeVariableInitializers()
} }
@@ -64,7 +61,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
} }
private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode): Set<VariableDescriptor> { private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode): Set<VariableDescriptor> {
var declaredVariables: Set<VariableDescriptor>? = declaredVariablesForDeclaration[pseudocode] var declaredVariables = declaredVariablesForDeclaration[pseudocode]
if (declaredVariables == null) { if (declaredVariables == null) {
declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode) declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode)
declaredVariablesForDeclaration.put(pseudocode, declaredVariables) declaredVariablesForDeclaration.put(pseudocode, declaredVariables)
@@ -89,34 +86,29 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
// variable initializers // variable initializers
private fun computeVariableInitializers(): MutableMap<Instruction, Edges<MutableMap<VariableDescriptor, VariableControlFlowState>>> { private fun computeVariableInitializers(): Map<Instruction, Edges<InitControlFlowInfo>> {
val lexicalScopeVariableInfo = pseudocodeVariableDataCollector.lexicalScopeVariableInfo val lexicalScopeVariableInfo = pseudocodeVariableDataCollector.lexicalScopeVariableInfo
return pseudocodeVariableDataCollector.collectData( return pseudocodeVariableDataCollector.collectData(
TraversalOrder.FORWARD, /*mergeDataWithLocalDeclarations=*/ true, TraversalOrder.FORWARD, /*mergeDataWithLocalDeclarations=*/ true, InitControlFlowInfo()
object : InstructionDataMergeStrategy<VariableControlFlowState> { ) {
override operator fun invoke( instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> ->
instruction: Instruction,
incomingEdgesData: Collection<MutableMap<VariableDescriptor, VariableControlFlowState>>
): Edges<MutableMap<VariableDescriptor, VariableControlFlowState>> {
val enterInstructionData = mergeIncomingEdgesDataForInitializers(incomingEdgesData) val enterInstructionData = mergeIncomingEdgesDataForInitializers(incomingEdgesData)
val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny( val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
instruction, enterInstructionData, lexicalScopeVariableInfo) instruction, enterInstructionData, lexicalScopeVariableInfo)
return Edges(enterInstructionData, exitInstructionData) Edges(enterInstructionData, exitInstructionData)
} }
})
} }
private fun addVariableInitStateFromCurrentInstructionIfAny( private fun addVariableInitStateFromCurrentInstructionIfAny(
instruction: Instruction, instruction: Instruction,
enterInstructionData: MutableMap<VariableDescriptor, VariableControlFlowState>, enterInstructionData: InitControlFlowInfo,
lexicalScopeVariableInfo: LexicalScopeVariableInfo lexicalScopeVariableInfo: LexicalScopeVariableInfo): InitControlFlowInfo {
): MutableMap<VariableDescriptor, VariableControlFlowState> {
if (instruction is MagicInstruction) { if (instruction is MagicInstruction) {
if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) { if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
val exitInstructionData = Maps.newHashMap(enterInstructionData) val exitInstructionData = enterInstructionData.copy()
for (entry in enterInstructionData.entries) { for (entry in enterInstructionData.entries) {
if (!entry.value.definitelyInitialized()) { if (!entry.value.definitelyInitialized()) {
exitInstructionData.put(entry.key, exitInstructionData.put(entry.key,
@@ -130,7 +122,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
return enterInstructionData return enterInstructionData
} }
val variable = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false, bindingContext) ?: return enterInstructionData val variable = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false, bindingContext) ?: return enterInstructionData
val exitInstructionData = Maps.newHashMap(enterInstructionData) val exitInstructionData = enterInstructionData.copy()
if (instruction is WriteValueInstruction) { if (instruction is WriteValueInstruction) {
// if writing to already initialized object // if writing to already initialized object
if (!PseudocodeUtil.isThisOrNoDispatchReceiver(instruction, bindingContext)) { if (!PseudocodeUtil.isThisOrNoDispatchReceiver(instruction, bindingContext)) {
@@ -158,48 +150,43 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
// variable use // variable use
/*mergeDataWithLocalDeclarations=*///instruction instanceof WriteValueInstruction val variableUseStatusData: Map<Instruction, Edges<UseControlFlowInfo>>
val variableUseStatusData: MutableMap<Instruction, Edges<MutableMap<VariableDescriptor, VariableUseState>>>
get() = pseudocodeVariableDataCollector.collectData( get() = pseudocodeVariableDataCollector.collectData(
TraversalOrder.BACKWARD, true, TraversalOrder.BACKWARD, true, UseControlFlowInfo()
object : InstructionDataMergeStrategy<VariableUseState> { ) {
override operator fun invoke( instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
instruction: Instruction, val enterResult = UseControlFlowInfo()
incomingEdgesData: Collection<MutableMap<VariableDescriptor, VariableUseState>> for (edgeData in incomingEdgesData) {
): Edges<MutableMap<VariableDescriptor, VariableUseState>> { for (entry in edgeData.entries) {
val variableDescriptor = entry.key
val enterResult = Maps.newHashMap<VariableDescriptor, VariableUseState>() val variableUseState = entry.value
for (edgeData in incomingEdgesData) { enterResult.put(variableDescriptor, variableUseState.merge(enterResult[variableDescriptor]))
for (entry in edgeData.entries) { }
val variableDescriptor = entry.key }
val variableUseState = entry.value val variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, bindingContext)
enterResult.put(variableDescriptor, variableUseState.merge(enterResult[variableDescriptor])) if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
} Edges(enterResult, enterResult)
} }
val variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny( else {
instruction, true, bindingContext) val exitResult = enterResult.copy()
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) { if (instruction is ReadValueInstruction) {
return Edges(enterResult, enterResult) exitResult.put(variableDescriptor, VariableUseState.READ)
} }
val exitResult = Maps.newHashMap(enterResult) else {
if (instruction is ReadValueInstruction) { var variableUseState: VariableUseState? = enterResult[variableDescriptor]
exitResult.put(variableDescriptor, VariableUseState.READ) if (variableUseState == null) {
} variableUseState = VariableUseState.UNUSED
else {
var variableUseState: VariableUseState? = enterResult[variableDescriptor]
if (variableUseState == null) {
variableUseState = VariableUseState.UNUSED
}
when (variableUseState) {
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
exitResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
exitResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
}
}
return Edges(enterResult, exitResult)
} }
}) when (variableUseState) {
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
exitResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
exitResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
}
}
Edges(enterResult, exitResult)
}
}
companion object { companion object {
@@ -207,30 +194,32 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
fun getDefaultValueForInitializers( fun getDefaultValueForInitializers(
variable: VariableDescriptor, variable: VariableDescriptor,
instruction: Instruction, instruction: Instruction,
lexicalScopeVariableInfo: LexicalScopeVariableInfo): VariableControlFlowState { lexicalScopeVariableInfo: LexicalScopeVariableInfo
): VariableControlFlowState {
//todo: think of replacing it with "MapWithDefaultValue" //todo: think of replacing it with "MapWithDefaultValue"
val declaredIn = lexicalScopeVariableInfo.declaredIn[variable] val declaredIn = lexicalScopeVariableInfo.declaredIn[variable]
val declaredOutsideThisDeclaration = declaredIn == null //declared outside this pseudocode val declaredOutsideThisDeclaration =
|| declaredIn.lexicalScopeForContainingDeclaration != instruction.lexicalScope.lexicalScopeForContainingDeclaration declaredIn == null //declared outside this pseudocode
|| declaredIn.lexicalScopeForContainingDeclaration != instruction.lexicalScope.lexicalScopeForContainingDeclaration
return VariableControlFlowState.create(/*initState=*/declaredOutsideThisDeclaration) return VariableControlFlowState.create(/*initState=*/declaredOutsideThisDeclaration)
} }
private fun mergeIncomingEdgesDataForInitializers( private fun mergeIncomingEdgesDataForInitializers(
incomingEdgesData: Collection<MutableMap<VariableDescriptor, VariableControlFlowState>> incomingEdgesData: Collection<InitControlFlowInfo>
): MutableMap<VariableDescriptor, VariableControlFlowState> { ): InitControlFlowInfo {
val variablesInScope = Sets.newHashSet<VariableDescriptor>() val variablesInScope = Sets.newHashSet<VariableDescriptor>()
for (edgeData in incomingEdgesData) { for (edgeData in incomingEdgesData) {
variablesInScope.addAll(edgeData.keys) variablesInScope.addAll(edgeData.keys)
} }
val enterInstructionData = Maps.newHashMap<VariableDescriptor, VariableControlFlowState>() val enterInstructionData = InitControlFlowInfo()
for (variable in variablesInScope) { for (variable in variablesInScope) {
var initState: InitState? = null var initState: InitState? = null
var isDeclared = true var isDeclared = true
for (edgeData in incomingEdgesData) { for (edgeData in incomingEdgesData) {
val varControlFlowState = edgeData[variable] val varControlFlowState = edgeData[variable]
if (varControlFlowState != null) { if (varControlFlowState != null) {
initState = if (initState != null) initState.merge(varControlFlowState.initState) else varControlFlowState.initState initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState
if (!varControlFlowState.isDeclared) { if (!varControlFlowState.isDeclared) {
isDeclared = false isDeclared = false
} }
@@ -39,9 +39,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);
final Map<Instruction, Edges<Map<VariableDescriptor, VariableControlFlowState>>> variableInitializers = final Map<Instruction, Edges<InitControlFlowInfo>> variableInitializers =
pseudocodeVariablesData.getVariableInitializers(); pseudocodeVariablesData.getVariableInitializers();
final Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> useStatusData = final Map<Instruction, Edges<UseControlFlowInfo>> useStatusData =
pseudocodeVariablesData.getVariableUseStatusData(); pseudocodeVariablesData.getVariableUseStatusData();
final String initPrefix = " INIT:"; final String initPrefix = " INIT:";
final String usePrefix = " USE:"; final String usePrefix = " USE:";
@@ -51,16 +51,16 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest {
@Override @Override
public String invoke(Instruction instruction, Instruction next, Instruction prev) { public String invoke(Instruction instruction, Instruction next, Instruction prev) {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
Edges<Map<VariableDescriptor, VariableControlFlowState>> initializersEdges = variableInitializers.get(instruction); Edges<InitControlFlowInfo> initializersEdges = variableInitializers.get(instruction);
Edges<Map<VariableDescriptor, VariableControlFlowState>> previousInitializersEdges = variableInitializers.get(prev); Edges<InitControlFlowInfo> previousInitializersEdges = variableInitializers.get(prev);
String initializersData = ""; String initializersData = "";
if (initializersEdges != null && !initializersEdges.equals(previousInitializersEdges)) { if (initializersEdges != null && !initializersEdges.equals(previousInitializersEdges)) {
initializersData = dumpEdgesData(initPrefix, initializersEdges); initializersData = dumpEdgesData(initPrefix, initializersEdges);
} }
result.append(String.format("%1$-" + initializersColumnWidth + "s", initializersData)); result.append(String.format("%1$-" + initializersColumnWidth + "s", initializersData));
Edges<Map<VariableDescriptor, VariableUseState>> useStatusEdges = useStatusData.get(instruction); Edges<UseControlFlowInfo> useStatusEdges = useStatusData.get(instruction);
Edges<Map<VariableDescriptor, VariableUseState>> nextUseStatusEdges = useStatusData.get(next); Edges<UseControlFlowInfo> nextUseStatusEdges = useStatusData.get(next);
if (useStatusEdges != null && !useStatusEdges.equals(nextUseStatusEdges)) { if (useStatusEdges != null && !useStatusEdges.equals(nextUseStatusEdges)) {
result.append(dumpEdgesData(usePrefix, useStatusEdges)); result.append(dumpEdgesData(usePrefix, useStatusEdges));
} }
@@ -72,11 +72,11 @@ 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<Map<VariableDescriptor, VariableControlFlowState>>> data @NotNull Map<Instruction, Edges<InitControlFlowInfo>> data
) { ) {
int maxWidth = 0; int maxWidth = 0;
for (Instruction instruction : instructions) { for (Instruction instruction : instructions) {
Edges<Map<VariableDescriptor, VariableControlFlowState>> edges = data.get(instruction); Edges<InitControlFlowInfo> edges = data.get(instruction);
if (edges == null) continue; if (edges == null) continue;
int length = dumpEdgesData(prefix, edges).length(); int length = dumpEdgesData(prefix, edges).length();
if (maxWidth < length) { if (maxWidth < length) {
@@ -87,18 +87,18 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest {
} }
@NotNull @NotNull
private static <D> String dumpEdgesData(String prefix, @NotNull Edges<Map<VariableDescriptor, D>> edges) { private static <S, I extends ControlFlowInfo<S>> String dumpEdgesData(String prefix, @NotNull Edges<I> edges) {
return prefix + return prefix +
" in: " + renderVariableMap(edges.getIncoming()) + " in: " + renderVariableMap(edges.getIncoming()) +
" out: " + renderVariableMap(edges.getOutgoing()); " out: " + renderVariableMap(edges.getOutgoing());
} }
private static <D> String renderVariableMap(Map<VariableDescriptor, D> map) { private static <S> String renderVariableMap(Map<VariableDescriptor, S> map) {
List<String> result = Lists.newArrayList(); List<String> result = Lists.newArrayList();
for (Map.Entry<VariableDescriptor, D> entry : map.entrySet()) { for (Map.Entry<VariableDescriptor, S> entry : map.entrySet()) {
VariableDescriptor variable = entry.getKey(); VariableDescriptor variable = entry.getKey();
D data = entry.getValue(); S state = entry.getValue();
result.add(variable.getName() + "=" + data); result.add(variable.getName() + "=" + state);
} }
Collections.sort(result); Collections.sort(result);
return "{" + StringUtil.join(result, ", ") + "}"; return "{" + StringUtil.join(result, ", ") + "}";