Using Roslyn to Compile a C# Script at Runtime in Unity 6

Hi everyone! I am trying to move an app from the 2021 LTS to the upcoming one (Unity 6).

I am/was successfully using CSharpScript.EvaluateAsync to evaluate arbitrary C# code at runtime with the 2021 LTS.
I have imported the Microsoft.CodeAnalysis.CSharp.Scripting package and its dependencies as managed DLLs into my project and tried to run the following minimal script:

using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using System;
using UnityEngine;

public class TestScript : MonoBehaviour
{
    private async void Start()
    {
        string[] imports = new[] { "System", "UnityEngine" };
        var options = ScriptOptions.Default.AddImports(imports)
            .AddReferences(
            typeof(System.Object).Assembly,
            typeof(UnityEngine.Transform).Assembly,
            typeof(TestScript).Assembly
        );

        try
        {
            var result = await CSharpScript.EvaluateAsync("Debug.Log(\"Success!\")", options);
        }
        catch (Exception ex)
        {
            Debug.LogError(ex.Message + Environment.NewLine + ex.StackTrace);
        }
    }
}

This works in 2021 LTS, but produces the following error in both 2022 LTS and Unity 6 Preview:
(1,7): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.

The stack trace:

  at Microsoft.CodeAnalysis.Scripting.ScriptBuilder.ThrowIfAnyCompilationErrors (Microsoft.CodeAnalysis.DiagnosticBag diagnostics, Microsoft.CodeAnalysis.DiagnosticFormatter formatter) [0x0005c] in <6f66d36ae73e4c8480c58dfd5cd6dc5c>:0 
  at Microsoft.CodeAnalysis.Scripting.ScriptBuilder.CreateExecutor[T] (Microsoft.CodeAnalysis.Scripting.ScriptCompiler compiler, Microsoft.CodeAnalysis.Compilation compilation, System.Boolean emitDebugInformation, System.Threading.CancellationToken cancellationToken) [0x00040] in <6f66d36ae73e4c8480c58dfd5cd6dc5c>:0 
  at Microsoft.CodeAnalysis.Scripting.Script`1[T].GetExecutor (System.Threading.CancellationToken cancellationToken) [0x0002b] in <6f66d36ae73e4c8480c58dfd5cd6dc5c>:0 
  at Microsoft.CodeAnalysis.Scripting.Script`1[T].RunAsync (System.Object globals, System.Func`2[T,TResult] catchException, System.Threading.CancellationToken cancellationToken) [0x0001b] in <6f66d36ae73e4c8480c58dfd5cd6dc5c>:0 
  at Microsoft.CodeAnalysis.Scripting.Script`1[T].RunAsync (System.Object globals, System.Threading.CancellationToken cancellationToken) [0x00000] in <6f66d36ae73e4c8480c58dfd5cd6dc5c>:0 
  at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync[T] (System.String code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options, System.Object globals, System.Type globalsType, System.Threading.CancellationToken cancellationToken) [0x00019] in <5c854d48ef9d413facb69e5c1c208f03>:0 
  at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync[T] (System.String code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options, System.Object globals, System.Type globalsType, System.Threading.CancellationToken cancellationToken) [0x00000] in <5c854d48ef9d413facb69e5c1c208f03>:0 
  at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync (System.String code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options, System.Object globals, System.Type globalsType, System.Threading.CancellationToken cancellationToken) [0x00000] in <5c854d48ef9d413facb69e5c1c208f03>:0 
  at TestScript.Start () [0x00072] in D:\Unity\Projects\CSharpTest2022\Assets\TestScript.cs:21 
UnityEngine.Debug:LogError (object)
TestScript/<Start>d__0:MoveNext () (at Assets/TestScript.cs:25)
System.Runtime.CompilerServices.AsyncVoidMethodBuilder:Start<TestScript/<Start>d__0> (TestScript/<Start>d__0&)
TestScript:Start ()

I have tried to include the assembly with typeof(System.Object).Assembly above, to no avail.

As it is asking for a reference to netstandard, Version=2.1.0.0 (the runtime itself?) and not any other simple DLL, I am at a bit of a loss as to what I can do. I have tried changing the API compatibility level in the Player Settings window between .NET Standard and .NET Framework and sadly neither worked.

I would really like to port this over to a newer version of Unity, so I would appreciate any and all suggestions!

try

.AddReferences(MetadataReference.CreateFromFile(Assembly.Load("mscorlib.dll").Location));

or

.AddReferences(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location));

Edit: nvm dont use this if you use il2cpp, because mscorlib.dll does not exist at runtime and the above code will throw error that it can’t find mscorlib.dll

Thank you for the suggestion. I tried it on Unity 2022 LTS and Unity 6 Preview just now, and specifying the references by creating a MetadataReference from a file location sadly led to the same error message even on the Mono backend, but it got me to simplify the test case further to rely only on System:

public class TestScript3 : MonoBehaviour
{
    private async void Start()
    {
        var options = ScriptOptions.Default;
        options.AddReferences(typeof(System.Object).Assembly);
        try
        {
            var code = "System.IO.File.WriteAllText(\"D:/testfolder/test.txt\", \"Hello there!\");";
            var result = await CSharpScript.EvaluateAsync(code, options);
        }
        catch (Exception ex) { Debug.LogError(ex.Message + Environment.NewLine + ex.StackTrace); }
    }
}

This now gives a different error on 2022 LTS and Unity 6 Preview:
The method or operation is not implemented.

I have tried feeding in bogus paths which leads to an assembly load error, so it is loading a reference assembly whether I specify it by path ("mscorlib" or "netstandard") or with typeof(System.Object).Assembly. But somehow the API does not seem to be implemented in whatever assembly it is loading.

Does that help someone glean anything? Again, I would really appreciate any ideas.