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.codegen;
018    
019    import com.intellij.psi.PsiElement;
020    import com.intellij.psi.PsiFile;
021    import kotlin.collections.CollectionsKt;
022    import kotlin.jvm.functions.Function1;
023    import kotlin.text.StringsKt;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
027    import org.jetbrains.kotlin.codegen.context.CodegenContext;
028    import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile;
029    import org.jetbrains.kotlin.codegen.context.MethodContext;
030    import org.jetbrains.kotlin.codegen.context.RootContext;
031    import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
032    import org.jetbrains.kotlin.descriptors.*;
033    import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
034    import org.jetbrains.kotlin.load.kotlin.ModuleMapping;
035    import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt;
036    import org.jetbrains.kotlin.psi.KtFile;
037    import org.jetbrains.kotlin.psi.KtFunction;
038    import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
039    import org.jetbrains.kotlin.resolve.BindingContext;
040    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
041    import org.jetbrains.kotlin.resolve.DescriptorUtils;
042    import org.jetbrains.kotlin.resolve.inline.InlineUtil;
043    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
044    import org.jetbrains.kotlin.types.KotlinType;
045    
046    import java.io.File;
047    
048    import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
049    import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
050    import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation;
051    
052    public class JvmCodegenUtil {
053    
054        private JvmCodegenUtil() {
055        }
056    
057        public static boolean isJvmInterface(DeclarationDescriptor descriptor) {
058            if (descriptor instanceof ClassDescriptor) {
059                ClassKind kind = ((ClassDescriptor) descriptor).getKind();
060                return kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS;
061            }
062            return false;
063        }
064    
065        public static boolean isJvmInterface(KotlinType type) {
066            return isJvmInterface(type.getConstructor().getDeclarationDescriptor());
067        }
068    
069        public static boolean isConst(@NotNull CalculatedClosure closure) {
070            return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty();
071        }
072    
073        private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
074            boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
075            boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
076    
077            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
078    
079            return !isFakeOverride && !isDelegate &&
080                   (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
081                     ((context.getParentContext() instanceof FacadePartWithSourceFile)
082                      && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor)))
083                    && context.getContextKind() != OwnerKind.DEFAULT_IMPLS);
084        }
085    
086        private static boolean isWithinSameFile(
087                @Nullable KtFile callerFile,
088                @NotNull CallableMemberDescriptor descriptor
089        ) {
090            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
091            if (containingDeclaration instanceof PackageFragmentDescriptor) {
092                PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
093                PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null;
094                return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile;
095    
096            }
097            return false;
098        }
099    
100        public static boolean isCallInsideSameModuleAsDeclared(
101                @NotNull CallableMemberDescriptor declarationDescriptor,
102                @NotNull CodegenContext context,
103                @Nullable File outDirectory
104        ) {
105            if (context instanceof RootContext) {
106                return true;
107            }
108            DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
109    
110            CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
111            if (directMember instanceof DeserializedCallableMemberDescriptor) {
112                return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory);
113            }
114            else {
115                return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
116            }
117        }
118    
119        public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
120            return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()),
121                                     new Function1<DeclarationDescriptor, Boolean>() {
122                                         @Override
123                                         public Boolean invoke(DeclarationDescriptor descriptor) {
124                                             return descriptor instanceof CallableMemberDescriptor &&
125                                                    ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
126                                         }
127                                     }
128            );
129        }
130    
131        public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) {
132            return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor);
133        }
134    
135        public static boolean couldUseDirectAccessToProperty(
136                @NotNull PropertyDescriptor property,
137                boolean forGetter,
138                boolean isDelegated,
139                @NotNull MethodContext contextBeforeInline
140        ) {
141            if (KotlinTypeMapper.isAccessor(property)) return false;
142    
143            CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
144            // Inline functions can't use direct access because a field may not be visible at the call site
145            if (context.isInlineMethodContext()) {
146                return false;
147            }
148    
149            // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
150            if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
151    
152            // Delegated and extension properties have no backing fields
153            if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
154    
155            // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
156            if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
157    
158            PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
159    
160            // If there's no accessor declared we can use direct access
161            if (accessor == null) return true;
162    
163            // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
164            if (DescriptorPsiUtilsKt.hasBody(accessor)) return false;
165    
166            // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
167            return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
168        }
169    
170        private static boolean isDebuggerContext(@NotNull CodegenContext context) {
171            KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
172            return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file);
173        }
174    
175        @Nullable
176        public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
177                @NotNull ConstructorDescriptor descriptor,
178                @Nullable CalculatedClosure closure
179        ) {
180            //for compilation against sources
181            if (closure != null) {
182                return closure.getCaptureThis();
183            }
184    
185            //for compilation against binaries
186            //TODO: It's best to use this code also for compilation against sources
187            // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
188            ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
189            if (dispatchReceiver != null) {
190                ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
191                if (!expectedThisClass.getKind().isSingleton()) {
192                    return expectedThisClass;
193                }
194            }
195    
196            return null;
197        }
198    
199        @NotNull
200        public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
201            return descriptor instanceof PropertyAccessorDescriptor
202                   ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
203                   : descriptor;
204        }
205    
206        public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
207            PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
208            return InlineUtil.canBeInlineArgument(declaration) &&
209                   InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false);
210        }
211    
212        @NotNull
213        public static String getModuleName(ModuleDescriptor module) {
214            return StringsKt.removeSurrounding(module.getName().asString(), "<", ">");
215        }
216    
217        @NotNull
218        public static String getMappingFileName(@NotNull String moduleName) {
219            return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
220        }
221    
222        public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) {
223            if (!(descriptor instanceof JavaPropertyDescriptor)) return false;
224            return descriptor.isConst();
225        }
226    }