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