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.MetadataProperties;
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.LoopTranslator;
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.KtTokens;
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.BindingContextUtilsKt;
043 import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
044 import org.jetbrains.kotlin.resolve.constants.ConstantValue;
045 import org.jetbrains.kotlin.resolve.constants.NullValue;
046 import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
047 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
048 import org.jetbrains.kotlin.types.KotlinType;
049 import org.jetbrains.kotlin.types.TypeUtils;
050
051 import java.util.List;
052
053 import static org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor;
054 import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression;
055 import static org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator.translateAsFQReference;
056 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.*;
057 import static org.jetbrains.kotlin.js.translate.utils.ErrorReportingUtils.message;
058 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement;
059 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newVar;
060 import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
061 import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.translateInitializerForProperty;
062 import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure;
063
064 public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
065 @Override
066 protected JsNode emptyResult(@NotNull TranslationContext context) {
067 return context.getEmptyExpression();
068 }
069
070 @Override
071 @NotNull
072 public JsNode visitConstantExpression(@NotNull KtConstantExpression expression, @NotNull TranslationContext context) {
073 return translateConstantExpression(expression, context).source(expression);
074 }
075
076 @NotNull
077 private static JsNode translateConstantExpression(@NotNull KtConstantExpression expression, @NotNull TranslationContext context) {
078 CompileTimeConstant<?> compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext());
079 assert compileTimeValue != null : message(expression, "Expression is not compile time value: " + expression.getText() + " ");
080 KotlinType expectedType = context.bindingContext().getType(expression);
081 ConstantValue<?> constant = compileTimeValue.toConstantValue(expectedType != null ? expectedType : TypeUtils.NO_EXPECTED_TYPE);
082 if (constant instanceof NullValue) {
083 return JsLiteral.NULL;
084 }
085 Object value = constant.getValue();
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 KtBlockExpression jetBlock, @NotNull TranslationContext context) {
113 List<KtExpression> statements = jetBlock.getStatements();
114 JsBlock jsBlock = new JsBlock();
115 for (KtExpression statement : statements) {
116 JsNode jsNode = Translation.translateExpression(statement, context, jsBlock);
117 JsStatement jsStatement = convertToStatement(jsNode);
118 if (!JsAstUtils.isEmptyStatement(jsStatement)) {
119 jsBlock.getStatements().add(jsStatement);
120 }
121 }
122 return jsBlock;
123 }
124
125 @Override
126 public JsNode visitMultiDeclaration(@NotNull KtMultiDeclaration multiDeclaration, @NotNull TranslationContext context) {
127 KtExpression jetInitializer = multiDeclaration.getInitializer();
128 assert jetInitializer != null : "Initializer for multi declaration must be not null";
129 JsExpression initializer = Translation.translateAsExpression(jetInitializer, context);
130 return MultiDeclarationTranslator.translate(multiDeclaration, context.scope().declareTemporary(), initializer, context);
131 }
132
133 @Override
134 @NotNull
135 public JsNode visitReturnExpression(@NotNull KtReturnExpression jetReturnExpression,
136 @NotNull TranslationContext context) {
137 KtExpression returned = jetReturnExpression.getReturnedExpression();
138
139 // TODO: add related descriptor to context and use it here
140 KtDeclarationWithBody parent = PsiTreeUtil.getParentOfType(jetReturnExpression, KtDeclarationWithBody.class);
141 if (parent instanceof KtSecondaryConstructor) {
142 return new JsReturn(new JsNameRef(Namer.ANOTHER_THIS_PARAMETER_NAME)).source(jetReturnExpression);
143 }
144 if (returned == null) {
145 return new JsReturn(null).source(jetReturnExpression);
146 }
147 JsExpression jsReturnExpression = translateAsExpression(returned, context);
148 if (JsAstUtils.isEmptyExpression(jsReturnExpression)) {
149 return context.getEmptyExpression();
150 }
151 return new JsReturn(jsReturnExpression).source(jetReturnExpression);
152 }
153
154 @Override
155 @NotNull
156 public JsNode visitParenthesizedExpression(@NotNull KtParenthesizedExpression expression,
157 @NotNull TranslationContext context) {
158 KtExpression expressionInside = expression.getExpression();
159 if (expressionInside != null) {
160 return Translation.translateExpression(expressionInside, context);
161 }
162 return JsEmpty.INSTANCE$;
163 }
164
165 @Override
166 @NotNull
167 public JsNode visitBinaryExpression(@NotNull KtBinaryExpression expression,
168 @NotNull TranslationContext context) {
169 return BinaryOperationTranslator.translate(expression, context);
170 }
171
172 @Override
173 @NotNull
174 // assume it is a local variable declaration
175 public JsNode visitProperty(@NotNull KtProperty expression, @NotNull TranslationContext context) {
176 VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
177 JsExpression initializer = translateInitializerForProperty(expression, context);
178 if (initializer != null && JsAstUtils.isEmptyExpression(initializer)) {
179 return context.getEmptyExpression();
180 }
181
182 JsName name = context.getNameForDescriptor(descriptor);
183 if (isVarCapturedInClosure(context.bindingContext(), descriptor)) {
184 JsNameRef alias = getCapturedVarAccessor(name.makeRef());
185 initializer = JsAstUtils.wrapValue(alias, initializer == null ? JsLiteral.NULL : initializer);
186 }
187
188 return newVar(name, initializer).source(expression);
189 }
190
191 @Override
192 @NotNull
193 public JsNode visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, @NotNull TranslationContext context) {
194 return CallableReferenceTranslator.INSTANCE$.translate(expression, context);
195 }
196
197 @Override
198 @NotNull
199 public JsNode visitCallExpression(
200 @NotNull KtCallExpression expression,
201 @NotNull TranslationContext context
202 ) {
203 return CallExpressionTranslator.translate(expression, null, context).source(expression);
204 }
205
206 @Override
207 @NotNull
208 public JsNode visitIfExpression(@NotNull KtIfExpression expression, @NotNull TranslationContext context) {
209 assert expression.getCondition() != null : "condition should not ne null: " + expression.getText();
210 JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context);
211 if (JsAstUtils.isEmptyExpression(testExpression)) {
212 return testExpression;
213 }
214
215 boolean isKotlinExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext());
216
217 KtExpression thenExpression = expression.getThen();
218 KtExpression elseExpression = expression.getElse();
219
220 JsStatement thenStatement =
221 thenExpression != null ? Translation.translateAsStatementAndMergeInBlockIfNeeded(thenExpression, context) : null;
222 JsStatement elseStatement =
223 elseExpression != null ? Translation.translateAsStatementAndMergeInBlockIfNeeded(elseExpression, context) : null;
224
225 if (isKotlinExpression) {
226 JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement);
227 JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement);
228 boolean canBeJsExpression = jsThenExpression != null && jsElseExpression != null;
229 if (canBeJsExpression) {
230 return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression);
231 }
232 }
233 JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement);
234 return ifStatement.source(expression);
235 }
236
237 @Override
238 @NotNull
239 public JsExpression visitSimpleNameExpression(@NotNull KtSimpleNameExpression expression,
240 @NotNull TranslationContext context) {
241 return ReferenceTranslator.translateSimpleNameWithQualifier(expression, null, context).source(expression);
242 }
243
244 @Override
245 @NotNull
246 public JsNode visitWhileExpression(@NotNull KtWhileExpression expression, @NotNull TranslationContext context) {
247 return LoopTranslator.createWhile(false, expression, context);
248 }
249
250 @Override
251 @NotNull
252 public JsNode visitDoWhileExpression(@NotNull KtDoWhileExpression expression, @NotNull TranslationContext context) {
253 return LoopTranslator.createWhile(true, expression, context);
254 }
255
256 @Override
257 @NotNull
258 public JsNode visitStringTemplateExpression(@NotNull KtStringTemplateExpression expression,
259 @NotNull TranslationContext context) {
260 JsStringLiteral stringLiteral = resolveAsStringConstant(expression, context);
261 if (stringLiteral != null) {
262 return stringLiteral;
263 }
264 return resolveAsTemplate(expression, context).source(expression);
265 }
266
267 @NotNull
268 private static JsNode resolveAsTemplate(@NotNull KtStringTemplateExpression expression,
269 @NotNull TranslationContext context) {
270 return StringTemplateTranslator.translate(expression, context);
271 }
272
273 @Nullable
274 private static JsStringLiteral resolveAsStringConstant(@NotNull KtExpression expression,
275 @NotNull TranslationContext context) {
276 Object value = getCompileTimeValue(context.bindingContext(), expression);
277 if (value == null) {
278 return null;
279 }
280 assert value instanceof String : "Compile time constant template should be a String constant.";
281 String constantString = (String) value;
282 return context.program().getStringLiteral(constantString);
283 }
284
285 @Override
286 @NotNull
287 public JsNode visitDotQualifiedExpression(@NotNull KtDotQualifiedExpression expression,
288 @NotNull TranslationContext context) {
289 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context);
290 }
291
292 @Override
293 public JsNode visitLabeledExpression(
294 @NotNull KtLabeledExpression expression, TranslationContext context
295 ) {
296 KtExpression baseExpression = expression.getBaseExpression();
297 assert baseExpression != null;
298
299 if (BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext())) {
300 return Translation.translateAsExpression(baseExpression, context).source(expression);
301 }
302
303 JsScope scope = context.scope();
304 assert scope instanceof JsFunctionScope: "Labeled statement is unexpected outside of function scope";
305 JsFunctionScope functionScope = (JsFunctionScope) scope;
306
307 String labelIdent = getReferencedName(expression.getTargetLabel());
308
309 JsName labelName = functionScope.enterLabel(labelIdent);
310 JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context);
311 functionScope.exitLabel();
312
313 return new JsLabel(labelName, baseStatement).source(expression);
314 }
315
316 @Override
317 @NotNull
318 public JsNode visitPrefixExpression(
319 @NotNull KtPrefixExpression expression,
320 @NotNull TranslationContext context
321 ) {
322 return UnaryOperationTranslator.translate(expression, context).source(expression);
323 }
324
325 @Override
326 @NotNull
327 public JsNode visitPostfixExpression(@NotNull KtPostfixExpression expression,
328 @NotNull TranslationContext context) {
329 return UnaryOperationTranslator.translate(expression, context).source(expression);
330 }
331
332 @Override
333 @NotNull
334 public JsNode visitIsExpression(@NotNull KtIsExpression expression,
335 @NotNull TranslationContext context) {
336 return Translation.patternTranslator(context).translateIsExpression(expression);
337 }
338
339 @Override
340 @NotNull
341 public JsNode visitSafeQualifiedExpression(@NotNull KtSafeQualifiedExpression expression,
342 @NotNull TranslationContext context) {
343 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context).source(expression);
344 }
345
346 @Override
347 @Nullable
348 public JsNode visitWhenExpression(@NotNull KtWhenExpression expression,
349 @NotNull TranslationContext context) {
350 return WhenTranslator.translate(expression, context);
351 }
352
353 @Override
354 @NotNull
355 public JsNode visitBinaryWithTypeRHSExpression(@NotNull KtBinaryExpressionWithTypeRHS expression,
356 @NotNull TranslationContext context) {
357 JsExpression jsExpression = Translation.translateAsExpression(expression.getLeft(), context);
358
359 if (expression.getOperationReference().getReferencedNameElementType() != KtTokens.AS_KEYWORD)
360 return jsExpression.source(expression);
361
362 KtTypeReference right = expression.getRight();
363 assert right != null;
364
365 KotlinType rightType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.TYPE, right);
366 KotlinType leftType = BindingContextUtils.getTypeNotNull(context.bindingContext(), expression.getLeft());
367 if (TypeUtils.isNullableType(rightType) || !TypeUtils.isNullableType(leftType)) {
368 return jsExpression.source(expression);
369 }
370
371 // KT-2670
372 // we actually do not care for types in js
373 return TranslationUtils.sure(jsExpression, context).source(expression);
374 }
375
376 private static String getReferencedName(KtSimpleNameExpression expression) {
377 return expression.getReferencedName()
378 .replaceAll("^@", "")
379 .replaceAll("(?:^`(.*)`$)", "$1");
380 }
381
382 private static JsNameRef getTargetLabel(KtExpressionWithLabel expression, TranslationContext context) {
383 KtSimpleNameExpression labelElement = expression.getTargetLabel();
384 if (labelElement == null) {
385 return null;
386 }
387
388 String labelIdent = getReferencedName(labelElement);
389 JsScope scope = context.scope();
390 assert scope instanceof JsFunctionScope: "Labeled statement is unexpected outside of function scope";
391 JsName labelName = ((JsFunctionScope) scope).findLabel(labelIdent);
392 assert labelName != null;
393 return labelName.makeRef();
394 }
395
396 @Override
397 @NotNull
398 public JsNode visitBreakExpression(@NotNull KtBreakExpression expression,
399 @NotNull TranslationContext context) {
400 return new JsBreak(getTargetLabel(expression, context)).source(expression);
401 }
402
403 @Override
404 @NotNull
405 public JsNode visitContinueExpression(@NotNull KtContinueExpression expression,
406 @NotNull TranslationContext context) {
407 return new JsContinue(getTargetLabel(expression, context)).source(expression);
408 }
409
410 @Override
411 @NotNull
412 public JsNode visitFunctionLiteralExpression(@NotNull KtFunctionLiteralExpression expression, @NotNull TranslationContext context) {
413 return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral());
414 }
415
416 @Override
417 @NotNull
418 public JsNode visitNamedFunction(@NotNull KtNamedFunction expression, @NotNull TranslationContext context) {
419 JsExpression alias = new LiteralFunctionTranslator(context).translate(expression);
420
421 FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
422 JsName name = context.getNameForDescriptor(descriptor);
423 if (InlineUtil.isInline(descriptor)) {
424 MetadataProperties.setStaticRef(name, alias);
425 }
426
427 boolean isExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext());
428 JsNode result = isExpression ? alias : JsAstUtils.newVar(name, alias);
429
430 return result.source(expression);
431 }
432
433 @Override
434 @NotNull
435 public JsNode visitThisExpression(@NotNull KtThisExpression expression, @NotNull TranslationContext context) {
436 DeclarationDescriptor thisExpression =
437 getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
438 assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
439
440 return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression)).source(expression);
441 }
442
443 @Override
444 @NotNull
445 public JsNode visitArrayAccessExpression(@NotNull KtArrayAccessExpression expression,
446 @NotNull TranslationContext context) {
447 return AccessTranslationUtils.translateAsGet(expression, context);
448 }
449
450 @Override
451 @NotNull
452 public JsNode visitSuperExpression(@NotNull KtSuperExpression expression, @NotNull TranslationContext context) {
453 DeclarationDescriptor superClassDescriptor = context.bindingContext().get(BindingContext.REFERENCE_TARGET, expression.getInstanceReference());
454 assert superClassDescriptor != null: message(expression);
455 return translateAsFQReference(superClassDescriptor, context);
456 }
457
458 @Override
459 @NotNull
460 public JsNode visitForExpression(@NotNull KtForExpression expression,
461 @NotNull TranslationContext context) {
462 return LoopTranslator.translateForExpression(expression, context).source(expression);
463 }
464
465 @Override
466 @NotNull
467 public JsNode visitTryExpression(
468 @NotNull KtTryExpression expression,
469 @NotNull TranslationContext context
470 ) {
471 return new TryTranslator(expression, context).translate();
472 }
473
474 @Override
475 @NotNull
476 public JsNode visitThrowExpression(@NotNull KtThrowExpression expression,
477 @NotNull TranslationContext context) {
478 KtExpression thrownExpression = expression.getThrownExpression();
479 assert thrownExpression != null : "Thrown expression must not be null";
480 return new JsThrow(translateAsExpression(thrownExpression, context)).source(expression);
481 }
482
483 @Override
484 @NotNull
485 public JsNode visitObjectLiteralExpression(@NotNull KtObjectLiteralExpression expression,
486 @NotNull TranslationContext context) {
487 return ClassTranslator.generateObjectLiteral(expression.getObjectDeclaration(), context);
488 }
489
490 @Override
491 @NotNull
492 public JsNode visitObjectDeclaration(@NotNull KtObjectDeclaration expression,
493 @NotNull TranslationContext context) {
494 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), expression);
495 JsName name = context.getNameForDescriptor(descriptor);
496 JsExpression value = ClassTranslator.generateClassCreation(expression, context);
497 return newVar(name, value).source(expression);
498 }
499 }