C# CIL stloc.1 issue

  • 本文关键字:issue stloc CIL c# cil
  • 更新时间 :
  • 英文 :


前一个问题解决了,请继续到最后。

所以我这里有这个代码:

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;
}
}
}

它基本上做的是它在 IL 中更改方法的代码,并且应该更改它

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

到:

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

为了获取带有"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()

如您所见,它正在抱怨"stloc.1"行,我不知道为什么。如果有人知道如何解决此问题,请告诉我,我将不胜感激!

上。

我想在这里问另一个问题,而不是创建一个单独的问题。

以下是更改的内容:

//          // 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),
};

游戏现在向我显示此错误:

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()

你认为这是因为我没有传递 RandomizeInPlace 的论据吗?

UPD2:

我已经更新了游乐场,这是我当前的代码和错误:

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

据我所知:

  • 在代码中,没有局部变量。所以stloc.1行不通。
  • 在您的示例中,您正在查看调试代码。这是创建和设置局部变量以简化调试※。请改用发布代码。

※:看到代码使用stloc.0(设置第一个局部变量)后跟ldloc.0(加载第一个局部变量),并且在其他任何地方都没有使用。stloc.1ldloc.1也是如此。在stlocldloc之间有"序列点:隐藏",指示可以添加断点并检查帮助程序局部变量值的位置。

如果您坚持添加局部变量,请查看 ILGenerator.DeclareLocal。


补遗

在原始代码中,您有:

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

这是在做三件事:

  1. 阅读curPawn(我们需要在这里this)
  2. 调用StartingPawnUtility.RandomizeInPlace(我们这里不需要this,这是一个静态调用)
  3. 设置curPawn(我们需要在这里this)

总共 2 次使用this.因此,代码需要加载this(又名ldarg.0)两次。

现在,替换代码具有:

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

在这里,对RandomizeInPlace的调用不是静态调用。因此,代码需要再次加载this,总共三次(您甚至可以在代码中显式编写this的三种用法)。

这就是代码中连续三个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),
};

我想你想做一个静态调用,就像原始代码一样,这意味着删除一个ldarg.0,只留下两个。

注意:我认为您对其他呼叫指令也有类似的问题。


您可以查看操作码以获取有关 IL 指令的文档。

我已经解决了这个问题!

事实证明,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),
};

相关内容

  • 没有找到相关文章

最新更新