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    
017    package org.jetbrains.jet.lang.resolve.calls.tasks;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Sets;
021    import com.intellij.openapi.progress.ProgressIndicatorProvider;
022    import com.intellij.openapi.util.Pair;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.jet.lang.descriptors.*;
026    import org.jetbrains.jet.lang.psi.Call;
027    import org.jetbrains.jet.lang.psi.JetExpression;
028    import org.jetbrains.jet.lang.psi.JetReferenceExpression;
029    import org.jetbrains.jet.lang.psi.JetSuperExpression;
030    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
031    import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastUtils;
032    import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
033    import org.jetbrains.jet.lang.resolve.name.Name;
034    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035    import org.jetbrains.jet.lang.resolve.scopes.JetScopeUtils;
036    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
037    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
038    import org.jetbrains.jet.lang.types.ErrorUtils;
039    import org.jetbrains.jet.lang.types.JetType;
040    import org.jetbrains.jet.lang.types.PackageType;
041    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
042    
043    import java.util.*;
044    
045    import static org.jetbrains.jet.lang.resolve.calls.CallResolverUtil.isOrOverridesSynthesized;
046    import static org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind.*;
047    import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
048    
049    public class TaskPrioritizer {
050    
051        public static <D extends CallableDescriptor> void splitLexicallyLocalDescriptors(
052                @NotNull Collection<ResolutionCandidate<D>> allDescriptors,
053                @NotNull DeclarationDescriptor containerOfTheCurrentLocality,
054                @NotNull Collection<ResolutionCandidate<D>> local,
055                @NotNull Collection<ResolutionCandidate<D>> nonlocal
056        ) {
057            for (ResolutionCandidate<D> resolvedCall : allDescriptors) {
058                if (DescriptorUtils.isLocal(containerOfTheCurrentLocality, resolvedCall.getDescriptor())) {
059                    local.add(resolvedCall);
060                }
061                else {
062                    nonlocal.add(resolvedCall);
063                }
064            }
065        }
066    
067        @Nullable
068        public static JetSuperExpression getReceiverSuper(@NotNull ReceiverValue receiver) {
069            if (receiver instanceof ExpressionReceiver) {
070                ExpressionReceiver expressionReceiver = (ExpressionReceiver) receiver;
071                JetExpression expression = expressionReceiver.getExpression();
072                if (expression instanceof JetSuperExpression) {
073                    return (JetSuperExpression) expression;
074                }
075            }
076            return null;
077        }
078    
079        @NotNull
080        public static <D extends CallableDescriptor, F extends D> List<ResolutionTask<D, F>> computePrioritizedTasks(
081                @NotNull BasicCallResolutionContext context,
082                @NotNull Name name,
083                @NotNull TracingStrategy tracing,
084                @NotNull CallableDescriptorCollectors<D> callableDescriptorCollectors
085        ) {
086            List<Pair<JetScope, ReceiverValue>> variants = new ArrayList<Pair<JetScope, ReceiverValue>>(2);
087    
088            ReceiverValue explicitReceiver = context.call.getExplicitReceiver();
089            if (explicitReceiver.exists() && explicitReceiver.getType() instanceof PackageType) {
090                JetType receiverType = explicitReceiver.getType();
091                variants.add(Pair.create(receiverType.getMemberScope(), NO_RECEIVER));
092                ReceiverValue value = ((PackageType) receiverType).getReceiverValue();
093                if (value.exists()) {
094                    variants.add(Pair.create(context.scope, value));
095                }
096            }
097            else {
098                variants.add(Pair.create(context.scope, explicitReceiver));
099            }
100    
101            ResolutionTaskHolder<D, F> result =
102                    new ResolutionTaskHolder<D, F>(context, new MyPriorityProvider<D>(context), tracing);
103            for (Pair<JetScope, ReceiverValue> pair : variants) {
104                doComputeTasks(pair.second, new TaskPrioritizerContext<D, F>(name, result, context, pair.first, callableDescriptorCollectors));
105            }
106    
107            return result.getTasks();
108        }
109    
110        private static <D extends CallableDescriptor, F extends D> void doComputeTasks(
111                @NotNull ReceiverValue receiver,
112                @NotNull TaskPrioritizerContext<D, F> c
113        ) {
114            ProgressIndicatorProvider.checkCanceled();
115    
116            boolean resolveInvoke = c.context.call.getThisObject().exists();
117            if (resolveInvoke) {
118                addCandidatesForInvoke(receiver, c);
119                return;
120            }
121            Collection<ReceiverValue> implicitReceivers = Sets.newLinkedHashSet(JetScopeUtils.getImplicitReceiversHierarchyValues(c.scope));
122            if (receiver.exists()) {
123                addCandidatesForExplicitReceiver(receiver, implicitReceivers, c, /*isExplicit=*/true);
124                return;
125            }
126            addCandidatesForNoReceiver(implicitReceivers, c);
127        }
128    
129        private static <D extends CallableDescriptor, F extends D> void addCandidatesForExplicitReceiver(
130                @NotNull ReceiverValue explicitReceiver,
131                @NotNull Collection<ReceiverValue> implicitReceivers,
132                @NotNull TaskPrioritizerContext<D, F> c,
133                boolean isExplicit
134        ) {
135    
136            List<JetType> variantsForExplicitReceiver = AutoCastUtils.getAutoCastVariants(explicitReceiver, c.context);
137    
138            //members
139            for (CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) {
140                Collection<ResolutionCandidate<D>> members = Lists.newArrayList();
141                for (JetType type : variantsForExplicitReceiver) {
142                    Collection<D> membersForThisVariant =
143                            callableDescriptorCollector.getMembersByName(type, c.name, c.context.trace);
144                    convertWithReceivers(membersForThisVariant, explicitReceiver,
145                                         NO_RECEIVER, members, createKind(THIS_OBJECT, isExplicit), c.context.call);
146                }
147                c.result.addCandidates(members);
148            }
149    
150            for (CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) {
151                //member extensions
152                for (ReceiverValue implicitReceiver : implicitReceivers) {
153                    addMemberExtensionCandidates(implicitReceiver, explicitReceiver,
154                                                 callableDescriptorCollector, c, createKind(RECEIVER_ARGUMENT, isExplicit));
155                }
156                //extensions
157                Collection<ResolutionCandidate<D>> extensions = convertWithImpliedThis(
158                        c.scope, explicitReceiver, callableDescriptorCollector.getNonMembersByName(c.scope, c.name, c.context.trace),
159                        createKind(RECEIVER_ARGUMENT, isExplicit), c.context.call);
160                c.result.addCandidates(extensions);
161            }
162        }
163    
164        private static ExplicitReceiverKind createKind(ExplicitReceiverKind kind, boolean isExplicit) {
165            if (isExplicit) return kind;
166            return ExplicitReceiverKind.NO_EXPLICIT_RECEIVER;
167        }
168    
169        private static <D extends CallableDescriptor, F extends D> void addMemberExtensionCandidates(
170                @NotNull ReceiverValue thisObject,
171                @NotNull ReceiverValue receiverParameter,
172                @NotNull CallableDescriptorCollector<D> callableDescriptorCollector, TaskPrioritizerContext<D, F> c,
173                @NotNull ExplicitReceiverKind receiverKind
174        ) {
175            Collection<D> memberExtensions = callableDescriptorCollector.getNonMembersByName(
176                    thisObject.getType().getMemberScope(), c.name, c.context.trace);
177            c.result.addCandidates(convertWithReceivers(
178                    memberExtensions, thisObject, receiverParameter, receiverKind, c.context.call));
179        }
180    
181        private static <D extends CallableDescriptor, F extends D> void addCandidatesForNoReceiver(
182                @NotNull Collection<ReceiverValue> implicitReceivers,
183                @NotNull TaskPrioritizerContext<D, F> c
184        ) {
185            List<Collection<ResolutionCandidate<D>>> localsList = Lists.newArrayList();
186            List<Collection<ResolutionCandidate<D>>> nonlocalsList = Lists.newArrayList();
187            for (CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) {
188    
189                Collection<ResolutionCandidate<D>> members = convertWithImpliedThisAndNoReceiver(
190                        c.scope, callableDescriptorCollector.getNonExtensionsByName(c.scope, c.name, c.context.trace), c.context.call);
191    
192                List<ResolutionCandidate<D>> nonlocals = Lists.newArrayList();
193                List<ResolutionCandidate<D>> locals = Lists.newArrayList();
194                //noinspection unchecked,RedundantTypeArguments
195                TaskPrioritizer.<D>splitLexicallyLocalDescriptors(members, c.scope.getContainingDeclaration(), locals, nonlocals);
196    
197                localsList.add(locals);
198                nonlocalsList.add(nonlocals);
199            }
200    
201            //locals
202            c.result.addCandidates(localsList);
203    
204            //try all implicit receivers as explicit
205            for (ReceiverValue implicitReceiver : implicitReceivers) {
206                addCandidatesForExplicitReceiver(implicitReceiver, implicitReceivers, c, /*isExplicit=*/false);
207            }
208    
209            //nonlocals
210            c.result.addCandidates(nonlocalsList);
211        }
212    
213        private static <D extends CallableDescriptor, F extends D> void addCandidatesForInvoke(
214                @NotNull ReceiverValue explicitReceiver,
215                @NotNull TaskPrioritizerContext<D, F> c
216        ) {
217            List<ReceiverValue> implicitReceivers = JetScopeUtils.getImplicitReceiversHierarchyValues(c.scope);
218    
219            // For 'a.foo()' where foo has function type,
220            // a is explicitReceiver, foo is variableReceiver.
221            ReceiverValue variableReceiver = c.context.call.getThisObject();
222            assert variableReceiver.exists() : "'Invoke' call hasn't got variable receiver";
223    
224            // For invocation a.foo() explicit receiver 'a'
225            // can be a receiver for 'foo' variable
226            // or for 'invoke' function.
227    
228            // (1) a.foo + foo.invoke()
229            if (!explicitReceiver.exists()) {
230                addCandidatesForExplicitReceiver(variableReceiver, implicitReceivers, c, /*isExplicit=*/true);
231            }
232    
233            // (2) foo + a.invoke()
234    
235            // 'invoke' is member extension to explicit receiver while variable receiver is 'this object'
236            //trait A
237            //trait Foo { fun A.invoke() }
238    
239            if (explicitReceiver.exists()) {
240                //a.foo()
241                addCandidatesWhenInvokeIsMemberAndExtensionToExplicitReceiver(variableReceiver, explicitReceiver, c, BOTH_RECEIVERS);
242                return;
243            }
244            // with (a) { foo() }
245            for (ReceiverValue implicitReceiver : implicitReceivers) {
246                addCandidatesWhenInvokeIsMemberAndExtensionToExplicitReceiver(variableReceiver, implicitReceiver, c, THIS_OBJECT);
247            }
248        }
249    
250        private static <D extends CallableDescriptor, F extends D> void addCandidatesWhenInvokeIsMemberAndExtensionToExplicitReceiver(
251                @NotNull ReceiverValue thisObject,
252                @NotNull ReceiverValue receiverParameter,
253                @NotNull TaskPrioritizerContext<D, F> c,
254                @NotNull ExplicitReceiverKind receiverKind
255        ) {
256            for (CallableDescriptorCollector<D> callableDescriptorCollector : c.callableDescriptorCollectors) {
257                addMemberExtensionCandidates(thisObject, receiverParameter, callableDescriptorCollector, c, receiverKind);
258            }
259        }
260    
261        private static <D extends CallableDescriptor> Collection<ResolutionCandidate<D>> convertWithReceivers(
262                @NotNull Collection<D> descriptors,
263                @NotNull ReceiverValue thisObject,
264                @NotNull ReceiverValue receiverParameter,
265                @NotNull ExplicitReceiverKind explicitReceiverKind,
266                @NotNull Call call
267        ) {
268            Collection<ResolutionCandidate<D>> result = Lists.newArrayList();
269            convertWithReceivers(descriptors, thisObject, receiverParameter, result, explicitReceiverKind, call);
270            return result;
271        }
272    
273        private static <D extends CallableDescriptor> void convertWithReceivers(
274                @NotNull Collection<D> descriptors,
275                @NotNull ReceiverValue thisObject,
276                @NotNull ReceiverValue receiverParameter,
277                @NotNull Collection<ResolutionCandidate<D>> result,
278                @NotNull ExplicitReceiverKind explicitReceiverKind,
279                @NotNull Call call
280        ) {
281            for (D extension : descriptors) {
282                if (DescriptorUtils.isConstructorOfStaticNestedClass(extension)) {
283                    // We don't want static nested classes' constructors to be resolved with expectedThisObject
284                    continue;
285                }
286                ResolutionCandidate<D> candidate = ResolutionCandidate.create(call, extension);
287                candidate.setThisObject(thisObject);
288                candidate.setReceiverArgument(receiverParameter);
289                candidate.setExplicitReceiverKind(explicitReceiverKind);
290                result.add(candidate);
291            }
292        }
293    
294        public static <D extends CallableDescriptor> Collection<ResolutionCandidate<D>> convertWithImpliedThisAndNoReceiver(
295                @NotNull JetScope scope,
296                @NotNull Collection<? extends D> descriptors,
297                @NotNull Call call
298        ) {
299            return convertWithImpliedThis(scope, NO_RECEIVER, descriptors, NO_EXPLICIT_RECEIVER, call);
300        }
301    
302        public static <D extends CallableDescriptor> Collection<ResolutionCandidate<D>> convertWithImpliedThis(
303                @NotNull JetScope scope,
304                @NotNull ReceiverValue receiverParameter,
305                @NotNull Collection<? extends D> descriptors,
306                @NotNull ExplicitReceiverKind receiverKind,
307                @NotNull Call call
308        ) {
309            Collection<ResolutionCandidate<D>> result = Lists.newArrayList();
310            for (D descriptor : descriptors) {
311                ResolutionCandidate<D> candidate = ResolutionCandidate.create(call, descriptor);
312                candidate.setReceiverArgument(receiverParameter);
313                candidate.setExplicitReceiverKind(receiverKind);
314                if (setImpliedThis(scope, candidate)) {
315                    result.add(candidate);
316                }
317            }
318            return result;
319        }
320    
321        private static <D extends CallableDescriptor> boolean setImpliedThis(
322                @NotNull JetScope scope,
323                @NotNull ResolutionCandidate<D> candidate
324        ) {
325            ReceiverParameterDescriptor expectedThisObject = candidate.getDescriptor().getExpectedThisObject();
326            if (expectedThisObject == null) return true;
327            List<ReceiverParameterDescriptor> receivers = scope.getImplicitReceiversHierarchy();
328            for (ReceiverParameterDescriptor receiver : receivers) {
329                if (JetTypeChecker.INSTANCE.isSubtypeOf(receiver.getType(), expectedThisObject.getType())) {
330                    // TODO : Autocasts & nullability
331                    candidate.setThisObject(expectedThisObject.getValue());
332                    return true;
333                }
334            }
335            return false;
336        }
337    
338        public static <D extends CallableDescriptor, F extends D> List<ResolutionTask<D, F>> computePrioritizedTasksFromCandidates(
339                @NotNull BasicCallResolutionContext context,
340                @NotNull Collection<ResolutionCandidate<D>> candidates,
341                @NotNull TracingStrategy tracing
342        ) {
343            ResolutionTaskHolder<D, F> result = new ResolutionTaskHolder<D, F>(
344                    context, new MyPriorityProvider<D>(context), tracing);
345            result.addCandidates(candidates);
346            return result.getTasks();
347        }
348    
349        private static class MyPriorityProvider<D extends CallableDescriptor>
350                implements ResolutionTaskHolder.PriorityProvider<ResolutionCandidate<D>> {
351            private final BasicCallResolutionContext context;
352    
353            public MyPriorityProvider(BasicCallResolutionContext context) {
354                this.context = context;
355            }
356    
357            @Override
358            public int getPriority(ResolutionCandidate<D> call) {
359                return (isVisible(call) ? 2 : 0) + (isSynthesized(call) ? 0 : 1);
360            }
361    
362            @Override
363            public int getMaxPriority() {
364                return 3;
365            }
366    
367            private boolean isVisible(ResolutionCandidate<D> call) {
368                if (call == null) return false;
369                D candidateDescriptor = call.getDescriptor();
370                if (ErrorUtils.isError(candidateDescriptor)) return true;
371                return Visibilities.isVisible(candidateDescriptor, context.scope.getContainingDeclaration());
372            }
373    
374            private boolean isSynthesized(ResolutionCandidate<D> call) {
375                D descriptor = call.getDescriptor();
376                return descriptor instanceof CallableMemberDescriptor &&
377                       isOrOverridesSynthesized((CallableMemberDescriptor) descriptor);
378            }
379        }
380    
381        private static class TaskPrioritizerContext<D extends CallableDescriptor, F extends D> {
382            @NotNull public final Name name;
383            @NotNull public final ResolutionTaskHolder<D, F> result;
384            @NotNull public final BasicCallResolutionContext context;
385            @NotNull public final JetScope scope;
386            @NotNull public final CallableDescriptorCollectors<D> callableDescriptorCollectors;
387    
388            private TaskPrioritizerContext(
389                    @NotNull Name name,
390                    @NotNull ResolutionTaskHolder<D, F> result,
391                    @NotNull BasicCallResolutionContext context,
392                    @NotNull JetScope scope,
393                    @NotNull CallableDescriptorCollectors<D> callableDescriptorCollectors
394            ) {
395                this.name = name;
396                this.result = result;
397                this.context = context;
398                this.scope = scope;
399                this.callableDescriptorCollectors = callableDescriptorCollectors;
400            }
401        }
402    }