BepInEx / Il2CppInterop

A tool interoperate between CoreCLR and Il2Cpp at runtime
GNU Lesser General Public License v3.0
185 stars 59 forks source link

Injected fields returning injected types cause a crash #97

Open simonkellly opened 1 year ago

simonkellly commented 1 year ago

If you do the following

[BepInAutoPlugin]
public partial class TestPluginPlugin : BasePlugin
{
    public override void Load()
    {
        ClassInjector.RegisterTypeInIl2Cpp<MyBehaviour>();
        ClassInjector.RegisterTypeInIl2Cpp<MyTestObj>();

        MyBehaviour myBehaviour = new GameObject().AddComponent(Il2CppType.Of<MyBehaviour>()).Cast<MyBehaviour>();
        MyTestObj myTestObj = new GameObject().AddComponent(Il2CppType.Of<MyTestObj>()).Cast<MyTestObj>();
        myTestObj.myBehaviour.Value = myBehaviour;

        MyTestObj myNewTest = Object.Instantiate(myTestObj).Cast<MyTestObj>();
        Log.LogMessage("myNewTest.myBehaviour.mySpriteRenderer: " + myNewTest.myBehaviour.Value);
    }
}

public class MyBehaviour : MonoBehaviour
{
    public MyBehaviour(IntPtr ptr) : base(ptr) { }

    public Il2CppReferenceField<GameObject> mySpriteRenderer;
}

public class MyTestObj : MonoBehaviour
{
    public MyTestObj(IntPtr ptr) : base(ptr) { }

    public Il2CppReferenceField<MyBehaviour > myBehaviour;
}

The game will crash once you call Object.Instantiate(myTestObj)

Though, if you switch the return type to be something more generic i.e MonoBehaviour in myBehaviour, it works fine

Alexejhero commented 9 months ago

💤

Alexejhero commented 2 months ago

image

ds5678 commented 1 month ago

Decompiling that method (for my game) in ILSpy, I get some warnings, which may or may not be related.

public unsafe static T Instantiate<T>(T original) where T : Object
{
    //IL_0051->IL0056: Incompatible stack types: I vs Ref
    //IL_0044->IL0056: Incompatible stack types: I vs Ref
    IntPtr* ptr = stackalloc IntPtr[1];
    ref T reference;
    if (!typeof(T).IsValueType)
    {
        T val = original;
        reference = ref *(?*)((!(val is string)) ? IL2CPP.Il2CppObjectBaseToPtr(val as Il2CppObjectBase) : IL2CPP.ManagedStringToIl2Cpp(val as string));
    }
    else
    {
        reference = ref original;
    }
    *ptr = (nint)System.Runtime.CompilerServices.Unsafe.AsPointer(ref reference);
    System.Runtime.CompilerServices.Unsafe.SkipInit(out IntPtr exc);
    IntPtr objectPointer = IL2CPP.il2cpp_runtime_invoke(MethodInfoStoreGeneric_Instantiate_Public_Static_T_T_0<T>.Pointer, (IntPtr)0, (void**)ptr, ref exc);
    Il2CppException.RaiseExceptionIfNecessary(exc);
    return IL2CPP.PointerToValueGeneric<T>(objectPointer, isFieldPointer: false, valueTypeWouldBeBoxed: true);
}