您的位置:首页 > 移动开发 > Unity3D

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

2015-12-04 15:56 531 查看
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.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 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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: