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.descriptors.FunctionDescriptor;
024 import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
025 import org.jetbrains.jet.lang.psi.*;
026 import org.jetbrains.jet.lang.resolve.BindingContext;
027 import org.jetbrains.jet.lang.resolve.BindingContextUtils;
028 import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
029 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
030 import org.jetbrains.jet.lang.resolve.constants.NullValue;
031 import org.jetbrains.jet.lang.types.JetType;
032 import org.jetbrains.jet.lang.types.TypeUtils;
033 import org.jetbrains.jet.lexer.JetTokens;
034 import org.jetbrains.k2js.translate.context.TemporaryVariable;
035 import org.jetbrains.k2js.translate.context.TranslationContext;
036 import org.jetbrains.k2js.translate.declaration.ClassTranslator;
037 import org.jetbrains.k2js.translate.expression.loopTranslator.LoopTranslatorPackage;
038 import org.jetbrains.k2js.translate.general.Translation;
039 import org.jetbrains.k2js.translate.general.TranslatorVisitor;
040 import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
041 import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator;
042 import org.jetbrains.k2js.translate.reference.*;
043 import org.jetbrains.k2js.translate.utils.JsAstUtils;
044 import org.jetbrains.k2js.translate.utils.TranslationUtils;
045
046 import java.util.List;
047
048 import static org.jetbrains.jet.lang.resolve.BindingContextUtils.isVarCapturedInClosure;
049 import static org.jetbrains.k2js.translate.context.Namer.getCapturedVarAccessor;
050 import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression;
051 import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.translateAsFQReference;
052 import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
053 import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
054 import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement;
055 import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar;
056 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
057 import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty;
058
059 public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
060 @Override
061 @NotNull
062 public JsNode visitConstantExpression(@NotNull JetConstantExpression expression, @NotNull TranslationContext context) {
063 return translateConstantExpression(expression, context).source(expression);
064 }
065
066 @NotNull
067 private static JsNode translateConstantExpression(@NotNull JetConstantExpression expression, @NotNull TranslationContext context) {
068 CompileTimeConstant<?> compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression);
069
070 assert compileTimeValue != null : message(expression, "Expression is not compile time value: " + expression.getText() + " ");
071
072 if (compileTimeValue instanceof NullValue) {
073 return JsLiteral.NULL;
074 }
075
076 Object value = getCompileTimeValue(context.bindingContext(), expression, compileTimeValue);
077 if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
078 return context.program().getNumberLiteral(((Number) value).intValue());
079 }
080 else if (value instanceof Long) {
081 return JsAstUtils.newLong((Long) value, context);
082 }
083 else if (value instanceof Number) {
084 return context.program().getNumberLiteral(((Number) value).doubleValue());
085 }
086 else if (value instanceof Boolean) {
087 return JsLiteral.getBoolean((Boolean) value);
088 }
089
090 //TODO: test
091 if (value instanceof String) {
092 return context.program().getStringLiteral((String) value);
093 }
094 if (value instanceof Character) {
095 return context.program().getStringLiteral(value.toString());
096 }
097
098 throw new AssertionError(message(expression, "Unsupported constant expression: " + expression.getText() + " "));
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 for (JetElement statement : statements) {
107 assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
108 "should be of type JetExpression";
109 JsNode jsNode = Translation.translateExpression((JetExpression)statement, context, jsBlock);
110 JsStatement jsStatement = convertToStatement(jsNode);
111 if (!JsAstUtils.isEmptyStatement(jsStatement)) {
112 jsBlock.getStatements().add(jsStatement);
113 }
114 }
115 return jsBlock;
116 }
117
118 @Override
119 public JsNode visitMultiDeclaration(@NotNull JetMultiDeclaration multiDeclaration, @NotNull TranslationContext context) {
120 JetExpression jetInitializer = multiDeclaration.getInitializer();
121 assert jetInitializer != null : "Initializer for multi declaration must be not null";
122 JsExpression initializer = Translation.translateAsExpression(jetInitializer, context);
123 return MultiDeclarationTranslator.translate(multiDeclaration, context.scope().declareTemporary(), initializer, context);
124 }
125
126 @Override
127 @NotNull
128 public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
129 @NotNull TranslationContext context) {
130 JetExpression returned = jetReturnExpression.getReturnedExpression();
131 if (returned == null) {
132 return new JsReturn(null).source(jetReturnExpression);
133 }
134 JsExpression jsReturnExpression = translateAsExpression(returned, context);
135 if (JsAstUtils.isEmptyExpression(jsReturnExpression)) {
136 return context.getEmptyExpression();
137 }
138 return new JsReturn(jsReturnExpression).source(jetReturnExpression);
139 }
140
141 @Override
142 @NotNull
143 public JsNode visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression,
144 @NotNull TranslationContext context) {
145 JetExpression expressionInside = expression.getExpression();
146 if (expressionInside != null) {
147 return Translation.translateExpression(expressionInside, context);
148 }
149 return context.getEmptyStatement();
150 }
151
152 @Override
153 @NotNull
154 public JsNode visitBinaryExpression(@NotNull JetBinaryExpression expression,
155 @NotNull TranslationContext context) {
156 return BinaryOperationTranslator.translate(expression, context);
157 }
158
159 @Override
160 @NotNull
161 // assume it is a local variable declaration
162 public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) {
163 VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
164 JsExpression initializer = translateInitializerForProperty(expression, context);
165 if (initializer != null && JsAstUtils.isEmptyExpression(initializer)) {
166 return context.getEmptyExpression();
167 }
168
169 JsName name = context.getNameForDescriptor(descriptor);
170 if (isVarCapturedInClosure(context.bindingContext(), descriptor)) {
171 JsNameRef alias = getCapturedVarAccessor(name.makeRef());
172 initializer = JsAstUtils.wrapValue(alias, initializer == null ? JsLiteral.NULL : initializer);
173 }
174
175 return newVar(name, initializer).source(expression);
176 }
177
178 @Override
179 @NotNull
180 public JsNode visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, @NotNull TranslationContext context) {
181 return CallableReferenceTranslator.INSTANCE$.translate(expression, context);
182 }
183
184 @Override
185 @NotNull
186 public JsNode visitCallExpression(@NotNull JetCallExpression expression,
187 @NotNull TranslationContext context) {
188 if (InlinedCallExpressionTranslator.shouldBeInlined(expression, context) &&
189 BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext())) {
190 TemporaryVariable temporaryVariable = context.declareTemporary(null);
191 JsExpression callResult = CallExpressionTranslator.translate(expression, null, context).source(expression);
192 context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), callResult).makeStmt());
193 return temporaryVariable.reference();
194 } else {
195 return CallExpressionTranslator.translate(expression, null, context).source(expression);
196 }
197 }
198
199 @Override
200 @NotNull
201 public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) {
202 assert expression.getCondition() != null : "condition should not ne null: " + expression.getText();
203 JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context);
204 if (JsAstUtils.isEmptyExpression(testExpression)) {
205 return testExpression;
206 }
207
208 boolean isKotlinExpression = BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext());
209
210 JetExpression thenExpression = expression.getThen();
211 assert thenExpression != null : "then expression should not be null: " + expression.getText();
212 JetExpression elseExpression = expression.getElse();
213
214 JsStatement thenStatement = Translation.translateAsStatementAndMergeInBlockIfNeeded(thenExpression, context);
215 JsStatement elseStatement = (elseExpression != null) ? Translation.translateAsStatementAndMergeInBlockIfNeeded(elseExpression,
216 context) : null;
217
218 if (isKotlinExpression) {
219 JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement);
220 JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement);
221 boolean canBeJsExpression = jsThenExpression != null && jsElseExpression != null;
222 if (canBeJsExpression) {
223 return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression);
224 }
225 }
226 JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement);
227 return ifStatement.source(expression);
228 }
229
230 @Override
231 @NotNull
232 public JsExpression visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression,
233 @NotNull TranslationContext context) {
234 return ReferenceTranslator.translateSimpleNameWithQualifier(expression, null, context).source(expression);
235 }
236
237 @Override
238 @NotNull
239 public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) {
240 return LoopTranslatorPackage.createWhile(false, expression, context);
241 }
242
243 @Override
244 @NotNull
245 public JsNode visitDoWhileExpression(@NotNull JetDoWhileExpression expression, @NotNull TranslationContext context) {
246 return LoopTranslatorPackage.createWhile(true, expression, context);
247 }
248
249 @Override
250 @NotNull
251 public JsNode visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression,
252 @NotNull TranslationContext context) {
253 JsStringLiteral stringLiteral = resolveAsStringConstant(expression, context);
254 if (stringLiteral != null) {
255 return stringLiteral;
256 }
257 return resolveAsTemplate(expression, context).source(expression);
258 }
259
260 @NotNull
261 private static JsNode resolveAsTemplate(@NotNull JetStringTemplateExpression expression,
262 @NotNull TranslationContext context) {
263 return StringTemplateTranslator.translate(expression, context);
264 }
265
266 @Nullable
267 private static JsStringLiteral resolveAsStringConstant(@NotNull JetExpression expression,
268 @NotNull TranslationContext context) {
269 Object value = getCompileTimeValue(context.bindingContext(), expression);
270 if (value == null) {
271 return null;
272 }
273 assert value instanceof String : "Compile time constant template should be a String constant.";
274 String constantString = (String) value;
275 return context.program().getStringLiteral(constantString);
276 }
277
278 @Override
279 @NotNull
280 public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression,
281 @NotNull TranslationContext context) {
282 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context);
283 }
284
285 @Override
286 public JsNode visitLabeledExpression(
287 @NotNull JetLabeledExpression expression, TranslationContext context
288 ) {
289 JetExpression baseExpression = expression.getBaseExpression();
290 assert baseExpression != null;
291 JsName name = context.scope().declareName(getReferencedName(expression.getTargetLabel()));
292 JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context);
293 return new JsLabel(name, baseStatement).source(expression);
294 }
295
296 @Override
297 @NotNull
298 public JsNode visitPrefixExpression(
299 @NotNull JetPrefixExpression expression,
300 @NotNull TranslationContext context
301 ) {
302 return UnaryOperationTranslator.translate(expression, context).source(expression);
303 }
304
305 @Override
306 @NotNull
307 public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression,
308 @NotNull TranslationContext context) {
309 return UnaryOperationTranslator.translate(expression, context).source(expression);
310 }
311
312 @Override
313 @NotNull
314 public JsNode visitIsExpression(@NotNull JetIsExpression expression,
315 @NotNull TranslationContext context) {
316 return Translation.patternTranslator(context).translateIsExpression(expression);
317 }
318
319 @Override
320 @NotNull
321 public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression,
322 @NotNull TranslationContext context) {
323 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context).source(expression);
324 }
325
326 @Override
327 @Nullable
328 public JsNode visitWhenExpression(@NotNull JetWhenExpression expression,
329 @NotNull TranslationContext context) {
330 return WhenTranslator.translate(expression, context);
331 }
332
333 @Override
334 @NotNull
335 public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression,
336 @NotNull TranslationContext context) {
337 JsExpression jsExpression = Translation.translateAsExpression(expression.getLeft(), context);
338
339 if (expression.getOperationReference().getReferencedNameElementType() != JetTokens.AS_KEYWORD)
340 return jsExpression.source(expression);
341
342 JetTypeReference right = expression.getRight();
343 assert right != null;
344
345 JetType rightType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.TYPE, right);
346 JetType leftType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.EXPRESSION_TYPE, expression.getLeft());
347 if (TypeUtils.isNullableType(rightType) || !TypeUtils.isNullableType(leftType)) {
348 return jsExpression.source(expression);
349 }
350
351 // KT-2670
352 // we actually do not care for types in js
353 return TranslationUtils.sure(jsExpression, context).source(expression);
354 }
355
356 private static String getReferencedName(JetSimpleNameExpression expression) {
357 return expression.getReferencedName()
358 .replaceAll("^@", "")
359 .replaceAll("(?:^`(.*)`$)", "$1");
360 }
361
362 private static String getTargetLabel(JetExpressionWithLabel expression, TranslationContext context) {
363 JetSimpleNameExpression labelElement = expression.getTargetLabel();
364 if (labelElement == null) {
365 return null;
366 }
367 else {
368 JsName name = context.scope().findName(getReferencedName(labelElement));
369 assert name != null;
370 return name.getIdent();
371 }
372 }
373
374 @Override
375 @NotNull
376 public JsNode visitBreakExpression(@NotNull JetBreakExpression expression,
377 @NotNull TranslationContext context) {
378 return new JsBreak(getTargetLabel(expression, context)).source(expression);
379 }
380
381 @Override
382 @NotNull
383 public JsNode visitContinueExpression(@NotNull JetContinueExpression expression,
384 @NotNull TranslationContext context) {
385 return new JsContinue(getTargetLabel(expression, context)).source(expression);
386 }
387
388 @Override
389 @NotNull
390 public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, @NotNull TranslationContext context) {
391 return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral());
392 }
393
394 @Override
395 @NotNull
396 public JsNode visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) {
397 JsExpression alias = new LiteralFunctionTranslator(context).translate(expression);
398
399 FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
400 JsName name = context.getNameForDescriptor(descriptor);
401
402 return new JsVars(new JsVars.JsVar(name, alias)).source(expression);
403 }
404
405 @Override
406 @NotNull
407 public JsNode visitThisExpression(@NotNull JetThisExpression expression, @NotNull TranslationContext context) {
408 DeclarationDescriptor thisExpression =
409 getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
410 assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
411
412 return context.getThisObject(getReceiverParameterForDeclaration(thisExpression)).source(expression);
413 }
414
415 @Override
416 @NotNull
417 public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression,
418 @NotNull TranslationContext context) {
419 return AccessTranslationUtils.translateAsGet(expression, context);
420 }
421
422 @Override
423 @NotNull
424 public JsNode visitSuperExpression(@NotNull JetSuperExpression expression, @NotNull TranslationContext context) {
425 DeclarationDescriptor superClassDescriptor = context.bindingContext().get(BindingContext.REFERENCE_TARGET, expression.getInstanceReference());
426 assert superClassDescriptor != null: message(expression);
427 return translateAsFQReference(superClassDescriptor, context);
428 }
429
430 @Override
431 @NotNull
432 public JsNode visitForExpression(@NotNull JetForExpression expression,
433 @NotNull TranslationContext context) {
434 return LoopTranslatorPackage.translateForExpression(expression, context).source(expression);
435 }
436
437 @Override
438 @NotNull
439 public JsNode visitTryExpression(@NotNull JetTryExpression expression,
440 @NotNull TranslationContext context) {
441 return TryTranslator.translate(expression, context).source(expression);
442 }
443
444 @Override
445 @NotNull
446 public JsNode visitThrowExpression(@NotNull JetThrowExpression expression,
447 @NotNull TranslationContext context) {
448 JetExpression thrownExpression = expression.getThrownExpression();
449 assert thrownExpression != null : "Thrown expression must not be null";
450 return new JsThrow(translateAsExpression(thrownExpression, context)).source(expression);
451 }
452
453 @Override
454 @NotNull
455 public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression,
456 @NotNull TranslationContext context) {
457 return ClassTranslator.generateObjectLiteral(expression.getObjectDeclaration(), context);
458 }
459
460 @Override
461 @NotNull
462 public JsNode visitObjectDeclaration(@NotNull JetObjectDeclaration expression,
463 @NotNull TranslationContext context) {
464 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), expression);
465 JsName name = context.getNameForDescriptor(descriptor);
466 JsExpression value = ClassTranslator.generateClassCreation(expression, context);
467 return newVar(name, value).source(expression);
468 }
469 }