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.CollectionsKt; 022 import kotlin.StringsKt; 023 import kotlin.jvm.functions.Function1; 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.GenerationState; 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.kotlin.ModuleMapping; 037 import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt; 038 import org.jetbrains.kotlin.psi.KtFile; 039 import org.jetbrains.kotlin.psi.KtFunction; 040 import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt; 041 import org.jetbrains.kotlin.resolve.BindingContext; 042 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; 043 import org.jetbrains.kotlin.resolve.DescriptorUtils; 044 import org.jetbrains.kotlin.resolve.inline.InlineUtil; 045 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; 046 import org.jetbrains.kotlin.types.KotlinType; 047 import org.jetbrains.org.objectweb.asm.AnnotationVisitor; 048 049 import java.io.File; 050 051 import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT; 052 import static org.jetbrains.kotlin.descriptors.Modality.FINAL; 053 054 public class JvmCodegenUtil { 055 056 private JvmCodegenUtil() { 057 } 058 059 public static boolean isJvmInterface(DeclarationDescriptor descriptor) { 060 if (descriptor instanceof ClassDescriptor) { 061 ClassKind kind = ((ClassDescriptor) descriptor).getKind(); 062 return kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS; 063 } 064 return false; 065 } 066 067 public static boolean isJvmInterface(KotlinType type) { 068 return isJvmInterface(type.getConstructor().getDeclarationDescriptor()); 069 } 070 071 public static boolean isConst(@NotNull CalculatedClosure closure) { 072 return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty(); 073 } 074 075 private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) { 076 boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE; 077 boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION; 078 079 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal(); 080 081 return !isFakeOverride && !isDelegate && 082 (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) || 083 ((context.getParentContext() instanceof FacadePartWithSourceFile) 084 && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor))) 085 && context.getContextKind() != OwnerKind.DEFAULT_IMPLS); 086 } 087 088 private static boolean isWithinSameFile( 089 @Nullable KtFile callerFile, 090 @NotNull CallableMemberDescriptor descriptor 091 ) { 092 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal(); 093 if (containingDeclaration instanceof PackageFragmentDescriptor) { 094 PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor); 095 PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null; 096 return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile; 097 098 } 099 return false; 100 } 101 102 public static boolean isCallInsideSameModuleAsDeclared( 103 @NotNull CallableMemberDescriptor declarationDescriptor, 104 @NotNull CodegenContext context, 105 @Nullable File outDirectory 106 ) { 107 if (context instanceof RootContext) { 108 return true; 109 } 110 DeclarationDescriptor contextDescriptor = context.getContextDescriptor(); 111 112 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor); 113 if (directMember instanceof DeserializedCallableMemberDescriptor) { 114 return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory); 115 } 116 else { 117 return DescriptorUtils.areInSameModule(directMember, contextDescriptor); 118 } 119 } 120 121 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) { 122 return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()), 123 new Function1<DeclarationDescriptor, Boolean>() { 124 @Override 125 public Boolean invoke(DeclarationDescriptor descriptor) { 126 return descriptor instanceof CallableMemberDescriptor && 127 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT; 128 } 129 } 130 ); 131 } 132 133 public static boolean couldUseDirectAccessToProperty( 134 @NotNull PropertyDescriptor property, 135 boolean forGetter, 136 boolean isDelegated, 137 @NotNull MethodContext contextBeforeInline 138 ) { 139 if (JetTypeMapper.isAccessor(property)) return false; 140 141 CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext(); 142 // Inline functions can't use direct access because a field may not be visible at the call site 143 if (context.isInlineMethodContext()) { 144 return false; 145 } 146 147 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger 148 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false; 149 150 // Delegated and extension properties have no backing fields 151 if (isDelegated || property.getExtensionReceiverParameter() != null) return false; 152 153 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class 154 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false; 155 156 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter(); 157 158 // If there's no accessor declared we can use direct access 159 if (accessor == null) return true; 160 161 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access 162 if (accessor.hasBody()) return false; 163 164 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access 165 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL; 166 } 167 168 private static boolean isDebuggerContext(@NotNull CodegenContext context) { 169 KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor()); 170 return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file); 171 } 172 173 @Nullable 174 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall( 175 @NotNull ConstructorDescriptor descriptor, 176 @Nullable CalculatedClosure closure 177 ) { 178 //for compilation against sources 179 if (closure != null) { 180 return closure.getCaptureThis(); 181 } 182 183 //for compilation against binaries 184 //TODO: It's best to use this code also for compilation against sources 185 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes 186 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter(); 187 if (dispatchReceiver != null) { 188 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration(); 189 if (!expectedThisClass.getKind().isSingleton()) { 190 return expectedThisClass; 191 } 192 } 193 194 return null; 195 } 196 197 @NotNull 198 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) { 199 return descriptor instanceof PropertyAccessorDescriptor 200 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty() 201 : descriptor; 202 } 203 204 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) { 205 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor); 206 return InlineUtil.canBeInlineArgument(declaration) && 207 InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false); 208 } 209 210 @NotNull 211 public static String getModuleName(ModuleDescriptor module) { 212 return StringsKt.removeSurrounding(module.getName().asString(), "<", ">"); 213 } 214 215 @NotNull 216 public static String getMappingFileName(@NotNull String moduleName) { 217 return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT; 218 } 219 220 public static void writeAbiVersion(@NotNull AnnotationVisitor av) { 221 av.visit(JvmAnnotationNames.VERSION_FIELD_NAME, JvmAbi.VERSION.toArray()); 222 223 // TODO: drop after some time 224 av.visit(JvmAnnotationNames.OLD_ABI_VERSION_FIELD_NAME, 32); 225 } 226 227 public static void writeModuleName(@NotNull AnnotationVisitor av, @NotNull GenerationState state) { 228 String name = state.getModuleName(); 229 if (!name.equals(JvmAbi.DEFAULT_MODULE_NAME)) { 230 av.visit(JvmAnnotationNames.MODULE_NAME_FIELD_NAME, name); 231 } 232 } 233 }