private EditorServicesLoader(
     HostLogger logger,
     EditorServicesConfig hostConfig,
     ISessionFileWriter sessionFileWriter,
     IReadOnlyCollection <IDisposable> loggersToUnsubscribe)
 {
     _logger               = logger;
     _hostConfig           = hostConfig;
     _sessionFileWriter    = sessionFileWriter;
     _loggersToUnsubscribe = loggersToUnsubscribe;
 }
 public EditorServicesRunner(
     HostLogger logger,
     EditorServicesConfig config,
     ISessionFileWriter sessionFileWriter,
     IReadOnlyCollection <IDisposable> loggersToUnsubscribe)
 {
     _logger                 = logger;
     _config                 = config;
     _sessionFileWriter      = sessionFileWriter;
     _serverFactory          = EditorServicesServerFactory.Create(_config.LogPath, (int)_config.LogLevel, logger);
     _alreadySubscribedDebug = false;
     _loggersToUnsubscribe   = loggersToUnsubscribe;
 }
 public EditorServicesRunner(
     HostLogger logger,
     EditorServicesConfig config,
     ISessionFileWriter sessionFileWriter,
     IReadOnlyCollection <IDisposable> loggersToUnsubscribe)
 {
     _logger            = logger;
     _config            = config;
     _sessionFileWriter = sessionFileWriter;
     // NOTE: This factory helps to isolate `Microsoft.Extensions.Logging/DependencyInjection`.
     _serverFactory          = EditorServicesServerFactory.Create(_config.LogPath, (int)_config.LogLevel, logger);
     _alreadySubscribedDebug = false;
     _loggersToUnsubscribe   = loggersToUnsubscribe;
 }
        /// <summary>
        /// Create a new Editor Services loader.
        /// </summary>
        /// <param name="logger">The host logger to use.</param>
        /// <param name="hostConfig">The host configuration to start editor services with.</param>
        /// <param name="sessionFileWriter">The session file writer to write the session file with.</param>
        /// <returns></returns>
        public static EditorServicesLoader Create(
            HostLogger logger,
            EditorServicesConfig hostConfig,
            ISessionFileWriter sessionFileWriter,
            IReadOnlyCollection <IDisposable> loggersToUnsubscribe)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            if (hostConfig == null)
            {
                throw new ArgumentNullException(nameof(hostConfig));
            }

#if CoreCLR
            // In .NET Core, we add an event here to redirect dependency loading to the new AssemblyLoadContext we load PSES' dependencies into
            logger.Log(PsesLogLevel.Verbose, "Adding AssemblyResolve event handler for new AssemblyLoadContext dependency loading");

            var psesLoadContext = new PsesLoadContext(s_psesDependencyDirPath);

            if (hostConfig.LogLevel == PsesLogLevel.Diagnostic)
            {
                AppDomain.CurrentDomain.AssemblyLoad += (object sender, AssemblyLoadEventArgs args) =>
                {
                    logger.Log(
                        PsesLogLevel.Diagnostic,
                        $"Loaded into load context {AssemblyLoadContext.GetLoadContext(args.LoadedAssembly)}: {args.LoadedAssembly}");
                };
            }

            AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext defaultLoadContext, AssemblyName asmName) =>
            {
                logger.Log(PsesLogLevel.Diagnostic, $"Assembly resolve event fired for {asmName}");

                // We only want the Editor Services DLL; the new ALC will lazily load its dependencies automatically
                if (!string.Equals(asmName.Name, "Microsoft.PowerShell.EditorServices", StringComparison.Ordinal))
                {
                    return(null);
                }

                string asmPath = Path.Combine(s_psesDependencyDirPath, $"{asmName.Name}.dll");

                logger.Log(PsesLogLevel.Verbose, "Loading PSES DLL using new assembly load context");

                return(psesLoadContext.LoadFromAssemblyPath(asmPath));
            };
#else
            // In .NET Framework we add an event here to redirect dependency loading in the current AppDomain for PSES' dependencies
            logger.Log(PsesLogLevel.Verbose, "Adding AssemblyResolve event handler for dependency loading");

            if (hostConfig.LogLevel == PsesLogLevel.Diagnostic)
            {
                AppDomain.CurrentDomain.AssemblyLoad += (object sender, AssemblyLoadEventArgs args) =>
                {
                    logger.Log(
                        PsesLogLevel.Diagnostic,
                        $"Loaded {args.LoadedAssembly.GetName()}");
                };
            }

            // Unlike in .NET Core, we need to be look for all dependencies in .NET Framework, not just PSES.dll
            AppDomain.CurrentDomain.AssemblyResolve += (object sender, ResolveEventArgs args) =>
            {
                logger.Log(PsesLogLevel.Diagnostic, $"Assembly resolve event fired for {args.Name}");

                var asmName = new AssemblyName(args.Name);
                var dllName = $"{asmName.Name}.dll";

                // First look for the required assembly in the .NET Framework DLL dir
                string baseDirAsmPath = Path.Combine(s_psesBaseDirPath, dllName);
                if (File.Exists(baseDirAsmPath))
                {
                    logger.Log(PsesLogLevel.Diagnostic, $"Loading {args.Name} from PSES base dir into LoadFrom context");
                    return(Assembly.LoadFrom(baseDirAsmPath));
                }

                // Then look in the shared .NET Standard directory
                string asmPath = Path.Combine(s_psesDependencyDirPath, dllName);
                if (File.Exists(asmPath))
                {
                    logger.Log(PsesLogLevel.Diagnostic, $"Loading {args.Name} from PSES dependency dir into LoadFrom context");
                    return(Assembly.LoadFrom(asmPath));
                }

                return(null);
            };
#endif

            return(new EditorServicesLoader(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe));
        }
 /// <summary>
 /// Create a new Editor Services loader.
 /// </summary>
 /// <param name="logger">The host logger to use.</param>
 /// <param name="hostConfig">The host configuration to start editor services with.</param>
 /// <param name="sessionFileWriter">The session file writer to write the session file with.</param>
 /// <returns></returns>
 public static EditorServicesLoader Create(
     HostLogger logger,
     EditorServicesConfig hostConfig,
     ISessionFileWriter sessionFileWriter) => Create(logger, hostConfig, sessionFileWriter, loggersToUnsubscribe: null);