Are C# Expression Trees (or ILGenerator) allowed on iOS

So after switching to the 2017.1 experimental .NET 4.6 profile, I could finally leverage the full power of C# expression trees in my serialization framework. However, a good question arises: are C# expression trees (and thus ILGenerator) considered dynamic code generation by iOS, and thus not really allowed for iOS publishing?

Anyone has any insights into this?

Cheers!

as far as I know, everything that does dynamic code generation will either not compile or throw an exception on IL2CPP. you can test it on a build and see.

I don’t know if there is something that IL2CPP allows but is forbidden by Apple policies (short of writing your own bytecode interpreter)

You can parse, compile and execute expression trees on iOS with this asset using .CompileAot() extension on System.Linq.Expression.Expression instance.

The .NET 4.6 profile doesn’t change the IL2CPP (and Apple restrictions). It is still an ahead-of-time compiler which does not support dynamic code generation. Basically, you cannot use anything in the System.Reflection.Emit namespace.

Actually Unity team could implement IL interpreter for AOT environment. It would be in ‘gray’ area of Apple policy.

1 Like

Hey, link just goes to assert store front page for me…?

It is strange. I just copied it from browser address bar.
http://u3d.as/oCH ← link in old Asset Store.

1 Like

How about things under System.Linq.Expressions only? I might be a little misleading, in that I have no explicit usage of anything under the System.Reflection.Emit namespace, I exclusively use the Expression Trees (System.Linq.Expressions); but I somehow got the impression that under the hood, Expression Trees actually generates IL code during the “Compile()” phase?

So just to make the question exactly lean and mean: are C# Expression Trees (System.Linq.Expressions) allowed for iOS?

Hmm, I’ve actually got an idea: since my usage case doesn’t really involve “dynamic code generation”, even though the Expression Tree is used (with reflection) to inspect existing types and generate “logic” to process the data with the known types, it might actually be possible for me to take the compiled expression trees and create an assembly in a separate process (sort of a AOT process), and at the publishing or runtime, the generated assembly is shipped (or loaded at runtime), so there would be no runtime code generation whatsoever.

The bits I’m looking now is: LambdaExpression.CompileToMethod based on this SO post: https://stackoverflow.com/questions/15168150/how-to-save-an-expression-tree-as-the-main-entry-point-to-a-new-executable-disk

Please share your insights on this. Thanks again!

1 Like

I don’t think this will work. That example on Stack Overflow is using AssemblyBuilder, which is in System.Reflection.Emit. However to be sure, your best option is to try it in a small test project and see. We will do what we can to support dynamic code, as long as it does not violate an App Store restrictions.

2 Likes

that can be done in the editor though. I have something similar for protobuf.net: I generate the DLL from my data classes via an editor MenuItem and put it into the project.

2 Likes

Indeed, the editor uses Mono JIT, so runtime code generation is fully supported.

Exactly as M_R suggested, the assembly generation would be done in the editor (a separate phase), and the generated assembly would be put in the Unity project, and gets referenced from there.

A follow up question: does IL2CPP automatically process an (Unity compatible) assembly that resides in the current Unity project (e.g. under “Assets/DLL” folder, so it looks like: “Assets/DLL/MyAssembly.dll”)? Does IL2CPP work against “Assembly.Load(AssemblyName("MyAssembly"))”?

Note that I’m aware that Assembly.LoadFrom(assemblyFilePath) would not work (see: Il2CPP Loading C# Assembly at runtime - Unity Engine - Unity Discussions), but Assembly.Load(AssemblyName) seems only needing to find a “reference”, and I’m assuming it should work with IL2CPP (or with an IL2CPP processed assembly)?

Unfortunately, Assembly.Load(AssemblyName) does not work against IL2CPP ecosystem.

Based on my test (IL2CPP with the Xcode iOS simulator), if an assembly is referenced explicitly (such as invoking a function as defined inside the assembly in question), IL2CPP would pick the assembly up and does all the necessary transformation, and the generated C++ code is guaranteed to be referenced correctly, while if there are no explicit references to the assembly in question, the entire assembly would be stripped, and any runtime binding to the assembly would fail (even an in memory binding would fail, if implemented at all) - similar to the C++ linker that unreferenced symbols are stripped.

@lluo :

Yes, the behavior you see is expected. IL2CPP does not convert assemblies that are not directly used, in order to keep code size down. I would recommend using something from this assembly elsewhere in the project to get it pulled into the build.

If you are accessing parts of the code only via reflection, you will need to use a link.xml file to prevent the managed code stripper from removing that code as well.

I have heard that in C# recent version Expression.Compile has 2 implementation. It would fallback to use reflection interpreter in any platform that not support dynamic codegen

https://stackoverflow.com/a/47476886/1513380

If it’s true then it would be useful in some case that not really need performance. Such as let user add math script to control object movement

Would be anyone pleased to help test that is it really work on iOS and WebGL?

We don’t have support for an interpreter on iOS, WebGL, or any AOT platform in Unity. The Mono team has recently been working on an interpreter that might allow for something like this in the future, but it is not on the roadmap for Unity for the time being.

1 Like

According to my investigation the reflection interpreter is natively supported in .NET framework since before 4.0. If unity was brought dotnet framework as is I think it would also contain the interpreter in the 4.6 engine

Problem is I have no iOS device to test on my own. And the API for ensure interpreter usage is just added in 4.7.1