Skip to content

Commit

Permalink
CSScriptLib: restored ability to host on .NET Framework
Browse files Browse the repository at this point in the history
  • Loading branch information
oleg-shilo committed Jan 12, 2025
1 parent b53569d commit 1945af6
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 8 deletions.
1 change: 0 additions & 1 deletion src/BuildServer/out/build.runtimeconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"version": "9.0.0"
},
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
Expand Down
42 changes: 37 additions & 5 deletions src/CSScriptLib/src/CSScriptLib/Evaluator.Roslyn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,39 @@ public static void LoadCompilers()
CSharpScript.EvaluateAsync("1 + 2"); //this will loaded all required assemblies
}

// NOTE: it's important to keep ToMetadataOnCore and ToMetadataOnFramework in separate methods to prevent
// JIT from loading the code that is not compatible with the host app CLR version
static AssemblyMetadata ToMetadata(Assembly asm) => Runtime.IsCore ? ToMetadataOnCore(asm) : ToMetadataOnFramework(asm);

static AssemblyMetadata ToMetadataOnCore(Assembly asm)
{
// this way of loading metadata is faster than the one based on reading the assembly file
// but TryGetRawMetadata is not available on .NET Framework
unsafe
{
if (asm.TryGetRawMetadata(out var blob, out var length))
return AssemblyMetadata.Create(ModuleMetadata.CreateFromMetadata((IntPtr)blob, length));
return null;
}
}

static AssemblyMetadata ToMetadataOnFramework(Assembly asm)
{
// this way of loading metadata is slower than the one based on TryGetRawMetadata
// but TryGetRawMetadata is not available on .NET Framework

var asmPath = asm.Location();
if (asmPath.HasText())
try
{
byte[] assemblyBytes = File.ReadAllBytes(asmPath);
var moduleMetadata = ModuleMetadata.CreateFromImage(assemblyBytes);
var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata);
}
catch { }
return null;
}

/// <summary>
/// Compiles the specified script text.
/// </summary>
Expand Down Expand Up @@ -497,13 +530,12 @@ void add_code(string file, string[] codeLines, int lineOffset)

var refs = AppDomain.CurrentDomain.GetAssemblies(); // from appdomain
var explicitRefs = this.refAssemblies.Except(refs); // from code

foreach (var asm in refs.Concat(explicitRefs))
{
unsafe
{
if (asm.TryGetRawMetadata(out var blob, out var length))
references.Add(AssemblyMetadata.Create(ModuleMetadata.CreateFromMetadata((IntPtr)blob, length)).GetReference());
}
var metadata = ToMetadata(asm);
if (metadata != null)
references.Add(metadata.GetReference());
}

// add references from code and host-specified
Expand Down
22 changes: 22 additions & 0 deletions src/CSScriptLib/src/Client.NET-Framework (CodeDom)/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Xml.Linq;
using CSScripting;
using CSScriptLib;

Expand All @@ -13,6 +15,19 @@ namespace ConsoleApp1
class Program
{
static void Main(string[] args)
{
// This solution only provided for the demo purposes.
// note that CSScriptLib is compiled against the latest `Microsoft.CodeAnalysis.dll`. However .NET Framework does not
// support this version of `Microsoft.CodeAnalysis.dll` so the project packages are referencing older version of Microsoft.CodeAnalysis.dll
// but we need to use `SimpleAsmProbing` to load the compatible version of `Microsoft.CodeAnalysis.dll` at runtime.

using (SimpleAsmProbing.For(Assembly.GetExecutingAssembly().Location.GetDirName()))
{
main(args);
}
}

static void main(string[] args)
{
// note that csc.exe compiler references some assemblies by default so we need
// to use WithRefAssembliesFilter to avoid "referenced assembly duplication" compiler error
Expand All @@ -38,6 +53,13 @@ static void Main(string[] args)
Console.WriteLine(" next run: " + sw.ElapsedMilliseconds);
}

// private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
// {
// var ttt = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == args.Name);

// throw new NotImplementedException();
// }

static void Test_CodeDom()
{
dynamic script = CSScript.CodeDomEvaluator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,19 @@ class Program
{
static void Main(string[] args)
{
#pragma warning ("This solution will no longer work on .NET4.8 for the CS-Script versions starting from v4.8.13. The problem is caused by the latest Roslyn stopping supporting `System.Runtime.Loader` on .NET Framework.")
// this solution only provided for the demo purposes.
// This solution only provided for the demo purposes.
// note that CSScriptLib is compiled against the latest `Microsoft.CodeAnalysis.dll`. However .NET Framework does not
// support this version of `Microsoft.CodeAnalysis.dll` so the project packages are referencing older version of Microsoft.CodeAnalysis.dll
// but we need to use `SimpleAsmProbing` to load the compatible version of `Microsoft.CodeAnalysis.dll` at runtime.

using (SimpleAsmProbing.For(Assembly.GetExecutingAssembly().Location.GetDirName()))
{
main(args);
}
}

static void main(string[] args)
{
Test_Roslyn();

NetCompiler.EnableLatestSyntax();
Expand Down

0 comments on commit 1945af6

Please # to comment.