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 org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.descriptors.*;
023    import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
024    import org.jetbrains.kotlin.js.translate.context.Namer;
025    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
026    import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
027    import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
028    import org.jetbrains.kotlin.psi.JetExpression;
029    import org.jetbrains.kotlin.psi.JetQualifiedExpression;
030    import org.jetbrains.kotlin.psi.JetSimpleNameExpression;
031    import org.jetbrains.kotlin.resolve.DescriptorUtils;
032    
033    import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
034    import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.setQualifier;
035    import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName;
036    import static org.jetbrains.kotlin.psi.JetPsiUtil.isBackingFieldReference;
037    
038    public final class ReferenceTranslator {
039    
040        private ReferenceTranslator() {
041        }
042    
043        @NotNull
044        public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression,
045                @NotNull TranslationContext context) {
046            return getAccessTranslator(expression, context).translateAsGet();
047        }
048    
049        @NotNull
050        public static JsExpression translateSimpleNameWithQualifier(
051                @NotNull JetSimpleNameExpression expression,
052                @Nullable JsExpression qualifier,
053                @NotNull TranslationContext context
054        ) {
055            JsExpression simpleName = translateSimpleName(expression, context);
056    
057            // Ignore qualifier if expression is EnumEntry or companion object reference and always use FQ name.
058            DeclarationDescriptor descriptor = BindingUtils.getDescriptorForReferenceExpression(context.bindingContext(), expression);
059            //TODO: should go away when objects inside classes are supported
060            if (DescriptorUtils.isCompanionObject(descriptor) && !AnnotationsUtils.isNativeObject(descriptor)) {
061                return simpleName;
062            }
063            if (descriptor instanceof ClassDescriptor) {
064                ClassDescriptor entryClass = (ClassDescriptor) descriptor;
065                if (entryClass.getKind() == ClassKind.ENUM_ENTRY && !AnnotationsUtils.isNativeObject(entryClass)) {
066                    DeclarationDescriptor enumClass = entryClass.getContainingDeclaration();
067                    qualifier = Namer.getCompanionObjectAccessor(translateAsFQReference(enumClass, context));
068                }
069            }
070    
071            if (qualifier != null) { // TODO: hack for nested Object
072                setQualifier(simpleName, qualifier);
073            }
074    
075            return simpleName;
076        }
077    
078        @NotNull
079        public static JsExpression translateAsFQReference(@NotNull DeclarationDescriptor referencedDescriptor,
080                @NotNull TranslationContext context) {
081            JsExpression alias = context.getAliasForDescriptor(referencedDescriptor);
082            return alias != null ? alias : context.getQualifiedReference(referencedDescriptor);
083        }
084    
085        @NotNull
086        public static JsExpression translateAsLocalNameReference(@NotNull DeclarationDescriptor descriptor,
087                @NotNull TranslationContext context) {
088            if (descriptor instanceof FunctionDescriptor || descriptor instanceof VariableDescriptor) {
089                JsExpression alias = context.getAliasForDescriptor(descriptor);
090                if (alias != null) {
091                    return alias;
092                }
093            }
094            return context.getNameForDescriptor(descriptor).makeRef();
095        }
096    
097        @NotNull
098        public static AccessTranslator getAccessTranslator(@NotNull JetSimpleNameExpression referenceExpression,
099                @NotNull TranslationContext context) {
100            return getAccessTranslator(referenceExpression, null, context);
101        }
102    
103        @NotNull
104        public static AccessTranslator getAccessTranslator(@NotNull JetSimpleNameExpression referenceExpression,
105                @Nullable JsExpression receiver,
106                @NotNull TranslationContext context) {
107            if (isBackingFieldReference(referenceExpression, getDescriptorForReferenceExpression(context.bindingContext(), referenceExpression))) {
108                return BackingFieldAccessTranslator.newInstance(referenceExpression, context);
109            }
110            if (canBePropertyAccess(referenceExpression, context)) {
111                return VariableAccessTranslator.newInstance(context, referenceExpression, receiver);
112            }
113            if (CompanionObjectAccessTranslator.isCompanionObjectReference(referenceExpression, context)) {
114                return CompanionObjectAccessTranslator.newInstance(referenceExpression, context);
115            }
116            return ReferenceAccessTranslator.newInstance(referenceExpression, context);
117        }
118    
119        public static boolean canBePropertyAccess(@NotNull JetExpression expression, @NotNull TranslationContext context) {
120            JetSimpleNameExpression simpleNameExpression = null;
121            if (expression instanceof JetQualifiedExpression) {
122                simpleNameExpression = getSelectorAsSimpleName((JetQualifiedExpression) expression);
123            }
124            else if (expression instanceof JetSimpleNameExpression) {
125                simpleNameExpression = (JetSimpleNameExpression) expression;
126            }
127    
128            if (simpleNameExpression == null) return false;
129    
130            DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), simpleNameExpression);
131    
132            // Skip ValueParameterDescriptor because sometime we can miss resolved call for it, e.g. when set something to delegated property.
133            return descriptor instanceof VariableDescriptor && !(descriptor instanceof ValueParameterDescriptor);
134        }
135    
136    }