- Added try-catch to assembly compilation call.

- Added empty list checks for metadata references building.
- Made ACL cleanup more reliable.
- Updated some GET functions in AssemblyManager to have better reliability and error handling.
This commit is contained in:
MapleWheels
2023-10-26 10:07:01 -04:00
parent e237389ad9
commit 342a6bbcf9
2 changed files with 69 additions and 33 deletions

View File

@@ -260,7 +260,8 @@ public class AssemblyManager
}
catch (Exception e)
{
this.OnException?.Invoke($"{nameof(AssemblyManager)}::{nameof(GetTypesByName)}() | Error: {e.Message}", e);
this.OnException?.Invoke(
$"{nameof(AssemblyManager)}::{nameof(GetTypesByName)}() | Error: {e.Message}", e);
}
}
}
@@ -309,12 +310,16 @@ public class AssemblyManager
OpsLockLoaded.EnterReadLock();
try
{
return AssemblyLoadContext.Default.Assemblies
.SelectMany(a => a.GetSafeTypes())
return _defaultContextTypes
.Select(kvp => kvp.Value)
.Concat(LoadedACLs
.SelectMany(kvp => kvp.Value.AssembliesTypes.Select(kv => kv.Value)))
.SelectMany(kvp => kvp.Value?.AssembliesTypes.Select(kv => kv.Value)))
.ToImmutableList();
}
catch
{
return ImmutableList<Type>.Empty;
}
finally
{
OpsLockLoaded.ExitReadLock();
@@ -332,8 +337,17 @@ public class AssemblyManager
OpsLockLoaded.EnterReadLock();
try
{
if (LoadedACLs.IsEmpty)
{
return ImmutableList<LoadedACL>.Empty;
}
return LoadedACLs.Select(kvp => kvp.Value).ToImmutableList();
}
catch
{
return ImmutableList<LoadedACL>.Empty;
}
finally
{
OpsLockLoaded.ExitReadLock();
@@ -387,8 +401,18 @@ public class AssemblyManager
return AssemblyLoadingSuccessState.AlreadyLoaded;
// compile
var state = acl.Acl.CompileAndLoadScriptAssembly(compiledAssemblyName, syntaxTree, externalMetadataReferences,
compilationOptions, out var messages, externFileAssemblyRefs);
AssemblyLoadingSuccessState state;
string messages;
try
{
state = acl.Acl.CompileAndLoadScriptAssembly(compiledAssemblyName, syntaxTree, externalMetadataReferences,
compilationOptions, out messages, externFileAssemblyRefs);
}
catch (Exception e)
{
ModUtils.Logging.PrintError($"{nameof(AssemblyManager)}::{nameof(LoadAssemblyFromMemory)}() | Failed to compile and load assemblies for [ {compiledAssemblyName} / {friendlyName} ]! Details: {e.Message}");
return AssemblyLoadingSuccessState.InvalidAssembly;
}
// get types
if (state is AssemblyLoadingSuccessState.Success)
@@ -654,7 +678,15 @@ public class AssemblyManager
OpsLockUnloaded.EnterWriteLock();
try
{
if (id.Equals(Guid.Empty) || !LoadedACLs.ContainsKey(id) || LoadedACLs[id] is null)
if (LoadedACLs.ContainsKey(id) && LoadedACLs[id] == null)
{
if (!LoadedACLs.TryRemove(id, out _))
{
ModUtils.Logging.PrintWarning($"An ACL with the GUID {id.ToString()} was found as null. Unable to remove null ACL entry.");
}
}
if (id.Equals(Guid.Empty) || !LoadedACLs.ContainsKey(id))
{
return false; // nothing to dispose of
}

View File

@@ -143,7 +143,7 @@ public class MemoryFileAssemblyContextLoader : AssemblyLoadContext
}
var externAssemblyRefs = externFileAssemblyReferences is not null ? externFileAssemblyReferences.ToImmutableList() : ImmutableList<Assembly>.Empty;
var externAssemblyNames = externAssemblyRefs.Any() ? externAssemblyRefs
var externAssemblyNames = !externAssemblyRefs.IsEmpty ? externAssemblyRefs
.Where(a => a.FullName is not null)
.Select(a => a.FullName).ToImmutableHashSet()
: ImmutableHashSet<string>.Empty;
@@ -178,35 +178,39 @@ public class MemoryFileAssemblyContextLoader : AssemblyLoadContext
.Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
).ToList());
// build metadata refs from ACL assemblies from files/disk.
foreach (AssemblyManager.LoadedACL loadedAcl in _assemblyManager.GetAllLoadedACLs())
ImmutableList<AssemblyManager.LoadedACL> loadedAcls = _assemblyManager.GetAllLoadedACLs().ToImmutableList();
if (!loadedAcls.IsEmpty)
{
if(loadedAcl.Acl.IsTemplateMode || loadedAcl.Acl.IsDisposed)
continue;
metadataReferences.AddRange(loadedAcl.Acl.Assemblies
.Where(a =>
{
if (a.IsDynamic || string.IsNullOrWhiteSpace(a.Location) || a.Location.Contains("xunit"))
return false;
if (a.FullName is null)
return true;
return !externAssemblyNames.Contains(a.FullName); // exclude duplicates
})
.Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
.Union(externAssemblyRefs // add custom supplied assemblies
.Where(a => !(a.IsDynamic || string.IsNullOrEmpty(a.Location) || a.Location.Contains("xunit")))
// build metadata refs from ACL assemblies from files/disk.
foreach (AssemblyManager.LoadedACL loadedAcl in loadedAcls)
{
if(loadedAcl?.Acl is null || loadedAcl.Acl.IsTemplateMode || loadedAcl.Acl.IsDisposed)
continue;
metadataReferences.AddRange(loadedAcl.Acl.Assemblies
.Where(a =>
{
if (a.IsDynamic || string.IsNullOrWhiteSpace(a.Location) || a.Location.Contains("xunit"))
return false;
if (a.FullName is null)
return true;
return !externAssemblyNames.Contains(a.FullName); // exclude duplicates
})
.Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
).ToList());
}
.Union(externAssemblyRefs // add custom supplied assemblies
.Where(a => !(a.IsDynamic || string.IsNullOrEmpty(a.Location) || a.Location.Contains("xunit")))
.Select(a => MetadataReference.CreateFromFile(a.Location) as MetadataReference)
).ToList());
}
// build metadata refs from in-memory images
foreach (var loadedAcl in _assemblyManager.GetAllLoadedACLs())
{
if (loadedAcl.Acl.CompiledAssemblyImage is null || loadedAcl.Acl.CompiledAssemblyImage.Length == 0)
continue;
metadataReferences.Add(MetadataReference.CreateFromImage(loadedAcl.Acl.CompiledAssemblyImage));
// build metadata refs from in-memory images
foreach (var loadedAcl in loadedAcls)
{
if (loadedAcl.Acl.CompiledAssemblyImage is null || loadedAcl.Acl.CompiledAssemblyImage.Length == 0)
continue;
metadataReferences.Add(MetadataReference.CreateFromImage(loadedAcl.Acl.CompiledAssemblyImage));
}
}
// Change inaccessible options to allow public access to restricted members
var topLevelBinderFlagsProperty = typeof(CSharpCompilationOptions).GetProperty("TopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic);
topLevelBinderFlagsProperty?.SetValue(compilationOptions, (uint)1 << 22);