Skip to content

Commit 876e0f0

Browse files
authored
Pool objects created when passing objects from generated assemblies to the user (#92)
1 parent 975e421 commit 876e0f0

11 files changed

Lines changed: 161 additions & 39 deletions

File tree

Il2CppInterop.Generator/Extensions/ILGeneratorEx.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,20 +354,20 @@ public static void EmitPointerToObject(this ILProcessor body, TypeReference orig
354354
}
355355
else
356356
{
357-
var createRealObject = body.Create(OpCodes.Newobj,
358-
new MethodReference(".ctor", imports.Module.Void(), convertedReturnType)
359-
{ Parameters = { new ParameterDefinition(imports.Module.IntPtr()) }, HasThis = true });
357+
var createPoolObject = body.Create(OpCodes.Call,
358+
imports.Module.ImportReference(new GenericInstanceMethod(imports.Il2CppObjectPool_Get.Value)
359+
{ GenericArguments = { convertedReturnType } }));
360360
var endNop = body.Create(OpCodes.Nop);
361361

362362
body.Append(loadPointer);
363363
if (extraDerefForNonValueTypes) body.Emit(OpCodes.Ldind_I);
364364
body.Emit(OpCodes.Dup);
365-
body.Emit(OpCodes.Brtrue_S, createRealObject);
365+
body.Emit(OpCodes.Brtrue_S, createPoolObject);
366366
body.Emit(OpCodes.Pop);
367367
body.Emit(OpCodes.Ldnull);
368368
body.Emit(OpCodes.Br, endNop);
369369

370-
body.Append(createRealObject);
370+
body.Append(createPoolObject);
371371
body.Append(endNop);
372372
}
373373
}

Il2CppInterop.Generator/Utils/RuntimeAssemblyReferences.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public RuntimeAssemblyReferences(ModuleDefinition module, RewriteGlobalContext g
3636
public Lazy<MethodReference> IL2CPP_ManagedStringToIl2Cpp { get; private set; }
3737
public Lazy<MethodReference> Il2CppObjectBase_Cast { get; private set; }
3838
public Lazy<MethodReference> Il2CppObjectBase_TryCast { get; private set; }
39+
public Lazy<MethodReference> Il2CppObjectPool_Get { get; private set; }
3940
public Lazy<MethodReference> IL2CPP_ResolveICall { get; private set; }
4041
public Lazy<MethodReference> IL2CPP_il2cpp_gc_wbarrier_set_field { get; private set; }
4142
public Lazy<MethodReference> IL2CPP_FieldWriteWbarrierStub { get; private set; }
@@ -76,6 +77,7 @@ public RuntimeAssemblyReferences(ModuleDefinition module, RewriteGlobalContext g
7677
: IL2CPP_FieldWriteWbarrierStub.Value;
7778

7879
public TypeReference Il2CppObjectBase { get; private set; }
80+
public TypeReference Il2CppObjectPool { get; private set; }
7981
public TypeReference Il2CppStringArray { get; private set; }
8082
public TypeReference Il2CppArrayBase { get; private set; }
8183
public TypeReference Il2CppStructArray { get; private set; }
@@ -112,6 +114,8 @@ private void InitTypeRefs()
112114
Il2CppObjectBase =
113115
new TypeReference("Il2CppInterop.Runtime.InteropTypes", "Il2CppObjectBase", Module, assemblyRef);
114116

117+
Il2CppObjectPool = new TypeReference("Il2CppInterop.Runtime.Runtime", "Il2CppObjectPool", Module, assemblyRef);
118+
115119
Il2CppStringArray = new TypeReference("Il2CppInterop.Runtime.InteropTypes.Arrays", "Il2CppStringArray", Module,
116120
assemblyRef);
117121

@@ -142,6 +146,7 @@ private void InitTypeRefs()
142146
Il2CppException = new TypeReference("Il2CppInterop.Runtime", "Il2CppException", Module, assemblyRef);
143147

144148
allTypes["Il2CppInterop.Runtime.InteropTypes.Il2CppObjectBase"] = Il2CppObjectBase;
149+
allTypes["Il2CppInterop.Runtime.Runtime.Il2CppObjectPool"] = Il2CppObjectPool;
145150
allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStringArray"] = Il2CppStringArray;
146151
allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppReferenceArray<T>"] = Il2CppReferenceArray;
147152
allTypes["Il2CppInterop.Runtime.InteropTypes.Arrays.Il2CppStructArray<T>"] = Il2CppStructArray;
@@ -281,6 +286,18 @@ private void InitMethodRefs()
281286
return mr;
282287
});
283288

289+
Il2CppObjectPool_Get = new Lazy<MethodReference>(() =>
290+
{
291+
var mr = new MethodReference("Get", Module.Void(),
292+
ResolveType("Il2CppInterop.Runtime.Runtime.Il2CppObjectPool"));
293+
var gp0 = new GenericParameter("T", mr);
294+
mr.GenericParameters.Add(gp0);
295+
mr.ReturnType = gp0;
296+
mr.HasThis = false;
297+
mr.Parameters.Add(new ParameterDefinition("ptr", ParameterAttributes.None, ResolveType("System.IntPtr")));
298+
return mr;
299+
});
300+
284301
IL2CPP_ResolveICall = new Lazy<MethodReference>(() =>
285302
{
286303
var mr = new MethodReference("ResolveICall", Module.Void(),

Il2CppInterop.HarmonySupport/Il2CppDetourMethodPatcher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ void EmitCreateIl2CppObject(Type originalType)
405405
il.Emit(OpCodes.Br_S, endLabel);
406406

407407
il.MarkLabel(notNullLabel);
408-
il.Emit(OpCodes.Newobj, AccessTools.DeclaredConstructor(originalType, new[] { typeof(IntPtr) }));
408+
il.Emit(OpCodes.Call, AccessTools.Method(typeof(Il2CppObjectPool), nameof(Il2CppObjectPool.Get)).MakeGenericMethod(originalType));
409409

410410
il.MarkLabel(endLabel);
411411
}

Il2CppInterop.Runtime/IL2CPP.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,7 @@ private static T GenerateDelegateForMissingICall<T>(string signature) where T :
300300
if (typeof(T).IsValueType)
301301
return Il2CppObjectBase.UnboxUnsafe<T>(objectPointer);
302302

303-
var il2CppObjectBase = Il2CppObjectBase.CreateUnsafe<T>(objectPointer);
304-
return Unsafe.As<Il2CppObjectBase, T>(ref il2CppObjectBase);
303+
return Il2CppObjectPool.Get<T>(objectPointer);
305304
}
306305

307306
public static string RenderTypeName<T>(bool addRefMarker = false)

Il2CppInterop.Runtime/Il2CppInterop.Runtime.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<Platforms>AnyCPU</Platforms>
1212
<ImplicitUsings>false</ImplicitUsings>
1313
</PropertyGroup>
14-
14+
1515
<ItemGroup>
1616
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
1717
<_Parameter1>Il2CppInterop.HarmonySupport</_Parameter1>

Il2CppInterop.Runtime/Injection/Hook.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,23 @@ internal abstract class Hook<T> where T : Delegate
1818
public abstract T GetDetour();
1919
public abstract IntPtr FindTargetMethod();
2020

21+
public virtual void TargetMethodNotFound()
22+
{
23+
throw new Exception($"Required target method {TargetMethodName} not found");
24+
}
25+
2126
public void ApplyHook()
2227
{
2328
if (_isApplied) return;
2429

2530
var methodPtr = FindTargetMethod();
31+
32+
if (methodPtr == IntPtr.Zero)
33+
{
34+
TargetMethodNotFound();
35+
return;
36+
}
37+
2638
Logger.Instance.LogTrace("{MethodName} found: 0x{MethodPtr}", TargetMethodName, methodPtr.ToInt64().ToString("X2"));
2739

2840
_detour = GetDetour();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Linq;
3+
using System.Runtime.InteropServices;
4+
using Il2CppInterop.Common;
5+
using Il2CppInterop.Runtime.Runtime;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace Il2CppInterop.Runtime.Injection.Hooks;
9+
10+
internal class GarbageCollector_RunFinalizer_Patch : Hook<GarbageCollector_RunFinalizer_Patch.MethodDelegate>
11+
{
12+
public override string TargetMethodName => "GarbageCollector::RunFinalizer";
13+
public override MethodDelegate GetDetour() => Hook;
14+
15+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
16+
public delegate void MethodDelegate(IntPtr obj, IntPtr data);
17+
18+
private void Hook(IntPtr obj, IntPtr data)
19+
{
20+
unsafe
21+
{
22+
var nativeClassStruct = UnityVersionHandler.Wrap((Il2CppClass*)IL2CPP.il2cpp_object_get_class(obj));
23+
if (nativeClassStruct.HasFinalize)
24+
{
25+
Original(obj, data);
26+
}
27+
}
28+
Il2CppObjectPool.Remove(obj);
29+
}
30+
31+
private static readonly MemoryUtils.SignatureDefinition[] s_signatures =
32+
{
33+
new()
34+
{
35+
// Among Us - 2020.3.22 (x86)
36+
pattern = "\x55\x8B\xEC\x51\x56\x8B\x75\x08\xC7\x45\x00\x00\x00\x00\x00",
37+
mask = "xxxxxxxxxx?????",
38+
xref = false
39+
},
40+
new()
41+
{
42+
// Test Game - 2021.3.22 (x64)
43+
pattern = "\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\x48\xC7\x44\x24\x30\x00\x00\x00\x00\x48\x8B",
44+
mask = "xxxxxxxxxxxxxxxxxxxx",
45+
xref = false,
46+
}
47+
};
48+
49+
public override IntPtr FindTargetMethod()
50+
{
51+
return s_signatures
52+
.Select(s => MemoryUtils.FindSignatureInModule(InjectorHelpers.Il2CppModule, s))
53+
.FirstOrDefault(p => p != 0);
54+
}
55+
56+
public override void TargetMethodNotFound()
57+
{
58+
Il2CppObjectPool.DisableCaching = true;
59+
Logger.Instance.LogWarning("{MethodName} not found, disabling Il2CppObjectPool", TargetMethodName);
60+
}
61+
}

Il2CppInterop.Runtime/Injection/InjectorHelpers.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ private static void CreateInjectedAssembly()
6767
private static readonly Class_GetFieldDefaultValue_Hook GetFieldDefaultValueHook = new();
6868
private static readonly Class_FromIl2CppType_Hook FromIl2CppTypeHook = new();
6969
private static readonly Class_FromName_Hook FromNameHook = new();
70+
private static readonly GarbageCollector_RunFinalizer_Patch RunFinalizerPatch = new();
7071

7172
internal static void Setup()
7273
{
@@ -77,6 +78,7 @@ internal static void Setup()
7778
ClassInit ??= FindClassInit();
7879
FromIl2CppTypeHook.ApplyHook();
7980
FromNameHook.ApplyHook();
81+
RunFinalizerPatch.ApplyHook();
8082
}
8183

8284
internal static long CreateClassToken(IntPtr classPointer)

Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ namespace Il2CppInterop.Runtime.InteropTypes.Fields;
55

66
public unsafe class Il2CppReferenceField<TRefObj> where TRefObj : Il2CppObjectBase
77
{
8-
private static bool? isInjectedType;
98
private readonly IntPtr _fieldPtr;
10-
119
private readonly Il2CppObjectBase _obj;
1210

1311
internal Il2CppReferenceField(Il2CppObjectBase obj, string fieldName)
@@ -25,13 +23,7 @@ public TRefObj Value
2523
public TRefObj? Get()
2624
{
2725
var ptr = *GetPointerToData();
28-
if (ptr == IntPtr.Zero) return null;
29-
if (isInjectedType == null)
30-
isInjectedType = RuntimeSpecificsStore.IsInjected(Il2CppClassPointerStore<TRefObj>.NativeClassPtr);
31-
32-
if (isInjectedType.Value && ClassInjectorBase.GetMonoObjectFromIl2CppPointer(ptr) is TRefObj monoObject)
33-
return monoObject;
34-
return (TRefObj)Activator.CreateInstance(typeof(TRefObj), ptr);
26+
return ptr == IntPtr.Zero ? null : Il2CppObjectPool.Get<TRefObj>(ptr);
3527
}
3628

3729
public void Set(TRefObj value)

Il2CppInterop.Runtime/InteropTypes/Il2CppObjectBase.cs

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class Il2CppObjectBase
1212
{
1313
private static readonly MethodInfo _unboxMethod = typeof(Il2CppObjectBase).GetMethod(nameof(Unbox));
1414
internal bool isWrapped;
15+
internal IntPtr pooledPtr;
1516

1617
private uint myGcHandle;
1718

@@ -86,7 +87,7 @@ public T Unbox<T>() where T : unmanaged
8687
private static readonly MethodInfo _createGCHandle = typeof(Il2CppObjectBase).GetMethod(nameof(CreateGCHandle))!;
8788
private static readonly FieldInfo _isWrapped = typeof(Il2CppObjectBase).GetField(nameof(isWrapped))!;
8889

89-
private static class InitializerStore<T>
90+
internal static class InitializerStore<T>
9091
{
9192
private static Func<IntPtr, T>? _initializer;
9293

@@ -145,26 +146,6 @@ private static Func<IntPtr, T> Create()
145146
public static Func<IntPtr, T> Initializer => _initializer ??= Create();
146147
}
147148

148-
internal static Il2CppObjectBase CreateUnsafe<T>(IntPtr pointer)
149-
{
150-
var nestedTypeClassPointer = Il2CppClassPointerStore<T>.NativeClassPtr;
151-
if (nestedTypeClassPointer == IntPtr.Zero)
152-
throw new ArgumentException($"{typeof(T)} is not an Il2Cpp reference type");
153-
154-
var ownClass = IL2CPP.il2cpp_object_get_class(pointer);
155-
if (!IL2CPP.il2cpp_class_is_assignable_from(nestedTypeClassPointer, ownClass))
156-
return null;
157-
158-
if (RuntimeSpecificsStore.IsInjected(ownClass))
159-
{
160-
var monoObject = ClassInjectorBase.GetMonoObjectFromIl2CppPointer(pointer);
161-
if (monoObject is T) return (Il2CppObjectBase)monoObject;
162-
}
163-
164-
var il2CppObjectBase = InitializerStore<T>.Initializer(pointer);
165-
return Unsafe.As<T, Il2CppObjectBase>(ref il2CppObjectBase);
166-
}
167-
168149
public T? TryCast<T>() where T : Il2CppObjectBase
169150
{
170151
var nestedTypeClassPointer = Il2CppClassPointerStore<T>.NativeClassPtr;
@@ -186,5 +167,8 @@ internal static Il2CppObjectBase CreateUnsafe<T>(IntPtr pointer)
186167
~Il2CppObjectBase()
187168
{
188169
IL2CPP.il2cpp_gchandle_free(myGcHandle);
170+
171+
if (pooledPtr == IntPtr.Zero) return;
172+
Il2CppObjectPool.Remove(pooledPtr);
189173
}
190174
}

0 commit comments

Comments
 (0)