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 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger 150 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false; 151 152 // Delegated and extension properties have no backing fields 153 if (isDelegated || property.getExtensionReceiverParameter() != null) return false; 154 155 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class 156 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false; 157 158 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter(); 159 160 // If there's no accessor declared we can use direct access 161 if (accessor == null) return true; 162 163 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access 164 if (DescriptorPsiUtilsKt.hasBody(accessor)) return false; 165 166 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access 167 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL; 168 } 169 170 private static boolean isDebuggerContext(@NotNull CodegenContext context) { 171 KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor()); 172 return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file); 173 } 174 175 @Nullable 176 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall( 177 @NotNull ConstructorDescriptor descriptor, 178 @Nullable CalculatedClosure closure 179 ) { 180 //for compilation against sources 181 if (closure != null) { 182 return closure.getCaptureThis(); 183 } 184 185 //for compilation against binaries 186 //TODO: It's best to use this code also for compilation against sources 187 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes 188 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter(); 189 if (dispatchReceiver != null) { 190 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration(); 191 if (!expectedThisClass.getKind().isSingleton()) { 192 return expectedThisClass; 193 } 194 } 195 196 return null; 197 } 198 199 @NotNull 200 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) { 201 return descriptor instanceof PropertyAccessorDescriptor 202 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty() 203 : descriptor; 204 } 205 206 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) { 207 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor); 208 return InlineUtil.canBeInlineArgument(declaration) && 209 InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false); 210 } 211 212 @NotNull 213 public static String getModuleName(ModuleDescriptor module) { 214 return StringsKt.removeSurrounding(module.getName().asString(), "<", ">"); 215 } 216 217 @NotNull 218 public static String getMappingFileName(@NotNull String moduleName) { 219 return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT; 220 } 221 222 public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) { 223 if (!(descriptor instanceof JavaPropertyDescriptor)) return false; 224 return descriptor.isConst(); 225 } 226 }