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.jvm.JavaResolverUtils; 033 import org.jetbrains.kotlin.resolve.jvm.JvmPackage; 034 import org.jetbrains.kotlin.types.*; 035 036 import java.util.*; 037 038 import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getBuiltIns; 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 JetType 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 JetType fixProjections(@NotNull JetType 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 JetTypeImpl.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 JetType getFunctionTypeForSamType(@NotNull JetType 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 JetType functionTypeDefault = ((JavaClassDescriptor) classifier).getFunctionTypeForSamInterface(); 096 097 if (functionTypeDefault != null) { 098 // Function2<String, String, Int>? 099 JetType substitute = TypeSubstitutor.create(samType).substitute(functionTypeDefault, Variance.INVARIANT); 100 101 if (substitute == null) return null; 102 103 JetType type = fixProjections(substitute); 104 if (type == null) return null; 105 106 if (JvmPackage.getPLATFORM_TYPES() && TypesPackage.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 JetType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) { 118 JetType returnType = function.getReturnType(); 119 assert returnType != null : "function is not initialized: " + function; 120 List<ValueParameterDescriptor> valueParameters = function.getValueParameters(); 121 List<JetType> parameterTypes = new ArrayList<JetType>(valueParameters.size()); 122 for (ValueParameterDescriptor parameter : valueParameters) { 123 parameterTypes.add(parameter.getType()); 124 } 125 return getBuiltIns(function).getFunctionType(Annotations.EMPTY, 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 JetType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType()); 155 assert parameterTypeUnsubstituted != null : "couldn't get function type for SAM type " + samInterface.getDefaultType(); 156 JetType 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.EMPTY, Name.identifier("function"), parameterType, false, null, SourceElement.NO_SOURCE); 161 162 JetType returnType = typeParameters.substitutor.substitute(samInterface.getDefaultType(), Variance.OUT_VARIANCE); 163 assert returnType != null : "couldn't substitute type: " + samInterface.getDefaultType() + 164 ", substitutor = " + typeParameters.substitutor; 165 166 result.initialize( 167 null, 168 null, 169 typeParameters.descriptors, 170 Arrays.asList(parameter), 171 returnType, 172 Modality.FINAL, 173 samInterface.getVisibility() 174 ); 175 176 return result; 177 } 178 179 public static boolean isSamType(@NotNull JetType type) { 180 return getFunctionTypeForSamType(type) != null; 181 } 182 183 public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) { 184 for (ValueParameterDescriptor param : fun.getValueParameters()) { 185 if (isSamType(param.getType())) { 186 return true; 187 } 188 } 189 return false; 190 } 191 192 @NotNull 193 public static SamAdapterDescriptor<JavaMethodDescriptor> createSamAdapterFunction(@NotNull final JavaMethodDescriptor original) { 194 final SamAdapterFunctionDescriptor result = new SamAdapterFunctionDescriptor(original); 195 return initSamAdapter(original, result, new FunctionInitializer() { 196 @Override 197 public void initialize( 198 @NotNull List<TypeParameterDescriptor> typeParameters, 199 @NotNull List<ValueParameterDescriptor> valueParameters, 200 @NotNull JetType returnType 201 ) { 202 result.initialize( 203 null, 204 original.getDispatchReceiverParameter(), 205 typeParameters, 206 valueParameters, 207 returnType, 208 Modality.FINAL, 209 original.getVisibility() 210 ); 211 } 212 }); 213 } 214 215 @NotNull 216 public static SamAdapterDescriptor<JavaConstructorDescriptor> createSamAdapterConstructor(@NotNull final JavaConstructorDescriptor original) { 217 final SamAdapterConstructorDescriptor result = new SamAdapterConstructorDescriptor(original); 218 return initSamAdapter(original, result, new FunctionInitializer() { 219 @Override 220 public void initialize( 221 @NotNull List<TypeParameterDescriptor> typeParameters, 222 @NotNull List<ValueParameterDescriptor> valueParameters, 223 @NotNull JetType returnType 224 ) { 225 result.initialize(typeParameters, valueParameters, original.getVisibility()); 226 result.setReturnType(returnType); 227 } 228 }); 229 } 230 231 @NotNull 232 private static <F extends FunctionDescriptor> SamAdapterDescriptor<F> initSamAdapter( 233 @NotNull F original, 234 @NotNull SamAdapterDescriptor<F> adapter, 235 @NotNull FunctionInitializer initializer 236 ) { 237 TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter); 238 239 JetType returnTypeUnsubstituted = original.getReturnType(); 240 assert returnTypeUnsubstituted != null : "Creating SAM adapter for not initialized original: " + original; 241 242 TypeSubstitutor substitutor = typeParameters.substitutor; 243 JetType returnType = substitutor.substitute(returnTypeUnsubstituted, Variance.INVARIANT); 244 assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted + 245 ", substitutor = " + substitutor; 246 247 248 List<ValueParameterDescriptor> valueParameters = createValueParametersForSamAdapter(original, adapter, substitutor); 249 250 initializer.initialize(typeParameters.descriptors, valueParameters, returnType); 251 252 return adapter; 253 } 254 255 public static List<ValueParameterDescriptor> createValueParametersForSamAdapter( 256 @NotNull FunctionDescriptor original, 257 @NotNull FunctionDescriptor samAdapter, 258 @NotNull TypeSubstitutor substitutor 259 ) { 260 List<ValueParameterDescriptor> originalValueParameters = original.getValueParameters(); 261 List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size()); 262 for (ValueParameterDescriptor originalParam : originalValueParameters) { 263 JetType originalType = originalParam.getType(); 264 JetType functionType = getFunctionTypeForSamType(originalType); 265 JetType newTypeUnsubstituted = functionType != null ? functionType : originalType; 266 JetType newType = substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE); 267 assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + substitutor; 268 269 ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl( 270 samAdapter, null, originalParam.getIndex(), originalParam.getAnnotations(), 271 originalParam.getName(), newType, false, null, SourceElement.NO_SOURCE 272 ); 273 valueParameters.add(newParam); 274 } 275 return valueParameters; 276 } 277 278 @NotNull 279 private static TypeParameters recreateAndInitializeTypeParameters( 280 @NotNull List<TypeParameterDescriptor> originalParameters, 281 @Nullable DeclarationDescriptor newOwner 282 ) { 283 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters = 284 JavaResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner); 285 TypeSubstitutor typeParametersSubstitutor = JavaResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters); 286 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) { 287 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey(); 288 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue(); 289 290 for (JetType upperBound : traitTypeParameter.getUpperBounds()) { 291 JetType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT); 292 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor; 293 funTypeParameter.addUpperBound(upperBoundSubstituted); 294 } 295 296 funTypeParameter.setInitialized(); 297 } 298 299 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values()); 300 return new TypeParameters(typeParameters, typeParametersSubstitutor); 301 } 302 303 // Returns null if not SAM interface 304 @Nullable 305 public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) { 306 FqName fqName = javaClass.getFqName(); 307 if (fqName == null || fqName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) { 308 return null; 309 } 310 if (!javaClass.isInterface() || javaClass.isAnnotationType()) { 311 return null; 312 } 313 314 return findOnlyAbstractMethod(javaClass); 315 } 316 317 @Nullable 318 private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) { 319 OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder(); 320 if (finder.find(javaClass.getDefaultType())) { 321 return finder.getFoundMethod(); 322 } 323 return null; 324 } 325 326 private static class TypeParameters { 327 public final List<TypeParameterDescriptor> descriptors; 328 public final TypeSubstitutor substitutor; 329 330 private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) { 331 this.descriptors = descriptors; 332 this.substitutor = substitutor; 333 } 334 } 335 336 private static abstract class FunctionInitializer { 337 public abstract void initialize( 338 @NotNull List<TypeParameterDescriptor> typeParameters, 339 @NotNull List<ValueParameterDescriptor> valueParameters, 340 @NotNull JetType returnType 341 ); 342 } 343 344 private static class OnlyAbstractMethodFinder { 345 private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object"); 346 347 private JavaMethod foundMethod; 348 private JavaTypeSubstitutor foundClassSubstitutor; 349 350 private boolean find(@NotNull JavaClassifierType classifierType) { 351 JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor(); 352 JavaClassifier classifier = classifierType.getClassifier(); 353 if (classifier == null) { 354 return false; // can't resolve class -> not a SAM interface 355 } 356 assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier; 357 JavaClass javaClass = (JavaClass) classifier; 358 if (OBJECT_FQ_NAME.equals(javaClass.getFqName())) { 359 return true; 360 } 361 for (JavaMethod method : javaClass.getMethods()) { 362 363 //skip java 8 default methods 364 if (!method.isAbstract()) { 365 continue; 366 } 367 368 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface 369 continue; 370 } 371 if (!method.getTypeParameters().isEmpty()) { 372 return false; // if interface has generic methods, it is not a SAM interface 373 } 374 375 if (foundMethod == null) { 376 foundMethod = method; 377 foundClassSubstitutor = classSubstitutor; 378 continue; 379 } 380 381 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) { 382 return false; // different signatures 383 } 384 } 385 386 for (JavaClassifierType t : classifierType.getSupertypes()) { 387 if (!find(t)) { 388 return false; 389 } 390 } 391 392 return true; 393 } 394 395 /** 396 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual 397 */ 398 private static boolean areSignaturesErasureEqual( 399 @NotNull JavaMethod method1, 400 @NotNull JavaTypeSubstitutor substitutor1, 401 @NotNull JavaMethod method2, 402 @NotNull JavaTypeSubstitutor substitutor2 403 ) { 404 if (!method1.getName().equals(method2.getName())) return false; 405 406 Collection<JavaValueParameter> parameters1 = method1.getValueParameters(); 407 Collection<JavaValueParameter> parameters2 = method2.getValueParameters(); 408 if (parameters1.size() != parameters2.size()) return false; 409 410 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) { 411 JavaValueParameter param1 = it1.next(); 412 JavaValueParameter param2 = it2.next(); 413 if (param1.isVararg() != param2.isVararg()) return false; 414 415 JavaType type1 = JavaResolverUtils.erasure(substitutor1.substitute(param1.getType()), substitutor1); 416 JavaType type2 = JavaResolverUtils.erasure(substitutor2.substitute(param2.getType()), substitutor2); 417 if (!(type1 == null ? type2 == null : type1.equals(type2))) return false; 418 } 419 420 return true; 421 } 422 423 @Nullable 424 private JavaMethod getFoundMethod() { 425 return foundMethod; 426 } 427 } 428 }