Support new inline function format in JS DCE tool
This commit is contained in:
@@ -31,4 +31,6 @@ interface AnalysisResult {
|
||||
val functionsToEnter: Set<JsFunction>
|
||||
|
||||
val invocationsToSkip: Set<JsInvocation>
|
||||
|
||||
val functionsToSkip: Set<Context.Node>
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.js.dce
|
||||
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.dce.Context.Node
|
||||
import org.jetbrains.kotlin.js.inline.util.collectDefinedNames
|
||||
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
|
||||
@@ -30,6 +31,7 @@ class Analyzer(private val context: Context) : JsVisitor() {
|
||||
private val invocationsToSkip = mutableSetOf<JsInvocation>()
|
||||
val moduleMapping = mutableMapOf<JsStatement, String>()
|
||||
private val functionsToEnter = mutableSetOf<JsFunction>()
|
||||
private val functionsToSkip = mutableSetOf<Context.Node>()
|
||||
|
||||
val analysisResult = object : AnalysisResult {
|
||||
override val nodeMap: Map<JsNode, Node> get() = this@Analyzer.nodeMap
|
||||
@@ -41,6 +43,8 @@ class Analyzer(private val context: Context) : JsVisitor() {
|
||||
override val functionsToEnter: Set<JsFunction> get() = this@Analyzer.functionsToEnter
|
||||
|
||||
override val invocationsToSkip: Set<JsInvocation> get() = this@Analyzer.invocationsToSkip
|
||||
|
||||
override val functionsToSkip: Set<Context.Node> get() = this@Analyzer.functionsToSkip
|
||||
}
|
||||
|
||||
override fun visitVars(x: JsVars) {
|
||||
@@ -87,13 +91,13 @@ class Analyzer(private val context: Context) : JsVisitor() {
|
||||
}
|
||||
}
|
||||
|
||||
// Object.defineProperty()
|
||||
when {
|
||||
// Object.defineProperty()
|
||||
context.isObjectDefineProperty(function) ->
|
||||
handleObjectDefineProperty(x, expression.arguments.getOrNull(0), expression.arguments.getOrNull(1),
|
||||
expression.arguments.getOrNull(2))
|
||||
|
||||
// Kotlin.defineModule()
|
||||
// Kotlin.defineModule()
|
||||
context.isDefineModule(function) ->
|
||||
// (just remove it)
|
||||
astNodesToEliminate += x
|
||||
@@ -227,13 +231,25 @@ class Analyzer(private val context: Context) : JsVisitor() {
|
||||
return leftNode
|
||||
}
|
||||
|
||||
// lhs = Kotlin.defineInlineFunction('fqn', function() { ... })
|
||||
// lhs = Kotlin.defineInlineFunction('fqn', <function declaration>)
|
||||
// where <function declaration> is one of
|
||||
// - function() { ... }
|
||||
// - wrapFunction(function() { ... })
|
||||
if (context.isDefineInlineFunction(function) && rhs.arguments.size == 2) {
|
||||
leftNode.functions += rhs.arguments[1] as JsFunction
|
||||
val defineInlineFunctionNode = context.extractNode(function)
|
||||
if (defineInlineFunctionNode != null) {
|
||||
leftNode.dependencies += defineInlineFunctionNode
|
||||
tryExtractFunction(rhs.arguments[1])?.let { (inlineableFunction, additionalDeps) ->
|
||||
leftNode.functions += inlineableFunction
|
||||
val defineInlineFunctionNode = context.extractNode(function)
|
||||
if (defineInlineFunctionNode != null) {
|
||||
leftNode.dependencies += defineInlineFunctionNode
|
||||
}
|
||||
leftNode.dependencies += additionalDeps
|
||||
return leftNode
|
||||
}
|
||||
}
|
||||
|
||||
tryExtractFunction(rhs)?.let { (functionBody, additionalDeps) ->
|
||||
leftNode.functions += functionBody
|
||||
leftNode.dependencies += additionalDeps
|
||||
return leftNode
|
||||
}
|
||||
}
|
||||
@@ -247,7 +263,7 @@ class Analyzer(private val context: Context) : JsVisitor() {
|
||||
val reassignValue = reassignment.arg2
|
||||
if (reassignNode == secondNode && reassignNode != null && reassignValue is JsObjectLiteral &&
|
||||
reassignValue.propertyInitializers.isEmpty()
|
||||
) {
|
||||
) {
|
||||
return processAssignment(node, lhs, rhs.arg1)
|
||||
}
|
||||
}
|
||||
@@ -278,6 +294,31 @@ class Analyzer(private val context: Context) : JsVisitor() {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun tryExtractFunction(expression: JsExpression): Pair<JsFunction, List<Context.Node>>? {
|
||||
when (expression) {
|
||||
is JsFunction -> return Pair(expression, emptyList())
|
||||
is JsInvocation -> {
|
||||
if (context.isWrapFunction(expression.qualifier)) {
|
||||
(expression.arguments.getOrNull(0) as? JsFunction)?.let { wrapper ->
|
||||
val statementsWithoutBody = wrapper.body.statements.filter { it !is JsReturn }
|
||||
JsBlock(statementsWithoutBody).let {
|
||||
context.addNodesForLocalVars(collectDefinedNames(it))
|
||||
accept(it)
|
||||
}
|
||||
|
||||
val wrapperNode = context.extractNode(expression.qualifier)?.also {
|
||||
functionsToSkip += it
|
||||
}
|
||||
val body = wrapper.body.statements.filterIsInstance<JsReturn>().first().expression as JsFunction
|
||||
return Pair(body, listOfNotNull(wrapperNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun handleObjectCreate(target: Node, arg: JsExpression?) {
|
||||
if (arg == null) return
|
||||
|
||||
|
||||
@@ -135,15 +135,18 @@ class ReachabilityTracker(
|
||||
}
|
||||
}
|
||||
|
||||
for (expr in node.functions) {
|
||||
reportAndNest("traverse: function", expr) {
|
||||
expr.collectLocalVariables().let {
|
||||
context.addNodesForLocalVars(it)
|
||||
context.namesOfLocalVars += it
|
||||
if (node !in analysisResult.functionsToSkip) {
|
||||
for (expr in node.functions) {
|
||||
reportAndNest("traverse: function", expr) {
|
||||
expr.collectLocalVariables().let {
|
||||
context.addNodesForLocalVars(it)
|
||||
context.namesOfLocalVars += it
|
||||
}
|
||||
withErasedThis { expr.body.accept(this) }
|
||||
}
|
||||
withErasedThis { expr.body.accept(this) }
|
||||
}
|
||||
}
|
||||
|
||||
for (expr in node.expressions) {
|
||||
reportAndNest("traverse: value", expr) {
|
||||
expr.accept(this)
|
||||
|
||||
@@ -27,6 +27,8 @@ fun Context.isDefineModule(function: JsExpression): Boolean = isKotlinFunction(f
|
||||
|
||||
fun Context.isDefineInlineFunction(function: JsExpression): Boolean = isKotlinFunction(function, "defineInlineFunction")
|
||||
|
||||
fun Context.isWrapFunction(function: JsExpression): Boolean = isKotlinFunction(function, "wrapFunction")
|
||||
|
||||
fun Context.isObjectFunction(function: JsExpression, functionName: String): Boolean {
|
||||
if (function !is JsNameRef) return false
|
||||
if (function.ident != functionName) return false
|
||||
|
||||
@@ -44,7 +44,10 @@ import org.jetbrains.kotlin.js.dce.DeadCodeElimination
|
||||
import org.jetbrains.kotlin.js.dce.InputFile
|
||||
import org.jetbrains.kotlin.js.facade.*
|
||||
import org.jetbrains.kotlin.js.parser.parse
|
||||
import org.jetbrains.kotlin.js.parser.sourcemaps.*
|
||||
import org.jetbrains.kotlin.js.parser.sourcemaps.SourceMapError
|
||||
import org.jetbrains.kotlin.js.parser.sourcemaps.SourceMapLocationRemapper
|
||||
import org.jetbrains.kotlin.js.parser.sourcemaps.SourceMapParser
|
||||
import org.jetbrains.kotlin.js.parser.sourcemaps.SourceMapSuccess
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
|
||||
import org.jetbrains.kotlin.incremental.js.TranslationResultValue
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 997
|
||||
var global = ""
|
||||
|
||||
fun log(message: String) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 996
|
||||
var global = ""
|
||||
|
||||
fun log(message: String) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 999
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 996
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
fun baz(x: String) = "($x)"
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 992
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
fun foo() = "OK"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 994
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.kt
|
||||
package lib1
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 509
|
||||
// EXPECTED_REACHABLE_NODES: 1012
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1695
|
||||
// EXPECTED_REACHABLE_NODES: 990
|
||||
// PROPERTY_WRITE_COUNT: name=publishedTopLevel_61zpoe$ count=1
|
||||
// PROPERTY_WRITE_COUNT: name=published_61zpoe$ count=1
|
||||
// PROPERTY_WRITE_COUNT: name=B count=1
|
||||
|
||||
Reference in New Issue
Block a user