C # CIL stloc.1 выпуск - PullRequest
       21

C # CIL stloc.1 выпуск

0 голосов
/ 08 июня 2019

Прежний вопрос решен, пожалуйста, продолжайте до конца.

Итак, у меня есть этот код здесь:

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

И чтобы получить код IL для части с помощью while, я пришелс этим примером программы , чтобы я мог получить код и изменить его для своих нужд, но, хотя я правильно скопировал код 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", и я не уверен почему.Если кто-нибудь знает, как решить эту проблему, пожалуйста, дайте мне знать, я был бы очень признателен!

UPD.

Я хотел бы задать еще один вопрос здесьчем создать отдельный.

Вот что изменилось:

//          // 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

Ответы [ 2 ]

1 голос
/ 08 июня 2019

Насколько я могу сказать:

  • В вашем коде у вас нет локальных переменных.Так что stloc.1 не будет работать.
  • В вашем примере вы смотрите на отладочный код.Который создает и устанавливает локальные переменные для облегчения отладки ※.Вместо этого используйте код выпуска.

※: убедитесь, что в коде используется stloc.0 (установить первую локальную переменную), затем ldloc.0 (загрузить первую локальную переменную), и они нигде не используютсяостальное.То же самое верно для stloc.1 и ldloc.1.Между stloc и ldloc имеется «точка последовательности: скрытая», которая указывает позицию, в которой вы можете добавить точку останова и проверить значение локальной переменной помощника.

Если вы настаиваете на добавлении локальных переменных, посмотрите на ILGenerator.DeclareLocal .


Приложение

В исходном коде у вас есть:

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

Это делает три вещи:

  1. Читать curPawn (нам нужно this здесь)
  2. Позвонить StartingPawnUtility.RandomizeInPlace (нам НЕ нужно this здесь, это статический вызов)
  3. Set 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, оставляя только два.

Примечание: я думаю, что у вас похожая проблема с другой инструкцией вызова.


Вы можете проверить OpCodes для документации по инструкциям IL.

0 голосов
/ 10 июня 2019

Я решил проблему!

Оказывается, 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),
  };
...