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