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