Skip to content

Commit

Permalink
- Added try-catch to assembly compilation call.
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
TBN-MapleWheels committed Oct 26, 2023
1 parent e237389 commit 342a6bb
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ public IEnumerable<Type> GetTypesByName(string typeName)
}
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);
}
}
}
Expand Down Expand Up @@ -309,12 +310,16 @@ public IEnumerable<Type> GetAllTypesInLoadedAssemblies()
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();
Expand All @@ -332,8 +337,17 @@ public IEnumerable<LoadedACL> GetAllLoadedACLs()
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();
Expand Down Expand Up @@ -387,8 +401,18 @@ public AssemblyLoadingSuccessState LoadAssemblyFromMemory([NotNull] string compi
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)
Expand Down Expand Up @@ -654,7 +678,15 @@ private bool DisposeACL(Guid id)
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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public AssemblyLoadingSuccessState CompileAndLoadScriptAssembly(
}

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;
Expand Down Expand Up @@ -178,35 +178,39 @@ public AssemblyLoadingSuccessState CompileAndLoadScriptAssembly(
.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);
Expand Down

0 comments on commit 342a6bb

Please sign in to comment.