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