简体   繁体   English

C#CIL stloc.1问题

[英]C# CIL stloc.1 issue

The former question is solved, please proceed to the end. 前一个问题已解决,请继续进行。

So I have this code here: 所以我在这里有这段代码:

using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;

namespace RandomPlus
{
    [HarmonyPatch (typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
    class Patch_RandomizeMethod
    {
        static void Prefix ()
        {
            RandomSettings.ResetRerollCounter ();
        }

        static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)
        {
            var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
                .GetField ("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
            var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
                .GetMethod ("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);
            var checkPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
                .GetMethod ("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);

            var codes = new List<CodeInstruction> (instructions);

            var appropriatePlace = 6;

            /* Removing the following code in its IL form */

            // this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);

            codes.RemoveRange (appropriatePlace, 5);

            /* Adding the following code in its IL form: */

//           do {
//            this.curPawn = StartingPawnUtility.RandomizeInPlace (this.curPawn);
//           } while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);

//
//          // loop start (head: IL_0016)
//          IL_0016: nop
//          IL_0017: ldarg.0
//          IL_0018: ldarg.0
//          IL_0019: ldarg.0
//          IL_001a: ldfld int32 C::curPawn
//          IL_001f: call instance int32 C::RandomizeInPlace(int32)
//          IL_0024: stfld int32 C::curPawn
//          IL_0029: nop
//          IL_002a: ldarg.0
//          IL_002b: call instance bool C::CheckPawnIsSatisfied()
//          IL_0030: ldc.i4.0
//          IL_0031: ceq
//          IL_0033: stloc.1
//          // sequence point: hidden
//          IL_0034: ldloc.1
//          IL_0035: brtrue.s IL_0016
//          // end loop

            List <CodeInstruction> newCodes = new List<CodeInstruction> {
                new CodeInstruction (OpCodes.Nop),
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
                new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
                new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
                new CodeInstruction (OpCodes.Nop),
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
                new CodeInstruction (OpCodes.Ldc_I4_0),
                new CodeInstruction (OpCodes.Ceq),
                new CodeInstruction (OpCodes.Stloc_1),
                new CodeInstruction (OpCodes.Ldloc_1),
            };

            newCodes [0].labels.Add (new Label ());

            var nopLabel = newCodes [0].labels [0];

            newCodes.Add (new CodeInstruction (OpCodes.Brtrue_S, nopLabel));

            codes.InsertRange (appropriatePlace, newCodes);

            for (var i = 0; i < codes.Count; i++) {
                Log.Message (codes [i].ToString ());
            }

            return codes;
        }
    }
}

What it basically does is it alters a method's code in IL and it is supposed to change this 它的基本作用是更改IL中的方法代码,并且应该对此进行更改

private void RandomizeCurPawn()
{
    if (!TutorSystem.AllowAction("RandomizePawn"))
    {
        return;
    }
    this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
    TutorSystem.Notify_Event("RandomizePawn");
}

into: 变成:

private void RandomizeCurPawn()
{
    if (!TutorSystem.AllowAction("RandomizePawn"))
    {
        return;
    }
    do
        {
            this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
        }
        while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);
    TutorSystem.Notify_Event("RandomizePawn");
}

And in order to get the IL code for the part with "while", I came up with this example program so I could get the code and change it for my needs but though I copied the IL code correctly (as far as I can tell), it doesn't work and throws an exception that says: 为了获得带有“ while”的零件的IL代码,我想出了这个示例程序,因此我可以获取代码并根据需要进行更改,但是尽管我正确地复制了IL代码(据我所知) ),它不起作用,并抛出一个异常,内容为:

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003c: stloc.1   


  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0 
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.<PatchAll>b__7_0 (System.Type type) [0x00000] in <filename unknown>:0 
  at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0 
  at RandomPlus.HarmonyPatches..cctor () [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
  at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0 
  at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0 
  at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0 
  at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0 
Verse.Log:Error(String)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update()
Verse.Root_Entry:Update()

As you can see, it is complaining about "stloc.1" line and I'm not sure why. 如您所见,它正在抱怨“ stloc.1”行,我不确定为什么。 If anyone knows how to fix this issue, please let me know, I would really appreciate that! 如果有人知道如何解决此问题,请告诉我,我将非常感谢!

UPD. UPD。

I'd like to ask another question here rather than to create a separate one. 我想在这里问另一个问题,而不是创建一个单独的问题。

Here is what's changed: 这是更改的地方:

//          // loop start (head: IL_000e)
//          IL_000e: ldarg.0
//          IL_000f: ldarg.0
//          IL_0010: ldarg.0
//          IL_0011: ldfld int32 C::curPawn
//          IL_0016: call instance int32 C::RandomizeInPlace(int32)
//          IL_001b: stfld int32 C::curPawn
//          IL_0020: ldarg.0
//          IL_0021: call instance bool C::CheckPawnIsSatisfied()
//          IL_0026: brfalse.s IL_000e
//          // end loop

            List <CodeInstruction> newCodes = new List<CodeInstruction> {
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
                new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
                new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
                new CodeInstruction (OpCodes.Ldarg_0),
                new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
            };

The game now shows me this error: 游戏现在向我显示此错误:

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003c: call      0x00000011


  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0 
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.<PatchAll>b__7_0 (System.Type type) [0x00000] in <filename unknown>:0 
  at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0 
  at RandomPlus.HarmonyPatches..cctor () [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
  at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0 
  at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0 
  at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0 
  at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0 
Verse.Log:Error(String)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()

Do you think it's because I don't pass an argument for RandomizeInPlace? 您是否认为这是因为我没有为RandomizeInPlace传递参数?

UPD2: UPD2:

I've updated the playground and here is my current code and error: 我已经更新了操场 ,这是我当前的代码和错误:

    List <CodeInstruction> newCodes = new List<CodeInstruction> {
        new CodeInstruction (OpCodes.Ldarg_0),
        new CodeInstruction (OpCodes.Ldarg_0),
        new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
        new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
        new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
        new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
    };

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003a: call      0x00000011

As far as I can tell: 据我所知:

  • In your code, you do not have local variables. 在您的代码中,您没有局部变量。 So stloc.1 won't work. 因此, stloc.1将不起作用。
  • In your example, you are looking at debug code. 在您的示例中,您正在查看调试代码。 Which is creating and setting local variables to ease debugging※. 正在创建和设置局部变量以简化调试※。 Use release code instead. 请改用发布代码。

※: See that the code uses stloc.0 (set the first local variable) followed by ldloc.0 (load the first local variable), and they are not used anywhere else. ※:看到代码使用stloc.0 (设置第一个局部变量),然后使用ldloc.0 (加载第一个局部变量),并且在其他任何地方都没有使用它们。 The same is true for stloc.1 and ldloc.1 . stloc.1ldloc.1也是如此。 There are "sequence point: hidden" in between of stloc and ldloc which indicates position where you could add a breakpoint and inspect the value of the helper local variable. stlocldloc之间有“顺序点:隐藏”,它指示您可以添加断点并检查帮助程序局部变量的值的位置。

If you insist in adding local variables, have a look at ILGenerator.DeclareLocal . 如果您坚持添加局部变量,请查看ILGenerator.DeclareLocal


Addendum 附录

In the original code, you have: 在原始代码中,您具有:

this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);

This is doing three things: 这是在做三件事:

  1. Read curPawn (we need this here) 阅读curPawn (我们在这里需要this
  2. Call StartingPawnUtility.RandomizeInPlace (we do NOT need this here, this is a static call) 调用StartingPawnUtility.RandomizeInPlace (在这里我们不需要this ,这是一个静态调用)
  3. Set curPawn (we need this here) 设置curPawn (我们在这里需要this

For a great total of 2 uses of this . 对于一个伟大的共有2所使用的this Thus, the code needs to load this (aka ldarg.0 ) twice. 因此,代码需要两次加载this (即ldarg.0 )。

Now, the replacement code has: 现在,替换代码具有:

this.curPawn = this.RandomizeInPlace (this.curPawn);

Here, the call to RandomizeInPlace is not a static call. 在这里,对RandomizeInPlace的调用不是静态调用。 Hence, the code needs need to load this once more, for a total of three times (you even have the three uses of this written explicitly in the code). 因此,该代码需要再次加载this代码,总共需要加载三遍(您甚至在代码中明确编写了this三种用法)。

This is why you have three consecutive ldarg.0 in your code: 这就是为什么您的代码中有三个连续的ldarg.0原因:

List <CodeInstruction> newCodes = new List<CodeInstruction> {
    new CodeInstruction (OpCodes.Ldarg_0), // <--
    new CodeInstruction (OpCodes.Ldarg_0), // <--
    new CodeInstruction (OpCodes.Ldarg_0), // <--
    new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
    new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
    new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
    new CodeInstruction (OpCodes.Ldarg_0),
    new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};

I suppose you want to do a static call, just like the original code, which imply to remove one ldarg.0 , leaving only two. 我想您想像原始代码一样进行静态调用,这意味着删除一个ldarg.0 ,仅保留两个。

Note: I think you a similar problem with the other call instruction. 注意:我认为您在其他调用说明中也遇到类似的问题。


You can check OpCodes for documentation on the IL instructions. 您可以在IL指令中查看OpCodes中的文档。

I've solved the issue! 我已经解决了这个问题!

Turns out, checkPawnIsSatisfied has to receive this.curPawn as an argument so I added a line that passes it as an argument to the method and it started working! 事实证明,checkPawnIsSatisfied必须接收this.curPawn作为参数,因此我添加了一行,将其作为参数传递给方法,它开始起作用!

  List <CodeInstruction> newCodes = new List<CodeInstruction> {
    new CodeInstruction (OpCodes.Ldarg_0),
    new CodeInstruction (OpCodes.Ldarg_0),
    new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
    new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
    new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
    new CodeInstruction (OpCodes.Ldarg_0),
    new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
    new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
  };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM