diff --git a/Directory.Build.props b/Directory.Build.props index 7c78cd3d..2cbffd1a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 21.1.12.5 + 21.6.21.1 diff --git a/Harmony/Harmony.csproj b/Harmony/Harmony.csproj index 7b098527..6282c8d9 100644 --- a/Harmony/Harmony.csproj +++ b/Harmony/Harmony.csproj @@ -11,13 +11,13 @@ Andreas Pardeike 0Harmony true - 2.0.5.0 + 2.1.0.0 LICENSE https://github.com/pardeike/Harmony false Harmony,Mono,Patch,Patching,Runtime,Detour,Detours,Aspect,Aspects - 2.0.5.0 - 2.0.5.0 + 2.1.0.0 + 2.1.0.0 HarmonyLogo.png https://raw.githubusercontent.com/pardeike/Harmony/master/HarmonyLogo.png true diff --git a/Harmony/Internal/MethodPatcher.cs b/Harmony/Internal/MethodPatcher.cs index 411c812b..362019fa 100644 --- a/Harmony/Internal/MethodPatcher.cs +++ b/Harmony/Internal/MethodPatcher.cs @@ -17,6 +17,7 @@ internal class MethodPatcher const string RESULT_VAR = "__result"; const string STATE_VAR = "__state"; const string EXCEPTION_VAR = "__exception"; + const string RUN_ORIGINA_VAR = "__runOriginal"; const string PARAM_INDEX_PREFIX = "__"; const string INSTANCE_FIELD_PREFIX = "___"; @@ -92,14 +93,14 @@ internal MethodInfo CreateReplacement(out Dictionary final prefixes.Union(postfixes).Union(finalizers).ToList().ForEach(fix => { - if (fix.DeclaringType is object && privateVars.ContainsKey(fix.DeclaringType.FullName) is false) + if (fix.DeclaringType is object && privateVars.ContainsKey(fix.DeclaringType.AssemblyQualifiedName) is false) { fix.GetParameters() .Where(patchParam => patchParam.Name == STATE_VAR) .Do(patchParam => { var privateStateVariable = DeclareLocalVariable(patchParam.ParameterType); - privateVars[fix.DeclaringType.FullName] = privateStateVariable; + privateVars[fix.DeclaringType.AssemblyQualifiedName] = privateStateVariable; }); } }); @@ -358,7 +359,7 @@ bool EmitOriginalBaseMethod() return true; } - void EmitCallParameter(MethodInfo patch, Dictionary variables, bool allowFirsParamPassthrough, out LocalBuilder tmpObjectVar, List> tmpBoxVars) + void EmitCallParameter(MethodInfo patch, Dictionary variables, LocalBuilder runOriginalVariable, bool allowFirsParamPassthrough, out LocalBuilder tmpObjectVar, List> tmpBoxVars) { tmpObjectVar = null; var isInstance = original.IsStatic is false; @@ -381,6 +382,15 @@ void EmitCallParameter(MethodInfo patch, Dictionary variab continue; } + if (patchParam.Name == RUN_ORIGINA_VAR) + { + if (runOriginalVariable != null) + emitter.Emit(OpCodes.Ldloc, runOriginalVariable); + else + emitter.Emit(OpCodes.Ldc_I4_0); + continue; + } + if (patchParam.Name == INSTANCE_PARAM) { if (original.IsStatic) @@ -415,13 +425,13 @@ void EmitCallParameter(MethodInfo patch, Dictionary variab // field access by index only works for declared fields fieldInfo = AccessTools.DeclaredField(original.DeclaringType, int.Parse(fieldName)); if (fieldInfo is null) - throw new ArgumentException($"No field found at given index in class {original.DeclaringType.FullName}", fieldName); + throw new ArgumentException($"No field found at given index in class {original.DeclaringType.AssemblyQualifiedName}", fieldName); } else { fieldInfo = AccessTools.Field(original.DeclaringType, fieldName); if (fieldInfo is null) - throw new ArgumentException($"No such field defined in class {original.DeclaringType.FullName}", fieldName); + throw new ArgumentException($"No such field defined in class {original.DeclaringType.AssemblyQualifiedName}", fieldName); } if (fieldInfo.IsStatic) @@ -438,7 +448,7 @@ void EmitCallParameter(MethodInfo patch, Dictionary variab if (patchParam.Name == STATE_VAR) { var ldlocCode = patchParam.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc; - if (variables.TryGetValue(patch.DeclaringType.FullName, out var stateVar)) + if (variables.TryGetValue(patch.DeclaringType.AssemblyQualifiedName, out var stateVar)) emitter.Emit(ldlocCode, stateVar); else emitter.Emit(OpCodes.Ldnull); @@ -642,7 +652,7 @@ void AddPrefixes(Dictionary variables, LocalBuilder runOri } var tmpBoxVars = new List>(); - EmitCallParameter(fix, variables, false, out var tmpObjectVar, tmpBoxVars); + EmitCallParameter(fix, variables, runOriginalVariable, false, out var tmpObjectVar, tmpBoxVars); emitter.Emit(OpCodes.Call, fix); if (tmpObjectVar != null) { @@ -685,7 +695,7 @@ bool AddPostfixes(Dictionary variables, bool passthroughPa throw new Exception("Methods without body cannot have postfixes. Use a transpiler instead."); var tmpBoxVars = new List>(); - EmitCallParameter(fix, variables, true, out var tmpObjectVar, tmpBoxVars); + EmitCallParameter(fix, variables, null, true, out var tmpObjectVar, tmpBoxVars); emitter.Emit(OpCodes.Call, fix); if (tmpObjectVar != null) { @@ -732,7 +742,7 @@ bool AddFinalizers(Dictionary variables, bool catchExcepti emitter.MarkBlockBefore(new ExceptionBlock(ExceptionBlockType.BeginExceptionBlock), out var label); var tmpBoxVars = new List>(); - EmitCallParameter(fix, variables, false, out var tmpObjectVar, tmpBoxVars); + EmitCallParameter(fix, variables, null, false, out var tmpObjectVar, tmpBoxVars); emitter.Emit(OpCodes.Call, fix); if (tmpObjectVar != null) { diff --git a/Harmony/Public/Harmony.cs b/Harmony/Public/Harmony.cs index d0c7f87f..e1f63809 100644 --- a/Harmony/Public/Harmony.cs +++ b/Harmony/Public/Harmony.cs @@ -1,3 +1,4 @@ +using MonoMod.Utils; using System; using System.Collections.Generic; using System.Diagnostics; @@ -50,7 +51,9 @@ public Harmony(string id) #if !NET5_0 if (string.IsNullOrEmpty(location)) location = new Uri(assembly.CodeBase).LocalPath; #endif - FileLog.Log($"### Harmony id={id}, version={version}, location={location}, env/clr={environment}, platform={platform}"); + var ptr_runtime = IntPtr.Size; + var ptr_env = PlatformHelper.Current; + FileLog.Log($"### Harmony id={id}, version={version}, location={location}, env/clr={environment}, platform={platform}, ptrsize:runtime/env={ptr_runtime}/{ptr_env}"); var callingMethod = AccessTools.GetOutsideCaller(); if (callingMethod.DeclaringType is object) { @@ -68,6 +71,7 @@ public Harmony(string id) } /// Searches the current assembly for Harmony annotations and uses them to create patches + /// This method can fail to use the correct assembly when being inlined. It calls StackTrace.GetFrame(1) which can point to the wrong method/assembly. If you are unsure or run into problems, use PatchAll(Assembly.GetExecutingAssembly()) instead. /// public void PatchAll() { diff --git a/HarmonyTests/Patching/Assets/PatchClasses.cs b/HarmonyTests/Patching/Assets/PatchClasses.cs index 94082c1b..13605e42 100644 --- a/HarmonyTests/Patching/Assets/PatchClasses.cs +++ b/HarmonyTests/Patching/Assets/PatchClasses.cs @@ -1129,6 +1129,35 @@ public static void Postfix(ref object __result) } } + public class Class22 + { + public static bool? bool1 = null; + public static bool? bool2 = null; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Method22() + { + try + { + } + catch + { + throw; + } + } + + public static bool Prefix1(bool __runOriginal) + { + bool1 = __runOriginal; + return false; + } + + public static void Prefix2(bool __runOriginal) + { + bool2 = __runOriginal; + } + } + public class InjectDelegateBaseClass { public string pre; diff --git a/HarmonyTests/Patching/StaticPatches.cs b/HarmonyTests/Patching/StaticPatches.cs index bb962993..e77b76c7 100644 --- a/HarmonyTests/Patching/StaticPatches.cs +++ b/HarmonyTests/Patching/StaticPatches.cs @@ -402,5 +402,34 @@ public void Test_Class18() Assert.IsTrue(Class18Patch.prefixExecuted, "prefixExecuted"); Assert.AreEqual((float)1, color.r); } + + [Test] + public void Test_Class22() + { + var instance = new Harmony("test"); + Assert.NotNull(instance, "instance"); + + var original = SymbolExtensions.GetMethodInfo(() => Class22.Method22()); + Assert.NotNull(original, "original"); + var prefix1 = SymbolExtensions.GetMethodInfo(() => Class22.Prefix1(false)); + Assert.NotNull(original, "prefix1"); + var prefix2 = SymbolExtensions.GetMethodInfo(() => Class22.Prefix2(false)); + Assert.NotNull(original, "prefix2"); + + var patched1 = instance.Patch(original, new HarmonyMethod(prefix1)); + Assert.NotNull(patched1, "patched1"); + + var patched2 = instance.Patch(original, new HarmonyMethod(prefix2)); + Assert.NotNull(patched2, "patched2"); + + Class22.bool1 = null; + Class22.bool2 = null; + Class22.Method22(); + + Assert.NotNull(Class22.bool1, "Class22.bool1"); + Assert.NotNull(Class22.bool2, "Class22.bool2"); + Assert.IsTrue(Class22.bool1.Value, "Class22.bool1.Value"); + Assert.IsFalse(Class22.bool2.Value, "Class22.bool2.Value"); + } } }