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