currently, I'm trying to use Roslyn C# compiler within my Unity project to compile some expressions during runtime so I can execute them as delegates instead of re-evaluating the expression text over and over again. Obviously, I need this for performance reasons.
After months of fighting against Unity's awkward behavior, I finally managed to implement about 90% of the business logic, install NuGet for Unity, include DynamicExpresso (which worked almost fine, but doesn't support lambda expressions, which I need) and try other NuGet packages like Microsoft.CodeDom.Providers.DotNetCompilerPlatform and Microsoft.CodeAnalysis as stated here.
The problems are:
- I started with some simple general functions of the form
f(x, y, z) = x * y + z, but this was too unflexible and has been abandoned quickly. DynamicExpressoworks, but doesn't support lambda expressions. It seems to be possible to rewrite the code to recursively handle lambda expressions, but I don't have enough time any more.Microsoft.CodeAnalysisbreaks the solution, because it has conflicts with other libraries and I had a hard time reverting this.Microsoft.CodeDom.Providers.DotNetCompilerPlatformat least doesn't break the solution and I could use it without any problems in a separate test project (which I've done tens of times before), however in Unity (editor and player) I get aSystem.Security.SecurityException(Couldn't impersonate token) when I try to compile withCSharpCodeProvider.CompileAssemblyFromSource(CompilerParameters, params string[]).
It seems there is some Roslyn support in Unity, but I'm unable to make it work. My code is like this:
using UnityEngine;
using CSCodeProvider = Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider;
string code = "public static class CompiledExpressions { public static int f(int x) => 2*x; }";
CompilerResults result = null;
Assembly asm = null;
var types = new List<Type>()
{
typeof(int),
typeof(Mathf),
typeof(GameObjectExtensions) // a helper class with extension functions for unimplemented trivial tasks with GameObjects
};
try
{
CSCodeProvider csprovider = new CSCodeProvider(new OurCompilerSettings()); // OurCompilerSettings just helps locate csc.exe
CompilerParameters cparams = new CompilerParameters();
cparams.GenerateExecutable = false;
cparams.GenerateInMemory = true;
cparams.ReferencedAssemblies.Clear();
cparams.ReferencedAssemblies.AddRange(types.Select(t => t.Assembly.Location).Distinct().ToArray());
result = csprovider.CompileAssemblyFromSource(cparams, new string[] { code }); // here comes the exception
if (!result.Errors.HasErrors)
asm = result.CompiledAssembly;
}
catch (Exception ex)
{
Debug.LogError(ex);
}
if (asm != null)
// do stuff
The error is:
System.Security.SecurityException: Couldn't impersonate token.
at System.Security.Principal.WindowsImpersonationContext..ctor (System.IntPtr token) [0x0001a] in <437ba245d8404784b9fbab9b439ac908>:0
at System.Security.Principal.WindowsIdentity.Impersonate (System.IntPtr userToken) [0x00000] in <437ba245d8404784b9fbab9b439ac908>:0
at Microsoft.CodeDom.Providers.DotNetCompilerPlatform.Compiler.RevertImpersonation () [0x00006] in <fefe506461e04012a8bd9f406d3641b2>:0
at Microsoft.CodeDom.Providers.DotNetCompilerPlatform.Compiler.FromSourceBatch (System.CodeDom.Compiler.CompilerParameters options, System.String[] sources) [0x000c1] in <fefe506461e04012a8bd9f406d3641b2>:0
at Microsoft.CodeDom.Providers.DotNetCompilerPlatform.Compiler.CompileAssemblyFromSourceBatch (System.CodeDom.Compiler.CompilerParameters options, System.String[] sources) [0x0001d] in <fefe506461e04012a8bd9f406d3641b2>:0
at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource (System.CodeDom.Compiler.CompilerParameters options, System.String[] sources) [0x00006] in <ae22a4e8f83c41d69684ae7f557133d9>:0
at MyProject.RoslynHelper.Compile () [0x0017e] in C:\Users\myname\source\repos\MyProject\MyProject\Assets\scripts\RoslynHelper.cs:137
0x00007FF77A09D52C (Unity) StackWalker::GetCurrentCallstack
0x00007FF77A0A0991 (Unity) StackWalker::ShowCallstack
0x00007FF7787E54D5 (Unity) GetStacktrace
0x00007FF77AD020CE (Unity) DebugStringToFile
0x00007FF77A1051E5 (Unity) DebugLogHandler_CUSTOM_Internal_Log
0x000001D42D6000EB (Mono JIT Code) (wrapper managed-to-native) UnityEngine.DebugLogHandler:Internal_Log (UnityEngine.LogType,UnityEngine.LogOption,string,UnityEngine.Object)
0x000001D4534AFF8B (Mono JIT Code) UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
0x000001D42D5F090E (Mono JIT Code) UnityEngine.Logger:Log (UnityEngine.LogType,object)
0x000001D42D5F055A (Mono JIT Code) UnityEngine.Debug:LogError (object)
0x000001D42D5E8B33 (Mono JIT Code) [RoslynHelper.cs:143] MyProject.RoslynHelper:Compile ()
0x000001D42D5910BB (Mono JIT Code) [Game.cs:107] MyProject.Game:.ctor ()
0x000001D45359FDCB (Mono JIT Code) [Game.cs:51] MyProject.Game:get_Instance ()
0x000001D42D613BDB (Mono JIT Code) [InteractionReceiver.cs:312] MyProject.InteractionReceiver:Update ()
0x000001D45317AC38 (Mono JIT Code) (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)
0x00007FF82844CBB0 (mono-2.0-bdwgc) [mini-runtime.c:2809] mono_jit_runtime_invoke
0x00007FF8283D2122 (mono-2.0-bdwgc) [object.c:2921] do_runtime_invoke
0x00007FF8283DB11F (mono-2.0-bdwgc) [object.c:2968] mono_runtime_invoke
0x00007FF77A01EFEE (Unity) scripting_method_invoke
0x00007FF77A018D3D (Unity) ScriptingInvocation::Invoke
0x00007FF779FE2965 (Unity) MonoBehaviour::CallMethodIfAvailable
0x00007FF779FE2A76 (Unity) MonoBehaviour::CallUpdateMethod
0x00007FF779690D38 (Unity) BaseBehaviourManager::CommonUpdate<BehaviourManager>
0x00007FF779699EB4 (Unity) BehaviourManager::Update
0x00007FF779AC7FE3 (Unity) `InitPlayerLoopCallbacks'::`2'::UpdateScriptRunBehaviourUpdateRegistrator::Forward
0x00007FF779AB13D8 (Unity) ExecutePlayerLoop
0x00007FF779AB14AD (Unity) ExecutePlayerLoop
0x00007FF779AB66F4 (Unity) PlayerLoop
0x00007FF777EC60FB (Unity) PlayerLoopController::UpdateScene
0x00007FF777EC4038 (Unity) Application::TickTimer
0x00007FF778809740 (Unity) MainMessageLoop
0x00007FF7788134DA (Unity) WinMain
0x00007FF77B79B012 (Unity) __scrt_common_main_seh
0x00007FF864D44034 (KERNEL32) BaseThreadInitThunk
0x00007FF867903691 (ntdll) RtlUserThreadStart
Has anyone an idea how to use runtime compiling within Unity?