/// <summary> /// LogObject: Logs the given object though WriteObject. /// </summary> /// <param name="obj">The object to be logged</param> /// <param name="command">The Invoke-PSLint command this logger is running through</param> public void LogObject(Object obj, InvokeScriptAnalyzerCommand command) { if (command == null) { throw new ArgumentNullException("command"); } if (obj == null) { throw new ArgumentNullException("diagnostic"); } command.WriteObject(obj); }
/// <summary> /// GetExternalRecord: Get external rules in parallel using RunspacePool and run each rule in its own runspace. /// </summary> /// <param name="ast"></param> /// <param name="token"></param> /// <param name="rules"></param> /// <param name="command"></param> /// <param name="filePath"></param> /// <returns></returns> public IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token, ExternalRule[] rules, InvokeScriptAnalyzerCommand command, string filePath) { // Defines InitialSessionState. InitialSessionState state = InitialSessionState.CreateDefault2(); // Groups rules by module paths and imports them. Dictionary<string, List<ExternalRule>> modules = rules .GroupBy<ExternalRule, string>(item => item.GetFullModulePath()) .ToDictionary(item => item.Key, item => item.ToList()); state.ImportPSModule(modules.Keys.ToArray<string>()); // Creates and opens RunspacePool RunspacePool rsp = RunspaceFactory.CreateRunspacePool(state); rsp.SetMinRunspaces(1); rsp.SetMaxRunspaces(5); rsp.Open(); // Groups rules by AstType and Tokens. Dictionary<string, List<ExternalRule>> astRuleGroups = rules .Where<ExternalRule>(item => item.GetParameter().EndsWith("ast", StringComparison.OrdinalIgnoreCase)) .GroupBy<ExternalRule, string>(item => item.GetParameterType()) .ToDictionary(item => item.Key, item => item.ToList()); Dictionary<string, List<ExternalRule>> tokenRuleGroups = rules .Where<ExternalRule>(item => item.GetParameter().EndsWith("token", StringComparison.OrdinalIgnoreCase)) .GroupBy<ExternalRule, string>(item => item.GetParameterType()) .ToDictionary(item => item.Key, item => item.ToList()); using (rsp) { // Defines the commands to be run. List<System.Management.Automation.PowerShell> powerShellCommands = new List<System.Management.Automation.PowerShell>(); // Defines the command results. List<IAsyncResult> powerShellCommandResults = new List<IAsyncResult>(); #region Builds and invokes commands list foreach (KeyValuePair<string, List<ExternalRule>> tokenRuleGroup in tokenRuleGroups) { foreach (IExternalRule rule in tokenRuleGroup.Value) { System.Management.Automation.PowerShell posh = System.Management.Automation.PowerShell.Create(); posh.RunspacePool = rsp; // Adds command to run external analyzer rule, like // Measure-CurlyBracket -ScriptBlockAst $ScriptBlockAst // Adds module name (source name) to handle ducplicate function names in different modules. string ruleName = string.Format("{0}\\{1}", rule.GetSourceName(), rule.GetName()); posh.Commands.AddCommand(ruleName); posh.Commands.AddParameter(rule.GetParameter(), token); // Merges results because external analyzer rules may throw exceptions. posh.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output); powerShellCommands.Add(posh); powerShellCommandResults.Add(posh.BeginInvoke()); } } foreach (KeyValuePair<string, List<ExternalRule>> astRuleGroup in astRuleGroups) { // Find all AstTypes that appeared in rule groups. IEnumerable<Ast> childAsts = ast.FindAll(new Func<Ast, bool>((testAst) => (astRuleGroup.Key.IndexOf(testAst.GetType().FullName,StringComparison.OrdinalIgnoreCase) != -1)), false); foreach (Ast childAst in childAsts) { foreach (IExternalRule rule in astRuleGroup.Value) { System.Management.Automation.PowerShell posh = System.Management.Automation.PowerShell.Create(); posh.RunspacePool = rsp; // Adds command to run external analyzer rule, like // Measure-CurlyBracket -ScriptBlockAst $ScriptBlockAst // Adds module name (source name) to handle ducplicate function names in different modules. string ruleName = string.Format("{0}\\{1}", rule.GetSourceName(), rule.GetName()); posh.Commands.AddCommand(ruleName); posh.Commands.AddParameter(rule.GetParameter(), childAst); // Merges results because external analyzer rules may throw exceptions. posh.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output); powerShellCommands.Add(posh); powerShellCommandResults.Add(posh.BeginInvoke()); } } } #endregion #region Collects the results from commands. List<DiagnosticRecord> diagnostics = new List<DiagnosticRecord>(); try { for (int i = 0; i < powerShellCommands.Count; i++) { // EndInvoke will wait for each command to finish, so we will be getting the commands // in the same order that they have been invoked withy BeginInvoke. PSDataCollection<PSObject> psobjects = powerShellCommands[i].EndInvoke(powerShellCommandResults[i]); foreach (var psobject in psobjects) { DiagnosticSeverity severity; IScriptExtent extent; string message = string.Empty; string ruleName = string.Empty; if (psobject != null && psobject.ImmediateBaseObject != null) { // Because error stream is merged to output stream, // we need to handle the error records. if (psobject.ImmediateBaseObject is ErrorRecord) { ErrorRecord record = (ErrorRecord)psobject.ImmediateBaseObject; command.WriteError(record); continue; } // DiagnosticRecord may not be correctly returned from external rule. try { Enum.TryParse<DiagnosticSeverity>(psobject.Properties["Severity"].Value.ToString().ToUpper(), out severity); message = psobject.Properties["Message"].Value.ToString(); extent = (IScriptExtent)psobject.Properties["Extent"].Value; ruleName = psobject.Properties["RuleName"].Value.ToString(); } catch (Exception ex) { command.WriteError(new ErrorRecord(ex, ex.HResult.ToString("X"), ErrorCategory.NotSpecified, this)); continue; } if (!string.IsNullOrEmpty(message)) { diagnostics.Add(new DiagnosticRecord(message, extent, ruleName, severity, null)); } } } } } //Catch exception where customized defined rules have exceptins when doing invoke catch(Exception ex) { command.WriteError(new ErrorRecord(ex, ex.HResult.ToString("X"), ErrorCategory.NotSpecified, this)); } return diagnostics; #endregion } }