001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.k2js.translate.expression; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import com.intellij.openapi.util.Pair; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.annotations.Nullable; 023import org.jetbrains.jet.lang.psi.*; 024import org.jetbrains.k2js.translate.context.TranslationContext; 025import org.jetbrains.k2js.translate.general.AbstractTranslator; 026import org.jetbrains.k2js.translate.general.Translation; 027import org.jetbrains.k2js.translate.utils.BindingUtils; 028import org.jetbrains.k2js.translate.utils.TranslationUtils; 029import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator; 030import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator; 031 032import java.util.List; 033 034import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement; 035import static org.jetbrains.k2js.translate.utils.JsAstUtils.negated; 036 037public final class WhenTranslator extends AbstractTranslator { 038 @Nullable 039 public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { 040 WhenTranslator translator = new WhenTranslator(expression, context); 041 042 if (BindingUtils.isStatement(context.bindingContext(), expression)) { 043 translator.translateAsStatement(context.dynamicContext().jsBlock().getStatements()); 044 return null; 045 } 046 047 return translator.translateAsExpression(); 048 } 049 050 @NotNull 051 private final JetWhenExpression whenExpression; 052 053 @Nullable 054 private final Pair<JsVars.JsVar, JsExpression> expressionToMatch; 055 056 @Nullable 057 private Pair<JsVars.JsVar, JsExpression> result; 058 059 private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { 060 super(context); 061 062 whenExpression = expression; 063 064 JetExpression subject = expression.getSubjectExpression(); 065 if (subject != null) { 066 expressionToMatch = TranslationUtils.createTemporaryIfNeed(Translation.translateAsExpression(subject, context()), context); 067 } 068 else { 069 expressionToMatch = null; 070 } 071 } 072 073 @Nullable 074 private JsNode translateAsExpression() { 075 result = context().dynamicContext().createTemporary(null); 076 translateAsStatement(context().dynamicContext().jsBlock().getStatements()); 077 return result.second; 078 } 079 080 private void translateAsStatement(List<JsStatement> statements) { 081 addTempVarsStatement(statements); 082 083 JsIf prevIf = null; 084 for (JetWhenEntry entry : whenExpression.getEntries()) { 085 JsStatement statement = withReturnValueCaptured(translateEntryExpression(entry)); 086 if (entry.isElse()) { 087 if (prevIf == null) { 088 statements.add(statement); 089 } 090 else { 091 prevIf.setElseStatement(statement); 092 } 093 break; 094 } 095 096 JsIf ifStatement = new JsIf(translateConditions(entry), statement); 097 if (prevIf == null) { 098 statements.add(ifStatement); 099 } 100 else { 101 prevIf.setElseStatement(ifStatement); 102 } 103 prevIf = ifStatement; 104 } 105 } 106 107 private void addTempVarsStatement(List<JsStatement> statements) { 108 JsVars vars = new JsVars(); 109 if (expressionToMatch != null && expressionToMatch.first != null) { 110 vars.add(expressionToMatch.first); 111 } 112 if (result != null) { 113 vars.add(result.first); 114 } 115 116 if (!vars.isEmpty()) { 117 statements.add(vars); 118 } 119 } 120 121 @NotNull 122 private JsStatement withReturnValueCaptured(@NotNull JsNode node) { 123 return result == null 124 ? convertToStatement(node) 125 : LastExpressionMutator.mutateLastExpression(node, new AssignToExpressionMutator(result.second)); 126 } 127 128 @NotNull 129 private JsNode translateEntryExpression(@NotNull JetWhenEntry entry) { 130 JetExpression expressionToExecute = entry.getExpression(); 131 assert expressionToExecute != null : "WhenEntry should have whenExpression to execute."; 132 return Translation.translateExpression(expressionToExecute, context()); 133 } 134 135 @NotNull 136 private JsExpression translateConditions(@NotNull JetWhenEntry entry) { 137 JetWhenCondition[] conditions = entry.getConditions(); 138 139 assert conditions.length > 0 : "When entry (not else) should have at least one condition"; 140 141 if (conditions.length == 1) { 142 return translateCondition(conditions[0]); 143 } 144 145 JsExpression result = translateCondition(conditions[0]); 146 for (int i = 1; i < conditions.length; i++) { 147 result = new JsBinaryOperation(JsBinaryOperator.OR, translateCondition(conditions[i]), result); 148 } 149 150 return result; 151 } 152 153 @NotNull 154 private JsExpression translateCondition(@NotNull JetWhenCondition condition) { 155 if ((condition instanceof JetWhenConditionIsPattern) || (condition instanceof JetWhenConditionWithExpression)) { 156 return translatePatternCondition(condition); 157 } 158 throw new AssertionError("Unsupported when condition " + condition.getClass()); 159 } 160 161 @NotNull 162 private JsExpression translatePatternCondition(@NotNull JetWhenCondition condition) { 163 JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition); 164 if (isNegated(condition)) { 165 return negated(patternMatchExpression); 166 } 167 return patternMatchExpression; 168 } 169 170 @NotNull 171 private JsExpression translateWhenConditionToBooleanExpression(@NotNull JetWhenCondition condition) { 172 if (condition instanceof JetWhenConditionIsPattern) { 173 return translateIsCondition((JetWhenConditionIsPattern) condition); 174 } 175 else if (condition instanceof JetWhenConditionWithExpression) { 176 return translateExpressionCondition((JetWhenConditionWithExpression) condition); 177 } 178 throw new AssertionError("Wrong type of JetWhenCondition"); 179 } 180 181 @NotNull 182 private JsExpression translateIsCondition(@NotNull JetWhenConditionIsPattern conditionIsPattern) { 183 JsExpression expressionToMatch = getExpressionToMatch(); 184 assert expressionToMatch != null : "An is-check is not allowed in when() without subject."; 185 186 JetTypeReference typeReference = conditionIsPattern.getTypeRef(); 187 assert typeReference != null : "An is-check must have a type reference."; 188 189 return Translation.patternTranslator(context()).translateIsCheck(expressionToMatch, typeReference); 190 } 191 192 @NotNull 193 private JsExpression translateExpressionCondition(@NotNull JetWhenConditionWithExpression condition) { 194 JetExpression patternExpression = condition.getExpression(); 195 assert patternExpression != null : "Expression pattern should have an expression."; 196 197 JsExpression expressionToMatch = getExpressionToMatch(); 198 if (expressionToMatch == null) { 199 return Translation.patternTranslator(context()).translateExpressionForExpressionPattern(patternExpression); 200 } 201 else { 202 return Translation.patternTranslator(context()).translateExpressionPattern(expressionToMatch, patternExpression); 203 } 204 } 205 206 @Nullable 207 private JsExpression getExpressionToMatch() { 208 return expressionToMatch != null ? expressionToMatch.second : null; 209 } 210 211 private static boolean isNegated(@NotNull JetWhenCondition condition) { 212 if (condition instanceof JetWhenConditionIsPattern) { 213 return ((JetWhenConditionIsPattern)condition).isNegated(); 214 } 215 return false; 216 } 217}