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.resolve.jvm.kotlinSignature;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.util.Function;
021    import com.intellij.util.containers.ContainerUtil;
022    import kotlin.collections.CollectionsKt;
023    import kotlin.jvm.functions.Function1;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
027    import org.jetbrains.kotlin.descriptors.*;
028    import org.jetbrains.kotlin.descriptors.annotations.Annotations;
029    import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
030    import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
031    import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor;
032    import org.jetbrains.kotlin.load.java.structure.JavaMethod;
033    import org.jetbrains.kotlin.name.FqNameUnsafe;
034    import org.jetbrains.kotlin.name.Name;
035    import org.jetbrains.kotlin.resolve.DescriptorUtils;
036    import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
037    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
038    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.KotlinToJvmSignatureMapper;
039    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.KotlinToJvmSignatureMapperKt;
040    import org.jetbrains.kotlin.types.KotlinType;
041    import org.jetbrains.kotlin.types.TypeUtils;
042    
043    import java.util.*;
044    
045    import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName;
046    
047    public class SignaturesPropagationData {
048    
049        private static final KotlinToJvmSignatureMapper SIGNATURE_MAPPER = ServiceLoader.load(
050                KotlinToJvmSignatureMapper.class,
051                KotlinToJvmSignatureMapper.class.getClassLoader()
052        ).iterator().next();
053    
054        private final ValueParameters modifiedValueParameters;
055        private final List<String> signatureErrors = new ArrayList<String>(0);
056        private final List<FunctionDescriptor> superFunctions;
057    
058        public SignaturesPropagationData(
059                @NotNull ClassDescriptor containingClass,
060                @NotNull KotlinType autoReturnType, // type built by JavaTypeTransformer from Java signature and @NotNull annotations
061                @Nullable KotlinType receiverType,
062                @NotNull List<ValueParameterDescriptor> autoValueParameters, // descriptors built by parameters resolver
063                @NotNull List<TypeParameterDescriptor> autoTypeParameters, // descriptors built by signature resolver
064                @NotNull JavaMethod method
065        ) {
066            assert receiverType == null : "Parameters before propagation have receiver type," +
067                                          " but propagation should be disabled for functions compiled from Kotlin in class: " +
068                                          DescriptorUtils.getFqName(containingClass);
069    
070            JavaMethodDescriptor autoMethodDescriptor =
071                    createAutoMethodDescriptor(containingClass, method, autoReturnType, autoValueParameters, autoTypeParameters);
072    
073            superFunctions = getSuperFunctionsForMethod(method, autoMethodDescriptor, containingClass);
074            modifiedValueParameters = modifyValueParametersAccordingToSuperMethods(autoValueParameters);
075        }
076    
077        @NotNull
078        private static JavaMethodDescriptor createAutoMethodDescriptor(
079                @NotNull ClassDescriptor containingClass,
080                @NotNull JavaMethod method, KotlinType autoReturnType,
081                @NotNull List<ValueParameterDescriptor> autoValueParameters,
082                @NotNull List<TypeParameterDescriptor> autoTypeParameters
083        ) {
084            JavaMethodDescriptor autoMethodDescriptor = JavaMethodDescriptor.createJavaMethod(
085                    containingClass,
086                    Annotations.Companion.getEMPTY(),
087                    method.getName(),
088                    //TODO: what to do?
089                    SourceElement.NO_SOURCE
090            );
091            autoMethodDescriptor.initialize(
092                    /* receiverParameterType = */ null,
093                    containingClass.getThisAsReceiverParameter(),
094                    autoTypeParameters,
095                    autoValueParameters,
096                    autoReturnType,
097                    Modality.OPEN,
098                    Visibilities.PUBLIC
099            );
100            return autoMethodDescriptor;
101        }
102    
103        public KotlinType getModifiedReceiverType() {
104            return modifiedValueParameters.receiverType;
105        }
106    
107        public List<ValueParameterDescriptor> getModifiedValueParameters() {
108            return modifiedValueParameters.descriptors;
109        }
110    
111        public boolean getModifiedHasStableParameterNames() {
112            return modifiedValueParameters.hasStableParameterNames;
113        }
114    
115        public List<String> getSignatureErrors() {
116            return signatureErrors;
117        }
118    
119        void reportError(String error) {
120            signatureErrors.add(error);
121        }
122    
123        private ValueParameters modifyValueParametersAccordingToSuperMethods(@NotNull List<ValueParameterDescriptor> parameters) {
124            KotlinType resultReceiverType = null;
125            List<ValueParameterDescriptor> resultParameters = new ArrayList<ValueParameterDescriptor>(parameters.size());
126    
127            boolean shouldBeExtension = checkIfShouldBeExtension();
128    
129            for (final ValueParameterDescriptor originalParam : parameters) {
130                final int originalIndex = originalParam.getIndex();
131                List<TypeAndName> typesFromSuperMethods = ContainerUtil.map(superFunctions,
132                        new Function<FunctionDescriptor, TypeAndName>() {
133                            @Override
134                            public TypeAndName fun(FunctionDescriptor superFunction) {
135                                ReceiverParameterDescriptor receiver = superFunction.getExtensionReceiverParameter();
136                                int index = receiver != null ? originalIndex - 1 : originalIndex;
137                                if (index == -1) {
138                                    assert receiver != null : "can't happen: index is -1, while function is not extension";
139                                    return new TypeAndName(receiver.getType(), originalParam.getName());
140                                }
141                                ValueParameterDescriptor parameter = superFunction.getValueParameters().get(index);
142                                return new TypeAndName(parameter.getType(), parameter.getName());
143                            }
144                        });
145    
146                VarargCheckResult varargCheckResult = checkVarargInSuperFunctions(originalParam);
147    
148                KotlinType altType = varargCheckResult.parameterType;
149    
150                if (shouldBeExtension && originalIndex == 0) {
151                    resultReceiverType = altType;
152                }
153                else {
154                    Name stableName = null;
155                    for (int i = 0; i < superFunctions.size(); i++) {
156                        if (superFunctions.get(i).hasStableParameterNames()) {
157                            // When there's more than one stable name in super functions, we pick the first one. This behaviour is similar to
158                            // the compiler front-end, except that it reports a warning in such cases
159                            // TODO: report a warning somewhere if there's more than one stable name in super functions
160                            stableName = typesFromSuperMethods.get(i).name;
161                            break;
162                        }
163                    }
164    
165                    resultParameters.add(new ValueParameterDescriptorImpl(
166                            originalParam.getContainingDeclaration(),
167                            null,
168                            shouldBeExtension ? originalIndex - 1 : originalIndex,
169                            originalParam.getAnnotations(),
170                            stableName != null ? stableName : originalParam.getName(),
171                            altType,
172                            originalParam.declaresDefaultValue(),
173                            originalParam.isCrossinline(),
174                            originalParam.isNoinline(),
175                            varargCheckResult.isVararg ? DescriptorUtilsKt.getBuiltIns(originalParam).getArrayElementType(altType) : null,
176                            SourceElement.NO_SOURCE
177                    ));
178                }
179            }
180    
181            boolean hasStableParameterNames = CollectionsKt.any(superFunctions, new Function1<FunctionDescriptor, Boolean>() {
182                @Override
183                public Boolean invoke(FunctionDescriptor descriptor) {
184                    return descriptor.hasStableParameterNames();
185                }
186            });
187    
188            return new ValueParameters(resultReceiverType, resultParameters, hasStableParameterNames);
189        }
190    
191        private static List<FunctionDescriptor> getSuperFunctionsForMethod(
192                @NotNull JavaMethod method,
193                @NotNull JavaMethodDescriptor autoMethodDescriptor,
194                @NotNull ClassDescriptor containingClass
195        ) {
196            List<FunctionDescriptor> superFunctions = Lists.newArrayList();
197    
198            // TODO: Add propagation for other kotlin descriptors (KT-3621)
199            Name name = method.getName();
200            JvmMethodSignature autoSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(autoMethodDescriptor);
201            for (KotlinType supertype : containingClass.getTypeConstructor().getSupertypes()) {
202                Collection<FunctionDescriptor> superFunctionCandidates = supertype.getMemberScope().getContributedFunctions(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS);
203                for (FunctionDescriptor candidate : superFunctionCandidates) {
204                    JvmMethodSignature candidateSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(candidate);
205                    if (KotlinToJvmSignatureMapperKt.erasedSignaturesEqualIgnoringReturnTypes(autoSignature, candidateSignature)) {
206                        superFunctions.add(candidate);
207                    }
208                }
209            }
210    
211            // sorting for diagnostic stability
212            Collections.sort(superFunctions, new Comparator<FunctionDescriptor>() {
213                @Override
214                public int compare(@NotNull FunctionDescriptor fun1, @NotNull FunctionDescriptor fun2) {
215                    FqNameUnsafe fqName1 = getFqName(fun1.getContainingDeclaration());
216                    FqNameUnsafe fqName2 = getFqName(fun2.getContainingDeclaration());
217                    return fqName1.asString().compareTo(fqName2.asString());
218                }
219            });
220            return superFunctions;
221        }
222    
223        private boolean checkIfShouldBeExtension() {
224            boolean someSupersExtension = false;
225            boolean someSupersNotExtension = false;
226    
227            for (FunctionDescriptor superFunction : superFunctions) {
228                if (superFunction.getExtensionReceiverParameter() != null)  {
229                    someSupersExtension = true;
230                }
231                else {
232                    someSupersNotExtension = true;
233                }
234            }
235    
236            if (someSupersExtension) {
237                if (someSupersNotExtension) {
238                    reportError("Incompatible super methods: some are extension functions, some are not");
239                }
240                else {
241                    return true;
242                }
243            }
244            return false;
245        }
246    
247        @NotNull
248        private VarargCheckResult checkVarargInSuperFunctions(@NotNull ValueParameterDescriptor originalParam) {
249            boolean someSupersVararg = false;
250            boolean someSupersNotVararg = false;
251            for (FunctionDescriptor superFunction : superFunctions) {
252                int originalIndex = originalParam.getIndex();
253                int index = superFunction.getExtensionReceiverParameter() != null ? originalIndex - 1 : originalIndex;
254                if (index != -1 && superFunction.getValueParameters().get(index).getVarargElementType() != null) {
255                    someSupersVararg = true;
256                }
257                else {
258                    someSupersNotVararg = true;
259                }
260            }
261    
262            KotlinType originalVarargElementType = originalParam.getVarargElementType();
263            KotlinType originalType = originalParam.getType();
264    
265            if (someSupersVararg && someSupersNotVararg) {
266                reportError("Incompatible super methods: some have vararg parameter, some have not");
267                return new VarargCheckResult(originalType, originalVarargElementType != null);
268            }
269    
270            if (someSupersVararg && originalVarargElementType == null) {
271                // convert to vararg
272    
273                assert isArrayType(originalType);
274                return new VarargCheckResult(TypeUtils.makeNotNullable(originalType), true);
275            }
276            else if (someSupersNotVararg && originalVarargElementType != null) {
277                // convert to non-vararg
278    
279                assert isArrayType(originalType);
280                return new VarargCheckResult(TypeUtils.makeNullable(originalType), false);
281            }
282            return new VarargCheckResult(originalType, originalVarargElementType != null);
283        }
284    
285        private static boolean isArrayType(@NotNull KotlinType type) {
286            return KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type);
287        }
288    
289        private static class VarargCheckResult {
290            public final KotlinType parameterType;
291            public final boolean isVararg;
292    
293            public VarargCheckResult(KotlinType parameterType, boolean isVararg) {
294                this.parameterType = parameterType;
295                this.isVararg = isVararg;
296            }
297        }
298    
299        private static class TypeAndName {
300            public final KotlinType type;
301            public final Name name;
302    
303            public TypeAndName(KotlinType type, Name name) {
304                this.type = type;
305                this.name = name;
306            }
307        }
308    
309        private static class ValueParameters {
310            private final KotlinType receiverType;
311            private final List<ValueParameterDescriptor> descriptors;
312            private final boolean hasStableParameterNames;
313    
314            public ValueParameters(
315                    @Nullable KotlinType receiverType,
316                    @NotNull List<ValueParameterDescriptor> descriptors,
317                    boolean hasStableParameterNames
318            ) {
319                this.receiverType = receiverType;
320                this.descriptors = descriptors;
321                this.hasStableParameterNames = hasStableParameterNames;
322            }
323        }
324    }