001    /*
002     * Copyright 2010-2016 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.collections.CollectionsKt;
022    import kotlin.jvm.functions.Function1;
023    import kotlin.text.StringsKt;
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.KotlinTypeMapper;
033    import org.jetbrains.kotlin.descriptors.*;
034    import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
035    import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
036    import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
037    import org.jetbrains.kotlin.load.kotlin.*;
038    import org.jetbrains.kotlin.psi.Call;
039    import org.jetbrains.kotlin.psi.KtFile;
040    import org.jetbrains.kotlin.psi.KtFunction;
041    import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
042    import org.jetbrains.kotlin.resolve.BindingContext;
043    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
044    import org.jetbrains.kotlin.resolve.DescriptorUtils;
045    import org.jetbrains.kotlin.resolve.inline.InlineUtil;
046    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
047    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
048    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
049    import org.jetbrains.kotlin.types.KotlinType;
050    import org.jetbrains.org.objectweb.asm.Opcodes;
051    
052    import java.io.File;
053    
054    import static org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS;
055    import static org.jetbrains.kotlin.descriptors.ClassKind.INTERFACE;
056    import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
057    import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
058    import static org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_CALL;
059    import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation;
060    
061    public class JvmCodegenUtil {
062    
063        private JvmCodegenUtil() {
064        }
065    
066        public static boolean isAnnotationOrJvmInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
067            return isAnnotationOrJvmInterfaceWithoutDefaults(descriptor, state.isJvm8Target(), state.isJvm8TargetWithDefaults());
068        }
069    
070        private static boolean isAnnotationOrJvmInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, boolean isJvm8Target, boolean isJvm8TargetWithDefaults) {
071            if (!isJvmInterface(descriptor)) {
072                return false;
073            }
074            if (ANNOTATION_CLASS == ((ClassDescriptor) descriptor).getKind()) return true;
075    
076            if (descriptor instanceof DeserializedClassDescriptor) {
077                SourceElement source = ((DeserializedClassDescriptor) descriptor).getSource();
078                if (source instanceof KotlinJvmBinarySourceElement) {
079                    KotlinJvmBinaryClass binaryClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass();
080                    assert binaryClass instanceof FileBasedKotlinClass :
081                            "KotlinJvmBinaryClass should be subclass of FileBasedKotlinClass, but " + binaryClass;
082                    /*TODO need add some flags to compiled code*/
083                    return true || ((FileBasedKotlinClass) binaryClass).getClassVersion() == Opcodes.V1_6;
084                }
085            }
086            return !isJvm8TargetWithDefaults;
087        }
088    
089        public static boolean isJvm8InterfaceWithDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
090            return isJvm8InterfaceWithDefaults(descriptor, state.isJvm8Target(), state.isJvm8TargetWithDefaults());
091        }
092    
093        public static boolean isJvm8InterfaceWithDefaults(@NotNull DeclarationDescriptor descriptor, boolean isJvm8Target, boolean isJvm8TargetWithDefaults) {
094            return DescriptorUtils.isInterface(descriptor) && !isAnnotationOrJvmInterfaceWithoutDefaults(descriptor, isJvm8Target, isJvm8TargetWithDefaults);
095        }
096    
097        public static boolean isJvm8InterfaceWithDefaultsMember(@NotNull CallableMemberDescriptor descriptor, @NotNull GenerationState state) {
098            DeclarationDescriptor declaration = descriptor.getContainingDeclaration();
099            return isJvm8InterfaceWithDefaults(declaration, state);
100        }
101    
102        public static boolean isNonDefaultInterfaceMember(@NotNull CallableMemberDescriptor descriptor, @NotNull GenerationState state) {
103            if (!isJvmInterface(descriptor.getContainingDeclaration())) {
104                return false;
105            }
106            if (descriptor instanceof JavaCallableMemberDescriptor) {
107                return descriptor.getModality() == Modality.ABSTRACT;
108            }
109    
110            return !isJvm8InterfaceWithDefaultsMember(descriptor, state);
111        }
112    
113        public static boolean isJvmInterface(DeclarationDescriptor descriptor) {
114            if (descriptor instanceof ClassDescriptor) {
115                ClassKind kind = ((ClassDescriptor) descriptor).getKind();
116                return kind == INTERFACE || kind == ANNOTATION_CLASS;
117            }
118            return false;
119        }
120    
121        public static boolean isJvmInterface(KotlinType type) {
122            return isJvmInterface(type.getConstructor().getDeclarationDescriptor());
123        }
124    
125        public static boolean isConst(@NotNull CalculatedClosure closure) {
126            return closure.getCaptureThis() == null &&
127                        closure.getCaptureReceiverType() == null &&
128                        closure.getCaptureVariables().isEmpty() &&
129                        !closure.isSuspend();
130        }
131    
132        private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
133            boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
134            boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
135    
136            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
137    
138            return !isFakeOverride && !isDelegate &&
139                   (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
140                     ((context.getParentContext() instanceof FacadePartWithSourceFile)
141                      && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor)))
142                    && context.getContextKind() != OwnerKind.DEFAULT_IMPLS);
143        }
144    
145        private static boolean isWithinSameFile(
146                @Nullable KtFile callerFile,
147                @NotNull CallableMemberDescriptor descriptor
148        ) {
149            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
150            if (containingDeclaration instanceof PackageFragmentDescriptor) {
151                PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
152                PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null;
153                return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile;
154    
155            }
156            return false;
157        }
158    
159        public static boolean isCallInsideSameModuleAsDeclared(
160                @NotNull CallableMemberDescriptor declarationDescriptor,
161                @NotNull CodegenContext context,
162                @Nullable File outDirectory
163        ) {
164            if (context instanceof RootContext) {
165                return true;
166            }
167            DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
168    
169            CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
170            if (directMember instanceof DeserializedCallableMemberDescriptor) {
171                return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory);
172            }
173            else {
174                return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
175            }
176        }
177    
178        public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
179            return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()),
180                                     new Function1<DeclarationDescriptor, Boolean>() {
181                                         @Override
182                                         public Boolean invoke(DeclarationDescriptor descriptor) {
183                                             return descriptor instanceof CallableMemberDescriptor &&
184                                                    ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
185                                         }
186                                     }
187            );
188        }
189    
190        public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) {
191            return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor);
192        }
193    
194        public static boolean couldUseDirectAccessToProperty(
195                @NotNull PropertyDescriptor property,
196                boolean forGetter,
197                boolean isDelegated,
198                @NotNull MethodContext contextBeforeInline,
199                boolean shouldInlineConstVals
200        ) {
201            if (shouldInlineConstVals && property.isConst()) return true;
202    
203            if (KotlinTypeMapper.isAccessor(property)) return false;
204    
205            CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
206            // Inline functions can't use direct access because a field may not be visible at the call site
207            if (context.isInlineMethodContext()) {
208                return false;
209            }
210    
211            if (!isCallInsideSameClassAsDeclared(property, context)) {
212                if (!isDebuggerContext(context)) {
213                    // Unless we are evaluating expression in debugger context, only properties of the same class can be directly accessed
214                    return false;
215                }
216                else {
217                    // In debugger we want to access through accessors if they are generated
218    
219                    // Non default accessors must always be generated
220                    for (PropertyAccessorDescriptor accessorDescriptor : property.getAccessors()) {
221                        if (!accessorDescriptor.isDefault()) {
222                            if (forGetter == accessorDescriptor instanceof PropertyGetterDescriptor) {
223                                return false;
224                            }
225                        }
226                    }
227    
228                    // If property overrides something, accessors must be generated too
229                    if (!property.getOverriddenDescriptors().isEmpty()) return false;
230                }
231            }
232    
233            // Delegated and extension properties have no backing fields
234            if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
235    
236            // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
237            if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
238    
239            PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
240    
241            // If there's no accessor declared we can use direct access
242            if (accessor == null) return true;
243    
244            // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
245            if (DescriptorPsiUtilsKt.hasBody(accessor)) return false;
246    
247            // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
248            return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
249        }
250    
251        private static boolean isDebuggerContext(@NotNull CodegenContext context) {
252            KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
253            return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file);
254        }
255    
256        @Nullable
257        public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
258                @NotNull ConstructorDescriptor descriptor,
259                @Nullable CalculatedClosure closure
260        ) {
261            //for compilation against sources
262            if (closure != null) {
263                return closure.getCaptureThis();
264            }
265    
266            //for compilation against binaries
267            //TODO: It's best to use this code also for compilation against sources
268            // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
269            ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
270            if (dispatchReceiver != null) {
271                ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
272                if (!expectedThisClass.getKind().isSingleton()) {
273                    return expectedThisClass;
274                }
275            }
276    
277            return null;
278        }
279    
280        @NotNull
281        public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
282            return DescriptorUtils.getDirectMember(descriptor);
283        }
284    
285        public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
286            PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
287            return InlineUtil.canBeInlineArgument(declaration) &&
288                   InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false);
289        }
290    
291        @NotNull
292        public static String getModuleName(ModuleDescriptor module) {
293            return StringsKt.removeSurrounding(module.getName().asString(), "<", ">");
294        }
295    
296        @NotNull
297        public static String getMappingFileName(@NotNull String moduleName) {
298            return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
299        }
300    
301        public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) {
302            if (!(descriptor instanceof JavaPropertyDescriptor)) return false;
303            return descriptor.isConst();
304        }
305    
306        @Nullable
307        public static KotlinType getPropertyDelegateType(
308                @NotNull VariableDescriptorWithAccessors descriptor,
309                @NotNull BindingContext bindingContext
310        ) {
311            VariableAccessorDescriptor getter = descriptor.getGetter();
312            if (getter != null) {
313                Call call = bindingContext.get(DELEGATED_PROPERTY_CALL, getter);
314                if (call != null) {
315                    assert call.getExplicitReceiver() != null : "No explicit receiver for call:" + call;
316                    return ((ReceiverValue) call.getExplicitReceiver()).getType();
317                }
318            }
319            return null;
320        }
321    
322        public static boolean isDelegatedLocalVariable(@NotNull DeclarationDescriptor descriptor) {
323            return descriptor instanceof LocalVariableDescriptor && ((LocalVariableDescriptor) descriptor).isDelegated();
324        }
325    }