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