Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

IL2CPP Type.MakeGenericType work around

Discussion in 'iOS and tvOS' started by JoshPeterson, Mar 18, 2015.

  1. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    As you may have experienced, IL2CPP does not yet provide full support for the Type.MakeGenericType method. Specifically, Type.MakeGenericType will only work for generic types which already exist in the code that IL2CPP has process ahead-of-time. For some code, (e.g. serailizers), this is a serious restriction. In the past, we have recommended that users work around this problem by explicitly declaring the necessary generic types in C# code (e.g. in a class derived from MonoBehaviour) like this:

    Code (CSharp):
    1. public static SomeGenericType<SomeOtherType> _unused;
    This work around is still valid, and the best solution, if it is possible. However, often the generic type is internal to an assembly, and cannot be used. For example, the MonoProperty type is internal to mscorlib.dll. So if you happen to see an exception at run-time like this:

    It is not possible to follow our normal work around and declare a field like this:

    Code (CSharp):
    1. public static System.Reflection.MonoProperty.Getter<string, string> _unused;
    So to work around this issue, we have added the ability for the user specify which types IL2CPP should always generate, even if they are not used C# code. Starting in (the upcoming) 4.6.3p4 release, you can create a file named il2cpp_extra_types.txt in the Assets directory (or any subdirectory of it) in your project. This file should contain a new entry for a type on each line, like this:

    SomeGenericType`1[SomeOtherType]
    System.Reflection.MonoProperty/Getter`2[System.String, System.String]


    Note two things about this syntax:
    1. It is not C# syntax for generic types, but rather, is it the syntax of the type name you should see in the exception message from MakeGenericType (when it fails). See this MSDN page for more information.
    2. For a nested type, like MonoProperty.Getter, there are a few different syntax choices. At 4.6.3p4, we do not support the "MonoProperty+Getter" syntax, but only the "MonoProperty/Getter" syntax. We will be improving this in the next patch release.
    We realize this is only another work around, and it has some limitations. We're continuing to work on full Type.MakeGenericType support. Although we can't yet set a release date, it is getting close. We hope this work around will help some projects until we have full MakeGenericType support ready.
     
  2. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    With the 4.6.4p1 release today, we now have support for Type.MakeGenericType for type arguments that are reference types. For those types, this workaround will not be necessary any longer.

    We do not support Type.MakeGenericType for type arguments that are value types, although some support for those cases may be coming in a future release. This work around is still necessary for value type cases.
     
  3. ccklokwerks

    ccklokwerks

    Joined:
    Oct 21, 2014
    Posts:
    62
    Josh, what is the state of this with respect to Unity 5? I'm currently on 5.0.1 and wondering what the support level is with 5.0.2 or 5.1.
     
  4. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @ccklokwerks

    Type.MakeGenericType is now fully supported, and should work as it does with the Mono scripting backend. There may be some bugs (although we have not had any in a while). I would recommend using the 5.0.2 version though, as 5.0.1 may have still had some partial support missing.

    Note that 5.1 and 5.0.2 will be pretty close with respect to IL2CPP code, as we keep all versions, 4.6, 5.0, and 5.1 on the same IL2CPP code base (with slight variations due to the release calendars). In the 5.1 series, you'll get the latest IL2CPP code and fixes in 5.1.0p1 (out this week), if you're looking to be on the leading edge.
     
  5. madebysoren

    madebysoren

    Joined:
    Aug 25, 2014
    Posts:
    1
    Hi Josh,

    I get the following even though I am on Unity 5.1.2f1:

    This happens at compile time, so I am not able to run the application. Any idea of what I am doing wrong, assuming that it should work now?

    Thanks,

    Søren
     
  6. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @madebysoren

    This looks like a subtly different error than the one discussed in this thread. This error looks like it is coming from the Mono C# compiler, and not IL2CPP. It still may be worthwhile to submit a bug report on this issue though, so that we can investigate it. At the moment, I don't have any suggestions about how to correct it.
     
  7. subjectZero

    subjectZero

    Joined:
    Oct 19, 2014
    Posts:
    37
    @JoshPeterson Im so glad i found this thread. Could you maybe help me out by pointing me in the right direction. I've only been coding for a while, I'm not that good at it. Here is the error I'm getting, this happens when i try run the game in Xcode on 64bit, testing on device . i just want to find out if this is the same error you are talking about. here is the error

    Code (CSharp):
    1. InvalidOperationException
    2.   at System.Reflection.MonoMethod.MakeGenericMethod (System.Type[] methodInstantiation) [0x00000] in <filename unknown>:0
    3.   at System.Linq.Queryable.MakeGeneric (System.Reflection.MethodBase method, System.Type[] parameters) [0x00000] in <filename unknown>:0
    4.   at System.Linq.Queryable.Where[TSource] (IQueryable`1 source, System.Linq.Expressions.Expression`1 predicate) [0x00000] in <filename unknown>:0
    5.   at AzureServicesForUnity.QueryHelpers.Linq.EasyTableQuery`1[T].Where (System.Linq.Expressions.Expression`1 predicate) [0x00000] in <filename unknown>:0
    6.   at hwsh_Login_Manager.hwsh_Select () [0x00000] in <filename unknown>:0
    7.   at hwsh_Login_Manager.AuthCallback (ILoginResult result) [0x00000] in <filename unknown>:0
    8.   at Facebook.Unity.CallbackManager.TryCallCallback[T] (System.Object callback, IResult result) [0x00000] in <filename unknown>:0
    9.   at Facebook.Unity.CallbackManager.CallCallback (System.Object callback, IResult result) [0x00000] in <filename unknown>:0
    10.   at Facebook.Unity.CallbackManager.OnFacebookResponse (IInternalResult result) [0x00000] in <filename unknown>:0
    11. (Filename: currently not available on il2cpp Line: -1)
    I am running unity 5.3.6f1

    Thank you in advance.

    P.S. the AzureServicesForUnity.QueryHelpers.Linq.EasyTableQuery is a custom script.
     
    Last edited: Oct 1, 2016
  8. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @subjectZero

    I suspect that this is a different issue from the one discussed in this thread, since IL2CPP now has full support for generic types.

    However, we can still try to sort out the cause of this problem. Can you provide/show the code in AzureServicesForUnity.QueryHelpers.Linq.EasyTableQuery? I would like to get more information about what that code is going in order to better understand what might be the cause.
     
    subjectZero likes this.
  9. subjectZero

    subjectZero

    Joined:
    Oct 19, 2014
    Posts:
    37
    Thanks Josh

    Here you can find a copy of the project https://www.dropbox.com/s/jv8swwgdkzhzn6v/AzureServicesForUnity Unity v5.4.zip?dl=0
    (i made a small version less than 5mb)

    This is the code we are executing.

    Code (CSharp):
    1.  public void SelectFiltered()
    2.     {
    3.         SelectFilteredExecute(false);
    4.     }
    5.     public void SelectFilteredCount()
    6.     {
    7.         SelectFilteredExecute(true);
    8.     }
    9.     private void SelectFilteredExecute(bool includeTotalCount) //This is the method we call
    10.     {
    11.         EasyTableQueryHelper<Highscore> queryHelper = new EasyTableQueryHelper<Highscore>();
    12.         string pn = "d";
    13.         var query = queryHelper.Where(x => x.score > 500 || x.playername.StartsWith(pn)).OrderBy(x => x.score);
    14.         if (includeTotalCount)
    15.             query = query.IncludeTotalCount();
    16.         EasyTables.Instance.SelectFiltered<Highscore>(query, x =>
    17.         {
    18.             if (x.Status == CallBackResult.Success)
    19.             {
    20.                 foreach (var item in x.Result.results)
    21.                 {
    22.                     Debug.Log(string.Format("ID is {0},score is {1}", item.id, item.score ));
    23.                 }
    24.                 if (includeTotalCount)
    25.                 {
    26.                     StatusText.text = string.Format("Brought {0} rows out of {1}", x.Result.results.Count(), x.Result.count);
    27.                 }
    28.                 else
    29.                 {
    30.                     StatusText.text = string.Format("Brought {0} rows", x.Result.results.Count());
    31.                 }
    32.             }
    33.             else
    34.             {
    35.                 ShowError(x.Exception.Message);
    36.             }
    37.         });
    38.         StatusText.text = "Loading...";
    39.     }
    40.     private void ShowError(string error)
    41.     {
    42.         Debug.Log(error);
    43.         StatusText.text = "Error: " + error;
    44.     }
    Here is the error:
    Code (CSharp):
    1.     InvalidOperationException
    2.       at System.Reflection.MonoMethod.MakeGenericMethod (System.Type[] methodInstantiation) [0x00000] in <filename unknown>:0
    3.       at System.Linq.Queryable.MakeGeneric (System.Reflection.MethodBase method, System.Type[] parameters) [0x00000] in <filename unknown>:0
    4.       at System.Linq.Queryable.Where[TSource] (IQueryable`1 source, System.Linq.Expressions.Expression`1 predicate) [0x00000] in <filename unknown>:0
    5.       at AzureServicesForUnity.QueryHelpers.Linq.EasyTableQuery`1[T].Where (System.Linq.Expressions.Expression`1 predicate) [0x00000] in <filename unknown>:0
    6.       at hwsh_Login_Manager.hwsh_Select () [0x00000] in <filename unknown>:0
    7.       at hwsh_Login_Manager.AuthCallback (ILoginResult result) [0x00000] in <filename unknown>:0
    8.       at Facebook.Unity.CallbackManager.TryCallCallback[T] (System.Object callback, IResult result) [0x00000] in <filename unknown>:0
    9.       at Facebook.Unity.CallbackManager.CallCallback (System.Object callback, IResult result) [0x00000] in <filename unknown>:0
    10.       at Facebook.Unity.CallbackManager.OnFacebookResponse (IInternalResult result) [0x00000] in <filename unknown>:0
    11.     (Filename: currently not available on il2cpp Line: -1)
    This is what the following code does:
    Code (CSharp):
    1. “AzureServicesForUnity.QueryHelpers.Linq.EasyTableQuery`1[T].Where
    2.         public EasyTableQuery<T> Where(Expression<Func<T, bool>> predicate)
    3.         {
    4.             if (predicate == null)
    5.             {
    6.                 throw new ArgumentNullException("predicate");
    7.             }
    8.             return this.QueryProvider.Create<T>(Queryable.Where(this.Query, predicate), this.Parameters, this.RequestTotalCount);
    9.         }
    In the example project just run any button with "select" on it to reproduce the error.

    Thank you extremely much for assisting us.
     
    Last edited: Oct 9, 2016
  10. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @subjectZero

    I think that this should work, so it looks like a bug. It works when I test it with the Mono scripting backend, at least. So IL2CPP should support it as well.

    Can you submit a bug report via the Unity editor and include a link to the project as you have provided it here? That way we can provide you proper feedback when the bug is corrected.
     
  11. subjectZero

    subjectZero

    Joined:
    Oct 19, 2014
    Posts:
    37
    @JoshPeterson

    Thanks so much for your feedback. I saw this post last night, and i wanted to prepare the bug report today and reply with the link to the bug report. But some weird stuff is going down. As far as i can deduce, the latest builds of unity bug reports are looked at first. And seeing as we have been waiting for a bug to be fixed (this was fixed in unity 5.5.0 b6 and another bug in unity 5.5.0 b7), i thought that i should submit the bug report with the latest version, correct?

    But now a bad access exception is thrown when the script is fired. I've tried using Il2CppExceptionWrapper and il2cpp_codegen_raise_exception but with the current version of unity the il2cpp_codegen_raise_exception literally makes Xcode freeze. So what would you suggest? As i was writing this i received correspondence via twitter and it was suggested that i submit both issues with the different versions of unity respectively. i will quickly make them and just post the links here.

    Thank you for your assistance.
     
  12. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Yes, please submit both bug reports. At least for IL2CPP bugs, we have a very low outstanding bug count, so we look at all bugs pretty quickly, not matter the version. I can't speak for bugs in other areas though.
     
    subjectZero likes this.
  13. subjectZero

    subjectZero

    Joined:
    Oct 19, 2014
    Posts:
    37
  14. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Thanks! We will investigate these bugs.
     
    subjectZero likes this.
  15. firestoke

    firestoke

    Joined:
    Oct 21, 2015
    Posts:
    13
    hi, I meet the same issue as subjectZero:

    Code (CSharp):
    1. get an exception: System.InvalidOperationException:
    2.   at System.Reflection.MonoMethod.MakeGenericMethod_impl (System.Type[] types) [0x00000] in <filename unknown>:0
    3.   at System.Reflection.MonoMethod.MakeGenericMethod (System.Type[] methodInstantiation) [0x00000] in <filename unknown>:0
    4.   at MyJsonObject.List2JsonArray[T] (System.Collections.Generic.List`1 aList) [0x00000] in <filename unknown>:0
    5.   at GameData.toJSON () [0x00000] in <filename unknown>:0
    6.   at Equipment.parseJSON (LitJson.JsonData data) [0x00000] in <filename unknown>:0
    7.   at GameData.save (System.String filePath) [0x00000] in <filename unknown>:0
    8.   at GameData.reset (Boolean bClearAll) [0x00000] in <filename unknown>:0
    9.   at MainScene.confirmedReset () [0x00000] in <filename unknown>:0
    10.   at MyConfirmDialog+OnOkayDelegate.Invoke () [0x00000] in <filename unknown>:0
    11.   at MyConfirmDialog.doOkay () [0x00000] in <filename unknown>:0
    12.   at UnityEngine.Events.UnityAction.Invoke () [0x00000] in <filename unknown>:0
    13.   at UnityEngine.Events.InvokableCall.Invoke (System.Object[] args) [0x00000] in <filename unknown>:0
    14.   at UnityEngine.Events.PersistentCall..ctor () [0x00000] in <filename unknown>:0
    15.   at UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0
    16.   at UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0
    17.   at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in <filename unknown>:0
    18.   at UnityEngine.UI.Button.Press () [0x00000] in <filename unknown>:0
    19.   at UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) [0x00000] in <filename unknown>:0
    20.   at UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerExitHandler handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00000] in <filename unknown>:0
    21.   at UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00000] in <filename unknown>:0
    22.   at UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1].Invoke (.T1 handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00000] in <filename unknown>:0
    23.   at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) [0x00000] in <filename unknown>:0
    24.   at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchPress (UnityEngine.EventSystems.PointerEventData pointerEvent, Boolean pressed, Boolean released) [0x00000] in <filename unknown>:0
    25.   at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchEvents () [0x00000] in <filename unknown>:0
    26.   at UnityEngine.EventSystems.StandaloneInputModule.Process () [0x00000] in <filename unknown>:0
    27.   at AdventureScene.generateNextWaveEnemies () [0x00000] in <filename unknown>:0
    28.   at UnityEngine.EventSystems.EventSystem.Update () [0x00000] in <filename unknown>:0
    29. UnityEngine.DebugLogHandler:Internal_Log(LogType, String, Object)
    30. UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
    31. UnityEngine.Debug:get_isDebugBuild()
    32. UnityEngine.Logger:Log(LogType, Object)
    33. LitJson.JsonData:System.Collections.IList.Remove(Object)
    34. UnityEngine.Debug:LogWarning(Object)
    35. GameData:save(String)
    36. GameData:reset(Boolean)
    37. MainScene:confirmedReset()
    38. OnOkayDelegate:Invoke()
    39. MyConfirmDialog:doOkay()
    40. UnityEngine.Events.UnityAction:Invoke()
    41. UnityEngine.Events.InvokableCall:Invoke(Object[])
    42. UnityEngine.Events.PersistentCall:.ctor()
    43. UnityEngine.Events.InvokableCallList:Invoke(Object[])
    44. UnityEngine.Events.UnityEventBase:Invoke(Object[])
    45. UnityEngine.Events.UnityEvent:Invoke()
    46. UnityEngine.UI.Button:Press()
    47. UnityEngine.UI.Button:OnPointerClick(PointerEventData)
    48. UnityEngine.EventSystems.ExecuteEvents:Execute(IPointerExitHandler, BaseEventData)
    49. UnityEngine.EventSystems.ExecuteEvents:Execute(IPointerClickHandler, BaseEventData)
    50. UnityEngine.EventSystems.EventFunction`1:Invoke(T1, BaseEventData)
    51. UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
    52. UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
    53. UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
    54. UnityEngine.EventSystems.StandaloneInputModule:Process()
    55. AdventureScene:generateNextWaveEnemies()
    56. UnityEngine.EventSystems.EventSystem:Update()
    here is the related code:
    Code (CSharp):
    1.     public static JsonData List2JsonArray<T>(List<T> aList)
    2.     {
    3.         var arr = new JsonData();
    4.         arr.SetJsonType(JsonType.Array);
    5.         if (aList != null)
    6.         {
    7.             foreach (var elm in aList)
    8.             {
    9.                 JsonData v = default(JsonData);
    10.                 if (typeof(T) == typeof(JsonData))
    11.                 {
    12.                     v = (JsonData)System.Convert.ChangeType(elm, typeof(JsonData));
    13.                 }
    14.                 else
    15.                 {
    16.                     string methodName = "toJSON";
    17.                     MethodInfo method = typeof(T).GetMethod(methodName);
    18.                     if (method != null)
    19.                     {
    20.                         MethodInfo generic = method.MakeGenericMethod();
    21.                         if (generic != null)
    22.                         {
    23.                             v = (JsonData)generic.Invoke(elm, null);
    24.                         }
    25.                         else
    26.                         {
    27.                             Debug.LogWarning("wrong paramters! methodName: " + methodName);
    28.                         }
    29.                     }
    30.                     else
    31.                     {
    32.                         // the element is not a MyJsonObject class
    33.                         if (typeof(T).IsEnum)
    34.                         { // if it's a enum value, convert it to integer first
    35.                             v = new JsonData(Utils.enum2Int(elm));
    36.                         }
    37.                         else
    38.                         {
    39.                             v = new JsonData(elm);
    40.                         }
    41.                     }
    42.                 }
    43.                 arr.Add(v);
    44.             }
    45.         }
    46.         else
    47.         {
    48.             Debug.LogWarning("the list parameter is null!");
    49.         }
    50.         return arr;
    51.     }
    and the configuration:


    I have tested in Unity 5.4.2f2, 5.4.3f1, 5.6.0f1.
    this problem all happened. any suggestion?

    there is no problem if I build the Android app or I change the scripting backend to Mono.
     
    Last edited: May 2, 2017
  16. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @firestoke

    Can you submit a bug report with this project? It might a general AOT limitation (which means we could help find a work around), or it might be a bug in Unity that we can correct.
     
  17. firestoke

    firestoke

    Joined:
    Oct 21, 2015
    Posts:
    13
  18. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Thanks, we will investigate this issue.
     
  19. Sprakle

    Sprakle

    Joined:
    Jun 8, 2013
    Posts:
    31
    I think I've found another way to break IL2CPP with MakeGenericType.

    The following code causes an ExecutionEngineException when run on iOS:

    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using UnityEngine;
    4.  
    5. namespace Assets
    6. {
    7.     public class BrokenLoader : MonoBehaviour
    8.     {
    9.         private void Start()
    10.         {
    11.             Type brokenType = typeof (MyClass<>);
    12.  
    13.             brokenType = brokenType.MakeGenericType(typeof (int));
    14.  
    15.             ConstructorInfo constructor = brokenType.GetConstructor(new Type[0]);
    16.  
    17.             object instance = constructor.Invoke(new object[0]);
    18.  
    19.             Debug.Log(instance);
    20.         }
    21.     }
    22.    
    23.     [Preserve]
    24.     public class MyClass<T>
    25.     {
    26.         [Preserve]
    27.         public MyClass()
    28.         {
    29.         }
    30.     }
    31.  
    32.     public class Preserve : Attribute
    33.     {
    34.        
    35.     }
    36. }
    I was able to replicate this problem on Unity 5.6.0f3 and 2017.1f1

    Bug Report: https://fogbugz.unity3d.com/default.asp?925933_259rbo56fr9d99lm
     
  20. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    @stuffses

    I believe that this is expected behavior for any AOT compile (IL2CPP or Mono). Generic types used with value types as the generic argument cannot be easily created by AOT compilers at runtime. We have a few ideas about how to improve this case, but we don't have anything production-ready yet.

    Is this an example of a more general problem in your code? If so, we might be able to suggest a work around, given more context.
     
  21. mafff

    mafff

    Joined:
    Jan 16, 2015
    Posts:
    16
    @JoshPeterson

    Did you add any support for value types and MakeGenericType in the more recent versions of il2cpp?
     
  22. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    No, we don't have anything new yet. We're working on it at the moment, actually. But I don't know yet when it will be ready.
     
  23. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    Hi, any news if value types in MakeGenericType on IL2CPP is working yet or will be working? If not, any workarounds? I can't think of another way around
     
  24. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    We do not have general support for MakeGenericType with value types in IL2CPP yet.

    As a work around, you can use all of of the generic types you will need in some dummy class that is available only at compile time. This will cause IL2CPP to generate the proper code required for MakeGenericType to work, but it can be a bit tedious.
     
  25. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    This doesnt really work in my case, since generic functions will be added by the user so I would not know what instances they would want
     
  26. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Then I don't have a good work around, unfortunately.
     
  27. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    That a shame :/ is this something that is still on the roadmap?
     
  28. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Yes, we do plan to implement it, but I don't have an ETA yet.
     
  29. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    Alright thanks!
     
  30. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    Is it possible to make this without being a MonoBehaviour derived class?
    My idea is to generate the class using Cecil so there is no manual work here.

    Does this also mean that for every method/field/property i want to access using Reflections must be explicitly added to this class?

    Thanks
     
  31. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Yes, that is possible. You will need to make sure the generated class has a [Preserve] attribute though so that it is not removed by the managed linker.

    No, that is not necessary. This is about creation of generic types and methods at run time only. You won't need to use this approach for non-generic types and methods.
     
    GilCat likes this.
  32. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    I managed to get it working like that.
    I can't access the methods unless I put a Preserve attribute. I want to access the constructor in order to instantiate a concrete instance of a generic type.
    If I put the [Preserve] on the type itself no luck, only if on the constructor I can go along.
     
  33. LVermeulen

    LVermeulen

    Joined:
    Jun 26, 2013
    Posts:
    39
    For the dummy class to make MakeGenericType work on ValueTypes - does it need a snippet for every possible combination of reference/value types? Like if I have Func<float,MonoBehavior>, do I also need Func<float,Component> to allow both to be created by MakeGenericType?

    We also allow the user to create generic functions so a massive amount of generated code snippets might not work. I think we might end up just replacing the value type with Object and then doing the type checking at runtime
     
  34. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Yes, unfortunately you will need every possible combination.

    If you do use System.Object instead, that will alleviate this issue, the generic code for all reference types is the same, so IL2CPP can find the proper code to call at run time.
     
  35. LVermeulen

    LVermeulen

    Joined:
    Jun 26, 2013
    Posts:
    39
    Thanks! Is it possible MakeGenericType will eventually work with Value types? Or has that been seen as not possible after a few attempts

    I think using System.Object should work for us, though then there is unavoidable Boxing allocations
     
  36. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Yes, it is possible, although that will come with some runtime performance cost. We don't have an ETA for releasing this support now, although it is something we are actively working on. Of course, there is a chance that the work won't pan out and may never be released.
     
  37. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    I would say runtime cost is better than "doesn't work" ;)
     
  38. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Yeah, it depends on your use case, which is something we're looking into.
     
    QFSW likes this.
  39. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    I would argue that if you're using dynamic enough reflection to require MakeGenericType, then you've accepted the performance hit. Granted, better performance is always better, but for this it is not a blocker in my opinion. You can always get it working then improve it later
     
  40. samfisher-cf

    samfisher-cf

    Joined:
    Jun 13, 2016
    Posts:
    5
    Does the il2cpp_extra_types.txt method still work? Needed it for a generic but I don't see it being passed to il2cpp.exe nor the specified generic class being generated in the output cpps. The class is in a third party dll in the Plugins folder. Tested on 2019.4.15f1.

    This is what I had in my txt file (names replaced for confidentiality, but same qualified name layout) :
    Utilities.Pool/Wrapper`2[System.Collections.Generic.Dictionary`2[System.Int32, Utilities.Lib.SomeContainerClass/SomeStruct], System.Collections.Generic.KeyValuePair`2[System.Int32, Utilities.Lib.SomeContainerClass/SomeStruct]]
     
  41. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    I would not recommend using il2cpp_extra_types.txt, as it is not really a supported feature. It was always kind of a work around. I believe that it was removed in some Unity version, although I'm not sure exactly when it was removed.

    Is it possible to instead use this class in C# code that you control?
     
  42. LVermeulen

    LVermeulen

    Joined:
    Jun 26, 2013
    Posts:
    39
    Is there any check we can do to see if a MakeGenericType created type will work? Looks like it only gives an exception once the instance is created
     
  43. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    I don't believe it is possible to check at compile time, unfortunately. The check can only occur at run time now.
     
  44. QFSW

    QFSW

    Joined:
    Mar 24, 2015
    Posts:
    2,906
    Perhaps I misunderstand them, but I didn't think they meant a compile time check, but more of a runtime check function that can be used before trying to create it
     
  45. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Oh, sorry, I misunderstood. At runtime you can wrap the call the MakeGenericType in a try/catch, catch the exception it will throw when it fails, and continue.
     
  46. LVermeulen

    LVermeulen

    Joined:
    Jun 26, 2013
    Posts:
    39
    It only fails on trying to create a instance of the type - not on just creating the type with MakeGenericType. Doesnt look like there is a way to check if a Type will work
     
  47. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    What does the failure look like? I thought it was a managed exception, but maybe I'm incorrect.
     
  48. LVermeulen

    LVermeulen

    Joined:
    Jun 26, 2013
    Posts:
    39
    Code (CSharp):
    1.  
    2. ExecutionEngineException: Attempting to call method 'EventVariable`1[[System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.ctor' for which no ahead of time (AOT) code was generated.
    3.  
    4. System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters) (at <00000000000000000000000000000000>:0)
    5. System.Activator.CreateInstance (System.Type type, System.Boolean nonPublic) (at <00000000000000000000000000000000>:0)
    6.  
    Looks like the only exception is on using CreateInstance. Something we can work around but a way to check before creating a instance would be useful
     
  49. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Ahh ok, that makes sense. The System.Type can be created, but the VM doesn't know if an instance of that type can be created until the code attempts to create one.
     
  50. zero27x

    zero27x

    Joined:
    Mar 22, 2018
    Posts:
    4
    @


    Hello, I came across your discussion, and I think I'm at the right place. I have the code

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class InOutParser : MonoBehaviour
    5. {
    6.     List<KeyValuePair<string, //In-Out key
    7.         List<KeyValuePair<string, //Class key
    8.             List<KeyValuePair<string,  //Var key
    9.                 List<KeyValuePair<string, string>> "value"=>"3"])
    10.                 >>
    11.             >>
    12.         >> responseList = new List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>>>>();
    13.  
    14.     public InOutParser(string strOut = "", string strIn = "")
    15.     {
    16.         if (strOut.Trim().Length != 0)
    17.         {
    18.             responseList.Add(new KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>>>("Out", getParsedList(strOut)));
    19.         }
    20.         if (strIn.Trim().Length != 0)
    21.         {
    22.             responseList.Add(new KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>>>("In", getParsedList(strOut)));
    23.         }
    24.     }
    25.  
    26.     private List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>> getParsedList(string str)
    27.     {
    28.         str = str.Trim();
    29.         List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>> resp = new List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>>();
    30.         string[] splitted = str.Split(';');
    31.         for (int i = 0; i < splitted.Length; i++)
    32.         {
    33.             string var_change = splitted[i];
    34.             if (var_change.Length > 0)
    35.             {
    36.                 string[] splitted_var = var_change.Split('.');
    37.                 string varClass = splitted_var[0];
    38.                 resp.Add(new KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>(varClass, new List<KeyValuePair<string, List<KeyValuePair<string, string>>>>()));
    39.                 string action = "equal";
    40.                 string[] subVarSplitted = splitted_var[1].Split('=');
    41.                 string varName = subVarSplitted[0];
    42.                 string value = subVarSplitted[1];
    43.                 switch (subVarSplitted[1][0].ToString())
    44.                 {
    45.                     case "/":
    46.                         action = "divide";
    47.                         value = value.Substring(1);
    48.                         break;
    49.  
    50.                     case "*":
    51.                         action = "multiply";
    52.                         value = value.Substring(1);
    53.                         break;
    54.  
    55.                     case "+":
    56.                         action = "plus";
    57.                         value = value.Substring(1);
    58.                         break;
    59.  
    60.                     case "-":
    61.                         action = "minus";
    62.                         value = value.Substring(1);
    63.                         break;
    64.  
    65.                 }
    66.                 List<KeyValuePair<string, string>> listChanges = new List<KeyValuePair<string, string>>();
    67.                 listChanges.Add(new KeyValuePair<string, string>("action", action));
    68.                 listChanges.Add(new KeyValuePair<string, string>("value", value));
    69.                 List<KeyValuePair<string, List<KeyValuePair<string, string>>>> classVars = new List<KeyValuePair<string, List<KeyValuePair<string, string>>>>();
    70.                 int index = resp.Count;
    71.                 int t_i = 0;
    72.                 foreach (var pair in resp)
    73.                 {
    74.                     if (pair.Key.Equals(varClass))
    75.                     {
    76.                         index = t_i;
    77.                         classVars = pair.Value;
    78.                     }
    79.                     t_i++;
    80.                 }
    81.                List<KeyValuePair<string, List<KeyValuePair<string, string>>>>> pair)
    82.              
    83.                 classVars.Add(new KeyValuePair<string, List<KeyValuePair<string, string>>>(varName, listChanges));
    84.                 resp[index] = new KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>(varClass, classVars);
    85.                 //Debug.Log("InOutParser: class: " + varClass+" var: "+varName+" action: "+action+" value: "+value);
    86.  
    87.             }
    88.         }
    89.         return resp;
    90.     }
    91.  
    92.     public List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>> getOutChanges()
    93.     {
    94.         List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>> resp = null;
    95.         foreach (var pair in responseList)
    96.         {
    97.             if (pair.Key.Equals("Out")) resp = pair.Value;
    98.         }
    99.         return resp;
    100.     }
    101.     public List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>> getInChanges()
    102.     {
    103.         List<KeyValuePair<string, List<KeyValuePair<string, List<KeyValuePair<string, string>>>>>> resp = null;
    104.         foreach (var pair in responseList)
    105.         {
    106.             if (pair.Key.Equals("In")) resp = pair.Value;
    107.         }
    108.        
    109.         return resp;
    110.     }
    111. }
    112.  
    When building arm64 il2cpp, on the device I get this error,
    ExecutionEngineException: The field 'responseList' in type 'InOutParser' has a type which was not generated by il2cpp.exe. Consider using a generic type which is not nested so deeply.
    and the program stops executing. What are the solutions, I'm new to this, and I do not understand at all what I am doing wrong