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 {
fooBoxTest();
}
public void testWhenWithOnlyElse() throws Exception {
fooBoxTest();
}
}
@@ -66,10 +66,11 @@ public final class DynamicContext {
return TemporaryVariable.create(temporaryName, initExpression);
}
//TODO: replace return type to make it more readable
@NotNull
public Pair<JsVar, JsNameRef> createTemporary(@Nullable JsExpression initExpression) {
public Pair<JsVar, JsExpression> createTemporary(@Nullable JsExpression 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
@@ -89,7 +89,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
"should be of type JetExpression";
JsNode jsNode = statement.accept(this, blockContext);
jsBlock.getStatements().add(convertToStatement(jsNode));
if (jsNode != null) {
jsBlock.getStatements().add(convertToStatement(jsNode));
}
}
return jsBlock;
}
@@ -286,7 +288,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
}
@Override
@NotNull
@Nullable
public JsNode visitWhenExpression(@NotNull JetWhenExpression expression,
@NotNull TranslationContext context) {
return Translation.translateWhenExpression(expression, context);
@@ -16,115 +16,113 @@
package org.jetbrains.k2js.translate.expression;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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.general.AbstractTranslator;
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.LastExpressionMutator;
import java.util.ArrayList;
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 {
@NotNull
@Nullable
public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext 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
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) {
super(context);
this.whenExpression = expression;
JsExpression expressionToMatch = translateExpressionToMatch(whenExpression);
this.expressionToMatch = expressionToMatch != null ? context.declareTemporary(expressionToMatch) : null;
this.dummyCounter = context.declareTemporary(program().getNumberLiteral(0));
this.result = context.declareTemporary(JsLiteral.NULL);
whenExpression = expression;
JetExpression subject = expression.getSubjectExpression();
if (subject != null) {
expressionToMatch = TranslationUtils.createTemporaryIfNeed(Translation.translateAsExpression(subject, context()), context);
}
else {
expressionToMatch = null;
}
}
@NotNull
JsNode translate() {
JsFor resultingFor = generateDummyFor();
resultingFor.setBody(new JsBlock(translateEntries()));
context().addStatementToCurrentBlock(resultingFor);
return result.reference();
@Nullable
private JsNode translateAsExpression() {
result = context().dynamicContext().createTemporary(null);
translateAsStatement(context().dynamicContext().jsBlock().getStatements());
return result.second;
}
@NotNull
private List<JsStatement> translateEntries() {
List<JsStatement> entries = new ArrayList<JsStatement>();
private void translateAsStatement(List<JsStatement> statements) {
addTempVarsStatement(statements);
JsIf prevIf = null;
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 JsStatement surroundWithDummyIf(@NotNull JsStatement entryStatement) {
JsNumberLiteral jsEntryNumber = program().getNumberLiteral(this.currentEntryNumber);
JsExpression stepNumberEqualsCurrentEntryNumber = equality(dummyCounter.reference(), jsEntryNumber);
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());
private void addTempVarsStatement(List<JsStatement> statements) {
JsVars vars = new JsVars();
if (expressionToMatch != null && expressionToMatch.first != null) {
vars.add(expressionToMatch.first);
}
return newSequence(initExpressions);
}
@NotNull
private JsBinaryOperation generateConditionStatement() {
JsNumberLiteral entriesNumber = program().getNumberLiteral(whenExpression.getEntries().size());
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;
if (result != null) {
vars.add(result.first);
}
if (!vars.isEmpty()) {
statements.add(vars);
}
JsExpression condition = translateConditions(entry);
return new JsIf(condition, addDummyBreakIfNeed(statementToExecute), null);
}
@NotNull
JsStatement withReturnValueCaptured(@NotNull JsNode node) {
return convertToStatement(LastExpressionMutator.mutateLastExpression(node,
new AssignToExpressionMutator(result.reference())));
private JsStatement withReturnValueCaptured(@NotNull JsNode node) {
return result == null
? convertToStatement(node)
: LastExpressionMutator.mutateLastExpression(node, new AssignToExpressionMutator(result.second));
}
@NotNull
@@ -136,32 +134,20 @@ public final class WhenTranslator extends AbstractTranslator {
@NotNull
private JsExpression translateConditions(@NotNull JetWhenEntry entry) {
List<JsExpression> conditions = new ArrayList<JsExpression>();
for (JetWhenCondition condition : entry.getConditions()) {
conditions.add(translateCondition(condition));
}
return anyOfThemIsTrue(conditions);
}
JetWhenCondition[] conditions = entry.getConditions();
@NotNull
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;
}
assert conditions.length > 0 : "When entry (not else) should have at least one condition";
@NotNull
private static JsExpression addCaseCondition(@Nullable JsExpression current, @NotNull JsExpression condition) {
if (current == null) {
return condition;
if (conditions.length == 1) {
return translateCondition(conditions[0]);
}
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
@@ -172,11 +158,6 @@ public final class WhenTranslator extends AbstractTranslator {
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
private JsExpression translatePatternCondition(@NotNull JetWhenCondition condition) {
JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition);
@@ -224,7 +205,7 @@ public final class WhenTranslator extends AbstractTranslator {
@Nullable
private JsExpression getExpressionToMatch() {
return expressionToMatch != null ? expressionToMatch.reference() : null;
return expressionToMatch != null ? expressionToMatch.second : null;
}
private static boolean isNegated(@NotNull JetWhenCondition condition) {
@@ -233,13 +214,4 @@ public final class WhenTranslator extends AbstractTranslator {
}
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 {
@NotNull
private final Pair<JsVars.JsVar, JsNameRef> iterator;
private final Pair<JsVars.JsVar, JsExpression> iterator;
@NotNull
public static JsStatement doTranslate(@NotNull JetForExpression expression,
@@ -118,7 +118,7 @@ public final class Translation {
return convertToStatement(translateExpression(expression, context));
}
@NotNull
@Nullable
public static JsNode translateWhenExpression(@NotNull JetWhenExpression expression,
@NotNull TranslationContext context) {
return WhenTranslator.translate(expression, context);
@@ -17,6 +17,7 @@
package org.jetbrains.k2js.translate.utils;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.openapi.util.Pair;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -74,6 +75,7 @@ public final class TranslationUtils {
JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ;
return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL);
}
@NotNull
public static List<JsExpression> translateArgumentList(@NotNull TranslationContext context,
@NotNull List<? extends ValueArgument> jetArguments) {
@@ -168,4 +170,23 @@ public final class TranslationUtils {
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
}