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