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 {
|
public final class JsParameter extends SourceInfoAwareJsNode implements HasName {
|
||||||
@NotNull
|
@NotNull
|
||||||
private final JsName name;
|
private final JsName name;
|
||||||
|
private boolean hasDefaultValue = false;
|
||||||
|
|
||||||
public JsParameter(@NotNull JsName name) {
|
public JsParameter(@NotNull JsName name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@@ -30,6 +31,14 @@ public final class JsParameter extends SourceInfoAwareJsNode implements HasName
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasDefaultValue() {
|
||||||
|
return hasDefaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasDefaultValue(boolean hasDefaultValue) {
|
||||||
|
this.hasDefaultValue = hasDefaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(JsVisitor v) {
|
public void accept(JsVisitor v) {
|
||||||
v.visitParameter(this);
|
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 com.intellij.util.containers.ContainerUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import static org.jetbrains.k2js.inline.InlinePackage.needToAlias;
|
import static org.jetbrains.k2js.inline.InlinePackage.*;
|
||||||
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 java.util.*;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
class FunctionInlineMutator {
|
class FunctionInlineMutator {
|
||||||
private static final String BREAK_LABEL = "break_inlined";
|
private static final String BREAK_LABEL = "break_inlined";
|
||||||
@@ -77,8 +74,12 @@ class FunctionInlineMutator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void process() {
|
private void process() {
|
||||||
|
List<JsExpression> arguments = getArguments();
|
||||||
|
List<JsParameter> parameters = getParameters();
|
||||||
|
|
||||||
replaceThis();
|
replaceThis();
|
||||||
aliasArgumentsIfNeeded(renamingContext, getArguments(), getParameters());
|
removeRedundantDefaultInitializers(arguments, parameters, body);
|
||||||
|
aliasArgumentsIfNeeded(renamingContext, arguments, parameters);
|
||||||
renameLocals(renamingContext, invokedFunction);
|
renameLocals(renamingContext, invokedFunction);
|
||||||
applyRenaming();
|
applyRenaming();
|
||||||
replaceReturns();
|
replaceReturns();
|
||||||
|
|||||||
+3
-1
@@ -130,7 +130,9 @@ public final class FunctionTranslator extends AbstractTranslator {
|
|||||||
|
|
||||||
public static void addParameters(List<JsParameter> list, FunctionDescriptor descriptor, TranslationContext context) {
|
public static void addParameters(List<JsParameter> list, FunctionDescriptor descriptor, TranslationContext context) {
|
||||||
for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) {
|
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