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 com.intellij.psi.PsiFile;
021    import kotlin.CollectionsKt;
022    import kotlin.StringsKt;
023    import kotlin.jvm.functions.Function1;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
027    import org.jetbrains.kotlin.codegen.context.CodegenContext;
028    import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile;
029    import org.jetbrains.kotlin.codegen.context.MethodContext;
030    import org.jetbrains.kotlin.codegen.context.RootContext;
031    import org.jetbrains.kotlin.codegen.state.GenerationState;
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.kotlin.ModuleMapping;
037    import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt;
038    import org.jetbrains.kotlin.psi.KtFile;
039    import org.jetbrains.kotlin.psi.KtFunction;
040    import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
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.inline.InlineUtil;
045    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
046    import org.jetbrains.kotlin.types.KotlinType;
047    import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
048    
049    import java.io.File;
050    
051    import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
052    import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
053    
054    public class JvmCodegenUtil {
055    
056        private JvmCodegenUtil() {
057        }
058    
059        public static boolean isJvmInterface(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 isJvmInterface(KotlinType type) {
068            return isJvmInterface(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 FacadePartWithSourceFile)
084                      && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor)))
085                    && context.getContextKind() != OwnerKind.DEFAULT_IMPLS);
086        }
087    
088        private static boolean isWithinSameFile(
089                @Nullable KtFile callerFile,
090                @NotNull CallableMemberDescriptor descriptor
091        ) {
092            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
093            if (containingDeclaration instanceof PackageFragmentDescriptor) {
094                PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
095                PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null;
096                return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile;
097    
098            }
099            return false;
100        }
101    
102        public static boolean isCallInsideSameModuleAsDeclared(
103                @NotNull CallableMemberDescriptor declarationDescriptor,
104                @NotNull CodegenContext context,
105                @Nullable File outDirectory
106        ) {
107            if (context instanceof RootContext) {
108                return true;
109            }
110            DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
111    
112            CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
113            if (directMember instanceof DeserializedCallableMemberDescriptor) {
114                return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory);
115            }
116            else {
117                return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
118            }
119        }
120    
121        public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
122            return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()),
123                                     new Function1<DeclarationDescriptor, Boolean>() {
124                                         @Override
125                                         public Boolean invoke(DeclarationDescriptor descriptor) {
126                                             return descriptor instanceof CallableMemberDescriptor &&
127                                                    ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
128                                         }
129                                     }
130            );
131        }
132    
133        public static boolean couldUseDirectAccessToProperty(
134                @NotNull PropertyDescriptor property,
135                boolean forGetter,
136                boolean isDelegated,
137                @NotNull MethodContext contextBeforeInline
138        ) {
139            if (JetTypeMapper.isAccessor(property)) return false;
140    
141            CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
142            // Inline functions can't use direct access because a field may not be visible at the call site
143            if (context.isInlineMethodContext()) {
144                return false;
145            }
146    
147            // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
148            if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
149    
150            // Delegated and extension properties have no backing fields
151            if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
152    
153            // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
154            if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
155    
156            PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
157    
158            // If there's no accessor declared we can use direct access
159            if (accessor == null) return true;
160    
161            // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
162            if (accessor.hasBody()) return false;
163    
164            // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
165            return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
166        }
167    
168        private static boolean isDebuggerContext(@NotNull CodegenContext context) {
169            KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
170            return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file);
171        }
172    
173        @Nullable
174        public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
175                @NotNull ConstructorDescriptor descriptor,
176                @Nullable CalculatedClosure closure
177        ) {
178            //for compilation against sources
179            if (closure != null) {
180                return closure.getCaptureThis();
181            }
182    
183            //for compilation against binaries
184            //TODO: It's best to use this code also for compilation against sources
185            // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
186            ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
187            if (dispatchReceiver != null) {
188                ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
189                if (!expectedThisClass.getKind().isSingleton()) {
190                    return expectedThisClass;
191                }
192            }
193    
194            return null;
195        }
196    
197        @NotNull
198        public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
199            return descriptor instanceof PropertyAccessorDescriptor
200                   ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
201                   : descriptor;
202        }
203    
204        public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
205            PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
206            return InlineUtil.canBeInlineArgument(declaration) &&
207                   InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false);
208        }
209    
210        @NotNull
211        public static String getModuleName(ModuleDescriptor module) {
212            return StringsKt.removeSurrounding(module.getName().asString(), "<", ">");
213        }
214    
215        @NotNull
216        public static String getMappingFileName(@NotNull String moduleName) {
217            return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
218        }
219    
220        public static void writeAbiVersion(@NotNull AnnotationVisitor av) {
221            av.visit(JvmAnnotationNames.VERSION_FIELD_NAME, JvmAbi.VERSION.toArray());
222    
223            // TODO: drop after some time
224            av.visit(JvmAnnotationNames.OLD_ABI_VERSION_FIELD_NAME, 32);
225        }
226    
227        public static void writeModuleName(@NotNull AnnotationVisitor av, @NotNull GenerationState state) {
228            String name = state.getModuleName();
229            if (!name.equals(JvmAbi.DEFAULT_MODULE_NAME)) {
230                av.visit(JvmAnnotationNames.MODULE_NAME_FIELD_NAME, name);
231            }
232        }
233    }