From 2f5c1711f25d8ed4011be2d867800abf088af57b Mon Sep 17 00:00:00 2001 From: Mhburg Date: Fri, 25 Sep 2020 09:48:08 -0400 Subject: [PATCH] Add handler cache for MakeDeepCopy --- Harmony/Tools/AccessTools.cs | 58 ++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/Harmony/Tools/AccessTools.cs b/Harmony/Tools/AccessTools.cs index 69ee1303..bef98965 100644 --- a/Harmony/Tools/AccessTools.cs +++ b/Harmony/Tools/AccessTools.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Reflection.Emit; using System.Runtime.Serialization; +using System.Threading; namespace HarmonyLib { @@ -15,6 +16,13 @@ namespace HarmonyLib /// public static class AccessTools { + /// + /// A cache for the or similar Add methods for different types. + /// + private static readonly Dictionary handlerCache = new Dictionary(); + + private static readonly ReaderWriterLockSlim handlerCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + /// Shortcut for to simplify the use of reflections and make it work for any access level /// // Note: This should a be const, but changing from static (readonly) to const breaks binary compatibility. @@ -1687,25 +1695,45 @@ public static object MakeDeepCopy(object source, Type resultType, Func m.Name == "Add" && m.GetParameters().Count() == 1); - if (addOperation is object) + handlerCacheLock.EnterUpgradeableReadLock(); + try { - var addableResult = Activator.CreateInstance(resultType); - var addInvoker = MethodInvoker.GetHandler(addOperation); - var newElementType = resultType.GetGenericArguments()[0]; - var i = 0; - foreach (var element in source as IEnumerable) + if (!handlerCache.TryGetValue(resultType, out FastInvokeHandler addInvoker)) + { + var addOperation = FirstMethod(resultType, m => m.Name == "Add" && m.GetParameters().Length == 1); + if (addOperation is object) + { + addInvoker = MethodInvoker.GetHandler(addOperation); + } + handlerCacheLock.EnterWriteLock(); + try + { + handlerCache[resultType] = addInvoker; + } + finally + { + handlerCacheLock.ExitWriteLock(); + } + } + if (addInvoker != null) { - var iStr = (i++).ToString(); - var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; - var newElement = MakeDeepCopy(element, newElementType, processor, path); - _ = addInvoker(addableResult, new object[] { newElement }); + var addableResult = Activator.CreateInstance(resultType); + var newElementType = resultType.GetGenericArguments()[0]; + var i = 0; + foreach (var element in source as IEnumerable) + { + var iStr = (i++).ToString(); + var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; + var newElement = MakeDeepCopy(element, newElementType, processor, path); + _ = addInvoker(addableResult, new object[] { newElement }); + } + return addableResult; } - return addableResult; } - - // TODO: add dictionaries support - // maybe use methods in Dictionary> + finally + { + handlerCacheLock.ExitUpgradeableReadLock(); + } } if (type.IsArray && resultType.IsArray)