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