diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt index aaf531ab685..bedeee8ef0b 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt @@ -70,7 +70,7 @@ class Analyzer(private val context: Context) : JsVisitor() { } is JsFunction -> expression.name?.let { context.nodes[it]?.original }?.let { nodeMap[x] = it - it.functions += expression + it.addFunction(expression) } is JsInvocation -> { val function = expression.qualifier @@ -161,8 +161,8 @@ class Analyzer(private val context: Context) : JsVisitor() { } val dependencyNodes = dependencies.expressions - .map { it as? JsStringLiteral ?: return } - .map { if (it.value == "exports") context.currentModule else context.globalScope.member(it.value) } + .map { it as? JsStringLiteral ?: return } + .map { if (it.value == "exports") context.currentModule else context.globalScope.member(it.value) } enterFunctionWithGivenNodes(function, dependencyNodes) astNodesToSkip += invocation.qualifier @@ -237,19 +237,23 @@ class Analyzer(private val context: Context) : JsVisitor() { // - wrapFunction(function() { ... }) if (context.isDefineInlineFunction(function) && rhs.arguments.size == 2) { tryExtractFunction(rhs.arguments[1])?.let { (inlineableFunction, additionalDeps) -> - leftNode.functions += inlineableFunction + leftNode.addFunction(inlineableFunction) val defineInlineFunctionNode = context.extractNode(function) if (defineInlineFunctionNode != null) { - leftNode.dependencies += defineInlineFunctionNode + leftNode.addDependency(defineInlineFunctionNode) + } + additionalDeps.forEach { + leftNode.addDependency(it) } - leftNode.dependencies += additionalDeps return leftNode } } tryExtractFunction(rhs)?.let { (functionBody, additionalDeps) -> - leftNode.functions += functionBody - leftNode.dependencies += additionalDeps + leftNode.addFunction(functionBody) + additionalDeps.forEach { + leftNode.addDependency(it) + } return leftNode } } @@ -271,14 +275,14 @@ class Analyzer(private val context: Context) : JsVisitor() { rhs is JsFunction -> { // lhs = function() { ... } // During reachability tracking phase: eliminate it if lhs is unreachable, traverse function otherwise - leftNode.functions += rhs + leftNode.addFunction(rhs) return leftNode } leftNode.qualifier?.memberName == Namer.METADATA -> { // lhs.$metadata$ = expression // During reachability tracking phase: eliminate it if lhs is unreachable, traverse expression // It's commonly used to supply class's metadata - leftNode.expressions += rhs + leftNode.addExpression(rhs) return leftNode } rhs is JsObjectLiteral && rhs.propertyInitializers.isEmpty() -> return leftNode @@ -323,8 +327,8 @@ class Analyzer(private val context: Context) : JsVisitor() { if (arg == null) return val prototypeNode = context.extractNode(arg) ?: return - target.dependencies += prototypeNode.original - target.expressions += arg + target.addDependency(prototypeNode.original) + target.addExpression(arg) } // Handle typeof foo === 'undefined' ? {} : foo, where foo is FQN 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 d21b038b623..4fcec576d7b 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 @@ -127,21 +127,35 @@ class Context { } class Node private constructor(val localName: JsName?, qualifier: Qualifier?) { - private val dependenciesImpl = mutableSetOf() - private val expressionsImpl = mutableSetOf() - private val functionsImpl = mutableSetOf() + private var _dependenciesImpl: MutableSet? = null + private var _expressionsImpl: MutableSet? = null + private var _functionsImpl: MutableSet? = null + private var _membersImpl: MutableMap? = null + private var _usedByAstNodesImpl: MutableSet? = null + + private val dependenciesImpl: MutableSet + get() = _dependenciesImpl ?: mutableSetOf().also { _dependenciesImpl = it } + private val expressionsImpl: MutableSet + get() = _expressionsImpl ?: mutableSetOf().also { _expressionsImpl = it } + private val functionsImpl: MutableSet + get() = _functionsImpl ?: mutableSetOf().also { _functionsImpl = it } + private val membersImpl: MutableMap + get() = _membersImpl ?: mutableMapOf().also { _membersImpl = it } + private val usedByAstNodesImpl: MutableSet + get() = _usedByAstNodesImpl ?: mutableSetOf().also { _usedByAstNodesImpl = it } + + private var rank = 0 private var hasSideEffectsImpl = false private var reachableImpl = false private var declarationReachableImpl = false - private val membersImpl = mutableMapOf() - private val usedByAstNodesImpl = mutableSetOf() - private var rank = 0 - val dependencies: MutableSet get() = original.dependenciesImpl + val dependencies: Set get() = original._dependenciesImpl ?: emptySet() - val expressions: MutableSet get() = original.expressionsImpl + val expressions: Set get() = original._expressionsImpl ?: emptySet() - val functions: MutableSet get() = original.functionsImpl + val functions: Set get() = original._functionsImpl ?: emptySet() + + val usedByAstNodes: Set get() = original._usedByAstNodesImpl ?: emptySet() var hasSideEffects: Boolean get() = original.hasSideEffectsImpl @@ -162,12 +176,9 @@ class Context { } var qualifier: Qualifier? = qualifier - get private set - val usedByAstNodes: MutableSet get() = original.usedByAstNodesImpl - - val memberNames: MutableSet get() = original.membersImpl.keys + val memberNames: Set get() = original._membersImpl?.keys ?: emptySet() constructor(localName: JsName? = null) : this(localName, null) @@ -180,7 +191,23 @@ class Context { } private set - val members: Map get() = original.membersImpl + val members: Map get() = original._membersImpl ?: emptyMap() + + fun addDependency(node: Node) { + original.dependenciesImpl += node + } + + fun addFunction(function: JsFunction) { + original.functionsImpl += function + } + + fun addExpression(expression: JsExpression) { + original.expressionsImpl += expression + } + + fun addUsedByAstNode(node: JsNode) { + original.usedByAstNodesImpl += node + } fun member(name: String): Node = original.membersImpl.getOrPut(name) { Node(null, Qualifier(this, name)) }.original @@ -224,15 +251,23 @@ class Context { other.membersImpl.clear() hasSideEffectsImpl = hasSideEffectsImpl || other.hasSideEffectsImpl - expressionsImpl += other.expressionsImpl - functionsImpl += other.functionsImpl - dependenciesImpl += other.dependenciesImpl - usedByAstNodesImpl += other.usedByAstNodesImpl + if (!other._expressionsImpl.isNullOrEmpty()) { + expressionsImpl += other.expressionsImpl + } + if (!other._functionsImpl.isNullOrEmpty()) { + functionsImpl += other.functionsImpl + } + if (!other._dependenciesImpl.isNullOrEmpty()) { + dependenciesImpl += other.dependenciesImpl + } + if (!other._usedByAstNodesImpl.isNullOrEmpty()) { + usedByAstNodesImpl += other.usedByAstNodesImpl + } - other.expressionsImpl.clear() - other.functionsImpl.clear() - other.dependenciesImpl.clear() - other.usedByAstNodesImpl.clear() + other._expressionsImpl = null + other._functionsImpl = null + other._dependenciesImpl = null + other._usedByAstNodesImpl = null } private fun merge(other: Node) { 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 ea44bb14514..4fd0081d74e 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 @@ -76,7 +76,7 @@ class ReachabilityTracker( if (!node.reachable) { reportAndNest("reach: referenced name $node", currentNodeWithLocation) { reach(node) - currentNodeWithLocation?.let { node.usedByAstNodes += it } + currentNodeWithLocation?.let { node.addUsedByAstNode(it) } } } return false @@ -101,7 +101,7 @@ class ReachabilityTracker( if (node != null && node.qualifier?.memberName in CALL_FUNCTIONS) { val parent = node.qualifier!!.parent reach(parent) - currentNodeWithLocation?.let { parent.usedByAstNodes += it } + currentNodeWithLocation?.let { parent.addUsedByAstNode(it) } } super.visitInvocation(invocation) }