From 4b79269cecf20faaec41ea41ad0a359adafab8ac Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 20 Sep 2017 15:39:49 +0300 Subject: [PATCH] Extract ReadOnly*ControlFlowInfo interfaces from *ControlFlowInfo It's necessary for further optimizations: not for all cases we actually need to build a map The naming is still a subject for discussion --- .../jetbrains/kotlin/cfg/ControlFlowInfo.kt | 25 ++++++++++++++----- .../cfg/ControlFlowInformationProvider.kt | 16 ++++++------ .../kotlin/cfg/PseudocodeVariablesData.kt | 4 +-- .../kotlin/cfg/AbstractDataFlowTest.java | 22 ++++++++-------- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt index b894cf2dfb8..7e129c3e3a4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInfo.kt @@ -22,10 +22,10 @@ import org.jetbrains.kotlin.descriptors.VariableDescriptor typealias ImmutableMap = javaslang.collection.Map typealias ImmutableHashMap = javaslang.collection.HashMap -abstract class ControlFlowInfo, D> +abstract class ControlFlowInfo, D : Any> internal constructor( protected val map: ImmutableMap = ImmutableHashMap.empty() -) : ImmutableMap by map { +) : ImmutableMap by map, ReadOnlyControlFlowInfo { abstract protected fun copy(newMap: ImmutableMap): S override fun put(key: VariableDescriptor, value: D): S = put(key, value, this[key].getOrElse(null as D?)) @@ -41,6 +41,9 @@ internal constructor( return copy(map.put(key, value)) } + override fun getOrNull(variableDescriptor: VariableDescriptor): D? = this[variableDescriptor].getOrElse(null as D?) + override fun asMap() = this + fun retainAll(predicate: (VariableDescriptor) -> Boolean): S = copy(map.removeAll(map.keySet().filterNot(predicate))) override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*, *>)?.map @@ -53,16 +56,26 @@ internal constructor( operator fun Tuple2.component1(): T = _1() operator fun Tuple2<*, T>.component2(): T = _2() -fun ImmutableMap.getOrNull(k: K): V? = this[k].getOrElse(null as V?) +interface ReadOnlyControlFlowInfo { + fun getOrNull(variableDescriptor: VariableDescriptor): D? + // Only used in tests + fun asMap(): ImmutableMap +} + +interface ReadOnlyInitControlFlowInfo : ReadOnlyControlFlowInfo { + fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean +} + +typealias ReadOnlyUseControlFlowInfo = ReadOnlyControlFlowInfo class InitControlFlowInfo(map: ImmutableMap = ImmutableHashMap.empty()) : - ControlFlowInfo(map) { + ControlFlowInfo(map), ReadOnlyInitControlFlowInfo { override fun copy(newMap: ImmutableMap) = InitControlFlowInfo(newMap) // this = output of EXHAUSTIVE_WHEN_ELSE instruction // merge = input of MergeInstruction // returns true if definite initialization in when happens here - fun checkDefiniteInitializationInWhen(merge: InitControlFlowInfo): Boolean { + override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean { for ((key, value) in iterator()) { if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY && merge.getOrNull(key)?.initState == InitState.INITIALIZED) { @@ -74,7 +87,7 @@ class InitControlFlowInfo(map: ImmutableMap = ImmutableHashMap.empty()) : - ControlFlowInfo(map) { + ControlFlowInfo(map), ReadOnlyUseControlFlowInfo { override fun copy(newMap: ImmutableMap) = UseControlFlowInfo(newMap) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt index 43c0516c842..01a51c363b0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt @@ -274,8 +274,8 @@ class ControlFlowInformationProvider private constructor( pseudocode.traverse(TraversalOrder.FORWARD, initializers) { instruction: Instruction, - enterData: ImmutableMap, - exitData: ImmutableMap -> + enterData: ReadOnlyInitControlFlowInfo, + exitData: ReadOnlyInitControlFlowInfo -> val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo) if (ctxt.variableDescriptor == null) return@traverse @@ -532,7 +532,7 @@ class ControlFlowInformationProvider private constructor( private fun recordInitializedVariables( pseudocode: Pseudocode, - initializersMap: Map> + initializersMap: Map> ) { val initializers = initializersMap[pseudocode.exitInstruction] ?: return val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false) @@ -554,8 +554,8 @@ class ControlFlowInformationProvider private constructor( val usedValueExpressions = hashSetOf() pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) { instruction: Instruction, - enterData: ImmutableMap, - _: ImmutableMap -> + enterData: ReadOnlyUseControlFlowInfo, + _: ReadOnlyUseControlFlowInfo -> val ctxt = VariableUseContext(instruction, reportedDiagnosticMap) val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false) @@ -1030,8 +1030,8 @@ class ControlFlowInformationProvider private constructor( private inner class VariableInitContext( instruction: Instruction, map: MutableMap>, - `in`: ImmutableMap, - out: ImmutableMap, + `in`: ReadOnlyInitControlFlowInfo, + out: ReadOnlyInitControlFlowInfo, blockScopeVariableInfo: BlockScopeVariableInfo ) : VariableContext(instruction, map) { internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`) @@ -1040,7 +1040,7 @@ class ControlFlowInformationProvider private constructor( private fun initialize( variableDescriptor: VariableDescriptor?, blockScopeVariableInfo: BlockScopeVariableInfo, - map: ImmutableMap + map: ReadOnlyInitControlFlowInfo ): VariableControlFlowState? { val state = map.getOrNull(variableDescriptor ?: return null) if (state != null) return state diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt index 03765764cfa..16a570addb6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt @@ -37,7 +37,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon private val declaredVariablesForDeclaration = hashMapOf>() - val variableInitializers: Map> by lazy { + val variableInitializers: Map> by lazy { computeVariableInitializers() } @@ -143,7 +143,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon // variable use - val variableUseStatusData: Map> + val variableUseStatusData: Map> get() = pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) { instruction: Instruction, incomingEdgesData: Collection -> diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java b/compiler/tests/org/jetbrains/kotlin/cfg/AbstractDataFlowTest.java index 949087bd872..83e808752b8 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); - Map> variableInitializers = + Map> variableInitializers = pseudocodeVariablesData.getVariableInitializers(); - Map> useStatusData = + Map>> useStatusData = pseudocodeVariablesData.getVariableUseStatusData(); String initPrefix = " INIT:"; String usePrefix = " USE:"; @@ -49,16 +49,16 @@ public abstract class AbstractDataFlowTest extends AbstractPseudocodeTest { dumpInstructions(pseudocode, out, (instruction, next, 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)); } @@ -69,11 +69,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) { @@ -85,10 +85,10 @@ 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()); + " in: " + renderVariableMap(edges.getIncoming().asMap()) + + " out: " + renderVariableMap(edges.getOutgoing().asMap()); } private static String renderVariableMap(javaslang.collection.Map map) {