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)
return leftNode
}
leftNode.qualifier?.memberName == Namer.METADATA -> {
leftNode.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
@@ -76,7 +76,7 @@ class Context {
fun extractNode(expression: JsExpression): Node? {
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)
path.fold(currentModule.original) { n, memberName -> n.member(memberName) }
}
@@ -134,7 +134,7 @@ class Context {
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 _expressionsImpl: MutableSet<JsExpression>? = null
private var _functionsImpl: MutableSet<JsFunction>? = null
@@ -183,7 +183,7 @@ class Context {
original.declarationReachableImpl = value
}
var qualifier: Qualifier? = qualifier
var parent: Node? = parent
private set
private var color: Byte = 0
@@ -196,7 +196,7 @@ class Context {
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
get() {
@@ -225,20 +225,20 @@ class Context {
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) {
val a = original
val b = other.original
if (a == b) return
if (a.qualifier == null && b.qualifier == null) {
if (a.parent == null && b.parent == null) {
a.merge(b)
}
else if (a.qualifier == null) {
else if (a.parent == null) {
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)
}
else {
@@ -257,12 +257,12 @@ class Context {
for ((name, member) in newMembers) {
membersImpl[name] = member
member.original.qualifier = Qualifier(this, member.original.qualifier!!.memberName)
member.original.parent = this
}
for ((name, member) in existingMembers) {
membersImpl[name]!!.original.merge(member.original)
membersImpl[name] = member.original
member.original.qualifier = Qualifier(this, member.original.qualifier!!.memberName)
member.original.parent = this
}
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> =
generateSequence(original) { it.qualifier?.parent?.original }.mapNotNull { it.qualifier?.memberName }
generateSequence(original) { it.parent?.original }.mapNotNull { it.memberName }
.toList().asReversed()
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 -> {}
else -> {
val node = context.extractNode(invocation.qualifier)
if (node != null && node.qualifier?.memberName in CALL_FUNCTIONS) {
val parent = node.qualifier!!.parent
if (node != null && node.memberName in CALL_FUNCTIONS) {
val parent = node.parent!!
reach(parent)
currentNodeWithLocation?.let { parent.addUsedByAstNode(it) }
}
@@ -178,15 +178,14 @@ class ReachabilityTracker(
var current = node
while (true) {
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) }
if (!dependency.reachable) {
reportAndNest("reach: dependency $dependency", null) { reach(dependency) }
}
}
val qualifier = current.qualifier ?: break
path += qualifier.memberName
current = qualifier.parent
path += current.memberName ?: break
current = current.parent!! // memberName != null => parent != null
}
}
@@ -202,7 +201,7 @@ class ReachabilityTracker(
reachableNodesImpl += node
}
node.original.qualifier?.parent?.let {
node.original.parent?.let {
reportAndNest("reach-decl: parent $it", null) {
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) {
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) {
sb.append(" (reachable")
@@ -83,11 +83,11 @@ fun Iterable<Node>.extractReachableRoots(context: Context): Iterable<Node> {
private fun Node.extractRootsImpl(target: MutableList<Node>, context: Context) {
if (!context.visit(original)) return
val qualifier = original.qualifier
if (qualifier == null) {
val parent = original.parent
if (parent == null) {
target += original
}
else {
qualifier.parent.extractRootsImpl(target, context)
parent.extractRootsImpl(target, context)
}
}