DefaultBinder.cs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Reflection;
  5. using System.Diagnostics;
  6. using CultureInfo = System.Globalization.CultureInfo;
  7. namespace System
  8. {
  9. #if CORERT
  10. public sealed
  11. #else
  12. internal
  13. #endif
  14. partial class DefaultBinder : Binder
  15. {
  16. // This method is passed a set of methods and must choose the best
  17. // fit. The methods all have the same number of arguments and the object
  18. // array args. On exit, this method will choice the best fit method
  19. // and coerce the args to match that method. By match, we mean all primitive
  20. // arguments are exact matchs and all object arguments are exact or subclasses
  21. // of the target. If the target OR is an interface, the object must implement
  22. // that interface. There are a couple of exceptions
  23. // thrown when a method cannot be returned. If no method matchs the args and
  24. // ArgumentException is thrown. If multiple methods match the args then
  25. // an AmbiguousMatchException is thrown.
  26. //
  27. // The most specific match will be selected.
  28. //
  29. public sealed override MethodBase BindToMethod(
  30. BindingFlags bindingAttr, MethodBase[] match, ref object[] args,
  31. ParameterModifier[] modifiers, CultureInfo cultureInfo, string[] names, out object state)
  32. {
  33. if (match == null || match.Length == 0)
  34. throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
  35. MethodBase[] candidates = (MethodBase[])match.Clone();
  36. int i;
  37. int j;
  38. state = null;
  39. #region Map named parameters to candidate parameter positions
  40. // We are creating an paramOrder array to act as a mapping
  41. // between the order of the args and the actual order of the
  42. // parameters in the method. This order may differ because
  43. // named parameters (names) may change the order. If names
  44. // is not provided, then we assume the default mapping (0,1,...)
  45. int[][] paramOrder = new int[candidates.Length][];
  46. for (i = 0; i < candidates.Length; i++)
  47. {
  48. ParameterInfo[] par = candidates[i].GetParametersNoCopy();
  49. // args.Length + 1 takes into account the possibility of a last paramArray that can be omitted
  50. paramOrder[i] = new int[(par.Length > args.Length) ? par.Length : args.Length];
  51. if (names == null)
  52. {
  53. // Default mapping
  54. for (j = 0; j < args.Length; j++)
  55. paramOrder[i][j] = j;
  56. }
  57. else
  58. {
  59. // Named parameters, reorder the mapping. If CreateParamOrder fails, it means that the method
  60. // doesn't have a name that matchs one of the named parameters so we don't consider it any further.
  61. if (!CreateParamOrder(paramOrder[i], par, names))
  62. candidates[i] = null;
  63. }
  64. }
  65. #endregion
  66. Type[] paramArrayTypes = new Type[candidates.Length];
  67. Type[] argTypes = new Type[args.Length];
  68. #region Cache the type of the provided arguments
  69. // object that contain a null are treated as if they were typeless (but match either object
  70. // references or value classes). We mark this condition by placing a null in the argTypes array.
  71. for (i = 0; i < args.Length; i++)
  72. {
  73. if (args[i] != null)
  74. {
  75. argTypes[i] = args[i].GetType();
  76. }
  77. }
  78. #endregion
  79. // Find the method that matches...
  80. int CurIdx = 0;
  81. bool defaultValueBinding = ((bindingAttr & BindingFlags.OptionalParamBinding) != 0);
  82. Type paramArrayType = null;
  83. #region Filter methods by parameter count and type
  84. for (i = 0; i < candidates.Length; i++)
  85. {
  86. paramArrayType = null;
  87. // If we have named parameters then we may have a hole in the candidates array.
  88. if (candidates[i] == null)
  89. continue;
  90. // Validate the parameters.
  91. ParameterInfo[] par = candidates[i].GetParametersNoCopy();
  92. #region Match method by parameter count
  93. if (par.Length == 0)
  94. {
  95. #region No formal parameters
  96. if (args.Length != 0)
  97. {
  98. if ((candidates[i].CallingConvention & CallingConventions.VarArgs) == 0)
  99. continue;
  100. }
  101. // This is a valid routine so we move it up the candidates list.
  102. paramOrder[CurIdx] = paramOrder[i];
  103. candidates[CurIdx++] = candidates[i];
  104. continue;
  105. #endregion
  106. }
  107. else if (par.Length > args.Length)
  108. {
  109. #region Shortage of provided parameters
  110. // If the number of parameters is greater than the number of args then
  111. // we are in the situation were we may be using default values.
  112. for (j = args.Length; j < par.Length - 1; j++)
  113. {
  114. if (par[j].DefaultValue == System.DBNull.Value)
  115. break;
  116. }
  117. if (j != par.Length - 1)
  118. continue;
  119. if (par[j].DefaultValue == System.DBNull.Value)
  120. {
  121. if (!par[j].ParameterType.IsArray)
  122. continue;
  123. if (!par[j].IsDefined(typeof(ParamArrayAttribute), true))
  124. continue;
  125. paramArrayType = par[j].ParameterType.GetElementType();
  126. }
  127. #endregion
  128. }
  129. else if (par.Length < args.Length)
  130. {
  131. #region Excess provided parameters
  132. // test for the ParamArray case
  133. int lastArgPos = par.Length - 1;
  134. if (!par[lastArgPos].ParameterType.IsArray)
  135. continue;
  136. if (!par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true))
  137. continue;
  138. if (paramOrder[i][lastArgPos] != lastArgPos)
  139. continue;
  140. paramArrayType = par[lastArgPos].ParameterType.GetElementType();
  141. #endregion
  142. }
  143. else
  144. {
  145. #region Test for paramArray, save paramArray type
  146. int lastArgPos = par.Length - 1;
  147. if (par[lastArgPos].ParameterType.IsArray
  148. && par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true)
  149. && paramOrder[i][lastArgPos] == lastArgPos)
  150. {
  151. if (!par[lastArgPos].ParameterType.IsAssignableFrom(argTypes[lastArgPos]))
  152. paramArrayType = par[lastArgPos].ParameterType.GetElementType();
  153. }
  154. #endregion
  155. }
  156. #endregion
  157. Type pCls = null;
  158. int argsToCheck = (paramArrayType != null) ? par.Length - 1 : args.Length;
  159. #region Match method by parameter type
  160. for (j = 0; j < argsToCheck; j++)
  161. {
  162. #region Classic argument coersion checks
  163. // get the formal type
  164. pCls = par[j].ParameterType;
  165. if (pCls.IsByRef)
  166. pCls = pCls.GetElementType();
  167. // the type is the same
  168. if (pCls == argTypes[paramOrder[i][j]])
  169. continue;
  170. // a default value is available
  171. if (defaultValueBinding && args[paramOrder[i][j]] == Type.Missing)
  172. continue;
  173. // the argument was null, so it matches with everything
  174. if (args[paramOrder[i][j]] == null)
  175. continue;
  176. // the type is Object, so it will match everything
  177. if (pCls == typeof(object))
  178. continue;
  179. // now do a "classic" type check
  180. if (pCls.IsPrimitive)
  181. {
  182. if (argTypes[paramOrder[i][j]] == null || !CanChangePrimitiveObjectToType(args[paramOrder[i][j]], pCls))
  183. {
  184. break;
  185. }
  186. }
  187. else
  188. {
  189. if (argTypes[paramOrder[i][j]] == null)
  190. continue;
  191. if (!pCls.IsAssignableFrom(argTypes[paramOrder[i][j]]))
  192. {
  193. if (argTypes[paramOrder[i][j]].IsCOMObject)
  194. {
  195. if (pCls.IsInstanceOfType(args[paramOrder[i][j]]))
  196. continue;
  197. }
  198. break;
  199. }
  200. }
  201. #endregion
  202. }
  203. if (paramArrayType != null && j == par.Length - 1)
  204. {
  205. #region Check that excess arguments can be placed in the param array
  206. for (; j < args.Length; j++)
  207. {
  208. if (paramArrayType.IsPrimitive)
  209. {
  210. if (argTypes[j] == null || !CanChangePrimitiveObjectToType(args[j], paramArrayType))
  211. break;
  212. }
  213. else
  214. {
  215. if (argTypes[j] == null)
  216. continue;
  217. if (!paramArrayType.IsAssignableFrom(argTypes[j]))
  218. {
  219. if (argTypes[j].IsCOMObject)
  220. {
  221. if (paramArrayType.IsInstanceOfType(args[j]))
  222. continue;
  223. }
  224. break;
  225. }
  226. }
  227. }
  228. #endregion
  229. }
  230. #endregion
  231. if (j == args.Length)
  232. {
  233. #region This is a valid routine so we move it up the candidates list
  234. paramOrder[CurIdx] = paramOrder[i];
  235. paramArrayTypes[CurIdx] = paramArrayType;
  236. candidates[CurIdx++] = candidates[i];
  237. #endregion
  238. }
  239. }
  240. #endregion
  241. // If we didn't find a method
  242. if (CurIdx == 0)
  243. throw new MissingMethodException(SR.MissingMember);
  244. if (CurIdx == 1)
  245. {
  246. #region Found only one method
  247. if (names != null)
  248. {
  249. state = new BinderState((int[])paramOrder[0].Clone(), args.Length, paramArrayTypes[0] != null);
  250. ReorderParams(paramOrder[0], args);
  251. }
  252. // If the parameters and the args are not the same length or there is a paramArray
  253. // then we need to create a argument array.
  254. ParameterInfo[] parms = candidates[0].GetParametersNoCopy();
  255. if (parms.Length == args.Length)
  256. {
  257. if (paramArrayTypes[0] != null)
  258. {
  259. object[] objs = new object[parms.Length];
  260. int lastPos = parms.Length - 1;
  261. Array.Copy(args, 0, objs, 0, lastPos);
  262. objs[lastPos] = Array.CreateInstance(paramArrayTypes[0], 1);
  263. ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
  264. args = objs;
  265. }
  266. }
  267. else if (parms.Length > args.Length)
  268. {
  269. object[] objs = new object[parms.Length];
  270. for (i = 0; i < args.Length; i++)
  271. objs[i] = args[i];
  272. for (; i < parms.Length - 1; i++)
  273. objs[i] = parms[i].DefaultValue;
  274. if (paramArrayTypes[0] != null)
  275. objs[i] = Array.CreateInstance(paramArrayTypes[0], 0); // create an empty array for the
  276. else
  277. objs[i] = parms[i].DefaultValue;
  278. args = objs;
  279. }
  280. else
  281. {
  282. if ((candidates[0].CallingConvention & CallingConventions.VarArgs) == 0)
  283. {
  284. object[] objs = new object[parms.Length];
  285. int paramArrayPos = parms.Length - 1;
  286. Array.Copy(args, 0, objs, 0, paramArrayPos);
  287. objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[0], args.Length - paramArrayPos);
  288. Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
  289. args = objs;
  290. }
  291. }
  292. #endregion
  293. return candidates[0];
  294. }
  295. int currentMin = 0;
  296. bool ambig = false;
  297. for (i = 1; i < CurIdx; i++)
  298. {
  299. #region Walk all of the methods looking the most specific method to invoke
  300. int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder[currentMin], paramArrayTypes[currentMin],
  301. candidates[i], paramOrder[i], paramArrayTypes[i], argTypes, args);
  302. if (newMin == 0)
  303. {
  304. ambig = true;
  305. }
  306. else if (newMin == 2)
  307. {
  308. currentMin = i;
  309. ambig = false;
  310. }
  311. #endregion
  312. }
  313. if (ambig)
  314. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  315. // Reorder (if needed)
  316. if (names != null)
  317. {
  318. state = new BinderState((int[])paramOrder[currentMin].Clone(), args.Length, paramArrayTypes[currentMin] != null);
  319. ReorderParams(paramOrder[currentMin], args);
  320. }
  321. // If the parameters and the args are not the same length or there is a paramArray
  322. // then we need to create a argument array.
  323. ParameterInfo[] parameters = candidates[currentMin].GetParametersNoCopy();
  324. if (parameters.Length == args.Length)
  325. {
  326. if (paramArrayTypes[currentMin] != null)
  327. {
  328. object[] objs = new object[parameters.Length];
  329. int lastPos = parameters.Length - 1;
  330. Array.Copy(args, 0, objs, 0, lastPos);
  331. objs[lastPos] = Array.CreateInstance(paramArrayTypes[currentMin], 1);
  332. ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
  333. args = objs;
  334. }
  335. }
  336. else if (parameters.Length > args.Length)
  337. {
  338. object[] objs = new object[parameters.Length];
  339. for (i = 0; i < args.Length; i++)
  340. objs[i] = args[i];
  341. for (; i < parameters.Length - 1; i++)
  342. objs[i] = parameters[i].DefaultValue;
  343. if (paramArrayTypes[currentMin] != null)
  344. {
  345. objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], 0);
  346. }
  347. else
  348. {
  349. objs[i] = parameters[i].DefaultValue;
  350. }
  351. args = objs;
  352. }
  353. else
  354. {
  355. if ((candidates[currentMin].CallingConvention & CallingConventions.VarArgs) == 0)
  356. {
  357. object[] objs = new object[parameters.Length];
  358. int paramArrayPos = parameters.Length - 1;
  359. Array.Copy(args, 0, objs, 0, paramArrayPos);
  360. objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[currentMin], args.Length - paramArrayPos);
  361. Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
  362. args = objs;
  363. }
  364. }
  365. return candidates[currentMin];
  366. }
  367. // Given a set of fields that match the base criteria, select a field.
  368. // if value is null then we have no way to select a field
  369. public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo cultureInfo)
  370. {
  371. if (match == null)
  372. {
  373. throw new ArgumentNullException(nameof(match));
  374. }
  375. int i;
  376. // Find the method that match...
  377. int CurIdx = 0;
  378. Type valueType = null;
  379. FieldInfo[] candidates = (FieldInfo[])match.Clone();
  380. // If we are a FieldSet, then use the value's type to disambiguate
  381. if ((bindingAttr & BindingFlags.SetField) != 0)
  382. {
  383. valueType = value.GetType();
  384. for (i = 0; i < candidates.Length; i++)
  385. {
  386. Type pCls = candidates[i].FieldType;
  387. if (pCls == valueType)
  388. {
  389. candidates[CurIdx++] = candidates[i];
  390. continue;
  391. }
  392. if (value == Empty.Value)
  393. {
  394. // the object passed in was null which would match any non primitive non value type
  395. if (pCls.IsClass)
  396. {
  397. candidates[CurIdx++] = candidates[i];
  398. continue;
  399. }
  400. }
  401. if (pCls == typeof(object))
  402. {
  403. candidates[CurIdx++] = candidates[i];
  404. continue;
  405. }
  406. if (pCls.IsPrimitive)
  407. {
  408. if (CanChangePrimitiveObjectToType(value, pCls))
  409. {
  410. candidates[CurIdx++] = candidates[i];
  411. continue;
  412. }
  413. }
  414. else
  415. {
  416. if (pCls.IsAssignableFrom(valueType))
  417. {
  418. candidates[CurIdx++] = candidates[i];
  419. continue;
  420. }
  421. }
  422. }
  423. if (CurIdx == 0)
  424. throw new MissingFieldException(SR.MissingField);
  425. if (CurIdx == 1)
  426. return candidates[0];
  427. }
  428. // Walk all of the methods looking the most specific method to invoke
  429. int currentMin = 0;
  430. bool ambig = false;
  431. for (i = 1; i < CurIdx; i++)
  432. {
  433. int newMin = FindMostSpecificField(candidates[currentMin], candidates[i]);
  434. if (newMin == 0)
  435. ambig = true;
  436. else
  437. {
  438. if (newMin == 2)
  439. {
  440. currentMin = i;
  441. ambig = false;
  442. }
  443. }
  444. }
  445. if (ambig)
  446. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  447. return candidates[currentMin];
  448. }
  449. // Given a set of methods that match the base criteria, select a method based
  450. // upon an array of types. This method should return null if no method matchs
  451. // the criteria.
  452. public sealed override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
  453. {
  454. int i;
  455. int j;
  456. Type[] realTypes = new Type[types.Length];
  457. for (i = 0; i < types.Length; i++)
  458. {
  459. realTypes[i] = types[i].UnderlyingSystemType;
  460. if (!(realTypes[i].IsRuntimeImplemented() || realTypes[i] is SignatureType))
  461. throw new ArgumentException(SR.Arg_MustBeType, nameof(types));
  462. }
  463. types = realTypes;
  464. // We don't automatically jump out on exact match.
  465. if (match == null || match.Length == 0)
  466. throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
  467. MethodBase[] candidates = (MethodBase[])match.Clone();
  468. // Find all the methods that can be described by the types parameter.
  469. // Remove all of them that cannot.
  470. int CurIdx = 0;
  471. for (i = 0; i < candidates.Length; i++)
  472. {
  473. ParameterInfo[] par = candidates[i].GetParametersNoCopy();
  474. if (par.Length != types.Length)
  475. continue;
  476. for (j = 0; j < types.Length; j++)
  477. {
  478. Type pCls = par[j].ParameterType;
  479. if (types[j].MatchesParameterTypeExactly(par[j]))
  480. continue;
  481. if (pCls == typeof(object))
  482. continue;
  483. Type type = types[j];
  484. if (type is SignatureType signatureType)
  485. {
  486. if (!(candidates[i] is MethodInfo methodInfo))
  487. break;
  488. type = signatureType.TryResolveAgainstGenericMethod(methodInfo);
  489. if (type == null)
  490. break;
  491. }
  492. if (pCls.IsPrimitive)
  493. {
  494. if (!(type.UnderlyingSystemType.IsRuntimeImplemented()) ||
  495. !CanChangePrimitive(type.UnderlyingSystemType, pCls.UnderlyingSystemType))
  496. break;
  497. }
  498. else
  499. {
  500. if (!pCls.IsAssignableFrom(type))
  501. break;
  502. }
  503. }
  504. if (j == types.Length)
  505. candidates[CurIdx++] = candidates[i];
  506. }
  507. if (CurIdx == 0)
  508. return null;
  509. if (CurIdx == 1)
  510. return candidates[0];
  511. // Walk all of the methods looking the most specific method to invoke
  512. int currentMin = 0;
  513. bool ambig = false;
  514. int[] paramOrder = new int[types.Length];
  515. for (i = 0; i < types.Length; i++)
  516. paramOrder[i] = i;
  517. for (i = 1; i < CurIdx; i++)
  518. {
  519. int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder, null, candidates[i], paramOrder, null, types, null);
  520. if (newMin == 0)
  521. ambig = true;
  522. else
  523. {
  524. if (newMin == 2)
  525. {
  526. currentMin = i;
  527. ambig = false;
  528. currentMin = i;
  529. }
  530. }
  531. }
  532. if (ambig)
  533. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  534. return candidates[currentMin];
  535. }
  536. // Given a set of properties that match the base criteria, select one.
  537. public sealed override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType,
  538. Type[] indexes, ParameterModifier[] modifiers)
  539. {
  540. // Allow a null indexes array. But if it is not null, every element must be non-null as well.
  541. if (indexes != null)
  542. {
  543. foreach (Type index in indexes)
  544. {
  545. if (index == null)
  546. throw new ArgumentNullException(nameof(indexes));
  547. }
  548. }
  549. if (match == null || match.Length == 0)
  550. throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
  551. PropertyInfo[] candidates = (PropertyInfo[])match.Clone();
  552. int i, j = 0;
  553. // Find all the properties that can be described by type indexes parameter
  554. int CurIdx = 0;
  555. int indexesLength = (indexes != null) ? indexes.Length : 0;
  556. for (i = 0; i < candidates.Length; i++)
  557. {
  558. if (indexes != null)
  559. {
  560. ParameterInfo[] par = candidates[i].GetIndexParameters();
  561. if (par.Length != indexesLength)
  562. continue;
  563. for (j = 0; j < indexesLength; j++)
  564. {
  565. Type pCls = par[j].ParameterType;
  566. // If the classes exactly match continue
  567. if (pCls == indexes[j])
  568. continue;
  569. if (pCls == typeof(object))
  570. continue;
  571. if (pCls.IsPrimitive)
  572. {
  573. if (!(indexes[j].UnderlyingSystemType.IsRuntimeImplemented()) ||
  574. !CanChangePrimitive(indexes[j].UnderlyingSystemType, pCls.UnderlyingSystemType))
  575. break;
  576. }
  577. else
  578. {
  579. if (!pCls.IsAssignableFrom(indexes[j]))
  580. break;
  581. }
  582. }
  583. }
  584. if (j == indexesLength)
  585. {
  586. if (returnType != null)
  587. {
  588. if (candidates[i].PropertyType.IsPrimitive)
  589. {
  590. if (!(returnType.UnderlyingSystemType.IsRuntimeImplemented()) ||
  591. !CanChangePrimitive(returnType.UnderlyingSystemType, candidates[i].PropertyType.UnderlyingSystemType))
  592. continue;
  593. }
  594. else
  595. {
  596. if (!candidates[i].PropertyType.IsAssignableFrom(returnType))
  597. continue;
  598. }
  599. }
  600. candidates[CurIdx++] = candidates[i];
  601. }
  602. }
  603. if (CurIdx == 0)
  604. return null;
  605. if (CurIdx == 1)
  606. return candidates[0];
  607. // Walk all of the properties looking the most specific method to invoke
  608. int currentMin = 0;
  609. bool ambig = false;
  610. int[] paramOrder = new int[indexesLength];
  611. for (i = 0; i < indexesLength; i++)
  612. paramOrder[i] = i;
  613. for (i = 1; i < CurIdx; i++)
  614. {
  615. int newMin = FindMostSpecificType(candidates[currentMin].PropertyType, candidates[i].PropertyType, returnType);
  616. if (newMin == 0 && indexes != null)
  617. newMin = FindMostSpecific(candidates[currentMin].GetIndexParameters(),
  618. paramOrder,
  619. null,
  620. candidates[i].GetIndexParameters(),
  621. paramOrder,
  622. null,
  623. indexes,
  624. null);
  625. if (newMin == 0)
  626. {
  627. newMin = FindMostSpecificProperty(candidates[currentMin], candidates[i]);
  628. if (newMin == 0)
  629. ambig = true;
  630. }
  631. if (newMin == 2)
  632. {
  633. ambig = false;
  634. currentMin = i;
  635. }
  636. }
  637. if (ambig)
  638. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  639. return candidates[currentMin];
  640. }
  641. // ChangeType
  642. // The default binder doesn't support any change type functionality.
  643. // This is because the default is built into the low level invoke code.
  644. public override object ChangeType(object value, Type type, CultureInfo cultureInfo)
  645. {
  646. throw new NotSupportedException(SR.NotSupported_ChangeType);
  647. }
  648. public sealed override void ReorderArgumentArray(ref object[] args, object state)
  649. {
  650. BinderState binderState = (BinderState)state;
  651. ReorderParams(binderState._argsMap, args);
  652. if (binderState._isParamArray)
  653. {
  654. int paramArrayPos = args.Length - 1;
  655. if (args.Length == binderState._originalSize)
  656. args[paramArrayPos] = ((object[])args[paramArrayPos])[0];
  657. else
  658. {
  659. // must be args.Length < state.originalSize
  660. object[] newArgs = new object[args.Length];
  661. Array.Copy(args, 0, newArgs, 0, paramArrayPos);
  662. for (int i = paramArrayPos, j = 0; i < newArgs.Length; i++, j++)
  663. {
  664. newArgs[i] = ((object[])args[paramArrayPos])[j];
  665. }
  666. args = newArgs;
  667. }
  668. }
  669. else
  670. {
  671. if (args.Length > binderState._originalSize)
  672. {
  673. object[] newArgs = new object[binderState._originalSize];
  674. Array.Copy(args, 0, newArgs, 0, binderState._originalSize);
  675. args = newArgs;
  676. }
  677. }
  678. }
  679. // Return any exact bindings that may exist. (This method is not defined on the
  680. // Binder and is used by RuntimeType.)
  681. public static MethodBase ExactBinding(MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
  682. {
  683. if (match == null)
  684. throw new ArgumentNullException(nameof(match));
  685. MethodBase[] aExactMatches = new MethodBase[match.Length];
  686. int cExactMatches = 0;
  687. for (int i = 0; i < match.Length; i++)
  688. {
  689. ParameterInfo[] par = match[i].GetParametersNoCopy();
  690. if (par.Length == 0)
  691. {
  692. continue;
  693. }
  694. int j;
  695. for (j = 0; j < types.Length; j++)
  696. {
  697. Type pCls = par[j].ParameterType;
  698. // If the classes exactly match continue
  699. if (!pCls.Equals(types[j]))
  700. break;
  701. }
  702. if (j < types.Length)
  703. continue;
  704. // Add the exact match to the array of exact matches.
  705. aExactMatches[cExactMatches] = match[i];
  706. cExactMatches++;
  707. }
  708. if (cExactMatches == 0)
  709. return null;
  710. if (cExactMatches == 1)
  711. return aExactMatches[0];
  712. return FindMostDerivedNewSlotMeth(aExactMatches, cExactMatches);
  713. }
  714. // Return any exact bindings that may exist. (This method is not defined on the
  715. // Binder and is used by RuntimeType.)
  716. public static PropertyInfo ExactPropertyBinding(PropertyInfo[] match, Type returnType, Type[] types, ParameterModifier[] modifiers)
  717. {
  718. if (match == null)
  719. throw new ArgumentNullException(nameof(match));
  720. PropertyInfo bestMatch = null;
  721. int typesLength = (types != null) ? types.Length : 0;
  722. for (int i = 0; i < match.Length; i++)
  723. {
  724. ParameterInfo[] par = match[i].GetIndexParameters();
  725. int j;
  726. for (j = 0; j < typesLength; j++)
  727. {
  728. Type pCls = par[j].ParameterType;
  729. // If the classes exactly match continue
  730. if (pCls != types[j])
  731. break;
  732. }
  733. if (j < typesLength)
  734. continue;
  735. if (returnType != null && returnType != match[i].PropertyType)
  736. continue;
  737. if (bestMatch != null)
  738. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  739. bestMatch = match[i];
  740. }
  741. return bestMatch;
  742. }
  743. private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1, Type paramArrayType1,
  744. ParameterInfo[] p2, int[] paramOrder2, Type paramArrayType2,
  745. Type[] types, object[] args)
  746. {
  747. // A method using params is always less specific than one not using params
  748. if (paramArrayType1 != null && paramArrayType2 == null) return 2;
  749. if (paramArrayType2 != null && paramArrayType1 == null) return 1;
  750. // now either p1 and p2 both use params or neither does.
  751. bool p1Less = false;
  752. bool p2Less = false;
  753. for (int i = 0; i < types.Length; i++)
  754. {
  755. if (args != null && args[i] == Type.Missing)
  756. continue;
  757. Type c1, c2;
  758. // If a param array is present, then either
  759. // the user re-ordered the parameters in which case
  760. // the argument to the param array is either an array
  761. // in which case the params is conceptually ignored and so paramArrayType1 == null
  762. // or the argument to the param array is a single element
  763. // in which case paramOrder[i] == p1.Length - 1 for that element
  764. // or the user did not re-order the parameters in which case
  765. // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286)
  766. // so any index >= p.Length - 1 is being put in the param array
  767. if (paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1)
  768. c1 = paramArrayType1;
  769. else
  770. c1 = p1[paramOrder1[i]].ParameterType;
  771. if (paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1)
  772. c2 = paramArrayType2;
  773. else
  774. c2 = p2[paramOrder2[i]].ParameterType;
  775. if (c1 == c2) continue;
  776. switch (FindMostSpecificType(c1, c2, types[i]))
  777. {
  778. case 0: return 0;
  779. case 1: p1Less = true; break;
  780. case 2: p2Less = true; break;
  781. }
  782. }
  783. // Two way p1Less and p2Less can be equal. All the arguments are the
  784. // same they both equal false, otherwise there were things that both
  785. // were the most specific type on....
  786. if (p1Less == p2Less)
  787. {
  788. // if we cannot tell which is a better match based on parameter types (p1Less == p2Less),
  789. // let's see which one has the most matches without using the params array (the longer one wins).
  790. if (!p1Less && args != null)
  791. {
  792. if (p1.Length > p2.Length)
  793. {
  794. return 1;
  795. }
  796. else if (p2.Length > p1.Length)
  797. {
  798. return 2;
  799. }
  800. }
  801. return 0;
  802. }
  803. else
  804. {
  805. return (p1Less == true) ? 1 : 2;
  806. }
  807. }
  808. private static int FindMostSpecificType(Type c1, Type c2, Type t)
  809. {
  810. // If the two types are exact move on...
  811. if (c1 == c2)
  812. return 0;
  813. if (t is SignatureType signatureType)
  814. {
  815. if (signatureType.MatchesExactly(c1))
  816. return 1;
  817. if (signatureType.MatchesExactly(c2))
  818. return 2;
  819. }
  820. else
  821. {
  822. if (c1 == t)
  823. return 1;
  824. if (c2 == t)
  825. return 2;
  826. }
  827. bool c1FromC2;
  828. bool c2FromC1;
  829. if (c1.IsByRef || c2.IsByRef)
  830. {
  831. if (c1.IsByRef && c2.IsByRef)
  832. {
  833. c1 = c1.GetElementType();
  834. c2 = c2.GetElementType();
  835. }
  836. else if (c1.IsByRef)
  837. {
  838. if (c1.GetElementType() == c2)
  839. return 2;
  840. c1 = c1.GetElementType();
  841. }
  842. else
  843. {
  844. if (c2.GetElementType() == c1)
  845. return 1;
  846. c2 = c2.GetElementType();
  847. }
  848. }
  849. if (c1.IsPrimitive && c2.IsPrimitive)
  850. {
  851. c1FromC2 = CanChangePrimitive(c2, c1);
  852. c2FromC1 = CanChangePrimitive(c1, c2);
  853. }
  854. else
  855. {
  856. c1FromC2 = c1.IsAssignableFrom(c2);
  857. c2FromC1 = c2.IsAssignableFrom(c1);
  858. }
  859. if (c1FromC2 == c2FromC1)
  860. return 0;
  861. if (c1FromC2)
  862. {
  863. return 2;
  864. }
  865. else
  866. {
  867. return 1;
  868. }
  869. }
  870. private static int FindMostSpecificMethod(MethodBase m1, int[] paramOrder1, Type paramArrayType1,
  871. MethodBase m2, int[] paramOrder2, Type paramArrayType2,
  872. Type[] types, object[] args)
  873. {
  874. // Find the most specific method based on the parameters.
  875. int res = FindMostSpecific(m1.GetParametersNoCopy(), paramOrder1, paramArrayType1,
  876. m2.GetParametersNoCopy(), paramOrder2, paramArrayType2, types, args);
  877. // If the match was not ambigous then return the result.
  878. if (res != 0)
  879. return res;
  880. // Check to see if the methods have the exact same name and signature.
  881. if (CompareMethodSig(m1, m2))
  882. {
  883. // Determine the depth of the declaring types for both methods.
  884. int hierarchyDepth1 = GetHierarchyDepth(m1.DeclaringType);
  885. int hierarchyDepth2 = GetHierarchyDepth(m2.DeclaringType);
  886. // The most derived method is the most specific one.
  887. if (hierarchyDepth1 == hierarchyDepth2)
  888. {
  889. return 0;
  890. }
  891. else if (hierarchyDepth1 < hierarchyDepth2)
  892. {
  893. return 2;
  894. }
  895. else
  896. {
  897. return 1;
  898. }
  899. }
  900. // The match is ambigous.
  901. return 0;
  902. }
  903. private static int FindMostSpecificField(FieldInfo cur1, FieldInfo cur2)
  904. {
  905. // Check to see if the fields have the same name.
  906. if (cur1.Name == cur2.Name)
  907. {
  908. int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType);
  909. int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType);
  910. if (hierarchyDepth1 == hierarchyDepth2)
  911. {
  912. Debug.Assert(cur1.IsStatic != cur2.IsStatic, "hierarchyDepth1 == hierarchyDepth2");
  913. return 0;
  914. }
  915. else if (hierarchyDepth1 < hierarchyDepth2)
  916. return 2;
  917. else
  918. return 1;
  919. }
  920. // The match is ambigous.
  921. return 0;
  922. }
  923. private static int FindMostSpecificProperty(PropertyInfo cur1, PropertyInfo cur2)
  924. {
  925. // Check to see if the fields have the same name.
  926. if (cur1.Name == cur2.Name)
  927. {
  928. int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType);
  929. int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType);
  930. if (hierarchyDepth1 == hierarchyDepth2)
  931. {
  932. return 0;
  933. }
  934. else if (hierarchyDepth1 < hierarchyDepth2)
  935. return 2;
  936. else
  937. return 1;
  938. }
  939. // The match is ambigous.
  940. return 0;
  941. }
  942. public static bool CompareMethodSig(MethodBase m1, MethodBase m2)
  943. {
  944. ParameterInfo[] params1 = m1.GetParametersNoCopy();
  945. ParameterInfo[] params2 = m2.GetParametersNoCopy();
  946. if (params1.Length != params2.Length)
  947. return false;
  948. int numParams = params1.Length;
  949. for (int i = 0; i < numParams; i++)
  950. {
  951. if (params1[i].ParameterType != params2[i].ParameterType)
  952. return false;
  953. }
  954. return true;
  955. }
  956. private static int GetHierarchyDepth(Type t)
  957. {
  958. int depth = 0;
  959. Type currentType = t;
  960. do
  961. {
  962. depth++;
  963. currentType = currentType.BaseType;
  964. } while (currentType != null);
  965. return depth;
  966. }
  967. internal static MethodBase FindMostDerivedNewSlotMeth(MethodBase[] match, int cMatches)
  968. {
  969. int deepestHierarchy = 0;
  970. MethodBase methWithDeepestHierarchy = null;
  971. for (int i = 0; i < cMatches; i++)
  972. {
  973. // Calculate the depth of the hierarchy of the declaring type of the
  974. // current method.
  975. int currentHierarchyDepth = GetHierarchyDepth(match[i].DeclaringType);
  976. // The two methods have the same name, signature, and hierarchy depth.
  977. // This can only happen if at least one is vararg or generic.
  978. if (currentHierarchyDepth == deepestHierarchy)
  979. {
  980. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  981. }
  982. // Check to see if this method is on the most derived class.
  983. if (currentHierarchyDepth > deepestHierarchy)
  984. {
  985. deepestHierarchy = currentHierarchyDepth;
  986. methWithDeepestHierarchy = match[i];
  987. }
  988. }
  989. return methWithDeepestHierarchy;
  990. }
  991. // This method will sort the vars array into the mapping order stored
  992. // in the paramOrder array.
  993. private static void ReorderParams(int[] paramOrder, object[] vars)
  994. {
  995. object[] varsCopy = new object[vars.Length];
  996. for (int i = 0; i < vars.Length; i++)
  997. varsCopy[i] = vars[i];
  998. for (int i = 0; i < vars.Length; i++)
  999. vars[i] = varsCopy[paramOrder[i]];
  1000. }
  1001. // This method will create the mapping between the Parameters and the underlying
  1002. // data based upon the names array. The names array is stored in the same order
  1003. // as the values and maps to the parameters of the method. We store the mapping
  1004. // from the parameters to the names in the paramOrder array. All parameters that
  1005. // don't have matching names are then stored in the array in order.
  1006. private static bool CreateParamOrder(int[] paramOrder, ParameterInfo[] pars, string[] names)
  1007. {
  1008. bool[] used = new bool[pars.Length];
  1009. // Mark which parameters have not been found in the names list
  1010. for (int i = 0; i < pars.Length; i++)
  1011. paramOrder[i] = -1;
  1012. // Find the parameters with names.
  1013. for (int i = 0; i < names.Length; i++)
  1014. {
  1015. int j;
  1016. for (j = 0; j < pars.Length; j++)
  1017. {
  1018. if (names[i].Equals(pars[j].Name))
  1019. {
  1020. paramOrder[j] = i;
  1021. used[i] = true;
  1022. break;
  1023. }
  1024. }
  1025. // This is an error condition. The name was not found. This
  1026. // method must not match what we sent.
  1027. if (j == pars.Length)
  1028. return false;
  1029. }
  1030. // Now we fill in the holes with the parameters that are unused.
  1031. int pos = 0;
  1032. for (int i = 0; i < pars.Length; i++)
  1033. {
  1034. if (paramOrder[i] == -1)
  1035. {
  1036. for (; pos < pars.Length; pos++)
  1037. {
  1038. if (!used[pos])
  1039. {
  1040. paramOrder[i] = pos;
  1041. pos++;
  1042. break;
  1043. }
  1044. }
  1045. }
  1046. }
  1047. return true;
  1048. }
  1049. internal class BinderState
  1050. {
  1051. internal readonly int[] _argsMap;
  1052. internal readonly int _originalSize;
  1053. internal readonly bool _isParamArray;
  1054. internal BinderState(int[] argsMap, int originalSize, bool isParamArray)
  1055. {
  1056. _argsMap = argsMap;
  1057. _originalSize = originalSize;
  1058. _isParamArray = isParamArray;
  1059. }
  1060. }
  1061. }
  1062. }