diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/AnalysisResult.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/AnalysisResult.kt index a93bce9c41e..15f9e9c2359 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/AnalysisResult.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/AnalysisResult.kt @@ -31,4 +31,6 @@ interface AnalysisResult { val functionsToEnter: Set val invocationsToSkip: Set + + val functionsToSkip: Set } diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt index 75073325bfd..b3b49667711 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/Analyzer.kt @@ -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() val moduleMapping = mutableMapOf() private val functionsToEnter = mutableSetOf() + private val functionsToSkip = mutableSetOf() val analysisResult = object : AnalysisResult { override val nodeMap: Map get() = this@Analyzer.nodeMap @@ -41,6 +43,8 @@ class Analyzer(private val context: Context) : JsVisitor() { override val functionsToEnter: Set get() = this@Analyzer.functionsToEnter override val invocationsToSkip: Set get() = this@Analyzer.invocationsToSkip + + override val functionsToSkip: Set 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', ) + // where 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>? { + 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().first().expression as JsFunction + return Pair(body, listOfNotNull(wrapperNode)) + } + } + } + } + + return null + } + private fun handleObjectCreate(target: Node, arg: JsExpression?) { if (arg == null) return diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt index 2566e47e9c0..429e522f80b 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/ReachabilityTracker.kt @@ -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) diff --git a/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt b/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt index a5c1a67fe95..1d5efa21184 100644 --- a/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt +++ b/js/js.dce/src/org/jetbrains/kotlin/js/dce/util.kt @@ -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 diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index 830692a1af7..c26c34cc857 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -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 diff --git a/js/js.translator/testData/box/inline/inlineInInlineWithLambda.kt b/js/js.translator/testData/box/inline/inlineInInlineWithLambda.kt index 7edcf765f52..01c4316c643 100644 --- a/js/js.translator/testData/box/inline/inlineInInlineWithLambda.kt +++ b/js/js.translator/testData/box/inline/inlineInInlineWithLambda.kt @@ -1,3 +1,4 @@ +// EXPECTED_REACHABLE_NODES: 997 var global = "" fun log(message: String) { diff --git a/js/js.translator/testData/box/inline/inlineInInlineWithLambdaPrivate.kt b/js/js.translator/testData/box/inline/inlineInInlineWithLambdaPrivate.kt index e103721a530..888d067b185 100644 --- a/js/js.translator/testData/box/inline/inlineInInlineWithLambdaPrivate.kt +++ b/js/js.translator/testData/box/inline/inlineInInlineWithLambdaPrivate.kt @@ -1,3 +1,4 @@ +// EXPECTED_REACHABLE_NODES: 996 var global = "" fun log(message: String) { diff --git a/js/js.translator/testData/box/inlineMultiModule/importObjectInstance.kt b/js/js.translator/testData/box/inlineMultiModule/importObjectInstance.kt index ffd5dd80d78..e7d29d57d91 100644 --- a/js/js.translator/testData/box/inlineMultiModule/importObjectInstance.kt +++ b/js/js.translator/testData/box/inlineMultiModule/importObjectInstance.kt @@ -1,3 +1,4 @@ +// EXPECTED_REACHABLE_NODES: 999 // MODULE: lib // FILE: lib.kt diff --git a/js/js.translator/testData/box/inlineMultiModule/inlineInInlineWithLambdaMultiModule.kt b/js/js.translator/testData/box/inlineMultiModule/inlineInInlineWithLambdaMultiModule.kt index 0d857acb8c3..19f5b44c282 100644 --- a/js/js.translator/testData/box/inlineMultiModule/inlineInInlineWithLambdaMultiModule.kt +++ b/js/js.translator/testData/box/inlineMultiModule/inlineInInlineWithLambdaMultiModule.kt @@ -1,3 +1,4 @@ +// EXPECTED_REACHABLE_NODES: 996 // MODULE: lib // FILE: lib.kt fun baz(x: String) = "($x)" diff --git a/js/js.translator/testData/box/inlineMultiModule/inlineableAliasForExternalDeclaration.kt b/js/js.translator/testData/box/inlineMultiModule/inlineableAliasForExternalDeclaration.kt index 16afe4aa3c7..51a5d79a428 100644 --- a/js/js.translator/testData/box/inlineMultiModule/inlineableAliasForExternalDeclaration.kt +++ b/js/js.translator/testData/box/inlineMultiModule/inlineableAliasForExternalDeclaration.kt @@ -1,3 +1,4 @@ +// EXPECTED_REACHABLE_NODES: 992 // MODULE: lib // FILE: lib.kt fun foo() = "OK" diff --git a/js/js.translator/testData/box/inlineMultiModule/internalNameClash.kt b/js/js.translator/testData/box/inlineMultiModule/internalNameClash.kt index 1d52ea9eea5..f4fd7c29d89 100644 --- a/js/js.translator/testData/box/inlineMultiModule/internalNameClash.kt +++ b/js/js.translator/testData/box/inlineMultiModule/internalNameClash.kt @@ -1,3 +1,4 @@ +// EXPECTED_REACHABLE_NODES: 994 // MODULE: lib1 // FILE: lib1.kt package lib1 diff --git a/js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt b/js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt index fbc4d570cb7..8eaf32f712a 100644 --- a/js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt +++ b/js/js.translator/testData/box/inlineMultiModule/localObjectLiteralWithInheritance.kt @@ -1,4 +1,4 @@ -// EXPECTED_REACHABLE_NODES: 509 +// EXPECTED_REACHABLE_NODES: 1012 // MODULE: lib // FILE: lib.kt diff --git a/js/js.translator/testData/box/multiModule/publishedApiMangling.kt b/js/js.translator/testData/box/multiModule/publishedApiMangling.kt index fe901808ffc..e374f7de4f7 100644 --- a/js/js.translator/testData/box/multiModule/publishedApiMangling.kt +++ b/js/js.translator/testData/box/multiModule/publishedApiMangling.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