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