ControlFlowInfo introduced to store variable states, related refactoring
This commit is contained in:
committed by
Mikhail Glukhikh
parent
0f3997c6ca
commit
52c3fb03a2
@@ -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, ", ") + "}";
|
||||||
|
|||||||
Reference in New Issue
Block a user