Unity provides a common scripting API and experience across all platforms it supports. However, some platforms have inherent restrictions. To help you understand these restrictions, the following table describes which restrictions apply to each platform and scripting backend:
Platform (scripting backend) | Ahead-of-time compile | No threads | .NET Core class libraries subset | |
---|---|---|---|---|
Android (IL2CPP) | ✔ | |||
Android (Mono) | ||||
iOS (IL2CPP) | ✔ | |||
Standalone (IL2CPP) | ✔ | |||
Standalone (Mono) | ||||
Universal Windows Platform (IL2CPP) | ✔ | |||
Universal Windows Platform (.NET) | ✔ | |||
WebGL (IL2CPP) | ✔ | ✔ |
Some platforms do not allow runtime code generation. Therefore, any managed code which depends upon just-in-time (JIT) compilation on the target device will fail. Instead, you must compile all of the managed code ahead-of-time (AOT). Often, this distinction doesn’t matter, but in a few specific cases, AOT platforms require additional consideration.
An AOT platform cannot implement any of the methods in the System.Reflection.Emit
namespace. The rest of System.Reflection
is acceptable, as long as the compiler can infer that the code used via reflection needs to exist at runtime.
AOT platforms might encounter issues with serialization and deserlization because of the use of reflection. If a type or method is only used via reflection as part of serialization or deserialization, the AOT compiler cannot detect that it needs to generate the code needs for the type or method.
If you use generic methods, the compiler must do some additional work to expand your written code to the code executed on the device. For example, you need different code for List
with an int
or a double
. If you use virtual methods, where the behavior is determined at runtime rather than compile time, the compiler can easily require runtime code generation in places where it is not entirely obvious from the source code.
The following code example works exactly as expected on a JIT platform (it prints “Message value: Zero” to the console once):
using UnityEngine;
using System;
public class AOTProblemExample : MonoBehaviour, IReceiver
{
public enum AnyEnum
{
Zero,
One,
}
void Start()
{
// Subtle trigger: The type of manager *must* be
// IManager, not Manager, to trigger the AOT problem.
IManager manager = new Manager();
manager.SendMessage(this, AnyEnum.Zero);
}
public void OnMessage<T>(T value)
{
Debug.LogFormat("Message value: {0}", value);
}
}
public class Manager : IManager
{
public void SendMessage<T>(IReceiver target, T value) {
target.OnMessage(value);
}
}
public interface IReceiver
{
void OnMessage<T>(T value);
}
public interface IManager
{
void SendMessage<T>(IReceiver target, T value);
}
However, when you execute this code on an AOT platform with the IL2CPP scripting backend, this exception occurs:
ExecutionEngineException: Attempting to call method 'AOTProblemExample::OnMessage<AOTProblemExample+AnyEnum>' for which no ahead of time (AOT) code was generated.
at Manager.SendMessage[T] (IReceiver target, .T value) [0x00000] in <filename unknown>:0
at AOTProblemExample.Start () [0x00000] in <filename unknown>:0
Likewise, the Mono scripting backend provides this similar exception:
ExecutionEngineException: Attempting to JIT compile method 'Manager:SendMessage<AOTProblemExample/AnyEnum> (IReceiver,AOTProblemExample/AnyEnum)' while running with --aot-only.
at AOTProblemExample.Start () [0x00000] in <filename unknown>:0
The AOT compiler does not recognize that it should generate code for the generic method OnMessage
with a T
of AnyEnum
, so it continues, skipping this method. When that method is called, and the runtime can’t find the proper code to execute, it returns this error message.
To work around an AOT issue like this, you can force the compiler to generate the proper code. To do this, add the following example method to the AOTProblemExample
class:
public void UsedOnlyForAOTCodeGeneration()
{
// IL2CPP needs only this line.
OnMessage(AnyEnum.Zero);
// Mono also needs this line. Note that we are
// calling directly on the Manager, not the IManager interface.
new Manager().SendMessage(null, AnyEnum.Zero);
// 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.");
}
When the compiler encounters the explicit call to OnMessage
with a T
of AnyEnum
, it generates the proper code for the runtime to execute. The method UsedOnlyForAOTCodeGeneration
does not need to be called; it just needs to exist for the compiler to recognize it.
Managed methods that need to be marshaled to a C function pointer so that they can be called from native code have a few restrictions on AOT platforms:
[MonoPInvokeCallback]
attributeSome platforms do not support the use of threads, so any managed code that uses the System.Threading
namespace fails at runtime. Also, some parts of the .NET class libraries implicitly depend upon threads. An often-used example is the System.Timers.Timer
class, which depends on support for threads.
IL2CPP does not support C# exception filters. You should modify the code that depends on exception filters into the proper catch
blocks.
IL2CPP does not support the System.TypedReference
type or the __makeref
C# keyword.
IL2CPP does not support reflection of the MarhsalAs
and FieldOffset
attributes at runtime. It does support these attributes at compile time. You should use these for proper platform invoke marshaling.
IL2CPP does not support the C# dynamic
keyword. This keyword requires JIT compilation, which is not possible with IL2CPP.
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.