BepInEx / HarmonyX

Harmony built on top of MonoMod.RuntimeDetours with additional features
MIT License
367 stars 44 forks source link

Could not load file or assembly System.Reflection.Emit.ILGeneration during Transpile #37

Closed fritzr closed 2 years ago

fritzr commented 3 years ago

Describe the bug When Transpiling using BepInEx and HarmonyX, I encounter an error at runtime complaining that System.Reflection.Emit.ILGeneration could not be found near the use of LocalBuilder and CodeInstruction.IsStLoc.

To Reproduce Steps to reproduce the behavior:

  1. The original method and its signature and class:
namespace Assets.Scripts.Objects.Items
{
    public class Stackable : Item, IQuantity
    {
        public virtual void SplitStack(Interaction interaction, int quantity);
    }
}
  1. The patch code:
    using Assets.Scripts.Objects.Items;
    [HarmonyPatch(typeof(Stackable), "SplitStack")]
    public class Stackable_SplitStack
    {
        static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> insnsIter)
        {
            foreach(var insn in insnsIter)
            {
                if (insn.IsStloc())
                {
                    if (insn.operand is LocalBuilder stackableVar)
                    {
                        ...
  1. The output of the Harmony debug log

See below

  1. The stacktrace or other errors:
[Error  :  HarmonyX] Failed to patch virtual void Assets.Scripts.Objects.Items.Stackable::SplitStack(Assets.Scripts.Objects.Interaction interaction, int quantity): System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeLoadException: Could not load type of field 'MyMod.Stackable_SplitStack+<Transpiler>d__1:<stackableVar>5__5' (9) due to: Could not load file or assembly 'System.Reflection.Emit.ILGeneration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. assembly:System.Reflection.Emit.ILGeneration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a type:<unknown type> member:(null) signature:<none>
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <695d1cc93cca45069c528c15c9fdd749>:0 
   --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00048] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at HarmonyLib.Internal.Patching.ILManipulator.ApplyTranspilers (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Func`2[T,TResult] getLocal, System.Func`1[TResult] defineLabel) [0x00093] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Internal.Patching.ILManipulator.WriteTo (Mono.Cecil.Cil.MethodBody body, System.Reflection.MethodBase original) [0x00066] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteTranspiledMethod (MonoMod.Cil.ILContext ctx, System.Reflection.MethodBase original, System.Collections.Generic.List`1[T] transpilers, System.Boolean debug) [0x00073] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.MakePatched (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx, System.Collections.Generic.List`1[T] prefixes, System.Collections.Generic.List`1[T] postfixes, System.Collections.Generic.List`1[T] transpilers, System.Collections.Generic.List`1[T] finalizers, System.Collections.Generic.List`1[T] ilmanipulators, System.Boolean debug) [0x0003b] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
TypeLoadException: Could not load type of field 'MyMod.Stackable_SplitStack+<Transpiler>d__1:<stackableVar>5__5' (9) due to: Could not load file or assembly 'System.Reflection.Emit.ILGeneration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. assembly:System.Reflection.Emit.ILGeneration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a type:<unknown type> member:(null) signature:<none>
  at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in <695d1cc93cca45069c528c15c9fdd749>:0 
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
  at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00048] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <695d1cc93cca45069c528c15c9fdd749>:0 
  at HarmonyLib.Internal.Patching.ILManipulator.ApplyTranspilers (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Func`2[T,TResult] getLocal, System.Func`1[TResult] defineLabel) [0x00093] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Internal.Patching.ILManipulator.WriteTo (Mono.Cecil.Cil.MethodBody body, System.Reflection.MethodBase original) [0x00066] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteTranspiledMethod (MonoMod.Cil.ILContext ctx, System.Reflection.MethodBase original, System.Collections.Generic.List`1[T] transpilers, System.Boolean debug) [0x00073] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.MakePatched (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx, System.Collections.Generic.List`1[T] prefixes, System.Collections.Generic.List`1[T] postfixes, System.Collections.Generic.List`1[T] transpilers, System.Collections.Generic.List`1[T] finalizers, System.Collections.Generic.List`1[T] ilmanipulators, System.Boolean debug) [0x0003b] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
Rethrow as HarmonyException: IL Compile Error (unknown location)
  at HarmonyLib.Public.Patching.HarmonyManipulator.MakePatched (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx, System.Collections.Generic.List`1[T] prefixes, System.Collections.Generic.List`1[T] postfixes, System.Collections.Generic.List`1[T] transpilers, System.Collections.Generic.List`1[T] finalizers, System.Collections.Generic.List`1[T] ilmanipulators, System.Boolean debug) [0x002a8] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.Manipulate (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo, MonoMod.Cil.ILContext ctx) [0x00051] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.Manipulator (MonoMod.Cil.ILContext ctx) [0x0001d] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at MonoMod.Cil.ILContext.Invoke (MonoMod.Cil.ILContext+Manipulator manip) [0x00087] in <5be58f3c80ca41c4960cf35eb47d4341>:0 
  at MonoMod.RuntimeDetour.ILHook+Context.InvokeManipulator (Mono.Cecil.MethodDefinition def, MonoMod.Cil.ILContext+Manipulator cb) [0x00012] in <a3e7db5d9f924acea1a3fada3479a63b>:0 
  at (wrapper dynamic-method) MonoMod.RuntimeDetour.ILHook+Context.DMD<MonoMod.RuntimeDetour.ILHook+Context::Refresh>(MonoMod.RuntimeDetour.ILHook/Context)
  at (wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.Trampoline<MonoMod.RuntimeDetour.ILHook+Context::Refresh>?274797568(object)
  at HarmonyLib.Internal.RuntimeFixes.StackTraceFixes.OnILChainRefresh (System.Object self) [0x00000] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at MonoMod.RuntimeDetour.ILHook.Apply () [0x00059] in <a3e7db5d9f924acea1a3fada3479a63b>:0 
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) [0x00047] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
Rethrow as HarmonyException: IL Compile Error (unknown location)
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) [0x0005f] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo) [0x00033] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
Rethrow as HarmonyException: IL Compile Error (unknown location)
  at HarmonyLib.PatchClassProcessor.ReportException (System.Exception exception, System.Reflection.MethodBase original) [0x00045] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.PatchClassProcessor.Patch () [0x00084] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Harmony.<PatchAll>b__11_0 (System.Type type) [0x00007] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.CollectionExtensions.Do[T] (System.Collections.Generic.IEnumerable`1[T] sequence, System.Action`1[T] action) [0x00014] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Harmony.PatchAll (System.Reflection.Assembly assembly) [0x00006] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at HarmonyLib.Harmony.PatchAll () [0x0001b] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0 
  at MyMod.Plugin.Awake () [0x00029] in <db3587d50c3340b9b774b44a545dc379>:0 

Expected behavior I expect the above use of LocalBuilder and CodeInstruction.IsStLoc to transpile without error.

Build environment:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <Configurations>Debug;Release;Debug-Deploy</Configurations>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="BepInEx.Analyzers" Version="1.*" PrivateAssets="all" />
    <PackageReference Include="BepInEx.Core" Version="5.*" />
    <PackageReference Include="UnityEngine.Modules" Version="2021.2.1" IncludeAssets="compile" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="Assembly-CSharp">
      <HintPath>Libs\Assembly-CSharp.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>

Runtime environment:

Additional context After experiencing this error, I assumed the game's runtime did not provide the System.Reflection.Emit.ILGeneration library, so I tried downloading it from Nuget (System.Reflection.Emit.ILGeneration==4.7.0) and stashing the ILGeneration DLL along with the output DLL under BepInEx\plugins. Then I received a new "missing method exception" for the usage of IsStloc, shown below.

[Error  :  HarmonyX] Failed to patch virtual void Assets.Scripts.Objects.Items.Stackable::SplitStack(Assets.Scripts.Objects.Interaction interaction, int quantity): System.MissingMethodException: bool HarmonyLib.CodeInstructionExtensions.IsStloc(HarmonyLib.CodeInstruction,System.Reflection.Emit.LocalBuilder)
  at HarmonyLib.Internal.Patching.ILManipulator+<NormalizeInstructions>d__23.MoveNext () [0x000ab] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0
  at System.Collections.Generic.List`1[T]..ctor (System.Collections.Generic.IEnumerable`1[T] collection) [0x00077] in <695d1cc93cca45069c528c15c9fdd749>:0
  at System.Linq.Enumerable.ToList[TSource] (System.Collections.Generic.IEnumerable`1[T] source) [0x00018] in <351e49e2a5bf4fd6beabb458ce2255f3>:0
  at HarmonyLib.Internal.Patching.ILManipulator.ApplyTranspilers (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Func`2[T,TResult] getLocal, System.Func`1[TResult] defineLabel) [0x000ab] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0
  at HarmonyLib.Internal.Patching.ILManipulator.WriteTo (Mono.Cecil.Cil.MethodBody body, System.Reflection.MethodBase original) [0x00066] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteTranspiledMethod (MonoMod.Cil.ILContext ctx, System.Reflection.MethodBase original, System.Collections.Generic.List`1[T] transpilers, System.Boolean debug) [0x00073] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0
  at HarmonyLib.Public.Patching.HarmonyManipulator.MakePatched (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx, System.Collections.Generic.List`1[T] prefixes, System.Collections.Generic.List`1[T] postfixes, System.Collections.Generic.List`1[T] transpilers, System.Collections.Generic.List`1[T] finalizers, System.Collections.Generic.List`1[T] ilmanipulators, System.Boolean debug) [0x0003b] in <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0
[Error  : Unity Log] MissingMethodException: bool HarmonyLib.CodeInstructionExtensions.IsStloc(HarmonyLib.CodeInstruction,System.Reflection.Emit.LocalBuilder)
Stack trace:
HarmonyLib.Internal.Patching.ILManipulator+<NormalizeInstructions>d__23.MoveNext () (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
System.Collections.Generic.List`1[T]..ctor (System.Collections.Generic.IEnumerable`1[T] collection) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Linq.Enumerable.ToList[TSource] (System.Collections.Generic.IEnumerable`1[T] source) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
HarmonyLib.Internal.Patching.ILManipulator.ApplyTranspilers (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Func`2[T,TResult] getLocal, System.Func`1[TResult] defineLabel) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Internal.Patching.ILManipulator.WriteTo (Mono.Cecil.Cil.MethodBody body, System.Reflection.MethodBase original) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Public.Patching.HarmonyManipulator.WriteTranspiledMethod (MonoMod.Cil.ILContext ctx, System.Reflection.MethodBase original, System.Collections.Generic.List`1[T] transpilers, System.Boolean debug) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Public.Patching.HarmonyManipulator.MakePatched (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx, System.Collections.Generic.List`1[T] prefixes, System.Collections.Generic.List`1[T] postfixes, System.Collections.Generic.List`1[T] transpilers, System.Collections.Generic.List`1[T] finalizers, System.Collections.Generic.List`1[T] ilmanipulators, System.Boolean debug) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
Rethrow as HarmonyException: IL Compile Error (unknown location)
HarmonyLib.Public.Patching.HarmonyManipulator.MakePatched (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx, System.Collections.Generic.List`1[T] prefixes, System.Collections.Generic.List`1[T] postfixes, System.Collections.Generic.List`1[T] transpilers, System.Collections.Generic.List`1[T] finalizers, System.Collections.Generic.List`1[T] ilmanipulators, System.Boolean debug) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Public.Patching.HarmonyManipulator.Manipulate (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo, MonoMod.Cil.ILContext ctx) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Public.Patching.ManagedMethodPatcher.Manipulator (MonoMod.Cil.ILContext ctx) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
MonoMod.Cil.ILContext.Invoke (MonoMod.Cil.ILContext+Manipulator manip) (at <5be58f3c80ca41c4960cf35eb47d4341>:0)
MonoMod.RuntimeDetour.ILHook+Context.InvokeManipulator (Mono.Cecil.MethodDefinition def, MonoMod.Cil.ILContext+Manipulator cb) (at <a3e7db5d9f924acea1a3fada3479a63b>:0)
(wrapper dynamic-method) MonoMod.RuntimeDetour.ILHook+Context.DMD<MonoMod.RuntimeDetour.ILHook+Context::Refresh>(MonoMod.RuntimeDetour.ILHook/Context)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.Trampoline<MonoMod.RuntimeDetour.ILHook+Context::Refresh>?1979709440(object)
HarmonyLib.Internal.RuntimeFixes.StackTraceFixes.OnILChainRefresh (System.Object self) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
MonoMod.RuntimeDetour.ILHook.Apply () (at <a3e7db5d9f924acea1a3fada3479a63b>:0)
HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
Rethrow as HarmonyException: IL Compile Error (unknown location)
HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
Rethrow as HarmonyException: IL Compile Error (unknown location)
HarmonyLib.PatchClassProcessor.ReportException (System.Exception exception, System.Reflection.MethodBase original) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.PatchClassProcessor.Patch () (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Harmony.<PatchAll>b__11_0 (System.Type type) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.CollectionExtensions.Do[T] (System.Collections.Generic.IEnumerable`1[T] sequence, System.Action`1[T] action) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Harmony.PatchAll (System.Reflection.Assembly assembly) (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
HarmonyLib.Harmony.PatchAll () (at <7f26c0a74c5b43c2a5fc5efd29ec63d6>:0)
MyMod.Plugin.Awake () (at <079d25c53ee14ff8970feb6bda7e4306>:0)
UnityEngine.GameObject:AddComponent(Type)
BepInEx.Bootstrap.Chainloader:Start()
UnityEngine.Application:.cctor()
Cysharp.Threading.Tasks.PlayerLoopHelper:Init()

I'm sort of stuck on how to work around this or what the issue is.

fritzr commented 2 years ago

Workaround is to retarget from netstandard2.0 to net472 from BepInEx/BepInEx#328.