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.descriptorUtil.DescriptorUtilsKt; 033 import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolverKt; 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 : type.getMemberScope().getAllDescriptors()) { 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 (JavaDescriptorResolverKt.getPLATFORM_TYPES() && 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(typeParameters, 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 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters = 292 JavaResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner); 293 TypeSubstitutor typeParametersSubstitutor = JavaResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters); 294 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) { 295 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey(); 296 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue(); 297 298 for (KotlinType upperBound : traitTypeParameter.getUpperBounds()) { 299 KotlinType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT); 300 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor; 301 funTypeParameter.addUpperBound(upperBoundSubstituted); 302 } 303 304 funTypeParameter.setInitialized(); 305 } 306 307 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values()); 308 return new TypeParameters(typeParameters, typeParametersSubstitutor); 309 } 310 311 // Returns null if not SAM interface 312 @Nullable 313 public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) { 314 FqName fqName = javaClass.getFqName(); 315 if (fqName == null || fqName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) { 316 return null; 317 } 318 if (!javaClass.isInterface() || javaClass.isAnnotationType()) { 319 return null; 320 } 321 322 return findOnlyAbstractMethod(javaClass); 323 } 324 325 @Nullable 326 private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) { 327 OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder(); 328 if (finder.find(javaClass.getDefaultType())) { 329 return finder.getFoundMethod(); 330 } 331 return null; 332 } 333 334 private static class TypeParameters { 335 public final List<TypeParameterDescriptor> descriptors; 336 public final TypeSubstitutor substitutor; 337 338 private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) { 339 this.descriptors = descriptors; 340 this.substitutor = substitutor; 341 } 342 } 343 344 private static abstract class FunctionInitializer { 345 public abstract void initialize( 346 @NotNull List<TypeParameterDescriptor> typeParameters, 347 @NotNull List<ValueParameterDescriptor> valueParameters, 348 @NotNull KotlinType returnType 349 ); 350 } 351 352 private static class OnlyAbstractMethodFinder { 353 private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object"); 354 355 private JavaMethod foundMethod; 356 private JavaTypeSubstitutor foundClassSubstitutor; 357 358 private boolean find(@NotNull JavaClassifierType classifierType) { 359 JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor(); 360 JavaClassifier classifier = classifierType.getClassifier(); 361 if (classifier == null) { 362 return false; // can't resolve class -> not a SAM interface 363 } 364 assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier; 365 JavaClass javaClass = (JavaClass) classifier; 366 if (OBJECT_FQ_NAME.equals(javaClass.getFqName())) { 367 return true; 368 } 369 for (JavaMethod method : javaClass.getMethods()) { 370 371 //skip java 8 default methods 372 if (!method.isAbstract()) { 373 continue; 374 } 375 376 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface 377 continue; 378 } 379 if (!method.getTypeParameters().isEmpty()) { 380 return false; // if interface has generic methods, it is not a SAM interface 381 } 382 383 if (foundMethod == null) { 384 foundMethod = method; 385 foundClassSubstitutor = classSubstitutor; 386 continue; 387 } 388 389 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) { 390 return false; // different signatures 391 } 392 } 393 394 for (JavaClassifierType t : classifierType.getSupertypes()) { 395 if (!find(t)) { 396 return false; 397 } 398 } 399 400 return true; 401 } 402 403 /** 404 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual 405 */ 406 private static boolean areSignaturesErasureEqual( 407 @NotNull JavaMethod method1, 408 @NotNull JavaTypeSubstitutor substitutor1, 409 @NotNull JavaMethod method2, 410 @NotNull JavaTypeSubstitutor substitutor2 411 ) { 412 if (!method1.getName().equals(method2.getName())) return false; 413 414 Collection<JavaValueParameter> parameters1 = method1.getValueParameters(); 415 Collection<JavaValueParameter> parameters2 = method2.getValueParameters(); 416 if (parameters1.size() != parameters2.size()) return false; 417 418 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) { 419 JavaValueParameter param1 = it1.next(); 420 JavaValueParameter param2 = it2.next(); 421 if (param1.isVararg() != param2.isVararg()) return false; 422 423 JavaType type1 = JavaResolverUtils.erasure(substitutor1.substitute(param1.getType()), substitutor1); 424 JavaType type2 = JavaResolverUtils.erasure(substitutor2.substitute(param2.getType()), substitutor2); 425 if (!(type1 == null ? type2 == null : type1.equals(type2))) return false; 426 } 427 428 return true; 429 } 430 431 @Nullable 432 private JavaMethod getFoundMethod() { 433 return foundMethod; 434 } 435 } 436 }