diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt index 8603df06d2e..c01ad90a15d 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.js.parser.parseFunction import org.jetbrains.kotlin.js.parser.sourcemaps.* import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.expression.InlineMetadata +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.inline.InlineStrategy @@ -243,6 +244,7 @@ class FunctionReader( replaceExternalNames(function, replacements, allDefinedNames) wrapperStatements?.forEach { replaceExternalNames(it, replacements, allDefinedNames) } function.markInlineArguments(descriptor) + markDefaultParams(function) info.wrapFunctionVariable.let { wrapFunction -> for (externalName in (collectReferencedNames(function) - allDefinedNames).filter { it.ident == wrapFunction }) { @@ -270,6 +272,26 @@ class FunctionReader( return FunctionWithWrapper(function, wrapper) } + + private fun markDefaultParams(function: JsFunction) { + val paramsByNames = function.parameters.associate { it.name to it } + for (ifStatement in function.body.statements) { + if (ifStatement !is JsIf || ifStatement.elseStatement != null) break + val thenStatement = ifStatement.thenStatement as? JsExpressionStatement ?: break + val testExpression = ifStatement.ifExpression as? JsBinaryOperation ?: break + + if (testExpression.operator != JsBinaryOperator.REF_EQ) break + val testLhs = testExpression.arg1 as? JsNameRef ?: break + val param = paramsByNames[testLhs.name] ?: break + if (testLhs.qualifier != null) break + if ((testExpression.arg2 as? JsPrefixOperation)?.operator != JsUnaryOperator.VOID) break + + val (assignLhs) = JsAstUtils.decomposeAssignmentToVariable(thenStatement.expression) ?: break + if (assignLhs != testLhs.name) break + + param.hasDefaultValue = true + } + } } private val Char.isWhitespaceOrComma: Boolean diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt index 96b8060ced2..0e13ec1293c 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.staticRef import org.jetbrains.kotlin.js.inline.context.NamingContext import org.jetbrains.kotlin.js.inline.util.rewriters.LabelNameRefreshingVisitor +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils fun aliasArgumentsIfNeeded( context: NamingContext, @@ -30,7 +31,12 @@ fun aliasArgumentsIfNeeded( ) { require(arguments.size <= parameters.size) { "arguments.size (${arguments.size}) should be less or equal to parameters.size (${parameters.size})" } + val defaultParams = mutableListOf() for ((arg, param) in arguments.zip(parameters)) { + if (JsAstUtils.isUndefinedExpression(arg)) { + defaultParams += param + continue + } val paramName = param.name val replacement = JsScope.declareTemporaryName(paramName.ident).apply { @@ -42,7 +48,7 @@ fun aliasArgumentsIfNeeded( context.replaceName(paramName, replacement) } - val defaultParams = parameters.subList(arguments.size, parameters.size) + defaultParams += parameters.subList(arguments.size, parameters.size) for (defaultParam in defaultParams) { val paramName = defaultParam.name val freshName = JsScope.declareTemporaryName(paramName.ident) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 49ad27ab878..e5fb9d3cb6a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -5096,6 +5096,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("multiModuleDefaultArgsCleanup.kt") + public void testMultiModuleDefaultArgsCleanup() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineSizeReduction/multiModuleDefaultArgsCleanup.kt"); + doTest(fileName); + } + @TestMetadata("noDuplicateVariableDeclaration.kt") public void testNoDuplicateVariableDeclaration() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineSizeReduction/noDuplicateVariableDeclaration.kt"); diff --git a/js/js.translator/testData/box/inlineSizeReduction/multiModuleDefaultArgsCleanup.kt b/js/js.translator/testData/box/inlineSizeReduction/multiModuleDefaultArgsCleanup.kt new file mode 100644 index 00000000000..303d60680b7 --- /dev/null +++ b/js/js.translator/testData/box/inlineSizeReduction/multiModuleDefaultArgsCleanup.kt @@ -0,0 +1,17 @@ +// EXPECTED_REACHABLE_NODES: 1018 +// MODULE: lib +// FILE: lib.kt +inline fun foo(x: String = "x", y: String = "y") = x + y + +// MODULE: main(lib) +// FILE: main.kt +// CHECK_VARS_COUNT: function=test count=0 + +fun test() = foo() + ";" + foo(x = "X") + ";" + foo(y = "Y") + ";" + foo(x = "X", y = "Y") + +fun box(): String { + val r = test() + if (test() != "xy;Xy;xY;XY") return "fail: $r" + + return "OK" +} \ No newline at end of file