Il2CppInterop
Il2CppInterop copied to clipboard
Il2CppSystem.ValueType parameters with default value throw an exception when not provided.
As an example and for ease of demonstration, unhollowed code from BONELAB is provided:
[CallerCount(14)]
[CachedScanResults(RefRangeStart = 929796, RefRangeEnd = 929810, XrefRangeStart = 929785, XrefRangeEnd = 929796, MetadataInitTokenRva = 0L, MetadataInitFlagRva = 0L)]
public unsafe static UniTask Delay([DefaultParameterValue(null)] int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = null)
{
System.IntPtr* ptr = stackalloc System.IntPtr[4];
*ptr = (nint)(&millisecondsDelay);
*(bool**)((byte*)ptr + checked((nuint)1u * unchecked((nuint)sizeof(System.IntPtr)))) = &ignoreTimeScale;
*(PlayerLoopTiming**)((byte*)ptr + checked((nuint)2u * unchecked((nuint)sizeof(System.IntPtr)))) = &delayTiming;
*(System.IntPtr*)((byte*)ptr + checked((nuint)3u * unchecked((nuint)sizeof(System.IntPtr)))) = IL2CPP.il2cpp_object_unbox(IL2CPP.Il2CppObjectBaseToPtrNotNull(cancellationToken));
System.Runtime.CompilerServices.Unsafe.SkipInit(out System.IntPtr exc);
System.IntPtr pointer = IL2CPP.il2cpp_runtime_invoke(NativeMethodInfoPtr_Delay_Public_Static_UniTask_Int32_Boolean_PlayerLoopTiming_CancellationToken_0, (System.IntPtr)0, (void**)ptr, ref exc);
Il2CppException.RaiseExceptionIfNecessary(exc);
return new UniTask(pointer);
}
The relevant portions are the following:
- The parameter
CancellationToken cancellationToken = null
- The conversion/packing of this parameter
IL2CPP.il2cpp_object_unbox(IL2CPP.Il2CppObjectBaseToPtrNotNull(cancellationToken))
The parameter is of class type CancellationToken
, extending Il2CppSystem.ValueType
(unhollowed version of the struct CancellationToken
)
In the source code for this method in the UniTask repository, this parameter is normally defined as CancellationToken cancellationToken = default(CancellationToken)
The unhollowed parameter being defined as null causes a problem when this method is called without filling out all parameters, because obviously Il2CppObjectBaseToPtrNotNull
throws an exception when it is null.
This can be remedied on a per-method basis by prefixing the offending methods and replacing parameters, but this is manual and still represents a bug in the generated IL. A solution could be making the IL mirror IL2CPP.Il2CppObjectBaseToPtrNotNull(cancellationToken ?? new CancellationToken())
, or keep a cached copy of a default CancellationToken and retrieving that field, to avoid recreating objects, but there's more complexity to keeping that field and possibly making sure it's not collected.