IL2CPP: We’ve spotted a case where delegate invocation fails 100% of the time when used in conjunction with nested callback(s) and C# extension methods. We’ve submitted a bug report (695900) and we’re posting here in case anyone else is experiencing the same problem.
We’ve simplified the test case to the following. It may look like a strange use-case but the pattern is quite common; it’s just the simplification that makes it look odd.
using System;
using UnityEngine;
public class TestCase : MonoBehaviour
{
void Start ()
{
Action action = null;
NestedCallback(action.InvokeIfNotNull);
}
private void NestedCallback(Action action)
{
action();
}
}
public static class DelegateExtensions
{
public static void InvokeIfNotNull(this Action action)
{
if (action != null)
action();
}
}
The crash occurs because the delegate passed to InvokeIfNotNull should be null, but it arrives as an instance. This is clearly wrong. The CPP then attempts to GetVirtualInvokeData and crashes.
The code above executes correctly when compiled with csc and msc so is likely a new use case for il2cpp to consider.
The following code does not crash, suggesting that the wrapping of the named extension method on line 10 above (action.InvokeIfNotNull) and subsequent invocation on line 15 produces incorrect CPP where the delegate is considered to be instantiated when it should reference null.
Action action = null;
action.InvokeIfNotNull();
The workaround is to avoid wrapping calls to named extension methods of a delegate where the delegate is null. The following code executes without crashing:
Action action = null;
NestedCallback(() => {
if (action != null)
action();
});
For those with the occasional use of this pattern, the above workaround is acceptable. For those with more frequent use the incorrect implementation of the CPP is a big problem. It currently prevents us releasing with IL2CPP compiled code and therefore 64-bit iOS support.