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