MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#nullable enable5 6using System;7using System.Collections.Generic;8using System.Collections.ObjectModel;9using System.Management.Automation.Internal;10using System.Management.Automation.Subsystem.DSC;11using System.Management.Automation.Subsystem.Feedback;12using System.Management.Automation.Subsystem.Prediction;13 14namespace System.Management.Automation.Subsystem15{16 /// <summary>17 /// Class used to manage subsystems.18 /// </summary>19 public static class SubsystemManager20 {21 private static readonly ReadOnlyCollection<SubsystemInfo> s_subsystems;22 private static readonly ReadOnlyDictionary<Type, SubsystemInfo> s_subSystemTypeMap;23 private static readonly ReadOnlyDictionary<SubsystemKind, SubsystemInfo> s_subSystemKindMap;24 25 static SubsystemManager()26 {27 var subsystems = new SubsystemInfo[]28 {29 SubsystemInfo.Create<ICommandPredictor>(30 SubsystemKind.CommandPredictor,31 allowUnregistration: true,32 allowMultipleRegistration: true),33 34 SubsystemInfo.Create<ICrossPlatformDsc>(35 SubsystemKind.CrossPlatformDsc,36 allowUnregistration: true,37 allowMultipleRegistration: false),38 39 SubsystemInfo.Create<IFeedbackProvider>(40 SubsystemKind.FeedbackProvider,41 allowUnregistration: true,42 allowMultipleRegistration: true),43 };44 45 var subSystemTypeMap = new Dictionary<Type, SubsystemInfo>(subsystems.Length);46 var subSystemKindMap = new Dictionary<SubsystemKind, SubsystemInfo>(subsystems.Length);47 48 foreach (var subsystem in subsystems)49 {50 subSystemTypeMap.Add(subsystem.SubsystemType, subsystem);51 subSystemKindMap.Add(subsystem.Kind, subsystem);52 }53 54 s_subsystems = new ReadOnlyCollection<SubsystemInfo>(subsystems);55 s_subSystemTypeMap = new ReadOnlyDictionary<Type, SubsystemInfo>(subSystemTypeMap);56 s_subSystemKindMap = new ReadOnlyDictionary<SubsystemKind, SubsystemInfo>(subSystemKindMap);57 58 // Register built-in suggestion providers.59 RegisterSubsystem(SubsystemKind.FeedbackProvider, new GeneralCommandErrorFeedback());60 }61 62 #region internal - Retrieve subsystem proxy object63 64 /// <summary>65 /// Get the proxy object registered for a specific subsystem.66 /// Return null when the given subsystem is not registered.67 /// </summary>68 /// <remarks>69 /// Design point:70 /// The implementation proxy object is not supposed to expose to users.71 /// Users shouldn't depend on a implementation proxy object directly, but instead should depend on PowerShell APIs.72 /// <para/>73 /// Example: if a user want to use prediction functionality, he/she should use the PowerShell prediction API instead of74 /// directly interacting with the implementation proxy object of `IPrediction`.75 /// </remarks>76 /// <typeparam name="TConcreteSubsystem">The concrete subsystem base type.</typeparam>77 /// <returns>The most recently registered implementation object of the concrete subsystem.</returns>78 internal static TConcreteSubsystem? GetSubsystem<TConcreteSubsystem>()79 where TConcreteSubsystem : class, ISubsystem80 {81 if (s_subSystemTypeMap.TryGetValue(typeof(TConcreteSubsystem), out SubsystemInfo? subsystemInfo))82 {83 var subsystemInfoImpl = (SubsystemInfoImpl<TConcreteSubsystem>)subsystemInfo;84 return subsystemInfoImpl.GetImplementation();85 }86 87 throw new ArgumentException(88 StringUtil.Format(89 SubsystemStrings.SubsystemTypeUnknown,90 typeof(TConcreteSubsystem).FullName));91 }92 93 /// <summary>94 /// Get all the proxy objects registered for a specific subsystem.95 /// Return an empty collection when the given subsystem is not registered.96 /// </summary>97 /// <typeparam name="TConcreteSubsystem">The concrete subsystem base type.</typeparam>98 /// <returns>A readonly collection of all implementation objects registered for the concrete subsystem.</returns>99 internal static ReadOnlyCollection<TConcreteSubsystem> GetSubsystems<TConcreteSubsystem>()100 where TConcreteSubsystem : class, ISubsystem101 {102 if (s_subSystemTypeMap.TryGetValue(typeof(TConcreteSubsystem), out SubsystemInfo? subsystemInfo))103 {104 var subsystemInfoImpl = (SubsystemInfoImpl<TConcreteSubsystem>)subsystemInfo;105 return subsystemInfoImpl.GetAllImplementations();106 }107 108 throw new ArgumentException(109 StringUtil.Format(110 SubsystemStrings.SubsystemTypeUnknown,111 typeof(TConcreteSubsystem).FullName));112 }113 114 #endregion115 116 #region public - Subsystem metadata117 118 /// <summary>119 /// Get the information about all subsystems.120 /// </summary>121 /// <returns>A readonly collection of all <see cref="SubsystemInfo"/> objects.</returns>122 public static ReadOnlyCollection<SubsystemInfo> GetAllSubsystemInfo()123 {124 return s_subsystems;125 }126 127 /// <summary>128 /// Get the information about a subsystem by the subsystem type.129 /// </summary>130 /// <param name="subsystemType">The base type of a specific concrete subsystem.</param>131 /// <returns>The <see cref="SubsystemInfo"/> object that represents the concrete subsystem.</returns>132 public static SubsystemInfo GetSubsystemInfo(Type subsystemType)133 {134 ArgumentNullException.ThrowIfNull(subsystemType);135 136 if (s_subSystemTypeMap.TryGetValue(subsystemType, out SubsystemInfo? subsystemInfo))137 {138 return subsystemInfo;139 }140 141 throw new ArgumentException(142 subsystemType == typeof(ISubsystem)143 ? SubsystemStrings.MustUseConcreteSubsystemType144 : StringUtil.Format(145 SubsystemStrings.SubsystemTypeUnknown,146 subsystemType.FullName),147 nameof(subsystemType));148 }149 150 /// <summary>151 /// Get the information about a subsystem by the subsystem kind.152 /// </summary>153 /// <param name="kind">A specific <see cref="SubsystemKind"/>.</param>154 /// <returns>The <see cref="SubsystemInfo"/> object that represents the concrete subsystem.</returns>155 public static SubsystemInfo GetSubsystemInfo(SubsystemKind kind)156 {157 if (s_subSystemKindMap.TryGetValue(kind, out SubsystemInfo? subsystemInfo))158 {159 return subsystemInfo;160 }161 162 throw new ArgumentException(163 StringUtil.Format(164 SubsystemStrings.SubsystemKindUnknown,165 kind.ToString()),166 nameof(kind));167 }168 169 #endregion170 171 #region public - Subsystem registration172 173 /// <summary>174 /// Subsystem registration.175 /// </summary>176 /// <typeparam name="TConcreteSubsystem">The concrete subsystem base type.</typeparam>177 /// <typeparam name="TImplementation">The implementation type of that concrete subsystem.</typeparam>178 /// <param name="proxy">An instance of the implementation.</param>179 public static void RegisterSubsystem<TConcreteSubsystem, TImplementation>(TImplementation proxy)180 where TConcreteSubsystem : class, ISubsystem181 where TImplementation : class, TConcreteSubsystem182 {183 ArgumentNullException.ThrowIfNull(proxy);184 185 RegisterSubsystem(GetSubsystemInfo(typeof(TConcreteSubsystem)), proxy);186 }187 188 /// <summary>189 /// Register an implementation for a subsystem.190 /// </summary>191 /// <param name="kind">The target <see cref="SubsystemKind"/> of the registration.</param>192 /// <param name="proxy">An instance of the implementation.</param>193 public static void RegisterSubsystem(SubsystemKind kind, ISubsystem proxy)194 {195 ArgumentNullException.ThrowIfNull(proxy);196 197 SubsystemInfo info = GetSubsystemInfo(kind);198 if (!info.SubsystemType.IsAssignableFrom(proxy.GetType()))199 {200 throw new ArgumentException(201 StringUtil.Format(202 SubsystemStrings.ConcreteSubsystemNotImplemented,203 kind.ToString(),204 info.SubsystemType.Name),205 nameof(proxy));206 }207 208 RegisterSubsystem(info, proxy);209 }210 211 private static void RegisterSubsystem(SubsystemInfo subsystemInfo, ISubsystem proxy)212 {213 if (proxy.Id == Guid.Empty)214 {215 throw new ArgumentException(216 StringUtil.Format(217 SubsystemStrings.EmptyImplementationId,218 subsystemInfo.Kind.ToString()),219 nameof(proxy));220 }221 222 if (string.IsNullOrEmpty(proxy.Name))223 {224 throw new ArgumentException(225 StringUtil.Format(226 SubsystemStrings.NullOrEmptyImplementationName,227 subsystemInfo.Kind.ToString()),228 nameof(proxy));229 }230 231 if (string.IsNullOrEmpty(proxy.Description))232 {233 throw new ArgumentException(234 StringUtil.Format(235 SubsystemStrings.NullOrEmptyImplementationDescription,236 subsystemInfo.Kind.ToString()),237 nameof(proxy));238 }239 240 if (subsystemInfo.RequiredCmdlets.Count > 0 || subsystemInfo.RequiredFunctions.Count > 0)241 {242 // Process 'proxy.CmdletImplementationAssembly' and 'proxy.FunctionsToDefine'243 // Functions are added to global scope.244 // Cmdlets are loaded in a way like a snapin, making the 'Source' of the cmdlets to be 'Microsoft.PowerShell.Core'.245 //246 // For example, let's say the Job adapter is made a subsystem, then all `*-Job` cmdlets will be moved out of S.M.A247 // into a subsystem implementation DLL. After registration, all `*-Job` cmdlets should be back in the248 // 'Microsoft.PowerShell.Core' namespace to keep backward compatibility.249 //250 // Both cmdlets and functions are added to the default InitialSessionState used for creating a new Runspace,251 // so the subsystem works for all subsequent new runspaces after it's registered.252 // Take the Job adapter subsystem as an instance again, so when creating another Runspace after the registration,253 // all '*-Job' cmdlets should be available in the 'Microsoft.PowerShell.Core' namespace by default.254 }255 256 subsystemInfo.RegisterImplementation(proxy);257 }258 259 #endregion260 261 #region public - Subsystem unregistration262 263 /// <summary>264 /// Subsystem unregistration.265 /// Throw 'InvalidOperationException' when called for subsystems that cannot be unregistered.266 /// </summary>267 /// <typeparam name="TConcreteSubsystem">The base type of the target concrete subsystem of the un-registration.</typeparam>268 /// <param name="id">The Id of the implementation to be unregistered.</param>269 public static void UnregisterSubsystem<TConcreteSubsystem>(Guid id)270 where TConcreteSubsystem : class, ISubsystem271 {272 UnregisterSubsystem(GetSubsystemInfo(typeof(TConcreteSubsystem)), id);273 }274 275 /// <summary>276 /// Subsystem unregistration.277 /// Throw 'InvalidOperationException' when called for subsystems that cannot be unregistered.278 /// </summary>279 /// <param name="kind">The target <see cref="SubsystemKind"/> of the un-registration.</param>280 /// <param name="id">The Id of the implementation to be unregistered.</param>281 public static void UnregisterSubsystem(SubsystemKind kind, Guid id)282 {283 UnregisterSubsystem(GetSubsystemInfo(kind), id);284 }285 286 private static void UnregisterSubsystem(SubsystemInfo subsystemInfo, Guid id)287 {288 if (subsystemInfo.RequiredCmdlets.Count > 0 || subsystemInfo.RequiredFunctions.Count > 0)289 {290 throw new NotSupportedException("NotSupported yet: unregister subsystem that introduced new cmdlets/functions.");291 }292 293 ISubsystem impl = subsystemInfo.UnregisterImplementation(id);294 if (impl is IDisposable disposable)295 {296 try297 {298 disposable.Dispose();299 }300 catch301 {302 // It's OK to ignore all exceptions when disposing the object.303 }304 }305 }306 307 #endregion308 }309}310 