Translate when as if-else instead for loop.
This commit is contained in:
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user