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.resolve.calls.model.ResolvedCall; 046 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; 047 import org.jetbrains.kotlin.types.JetType; 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 isInterface(DeclarationDescriptor descriptor) { 060 if (descriptor instanceof ClassDescriptor) { 061 ClassKind kind = ((ClassDescriptor) descriptor).getKind(); 062 return kind == ClassKind.TRAIT || 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 == CodegenContext.STATIC) { 112 return true; 113 } 114 DeclarationDescriptor contextDescriptor = context.getContextDescriptor(); 115 116 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor); 117 if (directMember instanceof DeserializedCallableMemberDescriptor) { 118 return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory); 119 } 120 else { 121 return DescriptorUtils.areInSameModule(directMember, contextDescriptor); 122 } 123 } 124 125 private static boolean isContainedByCompiledPartOfOurModule( 126 @NotNull DeserializedCallableMemberDescriptor descriptor, 127 @Nullable File outDirectory 128 ) { 129 DeclarationDescriptor packageFragment = descriptor.getContainingDeclaration(); 130 if (packageFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) { 131 return true; 132 } 133 134 if (outDirectory == null) { 135 return false; 136 } 137 138 if (!(packageFragment instanceof LazyJavaPackageFragment)) { 139 return false; 140 } 141 142 KotlinJvmBinaryClass binaryClass = ((LazyJavaPackageFragment) packageFragment).getMemberScope().getKotlinBinaryClass(); 143 if (binaryClass instanceof VirtualFileKotlinClass) { 144 VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile(); 145 if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) { 146 File ioFile = VfsUtilCore.virtualToIoFile(file); 147 return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator); 148 } 149 } 150 151 return false; 152 } 153 154 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) { 155 return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(), 156 new Function1<DeclarationDescriptor, Boolean>() { 157 @Override 158 public Boolean invoke(DeclarationDescriptor descriptor) { 159 return descriptor instanceof CallableMemberDescriptor && 160 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT; 161 } 162 } 163 ); 164 } 165 166 public static boolean couldUseDirectAccessToProperty( 167 @NotNull PropertyDescriptor property, 168 boolean forGetter, 169 boolean isDelegated, 170 @NotNull MethodContext context 171 ) { 172 if (JetTypeMapper.isAccessor(property)) return false; 173 174 // Inline functions can't use direct access because a field may not be visible at the call site 175 if (context.isInlineFunction() && !Visibilities.isPrivate(property.getVisibility())) return false; 176 177 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger 178 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false; 179 180 // Delegated and extension properties have no backing fields 181 if (isDelegated || property.getExtensionReceiverParameter() != null) return false; 182 183 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class 184 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false; 185 186 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter(); 187 188 // If there's no accessor declared we can use direct access 189 if (accessor == null) return true; 190 191 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access 192 if (accessor.hasBody()) return false; 193 194 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access 195 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL; 196 } 197 198 private static boolean isDebuggerContext(@NotNull MethodContext context) { 199 JetFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor()); 200 return file != null && CodeFragmentUtilPackage.getSuppressDiagnosticsInDebugMode(file); 201 } 202 203 @Nullable 204 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall( 205 @NotNull ConstructorDescriptor descriptor, 206 @Nullable CalculatedClosure closure 207 ) { 208 //for compilation against sources 209 if (closure != null) { 210 return closure.getCaptureThis(); 211 } 212 213 //for compilation against binaries 214 //TODO: It's best to use this code also for compilation against sources 215 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes 216 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter(); 217 if (dispatchReceiver != null) { 218 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration(); 219 if (!expectedThisClass.getKind().isSingleton()) { 220 return expectedThisClass; 221 } 222 } 223 224 return null; 225 } 226 227 @NotNull 228 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) { 229 return descriptor instanceof PropertyAccessorDescriptor 230 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty() 231 : descriptor; 232 } 233 234 public static boolean isLambdaWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) { 235 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor); 236 return declaration instanceof JetFunctionLiteral && 237 declaration.getParent() instanceof JetFunctionLiteralExpression && 238 InlineDescriptorUtils.isInlineLambda((JetFunctionLiteralExpression) declaration.getParent(), bindingContext, false); 239 } 240 241 @Nullable 242 public static ResolvedCall<ConstructorDescriptor> getDelegationConstructorCall( 243 @NotNull BindingContext bindingContext, 244 @NotNull ConstructorDescriptor constructorDescriptor 245 ) { 246 return bindingContext.get(BindingContext.CONSTRUCTOR_RESOLVED_DELEGATION_CALL, constructorDescriptor); 247 } 248 }