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 org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
023 import org.jetbrains.jet.lang.psi.*;
024 import org.jetbrains.jet.lang.resolve.BindingContext;
025 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
026 import org.jetbrains.jet.lang.resolve.constants.NullValue;
027 import org.jetbrains.k2js.translate.context.TemporaryVariable;
028 import org.jetbrains.k2js.translate.context.TranslationContext;
029 import org.jetbrains.k2js.translate.declaration.ClassTranslator;
030 import org.jetbrains.k2js.translate.expression.foreach.ForTranslator;
031 import org.jetbrains.k2js.translate.general.Translation;
032 import org.jetbrains.k2js.translate.general.TranslatorVisitor;
033 import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
034 import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator;
035 import org.jetbrains.k2js.translate.reference.*;
036 import org.jetbrains.k2js.translate.utils.BindingUtils;
037 import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
038
039 import java.util.List;
040
041 import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression;
042 import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
043 import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
044 import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
045 import static org.jetbrains.k2js.translate.utils.PsiUtils.getObjectDeclarationName;
046 import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty;
047 import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
048
049 public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
050 @Override
051 @NotNull
052 public JsNode visitConstantExpression(@NotNull JetConstantExpression expression,
053 @NotNull TranslationContext context) {
054 CompileTimeConstant<?> compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression);
055 assert compileTimeValue != null;
056
057 if (compileTimeValue instanceof NullValue) {
058 return JsLiteral.NULL;
059 }
060
061 Object value = compileTimeValue.getValue();
062 if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
063 return context.program().getNumberLiteral(((Number) value).intValue());
064 }
065 else if (value instanceof Number) {
066 return context.program().getNumberLiteral(((Number) value).doubleValue());
067 }
068 else if (value instanceof Boolean) {
069 return JsLiteral.getBoolean((Boolean) value);
070 }
071
072 //TODO: test
073 if (value instanceof String) {
074 return context.program().getStringLiteral((String) value);
075 }
076 if (value instanceof Character) {
077 return context.program().getStringLiteral(value.toString());
078 }
079 throw new AssertionError(message(expression, "Unsupported constant expression"));
080 }
081
082 @Override
083 @NotNull
084 public JsNode visitBlockExpression(@NotNull JetBlockExpression jetBlock, @NotNull TranslationContext context) {
085 List<JetElement> statements = jetBlock.getStatements();
086 JsBlock jsBlock = new JsBlock();
087 TranslationContext blockContext = context.innerBlock(jsBlock);
088 for (JetElement statement : statements) {
089 assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
090 "should be of type JetExpression";
091 JsNode jsNode = statement.accept(this, blockContext);
092 if (jsNode != null) {
093 jsBlock.getStatements().add(convertToStatement(jsNode));
094 }
095 }
096 return jsBlock;
097 }
098
099 @Override
100 @NotNull
101 public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
102 @NotNull TranslationContext context) {
103 JetExpression returnedExpression = jetReturnExpression.getReturnedExpression();
104 if (returnedExpression != null) {
105 JsExpression jsExpression = translateAsExpression(returnedExpression, context);
106 return new JsReturn(jsExpression);
107 }
108 return new JsReturn();
109 }
110
111 @Override
112 @NotNull
113 public JsNode visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression,
114 @NotNull TranslationContext context) {
115 JetExpression expressionInside = expression.getExpression();
116 if (expressionInside != null) {
117 return expressionInside.accept(this, context);
118 }
119 return context.program().getEmptyStmt();
120 }
121
122 @Override
123 @NotNull
124 public JsNode visitBinaryExpression(@NotNull JetBinaryExpression expression,
125 @NotNull TranslationContext context) {
126 return BinaryOperationTranslator.translate(expression, context);
127 }
128
129 @Override
130 @NotNull
131 // assume it is a local variable declaration
132 public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) {
133 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), expression);
134 JsName jsPropertyName = context.getNameForDescriptor(descriptor);
135 JsExpression jsInitExpression = translateInitializerForProperty(expression, context);
136 return newVar(jsPropertyName, jsInitExpression);
137 }
138
139 @Override
140 @NotNull
141 public JsNode visitCallExpression(@NotNull JetCallExpression expression,
142 @NotNull TranslationContext context) {
143 return CallExpressionTranslator.translate(expression, null, CallType.NORMAL, context);
144 }
145
146 @Override
147 @NotNull
148 public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) {
149 JsExpression testExpression = translateConditionExpression(expression.getCondition(), context);
150 JetExpression thenExpression = expression.getThen();
151 JetExpression elseExpression = expression.getElse();
152 assert thenExpression != null;
153 JsNode thenNode = thenExpression.accept(this, context);
154 JsNode elseNode = elseExpression == null ? null : elseExpression.accept(this, context);
155
156 boolean isKotlinStatement = BindingUtils.isStatement(context.bindingContext(), expression);
157 boolean canBeJsExpression = thenNode instanceof JsExpression && elseNode instanceof JsExpression;
158 if (!isKotlinStatement && canBeJsExpression) {
159 return new JsConditional(testExpression, convertToExpression(thenNode), convertToExpression(elseNode));
160 }
161 else {
162 JsIf ifStatement = new JsIf(testExpression, convertToStatement(thenNode), elseNode == null ? null : convertToStatement(elseNode));
163 if (isKotlinStatement) {
164 return ifStatement;
165 }
166
167 TemporaryVariable result = context.declareTemporary(null);
168 AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
169 context.addStatementToCurrentBlock(mutateLastExpression(ifStatement, saveResultToTemporaryMutator));
170 return result.reference();
171 }
172 }
173
174 @Override
175 @NotNull
176 public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression,
177 @NotNull TranslationContext context) {
178 return ReferenceTranslator.translateSimpleName(expression, context);
179 }
180
181
182 @NotNull
183 private JsStatement translateNullableExpressionAsNotNullStatement(@Nullable JetExpression nullableExpression,
184 @NotNull TranslationContext context) {
185 if (nullableExpression == null) {
186 return context.program().getEmptyStmt();
187 }
188 return convertToStatement(nullableExpression.accept(this, context));
189 }
190
191 @NotNull
192 private JsExpression translateConditionExpression(@Nullable JetExpression expression,
193 @NotNull TranslationContext context) {
194 JsExpression jsCondition = translateNullableExpression(expression, context);
195 assert (jsCondition != null) : "Condition should not be empty";
196 return convertToExpression(jsCondition);
197 }
198
199 @Nullable
200 private JsExpression translateNullableExpression(@Nullable JetExpression expression,
201 @NotNull TranslationContext context) {
202 if (expression == null) {
203 return null;
204 }
205 return convertToExpression(expression.accept(this, context));
206 }
207
208 @Override
209 @NotNull
210 public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) {
211 return createWhile(new JsWhile(), expression, context);
212 }
213
214 @Override
215 @NotNull
216 public JsNode visitDoWhileExpression(@NotNull JetDoWhileExpression expression, @NotNull TranslationContext context) {
217 return createWhile(new JsDoWhile(), expression, context);
218 }
219
220 private JsNode createWhile(@NotNull JsWhile result, @NotNull JetWhileExpressionBase expression, @NotNull TranslationContext context) {
221 result.setCondition(translateConditionExpression(expression.getCondition(), context));
222 result.setBody(translateNullableExpressionAsNotNullStatement(expression.getBody(), context));
223 return result;
224 }
225
226 @Override
227 @NotNull
228 public JsNode visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression,
229 @NotNull TranslationContext context) {
230 JsStringLiteral stringLiteral = resolveAsStringConstant(expression, context);
231 if (stringLiteral != null) {
232 return stringLiteral;
233 }
234 return resolveAsTemplate(expression, context);
235 }
236
237 @NotNull
238 private static JsNode resolveAsTemplate(@NotNull JetStringTemplateExpression expression,
239 @NotNull TranslationContext context) {
240 return StringTemplateTranslator.translate(expression, context);
241 }
242
243 @Nullable
244 private static JsStringLiteral resolveAsStringConstant(@NotNull JetExpression expression,
245 @NotNull TranslationContext context) {
246 Object value = getCompileTimeValue(context.bindingContext(), expression);
247 if (value == null) {
248 return null;
249 }
250 assert value instanceof String : "Compile time constant template should be a String constant.";
251 String constantString = (String) value;
252 return context.program().getStringLiteral(constantString);
253 }
254
255 @Override
256 @NotNull
257 public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression,
258 @NotNull TranslationContext context) {
259 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context);
260 }
261
262 @Override
263 @NotNull
264 public JsNode visitPrefixExpression(@NotNull JetPrefixExpression expression,
265 @NotNull TranslationContext context) {
266 return UnaryOperationTranslator.translate(expression, context);
267 }
268
269 @Override
270 @NotNull
271 public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression,
272 @NotNull TranslationContext context) {
273 return UnaryOperationTranslator.translate(expression, context);
274 }
275
276 @Override
277 @NotNull
278 public JsNode visitIsExpression(@NotNull JetIsExpression expression,
279 @NotNull TranslationContext context) {
280 return Translation.patternTranslator(context).translateIsExpression(expression);
281 }
282
283 @Override
284 @NotNull
285 public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression,
286 @NotNull TranslationContext context) {
287 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context);
288 }
289
290 @Override
291 @Nullable
292 public JsNode visitWhenExpression(@NotNull JetWhenExpression expression,
293 @NotNull TranslationContext context) {
294 return Translation.translateWhenExpression(expression, context);
295 }
296
297
298 @Override
299 @NotNull
300 public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression,
301 @NotNull TranslationContext context) {
302 // we actually do not care for types in js
303 return Translation.translateExpression(expression.getLeft(), context);
304 }
305
306 @Override
307 @NotNull
308 public JsNode visitBreakExpression(@NotNull JetBreakExpression expression,
309 @NotNull TranslationContext context) {
310 return new JsBreak();
311 }
312
313 @Override
314 @NotNull
315 public JsNode visitContinueExpression(@NotNull JetContinueExpression expression,
316 @NotNull TranslationContext context) {
317 return new JsContinue();
318 }
319
320 @Override
321 @NotNull
322 public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression,
323 @NotNull TranslationContext context) {
324 return context.literalFunctionTranslator().translate(expression);
325 }
326
327 @Override
328 @NotNull
329 public JsNode visitThisExpression(@NotNull JetThisExpression expression,
330 @NotNull TranslationContext context) {
331 DeclarationDescriptor thisExpression =
332 getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
333 assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
334 return context.getThisObject(thisExpression);
335 }
336
337 @Override
338 @NotNull
339 public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression,
340 @NotNull TranslationContext context) {
341 return AccessTranslationUtils.translateAsGet(expression, context);
342 }
343
344 @Override
345 @NotNull
346 public JsNode visitForExpression(@NotNull JetForExpression expression,
347 @NotNull TranslationContext context) {
348 return ForTranslator.translate(expression, context);
349 }
350
351 @Override
352 @NotNull
353 public JsNode visitTryExpression(@NotNull JetTryExpression expression,
354 @NotNull TranslationContext context) {
355 return TryTranslator.translate(expression, context);
356 }
357
358 @Override
359 @NotNull
360 public JsNode visitThrowExpression(@NotNull JetThrowExpression expression,
361 @NotNull TranslationContext context) {
362 JetExpression thrownExpression = expression.getThrownExpression();
363 assert thrownExpression != null : "Thrown expression must not be null";
364 return new JsThrow(translateAsExpression(thrownExpression, context));
365 }
366
367 @Override
368 @NotNull
369 public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression,
370 @NotNull TranslationContext context) {
371 return ClassTranslator.generateObjectLiteral(expression, context);
372 }
373
374 @Override
375 @NotNull
376 public JsNode visitObjectDeclaration(@NotNull JetObjectDeclaration expression,
377 @NotNull TranslationContext context) {
378 JetObjectDeclarationName objectDeclarationName = getObjectDeclarationName(expression);
379 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), objectDeclarationName);
380 JsName propertyName = context.getNameForDescriptor(descriptor);
381 JsExpression value = ClassTranslator.generateClassCreation(expression, context);
382 return newVar(propertyName, value);
383 }
384
385 @Override
386 @NotNull
387 public JsNode visitNamedFunction(@NotNull JetNamedFunction function,
388 @NotNull TranslationContext context) {
389 return FunctionTranslator.newInstance(function, context).translateAsLocalFunction();
390 }
391 }