diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/dce/K2JSDce.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/dce/K2JSDce.kt index 109945611c3..b43423987a2 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/dce/K2JSDce.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/dce/K2JSDce.kt @@ -78,11 +78,10 @@ class K2JSDce : CLITool() { val dceResult = DeadCodeElimination.run(files, includedDeclarations, logConsumer) if (dceResult.status == DeadCodeEliminationStatus.FAILED) return ExitCode.COMPILATION_ERROR - val nodes = dceResult.reachableNodes.filterTo(mutableSetOf()) { it.reachable } val reachabilitySeverity = if (arguments.printReachabilityInfo) CompilerMessageSeverity.INFO else CompilerMessageSeverity.LOGGING messageCollector.report(reachabilitySeverity, "") - for (node in nodes.extractRoots()) { + for (node in dceResult.reachableNodes.extractReachableRoots(dceResult.context!!)) { printTree( node, { messageCollector.report(reachabilitySeverity, it) }, printNestedMembers = false, showLocations = true diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt index 4fcec576d7b..43b9961f147 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Context.kt @@ -126,6 +126,14 @@ class Context { } } + private var currentColor = 1.toByte() + + fun clearVisited() { + currentColor++ + } + + fun visit(n: Node) = n.visit(currentColor) + class Node private constructor(val localName: JsName?, qualifier: Qualifier?) { private var _dependenciesImpl: MutableSet? = null private var _expressionsImpl: MutableSet? = null @@ -178,6 +186,14 @@ class Context { var qualifier: Qualifier? = qualifier private set + private var color: Byte = 0 + + fun visit(c: Byte): Boolean { + val result = color != c + color = c + return result + } + val memberNames: Set get() = original._membersImpl?.keys ?: emptySet() constructor(localName: JsName? = null) : this(localName, null) diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt index 3e1c56a526d..c47c0509760 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeElimination.kt @@ -42,11 +42,14 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit val moduleMapping = mutableMapOf() private val reachableNames = mutableSetOf() - var reachableNodes = setOf() + var reachableNodes: Iterable = setOf() private set + var context: Context? = null + fun apply(root: JsNode) { val context = Context() + this.context = context val topLevelVars = collectDefinedNames(root) context.addNodesForLocalVars(topLevelVars) @@ -107,7 +110,7 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit block } - if (hasErrors) return DeadCodeEliminationResult(emptySet(), DeadCodeEliminationStatus.FAILED) + if (hasErrors) return DeadCodeEliminationResult(dce.context, emptySet(), DeadCodeEliminationStatus.FAILED) program.globalBlock.statements += blocks program.globalBlock.fixForwardNameReferences() @@ -139,7 +142,7 @@ class DeadCodeElimination(private val logConsumer: (DCELogLevel, String) -> Unit } } - return DeadCodeEliminationResult(dce.reachableNodes, DeadCodeEliminationStatus.OK) + return DeadCodeEliminationResult(dce.context, dce.reachableNodes, DeadCodeEliminationStatus.OK) } private class Reporter(private val fileName: String, private val logConsumer: (DCELogLevel, String) -> Unit) : ErrorReporter { diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeEliminationResult.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeEliminationResult.kt index 8c64569acf1..335538e3371 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeEliminationResult.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/DeadCodeEliminationResult.kt @@ -16,4 +16,4 @@ package org.jetbrains.kotlin.js.dce -class DeadCodeEliminationResult(val reachableNodes: Set, val status: DeadCodeEliminationStatus) \ No newline at end of file +class DeadCodeEliminationResult(val context: Context?, val reachableNodes: Iterable, val status: DeadCodeEliminationStatus) \ No newline at end of file diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt index 4fd0081d74e..4f062f6b1c7 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt @@ -29,11 +29,15 @@ class ReachabilityTracker( private val CALL_FUNCTIONS = setOf("call", "apply") } + init { + context.clearVisited() + } + private var currentNodeWithLocation: JsNode? = null private var depth = 0 - private val reachableNodesImpl = mutableSetOf() + private val reachableNodesImpl = mutableListOf() - val reachableNodes: Set get() = reachableNodesImpl + val reachableNodes: Iterable get() = reachableNodesImpl override fun visit(x: JsVars.JsVar) { if (shouldTraverse(x)) { @@ -137,7 +141,9 @@ class ReachabilityTracker( fun reach(node: Node) { if (node.reachable) return node.reachable = true - reachableNodesImpl += node + if (context.visit(node)) { + reachableNodesImpl += node + } reachDeclaration(node) @@ -192,7 +198,9 @@ class ReachabilityTracker( } else if (!node.declarationReachable) { node.declarationReachable = true - reachableNodesImpl += node + if (context.visit(node)) { + reachableNodesImpl += node + } node.original.qualifier?.parent?.let { reportAndNest("reach-decl: parent $it", null) { diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt index f984ad4f411..abb3c6e9545 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt @@ -73,20 +73,21 @@ fun JsLocation.asString(): String { return "$simpleFileName:${startLine + 1}" } -fun Set.extractRoots(): Set { - val result = mutableSetOf() - val visited = mutableSetOf() - forEach { it.original.extractRootsImpl(result, visited) } +fun Iterable.extractReachableRoots(context: Context): Iterable { + context.clearVisited() + + val result = mutableListOf() + forEach { if (it.reachable) it.original.extractRootsImpl(result, context) } return result } -private fun Node.extractRootsImpl(target: MutableSet, visited: MutableSet) { - if (!visited.add(original)) return +private fun Node.extractRootsImpl(target: MutableList, context: Context) { + if (!context.visit(original)) return val qualifier = original.qualifier if (qualifier == null) { target += original } else { - qualifier.parent.extractRootsImpl(target, visited) + qualifier.parent.extractRootsImpl(target, context) } }