From db0a46cb688ac47a06067b07dfe30bc2b65ec369 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 21 May 2020 23:29:23 -0400 Subject: [PATCH] rewrite HarmonyMethod to allow null (#711) --- .../ModLoading/Framework/RewriteHelper.cs | 11 ++++- .../RewriteFacades/HarmonyMethodFacade.cs | 45 +++++++++++++++++++ .../Rewriters/Harmony1AssemblyRewriter.cs | 4 ++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs diff --git a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs index 91c9dec3..36058b86 100644 --- a/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs +++ b/src/SMAPI/Framework/ModLoading/Framework/RewriteHelper.cs @@ -137,7 +137,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework /// Get whether a method definition matches the signature expected by a method reference. /// The method definition. /// The method reference. - public static bool HasMatchingSignature(MethodInfo definition, MethodReference reference) + public static bool HasMatchingSignature(MethodBase definition, MethodReference reference) { // // duplicated by HasMatchingSignature(MethodDefinition, MethodReference) below @@ -166,7 +166,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework public static bool HasMatchingSignature(MethodDefinition definition, MethodReference reference) { // - // duplicated by HasMatchingSignature(MethodInfo, MethodReference) above + // duplicated by HasMatchingSignature(MethodBase, MethodReference) above // // same name @@ -191,6 +191,13 @@ namespace StardewModdingAPI.Framework.ModLoading.Framework /// The method reference. public static bool HasMatchingSignature(Type type, MethodReference reference) { + if (reference.Name == ".ctor") + { + return type + .GetConstructors(BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public) + .Any(method => RewriteHelper.HasMatchingSignature(method, reference)); + } + return type .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public) .Any(method => RewriteHelper.HasMatchingSignature(method, reference)); diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs new file mode 100644 index 00000000..44c97401 --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/HarmonyMethodFacade.cs @@ -0,0 +1,45 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using HarmonyLib; + +namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades +{ + /// Maps Harmony 1.x methods to Harmony 2.x to avoid breaking older mods. + /// This is public to support SMAPI rewriting and should not be referenced directly by mods. + [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")] + [SuppressMessage("ReSharper", "CS1591", Justification = "Documentation not needed for facade classes.")] + public class HarmonyMethodFacade : HarmonyMethod + { + /********* + ** Public methods + *********/ + public HarmonyMethodFacade(MethodInfo method) + { + this.ImportMethodImpl(method); + } + + public HarmonyMethodFacade(Type type, string name, Type[] parameters = null) + { + this.ImportMethodImpl(AccessTools.Method(type, name, parameters)); + } + + + /********* + ** Private methods + *********/ + /// Import a method directly using the internal HarmonyMethod code. + /// The method to import. + private void ImportMethodImpl(MethodInfo methodInfo) + { + // A null method is no longer allowed in the constructor with Harmony 2.0, but the + // internal code still handles null fine. For backwards compatibility, this bypasses + // the new restriction when the mod hasn't been updated for Harmony 2.0 yet. + + MethodInfo importMethod = typeof(HarmonyMethod).GetMethod("ImportMethod", BindingFlags.Instance | BindingFlags.NonPublic); + if (importMethod == null) + throw new InvalidOperationException("Can't find 'HarmonyMethod.ImportMethod' method"); + importMethod.Invoke(this, new object[] { methodInfo }); + } + } +} diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs index ce6417a8..8fed170a 100644 --- a/src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs +++ b/src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs @@ -92,6 +92,10 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters toType = typeof(AccessToolsFacade); break; + case "HarmonyLib.HarmonyMethod": + toType = typeof(HarmonyMethodFacade); + break; + default: return false; }