| 
									
										
										
										
											2021-09-12 20:23:05 +02:00
										 |  |  | using System; | 
					
						
							|  |  |  | using System.Collections.Generic; | 
					
						
							|  |  |  | using System.IO; | 
					
						
							|  |  |  | using System.Reflection; | 
					
						
							|  |  |  | using System.Runtime.Loader; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace GodotPlugins | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     public class PluginLoadContext : AssemblyLoadContext | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         private readonly AssemblyDependencyResolver _resolver; | 
					
						
							|  |  |  |         private readonly ICollection<string> _sharedAssemblies; | 
					
						
							|  |  |  |         private readonly AssemblyLoadContext _mainLoadContext; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-28 04:56:46 +02:00
										 |  |  |         public string? AssemblyLoadedPath { get; private set; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 20:23:05 +02:00
										 |  |  |         public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies, | 
					
						
							| 
									
										
										
										
											2022-10-16 23:55:54 +02:00
										 |  |  |             AssemblyLoadContext mainLoadContext, bool isCollectible) | 
					
						
							|  |  |  |             : base(isCollectible) | 
					
						
							| 
									
										
										
										
											2021-09-12 20:23:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             _resolver = new AssemblyDependencyResolver(pluginPath); | 
					
						
							|  |  |  |             _sharedAssemblies = sharedAssemblies; | 
					
						
							|  |  |  |             _mainLoadContext = mainLoadContext; | 
					
						
							| 
									
										
										
										
											2023-01-31 23:04:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (string.IsNullOrEmpty(AppContext.BaseDirectory)) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs#L17-L35 | 
					
						
							|  |  |  |                 // but Assembly.Location is unavailable, because we load assemblies from memory. | 
					
						
							|  |  |  |                 string? baseDirectory = Path.GetDirectoryName(pluginPath); | 
					
						
							|  |  |  |                 if (baseDirectory != null) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     if (!Path.EndsInDirectorySeparator(baseDirectory)) | 
					
						
							| 
									
										
										
										
											2023-02-04 14:33:02 +01:00
										 |  |  |                         baseDirectory += Path.DirectorySeparatorChar; | 
					
						
							| 
									
										
										
										
											2023-01-31 23:04:39 +01:00
										 |  |  |                     // This SetData call effectively sets AppContext.BaseDirectory | 
					
						
							|  |  |  |                     // See https://github.com/dotnet/runtime/blob/v6.0.0/src/libraries/System.Private.CoreLib/src/System/AppContext.cs#L21-L25 | 
					
						
							|  |  |  |                     AppDomain.CurrentDomain.SetData("APP_CONTEXT_BASE_DIRECTORY", baseDirectory); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     // TODO: How to log from GodotPlugins? (delegate pointer?) | 
					
						
							|  |  |  |                     Console.Error.WriteLine("Failed to set AppContext.BaseDirectory. Dynamic loading of libraries may fail."); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-12 20:23:05 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         protected override Assembly? Load(AssemblyName assemblyName) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             if (assemblyName.Name == null) | 
					
						
							|  |  |  |                 return null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (_sharedAssemblies.Contains(assemblyName.Name)) | 
					
						
							|  |  |  |                 return _mainLoadContext.LoadFromAssemblyName(assemblyName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); | 
					
						
							|  |  |  |             if (assemblyPath != null) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2022-05-28 04:56:46 +02:00
										 |  |  |                 AssemblyLoadedPath = assemblyPath; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-12 20:23:05 +02:00
										 |  |  |                 // Load in memory to prevent locking the file | 
					
						
							|  |  |  |                 using var assemblyFile = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read); | 
					
						
							|  |  |  |                 string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (File.Exists(pdbPath)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read); | 
					
						
							|  |  |  |                     return LoadFromStream(assemblyFile, pdbFile); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return LoadFromStream(assemblyFile); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); | 
					
						
							|  |  |  |             if (libraryPath != null) | 
					
						
							|  |  |  |                 return LoadUnmanagedDllFromPath(libraryPath); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return IntPtr.Zero; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |