MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.IO;8using System.Management.Automation.Internal;9using System.Management.Automation.Language;10 11using Dbg = System.Management.Automation.Diagnostics;12 13namespace System.Management.Automation14{15 /// <summary>16 /// Searcher class for finding PS classes on the system.17 /// </summary>18 internal class PSClassSearcher : IEnumerable<PSClassInfo>, IEnumerator<PSClassInfo>19 {20 internal PSClassSearcher(21 string className,22 bool useWildCards,23 ExecutionContext context)24 {25 Diagnostics.Assert(context != null, "caller to verify context is not null");26 _context = context;27 28 Diagnostics.Assert(className != null, "caller to verify className is not null");29 _className = className;30 _useWildCards = useWildCards;31 _moduleInfoCache = new Dictionary<string, PSModuleInfo>(StringComparer.OrdinalIgnoreCase);32 }33 34 #region private properties35 36 private readonly string _className = null;37 private readonly ExecutionContext _context = null;38 private PSClassInfo _currentMatch = null;39 private IEnumerator<PSClassInfo> _matchingClass = null;40 private Collection<PSClassInfo> _matchingClassList = null;41 private readonly bool _useWildCards = false;42 private readonly Dictionary<string, PSModuleInfo> _moduleInfoCache = null;43 private readonly object _lockObject = new object();44 45 #endregion46 47 #region public methods48 49 /// <summary>50 /// Reset the Iterator.51 /// </summary>52 public void Reset()53 {54 _currentMatch = null;55 _matchingClass = null;56 }57 58 /// <summary>59 /// Reset and dispose the Iterator.60 /// </summary>61 public void Dispose()62 {63 Reset();64 GC.SuppressFinalize(this);65 }66 67 /// <summary>68 /// Get the Enumerator.69 /// </summary>70 /// <returns></returns>71 IEnumerator<PSClassInfo> IEnumerable<PSClassInfo>.GetEnumerator()72 {73 return this;74 }75 76 /// <summary>77 /// Get the Enumerator.78 /// </summary>79 /// <returns></returns>80 IEnumerator IEnumerable.GetEnumerator()81 {82 return this;83 }84 85 /// <summary>86 /// Move to the Next value in the enumerator.87 /// </summary>88 /// <returns></returns>89 public bool MoveNext()90 {91 _currentMatch = GetNextClass();92 93 if (_currentMatch != null)94 return true;95 96 return false;97 }98 99 /// <summary>100 /// Return the current PSClassInfo.101 /// </summary>102 PSClassInfo IEnumerator<PSClassInfo>.Current103 {104 get105 {106 return _currentMatch;107 }108 }109 110 /// <summary>111 /// Return the current PSClassInfo as object.112 /// </summary>113 object IEnumerator.Current114 {115 get116 {117 return ((IEnumerator<PSClassInfo>)this).Current;118 }119 }120 121 #endregion122 123 #region private methods124 125 /// <summary>126 /// Get all modules and find the matching type127 /// When found add them to the enumerator. If we have already got it, return the next resource.128 /// </summary>129 /// <returns>Next PSClassInfo object or null if none are found.</returns>130 private PSClassInfo GetNextClass()131 {132 PSClassInfo returnValue = null;133 WildcardPattern classNameMatcher = WildcardPattern.Get(_className, WildcardOptions.IgnoreCase);134 135 if (_matchingClassList == null)136 {137 _matchingClassList = new Collection<PSClassInfo>();138 139 if (FindTypeByModulePath(classNameMatcher))140 _matchingClass = _matchingClassList.GetEnumerator();141 else142 return null;143 }144 145 if (!_matchingClass.MoveNext())146 {147 _matchingClass = null;148 }149 else150 {151 returnValue = _matchingClass.Current;152 }153 154 return returnValue;155 }156 157 private bool FindTypeByModulePath(WildcardPattern classNameMatcher)158 {159 bool matchFound = false;160 161 var moduleList = ModuleUtils.GetDefaultAvailableModuleFiles(isForAutoDiscovery: false, _context);162 163 foreach (var modulePath in moduleList)164 {165 string expandedModulePath = IO.Path.GetFullPath(modulePath);166 var cachedClasses = AnalysisCache.GetExportedClasses(expandedModulePath, _context);167 168 if (cachedClasses != null)169 {170 // Exact match171 if (!_useWildCards)172 {173 if (cachedClasses.ContainsKey(_className))174 {175 var classInfo = CachedItemToPSClassInfo(classNameMatcher, modulePath);176 if (classInfo != null)177 {178 _matchingClassList.Add(classInfo);179 matchFound = true;180 }181 }182 }183 else184 {185 foreach (var className in cachedClasses.Keys)186 {187 if (classNameMatcher.IsMatch(className))188 {189 var classInfo = CachedItemToPSClassInfo(classNameMatcher, modulePath);190 if (classInfo != null)191 {192 _matchingClassList.Add(classInfo);193 matchFound = true;194 }195 }196 }197 }198 }199 }200 201 return matchFound;202 }203 204 /// <summary>205 /// Convert the cacheItem to a PSClassInfo object.206 /// For this, we call Get-Module -List with module name.207 /// </summary>208 /// <param name="classNameMatcher">Wildcard pattern matcher for comparing class name.</param>209 /// <param name="modulePath">Path to the module where the class is defined.</param>210 /// <returns>Converted PSClassInfo object.</returns>211 private PSClassInfo CachedItemToPSClassInfo(WildcardPattern classNameMatcher, string modulePath)212 {213 foreach (var module in GetPSModuleInfo(modulePath))214 {215 var exportedTypes = module.GetExportedTypeDefinitions();216 217 ScriptBlockAst ast = null;218 TypeDefinitionAst typeAst = null;219 220 if (!_useWildCards)221 {222 if (exportedTypes.TryGetValue(_className, out typeAst))223 {224 ast = typeAst.Parent.Parent as ScriptBlockAst;225 if (ast != null)226 return ConvertToClassInfo(module, ast, typeAst);227 }228 }229 else230 {231 foreach (var exportedType in exportedTypes)232 {233 if (exportedType.Value != null &&234 classNameMatcher.IsMatch(exportedType.Value.Name) &&235 exportedType.Value.IsClass)236 {237 ast = exportedType.Value.Parent.Parent as ScriptBlockAst;238 if (ast != null)239 return ConvertToClassInfo(module, ast, exportedType.Value);240 }241 }242 }243 }244 245 return null;246 }247 248 private Collection<PSModuleInfo> GetPSModuleInfo(string modulePath)249 {250 PSModuleInfo moduleInfo = null;251 252 lock (_lockObject)253 {254 _moduleInfoCache.TryGetValue(modulePath, out moduleInfo);255 }256 257 if (moduleInfo != null)258 {259 var returnValue = new Collection<PSModuleInfo>();260 returnValue.Add(moduleInfo);261 return returnValue;262 }263 264 CommandInfo commandInfo = new CmdletInfo("Get-Module", typeof(Microsoft.PowerShell.Commands.GetModuleCommand), null, null, _context);265 System.Management.Automation.Runspaces.Command getModuleCommand = new System.Management.Automation.Runspaces.Command(commandInfo);266 267 string moduleName = Path.GetFileNameWithoutExtension(modulePath);268 269 var modules = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)270 .AddCommand(getModuleCommand)271 .AddParameter("List", true)272 .AddParameter("Name", moduleName)273 .AddParameter("ErrorAction", ActionPreference.Ignore)274 .AddParameter("WarningAction", ActionPreference.Ignore)275 .AddParameter("InformationAction", ActionPreference.Ignore)276 .AddParameter("Verbose", false)277 .AddParameter("Debug", false)278 .Invoke<PSModuleInfo>();279 280 lock (_lockObject)281 {282 foreach (var module in modules)283 {284 _moduleInfoCache.Add(module.Path, module);285 }286 }287 288 return modules;289 }290 291 private static PSClassInfo ConvertToClassInfo(PSModuleInfo module, ScriptBlockAst ast, TypeDefinitionAst statement)292 {293 PSClassInfo classInfo = new PSClassInfo(statement.Name);294 Dbg.Assert(statement.Name != null, "statement should have a name.");295 classInfo.Module = module;296 Collection<PSClassMemberInfo> properties = new Collection<PSClassMemberInfo>();297 298 foreach (var member in statement.Members)299 {300 if (member is PropertyMemberAst propAst && !propAst.PropertyAttributes.HasFlag(PropertyAttributes.Hidden))301 {302 Dbg.Assert(propAst.Name != null, "PropName cannot be null");303 Dbg.Assert(propAst.PropertyType != null, "PropertyType cannot be null");304 Dbg.Assert(propAst.PropertyType.TypeName != null, "Property TypeName cannot be null");305 Dbg.Assert(propAst.Extent != null, "Property Extent cannot be null");306 Dbg.Assert(propAst.Extent.Text != null, "Property ExtentText cannot be null");307 308 PSClassMemberInfo classProperty = new PSClassMemberInfo(propAst.Name,309 propAst.PropertyType.TypeName.FullName,310 propAst.Extent.Text);311 properties.Add(classProperty);312 }313 }314 315 classInfo.UpdateMembers(properties);316 317 string mamlHelpFile = null;318 if (ast.GetHelpContent() != null)319 mamlHelpFile = ast.GetHelpContent().MamlHelpFile;320 321 if (!string.IsNullOrEmpty(mamlHelpFile))322 classInfo.HelpFile = mamlHelpFile;323 324 return classInfo;325 }326 327 #endregion328 }329}330 