ExtensionMethodCache.cs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Threading;
  6. using Jint.Extensions;
  7. namespace Jint.Runtime.Interop.Reflection
  8. {
  9. /// <summary>
  10. /// A extension method lookup that can be shared between engines, build based on extension methods provided via options.
  11. /// </summary>
  12. internal class ExtensionMethodCache
  13. {
  14. internal static readonly ExtensionMethodCache Empty = new(new Dictionary<Type, MethodInfo[]>());
  15. // starting point containing only extension methods targeting one type, based on given options configuration
  16. private readonly Dictionary<Type, MethodInfo[]> _allExtensionMethods;
  17. // cache of all possibilities for type including base types and implemented interfaces
  18. private Dictionary<Type, MethodInfo[]> _extensionMethods = new();
  19. private ExtensionMethodCache(Dictionary<Type, MethodInfo[]> extensionMethods)
  20. {
  21. _allExtensionMethods = extensionMethods;
  22. }
  23. internal static ExtensionMethodCache Build(List<Type> extensionMethodContainerTypes)
  24. {
  25. var methodsByTarget = extensionMethodContainerTypes
  26. .SelectMany(x => x.GetExtensionMethods())
  27. .GroupBy(x => x.GetParameters()[0].ParameterType)
  28. .ToDictionary(x => x.Key, x => x.ToArray());
  29. return new ExtensionMethodCache(methodsByTarget);
  30. }
  31. public bool HasMethods => _allExtensionMethods.Count > 0;
  32. public bool TryGetExtensionMethods(Type objectType, out MethodInfo[] methods)
  33. {
  34. var methodLookup = _extensionMethods;
  35. if (methodLookup.TryGetValue(objectType, out methods))
  36. {
  37. return true;
  38. }
  39. var results = new List<MethodInfo>();
  40. if (_allExtensionMethods.TryGetValue(objectType, out var ownExtensions))
  41. {
  42. results.AddRange(ownExtensions);
  43. }
  44. foreach (var parentType in GetParentTypes(objectType))
  45. {
  46. if (_allExtensionMethods.TryGetValue(parentType, out var parentExtensions))
  47. {
  48. results.AddRange(parentExtensions);
  49. }
  50. }
  51. methods = results.ToArray();
  52. // racy, we don't care, worst case we'll catch up later
  53. Interlocked.CompareExchange(ref _extensionMethods, new Dictionary<Type, MethodInfo[]>(methodLookup)
  54. {
  55. [objectType] = methods
  56. }, methodLookup);
  57. return methods.Length > 0;
  58. }
  59. private static IEnumerable<Type> GetParentTypes(Type type)
  60. {
  61. // is there any base type?
  62. if (type == null)
  63. {
  64. yield break;
  65. }
  66. // return all implemented or inherited interfaces
  67. foreach (var i in type.GetInterfaces())
  68. {
  69. yield return i;
  70. }
  71. // return all inherited types
  72. var currentBaseType = type.BaseType;
  73. while (currentBaseType != null)
  74. {
  75. yield return currentBaseType;
  76. currentBaseType = currentBaseType.BaseType;
  77. }
  78. }
  79. }
  80. }