Skip to content

Commit

Permalink
Cleanup arrays and annotate for nullability (#185)
Browse files Browse the repository at this point in the history
* Cleanup arrays and annotate for nullability

* Fix null in string array

* Update Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppReferenceArray.cs

Co-authored-by: js6pak <[email protected]>

* Simpler index check

* Update Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayBase.cs

Co-authored-by: js6pak <[email protected]>

* fix

* requested reference changes

* formatting

* null oblivious indexers

* try again

---------

Co-authored-by: js6pak <[email protected]>
  • Loading branch information
ds5678 and js6pak authored Oct 5, 2024
1 parent aa36797 commit a7a7e38
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 70 deletions.
2 changes: 1 addition & 1 deletion Il2CppInterop.Runtime/IL2CPP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public static IntPtr GetIl2CppMethod(IntPtr clazz, bool isGeneric, string method
return new string(chars, 0, length);
}

public static IntPtr ManagedStringToIl2Cpp(string str)
public static IntPtr ManagedStringToIl2Cpp(string? str)
{
if (str == null) return IntPtr.Zero;

Expand Down
21 changes: 18 additions & 3 deletions Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppArrayBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Il2CppInterop.Runtime.Runtime;

namespace Il2CppInterop.Runtime.InteropTypes.Arrays;

Expand All @@ -10,6 +12,11 @@ protected Il2CppArrayBase(IntPtr pointer) : base(pointer)
{
}

/// <summary>
/// The pointer to the first element in the array.
/// </summary>
private protected unsafe IntPtr ArrayStartPointer => IntPtr.Add(Pointer, sizeof(Il2CppObject) /* base */ + sizeof(void*) /* bounds */ + sizeof(nuint) /* max_length */);

public int Length => (int)IL2CPP.il2cpp_array_length(Pointer);

public abstract IEnumerator GetEnumerator();
Expand All @@ -18,6 +25,13 @@ private protected static bool ThrowImmutableLength()
{
throw new NotSupportedException("Arrays have immutable length");
}

private protected void ThrowIfIndexOutOfRange(int index)
{
if ((uint)index >= (uint)Length)
throw new ArgumentOutOfRangeException(nameof(index),
"Array index may not be negative or above length of the array");
}
}
public abstract class Il2CppArrayBase<T> : Il2CppArrayBase, IList<T>, IReadOnlyList<T>
{
Expand Down Expand Up @@ -102,7 +116,8 @@ protected static void StaticCtorBody(Type ownType)
Il2CppClassPointerStore<Il2CppArrayBase<T>>.CreatedTypeRedirect = ownType;
}

public static implicit operator T[](Il2CppArrayBase<T> il2CppArray)
[return: NotNullIfNotNull(nameof(il2CppArray))]
public static implicit operator T[]?(Il2CppArrayBase<T>? il2CppArray)
{
if (il2CppArray == null)
return null;
Expand Down Expand Up @@ -143,7 +158,7 @@ public IndexEnumerator(Il2CppArrayBase<T> array)

public void Dispose()
{
myArray = null;
myArray = null!;
}

public bool MoveNext()
Expand All @@ -156,7 +171,7 @@ public void Reset()
myIndex = -1;
}

object IEnumerator.Current => Current;
object? IEnumerator.Current => Current;
public T Current => myArray[myIndex];
}
}
61 changes: 21 additions & 40 deletions Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppReferenceArray.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Il2CppInterop.Runtime.Runtime;

namespace Il2CppInterop.Runtime.InteropTypes.Arrays;

public class Il2CppReferenceArray<T> : Il2CppArrayBase<T> where T : Il2CppObjectBase
public class Il2CppReferenceArray<T> : Il2CppArrayBase<T> where T : Il2CppObjectBase?
{
private static ConstructorInfo ourCachedInstanceCtor;
private static readonly int ourElementTypeSize;
private static readonly bool ourElementIsValueType;

Expand Down Expand Up @@ -41,27 +41,18 @@ public Il2CppReferenceArray(T[] arr) : base(AllocateArray(arr.Length))

public override T this[int index]
{
get
{
if (index < 0 || index >= Length)
throw new ArgumentOutOfRangeException(nameof(index),
"Array index may not be negative or above length of the array");
var arrayStartPointer = IntPtr.Add(Pointer, 4 * IntPtr.Size);
var elementPointer = IntPtr.Add(arrayStartPointer, index * ourElementTypeSize);
return WrapElement(elementPointer);
}
set
{
if (index < 0 || index >= Length)
throw new ArgumentOutOfRangeException(nameof(index),
"Array index may not be negative or above length of the array");
var arrayStartPointer = IntPtr.Add(Pointer, 4 * IntPtr.Size);
var elementPointer = IntPtr.Add(arrayStartPointer, index * ourElementTypeSize);
StoreValue(elementPointer, value?.Pointer ?? IntPtr.Zero);
}
get => WrapElement(GetElementPointer(index))!;
set => StoreValue(GetElementPointer(index), value?.Pointer ?? IntPtr.Zero);
}

private IntPtr GetElementPointer(int index)
{
ThrowIfIndexOutOfRange(index);
return IntPtr.Add(ArrayStartPointer, index * ourElementTypeSize);
}

public static implicit operator Il2CppReferenceArray<T>(T[] arr)
[return: NotNullIfNotNull(nameof(arr))]
public static implicit operator Il2CppReferenceArray<T>?(T[]? arr)
{
if (arr == null) return null;

Expand All @@ -77,8 +68,8 @@ private static unsafe void StoreValue(IntPtr targetPointer, IntPtr valuePointer)

var valueRawPointer = (byte*)IL2CPP.il2cpp_object_unbox(valuePointer);
var targetRawPointer = (byte*)targetPointer;
for (var i = 0; i < ourElementTypeSize; i++)
targetRawPointer[i] = valueRawPointer[i];

Unsafe.CopyBlock(targetRawPointer, valueRawPointer, (uint)ourElementTypeSize);
}
else
{
Expand All @@ -88,25 +79,15 @@ private static unsafe void StoreValue(IntPtr targetPointer, IntPtr valuePointer)

private static unsafe T? WrapElement(IntPtr memberPointer)
{
if (ourCachedInstanceCtor == null) ourCachedInstanceCtor = typeof(T).GetConstructor(new[] { typeof(IntPtr) });

if (ourElementIsValueType)
return (T)ourCachedInstanceCtor.Invoke(new object[]
{IL2CPP.il2cpp_value_box(Il2CppClassPointerStore<T>.NativeClassPtr, memberPointer)});

var referencePointer = *(IntPtr*)memberPointer;
if (referencePointer == IntPtr.Zero) return null;
memberPointer = IL2CPP.il2cpp_value_box(Il2CppClassPointerStore<T>.NativeClassPtr, memberPointer);
else
memberPointer = *(IntPtr*)memberPointer;

if (typeof(Il2CppObjectBase).IsAssignableFrom(typeof(T)))
{
var typePtr = Il2CppClassPointerStore<T>.NativeClassPtr;
var referenceClassPtr = IL2CPP.il2cpp_object_get_class(referencePointer);
if (RuntimeSpecificsStore.IsInjected(typePtr) &&
IL2CPP.il2cpp_class_is_assignable_from(typePtr, referenceClassPtr))
return ClassInjectorBase.GetMonoObjectFromIl2CppPointer(referencePointer) as T;
}
if (memberPointer == IntPtr.Zero)
return default;

return (T)ourCachedInstanceCtor.Invoke(new object[] { referencePointer });
return Il2CppObjectPool.Get<T>(memberPointer);
}

private static IntPtr AllocateArray(long size)
Expand Down
34 changes: 13 additions & 21 deletions Il2CppInterop.Runtime/InteropTypes/Arrays/Il2CppStringArray.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace Il2CppInterop.Runtime.InteropTypes.Arrays;

Expand All @@ -17,35 +18,26 @@ public Il2CppStringArray(long size) : base(AllocateArray(size))
{
}

public Il2CppStringArray(string[] arr) : base(AllocateArray(arr.Length))
public Il2CppStringArray(string?[] arr) : base(AllocateArray(arr.Length))
{
for (var i = 0; i < arr.Length; i++)
this[i] = arr[i];
}

#nullable disable
public override unsafe string this[int index]
{
get
{
if (index < 0 || index >= Length)
throw new ArgumentOutOfRangeException(nameof(index),
"Array index may not be negative or above length of the array");
var arrayStartPointer = IntPtr.Add(Pointer, 4 * IntPtr.Size);
var elementPointer = IntPtr.Add(arrayStartPointer, index * IntPtr.Size);
return IL2CPP.Il2CppStringToManaged(*(IntPtr*)elementPointer);
}
set
{
if (index < 0 || index >= Length)
throw new ArgumentOutOfRangeException(nameof(index),
"Array index may not be negative or above length of the array");
var arrayStartPointer = IntPtr.Add(Pointer, 4 * IntPtr.Size);
var elementPointer = IntPtr.Add(arrayStartPointer, index * IntPtr.Size);
*(IntPtr*)elementPointer = IL2CPP.ManagedStringToIl2Cpp(value);
}
get => IL2CPP.Il2CppStringToManaged(*GetElementPointer(index));
set => *GetElementPointer(index) = IL2CPP.ManagedStringToIl2Cpp(value);
}
#nullable enable
private unsafe IntPtr* GetElementPointer(int index)
{
ThrowIfIndexOutOfRange(index);
return (IntPtr*)IntPtr.Add(ArrayStartPointer, index * IntPtr.Size).ToPointer();
}

public static implicit operator Il2CppStringArray(string[] arr)
[return: NotNullIfNotNull(nameof(arr))]
public static implicit operator Il2CppStringArray?(string?[]? arr)
{
if (arr == null) return null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ static Il2CppStructArray()
StaticCtorBody(typeof(Il2CppStructArray<T>));
}

/// <summary>
/// The pointer to the first element in the array.
/// </summary>
private IntPtr ArrayStartPointer => IntPtr.Add(Pointer, 4 * IntPtr.Size);

public Il2CppStructArray(IntPtr nativeObject) : base(nativeObject)
{
}
Expand Down

0 comments on commit a7a7e38

Please sign in to comment.