All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.load.java.sam;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils;
import org.jetbrains.kotlin.load.java.descriptors.*;
import org.jetbrains.kotlin.load.java.lazy.types.LazyJavaTypeResolver;
import org.jetbrains.kotlin.load.java.structure.*;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.JavaResolverUtils;
import org.jetbrains.kotlin.types.*;

import java.util.*;

import static org.jetbrains.kotlin.types.Variance.INVARIANT;

public class SingleAbstractMethodUtils {
    private SingleAbstractMethodUtils() {
    }

    @NotNull
    public static List getAbstractMembers(@NotNull KotlinType type) {
        List abstractMembers = new ArrayList();
        for (DeclarationDescriptor member : DescriptorUtils.getAllDescriptors(type.getMemberScope())) {
            if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) {
                abstractMembers.add((CallableMemberDescriptor) member);
            }
        }
        return abstractMembers;
    }

    private static KotlinType fixProjections(@NotNull KotlinType functionType) {
        //removes redundant projection kinds and detects conflicts

        List typeParameters = functionType.getConstructor().getParameters();
        List arguments = new ArrayList(typeParameters.size());
        for (TypeParameterDescriptor typeParameter : typeParameters) {
            Variance variance = typeParameter.getVariance();
            TypeProjection argument = functionType.getArguments().get(typeParameter.getIndex());
            Variance kind = argument.getProjectionKind();
            if (kind != INVARIANT && variance != INVARIANT) {
                if (kind == variance) {
                    arguments.add(new TypeProjectionImpl(argument.getType()));
                }
                else {
                    return null;
                }
            }
            else {
                 arguments.add(argument);
            }
        }
        ClassifierDescriptor classifier = functionType.getConstructor().getDeclarationDescriptor();
        assert classifier instanceof ClassDescriptor : "Not class: " + classifier;
        return KotlinTypeImpl.create(
                functionType.getAnnotations(),
                functionType.getConstructor(),
                functionType.isMarkedNullable(),
                arguments,
                ((ClassDescriptor) classifier).getMemberScope(arguments)
        );
    }

    @Nullable
    public static KotlinType getFunctionTypeForSamType(@NotNull KotlinType samType) {
        // e.g. samType == Comparator?

        ClassifierDescriptor classifier = samType.getConstructor().getDeclarationDescriptor();
        if (classifier instanceof JavaClassDescriptor) {
            // Function2
            KotlinType functionTypeDefault = ((JavaClassDescriptor) classifier).getFunctionTypeForSamInterface();

            if (functionTypeDefault != null) {
                // Function2?
                KotlinType substitute = TypeSubstitutor.create(samType).substitute(functionTypeDefault, Variance.INVARIANT);

                if (substitute == null) return null;

                KotlinType type = fixProjections(substitute);
                if (type == null) return null;

                if (FlexibleTypesKt.isNullabilityFlexible(samType)) {
                    return LazyJavaTypeResolver.FlexibleJavaClassifierTypeCapabilities.create(type, TypeUtils.makeNullable(type));
                }

                return TypeUtils.makeNullableAsSpecified(type, samType.isMarkedNullable());
            }
        }
        return null;
    }

    @NotNull
    public static KotlinType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) {
        KotlinType returnType = function.getReturnType();
        assert returnType != null : "function is not initialized: " + function;
        List valueParameters = function.getValueParameters();
        List parameterTypes = new ArrayList(valueParameters.size());
        for (ValueParameterDescriptor parameter : valueParameters) {
            parameterTypes.add(parameter.getType());
        }
        return DescriptorUtilsKt.getBuiltIns(function).getFunctionType(Annotations.Companion.getEMPTY(), null, parameterTypes, returnType);
    }

    private static boolean isSamInterface(@NotNull ClassDescriptor klass) {
        if (klass.getKind() != ClassKind.INTERFACE) {
            return false;
        }

        List abstractMembers = getAbstractMembers(klass.getDefaultType());
        if (abstractMembers.size() == 1) {
            CallableMemberDescriptor member = abstractMembers.get(0);
            if (member instanceof SimpleFunctionDescriptor) {
                return member.getTypeParameters().isEmpty();
            }
        }
        return false;
    }

    @NotNull
    public static SamConstructorDescriptor createSamConstructorFunction(
            @NotNull DeclarationDescriptor owner,
            @NotNull JavaClassDescriptor samInterface
    ) {
        assert isSamInterface(samInterface) : samInterface;

        SamConstructorDescriptor result = new SamConstructorDescriptor(owner, samInterface);

        TypeParameters typeParameters = recreateAndInitializeTypeParameters(samInterface.getTypeConstructor().getParameters(), result);

        KotlinType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType());
        assert parameterTypeUnsubstituted != null : "couldn't get function type for SAM type " + samInterface.getDefaultType();
        KotlinType parameterType = typeParameters.substitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE);
        assert parameterType != null : "couldn't substitute type: " + parameterTypeUnsubstituted +
                                       ", substitutor = " + typeParameters.substitutor;
        ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl(
                result, null, 0, Annotations.Companion.getEMPTY(), Name.identifier("function"), parameterType,
                /* declaresDefaultValue = */ false,
                /* isCrossinline = */ false,
                /* isNoinline = */ false,
                null, SourceElement.NO_SOURCE);

        KotlinType returnType = typeParameters.substitutor.substitute(samInterface.getDefaultType(), Variance.OUT_VARIANCE);
        assert returnType != null : "couldn't substitute type: " + samInterface.getDefaultType() +
                                    ", substitutor = " + typeParameters.substitutor;

        result.initialize(
                null,
                null,
                typeParameters.descriptors,
                Arrays.asList(parameter),
                returnType,
                Modality.FINAL,
                samInterface.getVisibility()
        );

        return result;
    }

    public static boolean isSamType(@NotNull KotlinType type) {
        return getFunctionTypeForSamType(type) != null;
    }

    public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) {
        for (ValueParameterDescriptor param : fun.getValueParameters()) {
            if (isSamType(param.getType())) {
                return true;
            }
        }
        return false;
    }

    @NotNull
    public static SamAdapterDescriptor createSamAdapterFunction(@NotNull final JavaMethodDescriptor original) {
        final SamAdapterFunctionDescriptor result = new SamAdapterFunctionDescriptor(original);
        return initSamAdapter(original, result, new FunctionInitializer() {
            @Override
            public void initialize(
                    @NotNull List typeParameters,
                    @NotNull List valueParameters,
                    @NotNull KotlinType returnType
            ) {
                result.initialize(
                        null,
                        original.getDispatchReceiverParameter(),
                        typeParameters,
                        valueParameters,
                        returnType,
                        Modality.FINAL,
                        original.getVisibility()
                );
            }
        });
    }

    @NotNull
    public static SamAdapterDescriptor createSamAdapterConstructor(@NotNull final JavaConstructorDescriptor original) {
        final SamAdapterConstructorDescriptor result = new SamAdapterConstructorDescriptor(original);
        return initSamAdapter(original, result, new FunctionInitializer() {
            @Override
            public void initialize(
                    @NotNull List typeParameters,
                    @NotNull List valueParameters,
                    @NotNull KotlinType returnType
            ) {
                result.initialize(valueParameters, original.getVisibility());
                result.setReturnType(returnType);
            }
        });
    }

    @NotNull
    private static  SamAdapterDescriptor initSamAdapter(
            @NotNull F original,
            @NotNull SamAdapterDescriptor adapter,
            @NotNull FunctionInitializer initializer
    ) {
        TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter);

        KotlinType returnTypeUnsubstituted = original.getReturnType();
        assert returnTypeUnsubstituted != null : "Creating SAM adapter for not initialized original: " + original;

        TypeSubstitutor substitutor = typeParameters.substitutor;
        KotlinType returnType = substitutor.substitute(returnTypeUnsubstituted, Variance.INVARIANT);
        assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted +
                                        ", substitutor = " + substitutor;


        List valueParameters = createValueParametersForSamAdapter(original, adapter, substitutor);

        initializer.initialize(typeParameters.descriptors, valueParameters, returnType);

        return adapter;
    }

    public static List createValueParametersForSamAdapter(
            @NotNull FunctionDescriptor original,
            @NotNull FunctionDescriptor samAdapter,
            @NotNull TypeSubstitutor substitutor
    ) {
        List originalValueParameters = original.getValueParameters();
        List valueParameters = new ArrayList(originalValueParameters.size());
        for (ValueParameterDescriptor originalParam : originalValueParameters) {
            KotlinType originalType = originalParam.getType();
            KotlinType functionType = getFunctionTypeForSamType(originalType);
            KotlinType newTypeUnsubstituted = functionType != null ? functionType : originalType;
            KotlinType newType = substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE);
            assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + substitutor;

            ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl(
                    samAdapter, null, originalParam.getIndex(), originalParam.getAnnotations(),
                    originalParam.getName(), newType,
                    /* declaresDefaultValue = */ false,
                    /* isCrossinline = */ false,
                    /* isNoinline = */ false,
                    null, SourceElement.NO_SOURCE
            );
            valueParameters.add(newParam);
        }
        return valueParameters;
    }

    @NotNull
    private static TypeParameters recreateAndInitializeTypeParameters(
            @NotNull List originalParameters,
            @Nullable DeclarationDescriptor newOwner
    ) {
        if (newOwner instanceof SamAdapterConstructorDescriptor) {
            return new TypeParameters(originalParameters, TypeSubstitutor.EMPTY);
        }

        Map traitToFunTypeParameters =
                JavaResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner);
        TypeSubstitutor typeParametersSubstitutor = JavaResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters);
        for (Map.Entry mapEntry : traitToFunTypeParameters.entrySet()) {
            TypeParameterDescriptor traitTypeParameter = mapEntry.getKey();
            TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue();

            for (KotlinType upperBound : traitTypeParameter.getUpperBounds()) {
                KotlinType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT);
                assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor;
                funTypeParameter.addUpperBound(upperBoundSubstituted);
            }

            funTypeParameter.setInitialized();
        }

        List typeParameters = new ArrayList(traitToFunTypeParameters.values());
        return new TypeParameters(typeParameters, typeParametersSubstitutor);
    }

    // Returns null if not SAM interface
    @Nullable
    public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) {
        FqName fqName = javaClass.getFqName();
        if (fqName == null || fqName.toUnsafe().startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
            return null;
        }
        if (!javaClass.isInterface() || javaClass.isAnnotationType()) {
            return null;
        }

        return findOnlyAbstractMethod(javaClass);
    }

    @Nullable
    private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) {
        OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder();
        if (finder.find(javaClass.getDefaultType())) {
            return finder.getFoundMethod();
        }
        return null;
    }

    private static class TypeParameters {
        public final List descriptors;
        public final TypeSubstitutor substitutor;

        private TypeParameters(List descriptors, TypeSubstitutor substitutor) {
            this.descriptors = descriptors;
            this.substitutor = substitutor;
        }
    }

    private static abstract class FunctionInitializer {
        public abstract void initialize(
                @NotNull List typeParameters,
                @NotNull List valueParameters,
                @NotNull KotlinType returnType
        );
    }

    private static class OnlyAbstractMethodFinder {
        private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object");

        private JavaMethod foundMethod;
        private JavaTypeSubstitutor foundClassSubstitutor;

        private boolean find(@NotNull JavaClassifierType classifierType) {
            JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor();
            JavaClassifier classifier = classifierType.getClassifier();
            if (classifier == null) {
                return false; // can't resolve class -> not a SAM interface
            }
            assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier;
            JavaClass javaClass = (JavaClass) classifier;
            if (OBJECT_FQ_NAME.equals(javaClass.getFqName())) {
                return true;
            }
            for (JavaMethod method : javaClass.getMethods()) {

                //skip java 8 default methods
                if (!method.isAbstract()) {
                    continue;
                }

                if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
                    continue;
                }
                if (!method.getTypeParameters().isEmpty()) {
                    return false; // if interface has generic methods, it is not a SAM interface
                }

                if (foundMethod == null) {
                    foundMethod = method;
                    foundClassSubstitutor = classSubstitutor;
                    continue;
                }

                if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) {
                    return false; // different signatures
                }
            }

            for (JavaClassifierType t : classifierType.getSupertypes()) {
                if (!find(t)) {
                    return false;
                }
            }

            return true;
        }

        /**
         * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual
         */
        private static boolean areSignaturesErasureEqual(
                @NotNull JavaMethod method1,
                @NotNull JavaTypeSubstitutor substitutor1,
                @NotNull JavaMethod method2,
                @NotNull JavaTypeSubstitutor substitutor2
        ) {
            if (!method1.getName().equals(method2.getName())) return false;

            Collection parameters1 = method1.getValueParameters();
            Collection parameters2 = method2.getValueParameters();
            if (parameters1.size() != parameters2.size()) return false;

            for (Iterator it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) {
                JavaValueParameter param1 = it1.next();
                JavaValueParameter param2 = it2.next();
                if (param1.isVararg() != param2.isVararg()) return false;

                JavaType type1 = JavaResolverUtils.erasure(substitutor1.substitute(param1.getType()), substitutor1);
                JavaType type2 = JavaResolverUtils.erasure(substitutor2.substitute(param2.getType()), substitutor2);
                if (!(type1 == null ? type2 == null : type1.equals(type2))) return false;
            }

            return true;
        }

        @Nullable
        private JavaMethod getFoundMethod() {
            return foundMethod;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy