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;
018    
019    import com.google.common.base.Function;
020    import com.google.common.collect.Collections2;
021    import com.google.common.collect.Lists;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
024    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
025    import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
026    import org.jetbrains.jet.lang.psi.*;
027    import org.jetbrains.jet.lang.resolve.ChainedTemporaryBindingTrace;
028    import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace;
029    import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace;
030    import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
031    import org.jetbrains.jet.lang.resolve.calls.context.CallCandidateResolutionContext;
032    import org.jetbrains.jet.lang.resolve.calls.context.ContextDependency;
033    import org.jetbrains.jet.lang.resolve.calls.model.MutableResolvedCall;
034    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallImpl;
035    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCallImpl;
036    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
037    import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
038    import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind;
039    import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate;
040    import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask;
041    import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategyForInvoke;
042    import org.jetbrains.jet.lang.resolve.calls.util.DelegatingCall;
043    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
044    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
045    import org.jetbrains.jet.lang.types.JetType;
046    
047    import java.util.Collection;
048    import java.util.Collections;
049    import java.util.List;
050    
051    /**
052     * CallTransformer treats specially 'variable as function' call case, other cases keeps unchanged (base realization).
053     *
054     * For the call 'b.foo(1)' where foo is a variable that has method 'invoke' (for example of function type)
055     * CallTransformer creates two contexts, two calls in each, and performs second ('invoke') call resolution:
056     *
057     *   context#1. calls: 'b.foo' 'invoke(1)'
058     *   context#2. calls: 'foo'   'b.invoke(1)'
059     *
060     * If success VariableAsFunctionResolvedCall is created.
061     */
062    public class CallTransformer<D extends CallableDescriptor, F extends D> {
063        private CallTransformer() {}
064    
065        /**
066         * Returns two contexts for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER), one context otherwise
067         */
068        @NotNull
069        public Collection<CallCandidateResolutionContext<D>> createCallContexts(@NotNull ResolutionCandidate<D> candidate,
070                @NotNull ResolutionTask<D, F> task,
071                @NotNull TemporaryBindingTrace candidateTrace
072        ) {
073            ResolvedCallImpl<D> candidateCall = ResolvedCallImpl.create(candidate, candidateTrace, task.tracing, task.dataFlowInfoForArguments);
074            return Collections.singleton(CallCandidateResolutionContext.create(candidateCall, task, candidateTrace, task.tracing));
075        }
076    
077        /**
078         * Returns collection of resolved calls for 'invoke' for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER),
079         * the resolved call from callCandidateResolutionContext otherwise
080         */
081        @NotNull
082        public Collection<MutableResolvedCall<F>> transformCall(@NotNull CallCandidateResolutionContext<D> callCandidateResolutionContext,
083                @NotNull CallResolver callResolver,
084                @NotNull ResolutionTask<D, F> task
085        ) {
086            return Collections.singleton((MutableResolvedCall<F>) callCandidateResolutionContext.candidateCall);
087        }
088    
089    
090        public static CallTransformer<VariableDescriptor, VariableDescriptor> PROPERTY_CALL_TRANSFORMER = new CallTransformer<VariableDescriptor, VariableDescriptor>();
091    
092        public static CallTransformer<CallableDescriptor, FunctionDescriptor> FUNCTION_CALL_TRANSFORMER = new CallTransformer<CallableDescriptor, FunctionDescriptor>() {
093            @NotNull
094            @Override
095            public Collection<CallCandidateResolutionContext<CallableDescriptor>> createCallContexts(@NotNull ResolutionCandidate<CallableDescriptor> candidate,
096                    @NotNull ResolutionTask<CallableDescriptor, FunctionDescriptor> task, @NotNull TemporaryBindingTrace candidateTrace) {
097    
098                if (candidate.getDescriptor() instanceof FunctionDescriptor) {
099                    return super.createCallContexts(candidate, task, candidateTrace);
100                }
101    
102                assert candidate.getDescriptor() instanceof VariableDescriptor;
103    
104                boolean hasReceiver = candidate.getReceiverArgument().exists();
105                Call variableCall = stripCallArguments(task.call);
106                ResolutionCandidate<CallableDescriptor> variableCandidate = getVariableCallCandidate(candidate, variableCall);
107                if (!hasReceiver) {
108                    CallCandidateResolutionContext<CallableDescriptor> context = CallCandidateResolutionContext.create(
109                            ResolvedCallImpl.create(variableCandidate, candidateTrace, task.tracing, task.dataFlowInfoForArguments), task, candidateTrace, task.tracing, variableCall);
110                    return Collections.singleton(context);
111                }
112                CallCandidateResolutionContext<CallableDescriptor> contextWithReceiver = createContextWithChainedTrace(
113                        variableCandidate, variableCall, candidateTrace, task, ReceiverValue.NO_RECEIVER);
114    
115                Call variableCallWithoutReceiver = stripReceiver(variableCall);
116                ResolutionCandidate<CallableDescriptor> candidateWithoutReceiver = ResolutionCandidate.create(
117                        variableCandidate.getCall(),
118                        variableCandidate.getDescriptor(),
119                        variableCandidate.getThisObject(),
120                        ReceiverValue.NO_RECEIVER,
121                        ExplicitReceiverKind.NO_EXPLICIT_RECEIVER, false);
122    
123                CallCandidateResolutionContext<CallableDescriptor> contextWithoutReceiver = createContextWithChainedTrace(
124                        candidateWithoutReceiver, variableCallWithoutReceiver, candidateTrace, task, variableCall.getExplicitReceiver());
125    
126                return Lists.newArrayList(contextWithReceiver, contextWithoutReceiver);
127            }
128    
129            private CallCandidateResolutionContext<CallableDescriptor> createContextWithChainedTrace(
130                    @NotNull ResolutionCandidate<CallableDescriptor> candidate, @NotNull Call call, @NotNull TemporaryBindingTrace temporaryTrace,
131                    @NotNull ResolutionTask<CallableDescriptor, FunctionDescriptor> task, @NotNull ReceiverValue receiverValue
132            ) {
133                ChainedTemporaryBindingTrace chainedTrace = ChainedTemporaryBindingTrace.create(temporaryTrace, "chained trace to resolve candidate", candidate);
134                ResolvedCallImpl<CallableDescriptor> resolvedCall = ResolvedCallImpl.create(candidate, chainedTrace, task.tracing, task.dataFlowInfoForArguments);
135                return CallCandidateResolutionContext.create(resolvedCall, task, chainedTrace, task.tracing, call, receiverValue);
136            }
137    
138            @NotNull
139            private ResolutionCandidate<CallableDescriptor> getVariableCallCandidate(
140                    @NotNull ResolutionCandidate<CallableDescriptor> candidate,
141                    @NotNull Call variableCall
142            ) {
143                return ResolutionCandidate.create(
144                        variableCall,
145                        candidate.getDescriptor(),
146                        candidate.getThisObject(),
147                        candidate.getReceiverArgument(),
148                        candidate.getExplicitReceiverKind(),
149                        candidate.isSafeCall());
150            }
151    
152            private Call stripCallArguments(@NotNull Call call) {
153                return new DelegatingCall(call) {
154                    @Override
155                    public JetValueArgumentList getValueArgumentList() {
156                        return null;
157                    }
158    
159                    @NotNull
160                    @Override
161                    public List<? extends ValueArgument> getValueArguments() {
162                        return Collections.emptyList();
163                    }
164    
165                    @NotNull
166                    @Override
167                    public List<JetExpression> getFunctionLiteralArguments() {
168                        return Collections.emptyList();
169                    }
170    
171                    @NotNull
172                    @Override
173                    public List<JetTypeProjection> getTypeArguments() {
174                        return Collections.emptyList();
175                    }
176    
177                    @Override
178                    public JetTypeArgumentList getTypeArgumentList() {
179                        return null;
180                    }
181    
182                    @NotNull
183                    @Override
184                    public JetElement getCallElement() {
185                        JetExpression calleeExpression = getCalleeExpression();
186                        assert calleeExpression != null : "No callee expression: " + getCallElement().getText();
187    
188                        return calleeExpression;
189                    }
190                };
191            }
192    
193            private Call stripReceiver(@NotNull Call variableCall) {
194                return new DelegatingCall(variableCall) {
195                    @NotNull
196                    @Override
197                    public ReceiverValue getExplicitReceiver() {
198                        return ReceiverValue.NO_RECEIVER;
199                    }
200                };
201            }
202    
203            @NotNull
204            @Override
205            public Collection<MutableResolvedCall<FunctionDescriptor>> transformCall(
206                    @NotNull CallCandidateResolutionContext<CallableDescriptor> context,
207                    @NotNull CallResolver callResolver,
208                    @NotNull ResolutionTask<CallableDescriptor, FunctionDescriptor> task
209            ) {
210                CallableDescriptor descriptor = context.candidateCall.getCandidateDescriptor();
211                if (descriptor instanceof FunctionDescriptor) {
212                    return super.transformCall(context, callResolver, task);
213                }
214    
215                assert descriptor instanceof VariableDescriptor;
216                JetType returnType = descriptor.getReturnType();
217                if (returnType == null) {
218                    return Collections.emptyList();
219                }
220    
221                final MutableResolvedCall<VariableDescriptor> variableResolvedCall = (MutableResolvedCall)context.candidateCall;
222    
223                JetExpression calleeExpression = task.call.getCalleeExpression();
224                if (calleeExpression == null) return Collections.emptyList();
225    
226                ExpressionReceiver variableReceiver = new ExpressionReceiver(calleeExpression, variableResolvedCall.getResultingDescriptor().getType());
227                Call functionCall = new CallForImplicitInvoke(context.explicitExtensionReceiverForInvoke, variableReceiver, task.call);
228    
229                DelegatingBindingTrace variableCallTrace = context.candidateCall.getTrace();
230                BasicCallResolutionContext basicCallResolutionContext = BasicCallResolutionContext.create(
231                        context.replaceBindingTrace(variableCallTrace).replaceContextDependency(ContextDependency.DEPENDENT),
232                        functionCall, context.checkArguments, context.dataFlowInfoForArguments);
233    
234                // 'invoke' call resolve
235                TracingStrategyForInvoke tracingForInvoke = new TracingStrategyForInvoke(
236                        calleeExpression, functionCall, variableReceiver.getType());
237                OverloadResolutionResults<FunctionDescriptor> results = callResolver.resolveCallForInvoke(
238                        basicCallResolutionContext, tracingForInvoke);
239                Collection<MutableResolvedCall<FunctionDescriptor>> calls = ((OverloadResolutionResultsImpl<FunctionDescriptor>)results).getResultingCalls();
240    
241                return Collections2.transform(calls, new Function<MutableResolvedCall<FunctionDescriptor>, MutableResolvedCall<FunctionDescriptor>>() {
242                    @Override
243                    public MutableResolvedCall<FunctionDescriptor> apply(MutableResolvedCall<FunctionDescriptor> functionResolvedCall) {
244                        return new VariableAsFunctionResolvedCallImpl(functionResolvedCall, variableResolvedCall);
245                    }
246                });
247            }
248        };
249    
250        public static class CallForImplicitInvoke extends DelegatingCall {
251            final Call outerCall;
252            final ReceiverValue explicitExtensionReceiver;
253            final ExpressionReceiver calleeExpressionAsThisObject;
254            final JetSimpleNameExpression fakeInvokeExpression;
255    
256            public CallForImplicitInvoke(
257                    @NotNull ReceiverValue explicitExtensionReceiver,
258                    @NotNull ExpressionReceiver calleeExpressionAsThisObject,
259                    @NotNull Call call
260            ) {
261                super(call);
262                this.outerCall = call;
263                this.explicitExtensionReceiver = explicitExtensionReceiver;
264                this.calleeExpressionAsThisObject = calleeExpressionAsThisObject;
265                this.fakeInvokeExpression = (JetSimpleNameExpression) JetPsiFactory.createExpression(call.getCallElement().getProject(), "invoke");
266            }
267    
268            @NotNull
269            @Override
270            public ReceiverValue getExplicitReceiver() {
271                return explicitExtensionReceiver;
272            }
273    
274            @NotNull
275            @Override
276            public ReceiverValue getThisObject() {
277                return calleeExpressionAsThisObject;
278            }
279    
280            @Override
281            public JetExpression getCalleeExpression() {
282                return fakeInvokeExpression;
283            }
284    
285            @NotNull
286            @Override
287            public JetElement getCallElement() {
288                return outerCall.getCallElement();
289            }
290    
291            @NotNull
292            @Override
293            public CallType getCallType() {
294                return CallType.INVOKE;
295            }
296    
297            @NotNull
298            public Call getOuterCall() {
299                return outerCall;
300            }
301        }
302    }