4

This question is specifically about Unity3d IL2CPP and iOS.

Calling a generic method using reflection

class SourceValue<T> { public T value; }
class TargetValue<T> { public T value; }

static TargetValue<T> GenericMethod<T> (SourceValue<T> source) {
    return new TargetValue<T> { value = source.value };
}

void Main () {
    Type genericType = typeof(SourceValue<float>);
    Type typeArg = genericType.GenericTypeArguments[0];
    MethodInfo mi = GetType ().GetMethod ("GenericMethod", Flags | BindingFlags.Static);
    MethodInfo gmi = mi.MakeGenericMethod (typeArg);
    object src = new SourceValue<float> { value = 0.5f };
    object trg = gmi.Invoke (this, new object[] { src });
}

This works as expected when run in the Unity editor on mac. The invocation fails on iOS with error:

ExecutionEngineException: Attempting to call method 'GenericMethod<System.Single>' for which no ahead of time (AOT) code was generated.
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <00000000000000000000000000000000>:0  

Is it simply that the AOT system can't call generic methods or am i missing something?

Matti Jokipii
  • 561
  • 1
  • 6
  • 20

1 Answers1

10

Yes, since IL2CPP is an ahead-of-time (AOT) compiler, it only works for code that exists at compile time. No code here uses GenericMethod<float> in "real" C# source code, so IL2CPP does not know to generate the corresponding code to make that implementation work.

The real restriction here is that the generic argument type is float, which is a value type. You could use string (a reference type) in this case without any problems. IL2CPP shares the implementation of all generic types that have a generic argument which is a reference type (e.g. string, object, etc.). This is possible because all reference types in C# are the same size (IntrPtr.Size, to be exact).

So the limitation here is really two-fold:

  1. IL2CPP can only generate code is knows about at compile time
  2. This restriction applies only to generic types when the type argument is a value type.

Note that it is theoretically possible for IL2CPP to also share the implementation of generic types with value type generic arguments, although that has not yet been implemented.

EDIT: As of Unity 2022.2 this has been implemented - ExecutionEngineException will not longer happen, and the original scenario will work.

Josh Peterson
  • 2,299
  • 19
  • 21
  • Thank you so much! This answers all of my questions and gives me an excellent solution to the real life problem i need to tackle. I think i can handle most common value types as special cases with non generics and have rest of the code extendable with any non value type. This is superb! From another point of view, would GenericMethod> work? where Holder is a generic non value type. – Matti Jokipii May 17 '19 at 20:16
  • That will work if Holder is used in non-reflection C# code so that IL2CPP can notice it and generate code for it. – Josh Peterson May 20 '19 at 11:50
  • @JoshPeterson - Please could you kindly clarify in your answer if there needs to be a constraint on T that it's not a value type (like where T : class?) to avoid this issue – Jinglesting May 25 '23 at 12:28
  • Actually, I can change this answer some, as of Unity version 2022.2, this is no longer a problem at all! – Josh Peterson May 26 '23 at 17:05