001 /*
002 * Copyright 2010-2016 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 org.jetbrains.kotlin.js.backend.ast.JsExpression;
020 import org.jetbrains.kotlin.js.backend.ast.JsInvocation;
021 import org.jetbrains.kotlin.js.backend.ast.JsName;
022 import org.jetbrains.kotlin.js.backend.ast.JsNameRef;
023 import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
024 import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.kotlin.descriptors.*;
027 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
028 import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
029 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
030 import org.jetbrains.kotlin.psi.KtExpression;
031 import org.jetbrains.kotlin.psi.KtQualifiedExpression;
032 import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
033 import org.jetbrains.kotlin.resolve.DescriptorUtils;
034
035 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
036 import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName;
037 import static org.jetbrains.kotlin.psi.KtPsiUtil.isBackingFieldReference;
038
039 public final class ReferenceTranslator {
040
041 private ReferenceTranslator() {
042 }
043
044 @NotNull
045 public static JsExpression translateSimpleName(@NotNull KtSimpleNameExpression expression, @NotNull TranslationContext context) {
046 return getAccessTranslator(expression, context).translateAsGet();
047 }
048
049 @NotNull
050 public static JsExpression translateAsValueReference(@NotNull DeclarationDescriptor descriptor, @NotNull TranslationContext context) {
051 JsExpression alias = context.getAliasForDescriptor(descriptor);
052 if (alias != null) return alias;
053
054 if (shouldTranslateAsFQN(descriptor, context)) {
055 return context.getQualifiedReference(descriptor);
056 }
057
058 if (descriptor instanceof PropertyDescriptor) {
059 PropertyDescriptor property = (PropertyDescriptor) descriptor;
060 if (context.isFromCurrentModule(property)) {
061 return context.getInnerReference(property);
062 }
063 else {
064 JsExpression qualifier = context.getInnerReference(property.getContainingDeclaration());
065 JsName name = context.getNameForDescriptor(property);
066 return new JsNameRef(name, qualifier);
067 }
068 }
069
070 if (DescriptorUtils.isObject(descriptor) || DescriptorUtils.isEnumEntry(descriptor)) {
071 if (!context.isFromCurrentModule(descriptor)) {
072 return getLazyReferenceToObject((ClassDescriptor) descriptor, context);
073 }
074 else {
075 JsExpression functionRef = JsAstUtils.pureFqn(context.getNameForObjectInstance((ClassDescriptor) descriptor), null);
076 return new JsInvocation(functionRef);
077 }
078 }
079
080 return context.getInnerReference(descriptor);
081 }
082
083 @NotNull
084 public static JsExpression translateAsTypeReference(@NotNull ClassDescriptor descriptor, @NotNull TranslationContext context) {
085 if (AnnotationsUtils.isNativeObject(descriptor)) {
086 return context.getQualifiedReference(descriptor);
087 }
088 if (!shouldTranslateAsFQN(descriptor, context)) {
089 if (DescriptorUtils.isObject(descriptor) || DescriptorUtils.isEnumEntry(descriptor)) {
090 if (!context.isFromCurrentModule(descriptor)) {
091 return getPrototypeIfNecessary(descriptor, getLazyReferenceToObject(descriptor, context));
092 }
093 }
094 return context.getInnerReference(descriptor);
095 }
096
097 return getPrototypeIfNecessary(descriptor, context.getQualifiedReference(descriptor));
098 }
099
100 @NotNull
101 private static JsExpression getPrototypeIfNecessary(@NotNull ClassDescriptor descriptor, @NotNull JsExpression reference) {
102 if (DescriptorUtils.isObject(descriptor) || DescriptorUtils.isEnumEntry(descriptor)) {
103 JsNameRef getPrototypeRef = JsAstUtils.pureFqn("getPrototypeOf", JsAstUtils.pureFqn("Object", null));
104 JsInvocation getPrototypeInvocation = new JsInvocation(getPrototypeRef, reference);
105 MetadataProperties.setSideEffects(getPrototypeInvocation, SideEffectKind.PURE);
106 reference = JsAstUtils.pureFqn("constructor", getPrototypeInvocation);
107 }
108 return reference;
109 }
110
111 @NotNull
112 private static JsExpression getLazyReferenceToObject(@NotNull ClassDescriptor descriptor, @NotNull TranslationContext context) {
113 DeclarationDescriptor container = descriptor.getContainingDeclaration();
114 JsExpression qualifier = context.getInnerReference(container);
115 return new JsNameRef(context.getNameForDescriptor(descriptor), qualifier);
116 }
117
118 private static boolean shouldTranslateAsFQN(@NotNull DeclarationDescriptor descriptor, @NotNull TranslationContext context) {
119 return isLocalVarOrFunction(descriptor) ||
120 AnnotationsUtils.isNativeObject(descriptor) ||
121 AnnotationsUtils.isLibraryObject(descriptor) ||
122 context.isPublicInlineFunction();
123 }
124
125 private static boolean isLocalVarOrFunction(DeclarationDescriptor descriptor) {
126 return descriptor.getContainingDeclaration() instanceof FunctionDescriptor && !(descriptor instanceof ClassDescriptor);
127 }
128
129 @NotNull
130 public static AccessTranslator getAccessTranslator(@NotNull KtSimpleNameExpression referenceExpression,
131 @NotNull TranslationContext context) {
132 if (isBackingFieldReference(getDescriptorForReferenceExpression(context.bindingContext(), referenceExpression))) {
133 return BackingFieldAccessTranslator.newInstance(referenceExpression, context);
134 }
135 if (canBePropertyAccess(referenceExpression, context)) {
136 return VariableAccessTranslator.newInstance(context, referenceExpression, null);
137 }
138 if (CompanionObjectIntrinsicAccessTranslator.isCompanionObjectReference(referenceExpression, context)) {
139 return CompanionObjectIntrinsicAccessTranslator.newInstance(referenceExpression, context);
140 }
141 return ReferenceAccessTranslator.newInstance(referenceExpression, context);
142 }
143
144 public static boolean canBePropertyAccess(@NotNull KtExpression expression, @NotNull TranslationContext context) {
145 KtSimpleNameExpression simpleNameExpression = null;
146 if (expression instanceof KtQualifiedExpression) {
147 simpleNameExpression = getSelectorAsSimpleName((KtQualifiedExpression) expression);
148 }
149 else if (expression instanceof KtSimpleNameExpression) {
150 simpleNameExpression = (KtSimpleNameExpression) expression;
151 }
152
153 if (simpleNameExpression == null) return false;
154
155 DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), simpleNameExpression);
156
157 // Skip ValueParameterDescriptor because sometime we can miss resolved call for it, e.g. when set something to delegated property.
158 return descriptor instanceof VariableDescriptor && !(descriptor instanceof ValueParameterDescriptor);
159 }
160
161 }