JS inline: default parameter processing for inlined functions

When function is inlined initializers
for default parameters can be:
1. removed  if argument is not `void 0`
2. expanded otherwise
This commit is contained in:
Alexey Tsvetkov
2014-07-30 19:40:23 +04:00
committed by Zalim Bashorov
parent 1f68b43558
commit 87d108e75e
4 changed files with 163 additions and 8 deletions
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
public final class JsParameter extends SourceInfoAwareJsNode implements HasName {
@NotNull
private final JsName name;
private boolean hasDefaultValue = false;
public JsParameter(@NotNull JsName name) {
this.name = name;
@@ -30,6 +31,14 @@ public final class JsParameter extends SourceInfoAwareJsNode implements HasName
return name;
}
public boolean hasDefaultValue() {
return hasDefaultValue;
}
public void setHasDefaultValue(boolean hasDefaultValue) {
this.hasDefaultValue = hasDefaultValue;
}
@Override
public void accept(JsVisitor v) {
v.visitParameter(this);
@@ -0,0 +1,143 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.inline
import com.google.dart.compiler.backend.js.ast.*
import java.util.IdentityHashMap
import java.util.Collections
/**
* Removes initializers for default parameters with non-void0 arguments given
* Expands initializers for default parameters with void0 arguments given
*
* @see isInitializer
*/
fun removeRedundantDefaultInitializers(arguments: List<JsExpression>, parameters: List<JsParameter>, body: JsBlock) {
val toRemove = getInitializedParametersNames(arguments, parameters)
val toExpand = getUnInitializedParametersNames(arguments, parameters)
val statements = body.getStatements()
val newStatements = statements.flatMap {
when {
!isInitializer(it) -> listOf(it)
else -> {
val name = getNameFromInitializer(it)
when {
name in toRemove.keySet() -> listOf<JsStatement>()
name in toExpand.keySet() -> expand(it)
else -> listOf(it)
}
}
}
}.filterNotNull()
statements.clear()
statements.addAll(newStatements)
}
/**
* Tests if statement is an initializer for parameter with default value.
*
* This check assumes that default parameter initializer is generated like:
* if (defaultParam === void 0) {
* ...
* }
*/
private fun isInitializer(statement: JsStatement): Boolean {
return getNameFromInitializer(statement) != null
}
private fun getNameFromInitializer(statement: JsStatement): JsName? {
val jsIf = (statement as? JsIf)
val ifExpr = jsIf?.getIfExpression() as? JsBinaryOperation
return when {
jsIf?.getElseStatement() != null -> null
else -> getNameFromInitializer(ifExpr)
}
}
private fun getNameFromInitializer(initializerTestExpression: JsExpression?): JsName? {
val binOp = initializerTestExpression as? JsBinaryOperation
val arg1 = binOp?.getArg1()
val arg2 = binOp?.getArg2()
val operator = binOp?.getOperator()
return when {
operator != JsBinaryOperator.REF_EQ -> null
!isVoid0(arg2) -> null
else -> (arg1 as? JsNameRef)?.getName()
}
}
private fun isVoid0(expr: JsExpression?): Boolean {
val op = (expr as? JsUnaryOperation)?.getOperator()
return op == JsUnaryOperator.VOID
}
private fun getInitializedParametersNames(args: List<JsExpression>,
params: List<JsParameter>): Map<JsName, Boolean> {
val names = IdentityHashMap<JsName, Boolean>()
val argParams = (args zip params).stream()
val initialized = argParams.filter { it.second.hasDefaultValue() }
.filter { !isVoid0(it.first) }
.map { it.second }
initialized.map { it.getName() }
.forEach { names.put(it, true) }
return names
}
private fun getUnInitializedParametersNames(args: List<JsExpression>,
params: List<JsParameter>): Map<JsName, Boolean> {
val names = IdentityHashMap<JsName, Boolean>()
val argParams = (args zip params).stream()
val void0Params = argParams.filter { it.second.hasDefaultValue() }
.filter { isVoid0(it.first) }
.map { it.second }
val noArgsParams = params.drop(args.size).stream()
val uninitialized = void0Params.plus(noArgsParams)
uninitialized.map { it.getName() }
.forEach { names.put(it, true) }
return names
}
/**
* Changes initializer check:
* if (arg === void 0) { arg = ... }
* To list of statement(s):
* arg = ...
*/
private fun expand(initializer: JsStatement): Iterable<JsStatement> {
val then = (initializer as? JsIf)?.getThenStatement()
return when {
then is JsBlock -> then.getStatements()
then != null -> listOf(then)
else -> listOf()
}
}
@@ -20,13 +20,10 @@ import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import static org.jetbrains.k2js.inline.InlinePackage.needToAlias;
import static org.jetbrains.k2js.inline.InlinePackage.aliasArgumentsIfNeeded;
import static org.jetbrains.k2js.inline.InlinePackage.renameLocals;
import static org.jetbrains.k2js.inline.InlinePackage.replaceThisReference;
import static org.jetbrains.k2js.inline.InlinePackage.hasThisReference;
import static org.jetbrains.k2js.inline.InlinePackage.*;
import java.util.*;
import java.util.Collection;
import java.util.List;
class FunctionInlineMutator {
private static final String BREAK_LABEL = "break_inlined";
@@ -77,8 +74,12 @@ class FunctionInlineMutator {
}
private void process() {
List<JsExpression> arguments = getArguments();
List<JsParameter> parameters = getParameters();
replaceThis();
aliasArgumentsIfNeeded(renamingContext, getArguments(), getParameters());
removeRedundantDefaultInitializers(arguments, parameters, body);
aliasArgumentsIfNeeded(renamingContext, arguments, parameters);
renameLocals(renamingContext, invokedFunction);
applyRenaming();
replaceReturns();
@@ -130,7 +130,9 @@ public final class FunctionTranslator extends AbstractTranslator {
public static void addParameters(List<JsParameter> list, FunctionDescriptor descriptor, TranslationContext context) {
for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) {
list.add(new JsParameter(context.getNameForDescriptor(valueParameter)));
JsParameter jsParameter = new JsParameter(context.getNameForDescriptor(valueParameter));
jsParameter.setHasDefaultValue(valueParameter.hasDefaultValue());
list.add(jsParameter);
}
}