Files
BepInEx/Runtimes/Unity/BepInEx.Unity.IL2CPP/Utils/Collections/Il2CppManagedEnumerator.cs
T

89 lines
3.3 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Reflection.Emit;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppSystem;
using ArgumentNullException = System.ArgumentNullException;
using Il2CppIEnumerator = Il2CppSystem.Collections.IEnumerator;
using IntPtr = System.IntPtr;
using NotSupportedException = System.NotSupportedException;
using Type = System.Type;
namespace BepInEx.Unity.IL2CPP.Utils.Collections;
public class Il2CppManagedEnumerator : Object
{
private static readonly Dictionary<Type, System.Func<object, Object>> boxers = new();
private readonly IEnumerator enumerator;
static Il2CppManagedEnumerator()
{
ClassInjector.RegisterTypeInIl2Cpp<Il2CppManagedEnumerator>(new RegisterTypeOptions
{
Interfaces = new[] { typeof(Il2CppIEnumerator) }
});
}
public Il2CppManagedEnumerator(IntPtr ptr) : base(ptr) { }
public Il2CppManagedEnumerator(IEnumerator enumerator)
: base(ClassInjector.DerivedConstructorPointer<Il2CppManagedEnumerator>())
{
this.enumerator = enumerator ?? throw new ArgumentNullException(nameof(enumerator));
ClassInjector.DerivedConstructorBody(this);
}
public Object Current => enumerator.Current switch
{
Il2CppIEnumerator i => i.Cast<Object>(),
IEnumerator e => new Il2CppManagedEnumerator(e),
Object oo => oo,
{ } obj => ManagedToIl2CppObject(obj),
null => null
};
public bool MoveNext() => enumerator.MoveNext();
public void Reset() => enumerator.Reset();
private static System.Func<object, Object> GetValueBoxer(Type t)
{
if (boxers.TryGetValue(t, out var conv))
return conv;
var dm = new DynamicMethod($"Il2CppUnbox_{t.FullDescription()}", typeof(Object),
new[] { typeof(object) });
var il = dm.GetILGenerator();
var loc = il.DeclareLocal(t);
var classField = typeof(Il2CppClassPointerStore<>).MakeGenericType(t)
.GetField(nameof(Il2CppClassPointerStore<int>
.NativeClassPtr));
il.Emit(OpCodes.Ldsfld, classField);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Unbox_Any, t);
il.Emit(OpCodes.Stloc, loc);
il.Emit(OpCodes.Ldloca, loc);
il.Emit(OpCodes.Call,
typeof(Il2CppInterop.Runtime.IL2CPP).GetMethod(nameof(Il2CppInterop.Runtime.IL2CPP.il2cpp_value_box)));
il.Emit(OpCodes.Newobj, typeof(Object).GetConstructor(new[] { typeof(IntPtr) }));
il.Emit(OpCodes.Ret);
var converter = dm.CreateDelegate(typeof(System.Func<object, Object>)) as System.Func<object, Object>;
boxers[t] = converter;
return converter;
}
private static Object ManagedToIl2CppObject(object obj)
{
var t = obj.GetType();
if (obj is string s)
return new Object(Il2CppInterop.Runtime.IL2CPP.ManagedStringToIl2Cpp(s));
if (t.IsPrimitive)
return GetValueBoxer(t)(obj);
throw new NotSupportedException($"Type {t} cannot be converted directly to an Il2Cpp object");
}
}