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    }