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 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 }