- 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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user