001/*
002 * Copyright 2010-2013 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
017package org.jetbrains.jet.lang.resolve.java.sam;
018
019import com.google.common.collect.Lists;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.*;
023import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
024import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl;
025import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl;
026import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
027import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
028import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
029import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
030import org.jetbrains.jet.lang.resolve.java.kotlinSignature.SignaturesUtil;
031import org.jetbrains.jet.lang.resolve.name.Name;
032import org.jetbrains.jet.lang.types.*;
033import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
034
035import java.util.Arrays;
036import java.util.Collections;
037import java.util.List;
038import java.util.Map;
039
040import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
041
042public class SingleAbstractMethodUtils {
043
044    @NotNull
045    public static List<CallableMemberDescriptor> getAbstractMembers(@NotNull JetType type) {
046        List<CallableMemberDescriptor> abstractMembers = Lists.newArrayList();
047        for (DeclarationDescriptor member : type.getMemberScope().getAllDescriptors()) {
048            if (member instanceof CallableMemberDescriptor &&
049                    ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT &&
050                    !CallResolverUtil.isOrOverridesSynthesized((CallableMemberDescriptor) member)) {
051                abstractMembers.add((CallableMemberDescriptor) member);
052            }
053        }
054        return abstractMembers;
055    }
056
057    private static JetType fixProjections(@NotNull JetType functionType) {
058        //removes redundant projection kinds and detects conflicts
059
060        List<TypeProjection> arguments = Lists.newArrayList();
061        for (TypeParameterDescriptor typeParameter : functionType.getConstructor().getParameters()) {
062            Variance variance = typeParameter.getVariance();
063            TypeProjection argument = functionType.getArguments().get(typeParameter.getIndex());
064            Variance kind = argument.getProjectionKind();
065            if (kind != INVARIANT && variance != INVARIANT) {
066                if (kind == variance) {
067                    arguments.add(new TypeProjection(argument.getType()));
068                }
069                else {
070                    return null;
071                }
072            }
073            else {
074                 arguments.add(argument);
075            }
076        }
077        ClassifierDescriptor classifier = functionType.getConstructor().getDeclarationDescriptor();
078        assert classifier instanceof ClassDescriptor : "Not class: " + classifier;
079        return new JetTypeImpl(
080                functionType.getAnnotations(),
081                functionType.getConstructor(),
082                functionType.isNullable(),
083                arguments,
084                ((ClassDescriptor) classifier).getMemberScope(arguments)
085        );
086    }
087
088    @Nullable
089    private static JetType getFunctionTypeForSamType(@NotNull JetType samType) {
090        // e.g. samType == Comparator<String>?
091
092        ClassifierDescriptor classifier = samType.getConstructor().getDeclarationDescriptor();
093        if (classifier instanceof ClassDescriptorFromJvmBytecode) {
094            // Function2<T, T, Int>
095            JetType functionTypeDefault = ((ClassDescriptorFromJvmBytecode) classifier).getFunctionTypeForSamInterface();
096
097            if (functionTypeDefault != null) {
098                // Function2<String, String, Int>?
099                JetType substitute = TypeSubstitutor.create(samType).substitute(functionTypeDefault, Variance.INVARIANT);
100
101                return substitute == null ? null : fixProjections(TypeUtils.makeNullableAsSpecified(substitute, samType.isNullable()));
102            }
103        }
104        return null;
105    }
106
107    @NotNull
108    public static JetType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) {
109        JetType returnType = function.getReturnType();
110        assert returnType != null : "function is not initialized: " + function;
111        List<JetType> parameterTypes = Lists.newArrayList();
112        for (ValueParameterDescriptor parameter : function.getValueParameters()) {
113            parameterTypes.add(parameter.getType());
114        }
115        return KotlinBuiltIns.getInstance().getFunctionType(
116                Collections.<AnnotationDescriptor>emptyList(), null, parameterTypes, returnType);
117    }
118
119    private static boolean isSamInterface(@NotNull ClassDescriptor klass) {
120        if (klass.getKind() != ClassKind.TRAIT) {
121            return false;
122        }
123
124        List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass.getDefaultType());
125        if (abstractMembers.size() == 1) {
126            CallableMemberDescriptor member = abstractMembers.get(0);
127            if (member instanceof SimpleFunctionDescriptor) {
128                return member.getTypeParameters().isEmpty();
129            }
130        }
131        return false;
132    }
133
134    @NotNull
135    public static SimpleFunctionDescriptor createSamConstructorFunction(
136            @NotNull ClassOrNamespaceDescriptor owner,
137            @NotNull ClassDescriptor samInterface
138    ) {
139        assert isSamInterface(samInterface) : samInterface;
140
141        SimpleFunctionDescriptorImpl result = new SimpleFunctionDescriptorImpl(
142                owner,
143                samInterface.getAnnotations(),
144                samInterface.getName(),
145                CallableMemberDescriptor.Kind.SYNTHESIZED
146        );
147
148        TypeParameters typeParameters = recreateAndInitializeTypeParameters(samInterface.getTypeConstructor().getParameters(), result);
149
150        JetType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType());
151        assert parameterTypeUnsubstituted != null : "couldn't get function type for SAM type " + samInterface.getDefaultType();
152        JetType parameterType = typeParameters.substitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE);
153        assert parameterType != null : "couldn't substitute type: " + parameterType + ", substitutor = " + typeParameters.substitutor;
154        ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl(
155                result, 0, Collections.<AnnotationDescriptor>emptyList(), Name.identifier("function"), parameterType, false, null);
156
157        JetType returnType = typeParameters.substitutor.substitute(samInterface.getDefaultType(), Variance.OUT_VARIANCE);
158        assert returnType != null : "couldn't substitute type: " + returnType + ", substitutor = " + typeParameters.substitutor;
159
160        result.initialize(
161                null,
162                null,
163                typeParameters.descriptors,
164                Arrays.asList(parameter),
165                returnType,
166                Modality.FINAL,
167                samInterface.getVisibility(),
168                false
169        );
170
171        return result;
172    }
173
174    public static boolean isSamType(@NotNull JetType type) {
175        return getFunctionTypeForSamType(type) != null;
176    }
177
178    public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) {
179        for (ValueParameterDescriptor param : fun.getValueParameters()) {
180            if (isSamType(param.getType())) {
181                return true;
182            }
183        }
184        return false;
185    }
186
187    @NotNull
188    public static SimpleFunctionDescriptor createSamAdapterFunction(@NotNull final SimpleFunctionDescriptor original) {
189        final SimpleFunctionDescriptorImpl result = new SimpleFunctionDescriptorImpl(
190                original.getContainingDeclaration(),
191                original.getAnnotations(),
192                original.getName(),
193                CallableMemberDescriptor.Kind.SYNTHESIZED
194        );
195        FunctionInitializer initializer = new FunctionInitializer() {
196            @Override
197            public void initialize(
198                    @NotNull List<TypeParameterDescriptor> typeParameters,
199                    @NotNull List<ValueParameterDescriptor> valueParameters,
200                    @Nullable JetType returnType
201            ) {
202                result.initialize(
203                        null,
204                        original.getExpectedThisObject(),
205                        typeParameters,
206                        valueParameters,
207                        returnType,
208                        original.getModality(),
209                        original.getVisibility(),
210                        false
211                );
212            }
213        };
214        return initSamAdapter(original, result, initializer);
215    }
216
217    @NotNull
218    public static ConstructorDescriptor createSamAdapterConstructor(@NotNull final ConstructorDescriptor original) {
219        final ConstructorDescriptorImpl result = new ConstructorDescriptorImpl(
220                original.getContainingDeclaration(),
221                original.getAnnotations(),
222                original.isPrimary(),
223                CallableMemberDescriptor.Kind.SYNTHESIZED
224        );
225        FunctionInitializer initializer = new FunctionInitializer() {
226            @Override
227            public void initialize(
228                    @NotNull List<TypeParameterDescriptor> typeParameters,
229                    @NotNull List<ValueParameterDescriptor> valueParameters,
230                    @Nullable JetType returnType
231            ) {
232                result.initialize(
233                        typeParameters,
234                        valueParameters,
235                        original.getVisibility(),
236                        original.getExpectedThisObject() == ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER
237                );
238            }
239        };
240        return initSamAdapter(original, result, initializer);
241    }
242
243    private static <F extends FunctionDescriptor> F initSamAdapter(
244            @NotNull F original,
245            @NotNull F adapter,
246            @NotNull FunctionInitializer initializer
247    ) {
248        TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter);
249
250        JetType returnTypeUnsubstituted = original.getReturnType();
251        JetType returnType;
252        if (returnTypeUnsubstituted == null) { // return type may be null for not yet initialized constructors
253            returnType = null;
254        }
255        else {
256            returnType = typeParameters.substitutor.substitute(returnTypeUnsubstituted, Variance.OUT_VARIANCE);
257            assert returnType != null : "couldn't substitute type: " + returnType + ", substitutor = " + typeParameters.substitutor;
258        }
259
260        List<ValueParameterDescriptor> valueParameters = Lists.newArrayList();
261        for (ValueParameterDescriptor originalParam : original.getValueParameters()) {
262            JetType originalType = originalParam.getType();
263            JetType functionType = getFunctionTypeForSamType(originalType);
264            JetType newTypeUnsubstituted = functionType != null ? functionType : originalType;
265            JetType newType = typeParameters.substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE);
266            assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + typeParameters.substitutor;
267
268            ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl(
269                    adapter, originalParam.getIndex(), originalParam.getAnnotations(), originalParam.getName(), newType, false, null);
270            valueParameters.add(newParam);
271        }
272
273        initializer.initialize(typeParameters.descriptors, valueParameters, returnType);
274        return adapter;
275    }
276
277    @NotNull
278    private static TypeParameters recreateAndInitializeTypeParameters(
279            @NotNull List<TypeParameterDescriptor> originalParameters,
280            @Nullable DeclarationDescriptor newOwner
281    ) {
282        Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters =
283                SignaturesUtil.recreateTypeParametersAndReturnMapping(originalParameters, newOwner);
284        TypeSubstitutor typeParametersSubstitutor = SignaturesUtil.createSubstitutorForTypeParameters(traitToFunTypeParameters);
285        for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) {
286            TypeParameterDescriptor traitTypeParameter = mapEntry.getKey();
287            TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue();
288
289            for (JetType upperBound : traitTypeParameter.getUpperBounds()) {
290                JetType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT);
291                assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor;
292                funTypeParameter.addUpperBound(upperBoundSubstituted);
293            }
294
295            funTypeParameter.setInitialized();
296        }
297
298        List<TypeParameterDescriptor> typeParameters = Lists.<TypeParameterDescriptor>newArrayList(traitToFunTypeParameters.values());
299        return new TypeParameters(typeParameters, typeParametersSubstitutor);
300    }
301
302    @NotNull
303    public static SimpleFunctionDescriptor getAbstractMethodOfSamType(@NotNull JetType type) {
304        return (SimpleFunctionDescriptor) getAbstractMembers(type).get(0);
305    }
306
307    @NotNull
308    public static SimpleFunctionDescriptor getAbstractMethodOfSamInterface(@NotNull ClassDescriptor samInterface) {
309        return getAbstractMethodOfSamType(samInterface.getDefaultType());
310    }
311
312    private SingleAbstractMethodUtils() {
313    }
314
315    private static class TypeParameters {
316        public final List<TypeParameterDescriptor> descriptors;
317        public final TypeSubstitutor substitutor;
318
319        private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) {
320            this.descriptors = descriptors;
321            this.substitutor = substitutor;
322        }
323    }
324
325    private static abstract class FunctionInitializer {
326        public abstract void initialize(
327                @NotNull List<TypeParameterDescriptor> typeParameters,
328                @NotNull List<ValueParameterDescriptor> valueParameters,
329                @Nullable JetType returnType
330        );
331    }
332}