1. improve performance

This commit is contained in:
yangzhi 2023-07-10 15:20:12 +08:00
parent 4317b930aa
commit 0de3a3ad41
1 changed files with 49 additions and 27 deletions

View File

@ -4,10 +4,13 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using HarmonyLib; using HarmonyLib;
using Java.Util;
// using Microsoft.AppCenter.Crashes; // using Microsoft.AppCenter.Crashes;
using StardewModdingAPI.Framework; using StardewModdingAPI.Framework;
using StardewModdingAPI.Internal.Patching; using StardewModdingAPI.Internal.Patching;
@ -30,6 +33,10 @@ namespace StardewModdingAPI.Patches
/// <summary>An Instance of <see cref="Translator"/>.</summary> /// <summary>An Instance of <see cref="Translator"/>.</summary>
private static Translator Translator; private static Translator Translator;
private static PropertyInfo XmlTypeMappingObjectMapProperty = AccessTools.Property(typeof(XmlTypeMapping), "ObjectMap");
private static PropertyInfo EnumMapEnumNamesProperty = AccessTools.Property(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.EnumMap"), "EnumNames");
private static PropertyInfo EnumMapXmlNamesProperty = AccessTools.Property(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.EnumMap"), "XmlNames");
private static MethodInfo EnumMapGetXmlNameMethod = AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.EnumMap"), "GetXmlName");
/********* /*********
** Public methods ** Public methods
@ -57,7 +64,7 @@ namespace StardewModdingAPI.Patches
// ); // );
harmony.Patch( harmony.Patch(
original: AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.XmlSerializationReaderInterpreter"), "GetValueFromXmlString"), original: AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.XmlSerializationReaderInterpreter"), "GetValueFromXmlString"),
prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.XmlSerializationReaderInterpreter_PrefixGetValueFromXmlString)) transpiler: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.XmlSerializationReaderInterpreter_TranspileGetValueFromXmlString))
); );
harmony.Patch( harmony.Patch(
@ -179,17 +186,42 @@ namespace StardewModdingAPI.Patches
return null; return null;
} }
private static bool XmlSerializationReaderInterpreter_PrefixGetValueFromXmlString(string value, object typeData, ref object __result) private static IEnumerable<CodeInstruction> XmlSerializationReaderInterpreter_TranspileGetValueFromXmlString(ILGenerator gen, MethodBase original, IEnumerable<CodeInstruction> insns)
{ {
object schemaType = AccessTools.Property(typeData.GetType(), "SchemaType").GetValue(typeData); List<CodeInstruction> newInsns = new();
if (schemaType.ToString() == "Enum") foreach (var insn in insns)
{ {
if (value?.Length > 0) return true; if (insn.opcode == OpCodes.Bne_Un_S)
__result = null; {
return false; if (newInsns[newInsns.Count - 1].opcode == OpCodes.Ldc_I4_2)
} {
var lastIns = newInsns[newInsns.Count - 2];
if (lastIns.opcode == OpCodes.Callvirt && lastIns.operand is MethodInfo minfo && minfo.DeclaringType.FullName == "System.Xml.Serialization.TypeData" && minfo.Name == "get_SchemaType")
{
newInsns.Add(insn);
Label continueLabel = gen.DefineLabel();
Label retLabel = gen.DefineLabel();
newInsns.Add(new CodeInstruction(OpCodes.Ldarg_0));
newInsns.Add(new CodeInstruction(OpCodes.Brfalse_S, retLabel));
newInsns.Add(new CodeInstruction(OpCodes.Ldarg_0));
newInsns.Add(new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(string), nameof(string.Length))));
newInsns.Add(new CodeInstruction(OpCodes.Ldc_I4_0));
newInsns.Add(new CodeInstruction(OpCodes.Cgt));
newInsns.Add(new CodeInstruction(OpCodes.Brfalse_S, retLabel));
newInsns.Add(new CodeInstruction(OpCodes.Br_S, continueLabel));
return true; newInsns.Add(new CodeInstruction(OpCodes.Ldnull).WithLabels(retLabel));
newInsns.Add(new CodeInstruction(OpCodes.Ret));
CodeInstruction label = new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel);
newInsns.Add(label);
continue;
}
}
}
newInsns.Add(insn);
}
return newInsns;
} }
private static bool XmlSerializationReaderInterpreter_PrefixAddListValue(object listType, ref object list, int index, object value, bool canCreateInstance) private static bool XmlSerializationReaderInterpreter_PrefixAddListValue(object listType, ref object list, int index, object value, bool canCreateInstance)
@ -209,12 +241,12 @@ namespace StardewModdingAPI.Patches
{ {
if (listItemType.IsEnum && value != null) if (listItemType.IsEnum && value != null)
type.GetMethod("Add", new[] { typeof(string) }).Invoke(list, new[] { value.ToString() }); type.GetMethod("Add", new[] { typeof(string) }).Invoke(list, new[] { value.ToString() });
else else if(!listItemType.IsEnum)
type.GetMethod("Add", new[] { listItemType }).Invoke(list, new[] { value }); type.GetMethod("Add", new[] { listItemType }).Invoke(list, new[] { value });
} }
catch (Exception e) catch (Exception e)
{ {
e.GetType(); SaveGamePatch.Monitor.Log($"AddListValue error: {e}");
} }
return false; return false;
@ -228,11 +260,11 @@ namespace StardewModdingAPI.Patches
return false; return false;
} }
object objectMap = AccessTools.Property(typeMap.GetType(), "ObjectMap").GetValue(typeMap); object objectMap = SaveGamePatch.XmlTypeMappingObjectMapProperty.GetValue(typeMap);
if (ob is string enumString) if (ob is string enumString)
{ {
string[] enumNames = (string[])AccessTools.Property(objectMap.GetType(), "EnumNames").GetValue(objectMap); string[] enumNames = (string[])SaveGamePatch.EnumMapEnumNamesProperty.GetValue(objectMap);
string[] xmlNames = (string[])AccessTools.Property(objectMap.GetType(), "XmlNames").GetValue(objectMap); string[] xmlNames = (string[])SaveGamePatch.EnumMapXmlNamesProperty.GetValue(objectMap);
for (int i = 0; i < enumNames.Length; i++) for (int i = 0; i < enumNames.Length; i++)
if (enumString == enumNames[i]) if (enumString == enumNames[i])
{ {
@ -243,7 +275,7 @@ namespace StardewModdingAPI.Patches
__result = null; __result = null;
} }
else else
__result = (string)AccessTools.Method(objectMap.GetType(), "GetXmlName").Invoke(objectMap, new[] { typeMap.TypeFullName, ob }); __result = (string)SaveGamePatch.EnumMapGetXmlNameMethod.Invoke(objectMap, new[] { typeMap.TypeFullName, ob });
return false; return false;
} }
@ -262,6 +294,8 @@ namespace StardewModdingAPI.Patches
bool isRewrite = name == "StardewValley.Network.NetIntDictionary<System.Int32,Netcode.NetInt>" || bool isRewrite = name == "StardewValley.Network.NetIntDictionary<System.Int32,Netcode.NetInt>" ||
name == "StardewValley.Network.NetStringDictionary<System.String,Netcode.NetString>"; name == "StardewValley.Network.NetStringDictionary<System.String,Netcode.NetString>";
if (!isRewrite) return true;
Type runtimeType = (Type)AccessTools.Field(__instance.GetType(), "type").GetValue(__instance); Type runtimeType = (Type)AccessTools.Field(__instance.GetType(), "type").GetValue(__instance);
if (runtimeType == null) throw new InvalidOperationException("Property ListItemType is not supported for custom types"); if (runtimeType == null) throw new InvalidOperationException("Property ListItemType is not supported for custom types");
FieldInfo listItemTypeField = AccessTools.Field(__instance.GetType(), "listItemType"); FieldInfo listItemTypeField = AccessTools.Field(__instance.GetType(), "listItemType");
@ -273,7 +307,6 @@ namespace StardewModdingAPI.Patches
} }
Type type = null; Type type = null;
MethodInfo GetGenericListItemType = AccessTools.Method(__instance.GetType(), "GetGenericListItemType", new[] { typeof(Type) });
if (runtimeType.IsArray) if (runtimeType.IsArray)
{ {
listItemType = runtimeType.GetElementType(); listItemType = runtimeType.GetElementType();
@ -292,17 +325,6 @@ namespace StardewModdingAPI.Patches
"To be XML serializable, types which inherit from {0} must have an implementation of Add({1}) at all levels of their inheritance hierarchy. {2} does not implement Add({1}).", "To be XML serializable, types which inherit from {0} must have an implementation of Add({1}) at all levels of their inheritance hierarchy. {2} does not implement Add({1}).",
"ICollection", listItemType.FullName, type.FullName)); "ICollection", listItemType.FullName, type.FullName));
} }
else if (!isRewrite && (type = (Type)GetGenericListItemType.Invoke(null, new[] { runtimeType })) != null)
{
if (typeof(IDictionary<object, object>).IsAssignableFrom(runtimeType)) throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "The type {0} is not supported because it implements IDictionary.", runtimeType.FullName));
listItemType = type;
listItemTypeField.SetValue(__instance, listItemType);
if (runtimeType.GetMethod("Add", new[] { listItemType }) == null)
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
"To be XML serializable, types which inherit from {0} must have an implementation of Add({1}) at all levels of their inheritance hierarchy. {2} does not implement Add({1}).",
"ICollection", listItemType.FullName, type.FullName));
}
else else
{ {
MethodInfo methodInfo = runtimeType.GetMethod("GetEnumerator", Type.EmptyTypes); MethodInfo methodInfo = runtimeType.GetMethod("GetEnumerator", Type.EmptyTypes);