Как использовать компилятор Roslyn в Unity Player или во встроенной игре для выражений, которые могут содержать лямбда-выражения? - PullRequest
3 голосов
/ 16 марта 2020

В настоящее время я пытаюсь использовать компилятор Roslyn C# в своем проекте Unity для компиляции некоторых выражений во время выполнения, чтобы я мог выполнять их как делегаты вместо переоценки текста выражения снова и снова. Очевидно, мне это нужно по соображениям производительности.

После нескольких месяцев борьбы с неловким поведением Unity мне наконец удалось реализовать около 90% бизнес-логи c, установить NuGet для Unity, включая DynamicExpresso ( который работал почти нормально, но не поддерживает лямбда-выражения, которые мне нужны), и попробуйте другие пакеты NuGet, такие как Microsoft.CodeDom.Providers.DotNetCompilerPlatform и Microsoft.CodeAnalysis, как указано здесь .

Проблемы:

  • Я начал с некоторых простых общих функций вида f(x, y, z) = x * y + z, но это было слишком негибко и было заброшено быстро.
  • DynamicExpresso работает, но не поддерживает лямбда-выражения. Кажется возможным переписать код для рекурсивной обработки лямбда-выражений, но у меня больше нет времени.
  • Microsoft.CodeAnalysis нарушает решение, потому что оно конфликтует с другими библиотеками, и у меня был трудное время исправить это.
  • Microsoft.CodeDom.Providers.DotNetCompilerPlatform по крайней мере не нарушает решение, и я мог бы использовать его без каких-либо проблем в отдельном тестовом проекте (который я делал десятки раз ранее), однако в Unity (редактор и игрок) Я получаю System.Security.SecurityException (не могу выдать себя за токен), когда пытаюсь скомпилировать с CSharpCodeProvider.CompileAssemblyFromSource(CompilerParameters, params string[]).

Кажется, в Unity есть некоторая поддержка Roslyn, но я ' Я не могу заставить его работать. Мой код выглядит так:

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

Ошибка:

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

Кто-нибудь знает, как использовать компиляцию во время выполнения в Unity?

...