PageRenderTime 125ms CodeModel.GetById 60ms app.highlight 20ms RepoModel.GetById 41ms app.codeStats 0ms

/Microsoft.Scripting/Actions/Calls/MethodCandidate.cs

https://bitbucket.org/stefanrusek/xronos
C# | 307 lines | 216 code | 48 blank | 43 comment | 64 complexity | 70a8bf82b85e3d89b15298c664b9fc34 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Microsoft Public License, please send an email to 
  8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16#if CODEPLEX_40
 17using System;
 18#else
 19using System; using Microsoft;
 20#endif
 21using System.Collections.Generic;
 22using System.Diagnostics;
 23#if CODEPLEX_40
 24using System.Dynamic;
 25#else
 26using Microsoft.Scripting;
 27#endif
 28using System.Text;
 29using Microsoft.Contracts;
 30using Microsoft.Scripting.Actions;
 31using Microsoft.Scripting.Runtime;
 32using Microsoft.Scripting.Generation;
 33
 34namespace Microsoft.Scripting.Actions.Calls {
 35    /// <summary>
 36    /// MethodCandidate represents the different possible ways of calling a method or a set of method overloads.
 37    /// A single method can result in multiple MethodCandidates. Some reasons include:
 38    /// - Every optional parameter or parameter with a default value will result in a candidate
 39    /// - The presence of ref and out parameters will add a candidate for languages which want to return the updated values as return values.
 40    /// - ArgumentKind.List and ArgumentKind.Dictionary can result in a new candidate per invocation since the list might be different every time.
 41    ///
 42    /// Each MethodCandidate represents the parameter type for the candidate using ParameterWrapper.
 43    /// 
 44    /// Contrast this with MethodTarget which represents the real physical invocation of a method
 45    /// </summary>
 46    class MethodCandidate {
 47        private MethodTarget _target;
 48        private List<ParameterWrapper> _parameters;
 49        private NarrowingLevel _narrowingLevel;
 50
 51        internal MethodCandidate(MethodCandidate previous, NarrowingLevel narrowingLevel) {
 52            this._target = previous.Target;
 53            this._parameters = previous._parameters;
 54            _narrowingLevel = narrowingLevel;
 55        }
 56
 57        internal MethodCandidate(MethodTarget target, List<ParameterWrapper> parameters) {
 58            Debug.Assert(target != null);
 59
 60            _target = target;
 61            _parameters = parameters;
 62            _narrowingLevel = NarrowingLevel.None;
 63            parameters.TrimExcess();
 64        }
 65
 66        public MethodTarget Target {
 67            get { return _target; }
 68        }
 69
 70        public NarrowingLevel NarrowingLevel {
 71            get {
 72                return _narrowingLevel;
 73            }
 74        }
 75
 76        [Confined]
 77        public override string ToString() {
 78            return string.Format("MethodCandidate({0})", Target);
 79        }
 80
 81        internal bool IsApplicable(Type[] types, NarrowingLevel narrowingLevel, List<ConversionResult> conversionResults) {
 82            // attempt to convert each parameter
 83            bool res = true;
 84            for (int i = 0; i < types.Length; i++) {
 85                bool success = _parameters[i].HasConversionFrom(types[i], narrowingLevel);
 86
 87                conversionResults.Add(new ConversionResult(types[i], _parameters[i].Type, i, !success));
 88
 89                res &= success;
 90            }
 91
 92            return res;
 93        }
 94
 95        internal bool IsApplicable(DynamicMetaObject[] objects, NarrowingLevel narrowingLevel, List<ConversionResult> conversionResults) {
 96            // attempt to convert each parameter
 97            bool res = true;
 98            for (int i = 0; i < objects.Length; i++) {
 99                /*if (objects[i].NeedsDeferral) {
100                    conversionResults.Add(new ConversionResult(typeof(Dynamic), _parameters[i].Type, i, false));
101                } else*/ {
102                    bool success = _parameters[i].HasConversionFrom(objects[i].GetLimitType(), narrowingLevel);
103
104                    conversionResults.Add(new ConversionResult(objects[i].GetLimitType(), _parameters[i].Type, i, !success));
105
106                    res &= success;
107                }
108            }
109
110            return res;
111        }
112
113        internal static Candidate GetPreferredCandidate(MethodCandidate one, MethodCandidate two, CallTypes callType, Type[] actualTypes) {
114            Candidate cmpParams = ParameterWrapper.GetPreferredParameters(one.Parameters, two.Parameters, actualTypes);
115            if (cmpParams.Chosen()) {
116                return cmpParams;
117            }
118
119            Candidate ret = MethodTarget.CompareEquivalentParameters(one.Target, two.Target);
120            if (ret.Chosen()) {
121                return ret;
122            }
123
124            if (CompilerHelpers.IsStatic(one.Target.Method) && !CompilerHelpers.IsStatic(two.Target.Method)) {
125                return callType == CallTypes.ImplicitInstance ? Candidate.Two : Candidate.One;
126            } else if (!CompilerHelpers.IsStatic(one.Target.Method) && CompilerHelpers.IsStatic(two.Target.Method)) {
127                return callType == CallTypes.ImplicitInstance ? Candidate.One : Candidate.Two;
128            }
129
130            return Candidate.Equivalent;
131        }
132
133        internal static Candidate GetPreferredCandidate(MethodCandidate one, MethodCandidate two, CallTypes callType, DynamicMetaObject[] actualTypes) {
134            Candidate cmpParams = ParameterWrapper.GetPreferredParameters(one.Parameters, two.Parameters, actualTypes);
135            if (cmpParams.Chosen()) {
136                return cmpParams;
137            }
138
139            Candidate ret = MethodTarget.CompareEquivalentParameters(one.Target, two.Target);
140            if (ret.Chosen()) {
141                return ret;
142            }
143
144            if (CompilerHelpers.IsStatic(one.Target.Method) && !CompilerHelpers.IsStatic(two.Target.Method)) {
145                return callType == CallTypes.ImplicitInstance ? Candidate.Two : Candidate.One;
146            } else if (!CompilerHelpers.IsStatic(one.Target.Method) && CompilerHelpers.IsStatic(two.Target.Method)) {
147                return callType == CallTypes.ImplicitInstance ? Candidate.One : Candidate.Two;
148            }
149
150            return Candidate.Equivalent;
151        }
152
153
154        /// <summary>
155        /// Builds a new MethodCandidate which takes count arguments and the provided list of keyword arguments.
156        /// 
157        /// The basic idea here is to figure out which parameters map to params or a dictionary params and
158        /// fill in those spots w/ extra ParameterWrapper's.  
159        /// </summary>
160        internal MethodCandidate MakeParamsExtended(ActionBinder binder, int count, string[] names) {
161            Debug.Assert(BinderHelpers.IsParamsMethod(_target.Method));
162
163            List<ParameterWrapper> newParameters = new List<ParameterWrapper>(count);
164            
165            // keep track of which kw args map to a real argument, and which ones
166            // map to the params dictionary.
167            List<string> unusedNames = new List<string>(names);
168            List<int> unusedNameIndexes = new List<int>();
169            for (int i = 0; i < unusedNames.Count; i++) {
170                unusedNameIndexes.Add(i);
171            }
172
173            // if we don't have a param array we'll have a param dict which is type object
174            ParameterWrapper paramsArrayParameter = null, paramsDictParameter = null;
175            int paramsArrayIndex = -1, paramsDictIndex = -1;
176
177            for (int i = 0; i < _parameters.Count; i++) {
178                ParameterWrapper parameter = _parameters[i];
179
180                if (parameter.IsParamsDict) {
181                    paramsDictParameter = parameter;
182                    paramsDictIndex = i;
183                } else if (parameter.IsParamsArray) {
184                    paramsArrayParameter = parameter;
185                    paramsArrayIndex = i;
186                } else {
187                    int j = unusedNames.IndexOf(parameter.Name);
188                    if (j != -1) {
189                        unusedNames.RemoveAt(j);
190                        unusedNameIndexes.RemoveAt(j);
191                    }
192                    newParameters.Add(parameter);
193                }
194            }
195
196            if (paramsArrayIndex != -1) {
197                Type elementType = paramsArrayParameter.Type.GetElementType();
198                bool nonNullItems = CompilerHelpers.ProhibitsNullItems(paramsArrayParameter.ParameterInfo);
199
200                while (newParameters.Count < (count - unusedNames.Count)) {
201                    ParameterWrapper param = new ParameterWrapper(binder, paramsArrayParameter.ParameterInfo, elementType, null, nonNullItems, false, false);
202                    newParameters.Insert(System.Math.Min(paramsArrayIndex, newParameters.Count), param);
203                }
204            }
205
206            if (paramsDictIndex != -1) {
207                Type elementType = paramsDictParameter.Type.GetElementType();
208                bool nonNullItems = CompilerHelpers.ProhibitsNullItems(paramsDictParameter.ParameterInfo);
209                
210                foreach (string si in unusedNames) {
211                    newParameters.Add(new ParameterWrapper(binder, paramsDictParameter.ParameterInfo, typeof(object), si, nonNullItems, false, false));
212                }
213            } else if (unusedNames.Count != 0) {
214                // unbound kw args and no where to put them, can't call...
215                // TODO: We could do better here because this results in an incorrect arg # error message.
216                return null;
217            }
218
219            // if we have too many or too few args we also can't call
220            if (count != newParameters.Count) {
221                return null;
222            }
223
224            return new MethodCandidate(_target.MakeParamsExtended(count, unusedNames.ToArray(), unusedNameIndexes.ToArray()), newParameters);
225        }
226
227        internal string ToSignatureString(string name, CallTypes callType) {
228            StringBuilder buf = new StringBuilder(name);
229            buf.Append("(");
230            bool isFirstArg = true;
231            int i = 0;
232            if (callType == CallTypes.ImplicitInstance) i = 1;
233            for (; i < _parameters.Count; i++) {
234                if (isFirstArg) isFirstArg = false;
235                else buf.Append(", ");
236                buf.Append(_parameters[i].ToSignatureString());
237            }
238            buf.Append(")");
239            return buf.ToString(); //@todo add helper info for more interesting signatures
240        }
241
242        internal IList<ParameterWrapper> Parameters {
243            get {
244                return _parameters;
245            }
246        }
247
248        internal bool HasParamsDictionary() {
249            foreach (ParameterWrapper pw in _parameters) {
250                // can't bind to methods that are params dictionaries, only to their extended forms.
251                if (pw.IsParamsDict) return true;
252            }
253            return false;
254        }
255
256        internal bool TryGetNormalizedArguments<T>(T[] argTypes, string[] names, out T[] args, out CallFailure failure) {
257            if (names.Length == 0) {
258                // no named arguments, success!
259                args = argTypes;
260                failure = null;
261                return true;
262            }
263
264            T[] res = new T[argTypes.Length];
265            Array.Copy(argTypes, res, argTypes.Length - names.Length);
266            List<string> unboundNames = null;
267            List<string> duppedNames = null;
268
269            for (int i = 0; i < names.Length; i++) {
270                bool found = false;
271                for (int j = 0; j < _parameters.Count; j++) {
272                    if (_parameters[j].Name == names[i]) {
273                        found = true;
274
275                        if (res[j] != null) {
276                            if (duppedNames == null) duppedNames = new List<string>();
277                            duppedNames.Add(names[i]);
278                        } else {
279                            res[j] = argTypes[i + argTypes.Length - names.Length];
280                        }
281                        break;
282                    }
283                }
284
285                if (!found) {
286                    if (unboundNames == null) unboundNames = new List<string>();
287                    unboundNames.Add(names[i]);
288                }
289            }
290
291            if (unboundNames != null) {
292                failure = new CallFailure(Target, unboundNames.ToArray(), true);
293                args = null;
294                return false;
295            } else if (duppedNames != null) {
296                failure = new CallFailure(Target, duppedNames.ToArray(), false);
297                args = null;
298                return false;
299            }
300
301            failure = null;
302            args = res;
303            return true;
304        }
305    }
306
307}