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:
Alexey Andreev
2017-09-13 16:47:46 +03:00
parent e6ab3dc936
commit ee4d790f43
4 changed files with 52 additions and 1 deletions
@@ -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
@@ -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<JsParameter>()
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)
@@ -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");
@@ -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"
}