DefaultBinder.cs 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  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;
  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(); // TODO-NULLABLE: Indexer nullability tracked (https://github.com/dotnet/roslyn/issues/34644)
  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) // TODO-NULLABLE: Indexer nullability tracked (https://github.com/dotnet/roslyn/issues/34644)
  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;
  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 || !CanChangePrimitive(args[paramOrder[i][j]]?.GetType(), 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 || !CanChangePrimitive(args[j]?.GetType(), 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;
  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 (CanChangePrimitive(valueType, 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. {
  657. args[paramArrayPos] = ((object[])args[paramArrayPos]!)[0];
  658. }
  659. else
  660. {
  661. // must be args.Length < state.originalSize
  662. object[] newArgs = new object[args.Length];
  663. Array.Copy(args, 0, newArgs, 0, paramArrayPos);
  664. for (int i = paramArrayPos, j = 0; i < newArgs.Length; i++, j++)
  665. {
  666. newArgs[i] = ((object[])args[paramArrayPos]!)[j];
  667. }
  668. args = newArgs;
  669. }
  670. }
  671. else
  672. {
  673. if (args.Length > binderState._originalSize)
  674. {
  675. object[] newArgs = new object[binderState._originalSize];
  676. Array.Copy(args, 0, newArgs, 0, binderState._originalSize);
  677. args = newArgs;
  678. }
  679. }
  680. }
  681. // Return any exact bindings that may exist. (This method is not defined on the
  682. // Binder and is used by RuntimeType.)
  683. public static MethodBase? ExactBinding(MethodBase[] match, Type[] types, ParameterModifier[]? modifiers)
  684. {
  685. if (match == null)
  686. throw new ArgumentNullException(nameof(match));
  687. MethodBase[] aExactMatches = new MethodBase[match.Length];
  688. int cExactMatches = 0;
  689. for (int i = 0; i < match.Length; i++)
  690. {
  691. ParameterInfo[] par = match[i].GetParametersNoCopy();
  692. if (par.Length == 0)
  693. {
  694. continue;
  695. }
  696. int j;
  697. for (j = 0; j < types.Length; j++)
  698. {
  699. Type pCls = par[j].ParameterType;
  700. // If the classes exactly match continue
  701. if (!pCls.Equals(types[j]))
  702. break;
  703. }
  704. if (j < types.Length)
  705. continue;
  706. // Add the exact match to the array of exact matches.
  707. aExactMatches[cExactMatches] = match[i];
  708. cExactMatches++;
  709. }
  710. if (cExactMatches == 0)
  711. return null;
  712. if (cExactMatches == 1)
  713. return aExactMatches[0];
  714. return FindMostDerivedNewSlotMeth(aExactMatches, cExactMatches);
  715. }
  716. // Return any exact bindings that may exist. (This method is not defined on the
  717. // Binder and is used by RuntimeType.)
  718. public static PropertyInfo? ExactPropertyBinding(PropertyInfo[] match, Type? returnType, Type[]? types, ParameterModifier[]? modifiers)
  719. {
  720. if (match == null)
  721. throw new ArgumentNullException(nameof(match));
  722. PropertyInfo? bestMatch = null;
  723. int typesLength = (types != null) ? types.Length : 0;
  724. for (int i = 0; i < match.Length; i++)
  725. {
  726. ParameterInfo[] par = match[i].GetIndexParameters();
  727. int j;
  728. for (j = 0; j < typesLength; j++)
  729. {
  730. Type pCls = par[j].ParameterType;
  731. // If the classes exactly match continue
  732. if (pCls != types![j])
  733. break;
  734. }
  735. if (j < typesLength)
  736. continue;
  737. if (returnType != null && returnType != match[i].PropertyType)
  738. continue;
  739. if (bestMatch != null)
  740. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  741. bestMatch = match[i];
  742. }
  743. return bestMatch;
  744. }
  745. private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1, Type? paramArrayType1,
  746. ParameterInfo[] p2, int[] paramOrder2, Type? paramArrayType2,
  747. Type[] types, object?[]? args)
  748. {
  749. // A method using params is always less specific than one not using params
  750. if (paramArrayType1 != null && paramArrayType2 == null) return 2;
  751. if (paramArrayType2 != null && paramArrayType1 == null) return 1;
  752. // now either p1 and p2 both use params or neither does.
  753. bool p1Less = false;
  754. bool p2Less = false;
  755. for (int i = 0; i < types.Length; i++)
  756. {
  757. if (args != null && args[i] == Type.Missing)
  758. continue;
  759. Type c1, c2;
  760. // If a param array is present, then either
  761. // the user re-ordered the parameters in which case
  762. // the argument to the param array is either an array
  763. // in which case the params is conceptually ignored and so paramArrayType1 == null
  764. // or the argument to the param array is a single element
  765. // in which case paramOrder[i] == p1.Length - 1 for that element
  766. // or the user did not re-order the parameters in which case
  767. // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286)
  768. // so any index >= p.Length - 1 is being put in the param array
  769. if (paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1)
  770. c1 = paramArrayType1;
  771. else
  772. c1 = p1[paramOrder1[i]].ParameterType;
  773. if (paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1)
  774. c2 = paramArrayType2;
  775. else
  776. c2 = p2[paramOrder2[i]].ParameterType;
  777. if (c1 == c2) continue;
  778. switch (FindMostSpecificType(c1, c2, types[i]))
  779. {
  780. case 0: return 0;
  781. case 1: p1Less = true; break;
  782. case 2: p2Less = true; break;
  783. }
  784. }
  785. // Two way p1Less and p2Less can be equal. All the arguments are the
  786. // same they both equal false, otherwise there were things that both
  787. // were the most specific type on....
  788. if (p1Less == p2Less)
  789. {
  790. // if we cannot tell which is a better match based on parameter types (p1Less == p2Less),
  791. // let's see which one has the most matches without using the params array (the longer one wins).
  792. if (!p1Less && args != null)
  793. {
  794. if (p1.Length > p2.Length)
  795. {
  796. return 1;
  797. }
  798. else if (p2.Length > p1.Length)
  799. {
  800. return 2;
  801. }
  802. }
  803. return 0;
  804. }
  805. else
  806. {
  807. return (p1Less == true) ? 1 : 2;
  808. }
  809. }
  810. private static int FindMostSpecificType(Type c1, Type c2, Type? t)
  811. {
  812. // If the two types are exact move on...
  813. if (c1 == c2)
  814. return 0;
  815. if (t is SignatureType signatureType)
  816. {
  817. if (signatureType.MatchesExactly(c1))
  818. return 1;
  819. if (signatureType.MatchesExactly(c2))
  820. return 2;
  821. }
  822. else
  823. {
  824. if (c1 == t)
  825. return 1;
  826. if (c2 == t)
  827. return 2;
  828. }
  829. bool c1FromC2;
  830. bool c2FromC1;
  831. if (c1.IsByRef || c2.IsByRef)
  832. {
  833. if (c1.IsByRef && c2.IsByRef)
  834. {
  835. c1 = c1.GetElementType()!;
  836. c2 = c2.GetElementType()!;
  837. }
  838. else if (c1.IsByRef)
  839. {
  840. if (c1.GetElementType() == c2)
  841. return 2;
  842. c1 = c1.GetElementType()!;
  843. }
  844. else // if (c2.IsByRef)
  845. {
  846. if (c2.GetElementType() == c1)
  847. return 1;
  848. c2 = c2.GetElementType()!;
  849. }
  850. }
  851. if (c1.IsPrimitive && c2.IsPrimitive)
  852. {
  853. c1FromC2 = CanChangePrimitive(c2, c1);
  854. c2FromC1 = CanChangePrimitive(c1, c2);
  855. }
  856. else
  857. {
  858. c1FromC2 = c1.IsAssignableFrom(c2);
  859. c2FromC1 = c2.IsAssignableFrom(c1);
  860. }
  861. if (c1FromC2 == c2FromC1)
  862. return 0;
  863. if (c1FromC2)
  864. {
  865. return 2;
  866. }
  867. else
  868. {
  869. return 1;
  870. }
  871. }
  872. private static int FindMostSpecificMethod(MethodBase m1, int[] paramOrder1, Type? paramArrayType1,
  873. MethodBase m2, int[] paramOrder2, Type? paramArrayType2,
  874. Type[] types, object?[]? args)
  875. {
  876. // Find the most specific method based on the parameters.
  877. int res = FindMostSpecific(m1.GetParametersNoCopy(), paramOrder1, paramArrayType1,
  878. m2.GetParametersNoCopy(), paramOrder2, paramArrayType2, types, args);
  879. // If the match was not ambigous then return the result.
  880. if (res != 0)
  881. return res;
  882. // Check to see if the methods have the exact same name and signature.
  883. if (CompareMethodSig(m1, m2))
  884. {
  885. // Determine the depth of the declaring types for both methods.
  886. int hierarchyDepth1 = GetHierarchyDepth(m1.DeclaringType!);
  887. int hierarchyDepth2 = GetHierarchyDepth(m2.DeclaringType!);
  888. // The most derived method is the most specific one.
  889. if (hierarchyDepth1 == hierarchyDepth2)
  890. {
  891. return 0;
  892. }
  893. else if (hierarchyDepth1 < hierarchyDepth2)
  894. {
  895. return 2;
  896. }
  897. else
  898. {
  899. return 1;
  900. }
  901. }
  902. // The match is ambigous.
  903. return 0;
  904. }
  905. private static int FindMostSpecificField(FieldInfo cur1, FieldInfo cur2)
  906. {
  907. // Check to see if the fields have the same name.
  908. if (cur1.Name == cur2.Name)
  909. {
  910. int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType!);
  911. int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType!);
  912. if (hierarchyDepth1 == hierarchyDepth2)
  913. {
  914. Debug.Assert(cur1.IsStatic != cur2.IsStatic, "hierarchyDepth1 == hierarchyDepth2");
  915. return 0;
  916. }
  917. else if (hierarchyDepth1 < hierarchyDepth2)
  918. return 2;
  919. else
  920. return 1;
  921. }
  922. // The match is ambigous.
  923. return 0;
  924. }
  925. private static int FindMostSpecificProperty(PropertyInfo cur1, PropertyInfo cur2)
  926. {
  927. // Check to see if the fields have the same name.
  928. if (cur1.Name == cur2.Name)
  929. {
  930. int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType!);
  931. int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType!);
  932. if (hierarchyDepth1 == hierarchyDepth2)
  933. {
  934. return 0;
  935. }
  936. else if (hierarchyDepth1 < hierarchyDepth2)
  937. return 2;
  938. else
  939. return 1;
  940. }
  941. // The match is ambigous.
  942. return 0;
  943. }
  944. public static bool CompareMethodSig(MethodBase m1, MethodBase m2)
  945. {
  946. ParameterInfo[] params1 = m1.GetParametersNoCopy();
  947. ParameterInfo[] params2 = m2.GetParametersNoCopy();
  948. if (params1.Length != params2.Length)
  949. return false;
  950. int numParams = params1.Length;
  951. for (int i = 0; i < numParams; i++)
  952. {
  953. if (params1[i].ParameterType != params2[i].ParameterType)
  954. return false;
  955. }
  956. return true;
  957. }
  958. private static int GetHierarchyDepth(Type t)
  959. {
  960. int depth = 0;
  961. Type? currentType = t;
  962. do
  963. {
  964. depth++;
  965. currentType = currentType.BaseType;
  966. } while (currentType != null);
  967. return depth;
  968. }
  969. internal static MethodBase? FindMostDerivedNewSlotMeth(MethodBase[] match, int cMatches)
  970. {
  971. int deepestHierarchy = 0;
  972. MethodBase? methWithDeepestHierarchy = null;
  973. for (int i = 0; i < cMatches; i++)
  974. {
  975. // Calculate the depth of the hierarchy of the declaring type of the
  976. // current method.
  977. int currentHierarchyDepth = GetHierarchyDepth(match[i].DeclaringType!);
  978. // The two methods have the same name, signature, and hierarchy depth.
  979. // This can only happen if at least one is vararg or generic.
  980. if (currentHierarchyDepth == deepestHierarchy)
  981. {
  982. throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
  983. }
  984. // Check to see if this method is on the most derived class.
  985. if (currentHierarchyDepth > deepestHierarchy)
  986. {
  987. deepestHierarchy = currentHierarchyDepth;
  988. methWithDeepestHierarchy = match[i];
  989. }
  990. }
  991. return methWithDeepestHierarchy;
  992. }
  993. // This method will sort the vars array into the mapping order stored
  994. // in the paramOrder array.
  995. private static void ReorderParams(int[] paramOrder, object?[] vars)
  996. {
  997. object?[] varsCopy = new object[vars.Length];
  998. for (int i = 0; i < vars.Length; i++)
  999. varsCopy[i] = vars[i];
  1000. for (int i = 0; i < vars.Length; i++)
  1001. vars[i] = varsCopy[paramOrder[i]];
  1002. }
  1003. // This method will create the mapping between the Parameters and the underlying
  1004. // data based upon the names array. The names array is stored in the same order
  1005. // as the values and maps to the parameters of the method. We store the mapping
  1006. // from the parameters to the names in the paramOrder array. All parameters that
  1007. // don't have matching names are then stored in the array in order.
  1008. private static bool CreateParamOrder(int[] paramOrder, ParameterInfo[] pars, string[] names)
  1009. {
  1010. bool[] used = new bool[pars.Length];
  1011. // Mark which parameters have not been found in the names list
  1012. for (int i = 0; i < pars.Length; i++)
  1013. paramOrder[i] = -1;
  1014. // Find the parameters with names.
  1015. for (int i = 0; i < names.Length; i++)
  1016. {
  1017. int j;
  1018. for (j = 0; j < pars.Length; j++)
  1019. {
  1020. if (names[i].Equals(pars[j].Name))
  1021. {
  1022. paramOrder[j] = i;
  1023. used[i] = true;
  1024. break;
  1025. }
  1026. }
  1027. // This is an error condition. The name was not found. This
  1028. // method must not match what we sent.
  1029. if (j == pars.Length)
  1030. return false;
  1031. }
  1032. // Now we fill in the holes with the parameters that are unused.
  1033. int pos = 0;
  1034. for (int i = 0; i < pars.Length; i++)
  1035. {
  1036. if (paramOrder[i] == -1)
  1037. {
  1038. for (; pos < pars.Length; pos++)
  1039. {
  1040. if (!used[pos])
  1041. {
  1042. paramOrder[i] = pos;
  1043. pos++;
  1044. break;
  1045. }
  1046. }
  1047. }
  1048. }
  1049. return true;
  1050. }
  1051. // CanChangePrimitive
  1052. // This will determine if the source can be converted to the target type
  1053. internal static bool CanChangePrimitive(Type? source, Type? target)
  1054. {
  1055. if ((source == typeof(IntPtr) && target == typeof(IntPtr)) ||
  1056. (source == typeof(UIntPtr) && target == typeof(UIntPtr)))
  1057. return true;
  1058. Primitives widerCodes = s_primitiveConversions[(int)(Type.GetTypeCode(source))];
  1059. Primitives targetCode = (Primitives)(1 << (int)(Type.GetTypeCode(target)));
  1060. return (widerCodes & targetCode) != 0;
  1061. }
  1062. private static readonly Primitives[] s_primitiveConversions = {
  1063. /* Empty */ 0, // not primitive
  1064. /* Object */ 0, // not primitive
  1065. /* DBNull */ 0, // not primitive
  1066. /* Boolean */ Primitives.Boolean,
  1067. /* Char */ Primitives.Char | Primitives.UInt16 | Primitives.UInt32 | Primitives.Int32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
  1068. /* SByte */ Primitives.SByte | Primitives.Int16 | Primitives.Int32 | Primitives.Int64 | Primitives.Single | Primitives.Double,
  1069. /* Byte */ Primitives.Byte | Primitives.Char | Primitives.UInt16 | Primitives.Int16 | Primitives.UInt32 | Primitives.Int32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
  1070. /* Int16 */ Primitives.Int16 | Primitives.Int32 | Primitives.Int64 | Primitives.Single | Primitives.Double,
  1071. /* UInt16 */ Primitives.UInt16 | Primitives.UInt32 | Primitives.Int32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
  1072. /* Int32 */ Primitives.Int32 | Primitives.Int64 | Primitives.Single | Primitives.Double,
  1073. /* UInt32 */ Primitives.UInt32 | Primitives.UInt64 | Primitives.Int64 | Primitives.Single | Primitives.Double,
  1074. /* Int64 */ Primitives.Int64 | Primitives.Single | Primitives.Double,
  1075. /* UInt64 */ Primitives.UInt64 | Primitives.Single | Primitives.Double,
  1076. /* Single */ Primitives.Single | Primitives.Double,
  1077. /* Double */ Primitives.Double,
  1078. /* Decimal */ Primitives.Decimal,
  1079. /* DateTime */ Primitives.DateTime,
  1080. /* [Unused] */ 0,
  1081. /* String */ Primitives.String,
  1082. };
  1083. [Flags]
  1084. private enum Primitives
  1085. {
  1086. Boolean = 1 << TypeCode.Boolean,
  1087. Char = 1 << TypeCode.Char,
  1088. SByte = 1 << TypeCode.SByte,
  1089. Byte = 1 << TypeCode.Byte,
  1090. Int16 = 1 << TypeCode.Int16,
  1091. UInt16 = 1 << TypeCode.UInt16,
  1092. Int32 = 1 << TypeCode.Int32,
  1093. UInt32 = 1 << TypeCode.UInt32,
  1094. Int64 = 1 << TypeCode.Int64,
  1095. UInt64 = 1 << TypeCode.UInt64,
  1096. Single = 1 << TypeCode.Single,
  1097. Double = 1 << TypeCode.Double,
  1098. Decimal = 1 << TypeCode.Decimal,
  1099. DateTime = 1 << TypeCode.DateTime,
  1100. String = 1 << TypeCode.String,
  1101. }
  1102. internal class BinderState
  1103. {
  1104. internal readonly int[] _argsMap;
  1105. internal readonly int _originalSize;
  1106. internal readonly bool _isParamArray;
  1107. internal BinderState(int[] argsMap, int originalSize, bool isParamArray)
  1108. {
  1109. _argsMap = argsMap;
  1110. _originalSize = originalSize;
  1111. _isParamArray = isParamArray;
  1112. }
  1113. }
  1114. }
  1115. }