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.reference;
018    
019    import com.google.dart.compiler.backend.js.ast.JsExpression;
020    import com.google.dart.compiler.backend.js.ast.JsNode;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
024    import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
025    import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
026    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
027    import org.jetbrains.kotlin.js.translate.utils.ErrorReportingUtils;
028    import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
029    import org.jetbrains.kotlin.psi.*;
030    
031    import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression;
032    import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
033    import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getNotNullSimpleNameSelector;
034    import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelector;
035    
036    public final class QualifiedExpressionTranslator {
037    
038        private QualifiedExpressionTranslator() {
039        }
040    
041        @NotNull
042        public static AccessTranslator getAccessTranslator(@NotNull KtQualifiedExpression 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 JsNode translateQualifiedExpression(
055                @NotNull KtQualifiedExpression expression,
056                @NotNull TranslationContext context
057        ) {
058            JsExpression receiver = translateReceiver(expression, context);
059            KtExpression selector = getSelector(expression);
060            return dispatchToCorrectTranslator(receiver, selector, context);
061        }
062    
063        @NotNull
064        private static JsNode dispatchToCorrectTranslator(
065                @Nullable JsExpression receiver,
066                @NotNull KtExpression selector,
067                @NotNull TranslationContext context
068        ) {
069            if (ReferenceTranslator.canBePropertyAccess(selector, context)) {
070                assert selector instanceof KtSimpleNameExpression : "Selectors for properties must be simple names.";
071                return VariableAccessTranslator.newInstance(context, (KtSimpleNameExpression)selector, receiver).translateAsGet();
072            }
073            if (selector instanceof KtCallExpression) {
074                return invokeCallExpressionTranslator(receiver, selector, context);
075            }
076            //TODO: never get there
077            if (selector instanceof KtSimpleNameExpression) {
078                return ReferenceTranslator.translateSimpleNameWithQualifier((KtSimpleNameExpression) selector, receiver, context);
079            }
080            throw new AssertionError("Unexpected qualified expression: " + selector.getText());
081        }
082    
083        @NotNull
084        private static JsNode invokeCallExpressionTranslator(
085                @Nullable JsExpression receiver,
086                @NotNull KtExpression selector,
087                @NotNull TranslationContext context
088        ) {
089            try {
090                return CallExpressionTranslator.translate((KtCallExpression) selector, receiver, context);
091            } catch (RuntimeException e) {
092                throw  ErrorReportingUtils.reportErrorWithLocation(selector, e);
093            }
094        }
095    
096        @Nullable
097        private static JsExpression translateReceiver(@NotNull KtQualifiedExpression expression,
098                                                      @NotNull TranslationContext context) {
099            KtExpression receiverExpression = expression.getReceiverExpression();
100            if (isFullQualifierForExpression(receiverExpression, context)) {
101                return null;
102            }
103            return translateAsExpression(receiverExpression, context);
104        }
105    
106        //TODO: prove correctness
107        private static boolean isFullQualifierForExpression(@Nullable KtExpression receiverExpression, @NotNull TranslationContext context) {
108            if (receiverExpression == null) {
109                return false;
110            }
111            if (receiverExpression instanceof KtReferenceExpression) {
112                DeclarationDescriptor descriptorForReferenceExpression =
113                    getDescriptorForReferenceExpression(context.bindingContext(), (KtReferenceExpression)receiverExpression);
114                if (descriptorForReferenceExpression instanceof PackageViewDescriptor) {
115                    return true;
116                }
117            }
118            if (receiverExpression instanceof KtQualifiedExpression) {
119                return isFullQualifierForExpression(((KtQualifiedExpression)receiverExpression).getSelectorExpression(), context);
120            }
121            return false;
122        }
123    }