diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/PatternMatchingTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/PatternMatchingTest.java index 039af65cdb9..8a2de753ab0 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/PatternMatchingTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/PatternMatchingTest.java @@ -81,4 +81,8 @@ public final class PatternMatchingTest extends SingleFileTranslationTest { public void testWhenWithoutExpression() throws Exception { fooBoxTest(); } + + public void testWhenWithOnlyElse() throws Exception { + fooBoxTest(); + } } \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java index 0fd7f29bb8f..a59539d32d7 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java @@ -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 createTemporary(@Nullable JsExpression initExpression) { + public Pair createTemporary(@Nullable JsExpression initExpression) { JsVar var = new JsVar(currentScope.declareTemporary(), initExpression); - return new Pair(var, var.getName().makeRef()); + return Pair.create(var, (JsExpression) new JsNameRef(var.getName())); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java index bc190e3f32d..a39eefdeded 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -89,7 +89,9 @@ public final class ExpressionVisitor extends TranslatorVisitor { 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 { } @Override - @NotNull + @Nullable public JsNode visitWhenExpression(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { return Translation.translateWhenExpression(expression, context); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java index 6d4071b6d96..31fccce0a60 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/WhenTranslator.java @@ -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 expressionToMatch; + + @Nullable + private Pair 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 translateEntries() { - List entries = new ArrayList(); + private void translateAsStatement(List 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 initExpressions = Lists.newArrayList(dummyCounter.assignmentExpression()); - if (expressionToMatch != null) { - initExpressions.add(expressionToMatch.assignmentExpression()); + private void addTempVarsStatement(List 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 conditions = new ArrayList(); - for (JetWhenCondition condition : entry.getConditions()) { - conditions.add(translateCondition(condition)); - } - return anyOfThemIsTrue(conditions); - } + JetWhenCondition[] conditions = entry.getConditions(); - @NotNull - private static JsExpression anyOfThemIsTrue(List 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()); - } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java index 16372cd99c0..437ad5c8685 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/foreach/IteratorForTranslator.java @@ -33,7 +33,7 @@ import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange; public final class IteratorForTranslator extends ForTranslator { @NotNull - private final Pair iterator; + private final Pair iterator; @NotNull public static JsStatement doTranslate(@NotNull JetForExpression expression, diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java index 4308f2c4fde..f474310c1a4 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/general/Translation.java @@ -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); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index a2b8ab346f6..5a200ca1e8d 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -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 translateArgumentList(@NotNull TranslationContext context, @NotNull List 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 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); + } + } } diff --git a/js/js.translator/testFiles/patternMatching/cases/whenWithOnlyElse.kt b/js/js.translator/testFiles/patternMatching/cases/whenWithOnlyElse.kt new file mode 100644 index 00000000000..2339c758d84 --- /dev/null +++ b/js/js.translator/testFiles/patternMatching/cases/whenWithOnlyElse.kt @@ -0,0 +1,9 @@ +package foo + +fun box(): Boolean { + var a = 0 + when (a) { + else -> a = 2 + } + return a == 2 +}