PageRenderTime 219ms CodeModel.GetById 80ms app.highlight 88ms RepoModel.GetById 42ms app.codeStats 1ms

/Microsoft.Scripting/Actions/DefaultBinder.Operations.cs

https://bitbucket.org/stefanrusek/xronos
C# | 720 lines | 561 code | 106 blank | 53 comment | 94 complexity | a331eaf1de71e6135755ccfccb0cd135 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.Linq.Expressions;
 25#else
 26using Microsoft.Linq.Expressions;
 27#endif
 28using System.Reflection;
 29#if CODEPLEX_40
 30using System.Dynamic;
 31#else
 32using Microsoft.Scripting;
 33#endif
 34using System.Text;
 35using Microsoft.Scripting.Generation;
 36using Microsoft.Scripting.Runtime;
 37using Microsoft.Scripting.Utils;
 38using Microsoft.Scripting.Actions.Calls;
 39using AstUtils = Microsoft.Scripting.Ast.Utils;
 40
 41namespace Microsoft.Scripting.Actions {
 42#if CODEPLEX_40
 43    using Ast = System.Linq.Expressions.Expression;
 44#else
 45    using Ast = Microsoft.Linq.Expressions.Expression;
 46#endif
 47
 48    public partial class DefaultBinder : ActionBinder {
 49        public DynamicMetaObject DoOperation(string operation, params DynamicMetaObject[] args) {
 50            return DoOperation(operation, AstUtils.Constant(null, typeof(CodeContext)), args);
 51        }
 52
 53        public DynamicMetaObject DoOperation(string operation, Expression codeContext, params DynamicMetaObject[] args) {
 54            ContractUtils.RequiresNotNull(operation, "operation");
 55            ContractUtils.RequiresNotNull(codeContext, "codeContext");
 56            ContractUtils.RequiresNotNullItems(args, "args");
 57
 58            return MakeGeneralOperatorRule(operation, codeContext, args);   // Then try comparison / other ExpressionType
 59        }
 60
 61        private enum IndexType {
 62            Get,
 63            Set
 64        }
 65
 66        /// <summary>
 67        /// Creates the MetaObject for indexing directly into arrays or indexing into objects which have
 68        /// default members.  Returns null if we're not an indexing operation.
 69        /// </summary>
 70        public DynamicMetaObject GetIndex(DynamicMetaObject[] args) {
 71            if (args[0].GetLimitType().IsArray) {
 72                return MakeArrayIndexRule(IndexType.Get, args);
 73            }
 74
 75            return MakeMethodIndexRule(IndexType.Get, args);
 76        }
 77
 78        /// <summary>
 79        /// Creates the MetaObject for indexing directly into arrays or indexing into objects which have
 80        /// default members.  Returns null if we're not an indexing operation.
 81        /// </summary>
 82        public DynamicMetaObject SetIndex(DynamicMetaObject[] args) {
 83            if (args[0].LimitType.IsArray) {
 84                return MakeArrayIndexRule(IndexType.Set, args);
 85            }
 86
 87            return MakeMethodIndexRule(IndexType.Set, args);
 88        }
 89
 90        public DynamicMetaObject GetDocumentation(DynamicMetaObject target) {
 91            BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType);
 92
 93            object[] attrs = target.LimitType.GetCustomAttributes(typeof(DocumentationAttribute), true);
 94            string documentation = String.Empty;
 95
 96            if (attrs.Length > 0) {
 97                documentation = ((DocumentationAttribute)attrs[0]).Documentation;
 98            }
 99
100            return new DynamicMetaObject(
101                AstUtils.Constant(documentation),
102                restrictions
103            );
104        }
105
106        public DynamicMetaObject GetMemberNames(DynamicMetaObject target, Expression codeContext) {
107            BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType);
108
109            if (typeof(IMembersList).IsAssignableFrom(target.LimitType)) {
110                return MakeIMembersListRule(codeContext, target);
111            }
112
113            MemberInfo[] members = target.LimitType.GetMembers();
114            Dictionary<string, string> mems = new Dictionary<string, string>();
115            foreach (MemberInfo mi in members) {
116                mems[mi.Name] = mi.Name;
117            }
118
119            string[] res = new string[mems.Count];
120            mems.Keys.CopyTo(res, 0);
121
122            return new DynamicMetaObject(
123                AstUtils.Constant(res),
124                restrictions
125            );
126        }
127
128        public DynamicMetaObject GetCallSignatures(DynamicMetaObject target) {
129            return MakeCallSignatureResult(CompilerHelpers.GetMethodTargets(target.LimitType), target);
130        }
131
132        public DynamicMetaObject GetIsCallable(DynamicMetaObject target) {
133            // IsCallable() is tightly tied to Call actions. So in general, we need the call-action providers to also
134            // provide IsCallable() status. 
135            // This is just a rough fallback. We could also attempt to simulate the default CallBinder logic to see
136            // if there are any applicable calls targets, but that would be complex (the callbinder wants the argument list, 
137            // which we don't have here), and still not correct. 
138            BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType);
139
140            bool callable = false;
141            if (typeof(Delegate).IsAssignableFrom(target.LimitType) ||
142                typeof(MethodGroup).IsAssignableFrom(target.LimitType)) {
143                callable = true;
144            }
145
146            return new DynamicMetaObject(
147                AstUtils.Constant(callable),
148                restrictions
149            );
150        }
151
152        /// <summary>
153        /// Creates the meta object for the rest of the operations: comparisons and all other
154        /// ExpressionType.  If the operation cannot be completed a MetaObject which indicates an
155        /// error will be returned.
156        /// </summary>
157        private DynamicMetaObject MakeGeneralOperatorRule(string operation, Expression codeContext, DynamicMetaObject[] args) {
158            OperatorInfo info = OperatorInfo.GetOperatorInfo(operation);
159            DynamicMetaObject res;
160
161            if (CompilerHelpers.IsComparisonOperator(info.Operator)) {
162                res = MakeComparisonRule(info, codeContext, args);
163            } else {
164                res = MakeOperatorRule(info, codeContext, args);
165            }
166
167            return res;
168        }
169
170        #region Comparison operator
171
172        private DynamicMetaObject MakeComparisonRule(OperatorInfo info, Expression codeContext, DynamicMetaObject[] args) {
173            return
174                TryComparisonMethod(info, codeContext, args[0], args) ??   // check the first type if it has an applicable method
175                TryComparisonMethod(info, codeContext, args[0], args) ??   // then check the second type
176                TryNumericComparison(info, args) ??           // try Compare: cmp(x,y) (>, <, >=, <=, ==, !=) 0
177                TryInvertedComparison(info, args[0], args) ?? // try inverting the operator & result (e.g. if looking for Equals try NotEquals, LessThan for GreaterThan)...
178                TryInvertedComparison(info, args[0], args) ?? // inverted binding on the 2nd type
179                TryNullComparisonRule(args) ??                // see if we're comparing to null w/ an object ref or a Nullable<T>
180                TryPrimitiveCompare(info, args) ??            // see if this is a primitive type where we're comparing the two values.
181                MakeOperatorError(info, args);                // no comparisons are possible            
182        }
183
184        private DynamicMetaObject TryComparisonMethod(OperatorInfo info, Expression codeContext, DynamicMetaObject target, DynamicMetaObject[] args) {
185            MethodInfo[] targets = GetApplicableMembers(target.GetLimitType(), info);
186            if (targets.Length > 0) {
187                return TryMakeBindingTarget(targets, args, codeContext, BindingRestrictions.Empty);
188            }
189
190            return null;
191        }
192
193        private static DynamicMetaObject MakeOperatorError(OperatorInfo info, DynamicMetaObject[] args) {
194            return new DynamicMetaObject(
195                Ast.Throw(
196                    AstUtils.ComplexCallHelper(
197                        typeof(BinderOps).GetMethod("BadArgumentsForOperation"),
198                        ArrayUtils.Insert((Expression)AstUtils.Constant(info.Operator), DynamicUtils.GetExpressions(args))
199                    )
200                ),
201                BindingRestrictions.Combine(args)
202            );
203        }
204
205        private DynamicMetaObject TryNumericComparison(OperatorInfo info, DynamicMetaObject[] args) {
206            MethodInfo[] targets = FilterNonMethods(
207                args[0].GetLimitType(),
208                GetMember(OldDoOperationAction.Make(this, OperatorInfo.ExpressionTypeToOperator(info.Operator)),
209                args[0].GetLimitType(),
210                "Compare")
211            );
212
213            if (targets.Length > 0) {
214                MethodBinder mb = MethodBinder.MakeBinder(this, targets[0].Name, targets);
215                BindingTarget target = mb.MakeBindingTarget(CallTypes.None, args);
216                if (target.Success) {
217                    Expression call = AstUtils.Convert(target.MakeExpression(), typeof(int));
218                    switch (info.Operator) {
219                        case ExpressionType.GreaterThan: call = Ast.GreaterThan(call, AstUtils.Constant(0)); break;
220                        case ExpressionType.LessThan: call = Ast.LessThan(call, AstUtils.Constant(0)); break;
221                        case ExpressionType.GreaterThanOrEqual: call = Ast.GreaterThanOrEqual(call, AstUtils.Constant(0)); break;
222                        case ExpressionType.LessThanOrEqual: call = Ast.LessThanOrEqual(call, AstUtils.Constant(0)); break;
223                        case ExpressionType.Equal: call = Ast.Equal(call, AstUtils.Constant(0)); break;
224                        case ExpressionType.NotEqual: call = Ast.NotEqual(call, AstUtils.Constant(0)); break;
225                    }
226
227                    return new DynamicMetaObject(
228                        call,
229                        BindingRestrictions.Combine(target.RestrictedArguments.Objects)
230                    );
231                }
232            }
233
234            return null;
235        }
236
237        private DynamicMetaObject TryInvertedComparison(OperatorInfo info, DynamicMetaObject target, DynamicMetaObject[] args) {
238            ExpressionType revOp = GetInvertedOperator(info.Operator);
239            OperatorInfo revInfo = OperatorInfo.GetOperatorInfo(revOp);
240            Debug.Assert(revInfo != null);
241
242            // try the 1st type's opposite function result negated 
243            MethodBase[] targets = GetApplicableMembers(target.GetLimitType(), revInfo);
244            if (targets.Length > 0) {
245                return TryMakeInvertedBindingTarget(targets, args);
246            }
247
248            return null;
249        }
250
251        /// <summary>
252        /// Produces a rule for comparing a value to null - supports comparing object references and nullable types.
253        /// </summary>
254        private static DynamicMetaObject TryNullComparisonRule(DynamicMetaObject[] args) {
255            Type otherType = args[1].GetLimitType();
256
257            BindingRestrictions restrictions = BindingRestrictionsHelpers.GetRuntimeTypeRestriction(args[0].Expression, args[0].GetLimitType()).Merge(BindingRestrictions.Combine(args));
258
259            if (args[0].GetLimitType() == typeof(DynamicNull)) {
260                if (!otherType.IsValueType) {
261                    return new DynamicMetaObject(
262                        Ast.Equal(args[0].Expression, AstUtils.Constant(null)),
263                        restrictions
264                    );
265                } else if (otherType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
266                    return new DynamicMetaObject(
267                            Ast.Property(args[0].Expression, otherType.GetProperty("HasValue")),
268                        restrictions
269                    );
270                }
271            } else if (otherType == typeof(DynamicNull)) {
272                if (!args[0].GetLimitType().IsValueType) {
273                    return new DynamicMetaObject(
274                        Ast.Equal(args[0].Expression, AstUtils.Constant(null)),
275                        restrictions
276                    );
277                } else if (args[0].GetLimitType().GetGenericTypeDefinition() == typeof(Nullable<>)) {
278                    return new DynamicMetaObject(
279                        Ast.Property(args[0].Expression, otherType.GetProperty("HasValue")),
280                        restrictions
281                    );
282                }
283            }
284            return null;
285        }
286
287        private static DynamicMetaObject TryPrimitiveCompare(OperatorInfo info, DynamicMetaObject[] args) {
288            if (TypeUtils.GetNonNullableType(args[0].GetLimitType()) == TypeUtils.GetNonNullableType(args[1].GetLimitType()) &&
289                TypeUtils.IsNumeric(args[0].GetLimitType())) {
290                Expression arg0 = args[0].Expression;
291                Expression arg1 = args[1].Expression;
292
293                // TODO: Nullable<PrimitveType> Support
294                Expression expr;
295                switch (info.Operator) {
296                    case ExpressionType.Equal: expr = Ast.Equal(arg0, arg1); break;
297                    case ExpressionType.NotEqual: expr = Ast.NotEqual(arg0, arg1); break;
298                    case ExpressionType.GreaterThan: expr = Ast.GreaterThan(arg0, arg1); break;
299                    case ExpressionType.LessThan: expr = Ast.LessThan(arg0, arg1); break;
300                    case ExpressionType.GreaterThanOrEqual: expr = Ast.GreaterThanOrEqual(arg0, arg1); break;
301                    case ExpressionType.LessThanOrEqual: expr = Ast.LessThanOrEqual(arg0, arg1); break;
302                    default: throw new InvalidOperationException();
303                }
304
305                return new DynamicMetaObject(
306                    expr,
307                    BindingRestrictionsHelpers.GetRuntimeTypeRestriction(arg0, args[0].GetLimitType()).Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(arg1, args[0].GetLimitType())).Merge(BindingRestrictions.Combine(args))
308                );
309            }
310
311            return null;
312        }
313
314        #endregion
315
316        #region Operator Rule
317
318        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] // TODO: fix
319        private DynamicMetaObject MakeOperatorRule(OperatorInfo info, Expression codeContext, DynamicMetaObject[] args) {
320            return
321                TryForwardOperator(info, codeContext, args) ??
322                TryReverseOperator(info, codeContext, args) ??
323                TryPrimitiveOperator(info, args) ??
324                TryMakeDefaultUnaryRule(info, codeContext, args) ??
325                MakeOperatorError(info, args);
326        }
327
328        private static DynamicMetaObject TryPrimitiveOperator(OperatorInfo info, DynamicMetaObject[] args) {
329            if (args.Length == 2 &&
330                TypeUtils.GetNonNullableType(args[0].GetLimitType()) == TypeUtils.GetNonNullableType(args[1].GetLimitType()) &&
331                TypeUtils.IsArithmetic(args[0].GetLimitType())) {
332                // TODO: Nullable<PrimitveType> Support
333                Expression expr;
334                DynamicMetaObject self = args[0].Restrict(args[0].GetLimitType());
335                DynamicMetaObject arg0 = args[1].Restrict(args[0].GetLimitType());
336
337                switch (info.Operator) {
338                    case ExpressionType.Add: expr = Ast.Add(self.Expression, arg0.Expression); break;
339                    case ExpressionType.Subtract: expr = Ast.Subtract(self.Expression, arg0.Expression); break;
340                    case ExpressionType.Divide: expr = Ast.Divide(self.Expression, arg0.Expression); break;
341                    case ExpressionType.Modulo: expr = Ast.Modulo(self.Expression, arg0.Expression); break;
342                    case ExpressionType.Multiply: expr = Ast.Multiply(self.Expression, arg0.Expression); break;
343                    case ExpressionType.LeftShift: expr = Ast.LeftShift(self.Expression, arg0.Expression); break;
344                    case ExpressionType.RightShift: expr = Ast.RightShift(self.Expression, arg0.Expression); break;
345                    case ExpressionType.And: expr = Ast.And(self.Expression, arg0.Expression); break;
346                    case ExpressionType.Or: expr = Ast.Or(self.Expression, arg0.Expression); break;
347                    case ExpressionType.ExclusiveOr: expr = Ast.ExclusiveOr(self.Expression, arg0.Expression); break;
348                    default: throw new InvalidOperationException();
349                }
350
351                return new DynamicMetaObject(
352                    expr,
353                    self.Restrictions.Merge(arg0.Restrictions)
354                );
355            }
356
357            return null;
358        }
359
360        private DynamicMetaObject TryForwardOperator(OperatorInfo info, Expression codeContext, DynamicMetaObject[] args) {
361            MethodInfo[] targets = GetApplicableMembers(args[0].GetLimitType(), info);
362            BindingRestrictions restrictions = BindingRestrictions.Empty;
363
364            if (targets.Length > 0) {
365                return TryMakeBindingTarget(targets, args, codeContext, restrictions);
366            }
367
368            return null;
369        }
370
371        private DynamicMetaObject TryReverseOperator(OperatorInfo info, Expression codeContext, DynamicMetaObject[] args) {
372            // we need a special conversion for the return type on MemberNames
373            if (args.Length > 0) {
374                MethodInfo[] targets = GetApplicableMembers(args[0].LimitType, info);
375                if (targets.Length > 0) {
376                    return TryMakeBindingTarget(targets, args, codeContext, BindingRestrictions.Empty);
377                }
378            }
379
380            return null;
381        }
382
383        private static DynamicMetaObject TryMakeDefaultUnaryRule(OperatorInfo info, Expression codeContext, DynamicMetaObject[] args) {
384            if (args.Length == 1) {
385                BindingRestrictions restrictions = BindingRestrictionsHelpers.GetRuntimeTypeRestriction(args[0].Expression, args[0].GetLimitType()).Merge(BindingRestrictions.Combine(args));
386                switch (info.Operator) {
387                    case ExpressionType.IsTrue:
388                        if (args[0].GetLimitType() == typeof(bool)) {
389                            return args[0];
390                        }
391                        break;
392                    case ExpressionType.Negate:
393                        if (TypeUtils.IsArithmetic(args[0].GetLimitType())) {
394                            return new DynamicMetaObject(
395                                Ast.Negate(args[0].Expression),
396                                restrictions
397                            );
398                        }
399                        break;
400                    case ExpressionType.Not:
401                        if (TypeUtils.IsIntegerOrBool(args[0].GetLimitType())) {
402                            return new DynamicMetaObject(
403                                Ast.Not(args[0].Expression),
404                                restrictions
405                            );
406                        }
407                        break;
408                }
409            }
410            return null;
411        }
412
413        private static DynamicMetaObject MakeIMembersListRule(Expression codeContext, DynamicMetaObject target) {
414            return new DynamicMetaObject(
415                Ast.Call(
416                    typeof(BinderOps).GetMethod("GetStringMembers"),
417                    Ast.Call(
418                        AstUtils.Convert(target.Expression, typeof(IMembersList)),
419                        typeof(IMembersList).GetMethod("GetMemberNames"),
420                        codeContext
421                    )
422                ),
423                BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target.Expression, target.GetLimitType()).Merge(target.Restrictions)
424            );
425        }
426
427        private static DynamicMetaObject MakeCallSignatureResult(MethodBase[] methods, DynamicMetaObject target) {
428            List<string> arrres = new List<string>();
429
430            if (methods != null) {
431                foreach (MethodBase mb in methods) {
432                    StringBuilder res = new StringBuilder();
433                    string comma = "";
434                    foreach (ParameterInfo param in mb.GetParameters()) {
435                        if (param.ParameterType == typeof(CodeContext)) continue;
436
437                        res.Append(comma);
438                        res.Append(param.ParameterType.Name);
439                        res.Append(" ");
440                        res.Append(param.Name);
441                        comma = ", ";
442                    }
443                    arrres.Add(res.ToString());
444                }
445            }
446
447            return new DynamicMetaObject(
448                AstUtils.Constant(arrres.ToArray()),
449                BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target.Expression, target.GetLimitType()).Merge(target.Restrictions)
450            );
451        }
452
453        #endregion
454
455        #region Indexer Rule
456
457        private static Type GetArgType(DynamicMetaObject[] args, int index) {
458            return args[index].HasValue ? args[index].GetLimitType() : args[index].Expression.Type;
459        }
460
461        private DynamicMetaObject MakeMethodIndexRule(IndexType oper, DynamicMetaObject[] args) {
462            MethodInfo[] defaults = GetMethodsFromDefaults(args[0].GetLimitType().GetDefaultMembers(), oper);
463            if (defaults.Length != 0) {
464                MethodBinder binder = MethodBinder.MakeBinder(
465                    this,
466                    oper == IndexType.Get ? "get_Item" : "set_Item",
467                    defaults);
468
469                DynamicMetaObject[] selfWithArgs = args;
470                ParameterExpression arg2 = null;
471
472                if (oper == IndexType.Set) {
473                    Debug.Assert(args.Length >= 2);
474
475                    // need to save arg2 in a temp because it's also our result
476                    arg2 = Ast.Variable(args[2].Expression.Type, "arg2Temp");
477
478                    args[2] = new DynamicMetaObject(
479                        Ast.Assign(arg2, args[2].Expression),
480                        args[2].Restrictions
481                    );
482                }
483
484                BindingTarget target = binder.MakeBindingTarget(CallTypes.ImplicitInstance, selfWithArgs);
485
486                BindingRestrictions restrictions = BindingRestrictions.Combine(args);
487
488                if (target.Success) {
489                    if (oper == IndexType.Get) {
490                        return new DynamicMetaObject(
491                            target.MakeExpression(),
492                            restrictions.Merge(BindingRestrictions.Combine(target.RestrictedArguments.Objects))
493                        );
494                    } else {
495                        return new DynamicMetaObject(
496                            Ast.Block(
497                                new ParameterExpression[] { arg2 },
498                                target.MakeExpression(),
499                                arg2
500                            ),
501                            restrictions.Merge(BindingRestrictions.Combine(target.RestrictedArguments.Objects))
502                        );
503                    }
504                }
505
506                return MakeError(
507                    MakeInvalidParametersError(target),
508                    restrictions
509                );
510            }
511
512            return null;
513        }
514
515        private DynamicMetaObject MakeArrayIndexRule(IndexType oper, DynamicMetaObject[] args) {
516            if (CanConvertFrom(GetArgType(args, 1), typeof(int), false, NarrowingLevel.All)) {
517                BindingRestrictions restrictions = BindingRestrictionsHelpers.GetRuntimeTypeRestriction(args[0].Expression, args[0].GetLimitType()).Merge(BindingRestrictions.Combine(args));
518
519                if (oper == IndexType.Get) {
520                    return new DynamicMetaObject(
521                        Ast.ArrayAccess(
522                            args[0].Expression,
523                            ConvertIfNeeded(args[1].Expression, typeof(int))
524                        ),
525                        restrictions
526                    );
527                } else {
528                    return new DynamicMetaObject(
529                        Ast.Assign(
530                            Ast.ArrayAccess(
531                                args[0].Expression,
532                                ConvertIfNeeded(args[1].Expression, typeof(int))
533                            ),
534                            ConvertIfNeeded(args[2].Expression, args[0].GetLimitType().GetElementType())
535                        ),
536                        restrictions.Merge(args[1].Restrictions)
537                    );
538                }
539            }
540
541            return null;
542        }
543
544        private MethodInfo[] GetMethodsFromDefaults(MemberInfo[] defaults, IndexType op) {
545            List<MethodInfo> methods = new List<MethodInfo>();
546            foreach (MemberInfo mi in defaults) {
547                PropertyInfo pi = mi as PropertyInfo;
548
549                if (pi != null) {
550                    if (op == IndexType.Get) {
551                        MethodInfo method = pi.GetGetMethod(PrivateBinding); 
552                        if (method != null) methods.Add(method);
553                    } else if (op == IndexType.Set) {
554                        MethodInfo method = pi.GetSetMethod(PrivateBinding);
555                        if (method != null) methods.Add(method);
556                    }
557                }
558            }
559
560            // if we received methods from both declaring type & base types we need to filter them
561            Dictionary<MethodSignatureInfo, MethodInfo> dict = new Dictionary<MethodSignatureInfo, MethodInfo>();
562            foreach (MethodInfo mb in methods) {
563                MethodSignatureInfo args = new MethodSignatureInfo(mb.IsStatic, mb.GetParameters());
564                MethodInfo other;
565
566                if (dict.TryGetValue(args, out other)) {
567                    if (other.DeclaringType.IsAssignableFrom(mb.DeclaringType)) {
568                        // derived type replaces...
569                        dict[args] = mb;
570                    }
571                } else {
572                    dict[args] = mb;
573                }
574            }
575
576            return new List<MethodInfo>(dict.Values).ToArray();
577        }
578
579        #endregion
580
581        #region Common helpers
582
583        private DynamicMetaObject TryMakeBindingTarget(MethodInfo[] targets, DynamicMetaObject[] args, Expression codeContext, BindingRestrictions restrictions) {
584            MethodBinder mb = MethodBinder.MakeBinder(this, targets[0].Name, targets);
585
586            BindingTarget target = mb.MakeBindingTarget(CallTypes.None, args);
587            if (target.Success) {
588                return new DynamicMetaObject(
589                    target.MakeExpression(new ParameterBinderWithCodeContext(this, codeContext)),
590                    restrictions.Merge(BindingRestrictions.Combine(target.RestrictedArguments.Objects))
591                );
592            }
593
594            return null;
595        }
596
597        private DynamicMetaObject TryMakeInvertedBindingTarget(MethodBase[] targets, DynamicMetaObject[] args) {
598            MethodBinder mb = MethodBinder.MakeBinder(this, targets[0].Name, targets);
599            DynamicMetaObject[] selfArgs = args;
600            BindingTarget target = mb.MakeBindingTarget(CallTypes.None, selfArgs);
601
602            if (target.Success) {
603                return new DynamicMetaObject(
604                    Ast.Not(target.MakeExpression()),
605                    BindingRestrictions.Combine(target.RestrictedArguments.Objects)
606                );
607            }
608
609            return null;
610        }
611
612        private static ExpressionType GetInvertedOperator(ExpressionType op) {
613            switch (op) {
614                case ExpressionType.LessThan: return ExpressionType.GreaterThanOrEqual;
615                case ExpressionType.LessThanOrEqual: return ExpressionType.GreaterThan;
616                case ExpressionType.GreaterThan: return ExpressionType.LessThanOrEqual;
617                case ExpressionType.GreaterThanOrEqual: return ExpressionType.LessThan;
618                case ExpressionType.Equal: return ExpressionType.NotEqual;
619                case ExpressionType.NotEqual: return ExpressionType.Equal;
620                default: throw new InvalidOperationException();
621            }
622        }
623
624        private Expression ConvertIfNeeded(Expression expression, Type type) {
625            Assert.NotNull(expression, type);
626
627            if (expression.Type != type) {
628                return ConvertExpression(expression, type, ConversionResultKind.ExplicitCast, AstUtils.Constant(null, typeof(CodeContext)));
629            }
630            return expression;
631        }
632
633        private MethodInfo[] GetApplicableMembers(Type t, OperatorInfo info) {
634            Assert.NotNull(t, info);
635
636            OldDoOperationAction act = OldDoOperationAction.Make(this, OperatorInfo.ExpressionTypeToOperator(info.Operator));
637
638            MemberGroup members = GetMember(act, t, info.Name);
639            if (members.Count == 0 && info.AlternateName != null) {
640                members = GetMember(act, t, info.AlternateName);
641            }
642
643            // filter down to just methods
644            return FilterNonMethods(t, members);
645        }
646        
647        private static BindingRestrictions GetFallbackRestrictions(Type t, EventTracker et, DynamicMetaObject self) {
648            if (t == typeof(EventTracker)) {
649                //
650                // Test Generated:
651                //   BinderOps.GetEventHandlerType(((EventTracker)args[0]).Event) == et.Event.EventHandlerType
652                //
653                return BindingRestrictions.GetExpressionRestriction(
654                    Ast.Equal(
655                        Ast.Call(
656                            typeof(BinderOps).GetMethod("GetEventHandlerType"),
657                            Ast.Property(
658                                Ast.Convert(
659                                    self.Expression,
660                                    typeof(EventTracker)
661                                ),
662                                typeof(EventTracker).GetProperty("Event")
663                            )
664                        ),
665                        AstUtils.Constant(et.Event.EventHandlerType)
666                    )
667                );
668            } else if (t == typeof(BoundMemberTracker)) {
669                //
670                // Test Generated:
671                //   BinderOps.GetEventHandlerType(((EventTracker)((BoundMemberTracker)args[0]).BountTo).Event) == et.Event.EventHandlerType
672                //
673                return BindingRestrictions.GetExpressionRestriction(
674                    Ast.Equal(
675                        Ast.Call(
676                            typeof(BinderOps).GetMethod("GetEventHandlerType"),
677                            Ast.Property(
678                                Ast.Convert(
679                                    Ast.Property(
680                                        Ast.Convert(
681                                            self.Expression,
682                                            typeof(BoundMemberTracker)
683                                        ),
684                                        typeof(BoundMemberTracker).GetProperty("BoundTo")
685                                    ),
686                                    typeof(EventTracker)
687                                ),
688                                typeof(EventTracker).GetProperty("Event")
689                            )
690                        ),
691                        AstUtils.Constant(et.Event.EventHandlerType)
692                    )
693                );
694            }
695
696            return BindingRestrictions.Empty;
697        }
698
699        private static MethodInfo[] FilterNonMethods(Type t, MemberGroup members) {
700            Assert.NotNull(t, members);
701
702            List<MethodInfo> methods = new List<MethodInfo>(members.Count);
703            foreach (MemberTracker mi in members) {
704                if (mi.MemberType == TrackerTypes.Method) {
705                    MethodInfo method = ((MethodTracker)mi).Method;
706
707                    // don't call object methods for DynamicNull type, but if someone added
708                    // methods to null we'd call those.
709                    if (method.DeclaringType != typeof(object) || t != typeof(DynamicNull)) {
710                        methods.Add(method);
711                    }
712                }
713            }
714
715            return methods.ToArray();
716        }
717
718        #endregion
719    }
720}