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