[英]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: 据我所知:
stloc.1
won't work. stloc.1
将不起作用。 ※: 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.1
和ldloc.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. 在
stloc
和ldloc
之间有“顺序点:隐藏”,它指示您可以添加断点并检查帮助程序局部变量的值的位置。
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: 这是在做三件事:
curPawn
(we need this
here) curPawn
(我们在这里需要this
) StartingPawnUtility.RandomizeInPlace
(we do NOT need this
here, this is a static call) StartingPawnUtility.RandomizeInPlace
(在这里我们不需要this
,这是一个静态调用) 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.