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.openapi.vfs.StandardFileSystems;
020    import com.intellij.openapi.vfs.VfsUtilCore;
021    import com.intellij.openapi.vfs.VirtualFile;
022    import com.intellij.psi.PsiElement;
023    import kotlin.KotlinPackage;
024    import kotlin.jvm.functions.Function1;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
028    import org.jetbrains.kotlin.codegen.context.CodegenContext;
029    import org.jetbrains.kotlin.codegen.context.MethodContext;
030    import org.jetbrains.kotlin.codegen.context.PackageContext;
031    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
032    import org.jetbrains.kotlin.descriptors.*;
033    import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment;
034    import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
035    import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
036    import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider;
037    import org.jetbrains.kotlin.psi.JetFile;
038    import org.jetbrains.kotlin.psi.JetFunction;
039    import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilPackage;
040    import org.jetbrains.kotlin.resolve.BindingContext;
041    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
042    import org.jetbrains.kotlin.resolve.DescriptorUtils;
043    import org.jetbrains.kotlin.resolve.inline.InlineUtil;
044    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
045    import org.jetbrains.kotlin.types.JetType;
046    
047    import java.io.File;
048    
049    import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
050    import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
051    
052    public class JvmCodegenUtil {
053    
054        private JvmCodegenUtil() {
055        }
056    
057        public static boolean isInterface(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 isInterface(JetType type) {
066            return isInterface(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 PackageContext
082                      && isSamePackageInSameModule(context.getParentContext().getContextDescriptor(), containingDeclaration)))
083                    && context.getContextKind() != OwnerKind.TRAIT_IMPL);
084        }
085    
086        private static boolean isSamePackageInSameModule(
087                @NotNull DeclarationDescriptor callerOwner,
088                @NotNull DeclarationDescriptor calleeOwner
089        ) {
090            if (callerOwner instanceof PackageFragmentDescriptor && calleeOwner instanceof PackageFragmentDescriptor) {
091                PackageFragmentDescriptor callerFragment = (PackageFragmentDescriptor) callerOwner;
092                PackageFragmentDescriptor calleeFragment = (PackageFragmentDescriptor) calleeOwner;
093    
094                // backing field should be used directly within same module of same package
095                if (callerFragment == calleeFragment) {
096                    return true;
097                }
098                return callerFragment.getFqName().equals(calleeFragment.getFqName())
099                       && calleeFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment;
100            }
101            return false;
102        }
103    
104        public static boolean isCallInsideSameModuleAsDeclared(
105                @NotNull CallableMemberDescriptor declarationDescriptor,
106                @NotNull CodegenContext context,
107                @Nullable File outDirectory
108        ) {
109            if (context == CodegenContext.STATIC) {
110                return true;
111            }
112            DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
113    
114            CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
115            if (directMember instanceof DeserializedCallableMemberDescriptor) {
116                return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory);
117            }
118            else {
119                return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
120            }
121        }
122    
123        private static boolean isContainedByCompiledPartOfOurModule(
124                @NotNull DeserializedCallableMemberDescriptor descriptor,
125                @Nullable File outDirectory
126        ) {
127            DeclarationDescriptor packageFragment = descriptor.getContainingDeclaration();
128            if (packageFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) {
129                return true;
130            }
131    
132            if (outDirectory == null) {
133                return false;
134            }
135    
136            if (!(packageFragment instanceof LazyJavaPackageFragment)) {
137                return false;
138            }
139    
140            KotlinJvmBinaryClass binaryClass = ((LazyJavaPackageFragment) packageFragment).getMemberScope().getKotlinBinaryClass();
141            if (binaryClass instanceof VirtualFileKotlinClass) {
142                VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile();
143                if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) {
144                    File ioFile = VfsUtilCore.virtualToIoFile(file);
145                    return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator);
146                }
147            }
148    
149            return false;
150        }
151    
152        public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
153            return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
154                                     new Function1<DeclarationDescriptor, Boolean>() {
155                                         @Override
156                                         public Boolean invoke(DeclarationDescriptor descriptor) {
157                                             return descriptor instanceof CallableMemberDescriptor &&
158                                                    ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
159                                         }
160                                     }
161            );
162        }
163    
164        public static boolean couldUseDirectAccessToProperty(
165                @NotNull PropertyDescriptor property,
166                boolean forGetter,
167                boolean isDelegated,
168                @NotNull MethodContext context
169        ) {
170            if (JetTypeMapper.isAccessor(property)) return false;
171    
172            // Inline functions can't use direct access because a field may not be visible at the call site
173            if (context.isInlineFunction() &&
174                (!Visibilities.isPrivate(property.getVisibility()) || DescriptorUtils.isTopLevelDeclaration(property))) {
175                return false;
176            }
177    
178            // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
179            if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
180    
181            // Delegated and extension properties have no backing fields
182            if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
183    
184            // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
185            if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
186    
187            PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
188    
189            // If there's no accessor declared we can use direct access
190            if (accessor == null) return true;
191    
192            // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
193            if (accessor.hasBody()) return false;
194    
195            // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
196            return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
197        }
198    
199        private static boolean isDebuggerContext(@NotNull MethodContext context) {
200            JetFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
201            return file != null && CodeFragmentUtilPackage.getSuppressDiagnosticsInDebugMode(file);
202        }
203    
204        @Nullable
205        public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
206                @NotNull ConstructorDescriptor descriptor,
207                @Nullable CalculatedClosure closure
208        ) {
209            //for compilation against sources
210            if (closure != null) {
211                return closure.getCaptureThis();
212            }
213    
214            //for compilation against binaries
215            //TODO: It's best to use this code also for compilation against sources
216            // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
217            ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
218            if (dispatchReceiver != null) {
219                ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
220                if (!expectedThisClass.getKind().isSingleton()) {
221                    return expectedThisClass;
222                }
223            }
224    
225            return null;
226        }
227    
228        @NotNull
229        public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
230            return descriptor instanceof PropertyAccessorDescriptor
231                   ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
232                   : descriptor;
233        }
234    
235        public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
236            PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
237            return InlineUtil.canBeInlineArgument(declaration) &&
238                   InlineUtil.isInlinedArgument((JetFunction) declaration, bindingContext, false);
239        }
240    }