JS DCE: inline Qualifier class to reduce RAM usage

This commit is contained in:
Anton Bannykh
2021-01-28 15:01:47 +03:00
parent bbc6d2b993
commit 26ce6b5131
5 changed files with 24 additions and 27 deletions
@@ -278,7 +278,7 @@ class Analyzer(private val context: Context) : JsVisitor() {
leftNode.addFunction(rhs) leftNode.addFunction(rhs)
return leftNode return leftNode
} }
leftNode.qualifier?.memberName == Namer.METADATA -> { leftNode.memberName == Namer.METADATA -> {
// lhs.$metadata$ = expression // lhs.$metadata$ = expression
// During reachability tracking phase: eliminate it if lhs is unreachable, traverse expression // During reachability tracking phase: eliminate it if lhs is unreachable, traverse expression
// It's commonly used to supply class's metadata // It's commonly used to supply class's metadata
@@ -76,7 +76,7 @@ class Context {
fun extractNode(expression: JsExpression): Node? { fun extractNode(expression: JsExpression): Node? {
val node = extractNodeImpl(expression)?.original val node = extractNodeImpl(expression)?.original
return if (node != null && moduleExportsNode in generateSequence(node) { it.qualifier?.parent }) { return if (node != null && moduleExportsNode in generateSequence(node) { it.parent }) {
val path = node.pathFromRoot().drop(2) val path = node.pathFromRoot().drop(2)
path.fold(currentModule.original) { n, memberName -> n.member(memberName) } path.fold(currentModule.original) { n, memberName -> n.member(memberName) }
} }
@@ -134,7 +134,7 @@ class Context {
fun visit(n: Node) = n.visit(currentColor) fun visit(n: Node) = n.visit(currentColor)
class Node private constructor(val localName: JsName?, qualifier: Qualifier?) { class Node private constructor(val localName: JsName?, parent: Node?, val memberName: String?) {
private var _dependenciesImpl: MutableSet<Node>? = null private var _dependenciesImpl: MutableSet<Node>? = null
private var _expressionsImpl: MutableSet<JsExpression>? = null private var _expressionsImpl: MutableSet<JsExpression>? = null
private var _functionsImpl: MutableSet<JsFunction>? = null private var _functionsImpl: MutableSet<JsFunction>? = null
@@ -183,7 +183,7 @@ class Context {
original.declarationReachableImpl = value original.declarationReachableImpl = value
} }
var qualifier: Qualifier? = qualifier var parent: Node? = parent
private set private set
private var color: Byte = 0 private var color: Byte = 0
@@ -196,7 +196,7 @@ class Context {
val memberNames: Set<String> get() = original._membersImpl?.keys ?: emptySet() val memberNames: Set<String> get() = original._membersImpl?.keys ?: emptySet()
constructor(localName: JsName? = null) : this(localName, null) constructor(localName: JsName? = null) : this(localName, null, null)
var original: Node = this var original: Node = this
get() { get() {
@@ -225,20 +225,20 @@ class Context {
original.usedByAstNodesImpl += node original.usedByAstNodesImpl += node
} }
fun member(name: String): Node = original.membersImpl.getOrPut(name) { Node(null, Qualifier(this, name)) }.original fun member(name: String): Node = original.membersImpl.getOrPut(name) { Node(null, this, name) }.original
fun alias(other: Node) { fun alias(other: Node) {
val a = original val a = original
val b = other.original val b = other.original
if (a == b) return if (a == b) return
if (a.qualifier == null && b.qualifier == null) { if (a.parent == null && b.parent == null) {
a.merge(b) a.merge(b)
} }
else if (a.qualifier == null) { else if (a.parent == null) {
if (b.root() == a) a.makeDependencies(b) else b.evacuateFrom(a) if (b.root() == a) a.makeDependencies(b) else b.evacuateFrom(a)
} }
else if (b.qualifier == null) { else if (b.parent == null) {
if (a.root() == b) a.makeDependencies(b) else a.evacuateFrom(b) if (a.root() == b) a.makeDependencies(b) else a.evacuateFrom(b)
} }
else { else {
@@ -257,12 +257,12 @@ class Context {
for ((name, member) in newMembers) { for ((name, member) in newMembers) {
membersImpl[name] = member membersImpl[name] = member
member.original.qualifier = Qualifier(this, member.original.qualifier!!.memberName) member.original.parent = this
} }
for ((name, member) in existingMembers) { for ((name, member) in existingMembers) {
membersImpl[name]!!.original.merge(member.original) membersImpl[name]!!.original.merge(member.original)
membersImpl[name] = member.original membersImpl[name] = member.original
member.original.qualifier = Qualifier(this, member.original.qualifier!!.memberName) member.original.parent = this
} }
other.membersImpl.clear() other.membersImpl.clear()
@@ -301,14 +301,12 @@ class Context {
} }
} }
fun root(): Node = generateSequence(original) { it.qualifier?.parent?.original }.last() fun root(): Node = generateSequence(original) { it.parent?.original }.last()
fun pathFromRoot(): List<String> = fun pathFromRoot(): List<String> =
generateSequence(original) { it.qualifier?.parent?.original }.mapNotNull { it.qualifier?.memberName } generateSequence(original) { it.parent?.original }.mapNotNull { it.memberName }
.toList().asReversed() .toList().asReversed()
override fun toString(): String = (root().localName?.ident ?: "<unknown>") + pathFromRoot().joinToString("") { ".$it" } override fun toString(): String = (root().localName?.ident ?: "<unknown>") + pathFromRoot().joinToString("") { ".$it" }
} }
}
class Qualifier(val parent: Node, val memberName: String)
}
@@ -102,8 +102,8 @@ class ReachabilityTracker(
invocation in analysisResult.invocationsToSkip -> {} invocation in analysisResult.invocationsToSkip -> {}
else -> { else -> {
val node = context.extractNode(invocation.qualifier) val node = context.extractNode(invocation.qualifier)
if (node != null && node.qualifier?.memberName in CALL_FUNCTIONS) { if (node != null && node.memberName in CALL_FUNCTIONS) {
val parent = node.qualifier!!.parent val parent = node.parent!!
reach(parent) reach(parent)
currentNodeWithLocation?.let { parent.addUsedByAstNode(it) } currentNodeWithLocation?.let { parent.addUsedByAstNode(it) }
} }
@@ -178,15 +178,14 @@ class ReachabilityTracker(
var current = node var current = node
while (true) { while (true) {
for (ancestorDependency in current.dependencies) { for (ancestorDependency in current.dependencies) {
if (current in generateSequence(ancestorDependency) { it.qualifier?.parent }) continue if (current in generateSequence(ancestorDependency) { it.parent }) continue
val dependency = path.asReversed().fold(ancestorDependency) { n, memberName -> n.member(memberName) } val dependency = path.asReversed().fold(ancestorDependency) { n, memberName -> n.member(memberName) }
if (!dependency.reachable) { if (!dependency.reachable) {
reportAndNest("reach: dependency $dependency", null) { reach(dependency) } reportAndNest("reach: dependency $dependency", null) { reach(dependency) }
} }
} }
val qualifier = current.qualifier ?: break path += current.memberName ?: break
path += qualifier.memberName current = current.parent!! // memberName != null => parent != null
current = qualifier.parent
} }
} }
@@ -202,7 +201,7 @@ class ReachabilityTracker(
reachableNodesImpl += node reachableNodesImpl += node
} }
node.original.qualifier?.parent?.let { node.original.parent?.let {
reportAndNest("reach-decl: parent $it", null) { reportAndNest("reach-decl: parent $it", null) {
reachDeclaration(it) reachDeclaration(it)
} }
@@ -24,7 +24,7 @@ fun printTree(root: Node, consumer: (String) -> Unit, printNestedMembers: Boolea
private fun printTree(node: Node, consumer: (String) -> Unit, depth: Int, settings: Settings) { private fun printTree(node: Node, consumer: (String) -> Unit, depth: Int, settings: Settings) {
val sb = StringBuilder() val sb = StringBuilder()
sb.append(" ".repeat(depth)).append(node.qualifier?.memberName ?: node.toString()) sb.append(" ".repeat(depth)).append(node.memberName ?: node.toString())
if (node.reachable) { if (node.reachable) {
sb.append(" (reachable") sb.append(" (reachable")
@@ -83,11 +83,11 @@ fun Iterable<Node>.extractReachableRoots(context: Context): Iterable<Node> {
private fun Node.extractRootsImpl(target: MutableList<Node>, context: Context) { private fun Node.extractRootsImpl(target: MutableList<Node>, context: Context) {
if (!context.visit(original)) return if (!context.visit(original)) return
val qualifier = original.qualifier val parent = original.parent
if (qualifier == null) { if (parent == null) {
target += original target += original
} }
else { else {
qualifier.parent.extractRootsImpl(target, context) parent.extractRootsImpl(target, context)
} }
} }