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.load.java.sam; 018 019 import org.jetbrains.annotations.NotNull; 020 import org.jetbrains.annotations.Nullable; 021 import org.jetbrains.kotlin.builtins.KotlinBuiltIns; 022 import org.jetbrains.kotlin.descriptors.*; 023 import org.jetbrains.kotlin.descriptors.annotations.Annotations; 024 import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl; 025 import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl; 026 import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils; 027 import org.jetbrains.kotlin.load.java.descriptors.*; 028 import org.jetbrains.kotlin.load.java.lazy.types.LazyJavaTypeResolver; 029 import org.jetbrains.kotlin.load.java.structure.*; 030 import org.jetbrains.kotlin.name.FqName; 031 import org.jetbrains.kotlin.name.Name; 032 import org.jetbrains.kotlin.resolve.DescriptorUtils; 033 import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructorKt; 034 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; 035 import org.jetbrains.kotlin.resolve.jvm.JavaResolverUtils; 036 import org.jetbrains.kotlin.types.*; 037 038 import java.util.*; 039 040 import static org.jetbrains.kotlin.types.Variance.INVARIANT; 041 import static org.jetbrains.kotlin.types.Variance.IN_VARIANCE; 042 043 public class SingleAbstractMethodUtils { 044 private SingleAbstractMethodUtils() { 045 } 046 047 @NotNull 048 public static List<CallableMemberDescriptor> getAbstractMembers(@NotNull KotlinType type) { 049 List<CallableMemberDescriptor> abstractMembers = new ArrayList<CallableMemberDescriptor>(); 050 for (DeclarationDescriptor member : DescriptorUtils.getAllDescriptors(type.getMemberScope())) { 051 if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) { 052 abstractMembers.add((CallableMemberDescriptor) member); 053 } 054 } 055 return abstractMembers; 056 } 057 058 @Nullable 059 public static KotlinType getFunctionTypeForSamType(@NotNull KotlinType samType) { 060 // e.g. samType == Comparator<String>? 061 062 ClassifierDescriptor classifier = samType.getConstructor().getDeclarationDescriptor(); 063 if (classifier instanceof JavaClassDescriptor) { 064 // Function2<T, T, Int> 065 KotlinType functionTypeDefault = ((JavaClassDescriptor) classifier).getFunctionTypeForSamInterface(); 066 067 if (functionTypeDefault != null) { 068 KotlinType noProjectionsSamType = SingleAbstractMethodUtilsKt.nonProjectionParametrization(samType); 069 if (noProjectionsSamType == null) return null; 070 071 // Function2<String, String, Int>? 072 KotlinType type = TypeSubstitutor.create(noProjectionsSamType).substitute(functionTypeDefault, IN_VARIANCE); 073 assert type != null : "Substitution based on type with no projections '" + noProjectionsSamType + 074 "' should not end with conflict"; 075 076 if (FlexibleTypesKt.isNullabilityFlexible(samType)) { 077 return LazyJavaTypeResolver.FlexibleJavaClassifierTypeCapabilities.create(type, TypeUtils.makeNullable(type)); 078 } 079 080 return TypeUtils.makeNullableAsSpecified(type, samType.isMarkedNullable()); 081 } 082 } 083 return null; 084 } 085 086 @NotNull 087 public static KotlinType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) { 088 KotlinType returnType = function.getReturnType(); 089 assert returnType != null : "function is not initialized: " + function; 090 List<ValueParameterDescriptor> valueParameters = function.getValueParameters(); 091 List<KotlinType> parameterTypes = new ArrayList<KotlinType>(valueParameters.size()); 092 for (ValueParameterDescriptor parameter : valueParameters) { 093 parameterTypes.add(parameter.getType()); 094 } 095 return DescriptorUtilsKt.getBuiltIns(function).getFunctionType(Annotations.Companion.getEMPTY(), null, parameterTypes, returnType); 096 } 097 098 private static boolean isSamInterface(@NotNull ClassDescriptor klass) { 099 if (klass.getKind() != ClassKind.INTERFACE) { 100 return false; 101 } 102 103 List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass.getDefaultType()); 104 if (abstractMembers.size() == 1) { 105 CallableMemberDescriptor member = abstractMembers.get(0); 106 if (member instanceof SimpleFunctionDescriptor) { 107 return member.getTypeParameters().isEmpty(); 108 } 109 } 110 return false; 111 } 112 113 @NotNull 114 public static SamConstructorDescriptor createSamConstructorFunction( 115 @NotNull DeclarationDescriptor owner, 116 @NotNull JavaClassDescriptor samInterface 117 ) { 118 assert isSamInterface(samInterface) : samInterface; 119 120 SamConstructorDescriptor result = new SamConstructorDescriptor(owner, samInterface); 121 122 TypeParameters typeParameters = recreateAndInitializeTypeParameters(samInterface.getTypeConstructor().getParameters(), result); 123 124 KotlinType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType()); 125 assert parameterTypeUnsubstituted != null : "couldn't get function type for SAM type " + samInterface.getDefaultType(); 126 KotlinType parameterType = typeParameters.substitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE); 127 assert parameterType != null : "couldn't substitute type: " + parameterTypeUnsubstituted + 128 ", substitutor = " + typeParameters.substitutor; 129 ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl( 130 result, null, 0, Annotations.Companion.getEMPTY(), Name.identifier("function"), parameterType, 131 /* declaresDefaultValue = */ false, 132 /* isCrossinline = */ false, 133 /* isNoinline = */ false, 134 null, SourceElement.NO_SOURCE); 135 136 KotlinType returnType = typeParameters.substitutor.substitute(samInterface.getDefaultType(), Variance.OUT_VARIANCE); 137 assert returnType != null : "couldn't substitute type: " + samInterface.getDefaultType() + 138 ", substitutor = " + typeParameters.substitutor; 139 140 result.initialize( 141 null, 142 null, 143 typeParameters.descriptors, 144 Arrays.asList(parameter), 145 returnType, 146 Modality.FINAL, 147 samInterface.getVisibility() 148 ); 149 150 return result; 151 } 152 153 public static boolean isSamType(@NotNull KotlinType type) { 154 return getFunctionTypeForSamType(type) != null; 155 } 156 157 public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) { 158 for (ValueParameterDescriptor param : fun.getValueParameters()) { 159 if (isSamType(param.getType())) { 160 return true; 161 } 162 } 163 return false; 164 } 165 166 @NotNull 167 public static SamAdapterDescriptor<JavaMethodDescriptor> createSamAdapterFunction(@NotNull final JavaMethodDescriptor original) { 168 final SamAdapterFunctionDescriptor result = new SamAdapterFunctionDescriptor(original); 169 return initSamAdapter(original, result, new FunctionInitializer() { 170 @Override 171 public void initialize( 172 @NotNull List<TypeParameterDescriptor> typeParameters, 173 @NotNull List<ValueParameterDescriptor> valueParameters, 174 @NotNull KotlinType returnType 175 ) { 176 result.initialize( 177 null, 178 original.getDispatchReceiverParameter(), 179 typeParameters, 180 valueParameters, 181 returnType, 182 Modality.FINAL, 183 original.getVisibility() 184 ); 185 } 186 }); 187 } 188 189 @NotNull 190 public static SamAdapterDescriptor<JavaConstructorDescriptor> createSamAdapterConstructor(@NotNull final JavaConstructorDescriptor original) { 191 final SamAdapterConstructorDescriptor result = new SamAdapterConstructorDescriptor(original); 192 return initSamAdapter(original, result, new FunctionInitializer() { 193 @Override 194 public void initialize( 195 @NotNull List<TypeParameterDescriptor> typeParameters, 196 @NotNull List<ValueParameterDescriptor> valueParameters, 197 @NotNull KotlinType returnType 198 ) { 199 result.initialize(valueParameters, original.getVisibility()); 200 result.setReturnType(returnType); 201 } 202 }); 203 } 204 205 @NotNull 206 private static <F extends FunctionDescriptor> SamAdapterDescriptor<F> initSamAdapter( 207 @NotNull F original, 208 @NotNull SamAdapterDescriptor<F> adapter, 209 @NotNull FunctionInitializer initializer 210 ) { 211 TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter); 212 213 KotlinType returnTypeUnsubstituted = original.getReturnType(); 214 assert returnTypeUnsubstituted != null : "Creating SAM adapter for not initialized original: " + original; 215 216 TypeSubstitutor substitutor = typeParameters.substitutor; 217 KotlinType returnType = substitutor.substitute(returnTypeUnsubstituted, Variance.INVARIANT); 218 assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted + 219 ", substitutor = " + substitutor; 220 221 222 List<ValueParameterDescriptor> valueParameters = createValueParametersForSamAdapter(original, adapter, substitutor); 223 224 initializer.initialize(typeParameters.descriptors, valueParameters, returnType); 225 226 return adapter; 227 } 228 229 public static List<ValueParameterDescriptor> createValueParametersForSamAdapter( 230 @NotNull FunctionDescriptor original, 231 @NotNull FunctionDescriptor samAdapter, 232 @NotNull TypeSubstitutor substitutor 233 ) { 234 List<ValueParameterDescriptor> originalValueParameters = original.getValueParameters(); 235 List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size()); 236 for (ValueParameterDescriptor originalParam : originalValueParameters) { 237 KotlinType originalType = originalParam.getType(); 238 KotlinType functionType = getFunctionTypeForSamType(originalType); 239 KotlinType newTypeUnsubstituted = functionType != null ? functionType : originalType; 240 KotlinType newType = substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE); 241 assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + substitutor; 242 243 ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl( 244 samAdapter, null, originalParam.getIndex(), originalParam.getAnnotations(), 245 originalParam.getName(), newType, 246 /* declaresDefaultValue = */ false, 247 /* isCrossinline = */ false, 248 /* isNoinline = */ false, 249 null, SourceElement.NO_SOURCE 250 ); 251 valueParameters.add(newParam); 252 } 253 return valueParameters; 254 } 255 256 @NotNull 257 private static TypeParameters recreateAndInitializeTypeParameters( 258 @NotNull List<TypeParameterDescriptor> originalParameters, 259 @Nullable DeclarationDescriptor newOwner 260 ) { 261 if (newOwner instanceof SamAdapterConstructorDescriptor) { 262 return new TypeParameters(originalParameters, TypeSubstitutor.EMPTY); 263 } 264 265 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters = 266 JavaResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner); 267 TypeSubstitutor typeParametersSubstitutor = JavaResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters); 268 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) { 269 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey(); 270 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue(); 271 272 for (KotlinType upperBound : traitTypeParameter.getUpperBounds()) { 273 KotlinType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT); 274 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor; 275 funTypeParameter.addUpperBound(upperBoundSubstituted); 276 } 277 278 funTypeParameter.setInitialized(); 279 } 280 281 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values()); 282 return new TypeParameters(typeParameters, typeParametersSubstitutor); 283 } 284 285 // Returns null if not SAM interface 286 @Nullable 287 public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) { 288 FqName fqName = javaClass.getFqName(); 289 if (fqName == null || fqName.toUnsafe().startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) { 290 return null; 291 } 292 if (!javaClass.isInterface() || javaClass.isAnnotationType()) { 293 return null; 294 } 295 296 return findOnlyAbstractMethod(javaClass); 297 } 298 299 @Nullable 300 private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) { 301 OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder(); 302 if (finder.find(javaClass.getDefaultType())) { 303 return finder.getFoundMethod(); 304 } 305 return null; 306 } 307 308 private static class TypeParameters { 309 public final List<TypeParameterDescriptor> descriptors; 310 public final TypeSubstitutor substitutor; 311 312 private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) { 313 this.descriptors = descriptors; 314 this.substitutor = substitutor; 315 } 316 } 317 318 private static abstract class FunctionInitializer { 319 public abstract void initialize( 320 @NotNull List<TypeParameterDescriptor> typeParameters, 321 @NotNull List<ValueParameterDescriptor> valueParameters, 322 @NotNull KotlinType returnType 323 ); 324 } 325 326 private static class OnlyAbstractMethodFinder { 327 private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object"); 328 329 private JavaMethod foundMethod; 330 private JavaTypeSubstitutor foundClassSubstitutor; 331 332 private boolean find(@NotNull JavaClassifierType classifierType) { 333 JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor(); 334 JavaClassifier classifier = classifierType.getClassifier(); 335 if (classifier == null) { 336 return false; // can't resolve class -> not a SAM interface 337 } 338 assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier; 339 JavaClass javaClass = (JavaClass) classifier; 340 if (OBJECT_FQ_NAME.equals(javaClass.getFqName())) { 341 return true; 342 } 343 for (JavaMethod method : javaClass.getMethods()) { 344 345 //skip java 8 default methods 346 if (!method.isAbstract()) { 347 continue; 348 } 349 350 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface 351 continue; 352 } 353 if (!method.getTypeParameters().isEmpty()) { 354 return false; // if interface has generic methods, it is not a SAM interface 355 } 356 357 if (foundMethod == null) { 358 foundMethod = method; 359 foundClassSubstitutor = classSubstitutor; 360 continue; 361 } 362 363 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) { 364 return false; // different signatures 365 } 366 } 367 368 for (JavaClassifierType t : classifierType.getSupertypes()) { 369 if (!find(t)) { 370 return false; 371 } 372 } 373 374 return true; 375 } 376 377 /** 378 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual 379 */ 380 private static boolean areSignaturesErasureEqual( 381 @NotNull JavaMethod method1, 382 @NotNull JavaTypeSubstitutor substitutor1, 383 @NotNull JavaMethod method2, 384 @NotNull JavaTypeSubstitutor substitutor2 385 ) { 386 if (!method1.getName().equals(method2.getName())) return false; 387 388 Collection<JavaValueParameter> parameters1 = method1.getValueParameters(); 389 Collection<JavaValueParameter> parameters2 = method2.getValueParameters(); 390 if (parameters1.size() != parameters2.size()) return false; 391 392 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) { 393 JavaValueParameter param1 = it1.next(); 394 JavaValueParameter param2 = it2.next(); 395 if (param1.isVararg() != param2.isVararg()) return false; 396 397 JavaType type1 = JavaResolverUtils.erasure(substitutor1.substitute(param1.getType()), substitutor1); 398 JavaType type2 = JavaResolverUtils.erasure(substitutor2.substitute(param2.getType()), substitutor2); 399 if (!(type1 == null ? type2 == null : type1.equals(type2))) return false; 400 } 401 402 return true; 403 } 404 405 @Nullable 406 private JavaMethod getFoundMethod() { 407 return foundMethod; 408 } 409 } 410 }