diff --git a/Il2CppInterop.Runtime/IL2CPP.cs b/Il2CppInterop.Runtime/IL2CPP.cs index 0cb5acac..c70b4d10 100644 --- a/Il2CppInterop.Runtime/IL2CPP.cs +++ b/Il2CppInterop.Runtime/IL2CPP.cs @@ -369,6 +369,27 @@ public static void FieldWriteWbarrierStub(IntPtr obj, IntPtr targetAddress, IntP *(IntPtr*)targetAddress = value; } + private static volatile bool s_fieldWriteWbarrierAvailable = true; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void FieldWriteWbarrier(IntPtr obj, IntPtr targetAddress, IntPtr value) + { + if (s_fieldWriteWbarrierAvailable) + { + try + { + il2cpp_gc_wbarrier_set_field(obj, targetAddress, value); + return; + } + catch (EntryPointNotFoundException) + { + s_fieldWriteWbarrierAvailable = false; + } + } + + FieldWriteWbarrierStub(obj, targetAddress, value); + } + // IL2CPP Functions [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public static extern void il2cpp_init(IntPtr domain_name); diff --git a/Il2CppInterop.Runtime/Injection/ClassInjector.cs b/Il2CppInterop.Runtime/Injection/ClassInjector.cs index b9fb6486..89d8c4d9 100644 --- a/Il2CppInterop.Runtime/Injection/ClassInjector.cs +++ b/Il2CppInterop.Runtime/Injection/ClassInjector.cs @@ -250,6 +250,7 @@ public static void RegisterTypeInIl2Cpp(Type type, RegisterTypeOptions options) .Where(IsFieldEligible) .ToArray(); classPointer.FieldCount = (ushort)fieldsToInject.Length; + classPointer.HasReferences = baseClassPointer.HasReferences; var il2cppFields = (Il2CppFieldInfo*)Marshal.AllocHGlobal(classPointer.FieldCount * UnityVersionHandler.FieldInfoSize()); @@ -266,6 +267,9 @@ public static void RegisterTypeInIl2Cpp(Type type, RegisterTypeOptions options) : fieldsToInject[i].FieldType.GenericTypeArguments[0]; var fieldAttributes = fieldsToInject[i].Attributes; var fieldInfoClass = Il2CppClassPointerStore.GetNativeClassPointer(fieldType); + if (fieldInfoClass == IntPtr.Zero) + throw new Exception($"Type {fieldType} in {type}.{fieldsToInject[i].Name} doesn't exist in Il2Cpp"); + if (!_injectedFieldTypes.TryGetValue((fieldType, fieldAttributes), out var fieldTypePtr)) { var classType = @@ -283,10 +287,11 @@ public static void RegisterTypeInIl2Cpp(Type type, RegisterTypeOptions options) } fieldInfo.Type = (Il2CppTypeStruct*)fieldTypePtr; - if (fieldInfoClass == IntPtr.Zero) - throw new Exception($"Type {fieldType} in {type}.{fieldsToInject[i].Name} doesn't exist in Il2Cpp"); + var fieldIsValueType = IL2CPP.il2cpp_class_is_valuetype(fieldInfoClass); + classPointer.HasReferences |= !fieldIsValueType || + IL2CPP.il2cpp_class_has_references(fieldInfoClass); - if (IL2CPP.il2cpp_class_is_valuetype(fieldInfoClass)) + if (fieldIsValueType) { uint _align = 0; var fieldSize = IL2CPP.il2cpp_class_value_size(fieldInfoClass, ref _align); diff --git a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs b/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs index ee5c8024..470b03e2 100644 --- a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs +++ b/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppReferenceField.cs @@ -28,7 +28,9 @@ public TRefObj Value public void Set(TRefObj value) { - *GetPointerToData() = value != null ? value.Pointer : IntPtr.Zero; + var targetAddress = (IntPtr)GetPointerToData(); + IL2CPP.FieldWriteWbarrier(_obj.Pointer, targetAddress, + value != null ? value.Pointer : IntPtr.Zero); } public static implicit operator TRefObj(Il2CppReferenceField _this) diff --git a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppStringField.cs b/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppStringField.cs index 50a98e98..5ac76026 100644 --- a/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppStringField.cs +++ b/Il2CppInterop.Runtime/InteropTypes/Fields/Il2CppStringField.cs @@ -30,7 +30,9 @@ public string Value public void Set(string value) { - *GetPointerToData() = IL2CPP.ManagedStringToIl2Cpp(value); + var targetAddress = (IntPtr)GetPointerToData(); + IL2CPP.FieldWriteWbarrier(_obj.Pointer, targetAddress, + IL2CPP.ManagedStringToIl2Cpp(value)); } public static implicit operator string(Il2CppStringField _this) diff --git a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs index 930da95f..8bafa5e3 100644 --- a/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs +++ b/Il2CppInterop.Runtime/Runtime/VersionSpecific/Class/Interfaces.cs @@ -31,6 +31,11 @@ public interface INativeClassStruct : INativeStruct bool Initialized { get; set; } bool InitializedAndNoError { get; set; } bool SizeInited { get; set; } + bool HasReferences + { + get => false; + set { } + } bool HasFinalize { get; set; } bool IsVtableInitialized { get; set; }