HarmonyX icon indicating copy to clipboard operation
HarmonyX copied to clipboard

TypeLoadException when patching a method in a class, which inherits a class that has a generic argument of type enum

Open btastic opened this issue 1 year ago • 4 comments

Describe the bug I want to patch a method which is residing in a class, that inherits from a class which has a generic argument of 'T' where 'T' is enum.

To Reproduce I compiled a little package with a simple repro.

You can download the zip file here: https://we.tl/t-yfbtmiVmA5

The packaged game is prepatched with MelonLoader 0.6.1

Steps to reproduce the behavior:

  1. The original method and its signature and class
using System;
using UnityEngine;

public enum GameScreen
{
    Title = 0,
    Game = 1,
}

public class MyGameScreen : GameScreen<GameScreen>
{
    private int _something = 0;

    public void SetSomething(int i)
    {
        _something = i;
    }

    void Start()
    {
    }

    void Update()
    {
    }
}

public class GameScreen<T> : MonoBehaviour where T : Enum
{

}
  1. The patch code
[HarmonyPatch(typeof(MyGameScreen), "SetSomething", new Type[] { typeof(int) })]
public static class Patch
{
    private static void Prefix()
    {
        // The code inside this method will run before 'PrivateMethod' is executed
    }

    private static void Postfix()
    {
        // The code inside this method will run after 'PrivateMethod' has executed
    }
}

public class Mod : MelonMod
{

}

Errors:

[14:48:45.178] [PWS_Tool_Mod] System.TypeLoadException: GenericArguments[0], 'Il2Cpp.GameScreen', on 'Il2Cpp.GameScreen`1[T]' violates the constraint of type parameter 'T'.
   at System.Reflection.CustomAttribute._CreateCaObject(RuntimeModule pModule, RuntimeType type, IRuntimeMethodInfo pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs)
   at System.Reflection.CustomAttribute.CreateCaObject(RuntimeModule module, RuntimeType type, IRuntimeMethodInfo ctor, IntPtr& blob, IntPtr blobEnd, Int32& namedArgs)
   at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
   at System.RuntimeType.GetCustomAttributes(Boolean inherit)
   at HarmonyLib.HarmonyMethodExtensions.GetFromType(Type type)
   at HarmonyLib.PatchClassProcessor..ctor(Harmony instance, Type type, Boolean allowUnannotatedType)
   at HarmonyLib.PatchClassProcessor..ctor(Harmony instance, Type type)
   at HarmonyLib.Harmony.CreateClassProcessor(Type type)
   at HarmonyLib.Harmony.<PatchAll>b__11_0(Type type)
   at HarmonyLib.CollectionExtensions.Do[T](IEnumerable`1 sequence, Action`1 action)
   at HarmonyLib.Harmony.PatchAll(Assembly assembly)
   at MelonLoader.MelonMod.HarmonyInit() in D:\a\MelonLoader\MelonLoader\MelonLoader\Melons\MelonMod.cs:line 40
   at MelonLoader.MelonEvent.<>c.<Invoke>b__1_0(LemonAction x) in D:\a\MelonLoader\MelonLoader\MelonLoader\Melons\Events\MelonEvent.cs:line 174
   at MelonLoader.MelonEventBase`1.Invoke(Action`1 delegateInvoker) in D:\a\MelonLoader\MelonLoader\MelonLoader\Melons\Events\MelonEvent.cs:line 143

Expected behavior It should not error..?

Screenshots / Code Not much to say here. I originally intend to patch a game which uses the same unity version, the same code structure etc.

Runtime environment (please complete the following information):

  • OS: Windows 11
  • .NET version: .NET Standard
  • Harmony version: 2.10.1.0
  • Name of game or host application: doesn't matter, I included a custom made sample which reproduces the same error as in the game

Additional context Add any other context about the problem here.

btastic avatar Apr 13 '23 17:04 btastic

It seems that the issue lies within how Il2CPP code is generated, maybe it can't be done in HarmonyX?

I added a reproducible branch here: https://github.com/btastic/HarmonyX/tree/generics_il2cpp (it includes some IL2CPP dll files)

I added a new test class GenericsFromIl2Cpp which will fail with the error message I wrote earlier

btastic avatar Apr 13 '23 18:04 btastic

Il2CPP code could in theory be changed to allow this, so that all instances of Il2CPPSystem.Enum are replaced with System.Enum. This would, however, be a breaking change, and it would mean that methods of the Enum class itself could no longer be patched.

plokmijnuhby avatar Dec 13 '23 21:12 plokmijnuhby

I wonder if the best plan is to introduce some sort of equivalent to each type of enum that inherits from Il2CPPSystem.Enum, that can be used as a parameter? Maybe GenericType<AnEnum> is replaced with GenericType<Il2CPPReferenceEnum<AnEnum>>, or something like that?

plokmijnuhby avatar Dec 13 '23 21:12 plokmijnuhby

Was there any progress on that? Im facing a similar issue

Azim avatar May 26 '24 14:05 Azim