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