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