Translate when as if-else instead for loop.

This commit is contained in:
develar
2013-06-21 16:21:28 +04:00
committed by Zalim Bashorov
parent bc043fb964
commit 9cd22942e2
8 changed files with 124 additions and 115 deletions
@@ -81,4 +81,8 @@ public final class PatternMatchingTest extends SingleFileTranslationTest {
public void testWhenWithoutExpression() throws Exception { public void testWhenWithoutExpression() throws Exception {
fooBoxTest(); fooBoxTest();
} }
public void testWhenWithOnlyElse() throws Exception {
fooBoxTest();
}
} }
@@ -66,10 +66,11 @@ public final class DynamicContext {
return TemporaryVariable.create(temporaryName, initExpression); return TemporaryVariable.create(temporaryName, initExpression);
} }
//TODO: replace return type to make it more readable
@NotNull @NotNull
public Pair<JsVar, JsNameRef> createTemporary(@Nullable JsExpression initExpression) { public Pair<JsVar, JsExpression> createTemporary(@Nullable JsExpression initExpression) {
JsVar var = new JsVar(currentScope.declareTemporary(), initExpression); JsVar var = new JsVar(currentScope.declareTemporary(), initExpression);
return new Pair<JsVar, JsNameRef>(var, var.getName().makeRef()); return Pair.create(var, (JsExpression) new JsNameRef(var.getName()));
} }
@NotNull @NotNull
@@ -89,7 +89,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
assert statement instanceof JetExpression : "Elements in JetBlockExpression " + assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
"should be of type JetExpression"; "should be of type JetExpression";
JsNode jsNode = statement.accept(this, blockContext); JsNode jsNode = statement.accept(this, blockContext);
jsBlock.getStatements().add(convertToStatement(jsNode)); if (jsNode != null) {
jsBlock.getStatements().add(convertToStatement(jsNode));
}
} }
return jsBlock; return jsBlock;
} }
@@ -286,7 +288,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
} }
@Override @Override
@NotNull @Nullable
public JsNode visitWhenExpression(@NotNull JetWhenExpression expression, public JsNode visitWhenExpression(@NotNull JetWhenExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return Translation.translateWhenExpression(expression, context); return Translation.translateWhenExpression(expression, context);
@@ -16,115 +16,113 @@
package org.jetbrains.k2js.translate.expression; package org.jetbrains.k2js.translate.expression;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator; import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator; import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.negated;
public final class WhenTranslator extends AbstractTranslator { public final class WhenTranslator extends AbstractTranslator {
@Nullable
@NotNull
public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) {
WhenTranslator translator = new WhenTranslator(expression, context); WhenTranslator translator = new WhenTranslator(expression, context);
return translator.translate();
if (BindingUtils.isStatement(context.bindingContext(), expression)) {
translator.translateAsStatement(context.dynamicContext().jsBlock().getStatements());
return null;
}
return translator.translateAsExpression();
} }
@NotNull @NotNull
private final JetWhenExpression whenExpression; private final JetWhenExpression whenExpression;
@Nullable
private final TemporaryVariable expressionToMatch;
@NotNull
private final TemporaryVariable dummyCounter;
@NotNull
private final TemporaryVariable result;
private int currentEntryNumber = 0; @Nullable
private final Pair<JsVars.JsVar, JsExpression> expressionToMatch;
@Nullable
private Pair<JsVars.JsVar, JsExpression> result;
private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) {
super(context); super(context);
this.whenExpression = expression;
JsExpression expressionToMatch = translateExpressionToMatch(whenExpression); whenExpression = expression;
this.expressionToMatch = expressionToMatch != null ? context.declareTemporary(expressionToMatch) : null;
this.dummyCounter = context.declareTemporary(program().getNumberLiteral(0)); JetExpression subject = expression.getSubjectExpression();
this.result = context.declareTemporary(JsLiteral.NULL); if (subject != null) {
expressionToMatch = TranslationUtils.createTemporaryIfNeed(Translation.translateAsExpression(subject, context()), context);
}
else {
expressionToMatch = null;
}
} }
@NotNull @Nullable
JsNode translate() { private JsNode translateAsExpression() {
JsFor resultingFor = generateDummyFor(); result = context().dynamicContext().createTemporary(null);
resultingFor.setBody(new JsBlock(translateEntries())); translateAsStatement(context().dynamicContext().jsBlock().getStatements());
context().addStatementToCurrentBlock(resultingFor); return result.second;
return result.reference();
} }
@NotNull private void translateAsStatement(List<JsStatement> statements) {
private List<JsStatement> translateEntries() { addTempVarsStatement(statements);
List<JsStatement> entries = new ArrayList<JsStatement>();
JsIf prevIf = null;
for (JetWhenEntry entry : whenExpression.getEntries()) { for (JetWhenEntry entry : whenExpression.getEntries()) {
entries.add(surroundWithDummyIf(translateEntry(entry))); JsStatement statement = withReturnValueCaptured(translateEntryExpression(entry));
if (entry.isElse()) {
if (prevIf == null) {
statements.add(statement);
}
else {
prevIf.setElseStatement(statement);
}
break;
}
JsIf ifStatement = new JsIf(translateConditions(entry), statement);
if (prevIf == null) {
statements.add(ifStatement);
}
else {
prevIf.setElseStatement(ifStatement);
}
prevIf = ifStatement;
} }
return entries;
} }
@NotNull private void addTempVarsStatement(List<JsStatement> statements) {
private JsStatement surroundWithDummyIf(@NotNull JsStatement entryStatement) { JsVars vars = new JsVars();
JsNumberLiteral jsEntryNumber = program().getNumberLiteral(this.currentEntryNumber); if (expressionToMatch != null && expressionToMatch.first != null) {
JsExpression stepNumberEqualsCurrentEntryNumber = equality(dummyCounter.reference(), jsEntryNumber); vars.add(expressionToMatch.first);
currentEntryNumber++;
return new JsIf(stepNumberEqualsCurrentEntryNumber, entryStatement, null);
}
@NotNull
private JsFor generateDummyFor() {
return new JsFor(generateInitExpressions(), generateConditionStatement(), generateIncrementStatement());
}
@NotNull
private JsExpression generateInitExpressions() {
List<JsExpression> initExpressions = Lists.newArrayList(dummyCounter.assignmentExpression());
if (expressionToMatch != null) {
initExpressions.add(expressionToMatch.assignmentExpression());
} }
return newSequence(initExpressions); if (result != null) {
} vars.add(result.first);
}
@NotNull
private JsBinaryOperation generateConditionStatement() { if (!vars.isEmpty()) {
JsNumberLiteral entriesNumber = program().getNumberLiteral(whenExpression.getEntries().size()); statements.add(vars);
return new JsBinaryOperation(JsBinaryOperator.LT, dummyCounter.reference(), entriesNumber);
}
@NotNull
private JsPrefixOperation generateIncrementStatement() {
return new JsPrefixOperation(JsUnaryOperator.INC, dummyCounter.reference());
}
@NotNull
private JsStatement translateEntry(@NotNull JetWhenEntry entry) {
JsStatement statementToExecute = withReturnValueCaptured(translateEntryExpression(entry));
if (entry.isElse()) {
return statementToExecute;
} }
JsExpression condition = translateConditions(entry);
return new JsIf(condition, addDummyBreakIfNeed(statementToExecute), null);
} }
@NotNull @NotNull
JsStatement withReturnValueCaptured(@NotNull JsNode node) { private JsStatement withReturnValueCaptured(@NotNull JsNode node) {
return convertToStatement(LastExpressionMutator.mutateLastExpression(node, return result == null
new AssignToExpressionMutator(result.reference()))); ? convertToStatement(node)
: LastExpressionMutator.mutateLastExpression(node, new AssignToExpressionMutator(result.second));
} }
@NotNull @NotNull
@@ -136,32 +134,20 @@ public final class WhenTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression translateConditions(@NotNull JetWhenEntry entry) { private JsExpression translateConditions(@NotNull JetWhenEntry entry) {
List<JsExpression> conditions = new ArrayList<JsExpression>(); JetWhenCondition[] conditions = entry.getConditions();
for (JetWhenCondition condition : entry.getConditions()) {
conditions.add(translateCondition(condition));
}
return anyOfThemIsTrue(conditions);
}
@NotNull assert conditions.length > 0 : "When entry (not else) should have at least one condition";
private static JsExpression anyOfThemIsTrue(List<JsExpression> conditions) {
assert !conditions.isEmpty() : "When entry (not else) should have at least one condition";
JsExpression current = null;
for (JsExpression condition : conditions) {
current = addCaseCondition(current, condition);
}
assert current != null;
return current;
}
@NotNull if (conditions.length == 1) {
private static JsExpression addCaseCondition(@Nullable JsExpression current, @NotNull JsExpression condition) { return translateCondition(conditions[0]);
if (current == null) {
return condition;
} }
else {
return or(current, condition); JsExpression result = translateCondition(conditions[0]);
for (int i = 1; i < conditions.length; i++) {
result = new JsBinaryOperation(JsBinaryOperator.OR, translateCondition(conditions[i]), result);
} }
return result;
} }
@NotNull @NotNull
@@ -172,11 +158,6 @@ public final class WhenTranslator extends AbstractTranslator {
throw new AssertionError("Unsupported when condition " + condition.getClass()); throw new AssertionError("Unsupported when condition " + condition.getClass());
} }
@NotNull
private static JsStatement addDummyBreakIfNeed(@NotNull JsStatement statement) {
return statement instanceof JsReturn ? statement : new JsBlock(statement, new JsBreak());
}
@NotNull @NotNull
private JsExpression translatePatternCondition(@NotNull JetWhenCondition condition) { private JsExpression translatePatternCondition(@NotNull JetWhenCondition condition) {
JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition); JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition);
@@ -224,7 +205,7 @@ public final class WhenTranslator extends AbstractTranslator {
@Nullable @Nullable
private JsExpression getExpressionToMatch() { private JsExpression getExpressionToMatch() {
return expressionToMatch != null ? expressionToMatch.reference() : null; return expressionToMatch != null ? expressionToMatch.second : null;
} }
private static boolean isNegated(@NotNull JetWhenCondition condition) { private static boolean isNegated(@NotNull JetWhenCondition condition) {
@@ -233,13 +214,4 @@ public final class WhenTranslator extends AbstractTranslator {
} }
return false; return false;
} }
@Nullable
private JsExpression translateExpressionToMatch(@NotNull JetWhenExpression expression) {
JetExpression subject = expression.getSubjectExpression();
if (subject == null) {
return null;
}
return Translation.translateAsExpression(subject, context());
}
} }
@@ -33,7 +33,7 @@ import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange;
public final class IteratorForTranslator extends ForTranslator { public final class IteratorForTranslator extends ForTranslator {
@NotNull @NotNull
private final Pair<JsVars.JsVar, JsNameRef> iterator; private final Pair<JsVars.JsVar, JsExpression> iterator;
@NotNull @NotNull
public static JsStatement doTranslate(@NotNull JetForExpression expression, public static JsStatement doTranslate(@NotNull JetForExpression expression,
@@ -118,7 +118,7 @@ public final class Translation {
return convertToStatement(translateExpression(expression, context)); return convertToStatement(translateExpression(expression, context));
} }
@NotNull @Nullable
public static JsNode translateWhenExpression(@NotNull JetWhenExpression expression, public static JsNode translateWhenExpression(@NotNull JetWhenExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return WhenTranslator.translate(expression, context); return WhenTranslator.translate(expression, context);
@@ -17,6 +17,7 @@
package org.jetbrains.k2js.translate.utils; package org.jetbrains.k2js.translate.utils;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.openapi.util.Pair;
import com.intellij.util.SmartList; import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -74,6 +75,7 @@ public final class TranslationUtils {
JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ; JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ;
return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL); return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL);
} }
@NotNull @NotNull
public static List<JsExpression> translateArgumentList(@NotNull TranslationContext context, public static List<JsExpression> translateArgumentList(@NotNull TranslationContext context,
@NotNull List<? extends ValueArgument> jetArguments) { @NotNull List<? extends ValueArgument> jetArguments) {
@@ -168,4 +170,23 @@ public final class TranslationUtils {
return false; return false;
} }
public static boolean isCacheNeeded(@NotNull JsExpression expression) {
return !(expression instanceof JsLiteral) &&
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
}
@NotNull
public static Pair<JsVars.JsVar, JsExpression> createTemporaryIfNeed(
@NotNull JsExpression expression,
@NotNull TranslationContext context
) {
// don't create temp variable for simple expression
if (isCacheNeeded(expression)) {
return context.dynamicContext().createTemporary(expression);
}
else {
return Pair.create(null, expression);
}
}
} }
@@ -0,0 +1,9 @@
package foo
fun box(): Boolean {
var a = 0
when (a) {
else -> a = 2
}
return a == 2
}