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.psi.PsiElement; 020 import com.intellij.psi.PsiFile; 021 import kotlin.collections.CollectionsKt; 022 import kotlin.jvm.functions.Function1; 023 import kotlin.text.StringsKt; 024 import org.jetbrains.annotations.NotNull; 025 import org.jetbrains.annotations.Nullable; 026 import org.jetbrains.kotlin.codegen.binding.CalculatedClosure; 027 import org.jetbrains.kotlin.codegen.context.CodegenContext; 028 import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile; 029 import org.jetbrains.kotlin.codegen.context.MethodContext; 030 import org.jetbrains.kotlin.codegen.context.RootContext; 031 import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; 032 import org.jetbrains.kotlin.descriptors.*; 033 import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor; 034 import org.jetbrains.kotlin.load.kotlin.ModuleMapping; 035 import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt; 036 import org.jetbrains.kotlin.psi.KtFile; 037 import org.jetbrains.kotlin.psi.KtFunction; 038 import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt; 039 import org.jetbrains.kotlin.resolve.BindingContext; 040 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; 041 import org.jetbrains.kotlin.resolve.DescriptorUtils; 042 import org.jetbrains.kotlin.resolve.inline.InlineUtil; 043 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; 044 import org.jetbrains.kotlin.types.KotlinType; 045 046 import java.io.File; 047 048 import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT; 049 import static org.jetbrains.kotlin.descriptors.Modality.FINAL; 050 import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation; 051 052 public class JvmCodegenUtil { 053 054 private JvmCodegenUtil() { 055 } 056 057 public static boolean isJvmInterface(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 isJvmInterface(KotlinType type) { 066 return isJvmInterface(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 FacadePartWithSourceFile) 082 && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor))) 083 && context.getContextKind() != OwnerKind.DEFAULT_IMPLS); 084 } 085 086 private static boolean isWithinSameFile( 087 @Nullable KtFile callerFile, 088 @NotNull CallableMemberDescriptor descriptor 089 ) { 090 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal(); 091 if (containingDeclaration instanceof PackageFragmentDescriptor) { 092 PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor); 093 PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null; 094 return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile; 095 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 instanceof RootContext) { 106 return true; 107 } 108 DeclarationDescriptor contextDescriptor = context.getContextDescriptor(); 109 110 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor); 111 if (directMember instanceof DeserializedCallableMemberDescriptor) { 112 return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory); 113 } 114 else { 115 return DescriptorUtils.areInSameModule(directMember, contextDescriptor); 116 } 117 } 118 119 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) { 120 return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()), 121 new Function1<DeclarationDescriptor, Boolean>() { 122 @Override 123 public Boolean invoke(DeclarationDescriptor descriptor) { 124 return descriptor instanceof CallableMemberDescriptor && 125 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT; 126 } 127 } 128 ); 129 } 130 131 public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) { 132 return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor); 133 } 134 135 public static boolean couldUseDirectAccessToProperty( 136 @NotNull PropertyDescriptor property, 137 boolean forGetter, 138 boolean isDelegated, 139 @NotNull MethodContext contextBeforeInline 140 ) { 141 if (KotlinTypeMapper.isAccessor(property)) return false; 142 143 CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext(); 144 // Inline functions can't use direct access because a field may not be visible at the call site 145 if (context.isInlineMethodContext()) { 146 return false; 147 } 148 149 if (!isCallInsideSameClassAsDeclared(property, context)) { 150 if (!isDebuggerContext(context)) { 151 // Unless we are evaluating expression in debugger context, only properties of the same class can be directly accessed 152 return false; 153 } 154 else { 155 // In debugger we want to access through accessors if they are generated 156 157 // Non default accessors must always be generated 158 for (PropertyAccessorDescriptor accessorDescriptor : property.getAccessors()) { 159 if (!accessorDescriptor.isDefault()) { 160 if (forGetter == accessorDescriptor instanceof PropertyGetterDescriptor) { 161 return false; 162 } 163 } 164 } 165 166 // If property overrides something, accessors must be generated too 167 if (!property.getOverriddenDescriptors().isEmpty()) return false; 168 } 169 } 170 171 // Delegated and extension properties have no backing fields 172 if (isDelegated || property.getExtensionReceiverParameter() != null) return false; 173 174 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class 175 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false; 176 177 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter(); 178 179 // If there's no accessor declared we can use direct access 180 if (accessor == null) return true; 181 182 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access 183 if (DescriptorPsiUtilsKt.hasBody(accessor)) return false; 184 185 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access 186 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL; 187 } 188 189 private static boolean isDebuggerContext(@NotNull CodegenContext context) { 190 KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor()); 191 return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file); 192 } 193 194 @Nullable 195 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall( 196 @NotNull ConstructorDescriptor descriptor, 197 @Nullable CalculatedClosure closure 198 ) { 199 //for compilation against sources 200 if (closure != null) { 201 return closure.getCaptureThis(); 202 } 203 204 //for compilation against binaries 205 //TODO: It's best to use this code also for compilation against sources 206 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes 207 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter(); 208 if (dispatchReceiver != null) { 209 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration(); 210 if (!expectedThisClass.getKind().isSingleton()) { 211 return expectedThisClass; 212 } 213 } 214 215 return null; 216 } 217 218 @NotNull 219 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) { 220 return descriptor instanceof PropertyAccessorDescriptor 221 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty() 222 : descriptor; 223 } 224 225 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) { 226 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor); 227 return InlineUtil.canBeInlineArgument(declaration) && 228 InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false); 229 } 230 231 @NotNull 232 public static String getModuleName(ModuleDescriptor module) { 233 return StringsKt.removeSurrounding(module.getName().asString(), "<", ">"); 234 } 235 236 @NotNull 237 public static String getMappingFileName(@NotNull String moduleName) { 238 return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT; 239 } 240 241 public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) { 242 if (!(descriptor instanceof JavaPropertyDescriptor)) return false; 243 return descriptor.isConst(); 244 } 245 }