Skip to content
This repository has been archived by the owner on Feb 16, 2024. It is now read-only.

AOT Exception Patterns and Hacks

Roman edited this page Apr 11, 2019 · 3 revisions

Runnning on iOS sometimes raise runtime exception.

System.ExecutionEngineException: Attempting to JIT compile method

Generics is easy to cause a problem of AOT. UniRx use generics pipeline heavyly. But we cannnot be afraid if we know a pattern and counterplan. If you can't avoid AOT problem, please report to GitHub issues.

Interlocked.CompareExchange

// ExecutionEngineException: Attempting to JIT compile method '(wrapper native-to-managed)' while running with --aot-only
var a = "hoge";
Interlocked.CompareExchange<string>(ref a, "hugahuga", "hoge");

// It's Ok! CompareExchange(int, object, etc...) works.
object b = "hoge";
Interlocked.CompareExchange(ref b, "hugahuga", "hoge");

We can't use Interlocked.CompareExchange[T]. (wrapper native-to-managed) exception needs MonoPInvokeCallback(see:Unity TroubleShooting), but Interlocked.CompareExchange[T] is internal, it's in mscorlib.dll. Alterenate, we can use Interlocked.CompareExchange(object).

Interface + GenericsMethod + Struct

The Three pair kills application.

public interface IMyInterface
{
    void MyMethod<T>(T x);
}

public class MyImpl : IMyInterface
{
    public void MyMethod<T>(T x)
    {
    }
}

IMyInterface intf = new MyImpl();
intf.MyMethod("hogehoge"); // ReferenceType is not dead

// System.ExecutionEngineException: Attempting to JIT compile method 'MyImpl:MyMethod<int> (int)' while running with --aot-only.
intf.MyMethod(100); // die

We can avoid by compiler hint.

// don't need call, write specified type anywhere. 
static void _CompilerHint()
{
    new MyImpl().MyMethod(default(int));
}

void Awake()
{
    IMyInterface intf = new MyImpl();
    intf.MyMethod(100); // Struct but not dead
}

Lambda + Generics + Struct

static void Death<T>()
{
    var act = new Action<T>(_ => { Debug.Log("hoge"); }); // not dead yet 

    // System.ExecutionEngineException: Attempting to JIT compile method '<Death>b__0<int> (int)' while running with --aot-only.
    act(default(T)); // die
}

// call with struct cause exception(If class run perfectly) 
Death<int>();

We can avoid by capture external value.

static void Death<T>()
{
    var _dummy = 0;
    var act = new Action<T>(_ => 
    {
        Debug.Log("hoge");

        _dummy.GetHashCode(); // capture external variable
    });

    act(default(T)); // not dead
}

Death<int>();

This technique is very useful for use UniRx because Rx(LINQ) use lambda heavily.

GenericsMethod + Struct + Class

This is complex and seriously problem.

public static void Run()
{
    // call by reference type cause exception
    // System.ExecutionEngineException: Attempting to JIT compile method 'Method2<int, object> ()' while running with --aot-only.
    Method1<object>();
}

public static void Method1<T1>()
{
    Method2<int, T1>(); // one side as value type
}

// Two type args and has return type
static string Method2<T1, T2>()
{
    return "";
}

Enum Array(List)

Enum is sometimes dangerous.

public enum MyEnum
{
    Apple
}
 
// System.ExecutionEngineException: Attempting to JIT compile method '(wrapper managed-to-managed) MyEnum[]:System.Collections.Generic.ICollection`1.CopyTo (UniRx.MyEnum[],int)' while running with --aot-only.
new[] { MyEnum.Apple }.ToArray();

We can avoid UniRx's Utility - AsSafeEnumerable.

new[] { MyEnum.Apple }.AsSafeEnumerable().ToArray();

AotSafe Utilities

Struct often cause storange behaviour. You can wrap IEnumerable/IObservable's element to Tuple1(as class wrapper).

Enumerable.Range(1, 10).WrapValueToClass(); // IEnumerable<Tuple<int>>
Observable.Range(1, 10).WrapValueToClass(); // IObservable<Tuple<int>>

LINQ and AOT

Some LINQ methods can't work on iOS. It's not LINQ limitation because mono 2.8 already fixed(such as this patch https://github.com/mono/mono/commit/071f495d6a4ce4951e2b2c9069586bd5bcde5fbb ). But Unity's mono runtime is 2.6. I post upgrade Enumerable.cs request on Unity Feedback. Upgrade Enumerable.cs for avoid AOT Problem of LINQ(Average etc...) .I'm glad to if you vote.