From 52c3fb03a2a2e1d9780289d033296adeab72a5bb Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 25 Dec 2015 13:57:58 +0300 Subject: [PATCH] ControlFlowInfo introduced to store variable states, related refactoring --- .../jetbrains/kotlin/cfg/ControlFlowInfo.kt | 29 ++++ .../cfg/ControlFlowInformationProvider.java | 12 +- .../kotlin/cfg/PseudocodeTraverser.kt | 41 +++--- .../cfg/PseudocodeVariableDataCollector.kt | 39 +++--- .../kotlin/cfg/PseudocodeVariablesData.kt | 131 ++++++++---------- .../kotlin/cfg/AbstractDataFlowTest.java | 26 ++-- 6 files changed, 144 insertions(+), 134 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt index 24a8ef89eee..c62f8934ced 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt @@ -16,6 +16,35 @@ package org.jetbrains.kotlin.cfg +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import java.util.* + +open class ControlFlowInfo internal constructor(protected val map: MutableMap = hashMapOf()) : + MutableMap by map { + open fun copy() = ControlFlowInfo(HashMap(map)) + + fun retainAll(predicate: (VariableDescriptor) -> Boolean): ControlFlowInfo { + 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 = hashMapOf()) : + ControlFlowInfo(map) { + override fun copy() = InitControlFlowInfo(HashMap(map)) +} + +class UseControlFlowInfo(map: MutableMap = hashMapOf()) : + ControlFlowInfo(map) { + override fun copy() = UseControlFlowInfo(HashMap(map)) +} + enum class InitState(private val s: String) { // Definitely initialized INITIALIZED("I"), diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java index 19419e83a51..1c66769db9e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java @@ -301,7 +301,7 @@ public class ControlFlowInformationProvider { final boolean processClassOrObject = subroutine instanceof KtClassOrObject; PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData(); - Map>> initializers = + Map> initializers = pseudocodeVariablesData.getVariableInitializers(); final Set declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, true); final LexicalScopeVariableInfo lexicalScopeVariableInfo = pseudocodeVariablesData.getLexicalScopeVariableInfo(); @@ -350,8 +350,7 @@ public class ControlFlowInformationProvider { public void recordInitializedVariables() { PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData(); Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode(); - Map>> initializers = - pseudocodeVariablesData.getVariableInitializers(); + Map> initializers = pseudocodeVariablesData.getVariableInitializers(); recordInitializedVariables(pseudocode, initializers); for (LocalFunctionDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) { recordInitializedVariables(instruction.getBody(), initializers); @@ -517,9 +516,9 @@ public class ControlFlowInformationProvider { private void recordInitializedVariables( @NotNull Pseudocode pseudocode, - @NotNull Map>> initializersMap + @NotNull Map> initializersMap ) { - Edges> initializers = initializersMap.get(pseudocode.getExitInstruction()); + Edges initializers = initializersMap.get(pseudocode.getExitInstruction()); if (initializers == null) return; Set declaredVariables = getPseudocodeVariablesData().getDeclaredVariables(pseudocode, false); for (VariableDescriptor variable : declaredVariables) { @@ -536,8 +535,7 @@ public class ControlFlowInformationProvider { public void markUnusedVariables() { final PseudocodeVariablesData pseudocodeVariablesData = getPseudocodeVariablesData(); - Map>> variableStatusData = - pseudocodeVariablesData.getVariableUseStatusData(); + Map> variableStatusData = pseudocodeVariablesData.getVariableUseStatusData(); final Map> reportedDiagnosticMap = Maps.newHashMap(); InstructionDataAnalyzeStrategy> variableStatusAnalyzeStrategy = new InstructionDataAnalyzeStrategy>() { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt index db2dd99e2c8..474cd6a7d84 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeTraverser.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.cfg.pseudocodeTraverser +import org.jetbrains.kotlin.cfg.ControlFlowInfo import org.jetbrains.kotlin.cfg.pseudocode.* import java.util.* import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD @@ -54,16 +55,16 @@ public fun Pseudocode.traverse( } } -public fun Pseudocode.collectData( +public fun > Pseudocode.collectData( traversalOrder: TraversalOrder, mergeDataWithLocalDeclarations: Boolean, - mergeEdges: (Instruction, Collection) -> Edges, - updateEdge: (Instruction, Instruction, D) -> D, - initialDataValue: D -): Map> { - val edgesMap = LinkedHashMap>() - initializeEdgesMap(edgesMap, initialDataValue) - edgesMap.put(getStartInstruction(traversalOrder), Edges(initialDataValue, initialDataValue)) + mergeEdges: (Instruction, Collection) -> Edges, + updateEdge: (Instruction, Instruction, I) -> I, + initialInfo: I +): Map> { + val edgesMap = LinkedHashMap>() + initializeEdgesMap(edgesMap, initialInfo) + edgesMap.put(getStartInstruction(traversalOrder), Edges(initialInfo, initialInfo)) val changed = BooleanArray(1) changed[0] = true @@ -76,26 +77,26 @@ public fun Pseudocode.collectData( return edgesMap } -private fun Pseudocode.initializeEdgesMap( - edgesMap: MutableMap>, - initialDataValue: D +private fun Pseudocode.initializeEdgesMap( + edgesMap: MutableMap>, + initialInfo: I ) { val instructions = getInstructions() - val initialEdge = Edges(initialDataValue, initialDataValue) + val initialEdge = Edges(initialInfo, initialInfo) for (instruction in instructions) { edgesMap.put(instruction, initialEdge) if (instruction is LocalFunctionDeclarationInstruction) { - instruction.body.initializeEdgesMap(edgesMap, initialDataValue) + instruction.body.initializeEdgesMap(edgesMap, initialInfo) } } } -private fun Pseudocode.collectDataFromSubgraph( +private fun > Pseudocode.collectDataFromSubgraph( traversalOrder: TraversalOrder, mergeDataWithLocalDeclarations: Boolean, - edgesMap: MutableMap>, - mergeEdges: (Instruction, Collection) -> Edges, - updateEdge: (Instruction, Instruction, D) -> D, + edgesMap: MutableMap>, + mergeEdges: (Instruction, Collection) -> Edges, + updateEdge: (Instruction, Instruction, I) -> I, previousSubGraphInstructions: Collection, changed: BooleanArray, isLocal: Boolean @@ -120,8 +121,8 @@ private fun Pseudocode.collectDataFromSubgraph( val previousInstructions = getPreviousIncludingSubGraphInstructions() fun updateEdgeDataForInstruction( - previousValue: Edges?, - newValue: Edges? + previousValue: Edges?, + newValue: Edges? ) { if (previousValue != newValue && newValue != null) { changed[0] = true @@ -151,7 +152,7 @@ private fun Pseudocode.collectDataFromSubgraph( } val previousDataValue = edgesMap.get(instruction) - val incomingEdgesData = HashSet() + val incomingEdgesData = HashSet() for (previousInstruction in previousInstructions) { val previousData = edgesMap.get(previousInstruction) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt index 6f9a0841927..43045672f65 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.resolve.BindingContext import java.util.ArrayList -import java.util.Collections import java.util.HashMap public class PseudocodeVariableDataCollector( @@ -36,40 +35,38 @@ public class PseudocodeVariableDataCollector( ) { val lexicalScopeVariableInfo = computeLexicalScopeVariableInfo(pseudocode) - @Suppress("UNCHECKED_CAST") - public fun collectData( + public fun > collectData( traversalOrder: TraversalOrder, mergeDataWithLocalDeclarations: Boolean, - instructionDataMergeStrategy: InstructionDataMergeStrategy - ): MutableMap>> { - val result = pseudocode.collectData( + initialInfo: I, + instructionDataMergeStrategy: (Instruction, Collection) -> Edges + ): Map> { + return pseudocode.collectData( traversalOrder, mergeDataWithLocalDeclarations, - //see KT-4605 - instructionDataMergeStrategy as - (Instruction, Collection>) -> Edges>, - { from, to, data -> filterOutVariablesOutOfScope(from, to, data)}, - Collections.emptyMap()) - //see KT-4605 - return result as MutableMap>> + instructionDataMergeStrategy, + { from, to, info -> filterOutVariablesOutOfScope(from, to, info) }, + initialInfo + ) } - private fun filterOutVariablesOutOfScope( + private fun > filterOutVariablesOutOfScope( from: Instruction, to: Instruction, - data: Map - ): Map { + info: I + ): I { // 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 - 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. // 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] // '-1' for variables declared outside this pseudocode val depth = lexicalScope?.depth ?: -1 depth <= toDepth - } + } as I } fun computeLexicalScopeVariableInfo(pseudocode: Pseudocode): LexicalScopeVariableInfo { @@ -96,10 +93,6 @@ public class PseudocodeVariableDataCollector( } } -//todo may be a type alias -interface InstructionDataMergeStrategy : - (Instruction, Collection>) -> Edges> - public interface LexicalScopeVariableInfo { val declaredIn : Map val scopeVariables : Map> diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt index b11eec20b57..3167a0ce7bd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt @@ -21,10 +21,7 @@ import com.google.common.collect.Sets import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil 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.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.eval.* import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder @@ -38,7 +35,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon private val declaredVariablesForDeclaration = Maps.newHashMap>() - val variableInitializers: MutableMap>> by lazy { + val variableInitializers: Map> by lazy { computeVariableInitializers() } @@ -64,7 +61,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon } private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode): Set { - var declaredVariables: Set? = declaredVariablesForDeclaration[pseudocode] + var declaredVariables = declaredVariablesForDeclaration[pseudocode] if (declaredVariables == null) { declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode) declaredVariablesForDeclaration.put(pseudocode, declaredVariables) @@ -89,34 +86,29 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon // variable initializers - private fun computeVariableInitializers(): MutableMap>> { + private fun computeVariableInitializers(): Map> { val lexicalScopeVariableInfo = pseudocodeVariableDataCollector.lexicalScopeVariableInfo return pseudocodeVariableDataCollector.collectData( - TraversalOrder.FORWARD, /*mergeDataWithLocalDeclarations=*/ true, - object : InstructionDataMergeStrategy { - override operator fun invoke( - instruction: Instruction, - incomingEdgesData: Collection> - ): Edges> { + TraversalOrder.FORWARD, /*mergeDataWithLocalDeclarations=*/ true, InitControlFlowInfo() + ) { + instruction: Instruction, incomingEdgesData: Collection -> - val enterInstructionData = mergeIncomingEdgesDataForInitializers(incomingEdgesData) - val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny( - instruction, enterInstructionData, lexicalScopeVariableInfo) - return Edges(enterInstructionData, exitInstructionData) - } - }) + val enterInstructionData = mergeIncomingEdgesDataForInitializers(incomingEdgesData) + val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny( + instruction, enterInstructionData, lexicalScopeVariableInfo) + Edges(enterInstructionData, exitInstructionData) + } } private fun addVariableInitStateFromCurrentInstructionIfAny( instruction: Instruction, - enterInstructionData: MutableMap, - lexicalScopeVariableInfo: LexicalScopeVariableInfo - ): MutableMap { + enterInstructionData: InitControlFlowInfo, + lexicalScopeVariableInfo: LexicalScopeVariableInfo): InitControlFlowInfo { if (instruction is MagicInstruction) { if (instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) { - val exitInstructionData = Maps.newHashMap(enterInstructionData) + val exitInstructionData = enterInstructionData.copy() for (entry in enterInstructionData.entries) { if (!entry.value.definitelyInitialized()) { exitInstructionData.put(entry.key, @@ -130,7 +122,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon 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 writing to already initialized object if (!PseudocodeUtil.isThisOrNoDispatchReceiver(instruction, bindingContext)) { @@ -158,48 +150,43 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon // variable use - /*mergeDataWithLocalDeclarations=*///instruction instanceof WriteValueInstruction - val variableUseStatusData: MutableMap>> + val variableUseStatusData: Map> get() = pseudocodeVariableDataCollector.collectData( - TraversalOrder.BACKWARD, true, - object : InstructionDataMergeStrategy { - override operator fun invoke( - instruction: Instruction, - incomingEdgesData: Collection> - ): Edges> { - - val enterResult = Maps.newHashMap() - for (edgeData in incomingEdgesData) { - for (entry in edgeData.entries) { - val variableDescriptor = entry.key - val variableUseState = entry.value - enterResult.put(variableDescriptor, variableUseState.merge(enterResult[variableDescriptor])) - } - } - val variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny( - instruction, true, bindingContext) - if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) { - return Edges(enterResult, enterResult) - } - val exitResult = Maps.newHashMap(enterResult) - if (instruction is ReadValueInstruction) { - exitResult.put(variableDescriptor, VariableUseState.READ) - } - 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) + TraversalOrder.BACKWARD, true, UseControlFlowInfo() + ) { + instruction: Instruction, incomingEdgesData: Collection -> + val enterResult = UseControlFlowInfo() + for (edgeData in incomingEdgesData) { + for (entry in edgeData.entries) { + val variableDescriptor = entry.key + val variableUseState = entry.value + enterResult.put(variableDescriptor, variableUseState.merge(enterResult[variableDescriptor])) + } + } + val variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true, bindingContext) + if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) { + Edges(enterResult, enterResult) + } + else { + val exitResult = enterResult.copy() + if (instruction is ReadValueInstruction) { + exitResult.put(variableDescriptor, VariableUseState.READ) + } + 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) + } + } + Edges(enterResult, exitResult) + } + } companion object { @@ -207,30 +194,32 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon fun getDefaultValueForInitializers( variable: VariableDescriptor, instruction: Instruction, - lexicalScopeVariableInfo: LexicalScopeVariableInfo): VariableControlFlowState { + lexicalScopeVariableInfo: LexicalScopeVariableInfo + ): VariableControlFlowState { //todo: think of replacing it with "MapWithDefaultValue" val declaredIn = lexicalScopeVariableInfo.declaredIn[variable] - val declaredOutsideThisDeclaration = declaredIn == null //declared outside this pseudocode - || declaredIn.lexicalScopeForContainingDeclaration != instruction.lexicalScope.lexicalScopeForContainingDeclaration + val declaredOutsideThisDeclaration = + declaredIn == null //declared outside this pseudocode + || declaredIn.lexicalScopeForContainingDeclaration != instruction.lexicalScope.lexicalScopeForContainingDeclaration return VariableControlFlowState.create(/*initState=*/declaredOutsideThisDeclaration) } private fun mergeIncomingEdgesDataForInitializers( - incomingEdgesData: Collection> - ): MutableMap { + incomingEdgesData: Collection + ): InitControlFlowInfo { val variablesInScope = Sets.newHashSet() for (edgeData in incomingEdgesData) { variablesInScope.addAll(edgeData.keys) } - val enterInstructionData = Maps.newHashMap() + val enterInstructionData = InitControlFlowInfo() for (variable in variablesInScope) { var initState: InitState? = null var isDeclared = true for (edgeData in incomingEdgesData) { val varControlFlowState = edgeData[variable] if (varControlFlowState != null) { - initState = if (initState != null) initState.merge(varControlFlowState.initState) else varControlFlowState.initState + initState = initState?.merge(varControlFlowState.initState) ?: varControlFlowState.initState if (!varControlFlowState.isDeclared) { isDeclared = false } diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java b/compiler/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java index f3e561bb530..1f8e9874f56 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java @@ -39,9 +39,9 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest { @NotNull BindingContext bindingContext ) { PseudocodeVariablesData pseudocodeVariablesData = new PseudocodeVariablesData(pseudocode.getRootPseudocode(), bindingContext); - final Map>> variableInitializers = + final Map> variableInitializers = pseudocodeVariablesData.getVariableInitializers(); - final Map>> useStatusData = + final Map> useStatusData = pseudocodeVariablesData.getVariableUseStatusData(); final String initPrefix = " INIT:"; final String usePrefix = " USE:"; @@ -51,16 +51,16 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest { @Override public String invoke(Instruction instruction, Instruction next, Instruction prev) { StringBuilder result = new StringBuilder(); - Edges> initializersEdges = variableInitializers.get(instruction); - Edges> previousInitializersEdges = variableInitializers.get(prev); + Edges initializersEdges = variableInitializers.get(instruction); + Edges previousInitializersEdges = variableInitializers.get(prev); String initializersData = ""; if (initializersEdges != null && !initializersEdges.equals(previousInitializersEdges)) { initializersData = dumpEdgesData(initPrefix, initializersEdges); } result.append(String.format("%1$-" + initializersColumnWidth + "s", initializersData)); - Edges> useStatusEdges = useStatusData.get(instruction); - Edges> nextUseStatusEdges = useStatusData.get(next); + Edges useStatusEdges = useStatusData.get(instruction); + Edges nextUseStatusEdges = useStatusData.get(next); if (useStatusEdges != null && !useStatusEdges.equals(nextUseStatusEdges)) { result.append(dumpEdgesData(usePrefix, useStatusEdges)); } @@ -72,11 +72,11 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest { private static int countDataColumnWidth( @NotNull String prefix, @NotNull List instructions, - @NotNull Map>> data + @NotNull Map> data ) { int maxWidth = 0; for (Instruction instruction : instructions) { - Edges> edges = data.get(instruction); + Edges edges = data.get(instruction); if (edges == null) continue; int length = dumpEdgesData(prefix, edges).length(); if (maxWidth < length) { @@ -87,18 +87,18 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest { } @NotNull - private static String dumpEdgesData(String prefix, @NotNull Edges> edges) { + private static > String dumpEdgesData(String prefix, @NotNull Edges edges) { return prefix + " in: " + renderVariableMap(edges.getIncoming()) + " out: " + renderVariableMap(edges.getOutgoing()); } - private static String renderVariableMap(Map map) { + private static String renderVariableMap(Map map) { List result = Lists.newArrayList(); - for (Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { VariableDescriptor variable = entry.getKey(); - D data = entry.getValue(); - result.add(variable.getName() + "=" + data); + S state = entry.getValue(); + result.add(variable.getName() + "=" + state); } Collections.sort(result); return "{" + StringUtil.join(result, ", ") + "}";