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