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