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.model;
018    
019    import com.google.common.collect.Maps;
020    import com.intellij.util.Function;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
024    import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
025    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
026    import org.jetbrains.jet.lang.psi.Call;
027    import org.jetbrains.jet.lang.psi.ValueArgument;
028    import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace;
029    import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
030    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
031    import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus;
032    import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind;
033    import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate;
034    import org.jetbrains.jet.lang.resolve.calls.tasks.TracingStrategy;
035    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
036    import org.jetbrains.jet.lang.types.JetType;
037    import org.jetbrains.jet.lang.types.TypeProjection;
038    import org.jetbrains.jet.lang.types.TypeSubstitutor;
039    
040    import java.util.*;
041    
042    import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.INCOMPLETE_TYPE_INFERENCE;
043    import static org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus.UNKNOWN_STATUS;
044    
045    public class ResolvedCallImpl<D extends CallableDescriptor> implements MutableResolvedCall<D> {
046    
047        public static final Function<MutableResolvedCall<?>, CallableDescriptor> MAP_TO_CANDIDATE = new Function<MutableResolvedCall<?>, CallableDescriptor>() {
048            @Override
049            public CallableDescriptor fun(MutableResolvedCall<?> resolvedCall) {
050                return resolvedCall.getCandidateDescriptor();
051            }
052        };
053    
054        public static final Function<MutableResolvedCall<?>, CallableDescriptor> MAP_TO_RESULT = new Function<MutableResolvedCall<?>, CallableDescriptor>() {
055            @Override
056            public CallableDescriptor fun(MutableResolvedCall<?> resolvedCall) {
057                return resolvedCall.getResultingDescriptor();
058            }
059        };
060    
061        @NotNull
062        public static <D extends CallableDescriptor> ResolvedCallImpl<D> create(
063                @NotNull ResolutionCandidate<D> candidate,
064                @NotNull DelegatingBindingTrace trace,
065                @NotNull TracingStrategy tracing,
066                @NotNull MutableDataFlowInfoForArguments dataFlowInfoForArguments
067        ) {
068            return new ResolvedCallImpl<D>(candidate, trace, tracing, dataFlowInfoForArguments);
069        }
070    
071        private final Call call;
072        private final D candidateDescriptor;
073        private D resultingDescriptor; // Probably substituted
074        private final ReceiverValue dispatchReceiver; // receiver object of a method
075        private final ReceiverValue extensionReceiver; // receiver of an extension function
076        private final ExplicitReceiverKind explicitReceiverKind;
077        private final boolean isSafeCall;
078    
079        private final Map<TypeParameterDescriptor, JetType> typeArguments = Maps.newLinkedHashMap();
080        private final Map<ValueParameterDescriptor, ResolvedValueArgument> valueArguments = Maps.newLinkedHashMap();
081        private final MutableDataFlowInfoForArguments dataFlowInfoForArguments;
082        private final Map<ValueArgument, ArgumentMatchImpl> argumentToParameterMap = Maps.newHashMap();
083    
084        private DelegatingBindingTrace trace;
085        private TracingStrategy tracing;
086        private ResolutionStatus status = UNKNOWN_STATUS;
087        private ConstraintSystem constraintSystem = null;
088        private Boolean hasInferredReturnType = null;
089        private boolean completed = false;
090    
091        private ResolvedCallImpl(
092                @NotNull ResolutionCandidate<D> candidate,
093                @NotNull DelegatingBindingTrace trace,
094                @NotNull TracingStrategy tracing,
095                @NotNull MutableDataFlowInfoForArguments dataFlowInfoForArguments
096        ) {
097            this.call = candidate.getCall();
098            this.candidateDescriptor = candidate.getDescriptor();
099            this.dispatchReceiver = candidate.getDispatchReceiver();
100            this.extensionReceiver = candidate.getExtensionReceiver();
101            this.explicitReceiverKind = candidate.getExplicitReceiverKind();
102            this.isSafeCall = candidate.isSafeCall();
103            this.trace = trace;
104            this.tracing = tracing;
105            this.dataFlowInfoForArguments = dataFlowInfoForArguments;
106        }
107    
108        @Override
109        @NotNull
110        public ResolutionStatus getStatus() {
111            return status;
112        }
113    
114        @Override
115        public void addStatus(@NotNull ResolutionStatus status) {
116            this.status = this.status.combine(status);
117        }
118    
119        @Override
120        public void setStatusToSuccess() {
121            assert status == INCOMPLETE_TYPE_INFERENCE || status == UNKNOWN_STATUS;
122            status = ResolutionStatus.SUCCESS;
123        }
124    
125        @Override
126        @NotNull
127        public DelegatingBindingTrace getTrace() {
128            assertNotCompleted("Trace");
129            return trace;
130        }
131    
132        @NotNull
133        public TracingStrategy getTracing() {
134            assertNotCompleted("TracingStrategy");
135            return tracing;
136        }
137    
138        @NotNull
139        @Override
140        public Call getCall() {
141            return call;
142        }
143    
144        @Override
145        @NotNull
146        public D getCandidateDescriptor() {
147            return candidateDescriptor;
148        }
149    
150        @Override
151        @NotNull
152        public D getResultingDescriptor() {
153            return resultingDescriptor == null ? candidateDescriptor : resultingDescriptor;
154        }
155    
156        @Override
157        public void setResultingSubstitutor(@NotNull TypeSubstitutor substitutor) {
158            resultingDescriptor = (D) candidateDescriptor.substitute(substitutor);
159            assert resultingDescriptor != null : candidateDescriptor;
160    
161            for (TypeParameterDescriptor typeParameter : candidateDescriptor.getTypeParameters()) {
162                TypeProjection typeArgumentProjection = substitutor.getSubstitution().get(typeParameter.getTypeConstructor());
163                if (typeArgumentProjection != null) {
164                    typeArguments.put(typeParameter, typeArgumentProjection.getType());
165                }
166            }
167    
168            Map<ValueParameterDescriptor, ValueParameterDescriptor> substitutedParametersMap = Maps.newHashMap();
169            for (ValueParameterDescriptor valueParameterDescriptor : resultingDescriptor.getValueParameters()) {
170                substitutedParametersMap.put(valueParameterDescriptor.getOriginal(), valueParameterDescriptor);
171            }
172    
173            Map<ValueParameterDescriptor, ResolvedValueArgument> originalValueArguments = Maps.newLinkedHashMap(valueArguments);
174            valueArguments.clear();
175            for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : originalValueArguments.entrySet()) {
176                ValueParameterDescriptor substitutedVersion = substitutedParametersMap.get(entry.getKey().getOriginal());
177                assert substitutedVersion != null : entry.getKey();
178                valueArguments.put(substitutedVersion, entry.getValue());
179            }
180    
181            Map<ValueArgument, ArgumentMatchImpl> originalArgumentToParameterMap = Maps.newLinkedHashMap(argumentToParameterMap);
182            argumentToParameterMap.clear();
183            for (Map.Entry<ValueArgument, ArgumentMatchImpl> entry : originalArgumentToParameterMap.entrySet()) {
184                ArgumentMatchImpl argumentMatch = entry.getValue();
185                ValueParameterDescriptor valueParameterDescriptor = argumentMatch.getValueParameter();
186                ValueParameterDescriptor substitutedVersion = substitutedParametersMap.get(valueParameterDescriptor.getOriginal());
187                assert substitutedVersion != null : valueParameterDescriptor;
188                argumentToParameterMap.put(entry.getKey(), argumentMatch.replaceValueParameter(substitutedVersion));
189            }
190        }
191    
192        @Override
193        public void setConstraintSystem(@NotNull ConstraintSystem constraintSystem) {
194            this.constraintSystem = constraintSystem;
195        }
196    
197        @Nullable
198        @Override
199        public ConstraintSystem getConstraintSystem() {
200            assertNotCompleted("ConstraintSystem");
201            return constraintSystem;
202        }
203    
204        @Override
205        public void recordValueArgument(@NotNull ValueParameterDescriptor valueParameter, @NotNull ResolvedValueArgument valueArgument) {
206            assert !valueArguments.containsKey(valueParameter) : valueParameter + " -> " + valueArgument;
207            valueArguments.put(valueParameter, valueArgument);
208            for (ValueArgument argument : valueArgument.getArguments()) {
209                argumentToParameterMap.put(argument, new ArgumentMatchImpl(valueParameter));
210            }
211        }
212    
213        @Override
214        @NotNull
215        public ReceiverValue getExtensionReceiver() {
216            return extensionReceiver;
217        }
218    
219        @Override
220        @NotNull
221        public ReceiverValue getDispatchReceiver() {
222            return dispatchReceiver;
223        }
224    
225        @Override
226        @NotNull
227        public ExplicitReceiverKind getExplicitReceiverKind() {
228            return explicitReceiverKind;
229        }
230    
231        @Override
232        @NotNull
233        public Map<ValueParameterDescriptor, ResolvedValueArgument> getValueArguments() {
234            return valueArguments;
235        }
236    
237        @Nullable
238        @Override
239        public List<ResolvedValueArgument> getValueArgumentsByIndex() {
240            List<ResolvedValueArgument> arguments = new ArrayList<ResolvedValueArgument>(candidateDescriptor.getValueParameters().size());
241            for (int i = 0; i < candidateDescriptor.getValueParameters().size(); ++i) {
242                arguments.add(null);
243            }
244            
245            for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : valueArguments.entrySet()) {
246                ValueParameterDescriptor parameterDescriptor = entry.getKey();
247                ResolvedValueArgument value = entry.getValue();
248                ResolvedValueArgument oldValue = arguments.set(parameterDescriptor.getIndex(), value);
249                if (oldValue != null) {
250                    return null;
251                }
252            }
253    
254            for (int i = 0; i < arguments.size(); i++) {
255                Object o = arguments.get(i);
256                if (o == null) {
257                    return null;
258                }
259            }
260            
261            return arguments;
262        }
263    
264        @Override
265        public void recordArgumentMatchStatus(@NotNull ValueArgument valueArgument, @NotNull ArgumentMatchStatus matchStatus) {
266            ArgumentMatchImpl argumentMatch = argumentToParameterMap.get(valueArgument);
267            argumentMatch.recordMatchStatus(matchStatus);
268        }
269    
270        @NotNull
271        @Override
272        public ArgumentMapping getArgumentMapping(@NotNull ValueArgument valueArgument) {
273            ArgumentMatch argumentMatch = argumentToParameterMap.get(valueArgument);
274            if (argumentMatch == null) {
275                return ArgumentUnmapped.INSTANCE$;
276            }
277            return argumentMatch;
278        }
279    
280        @NotNull
281        @Override
282        public Map<TypeParameterDescriptor, JetType> getTypeArguments() {
283            return typeArguments;
284        }
285    
286        @Override
287        public boolean isSafeCall() {
288            return isSafeCall;
289        }
290    
291        @NotNull
292        @Override
293        public MutableDataFlowInfoForArguments getDataFlowInfoForArguments() {
294            return dataFlowInfoForArguments;
295        }
296    
297        @Override
298        public boolean hasInferredReturnType() {
299            if (!completed) {
300                hasInferredReturnType = constraintSystem == null || CallResolverUtil.hasInferredReturnType(candidateDescriptor, constraintSystem);
301            }
302            assert hasInferredReturnType != null : "The property 'hasInferredReturnType' was not set when the call was completed.";
303            return hasInferredReturnType;
304        }
305    
306        @Override
307        public void markCallAsCompleted() {
308            if (!completed) {
309                hasInferredReturnType();
310            }
311            trace = null;
312            constraintSystem = null;
313            tracing = null;
314            completed = true;
315        }
316    
317        @Override
318        public boolean isCompleted() {
319            return completed;
320        }
321    
322        private void assertNotCompleted(String elementName) {
323            assert !completed: elementName + " is erased after resolution completion.";
324        }
325    }