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