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

org.jetbrains.kotlin.resolve.jvm.kotlinSignature.SignaturesPropagationData Maven / Gradle / Ivy

There is a newer version: 2.0.0
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.resolve.jvm.kotlinSignature;

import com.google.common.collect.Lists;
import kotlin.collections.CollectionsKt;
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.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor;
import org.jetbrains.kotlin.load.java.descriptors.UtilKt;
import org.jetbrains.kotlin.load.java.structure.JavaMethod;
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.jvmSignature.KotlinToJvmSignatureMapper;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.KotlinToJvmSignatureMapperKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.org.objectweb.asm.commons.Method;

import java.util.*;

import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName;

public class SignaturesPropagationData {

    private static final KotlinToJvmSignatureMapper SIGNATURE_MAPPER = ServiceLoader.load(
            KotlinToJvmSignatureMapper.class,
            KotlinToJvmSignatureMapper.class.getClassLoader()
    ).iterator().next();

    private final ValueParameters modifiedValueParameters;
    private final List signatureErrors = new ArrayList<>(0);
    private final List superFunctions;

    public SignaturesPropagationData(
            @NotNull ClassDescriptor containingClass,
            @NotNull KotlinType autoReturnType, // type built by JavaTypeTransformer from Java signature and @NotNull annotations
            @Nullable KotlinType receiverType,
            @NotNull List autoValueParameters, // descriptors built by parameters resolver
            @NotNull List autoTypeParameters, // descriptors built by signature resolver
            @NotNull JavaMethod method
    ) {
        assert receiverType == null : "Parameters before propagation have receiver type," +
                                      " but propagation should be disabled for functions compiled from Kotlin in class: " +
                                      DescriptorUtils.getFqName(containingClass);

        JavaMethodDescriptor autoMethodDescriptor =
                createAutoMethodDescriptor(containingClass, method, autoReturnType, autoValueParameters, autoTypeParameters);

        boolean hasStableParameterNames = autoValueParameters.stream().allMatch(it -> UtilKt.getParameterNameAnnotation(it) != null);

        superFunctions = getSuperFunctionsForMethod(method, autoMethodDescriptor, containingClass);
        modifiedValueParameters = superFunctions.isEmpty()
                                  ? new ValueParameters(null, autoValueParameters, hasStableParameterNames)
                                  : modifyValueParametersAccordingToSuperMethods(autoValueParameters, hasStableParameterNames);
    }

    @NotNull
    private static JavaMethodDescriptor createAutoMethodDescriptor(
            @NotNull ClassDescriptor containingClass,
            @NotNull JavaMethod method, KotlinType autoReturnType,
            @NotNull List autoValueParameters,
            @NotNull List autoTypeParameters
    ) {
        JavaMethodDescriptor autoMethodDescriptor = JavaMethodDescriptor.createJavaMethod(
                containingClass,
                Annotations.Companion.getEMPTY(),
                method.getName(),
                //TODO: what to do?
                SourceElement.NO_SOURCE
        );
        autoMethodDescriptor.initialize(
                null,
                containingClass.getThisAsReceiverParameter(),
                autoTypeParameters,
                autoValueParameters,
                autoReturnType,
                Modality.OPEN,
                DescriptorVisibilities.PUBLIC
        );
        return autoMethodDescriptor;
    }

    public KotlinType getModifiedReceiverType() {
        return modifiedValueParameters.receiverType;
    }

    public List getModifiedValueParameters() {
        return modifiedValueParameters.descriptors;
    }

    public boolean getModifiedHasStableParameterNames() {
        return modifiedValueParameters.hasStableParameterNames;
    }

    public List getSignatureErrors() {
        return signatureErrors;
    }

    void reportError(String error) {
        signatureErrors.add(error);
    }

    private ValueParameters modifyValueParametersAccordingToSuperMethods(
            @NotNull List parameters,
            boolean annotatedWithParameterName
    ) {
        KotlinType resultReceiverType = null;
        List resultParameters = new ArrayList<>(parameters.size());

        boolean shouldBeExtension = checkIfShouldBeExtension();

        for (ValueParameterDescriptor originalParam : parameters) {
            int originalIndex = originalParam.getIndex();
            List typesFromSuperMethods = CollectionsKt.map(
                    superFunctions,
                    superFunction -> {
                        ReceiverParameterDescriptor receiver = superFunction.getExtensionReceiverParameter();
                        int index = receiver != null ? originalIndex - 1 : originalIndex;
                        if (index == -1) {
                            assert receiver != null : "can't happen: index is -1, while function is not extension";
                            return new TypeAndName(receiver.getType(), originalParam.getName());
                        }
                        ValueParameterDescriptor parameter = superFunction.getValueParameters().get(index);
                        return new TypeAndName(parameter.getType(), parameter.getName());
                    }
            );

            VarargCheckResult varargCheckResult = checkVarargInSuperFunctions(originalParam);

            KotlinType altType = varargCheckResult.parameterType;

            if (shouldBeExtension && originalIndex == 0) {
                resultReceiverType = altType;
            }
            else {
                Name stableName = null;
                for (int i = 0; i < superFunctions.size(); i++) {
                    if (superFunctions.get(i).hasStableParameterNames()) {
                        // When there's more than one stable name in super functions, we pick the first one. This behaviour is similar to
                        // the compiler front-end, except that it reports a warning in such cases
                        // TODO: report a warning somewhere if there's more than one stable name in super functions
                        stableName = typesFromSuperMethods.get(i).name;
                        break;
                    }
                }

                AnnotationDescriptor currentName = UtilKt.getParameterNameAnnotation(originalParam);
                boolean shouldTakeOldName = currentName == null && stableName != null;

                resultParameters.add(new ValueParameterDescriptorImpl(
                        originalParam.getContainingDeclaration(),
                        null,
                        shouldBeExtension ? originalIndex - 1 : originalIndex,
                        originalParam.getAnnotations(),
                        shouldTakeOldName ? stableName : originalParam.getName(),
                        altType,
                        originalParam.declaresDefaultValue(),
                        originalParam.isCrossinline(),
                        originalParam.isNoinline(),
                        varargCheckResult.isVararg ? DescriptorUtilsKt.getBuiltIns(originalParam).getArrayElementType(altType) : null,
                        SourceElement.NO_SOURCE
                ));
            }
        }

        boolean hasStableParameterNames =
                annotatedWithParameterName || CollectionsKt.any(superFunctions, CallableDescriptor::hasStableParameterNames);

        return new ValueParameters(resultReceiverType, resultParameters, hasStableParameterNames);
    }

    private static List getSuperFunctionsForMethod(
            @NotNull JavaMethod method,
            @NotNull JavaMethodDescriptor autoMethodDescriptor,
            @NotNull ClassDescriptor containingClass
    ) {
        List superFunctions = Lists.newArrayList();

        // TODO: Add propagation for other kotlin descriptors (KT-3621)
        Name name = method.getName();
        Method autoSignature = null;
        boolean autoMethodContainsVararg = SignaturePropagationUtilKt.containsVarargs(autoMethodDescriptor);
        for (KotlinType supertype : containingClass.getTypeConstructor().getSupertypes()) {
            Collection superFunctionCandidates =
                    supertype.getMemberScope().getContributedFunctions(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS);

            if (!autoMethodContainsVararg && !SignaturePropagationUtilKt.containsAnyNotTrivialSignature(superFunctionCandidates)) continue;

            if (autoSignature == null) {
                autoSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(autoMethodDescriptor);
            }

            for (FunctionDescriptor candidate : superFunctionCandidates) {
                // Skip suspend super functions, because we doesn't process them correctly by now
                // Moreover, we fail with exception sometimes
                // TODO: remove this continue when KT-15747 is fixed
                if (candidate.isSuspend()) continue;
                Method candidateSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(candidate);
                if (KotlinToJvmSignatureMapperKt.erasedSignaturesEqualIgnoringReturnTypes(autoSignature, candidateSignature)) {
                    superFunctions.add(candidate);
                }
            }
        }

        // sorting for diagnostic stability
        superFunctions.sort(Comparator.comparing(fun -> getFqName(fun.getContainingDeclaration()).asString()));

        return superFunctions;
    }

    private boolean checkIfShouldBeExtension() {
        boolean someSupersExtension = false;
        boolean someSupersNotExtension = false;

        for (FunctionDescriptor superFunction : superFunctions) {
            if (superFunction.getExtensionReceiverParameter() != null)  {
                someSupersExtension = true;
            }
            else {
                someSupersNotExtension = true;
            }
        }

        if (someSupersExtension) {
            if (someSupersNotExtension) {
                reportError("Incompatible super methods: some are extension functions, some are not");
            }
            else {
                return true;
            }
        }
        return false;
    }

    @NotNull
    private VarargCheckResult checkVarargInSuperFunctions(@NotNull ValueParameterDescriptor originalParam) {
        boolean someSupersVararg = false;
        boolean someSupersNotVararg = false;
        for (FunctionDescriptor superFunction : superFunctions) {
            int originalIndex = originalParam.getIndex();
            int index = superFunction.getExtensionReceiverParameter() != null ? originalIndex - 1 : originalIndex;
            if (index != -1 && superFunction.getValueParameters().get(index).getVarargElementType() != null) {
                someSupersVararg = true;
            }
            else {
                someSupersNotVararg = true;
            }
        }

        KotlinType originalVarargElementType = originalParam.getVarargElementType();
        KotlinType originalType = originalParam.getType();

        if (someSupersVararg && someSupersNotVararg) {
            reportError("Incompatible super methods: some have vararg parameter, some have not");
            return new VarargCheckResult(originalType, originalVarargElementType != null);
        }

        if (someSupersVararg && originalVarargElementType == null) {
            // convert to vararg

            assert isArrayType(originalType);
            return new VarargCheckResult(TypeUtils.makeNotNullable(originalType), true);
        }
        else if (someSupersNotVararg && originalVarargElementType != null) {
            // convert to non-vararg

            assert isArrayType(originalType);
            return new VarargCheckResult(TypeUtils.makeNullable(originalType), false);
        }
        return new VarargCheckResult(originalType, originalVarargElementType != null);
    }

    private static boolean isArrayType(@NotNull KotlinType type) {
        return KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type);
    }

    private static class VarargCheckResult {
        public final KotlinType parameterType;
        public final boolean isVararg;

        public VarargCheckResult(KotlinType parameterType, boolean isVararg) {
            this.parameterType = parameterType;
            this.isVararg = isVararg;
        }
    }

    private static class TypeAndName {
        public final KotlinType type;
        public final Name name;

        public TypeAndName(KotlinType type, Name name) {
            this.type = type;
            this.name = name;
        }
    }

    private static class ValueParameters {
        private final KotlinType receiverType;
        private final List descriptors;
        private final boolean hasStableParameterNames;

        public ValueParameters(
                @Nullable KotlinType receiverType,
                @NotNull List descriptors,
                boolean hasStableParameterNames
        ) {
            this.receiverType = receiverType;
            this.descriptors = descriptors;
            this.hasStableParameterNames = hasStableParameterNames;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy