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.reference;
018    
019    import com.google.dart.compiler.backend.js.ast.JsExpression;
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.PackageViewDescriptor;
024    import org.jetbrains.jet.lang.psi.*;
025    import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
026    import org.jetbrains.k2js.translate.context.TemporaryVariable;
027    import org.jetbrains.k2js.translate.context.TranslationContext;
028    import org.jetbrains.k2js.translate.utils.ErrorReportingUtils;
029    import org.jetbrains.k2js.translate.utils.JsAstUtils;
030    
031    import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression;
032    import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
033    import static org.jetbrains.k2js.translate.utils.PsiUtils.getNotNullSimpleNameSelector;
034    import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelector;
035    
036    public final class QualifiedExpressionTranslator {
037    
038        private QualifiedExpressionTranslator() {
039        }
040    
041        @NotNull
042        public static AccessTranslator getAccessTranslator(@NotNull JetQualifiedExpression expression,
043                                                           @NotNull TranslationContext context, boolean forceOrderOfEvaluation) {
044            JsExpression receiver = translateReceiver(expression, context);
045            if (forceOrderOfEvaluation && receiver != null) {
046                TemporaryVariable temporaryVariable = context.declareTemporary(null);
047                context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), receiver).makeStmt());
048                receiver = temporaryVariable.reference();
049            }
050            return VariableAccessTranslator.newInstance(context, getNotNullSimpleNameSelector(expression), receiver);
051        }
052    
053        @NotNull
054        public static JsExpression translateQualifiedExpression(@NotNull JetQualifiedExpression expression,
055                                                                @NotNull TranslationContext context) {
056            JsExpression receiver = translateReceiver(expression, context);
057            JetExpression selector = getSelector(expression);
058            return dispatchToCorrectTranslator(receiver, selector, context);
059        }
060    
061        @NotNull
062        private static JsExpression dispatchToCorrectTranslator(
063                @Nullable JsExpression receiver,
064                @NotNull JetExpression selector,
065                @NotNull TranslationContext context
066        ) {
067            if (ReferenceTranslator.canBePropertyAccess(selector, context)) {
068                assert selector instanceof JetSimpleNameExpression : "Selectors for properties must be simple names.";
069                return VariableAccessTranslator.newInstance(context, (JetSimpleNameExpression)selector, receiver).translateAsGet();
070            }
071            if (selector instanceof JetCallExpression) {
072                if (InlinedCallExpressionTranslator.shouldBeInlined((JetCallExpression) selector, context) &&
073                    BindingContextUtilPackage.isUsedAsExpression(selector, context.bindingContext())) {
074                    TemporaryVariable temporaryVariable = context.declareTemporary(null);
075                    JsExpression result = invokeCallExpressionTranslator(receiver, selector, context);
076                    context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), result).makeStmt());
077                    return temporaryVariable.reference();
078                } else {
079                    return invokeCallExpressionTranslator(receiver, selector, context);
080                }
081            }
082            //TODO: never get there
083            if (selector instanceof JetSimpleNameExpression) {
084                return ReferenceTranslator.translateSimpleNameWithQualifier((JetSimpleNameExpression) selector, receiver, context);
085            }
086            throw new AssertionError("Unexpected qualified expression: " + selector.getText());
087        }
088    
089        @NotNull
090        private static JsExpression invokeCallExpressionTranslator(
091                @Nullable JsExpression receiver,
092                @NotNull JetExpression selector,
093                @NotNull TranslationContext context
094        ) {
095            try {
096                return CallExpressionTranslator.translate((JetCallExpression) selector, receiver, context);
097            } catch (RuntimeException e) {
098                throw  ErrorReportingUtils.reportErrorWithLocation(selector, e);
099            }
100        }
101    
102        @Nullable
103        private static JsExpression translateReceiver(@NotNull JetQualifiedExpression expression,
104                                                      @NotNull TranslationContext context) {
105            JetExpression receiverExpression = expression.getReceiverExpression();
106            if (isFullQualifierForExpression(receiverExpression, context)) {
107                return null;
108            }
109            return translateAsExpression(receiverExpression, context);
110        }
111    
112        //TODO: prove correctness
113        private static boolean isFullQualifierForExpression(@Nullable JetExpression receiverExpression, @NotNull TranslationContext context) {
114            if (receiverExpression == null) {
115                return false;
116            }
117            if (receiverExpression instanceof JetReferenceExpression) {
118                DeclarationDescriptor descriptorForReferenceExpression =
119                    getDescriptorForReferenceExpression(context.bindingContext(), (JetReferenceExpression)receiverExpression);
120                if (descriptorForReferenceExpression instanceof PackageViewDescriptor) {
121                    return true;
122                }
123            }
124            if (receiverExpression instanceof JetQualifiedExpression) {
125                return isFullQualifierForExpression(((JetQualifiedExpression)receiverExpression).getSelectorExpression(), context);
126            }
127            return false;
128        }
129    }