Example #1
0
        private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath)
        {
            if (assemblyFullPath == null)
            {
                throw new ArgumentNullException(nameof(assemblyFullPath));
            }

            assemblyFullPath = Path.GetFullPath(assemblyFullPath);

            try
            {
                lock (loadedAssemblies)
                {
                    LoadedAssembly loadedAssembly;
                    if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly))
                    {
                        return(loadedAssembly.Assembly);
                    }

                    if (!File.Exists(assemblyFullPath))
                    {
                        return(null);
                    }

                    // Find pdb (if it exists)
                    var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb");
                    if (!File.Exists(pdbFullPath))
                    {
                        pdbFullPath = null;
                    }

                    // PreLoad the assembly into memory without locking it
                    var assemblyBytes = File.ReadAllBytes(assemblyFullPath);
                    var pdbBytes      = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null;

                    // Load the assembly into the current AppDomain
                    Assembly assembly;
                    if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory())
                    {
                        // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load
                        // This happens usually when opening Xenko assemblies themselves
                        assembly = Assembly.LoadFrom(assemblyFullPath);
                    }
                    else
                    {
                        // Load .deps.json file (if any)
                        var depsFile = Path.ChangeExtension(assemblyFullPath, ".deps.json");
                        Dictionary <string, string> dependenciesMapping = null;
                        if (File.Exists(depsFile))
                        {
                            // Read file
                            var dependenciesReader         = new DependencyContextJsonReader();
                            DependencyContext dependencies = null;
                            using (var dependenciesStream = File.OpenRead(depsFile))
                            {
                                dependencies = dependenciesReader.Read(dependenciesStream);
                            }

                            // Locate NuGet package folder
                            var settings             = NuGet.Configuration.Settings.LoadDefaultSettings(Path.GetDirectoryName(assemblyFullPath));
                            var globalPackagesFolder = NuGet.Configuration.SettingsUtility.GetGlobalPackagesFolder(settings);

                            // Build list of assemblies listed in .deps.json file
                            dependenciesMapping = new Dictionary <string, string>();
                            foreach (var library in dependencies.RuntimeLibraries)
                            {
                                if (library.Path == null)
                                {
                                    continue;
                                }

                                foreach (var runtimeAssemblyGroup in library.RuntimeAssemblyGroups)
                                {
                                    foreach (var runtimeFile in runtimeAssemblyGroup.RuntimeFiles)
                                    {
                                        var fullPath = Path.Combine(globalPackagesFolder, library.Path, runtimeFile.Path);
                                        if (File.Exists(fullPath))
                                        {
                                            var assemblyName = Path.GetFileNameWithoutExtension(runtimeFile.Path);

                                            // TODO: Properly deal with file duplicates (same file in multiple package, or RID conflicts)
                                            if (!dependenciesMapping.ContainsKey(assemblyName))
                                            {
                                                dependenciesMapping.Add(assemblyName, fullPath);
                                            }
                                        }
                                    }
                                }

                                // It seems .deps.json files don't contain library info if referencer is non-RID specific and dependency is RID specific
                                // (i.e. compiling Game project without runtime identifier and a dependency needs runtime identifier)
                                // TODO: Look for a proper way to query that properly, maybe using NuGet API? however some constraints are:
                                // - limited info (only .deps.json available from path, no project info; we might need to reconstruct a proper NuGet restore request from .deps.json)
                                // - need to be fast (make sure to use NuGet caching mechanism)
                                if (library.RuntimeAssemblyGroups.Count == 0)
                                {
                                    var runtimeFolder = new[] { "win", "any" }
                                    .Select(runtime => Path.Combine(globalPackagesFolder, library.Path, "runtimes", runtime))
                                    .Where(Directory.Exists)
                                    .SelectMany(folder => Directory.EnumerateDirectories(Path.Combine(folder, "lib")))
                                    .FirstOrDefault(file => Path.GetFileName(file).StartsWith("net"));     // Only consider framework netXX and netstandardX.X
                                    if (runtimeFolder != null)
                                    {
                                        foreach (var runtimeFile in Directory.EnumerateFiles(runtimeFolder, "*.dll"))
                                        {
                                            var assemblyName = Path.GetFileNameWithoutExtension(runtimeFile);

                                            // TODO: Properly deal with file duplicates (same file in multiple package, or RID conflicts)
                                            if (!dependenciesMapping.ContainsKey(assemblyName))
                                            {
                                                dependenciesMapping.Add(assemblyName, runtimeFile);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        // TODO: Is using AppDomain would provide more opportunities for unloading?
                        assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes);

                        loadedAssembly = new LoadedAssembly(this, assemblyFullPath, assembly, dependenciesMapping);
                        loadedAssemblies.Add(loadedAssembly);
                        loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly);

                        // Force assembly resolve with proper name (with proper context)
                        var previousSearchDirectory = currentSearchDirectory;
                        var previousContainer       = currentContainer;
                        try
                        {
                            currentContainer       = this;
                            currentSearchDirectory = Path.GetDirectoryName(assemblyFullPath);

                            Assembly.Load(assembly.FullName);
                        }
                        finally
                        {
                            currentContainer       = previousContainer;
                            currentSearchDirectory = previousSearchDirectory;
                        }
                    }

                    // Make sure there is no duplicate
                    Debug.Assert(!assemblyToContainers.TryGetValue(assembly, out var _));
                    // Add to mapping
                    assemblyToContainers.GetValue(assembly, _ => loadedAssembly);

                    // Make sure that Module initializer are called
                    foreach (var module in assembly.Modules)
                    {
                        RuntimeHelpers.RunModuleConstructor(module.ModuleHandle);
                    }
                    return(assembly);
                }
            }
            catch (Exception exception)
            {
                log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception);
                var loaderException = exception as ReflectionTypeLoadException;
                if (loaderException != null)
                {
                    foreach (var exceptionForType in loaderException.LoaderExceptions)
                    {
                        log.Error("Unable to load type. See exception.", exceptionForType);
                    }
                }
            }
            return(null);
        }
Example #2
0
        private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath)
        {
            if (assemblyFullPath == null)
            {
                throw new ArgumentNullException(nameof(assemblyFullPath));
            }

            assemblyFullPath = Path.GetFullPath(assemblyFullPath);

            try
            {
                lock (loadedAssemblies)
                {
                    LoadedAssembly loadedAssembly;
                    if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly))
                    {
                        return(loadedAssembly.Assembly);
                    }

                    if (!File.Exists(assemblyFullPath))
                    {
                        return(null);
                    }

                    // Find pdb (if it exists)
                    var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb");
                    if (!File.Exists(pdbFullPath))
                    {
                        pdbFullPath = null;
                    }

                    // PreLoad the assembly into memory without locking it
                    var assemblyBytes = File.ReadAllBytes(assemblyFullPath);
                    var pdbBytes      = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null;

                    // Load the assembly into the current AppDomain
                    Assembly assembly;
                    if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory())
                    {
                        // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load
                        // This happens usually when opening Xenko assemblies themselves
                        assembly = Assembly.LoadFrom(assemblyFullPath);
                    }
                    else
                    {
                        // Load .deps.json file (if any)
                        var depsFile = Path.ChangeExtension(assemblyFullPath, ".deps.json");
                        Dictionary <string, string> dependenciesMapping = null;
                        if (File.Exists(depsFile))
                        {
                            // Read file
                            var dependenciesReader         = new DependencyContextJsonReader();
                            DependencyContext dependencies = null;
                            using (var dependenciesStream = File.OpenRead(depsFile))
                            {
                                dependencies = dependenciesReader.Read(dependenciesStream);
                            }

                            // Locate NuGet package folder
                            var settings             = NuGet.Configuration.Settings.LoadDefaultSettings(Path.GetDirectoryName(assemblyFullPath));
                            var globalPackagesFolder = NuGet.Configuration.SettingsUtility.GetGlobalPackagesFolder(settings);

                            // Build list of assemblies listed in .deps.json file
                            dependenciesMapping = new Dictionary <string, string>();
                            foreach (var library in dependencies.RuntimeLibraries)
                            {
                                if (library.Path == null)
                                {
                                    continue;
                                }

                                foreach (var runtimeAssemblyGroup in library.RuntimeAssemblyGroups)
                                {
                                    foreach (var runtimeFile in runtimeAssemblyGroup.RuntimeFiles)
                                    {
                                        var fullPath = Path.Combine(globalPackagesFolder, library.Path, runtimeFile.Path);
                                        if (File.Exists(fullPath))
                                        {
                                            var assemblyName = Path.GetFileNameWithoutExtension(runtimeFile.Path);

                                            // TODO: Properly deal with file duplicates (same file in multiple package, or RID conflicts)
                                            if (!dependenciesMapping.ContainsKey(assemblyName))
                                            {
                                                dependenciesMapping.Add(assemblyName, fullPath);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        // TODO: Is using AppDomain would provide more opportunities for unloading?
                        assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes);

                        loadedAssembly = new LoadedAssembly(this, assemblyFullPath, assembly, dependenciesMapping);
                        loadedAssemblies.Add(loadedAssembly);
                        loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly);

                        // Force assembly resolve with proper name (with proper context)
                        var previousSearchDirectory = currentSearchDirectory;
                        var previousContainer       = currentContainer;
                        try
                        {
                            currentContainer       = this;
                            currentSearchDirectory = Path.GetDirectoryName(assemblyFullPath);

                            Assembly.Load(assembly.FullName);
                        }
                        finally
                        {
                            currentContainer       = previousContainer;
                            currentSearchDirectory = previousSearchDirectory;
                        }
                    }

                    // Make sure there is no duplicate
                    Debug.Assert(!assemblyToContainers.TryGetValue(assembly, out var _));
                    // Add to mapping
                    assemblyToContainers.GetValue(assembly, _ => loadedAssembly);

                    // Make sure that Module initializer are called
                    foreach (var module in assembly.Modules)
                    {
                        RuntimeHelpers.RunModuleConstructor(module.ModuleHandle);
                    }
                    return(assembly);
                }
            }
            catch (Exception exception)
            {
                log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception);
                var loaderException = exception as ReflectionTypeLoadException;
                if (loaderException != null)
                {
                    foreach (var exceptionForType in loaderException.LoaderExceptions)
                    {
                        log.Error("Unable to load type. See exception.", exceptionForType);
                    }
                }
            }
            return(null);
        }
Example #3
0
        private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath)
        {
            if (assemblyFullPath == null)
            {
                throw new ArgumentNullException(nameof(assemblyFullPath));
            }

            assemblyFullPath = Path.GetFullPath(assemblyFullPath);

            try
            {
                lock (loadedAssemblies)
                {
                    LoadedAssembly loadedAssembly;
                    if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly))
                    {
                        return(loadedAssembly.Assembly);
                    }

                    if (!File.Exists(assemblyFullPath))
                    {
                        return(null);
                    }

                    // Find pdb (if it exists)
                    var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb");
                    if (!File.Exists(pdbFullPath))
                    {
                        pdbFullPath = null;
                    }

                    // PreLoad the assembly into memory without locking it
                    var assemblyBytes = File.ReadAllBytes(assemblyFullPath);
                    var pdbBytes      = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null;

                    // Load the assembly into the current AppDomain
                    Assembly assembly;
                    if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory())
                    {
                        // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load
                        // This happens usually when opening Xenko assemblies themselves
                        assembly = Assembly.LoadFrom(assemblyFullPath);
                    }
                    else
                    {
                        // TODO: Is using AppDomain would provide more opportunities for unloading?
                        assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes);

                        loadedAssembly = new LoadedAssembly(assemblyFullPath, assembly);
                        loadedAssemblies.Add(loadedAssembly);
                        loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly);

                        // Force assembly resolve with proper name
                        // (doing it here, because if done later, loadingInstance will be set to null and it won't work)
                        Assembly.Load(assembly.FullName);
                    }

                    // Make sure that all referenced assemblies are loaded here
                    foreach (var assemblyRef in assembly.GetReferencedAssemblies())
                    {
                        Assembly.Load(assemblyRef);
                    }

                    // Make sure that Module initializer are called
                    if (assembly.GetTypes().Length > 0)
                    {
                        foreach (var module in assembly.Modules)
                        {
                            RuntimeHelpers.RunModuleConstructor(module.ModuleHandle);
                        }
                    }
                    return(assembly);
                }
            }
            catch (Exception exception)
            {
                log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception);
                var loaderException = exception as ReflectionTypeLoadException;
                if (loaderException != null)
                {
                    foreach (var exceptionForType in loaderException.LoaderExceptions)
                    {
                        log.Error("Unable to load type. See exception.", exceptionForType);
                    }
                }
            }
            return(null);
        }
Example #4
0
        private Assembly LoadAssemblyFromPathInternal([NotNull] string assemblyFullPath)
        {
            if (assemblyFullPath == null)
            {
                throw new ArgumentNullException(nameof(assemblyFullPath));
            }

            assemblyFullPath = Path.GetFullPath(assemblyFullPath);

            try
            {
                lock (loadedAssemblies)
                {
                    LoadedAssembly loadedAssembly;
                    if (loadedAssembliesByName.TryGetValue(assemblyFullPath, out loadedAssembly))
                    {
                        return(loadedAssembly.Assembly);
                    }

                    if (!File.Exists(assemblyFullPath))
                    {
                        return(null);
                    }

                    // Find pdb (if it exists)
                    var pdbFullPath = Path.ChangeExtension(assemblyFullPath, ".pdb");
                    if (!File.Exists(pdbFullPath))
                    {
                        pdbFullPath = null;
                    }

                    // PreLoad the assembly into memory without locking it
                    var assemblyBytes = File.ReadAllBytes(assemblyFullPath);
                    var pdbBytes      = pdbFullPath != null?File.ReadAllBytes(pdbFullPath) : null;

                    // Load the assembly into the current AppDomain
                    Assembly assembly;
                    if (new UDirectory(AppDomain.CurrentDomain.BaseDirectory) == new UFile(assemblyFullPath).GetFullDirectory())
                    {
                        // If loading from base directory, don't even try to load through byte array, as Assembly.Load will notice there is a "safer" version to load
                        // This happens usually when opening Xenko assemblies themselves
                        assembly = Assembly.LoadFrom(assemblyFullPath);
                    }
                    else
                    {
                        // TODO: Is using AppDomain would provide more opportunities for unloading?
                        assembly = pdbBytes != null?Assembly.Load(assemblyBytes, pdbBytes) : Assembly.Load(assemblyBytes);

                        loadedAssembly = new LoadedAssembly(this, assemblyFullPath, assembly);
                        loadedAssemblies.Add(loadedAssembly);
                        loadedAssembliesByName.Add(assemblyFullPath, loadedAssembly);

                        // Force assembly resolve with proper name (with proper context)
                        var previousSearchDirectory = currentSearchDirectory;
                        var previousContainer       = currentContainer;
                        try
                        {
                            currentContainer       = this;
                            currentSearchDirectory = Path.GetDirectoryName(assemblyFullPath);

                            Assembly.Load(assembly.FullName);
                        }
                        finally
                        {
                            currentContainer       = previousContainer;
                            currentSearchDirectory = previousSearchDirectory;
                        }
                    }

                    // Make sure there is no duplicate
                    Debug.Assert(!assemblyToContainers.TryGetValue(assembly, out var _));
                    // Add to mapping
                    assemblyToContainers.GetValue(assembly, _ => loadedAssembly);

                    // Make sure that Module initializer are called
                    foreach (var module in assembly.Modules)
                    {
                        RuntimeHelpers.RunModuleConstructor(module.ModuleHandle);
                    }
                    return(assembly);
                }
            }
            catch (Exception exception)
            {
                log.Error($"Error while loading assembly reference [{assemblyFullPath}]", exception);
                var loaderException = exception as ReflectionTypeLoadException;
                if (loaderException != null)
                {
                    foreach (var exceptionForType in loaderException.LoaderExceptions)
                    {
                        log.Error("Unable to load type. See exception.", exceptionForType);
                    }
                }
            }
            return(null);
        }