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