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:
committed by
Zalim Bashorov
parent
1f68b43558
commit
87d108e75e
@@ -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();
|
||||
|
||||
+3
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user