2 using System.Collections.Generic;
3 using System.Collections.Immutable;
4 using System.Diagnostics.CodeAnalysis;
7 using System.Reflection;
8 using System.Runtime.CompilerServices;
9 using System.Runtime.Loader;
10 using Microsoft.CodeAnalysis;
11 using Microsoft.CodeAnalysis.CSharp;
14 [assembly: InternalsVisibleTo(
"CompiledAssembly")]
32 private readonly Dictionary<string, AssemblyDependencyResolver> _dependencyResolvers =
new();
40 this._assemblyManager = assemblyManager;
42 base.Unloading += OnUnload;
50 public AssemblyLoadingSuccessState
LoadFromFiles([NotNull] IEnumerable<string> assemblyFilePaths)
52 if (assemblyFilePaths is
null)
53 throw new ArgumentNullException(
54 $
"{nameof(MemoryFileAssemblyContextLoader)}::{nameof(LoadFromFiles)}() | The supplied filepath list is null.");
56 foreach (
string filepath
in assemblyFilePaths)
59 if (filepath.IsNullOrWhiteSpace())
61 string sanitizedFilePath = System.IO.Path.GetFullPath(filepath.CleanUpPath());
62 string directoryKey = System.IO.Path.GetDirectoryName(sanitizedFilePath);
64 if (directoryKey is
null)
65 return AssemblyLoadingSuccessState.BadFilePath;
68 if (!_dependencyResolvers.ContainsKey(directoryKey) || _dependencyResolvers[directoryKey] is
null)
70 _dependencyResolvers[directoryKey] =
new AssemblyDependencyResolver(sanitizedFilePath);
76 LoadFromAssemblyPath(sanitizedFilePath);
79 catch (ArgumentNullException ane)
81 ModUtils.Logging.PrintError($
"MemFileACL::{nameof(LoadFromFiles)}() | Error loading file path {sanitizedFilePath}. Details: {ane.Message} | {ane.StackTrace}");
82 return AssemblyLoadingSuccessState.BadFilePath;
84 catch (ArgumentException ae)
86 ModUtils.Logging.PrintError($
"MemFileACL::{nameof(LoadFromFiles)}() | Error loading file path {sanitizedFilePath}. Details: {ae.Message} | {ae.StackTrace}");
87 return AssemblyLoadingSuccessState.BadFilePath;
89 catch (FileLoadException fle)
91 ModUtils.Logging.PrintError($
"MemFileACL::{nameof(LoadFromFiles)}() | Error loading file path {sanitizedFilePath}. Details: {fle.Message} | {fle.StackTrace}");
92 return AssemblyLoadingSuccessState.CannotLoadFile;
94 catch (FileNotFoundException fnfe)
96 ModUtils.Logging.PrintError($
"MemFileACL::{nameof(LoadFromFiles)}() | Error loading file path {sanitizedFilePath}. Details: {fnfe.Message} | {fnfe.StackTrace}");
97 return AssemblyLoadingSuccessState.NoAssemblyFound;
99 catch (BadImageFormatException bife)
101 ModUtils.Logging.PrintError($
"MemFileACL::{nameof(LoadFromFiles)}() | Error loading file path {sanitizedFilePath}. Details: {bife.Message} | {bife.StackTrace}");
102 return AssemblyLoadingSuccessState.InvalidAssembly;
107 LuaCsLogger.LogError($
"Unable to load dependency assembly file at {filepath.CleanUpPath()} for the assembly named {CompiledAssembly?.FullName}. | Data: {e.Message} | InnerException: {e.InnerException}");
109 LuaCsLogger.ShowErrorOverlay($
"Unable to load dependency assembly file at {filepath} for the assembly named {CompiledAssembly?.FullName}. | Data: {e.Message} | InnerException: {e.InnerException}");
111 return AssemblyLoadingSuccessState.ACLLoadFailure;
115 return AssemblyLoadingSuccessState.Success;
136 [NotNull]
string assemblyName,
137 [NotNull] IEnumerable<SyntaxTree> syntaxTrees,
138 IEnumerable<MetadataReference> externMetadataReferences,
139 [NotNull] CSharpCompilationOptions compilationOptions,
140 out
string compilationMessages,
141 IEnumerable<Assembly> externFileAssemblyReferences =
null)
143 compilationMessages =
"";
147 return AssemblyLoadingSuccessState.AlreadyLoaded;
150 var externAssemblyRefs = externFileAssemblyReferences is not
null ? externFileAssemblyReferences.ToImmutableList() : ImmutableList<Assembly>.Empty;
151 var externAssemblyNames = externAssemblyRefs.Any() ? externAssemblyRefs
152 .Where(a => a.FullName is not
null)
153 .Select(a => a.FullName).ToImmutableHashSet()
154 : ImmutableHashSet<string>.Empty;
157 if (assemblyName.IsNullOrWhiteSpace())
158 throw new ArgumentNullException(
159 $
"{nameof(MemoryFileAssemblyContextLoader)}::{nameof(CompileAndLoadScriptAssembly)}() | The supplied assembly name is null!");
161 if (syntaxTrees is
null)
162 throw new ArgumentNullException(
163 $
"{nameof(MemoryFileAssemblyContextLoader)}::{nameof(CompileAndLoadScriptAssembly)}() | The supplied syntax tree is null!");
166 List<MetadataReference> metadataReferences =
new();
167 if (externMetadataReferences is not
null)
168 metadataReferences.AddRange(externMetadataReferences);
171 metadataReferences.AddRange(AssemblyLoadContext.Default.Assemblies
174 if (a.IsDynamic || string.IsNullOrWhiteSpace(a.Location) || a.Location.Contains(
"xunit"))
176 if (a.FullName is null)
178 return !externAssemblyNames.Contains(a.FullName);
180 .Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
181 .Union(externAssemblyRefs
182 .Where(a => !(a.IsDynamic ||
string.IsNullOrEmpty(a.Location) || a.Location.Contains(
"xunit")))
183 .Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
187 if (loadedAcls.Any())
192 if(loadedAcl?.Acl is
null || loadedAcl.Acl.IsTemplateMode || loadedAcl.Acl.IsDisposed)
194 metadataReferences.AddRange(loadedAcl.Acl.Assemblies
197 if (a.IsDynamic || string.IsNullOrWhiteSpace(a.Location) || a.Location.Contains(
"xunit"))
199 if (a.FullName is null)
201 return !externAssemblyNames.Contains(a.FullName);
203 .Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
204 .Union(externAssemblyRefs
205 .Where(a => !(a.IsDynamic ||
string.IsNullOrEmpty(a.Location) || a.Location.Contains(
"xunit")))
206 .Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
211 foreach (var loadedAcl
in loadedAcls)
213 if (loadedAcl?.Acl?.
CompiledAssemblyImage is
null || loadedAcl.Acl.CompiledAssemblyImage.Length == 0)
215 metadataReferences.Add(MetadataReference.CreateFromImage(loadedAcl.Acl.CompiledAssemblyImage));
220 var topLevelBinderFlagsProperty = typeof(CSharpCompilationOptions).GetProperty(
"TopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic);
221 topLevelBinderFlagsProperty?.SetValue(compilationOptions, (uint)1 << 22);
224 using var memoryCompilation =
new MemoryStream();
226 var result = CSharpCompilation.Create(assemblyName, syntaxTrees, metadataReferences, compilationOptions).Emit(memoryCompilation);
230 IEnumerable<Diagnostic> failures = result.Diagnostics.Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error);
231 foreach (Diagnostic diagnostic
in failures)
233 compilationMessages += $
"\n{diagnostic}";
236 return AssemblyLoadingSuccessState.InvalidAssembly;
240 memoryCompilation.Seek(0, SeekOrigin.Begin);
249 LuaCsLogger.LogError($
"Unable to load memory assembly from stream. | Data: {e.Message} | InnerException: {e.InnerException}");
251 LuaCsLogger.ShowErrorOverlay($
"Unable to load memory assembly from stream. | Data: {e.Message} | InnerException: {e.InnerException}");
253 return AssemblyLoadingSuccessState.CannotLoadFromStream;
256 return AssemblyLoadingSuccessState.Success;
259 [SuppressMessage(
"ReSharper",
"ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract")]
260 protected override Assembly
Load(AssemblyName assemblyName)
270 Assembly ass = this.Assemblies.FirstOrDefault(a =>
271 a.FullName is not
null && a.FullName.Equals(assemblyName.FullName),
null);
276 foreach (KeyValuePair<string,AssemblyDependencyResolver> pair
in _dependencyResolvers)
278 var asspath = pair.Value.ResolveAssemblyToPath(assemblyName);
281 ass = LoadFromAssemblyPath(asspath);
291 list = _assemblyManager.UnsafeGetAllLoadedACLs();
300 foreach (var loadedAcL
in list)
302 if (loadedAcL.Acl is
null || loadedAcL.Acl.IsTemplateMode || loadedAcL.Acl.IsDisposed)
307 ass = loadedAcL.
Acl.LoadFromAssemblyName(assemblyName);
318 ass = AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName);
331 private void OnUnload(AssemblyLoadContext alc)
335 _dependencyResolvers.Clear();
336 _assemblyManager =
null;
337 base.Unloading -= OnUnload;
MemoryFileAssemblyContextLoader Acl
Provides functionality for the loading, unloading and management of plugins implementing IAssemblyPlu...
IEnumerable< LoadedACL > GetAllLoadedACLs()
Returns a list of all loaded ACLs. WARNING: References to these ACLs outside of the AssemblyManager s...
AssemblyLoadContext to compile from syntax trees in memory and to load from disk/file....
MemoryFileAssemblyContextLoader(AssemblyManager assemblyManager)
AssemblyLoadingSuccessState CompileAndLoadScriptAssembly([NotNull] string assemblyName, [NotNull] IEnumerable< SyntaxTree > syntaxTrees, IEnumerable< MetadataReference > externMetadataReferences, [NotNull] CSharpCompilationOptions compilationOptions, out string compilationMessages, IEnumerable< Assembly > externFileAssemblyReferences=null)
Compiles the supplied syntaxtrees and options into an in-memory assembly image. Builds metadata from ...
Assembly CompiledAssembly
byte[] CompiledAssemblyImage
override Assembly Load(AssemblyName assemblyName)
AssemblyLoadingSuccessState LoadFromFiles([NotNull] IEnumerable< string > assemblyFilePaths)
Try to load the list of disk-file assemblies.