JS DCE: use Multimaps to reduce Node fields RAM
This commit is contained in:
@@ -9,6 +9,7 @@ dependencies {
|
|||||||
compile(project(":js:js.ast"))
|
compile(project(":js:js.ast"))
|
||||||
compile(project(":js:js.translator"))
|
compile(project(":js:js.translator"))
|
||||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||||
|
compileOnly(intellijDep()) { includeJars("guava", rootProject = rootProject) }
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.dce
|
package org.jetbrains.kotlin.js.dce
|
||||||
|
|
||||||
|
import com.google.common.collect.LinkedHashMultimap
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
|
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
|
||||||
import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
|
import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
|
||||||
@@ -24,6 +25,12 @@ import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.array
|
|||||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.index
|
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.index
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
|
// Collections per Node consumes too much RAM
|
||||||
|
private val nodeDependencies = LinkedHashMultimap.create<Node, Node>()
|
||||||
|
private val nodeExpressions = LinkedHashMultimap.create<Node, JsExpression>()
|
||||||
|
private val nodeFunctions = LinkedHashMultimap.create<Node, JsFunction>()
|
||||||
|
private val nodeUsedByAstNodes = LinkedHashMultimap.create<Node, JsNode>()
|
||||||
|
|
||||||
val globalScope = Node()
|
val globalScope = Node()
|
||||||
val moduleExportsNode = globalScope.member("module").member("exports")
|
val moduleExportsNode = globalScope.member("module").member("exports")
|
||||||
var currentModule = globalScope
|
var currentModule = globalScope
|
||||||
@@ -134,36 +141,24 @@ class Context {
|
|||||||
|
|
||||||
fun visit(n: Node) = n.visit(currentColor)
|
fun visit(n: Node) = n.visit(currentColor)
|
||||||
|
|
||||||
class Node private constructor(val localName: JsName?, parent: Node?, val memberName: String?) {
|
inner class Node private constructor(val localName: JsName?, parent: Node?, val memberName: String?) {
|
||||||
private var _dependenciesImpl: MutableSet<Node>? = null
|
|
||||||
private var _expressionsImpl: MutableSet<JsExpression>? = null
|
|
||||||
private var _functionsImpl: MutableSet<JsFunction>? = null
|
|
||||||
private var _membersImpl: MutableMap<String, Node>? = null
|
private var _membersImpl: MutableMap<String, Node>? = null
|
||||||
private var _usedByAstNodesImpl: MutableSet<JsNode>? = null
|
|
||||||
|
|
||||||
private val dependenciesImpl: MutableSet<Node>
|
|
||||||
get() = _dependenciesImpl ?: mutableSetOf<Node>().also { _dependenciesImpl = it }
|
|
||||||
private val expressionsImpl: MutableSet<JsExpression>
|
|
||||||
get() = _expressionsImpl ?: mutableSetOf<JsExpression>().also { _expressionsImpl = it }
|
|
||||||
private val functionsImpl: MutableSet<JsFunction>
|
|
||||||
get() = _functionsImpl ?: mutableSetOf<JsFunction>().also { _functionsImpl = it }
|
|
||||||
private val membersImpl: MutableMap<String, Node>
|
private val membersImpl: MutableMap<String, Node>
|
||||||
get() = _membersImpl ?: mutableMapOf<String, Node>().also { _membersImpl = it }
|
get() = _membersImpl ?: mutableMapOf<String, Node>().also { _membersImpl = it }
|
||||||
private val usedByAstNodesImpl: MutableSet<JsNode>
|
|
||||||
get() = _usedByAstNodesImpl ?: mutableSetOf<JsNode>().also { _usedByAstNodesImpl = it }
|
|
||||||
|
|
||||||
private var rank = 0
|
private var rank = 0
|
||||||
private var hasSideEffectsImpl = false
|
private var hasSideEffectsImpl = false
|
||||||
private var reachableImpl = false
|
private var reachableImpl = false
|
||||||
private var declarationReachableImpl = false
|
private var declarationReachableImpl = false
|
||||||
|
|
||||||
val dependencies: Set<Node> get() = original._dependenciesImpl ?: emptySet()
|
val dependencies: Set<Node> get() = nodeDependencies[original]
|
||||||
|
|
||||||
val expressions: Set<JsExpression> get() = original._expressionsImpl ?: emptySet()
|
val expressions: Set<JsExpression> get() = nodeExpressions[original]
|
||||||
|
|
||||||
val functions: Set<JsFunction> get() = original._functionsImpl ?: emptySet()
|
val functions: Set<JsFunction> get() = nodeFunctions[original]
|
||||||
|
|
||||||
val usedByAstNodes: Set<JsNode> get() = original._usedByAstNodesImpl ?: emptySet()
|
val usedByAstNodes: Set<JsNode> get() = nodeUsedByAstNodes[original]
|
||||||
|
|
||||||
var hasSideEffects: Boolean
|
var hasSideEffects: Boolean
|
||||||
get() = original.hasSideEffectsImpl
|
get() = original.hasSideEffectsImpl
|
||||||
@@ -210,19 +205,19 @@ class Context {
|
|||||||
val members: Map<String, Node> get() = original._membersImpl ?: emptyMap()
|
val members: Map<String, Node> get() = original._membersImpl ?: emptyMap()
|
||||||
|
|
||||||
fun addDependency(node: Node) {
|
fun addDependency(node: Node) {
|
||||||
original.dependenciesImpl += node
|
nodeDependencies.put(original, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFunction(function: JsFunction) {
|
fun addFunction(function: JsFunction) {
|
||||||
original.functionsImpl += function
|
nodeFunctions.put(original, function)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addExpression(expression: JsExpression) {
|
fun addExpression(expression: JsExpression) {
|
||||||
original.expressionsImpl += expression
|
nodeExpressions.put(original, expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addUsedByAstNode(node: JsNode) {
|
fun addUsedByAstNode(node: JsNode) {
|
||||||
original.usedByAstNodesImpl += node
|
nodeUsedByAstNodes.put(original, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun member(name: String): Node = original.membersImpl.getOrPut(name) { Node(null, this, name) }.original
|
fun member(name: String): Node = original.membersImpl.getOrPut(name) { Node(null, this, name) }.original
|
||||||
@@ -247,8 +242,8 @@ class Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeDependencies(other: Node) {
|
private fun makeDependencies(other: Node) {
|
||||||
dependenciesImpl += other
|
nodeDependencies.put(this, other)
|
||||||
other.dependenciesImpl += this
|
nodeDependencies.put(other, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evacuateFrom(other: Node) {
|
private fun evacuateFrom(other: Node) {
|
||||||
@@ -267,23 +262,15 @@ class Context {
|
|||||||
other.membersImpl.clear()
|
other.membersImpl.clear()
|
||||||
|
|
||||||
hasSideEffectsImpl = hasSideEffectsImpl || other.hasSideEffectsImpl
|
hasSideEffectsImpl = hasSideEffectsImpl || other.hasSideEffectsImpl
|
||||||
if (!other._expressionsImpl.isNullOrEmpty()) {
|
nodeExpressions.putAll(this, nodeExpressions[other])
|
||||||
expressionsImpl += other.expressionsImpl
|
nodeFunctions.putAll(this, nodeFunctions[other])
|
||||||
}
|
nodeDependencies.putAll(this, nodeDependencies[other])
|
||||||
if (!other._functionsImpl.isNullOrEmpty()) {
|
nodeUsedByAstNodes.putAll(this, nodeUsedByAstNodes[other])
|
||||||
functionsImpl += other.functionsImpl
|
|
||||||
}
|
|
||||||
if (!other._dependenciesImpl.isNullOrEmpty()) {
|
|
||||||
dependenciesImpl += other.dependenciesImpl
|
|
||||||
}
|
|
||||||
if (!other._usedByAstNodesImpl.isNullOrEmpty()) {
|
|
||||||
usedByAstNodesImpl += other.usedByAstNodesImpl
|
|
||||||
}
|
|
||||||
|
|
||||||
other._expressionsImpl = null
|
nodeExpressions.removeAll(other)
|
||||||
other._functionsImpl = null
|
nodeFunctions.removeAll(other)
|
||||||
other._dependenciesImpl = null
|
nodeDependencies.removeAll(other)
|
||||||
other._usedByAstNodesImpl = null
|
nodeUsedByAstNodes.removeAll(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun merge(other: Node) {
|
private fun merge(other: Node) {
|
||||||
|
|||||||
Reference in New Issue
Block a user