[Refactor-Minor]

- Refactored interface definition.
- Plugin Loading System Refactor (incomplete).
This commit is contained in:
MapleWheels
2024-12-20 16:17:14 -05:00
committed by Maplewheels
parent 4b2bac7cd8
commit d2b9ca4c1b
16 changed files with 563 additions and 1010 deletions
@@ -3,23 +3,20 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Threading;
using Barotrauma.Extensions;
using Barotrauma.LuaCs;
using Barotrauma.LuaCs.Events;
using Microsoft.CodeAnalysis;
using Basic.Reference.Assemblies;
using FluentResults;
using FluentResults.LuaCs;
using LightInject;
using Microsoft.CodeAnalysis.CSharp;
using OneOf;
using Path = Barotrauma.IO.Path;
using Path = System.IO.Path;
[assembly: InternalsVisibleTo(IAssemblyLoaderService.InternalsAwareAssemblyName)]
@@ -34,59 +31,92 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
private set => ModUtils.Threading.SetBool(ref _isDisposed, value);
}
private int _isDisposed;
/// <summary>
/// This bool-int wrapper increments/decrements when set as true/false respectively and return true if the value > 0.
/// </summary>
private bool AreOperationRunning
{
get => Interlocked.CompareExchange(ref _operationsRunning, 0, 0) > 0;
set // we use the set as our inc/decr
{
if (value)
{
Interlocked.Add(ref _operationsRunning, 1);
}
else
{
Interlocked.Add(ref _operationsRunning, -1);
}
}
}
private int _operationsRunning;
//internal
private readonly IAssemblyManagementService _assemblyManagementService;
private readonly IEventService _eventService;
private readonly Action<AssemblyLoader> _onUnload;
/// <summary>
/// This lock is just to ensure that we do not load while disposing
/// </summary>
private readonly ReaderWriterLockSlim _operationsLock = new(LockRecursionPolicy.SupportsRecursion);
private readonly ConcurrentDictionary<string, AssemblyDependencyResolver> _dependencyResolvers = new();
private readonly ConcurrentDictionary<AssemblyOrStringKey, AssemblyData> _loadedAssemblyData = new();
private ThreadLocal<bool> _isResolving = new(static()=>false); // cyclic resolution exit
#region PublicAPI
private readonly ThreadLocal<bool> _isResolving = new(static()=>false); // cyclic resolution exit
public AssemblyLoader(IAssemblyManagementService assemblyManagementService,
IEventService eventService,
Guid id, string name,
bool isReferenceOnlyMode, Action<AssemblyLoader> onUnload)
: base(isCollectible: true, name: name)
{
_assemblyManagementService = assemblyManagementService;
_eventService = eventService;
Id = id;
IsReferenceOnlyMode = isReferenceOnlyMode;
_onUnload = onUnload;
if (_onUnload is not null)
{
base.Unloading += OnUnload;
}
}
public IEnumerable<MetadataReference> AssemblyReferences
{
get
{
if (IsDisposed || _loadedAssemblyData.IsEmpty)
yield return null;
AreOperationRunning = true;
foreach (var data in _loadedAssemblyData.Values)
{
yield return data.AssemblyReference;
}
AreOperationRunning = false;
}
}
public FluentResults.Result AddDependencyPaths(ImmutableArray<string> paths)
{
if (paths.Length == 0)
return FluentResults.Result.Ok();
var res = new FluentResults.Result();
foreach (var path in paths)
if (IsDisposed)
return FluentResults.Result.Fail($"Loader is disposed!");
AreOperationRunning = true;
try
{
try
if (paths.Length == 0)
return FluentResults.Result.Ok();
var res = new FluentResults.Result();
foreach (var path in paths)
{
var p = Path.GetFullPath(path.CleanUpPath());
_dependencyResolvers[p] = new AssemblyDependencyResolver(p);
}
catch (Exception ex)
{
return res.WithError(new ExceptionalError(ex)
.WithMetadata(MetadataType.Sources, path));
try
{
var p = Path.GetFullPath(path.CleanUpPath());
_dependencyResolvers[p] = new AssemblyDependencyResolver(p);
}
catch (Exception ex)
{
return res.WithError(new ExceptionalError(ex)
.WithMetadata(MetadataType.Sources, path));
}
}
return FluentResults.Result.Ok();
}
finally
{
AreOperationRunning = false;
}
return FluentResults.Result.Ok();
}
public FluentResults.Result<Assembly> CompileScriptAssembly(
@@ -96,209 +126,303 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
ImmutableArray<MetadataReference> metadataReferences,
CSharpCompilationOptions compilationOptions = null)
{
if (assemblyName.IsNullOrWhiteSpace())
{
return new FluentResults.Result<Assembly>().WithError(new Error($"The name provided is null!")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, syntaxTrees));
}
if (_loadedAssemblyData.ContainsKey(assemblyName))
{
return new FluentResults.Result<Assembly>().WithError(new Error($"The name provided is already assigned to an assembly!")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, syntaxTrees));
}
var compilationAssemblyName = compileWithInternalAccess ? IAssemblyLoaderService.InternalsAwareAssemblyName : assemblyName;
compilationOptions ??= new CSharpCompilationOptions(
outputKind: OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
concurrentBuild: true,
reportSuppressedDiagnostics: true,
allowUnsafe: true);
if (!compileWithInternalAccess)
{
typeof(CSharpCompilationOptions)
.GetProperty("TopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic)
?.SetValue(compilationOptions, (uint)1 << 22);
}
using var asmMemoryStream = new MemoryStream();
var result = CSharpCompilation.Create(compilationAssemblyName, syntaxTrees, metadataReferences, compilationOptions).Emit(asmMemoryStream);
if (!result.Success)
{
var res = new FluentResults.Result().WithError(
new Error($"Compilation failed for assembly {assemblyName}!"));
var failuresDiag = result.Diagnostics.Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error);
foreach (var diag in failuresDiag)
{
res = res.WithError(new Error(diag.GetMessage())
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.ExceptionDetails, diag.Descriptor.Description));
}
return res;
}
asmMemoryStream.Seek(0, SeekOrigin.Begin);
if (IsDisposed)
return FluentResults.Result.Fail($"Loader is disposed!");
AreOperationRunning = true;
try
{
var data = new AssemblyData(LoadFromStream(asmMemoryStream), asmMemoryStream.ToArray());
_loadedAssemblyData[data.Assembly] = data;
return new FluentResults.Result<Assembly>().WithSuccess($"Compiled assembly {assemblyName} successful.").WithValue(data.Assembly);
if (assemblyName.IsNullOrWhiteSpace())
{
return new Result<Assembly>().WithError(new Error($"The name provided is null!")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, syntaxTrees));
}
if (_loadedAssemblyData.ContainsKey(assemblyName))
{
return new Result<Assembly>().WithError(new Error($"The name provided is already assigned to an assembly!")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, syntaxTrees));
}
var compilationAssemblyName = compileWithInternalAccess ? IAssemblyLoaderService.InternalsAwareAssemblyName : assemblyName;
compilationOptions ??= new CSharpCompilationOptions(
outputKind: OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
concurrentBuild: true,
reportSuppressedDiagnostics: true,
allowUnsafe: true);
if (!compileWithInternalAccess)
{
typeof(CSharpCompilationOptions)
.GetProperty("TopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic)
?.SetValue(compilationOptions,
(uint)1 << 25 // CSharp.BinderFlags.AllowAwaitInUnsafeContext
| (uint)1 << 22 // CSharp.BinderFlags.IgnoreAccessibility
| (uint)1 << 1 // CSharp.BinderFlags.SuppressObsoleteChecks
);
}
using var asmMemoryStream = new MemoryStream();
var result = CSharpCompilation.Create(compilationAssemblyName, syntaxTrees, metadataReferences, compilationOptions).Emit(asmMemoryStream);
if (!result.Success)
{
var res = new FluentResults.Result().WithError(
new Error($"Compilation failed for assembly {assemblyName}!")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, syntaxTrees));
var failuresDiag = result.Diagnostics.Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error);
foreach (var diag in failuresDiag)
{
res = res.WithError(new Error(diag.GetMessage())
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.ExceptionDetails, diag.Descriptor.Description));
}
return res;
}
asmMemoryStream.Seek(0, SeekOrigin.Begin);
try
{
var data = new AssemblyData(LoadFromStream(asmMemoryStream), asmMemoryStream.ToArray());
_loadedAssemblyData[data.Assembly] = data;
return new Result<Assembly>().WithSuccess($"Compiled assembly {assemblyName} successful.").WithValue(data.Assembly);
}
catch (Exception ex)
{
return new FluentResults.Result().WithError(new ExceptionalError(ex));
}
}
catch (Exception ex)
finally
{
return new FluentResults.Result().WithError(new ExceptionalError(ex));
AreOperationRunning = false;
}
}
public FluentResults.Result<Assembly> LoadAssemblyFromFile(string assemblyFilePath,
ImmutableArray<string> additionalDependencyPaths)
{
if (assemblyFilePath.IsNullOrWhiteSpace())
return new FluentResults.Result<Assembly>().WithError(new Error($"The path provided is null!"));
if (additionalDependencyPaths.Any())
{
var r = AddDependencyPaths(additionalDependencyPaths);
if (!r.IsFailed)
{
// we have errors, loading may not work.
return FluentResults.Result.Fail(new Error($"Failed to load dependency paths")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath))
.WithErrors(r.Errors);
}
}
string sanitizedFilePath = Path.GetFullPath(assemblyFilePath.CleanUpPath());
string directoryKey = Path.GetDirectoryName(sanitizedFilePath);
if (directoryKey is null)
{
return FluentResults.Result.Fail(new Error($"Unable to load assembly: bath file path: {assemblyFilePath}")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, sanitizedFilePath));
}
if (IsDisposed)
return FluentResults.Result.Fail($"Loader is disposed!");
AreOperationRunning = true;
try
{
var assembly = LoadFromAssemblyPath(sanitizedFilePath);
_loadedAssemblyData[assembly] = new AssemblyData(assembly, sanitizedFilePath);
return new Result<Assembly>().WithSuccess($"Loaded assembly'{assembly.GetName()}'").WithValue(assembly);
if (assemblyFilePath.IsNullOrWhiteSpace())
return new Result<Assembly>().WithError(new Error($"The path provided is null!"));
if (additionalDependencyPaths.Any())
{
var r = AddDependencyPaths(additionalDependencyPaths);
if (!r.IsFailed)
{
// we have errors, loading may not work.
return FluentResults.Result.Fail(new Error($"Failed to load dependency paths")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath))
.WithErrors(r.Errors);
}
}
string sanitizedFilePath = Path.GetFullPath(assemblyFilePath.CleanUpPath());
string directoryKey = Path.GetDirectoryName(sanitizedFilePath);
if (directoryKey is null)
{
return FluentResults.Result.Fail(new Error($"Unable to load assembly: bath file path: {assemblyFilePath}")
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, sanitizedFilePath));
}
try
{
var assembly = LoadFromAssemblyPath(sanitizedFilePath);
_loadedAssemblyData[assembly] = new AssemblyData(assembly, sanitizedFilePath);
return new Result<Assembly>().WithSuccess($"Loaded assembly'{assembly.GetName()}'").WithValue(assembly);
}
catch (ArgumentNullException ane)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(ane)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, ane.Message)
.WithMetadata(MetadataType.StackTrace, ane.StackTrace));
}
catch (ArgumentException ae)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(ae)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, ae.Message)
.WithMetadata(MetadataType.StackTrace, ae.StackTrace));
}
catch (FileLoadException fle)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(fle)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, fle.Message)
.WithMetadata(MetadataType.StackTrace, fle.StackTrace));
}
catch (FileNotFoundException fnfe)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(fnfe)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, fnfe.Message)
.WithMetadata(MetadataType.StackTrace, fnfe.StackTrace));
}
catch (BadImageFormatException bife)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(bife)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, bife.Message)
.WithMetadata(MetadataType.StackTrace, bife.StackTrace));
}
catch (Exception e)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(e)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, e.Message)
.WithMetadata(MetadataType.StackTrace, e.StackTrace));
}
}
catch (ArgumentNullException ane)
finally
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(ane)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, ane.Message)
.WithMetadata(MetadataType.StackTrace, ane.StackTrace));
}
catch (ArgumentException ae)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(ae)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, ae.Message)
.WithMetadata(MetadataType.StackTrace, ae.StackTrace));
}
catch (FileLoadException fle)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(fle)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, fle.Message)
.WithMetadata(MetadataType.StackTrace, fle.StackTrace));
}
catch (FileNotFoundException fnfe)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(fnfe)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, fnfe.Message)
.WithMetadata(MetadataType.StackTrace, fnfe.StackTrace));
}
catch (BadImageFormatException bife)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(bife)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, bife.Message)
.WithMetadata(MetadataType.StackTrace, bife.StackTrace));
}
catch (Exception e)
{
return FluentResults.Result.Fail<Assembly>(new ExceptionalError(e)
.WithMetadata(MetadataType.ExceptionObject, this)
.WithMetadata(MetadataType.RootObject, assemblyFilePath)
.WithMetadata(MetadataType.ExceptionDetails, e.Message)
.WithMetadata(MetadataType.StackTrace, e.StackTrace));
AreOperationRunning = false;
}
}
public FluentResults.Result<Assembly> GetAssemblyByName(string assemblyName)
{
if (IsDisposed)
return FluentResults.Result.Fail(new Error($"Loader is disposed!"));
if (assemblyName.IsNullOrWhiteSpace())
{
return FluentResults.Result.Fail(new Error($"Assembly name is null")
.WithMetadata(MetadataType.ExceptionObject, this));
}
if (_loadedAssemblyData.TryGetValue(assemblyName, out var data))
AreOperationRunning = true;
try
{
return new FluentResults.Result<Assembly>().WithSuccess(new Success($"Assembly found")).WithValue(data.Assembly);
}
foreach (var assembly1 in this.Assemblies.Where(a => !_loadedAssemblyData.ContainsKey(a)))
{
if (assembly1.GetName().FullName == assemblyName)
if (_loadedAssemblyData.TryGetValue(assemblyName, out var data))
{
try
{
if (!assembly1.Location.IsNullOrWhiteSpace())
{
_loadedAssemblyData[assembly1] = new AssemblyData(assembly1, assembly1.Location);
}
// we don't have the original byte array so we can't store it.
}
catch (NotSupportedException nse) // dynamic assembly or location property threw
{
// ignored
}
return new FluentResults.Result<Assembly>().WithSuccess(new Success($"Assembly found")).WithValue(assembly1);
return new Result<Assembly>().WithSuccess(new Success($"Assembly found")).WithValue(data.Assembly);
}
}
return FluentResults.Result.Fail(new Error($"Assembly named { assemblyName } not found!"));
// search any assemblies that were background loaded and we're unaware of.
foreach (var assembly1 in this.Assemblies.Where(a => !_loadedAssemblyData.ContainsKey(a)))
{
if (assembly1.GetName().FullName == assemblyName)
{
try
{
if (!assembly1.Location.IsNullOrWhiteSpace())
{
_loadedAssemblyData[assembly1] = new AssemblyData(assembly1, assembly1.Location);
}
// we don't have the original byte array so we can't store it.
}
catch (NotSupportedException nse) // dynamic assembly or location property threw
{
// ignored
}
return new Result<Assembly>().WithSuccess(new Success($"Assembly found")).WithValue(assembly1);
}
}
return FluentResults.Result.Fail(new Error($"Assembly named { assemblyName } not found!"));
}
finally
{
AreOperationRunning = false;
}
}
public FluentResults.Result<ImmutableArray<Type>> GetTypesInAssemblies()
{
if (IsDisposed)
return FluentResults.Result.Fail(new Error($"Loader is disposed!"));
AreOperationRunning = true;
try
{
return new FluentResults.Result<ImmutableArray<Type>>().WithValue(_loadedAssemblyData.SelectMany(kvp=> kvp.Value.Types).ToImmutableArray());
return new FluentResults.Result<ImmutableArray<Type>>().WithValue(_loadedAssemblyData
.SelectMany(kvp => kvp.Value.Types).ToImmutableArray());
}
catch (Exception e)
{
return FluentResults.Result.Fail(new ExceptionalError(e));
}
finally
{
AreOperationRunning = false;
}
}
public IEnumerable<Type> UnsafeGetTypesInAssemblies()
{
if (IsDisposed)
yield return null;
AreOperationRunning = true;
try
{
if (_loadedAssemblyData.None())
{
yield return null;
}
else
{
foreach (var assemblyData in _loadedAssemblyData.Values)
{
foreach (var type in assemblyData.Types)
{
yield return type;
}
}
}
}
finally
{
AreOperationRunning = false;
}
}
public Result<Type> GetTypeInAssemblies(string typeName)
{
if (IsDisposed)
return FluentResults.Result.Fail(new Error($"Loader is disposed!"));
AreOperationRunning = true;
try
{
if (_loadedAssemblyData.IsEmpty)
return FluentResults.Result.Fail(new Error($"No assemblies loaded!"));
foreach (var assemblyData in _loadedAssemblyData)
{
if (assemblyData.Value.TypesByName.TryGetValue(typeName, out var type))
return new FluentResults.Result<Type>().WithSuccess($"Found type.").WithValue(type);
}
return FluentResults.Result.Fail(new Error($"No matching types found for { typeName }!"));
}
finally
{
AreOperationRunning = false;
}
}
public void Dispose()
{
Dispose(true);
if (IsDisposed)
return; // we don't want to invoke events twice nor cause strong GC handles.
IsDisposed = true;
this.Unload();
GC.SuppressFinalize(this);
}
#endregion
#region Internals
protected override Assembly Load(AssemblyName assemblyName)
{
if (_isResolving.Value)
@@ -309,8 +433,8 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
{
if (_loadedAssemblyData.TryGetValue(assemblyName.FullName, out var data))
return data.Assembly;
var idSpan = new[] { this.Id };
if (_assemblyManagementService.GetLoadedAssembly(assemblyName, in idSpan) is { IsSuccess: true } ret)
var ids = new[] { this.Id };
if (_assemblyManagementService.GetLoadedAssembly(assemblyName, in ids) is { IsSuccess: true } ret)
return ret.Value;
return null;
}
@@ -334,28 +458,22 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
private void OnUnload(AssemblyLoadContext context)
{
IsDisposed = true;
// Try to wait for loading ops on other threads if they happen to occur.
// Minor race condition on the loop exit but this loader is not intended to be thread-safe by design, this is just to cover edge cases.
DateTime timeout = DateTime.Now.AddSeconds(5);
while (timeout > DateTime.Now)
{
if (!AreOperationRunning)
break;
}
base.Unloading -= OnUnload;
var wf = new WeakReference<IAssemblyLoaderService>(this);
_eventService.PublishEvent<IEventAssemblyContextUnloading>((sub) => sub.OnAssemblyUnloading(wf));
_onUnload?.Invoke(this);
this.Dispose(true);
}
private void Dispose(bool disposing)
{
if (ModUtils.Threading.CheckClearAndSetBool(ref _isDisposed))
{
_operationsLock.EnterWriteLock();
try
{
_loadedAssemblyData.Clear();
}
finally
{
_operationsLock.ExitWriteLock();
}
}
this._dependencyResolvers.Clear();
this._loadedAssemblyData.Clear();
}
private readonly record struct AssemblyData
@@ -364,6 +482,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
public readonly OneOf<byte[], string> AssemblyImageOrPath;
public readonly MetadataReference AssemblyReference;
public readonly ImmutableArray<Type> Types;
public readonly ImmutableDictionary<string, Type> TypesByName;
public AssemblyData(Assembly assembly, byte[] assemblyImage)
{
@@ -371,6 +490,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
AssemblyImageOrPath = assemblyImage ?? throw new ArgumentNullException(nameof(assemblyImage));
AssemblyReference = MetadataReference.CreateFromImage(assemblyImage);
Types = assembly.GetSafeTypes().ToImmutableArray();
TypesByName = Types.ToImmutableDictionary(type => type.FullName, type => type);
}
public AssemblyData(Assembly assembly, string path)
@@ -379,6 +499,7 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
AssemblyImageOrPath = path ?? throw new ArgumentNullException(nameof(path));
AssemblyReference = MetadataReference.CreateFromFile(path);
Types = assembly.GetSafeTypes().ToImmutableArray();
TypesByName = Types.ToImmutableDictionary(type => type.FullName, type => type);
}
}
@@ -423,6 +544,4 @@ public sealed class AssemblyLoader : AssemblyLoadContext, IAssemblyLoaderService
public static implicit operator AssemblyOrStringKey(Assembly assembly) => new AssemblyOrStringKey(assembly);
public static implicit operator AssemblyOrStringKey(string name) => new AssemblyOrStringKey(name);
}
#endregion
}