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 com.intellij.util.containers.Stack; 023 import kotlin.Function1; 024 import kotlin.KotlinPackage; 025 import org.jetbrains.annotations.NotNull; 026 import org.jetbrains.annotations.Nullable; 027 import org.jetbrains.jet.codegen.binding.CalculatedClosure; 028 import org.jetbrains.jet.codegen.context.CodegenContext; 029 import org.jetbrains.jet.codegen.context.MethodContext; 030 import org.jetbrains.jet.codegen.context.PackageContext; 031 import org.jetbrains.jet.codegen.state.JetTypeMapper; 032 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor; 033 import org.jetbrains.jet.lang.descriptors.*; 034 import org.jetbrains.jet.lang.psi.JetFile; 035 import org.jetbrains.jet.lang.psi.codeFragmentUtil.CodeFragmentUtilPackage; 036 import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils; 037 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 038 import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil; 039 import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPackageFragmentDescriptor; 040 import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyPackageFragmentScopeForJavaPackage; 041 import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass; 042 import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileKotlinClass; 043 import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalPackageFragmentProvider; 044 import org.jetbrains.jet.lang.resolve.name.Name; 045 import org.jetbrains.jet.lang.resolve.scopes.JetScope; 046 import org.jetbrains.jet.lang.types.JetType; 047 import org.jetbrains.jet.lang.types.TypeUtils; 048 import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 049 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 050 051 import java.io.File; 052 import java.util.Arrays; 053 import java.util.Collection; 054 import java.util.List; 055 056 import static org.jetbrains.jet.lang.descriptors.Modality.ABSTRACT; 057 import static org.jetbrains.jet.lang.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.TRAIT || 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 public static <T> T peekFromStack(Stack<T> stack) { 081 return stack.empty() ? null : stack.peek(); 082 } 083 084 @Nullable 085 public static FunctionDescriptor getDeclaredFunctionByRawSignature( 086 @NotNull ClassDescriptor owner, 087 @NotNull Name name, 088 @NotNull ClassifierDescriptor returnedClassifier, 089 @NotNull ClassifierDescriptor... valueParameterClassifiers 090 ) { 091 Collection<FunctionDescriptor> functions = owner.getDefaultType().getMemberScope().getFunctions(name); 092 for (FunctionDescriptor function : functions) { 093 if (!CallResolverUtil.isOrOverridesSynthesized(function) 094 && function.getTypeParameters().isEmpty() 095 && valueParameterClassesMatch(function.getValueParameters(), Arrays.asList(valueParameterClassifiers)) 096 && rawTypeMatches(function.getReturnType(), returnedClassifier)) { 097 return function; 098 } 099 } 100 return null; 101 } 102 103 private static boolean valueParameterClassesMatch( 104 @NotNull List<ValueParameterDescriptor> parameters, 105 @NotNull List<ClassifierDescriptor> classifiers) { 106 if (parameters.size() != classifiers.size()) return false; 107 for (int i = 0; i < parameters.size(); i++) { 108 ValueParameterDescriptor parameterDescriptor = parameters.get(i); 109 ClassifierDescriptor classDescriptor = classifiers.get(i); 110 if (!rawTypeMatches(parameterDescriptor.getType(), classDescriptor)) { 111 return false; 112 } 113 } 114 return true; 115 } 116 117 private static boolean rawTypeMatches(JetType type, ClassifierDescriptor classifier) { 118 return type.getConstructor().getDeclarationDescriptor().getOriginal() == classifier.getOriginal(); 119 } 120 121 private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) { 122 boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE; 123 boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION; 124 125 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal(); 126 127 return !isFakeOverride && !isDelegate && 128 (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) || 129 (context.getParentContext() instanceof PackageContext 130 && isSamePackageInSameModule(context.getParentContext().getContextDescriptor(), containingDeclaration))) 131 && context.getContextKind() != OwnerKind.TRAIT_IMPL); 132 } 133 134 private static boolean isSamePackageInSameModule( 135 @NotNull DeclarationDescriptor callerOwner, 136 @NotNull DeclarationDescriptor calleeOwner 137 ) { 138 if (callerOwner instanceof PackageFragmentDescriptor && calleeOwner instanceof PackageFragmentDescriptor) { 139 PackageFragmentDescriptor callerFragment = (PackageFragmentDescriptor) callerOwner; 140 PackageFragmentDescriptor calleeFragment = (PackageFragmentDescriptor) calleeOwner; 141 142 // backing field should be used directly within same module of same package 143 if (callerFragment == calleeFragment) { 144 return true; 145 } 146 return callerFragment.getFqName().equals(calleeFragment.getFqName()) 147 && calleeFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment; 148 } 149 return false; 150 } 151 152 public static boolean isCallInsideSameModuleAsDeclared( 153 @NotNull CallableMemberDescriptor declarationDescriptor, 154 @NotNull CodegenContext context, 155 @Nullable File outDirectory 156 ) { 157 if (context == CodegenContext.STATIC) { 158 return true; 159 } 160 DeclarationDescriptor contextDescriptor = context.getContextDescriptor(); 161 162 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor); 163 if (directMember instanceof DeserializedCallableMemberDescriptor) { 164 return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory); 165 } 166 else { 167 return DescriptorUtils.areInSameModule(directMember, contextDescriptor); 168 } 169 } 170 171 private static boolean isContainedByCompiledPartOfOurModule( 172 @NotNull DeserializedCallableMemberDescriptor descriptor, 173 @Nullable File outDirectory 174 ) { 175 if (descriptor.getContainingDeclaration() instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) { 176 return true; 177 } 178 179 if (outDirectory == null) { 180 return false; 181 } 182 183 if (!(descriptor.getContainingDeclaration() instanceof JavaPackageFragmentDescriptor)) { 184 return false; 185 } 186 JavaPackageFragmentDescriptor packageFragment = (JavaPackageFragmentDescriptor) descriptor.getContainingDeclaration(); 187 JetScope packageScope = packageFragment.getMemberScope(); 188 if (!(packageScope instanceof LazyPackageFragmentScopeForJavaPackage)) { 189 return false; 190 } 191 KotlinJvmBinaryClass binaryClass = ((LazyPackageFragmentScopeForJavaPackage) packageScope).getKotlinBinaryClass(); 192 193 if (binaryClass instanceof VirtualFileKotlinClass) { 194 VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile(); 195 if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) { 196 File ioFile = VfsUtilCore.virtualToIoFile(file); 197 return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator); 198 } 199 } 200 return false; 201 } 202 203 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) { 204 return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(), 205 new Function1<DeclarationDescriptor, Boolean>() { 206 @Override 207 public Boolean invoke(DeclarationDescriptor descriptor) { 208 return descriptor instanceof CallableMemberDescriptor && 209 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT; 210 } 211 } 212 ); 213 } 214 215 public static boolean couldUseDirectAccessToProperty( 216 @NotNull PropertyDescriptor property, 217 boolean forGetter, 218 boolean isDelegated, 219 @NotNull MethodContext context 220 ) { 221 if (JetTypeMapper.isAccessor(property)) return false; 222 223 // Inline functions can't use direct access because a field may not be visible at the call site 224 if (context.isInlineFunction()) return false; 225 226 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger 227 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false; 228 229 // Delegated and extension properties have no backing fields 230 if (isDelegated || property.getReceiverParameter() != null) return false; 231 232 // Class object properties cannot be accessed directly because their backing fields are stored in the containing class 233 if (DescriptorUtils.isClassObject(property.getContainingDeclaration())) return false; 234 235 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter(); 236 237 // If there's no accessor declared we can use direct access 238 if (accessor == null) return true; 239 240 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access 241 if (accessor.hasBody()) return false; 242 243 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access 244 return property.getVisibility() == Visibilities.PRIVATE || accessor.getModality() == FINAL; 245 } 246 247 private static boolean isDebuggerContext(@NotNull MethodContext context) { 248 JetFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor()); 249 return file != null && CodeFragmentUtilPackage.getSkipVisibilityCheck(file); 250 } 251 252 @NotNull 253 public static ImplementationBodyCodegen getParentBodyCodegen(@Nullable MemberCodegen<?> classBodyCodegen) { 254 assert classBodyCodegen != null && classBodyCodegen.getParentCodegen() instanceof ImplementationBodyCodegen 255 : "Class object should have appropriate parent BodyCodegen"; 256 257 return (ImplementationBodyCodegen) classBodyCodegen.getParentCodegen(); 258 } 259 260 @Nullable 261 public static ClassDescriptor getExpectedThisObjectForConstructorCall( 262 @NotNull ConstructorDescriptor descriptor, 263 @Nullable CalculatedClosure closure 264 ) { 265 //for compilation against sources 266 if (closure != null) { 267 return closure.getCaptureThis(); 268 } 269 270 //for compilation against binaries 271 //TODO: It's best to use this code also for compilation against sources 272 // but sometimes structures that have expectedThisObject (bug?) mapped to static classes 273 ReceiverParameterDescriptor expectedThisObject = descriptor.getExpectedThisObject(); 274 if (expectedThisObject != null) { 275 ClassDescriptor expectedThisClass = (ClassDescriptor) expectedThisObject.getContainingDeclaration(); 276 if (!expectedThisClass.getKind().isSingleton()) { 277 return expectedThisClass; 278 } 279 } 280 281 return null; 282 } 283 284 public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) { 285 List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters(); 286 JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType()); 287 return "valueOf".equals(functionDescriptor.getName().asString()) 288 && methodTypeParameters.size() == 1 289 && JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString); 290 } 291 292 public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) { 293 List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters(); 294 return "values".equals(functionDescriptor.getName().asString()) 295 && methodTypeParameters.isEmpty(); 296 } 297 298 @NotNull 299 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) { 300 return descriptor instanceof PropertyAccessorDescriptor 301 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty() 302 : descriptor; 303 } 304 }