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    }