unity3d关于IOS的AOT编译注意事项 ( AOT Exception Patterns and Hacks )

来源:互联网 发布:cog数据库官网 编辑:程序博客网 时间:2024/05/29 05:53

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-onlyvar 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.ComapreExchange[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 typestatic 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 patchhttps://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.

0 0