summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-07-28 18:03:49 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-07-28 18:03:49 -0400
commite8ad5d0a2404720ec4f8cc75117e62bcc72cd068 (patch)
tree34d02dd1d5c1da317bbb6b2279da6a4b2ed3a17c
parentb4f307e1ba88df072efed3b94975fa45789ae1ef (diff)
downloadSMAPI-e8ad5d0a2404720ec4f8cc75117e62bcc72cd068.tar.gz
SMAPI-e8ad5d0a2404720ec4f8cc75117e62bcc72cd068.tar.bz2
SMAPI-e8ad5d0a2404720ec4f8cc75117e62bcc72cd068.zip
fix Data\Movies error regression when patching dictionary (#711)
-rw-r--r--src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs101
1 files changed, 82 insertions, 19 deletions
diff --git a/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs b/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs
index 9f5819d7..9d63ab2c 100644
--- a/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs
+++ b/src/SMAPI/Framework/TemporaryHacks/MiniMonoModHotfix.cs
@@ -8,12 +8,12 @@
// Special thanks to 0x0ade for submitting this worokaround! Copy/pasted and adapted from MonoMod.
using System;
+using System.Reflection;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
-using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using HarmonyLib;
+using System.Reflection.Emit;
// ReSharper disable once CheckNamespace -- Temporary hotfix submitted by the MonoMod author.
namespace MonoMod.Utils
@@ -24,38 +24,38 @@ namespace MonoMod.Utils
{
// .NET Framework can break member ordering if using Module.Resolve* on certain members.
- private static readonly object[] _NoArgs = new object[0];
- private static readonly object[] _CacheGetterArgs = { /* MemberListType.All */ 0, /* name apparently always null? */ null };
+ private static object[] _NoArgs = new object[0];
+ private static object[] _CacheGetterArgs = { /* MemberListType.All */ 0, /* name apparently always null? */ null };
- private static readonly Type t_RuntimeModule =
+ private static Type t_RuntimeModule =
typeof(Module).Assembly
.GetType("System.Reflection.RuntimeModule");
- private static readonly PropertyInfo p_RuntimeModule_RuntimeType =
+ private static PropertyInfo p_RuntimeModule_RuntimeType =
typeof(Module).Assembly
.GetType("System.Reflection.RuntimeModule")
?.GetProperty("RuntimeType", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
- private static readonly Type t_RuntimeType =
+ private static Type t_RuntimeType =
typeof(Type).Assembly
.GetType("System.RuntimeType");
- private static readonly PropertyInfo p_RuntimeType_Cache =
+ private static PropertyInfo p_RuntimeType_Cache =
typeof(Type).Assembly
.GetType("System.RuntimeType")
?.GetProperty("Cache", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
- private static readonly MethodInfo m_RuntimeTypeCache_GetFieldList =
+ private static MethodInfo m_RuntimeTypeCache_GetFieldList =
typeof(Type).Assembly
.GetType("System.RuntimeType+RuntimeTypeCache")
?.GetMethod("GetFieldList", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
- private static readonly MethodInfo m_RuntimeTypeCache_GetPropertyList =
+ private static MethodInfo m_RuntimeTypeCache_GetPropertyList =
typeof(Type).Assembly
.GetType("System.RuntimeType+RuntimeTypeCache")
?.GetMethod("GetPropertyList", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
- private static readonly ConditionalWeakTable<Type, object> _CacheFixed = new ConditionalWeakTable<Type, object>();
+ private static readonly ConditionalWeakTable<Type, CacheFixEntry> _CacheFixed = new ConditionalWeakTable<Type, CacheFixEntry>();
public static void Apply()
{
@@ -143,24 +143,80 @@ namespace MonoMod.Utils
if (!t_RuntimeType.IsInstanceOfType(type))
continue;
- _CacheFixed.GetValue(type, rt =>
- {
+ CacheFixEntry entry = _CacheFixed.GetValue(type, rt => {
+ CacheFixEntry entryNew = new CacheFixEntry();
+ object cache;
+ Array properties, fields;
+
+ // All RuntimeTypes MUST have a cache, the getter is non-virtual, it creates on demand and asserts non-null.
+ entryNew.Cache = cache = p_RuntimeType_Cache.GetValue(rt, _NoArgs);
+ entryNew.Properties = properties = _GetArray(cache, m_RuntimeTypeCache_GetPropertyList);
+ entryNew.Fields = fields = _GetArray(cache, m_RuntimeTypeCache_GetFieldList);
- object cache = p_RuntimeType_Cache.GetValue(rt, _NoArgs);
- _FixReflectionCacheOrder<PropertyInfo>(cache, m_RuntimeTypeCache_GetPropertyList);
- _FixReflectionCacheOrder<FieldInfo>(cache, m_RuntimeTypeCache_GetFieldList);
+ _FixReflectionCacheOrder<PropertyInfo>(properties);
+ _FixReflectionCacheOrder<FieldInfo>(fields);
- return new object();
+ entryNew.NeedsVerify = false;
+ return entryNew;
});
+
+ if (entry.NeedsVerify && !_Verify(entry, type))
+ {
+ lock (entry)
+ {
+ _FixReflectionCacheOrder<PropertyInfo>(entry.Properties);
+ _FixReflectionCacheOrder<FieldInfo>(entry.Fields);
+ }
+ }
+
+ entry.NeedsVerify = true;
}
}
- private static void _FixReflectionCacheOrder<T>(object cache, MethodInfo getter) where T : MemberInfo
+ private static bool _Verify(CacheFixEntry entry, Type type)
+ {
+ object cache;
+ Array properties, fields;
+
+ // The cache can sometimes be invalidated.
+ // TODO: Figure out if only the arrays get replaced or if the entire cache object gets replaced!
+ if (entry.Cache != (cache = p_RuntimeType_Cache.GetValue(type, _NoArgs)))
+ {
+ entry.Cache = cache;
+ entry.Properties = _GetArray(cache, m_RuntimeTypeCache_GetPropertyList);
+ entry.Fields = _GetArray(cache, m_RuntimeTypeCache_GetFieldList);
+ return false;
+
+ }
+ else if (entry.Properties != (properties = _GetArray(cache, m_RuntimeTypeCache_GetPropertyList)))
+ {
+ entry.Properties = properties;
+ entry.Fields = _GetArray(cache, m_RuntimeTypeCache_GetFieldList);
+ return false;
+
+ }
+ else if (entry.Fields != (fields = _GetArray(cache, m_RuntimeTypeCache_GetFieldList)))
+ {
+ entry.Fields = fields;
+ return false;
+
+ }
+ else
+ {
+ // Cache should still be the same, no re-fix necessary.
+ return true;
+ }
+ }
+
+ private static Array _GetArray(object cache, MethodInfo getter)
{
// Get and discard once, otherwise we might not be getting the actual backing array.
getter.Invoke(cache, _CacheGetterArgs);
- Array orig = (Array)getter.Invoke(cache, _CacheGetterArgs);
+ return (Array)getter.Invoke(cache, _CacheGetterArgs);
+ }
+ private static void _FixReflectionCacheOrder<T>(Array orig) where T : MemberInfo
+ {
// Sort using a short-lived list.
List<T> list = new List<T>(orig.Length);
for (int i = 0; i < orig.Length; i++)
@@ -172,5 +228,12 @@ namespace MonoMod.Utils
orig.SetValue(list[i], i);
}
+ private class CacheFixEntry
+ {
+ public object Cache;
+ public Array Properties;
+ public Array Fields;
+ public bool NeedsVerify;
+ }
}
}