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