JS DCE: inline Qualifier class to reduce RAM usage
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user