JS: optimize default arguments in cross-module inliner
This reduces size of circlet UI (of approx 2.5 mb) by about 90 kb
This commit is contained in:
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.js.parser.parseFunction
|
|||||||
import org.jetbrains.kotlin.js.parser.sourcemaps.*
|
import org.jetbrains.kotlin.js.parser.sourcemaps.*
|
||||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||||
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
|
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.js.translate.utils.JsDescriptorUtils.getModuleName
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
||||||
@@ -243,6 +244,7 @@ class FunctionReader(
|
|||||||
replaceExternalNames(function, replacements, allDefinedNames)
|
replaceExternalNames(function, replacements, allDefinedNames)
|
||||||
wrapperStatements?.forEach { replaceExternalNames(it, replacements, allDefinedNames) }
|
wrapperStatements?.forEach { replaceExternalNames(it, replacements, allDefinedNames) }
|
||||||
function.markInlineArguments(descriptor)
|
function.markInlineArguments(descriptor)
|
||||||
|
markDefaultParams(function)
|
||||||
|
|
||||||
info.wrapFunctionVariable.let { wrapFunction ->
|
info.wrapFunctionVariable.let { wrapFunction ->
|
||||||
for (externalName in (collectReferencedNames(function) - allDefinedNames).filter { it.ident == wrapFunction }) {
|
for (externalName in (collectReferencedNames(function) - allDefinedNames).filter { it.ident == wrapFunction }) {
|
||||||
@@ -270,6 +272,26 @@ class FunctionReader(
|
|||||||
|
|
||||||
return FunctionWithWrapper(function, wrapper)
|
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
|
private val Char.isWhitespaceOrComma: Boolean
|
||||||
|
|||||||
@@ -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.context.NamingContext
|
||||||
import org.jetbrains.kotlin.js.inline.util.rewriters.LabelNameRefreshingVisitor
|
import org.jetbrains.kotlin.js.inline.util.rewriters.LabelNameRefreshingVisitor
|
||||||
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
|
|
||||||
fun aliasArgumentsIfNeeded(
|
fun aliasArgumentsIfNeeded(
|
||||||
context: NamingContext,
|
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})" }
|
require(arguments.size <= parameters.size) { "arguments.size (${arguments.size}) should be less or equal to parameters.size (${parameters.size})" }
|
||||||
|
|
||||||
|
val defaultParams = mutableListOf<JsParameter>()
|
||||||
for ((arg, param) in arguments.zip(parameters)) {
|
for ((arg, param) in arguments.zip(parameters)) {
|
||||||
|
if (JsAstUtils.isUndefinedExpression(arg)) {
|
||||||
|
defaultParams += param
|
||||||
|
continue
|
||||||
|
}
|
||||||
val paramName = param.name
|
val paramName = param.name
|
||||||
|
|
||||||
val replacement = JsScope.declareTemporaryName(paramName.ident).apply {
|
val replacement = JsScope.declareTemporaryName(paramName.ident).apply {
|
||||||
@@ -42,7 +48,7 @@ fun aliasArgumentsIfNeeded(
|
|||||||
context.replaceName(paramName, replacement)
|
context.replaceName(paramName, replacement)
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaultParams = parameters.subList(arguments.size, parameters.size)
|
defaultParams += parameters.subList(arguments.size, parameters.size)
|
||||||
for (defaultParam in defaultParams) {
|
for (defaultParam in defaultParams) {
|
||||||
val paramName = defaultParam.name
|
val paramName = defaultParam.name
|
||||||
val freshName = JsScope.declareTemporaryName(paramName.ident)
|
val freshName = JsScope.declareTemporaryName(paramName.ident)
|
||||||
|
|||||||
@@ -5096,6 +5096,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("noDuplicateVariableDeclaration.kt")
|
||||||
public void testNoDuplicateVariableDeclaration() throws Exception {
|
public void testNoDuplicateVariableDeclaration() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineSizeReduction/noDuplicateVariableDeclaration.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineSizeReduction/noDuplicateVariableDeclaration.kt");
|
||||||
|
|||||||
+17
@@ -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"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user