3

I have a generic method for trying to get a generic value from a dictionary via key using my own TryGetValue like (of course cropped a lot to the essential)

public class BaseExample
{
    public virtual bool TryGetValue<T>(string key, out T value)
    {
        value = default;
        return false;
    }
}

public class Example : BaseExample
{
    private Dictionary<string, Item> _items = new Dictionary<string, Item>();

    public override bool TryGetValue<T>(string key, out T value)
    {
        value = default;
        if (!GetItem(key, value, out var setting)) { return false; }

        value = (T)setting.Value;
        return true;
    } 

    private bool GetItem<T>(string key, T value, out Item item)
    {
        item = null;

        if (!_items.ContainsKey(key))
        {
            item = null;
            return false;
        }

        item = _items[key];

        return true;
    }
}

This works in the Unity Editor but as soon as this is built to UWP and IL2CPP as soon as I try to run the method with e.g.

var value = example.TryGetValue<int>("SomeKey");

It throws a

 System.ExecutionEngineException: Attempting to call method 'Example::TryGetValue<System.Int32>' for which no ahead of time (AOT) code was generated.

What could be the reason for this and how can I fix it?

Hamid Yusifli
  • 9,688
  • 2
  • 24
  • 48
derHugo
  • 83,094
  • 9
  • 75
  • 115

3 Answers3

3

To work around an AOT issue like this, you can force the compiler to generate the proper code. You can add the following example method in your class:

public void UsedOnlyForAOTCodeGeneration() 
{
    // YOUR CODE HERE

    // Include an exception so we can be sure to know if this method is ever called.
    throw new InvalidOperationException(@"This method is used for AOT code generation only. 
    Do not call it at runtime.");
}
Jacksonkr
  • 31,583
  • 39
  • 180
  • 284
Hamid Yusifli
  • 9,688
  • 2
  • 24
  • 48
3

For my specific case (if it helps anyone) I was using

WrapperMethod<WeatherFrameInfo[]>();

WrapperMethod<T>() {
    JsonConvert.DeserializeObject<T>(json);
}

Which lead to the error:

2022/03/17 14:53:32.888 13468 13524 Error Unity ExecutionEngineException: Attempting to call method 'System.Collections.Generic.List`1[[WeatherFrameInfo, ScriptsAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::.cctor' for which no ahead of time (AOT) code was generated.
2022/03/17 14:53:32.888 13468 13524 Error Unity   at System.Reflection.RuntimeConstructorInfo.InternalInvoke (System.Object obj, System.Object[] parameters, System.Boolean wrapExceptions) [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtonsoft.Json.Utilities.LateBoundReflectionDelegateFactory+<>c__DisplayClass5_0`1[T].<CreateDefaultConstructor>b__1 () [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtonsoft.Json.Serialization.JsonArrayContract.CreateTemporaryCollection () [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewList (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonArrayContract contract, System.Boolean& createdFromNonDefaultCreator) [0x00000] in <00000000000000000000000000000000>:0 
2022/03/17 14:53:32.888 13468 13524 Error Unity   at Newtons

I theorized that Newtonsoft is converting my array input to a List at some point so I refactored my code to do that ahead of time and violá

WrapperMethod<List<WeatherFrameInfo>>();

WrapperMethod<T>() {
    JsonConvert.DeserializeObject<T>(json);
}
Jacksonkr
  • 31,583
  • 39
  • 180
  • 284
2

Ok after some further research and tests the conclusion to why this happened is as follows:

Since according method was part of an interface and only either called via interface or via the base class the Example.TryGetValue method was never explicitly called via this class.

Since the Example class lives in a different Assembly than the interface and Base class the code for Example.TryGetValue was somehow stripped of on compile time (since the compiler thinks it is never used anyway) and therefore missing later in the build.


I now know the reason and what to have in mind for the future but not yet a real solution except having an explicit implementation and calling that so it is not stripped of...

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Which class is it that implements the interface explicitly? Example or BaseExample or both? – NSJacob1 Jun 07 '20 at 17:30
  • If `BaseExample` implements the interface then so does `Example` right? ;) It's `BaseExample` in this case – derHugo Jun 07 '20 at 18:28
  • I was wondering if it changed the way it stripped if you explicitly implemented the interface on Example, instead of just through the inheritance of the Base class. – NSJacob1 Jun 08 '20 at 16:16