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