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.types.Variance.INVARIANT; 039 040 public class SingleAbstractMethodUtils { 041 private SingleAbstractMethodUtils() { 042 } 043 044 @NotNull 045 public static List<CallableMemberDescriptor> getAbstractMembers(@NotNull JetType type) { 046 List<CallableMemberDescriptor> abstractMembers = new ArrayList<CallableMemberDescriptor>(); 047 for (DeclarationDescriptor member : type.getMemberScope().getAllDescriptors()) { 048 if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) { 049 abstractMembers.add((CallableMemberDescriptor) member); 050 } 051 } 052 return abstractMembers; 053 } 054 055 private static JetType fixProjections(@NotNull JetType functionType) { 056 //removes redundant projection kinds and detects conflicts 057 058 List<TypeParameterDescriptor> typeParameters = functionType.getConstructor().getParameters(); 059 List<TypeProjection> arguments = new ArrayList<TypeProjection>(typeParameters.size()); 060 for (TypeParameterDescriptor typeParameter : typeParameters) { 061 Variance variance = typeParameter.getVariance(); 062 TypeProjection argument = functionType.getArguments().get(typeParameter.getIndex()); 063 Variance kind = argument.getProjectionKind(); 064 if (kind != INVARIANT && variance != INVARIANT) { 065 if (kind == variance) { 066 arguments.add(new TypeProjectionImpl(argument.getType())); 067 } 068 else { 069 return null; 070 } 071 } 072 else { 073 arguments.add(argument); 074 } 075 } 076 ClassifierDescriptor classifier = functionType.getConstructor().getDeclarationDescriptor(); 077 assert classifier instanceof ClassDescriptor : "Not class: " + classifier; 078 return new JetTypeImpl( 079 functionType.getAnnotations(), 080 functionType.getConstructor(), 081 functionType.isMarkedNullable(), 082 arguments, 083 ((ClassDescriptor) classifier).getMemberScope(arguments) 084 ); 085 } 086 087 @Nullable 088 private static JetType getFunctionTypeForSamType(@NotNull JetType samType, boolean isSamConstructor) { 089 // e.g. samType == Comparator<String>? 090 091 ClassifierDescriptor classifier = samType.getConstructor().getDeclarationDescriptor(); 092 if (classifier instanceof JavaClassDescriptor) { 093 // Function2<T, T, Int> 094 JetType functionTypeDefault = ((JavaClassDescriptor) classifier).getFunctionTypeForSamInterface(); 095 096 if (functionTypeDefault != null) { 097 // Function2<String, String, Int>? 098 JetType substitute = TypeSubstitutor.create(samType).substitute(functionTypeDefault, Variance.INVARIANT); 099 100 if (substitute == null) return null; 101 102 JetType fixedProjections = fixProjections(substitute); 103 if (fixedProjections == null) return null; 104 105 if (JvmPackage.getPLATFORM_TYPES() && !isSamConstructor) { 106 return LazyJavaTypeResolver.FlexibleJavaClassifierTypeCapabilities.create(fixedProjections, TypeUtils.makeNullable(fixedProjections)); 107 } 108 109 return TypeUtils.makeNullableAsSpecified(fixedProjections, !isSamConstructor && samType.isMarkedNullable()); 110 } 111 } 112 return null; 113 } 114 115 @NotNull 116 public static JetType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) { 117 JetType returnType = function.getReturnType(); 118 assert returnType != null : "function is not initialized: " + function; 119 List<ValueParameterDescriptor> valueParameters = function.getValueParameters(); 120 List<JetType> parameterTypes = new ArrayList<JetType>(valueParameters.size()); 121 for (ValueParameterDescriptor parameter : valueParameters) { 122 parameterTypes.add(parameter.getType()); 123 } 124 return KotlinBuiltIns.getInstance().getFunctionType( 125 Annotations.EMPTY, null, parameterTypes, returnType); 126 } 127 128 private static boolean isSamInterface(@NotNull ClassDescriptor klass) { 129 if (klass.getKind() != ClassKind.TRAIT) { 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(), true); 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, /* irrelevant */ false) != 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 @Nullable 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 @Nullable JetType returnType 224 ) { 225 result.initialize(typeParameters, valueParameters, original.getVisibility()); 226 result.setReturnType(result.getContainingDeclaration().getDefaultType()); 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 JetType returnType; 241 if (returnTypeUnsubstituted == null) { // return type may be null for not yet initialized constructors 242 returnType = null; 243 } 244 else { 245 returnType = typeParameters.substitutor.substitute(returnTypeUnsubstituted, Variance.OUT_VARIANCE); 246 assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted + 247 ", substitutor = " + typeParameters.substitutor; 248 } 249 250 List<ValueParameterDescriptor> originalValueParameters = original.getValueParameters(); 251 List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size()); 252 for (ValueParameterDescriptor originalParam : originalValueParameters) { 253 JetType originalType = originalParam.getType(); 254 JetType functionType = getFunctionTypeForSamType(originalType, false); 255 JetType newTypeUnsubstituted = functionType != null ? functionType : originalType; 256 JetType newType = typeParameters.substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE); 257 assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + typeParameters.substitutor; 258 259 ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl( 260 adapter, null, originalParam.getIndex(), originalParam.getAnnotations(), 261 originalParam.getName(), newType, false, null, SourceElement.NO_SOURCE 262 ); 263 valueParameters.add(newParam); 264 } 265 266 initializer.initialize(typeParameters.descriptors, valueParameters, returnType); 267 268 return adapter; 269 } 270 271 @NotNull 272 private static TypeParameters recreateAndInitializeTypeParameters( 273 @NotNull List<TypeParameterDescriptor> originalParameters, 274 @Nullable DeclarationDescriptor newOwner 275 ) { 276 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters = 277 JavaResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner); 278 TypeSubstitutor typeParametersSubstitutor = JavaResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters); 279 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) { 280 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey(); 281 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue(); 282 283 for (JetType upperBound : traitTypeParameter.getUpperBounds()) { 284 JetType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT); 285 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor; 286 funTypeParameter.addUpperBound(upperBoundSubstituted); 287 } 288 289 funTypeParameter.setInitialized(); 290 } 291 292 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values()); 293 return new TypeParameters(typeParameters, typeParametersSubstitutor); 294 } 295 296 // Returns null if not SAM interface 297 @Nullable 298 public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) { 299 FqName fqName = javaClass.getFqName(); 300 if (fqName == null || fqName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) { 301 return null; 302 } 303 if (!javaClass.isInterface() || javaClass.isAnnotationType()) { 304 return null; 305 } 306 307 return findOnlyAbstractMethod(javaClass); 308 } 309 310 @Nullable 311 private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) { 312 OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder(); 313 if (finder.find(javaClass.getDefaultType())) { 314 return finder.getFoundMethod(); 315 } 316 return null; 317 } 318 319 private static class TypeParameters { 320 public final List<TypeParameterDescriptor> descriptors; 321 public final TypeSubstitutor substitutor; 322 323 private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) { 324 this.descriptors = descriptors; 325 this.substitutor = substitutor; 326 } 327 } 328 329 private static abstract class FunctionInitializer { 330 public abstract void initialize( 331 @NotNull List<TypeParameterDescriptor> typeParameters, 332 @NotNull List<ValueParameterDescriptor> valueParameters, 333 @Nullable JetType returnType 334 ); 335 } 336 337 private static class OnlyAbstractMethodFinder { 338 private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object"); 339 340 private JavaMethod foundMethod; 341 private JavaTypeSubstitutor foundClassSubstitutor; 342 343 private boolean find(@NotNull JavaClassifierType classifierType) { 344 JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor(); 345 JavaClassifier classifier = classifierType.getClassifier(); 346 if (classifier == null) { 347 return false; // can't resolve class -> not a SAM interface 348 } 349 assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier; 350 JavaClass javaClass = (JavaClass) classifier; 351 if (OBJECT_FQ_NAME.equals(javaClass.getFqName())) { 352 return true; 353 } 354 for (JavaMethod method : javaClass.getMethods()) { 355 356 //skip java 8 default methods 357 if (!method.isAbstract()) { 358 continue; 359 } 360 361 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface 362 continue; 363 } 364 if (!method.getTypeParameters().isEmpty()) { 365 return false; // if interface has generic methods, it is not a SAM interface 366 } 367 368 if (foundMethod == null) { 369 foundMethod = method; 370 foundClassSubstitutor = classSubstitutor; 371 continue; 372 } 373 374 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) { 375 return false; // different signatures 376 } 377 } 378 379 for (JavaClassifierType t : classifierType.getSupertypes()) { 380 if (!find(t)) { 381 return false; 382 } 383 } 384 385 return true; 386 } 387 388 /** 389 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual 390 */ 391 private static boolean areSignaturesErasureEqual( 392 @NotNull JavaMethod method1, 393 @NotNull JavaTypeSubstitutor substitutor1, 394 @NotNull JavaMethod method2, 395 @NotNull JavaTypeSubstitutor substitutor2 396 ) { 397 if (!method1.getName().equals(method2.getName())) return false; 398 399 Collection<JavaValueParameter> parameters1 = method1.getValueParameters(); 400 Collection<JavaValueParameter> parameters2 = method2.getValueParameters(); 401 if (parameters1.size() != parameters2.size()) return false; 402 403 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) { 404 JavaValueParameter param1 = it1.next(); 405 JavaValueParameter param2 = it2.next(); 406 if (param1.isVararg() != param2.isVararg()) return false; 407 408 JavaType type1 = JavaResolverUtils.erasure(substitutor1.substitute(param1.getType()), substitutor1); 409 JavaType type2 = JavaResolverUtils.erasure(substitutor2.substitute(param2.getType()), substitutor2); 410 if (!(type1 == null ? type2 == null : type1.equals(type2))) return false; 411 } 412 413 return true; 414 } 415 416 @Nullable 417 private JavaMethod getFoundMethod() { 418 return foundMethod; 419 } 420 } 421 }