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