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