PageRenderTime 48ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

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