1212
1313using System . Text . RegularExpressions ;
1414using System ;
15+ using System . ComponentModel ;
1516using System . Collections . Generic ;
1617using System . Diagnostics . CodeAnalysis ;
1718using System . Globalization ;
2021using System . Management . Automation . Language ;
2122using System . IO ;
2223using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
24+ using System . Threading . Tasks ;
25+ using System . Collections . Concurrent ;
26+ using System . Threading ;
2327
2428namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . Commands
2529{
@@ -267,6 +271,14 @@ private void ProcessPath(string path)
267271
268272 }
269273
274+ ConcurrentBag < DiagnosticRecord > diagnostics ;
275+ ConcurrentBag < SuppressedRecord > suppressed ;
276+ Dictionary < string , List < RuleSuppression > > ruleSuppressions ;
277+ List < Regex > includeRegexList ;
278+ List < Regex > excludeRegexList ;
279+ CountdownEvent cde ;
280+ ConcurrentDictionary < string , List < object > > ruleDictionary ;
281+
270282 /// <summary>
271283 /// Analyzes a single script file.
272284 /// </summary>
@@ -275,15 +287,16 @@ private void AnalyzeFile(string filePath)
275287 {
276288 Token [ ] tokens = null ;
277289 ParseError [ ] errors = null ;
278- List < DiagnosticRecord > diagnostics = new List < DiagnosticRecord > ( ) ;
279- List < SuppressedRecord > suppressed = new List < SuppressedRecord > ( ) ;
290+ diagnostics = new ConcurrentBag < DiagnosticRecord > ( ) ;
291+ suppressed = new ConcurrentBag < SuppressedRecord > ( ) ;
292+ ruleDictionary = new ConcurrentDictionary < string , List < object > > ( ) ;
280293
281294 // Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
282295 List < KeyValuePair < CommandInfo , IScriptExtent > > cmdInfoTable = new List < KeyValuePair < CommandInfo , IScriptExtent > > ( ) ;
283296
284297 //Check wild card input for the Include/ExcludeRules and create regex match patterns
285- List < Regex > includeRegexList = new List < Regex > ( ) ;
286- List < Regex > excludeRegexList = new List < Regex > ( ) ;
298+ includeRegexList = new List < Regex > ( ) ;
299+ excludeRegexList = new List < Regex > ( ) ;
287300 if ( includeRule != null )
288301 {
289302 foreach ( string rule in includeRule )
@@ -331,7 +344,7 @@ private void AnalyzeFile(string filePath)
331344 return ;
332345 }
333346
334- Dictionary < string , List < RuleSuppression > > ruleSuppressions = Helper . Instance . GetRuleSuppression ( ast ) ;
347+ ruleSuppressions = Helper . Instance . GetRuleSuppression ( ast ) ;
335348
336349 foreach ( List < RuleSuppression > ruleSuppressionsList in ruleSuppressions . Values )
337350 {
@@ -360,44 +373,24 @@ private void AnalyzeFile(string filePath)
360373
361374 if ( ScriptAnalyzer . Instance . ScriptRules != null )
362375 {
363- foreach ( IScriptRule scriptRule in ScriptAnalyzer . Instance . ScriptRules )
376+ cde = new CountdownEvent ( ScriptAnalyzer . Instance . ScriptRules . Count ( ) ) ;
377+
378+ foreach ( var scriptRule in ScriptAnalyzer . Instance . ScriptRules )
364379 {
365- bool includeRegexMatch = false ;
366- bool excludeRegexMatch = false ;
367- foreach ( Regex include in includeRegexList )
368- {
369- if ( include . IsMatch ( scriptRule . GetName ( ) ) )
370- {
371- includeRegexMatch = true ;
372- break ;
373- }
374- }
380+ BackgroundWorker bg = new BackgroundWorker ( ) ;
381+ bg . DoWork += bg_DoWork ;
382+ bg . RunWorkerAsync ( new object [ ] { scriptRule } ) ;
383+ }
375384
376- foreach ( Regex exclude in excludeRegexList )
377- {
378- if ( exclude . IsMatch ( scriptRule . GetName ( ) ) )
379- {
380- excludeRegexMatch = true ;
381- break ;
382- }
383- }
385+ cde . Wait ( ) ;
384386
385- if ( ( includeRule == null || includeRegexMatch ) && ( excludeRule == null || ! excludeRegexMatch ) )
387+ foreach ( var rule in ruleDictionary . Keys )
388+ {
389+ List < object > verboseOrErrors = ruleDictionary [ rule ] ;
390+ WriteVerbose ( verboseOrErrors [ 0 ] as string ) ;
391+ if ( verboseOrErrors . Count == 2 )
386392 {
387- WriteVerbose ( string . Format ( CultureInfo . CurrentCulture , Strings . VerboseRunningMessage , scriptRule . GetName ( ) ) ) ;
388-
389- // Ensure that any unhandled errors from Rules are converted to non-terminating errors
390- // We want the Engine to continue functioning even if one or more Rules throws an exception
391- try
392- {
393- var records = Helper . Instance . SuppressRule ( scriptRule . GetName ( ) , ruleSuppressions , scriptRule . AnalyzeScript ( ast , filePath ) . ToList ( ) ) ;
394- diagnostics . AddRange ( records . Item2 ) ;
395- suppressed . AddRange ( records . Item1 ) ;
396- }
397- catch ( Exception scriptRuleException )
398- {
399- WriteError ( new ErrorRecord ( scriptRuleException , Strings . RuleErrorMessage , ErrorCategory . InvalidOperation , filePath ) ) ;
400- }
393+ WriteError ( verboseOrErrors [ 1 ] as ErrorRecord ) ;
401394 }
402395 }
403396 }
@@ -437,8 +430,14 @@ private void AnalyzeFile(string filePath)
437430 try
438431 {
439432 var records = Helper . Instance . SuppressRule ( tokenRule . GetName ( ) , ruleSuppressions , tokenRule . AnalyzeTokens ( tokens , filePath ) . ToList ( ) ) ;
440- diagnostics . AddRange ( records . Item2 ) ;
441- suppressed . AddRange ( records . Item1 ) ;
433+ foreach ( var record in records . Item2 )
434+ {
435+ diagnostics . Add ( record ) ;
436+ }
437+ foreach ( var suppressedRec in records . Item1 )
438+ {
439+ suppressed . Add ( suppressedRec ) ;
440+ }
442441 }
443442 catch ( Exception tokenRuleException )
444443 {
@@ -489,8 +488,14 @@ private void AnalyzeFile(string filePath)
489488 try
490489 {
491490 var records = Helper . Instance . SuppressRule ( dscResourceRule . GetName ( ) , ruleSuppressions , dscResourceRule . AnalyzeDSCClass ( ast , filePath ) . ToList ( ) ) ;
492- diagnostics . AddRange ( records . Item2 ) ;
493- suppressed . AddRange ( records . Item1 ) ;
491+ foreach ( var record in records . Item2 )
492+ {
493+ diagnostics . Add ( record ) ;
494+ }
495+ foreach ( var suppressedRec in records . Item1 )
496+ {
497+ suppressed . Add ( suppressedRec ) ;
498+ }
494499 }
495500 catch ( Exception dscResourceRuleException )
496501 {
@@ -532,8 +537,14 @@ private void AnalyzeFile(string filePath)
532537 try
533538 {
534539 var records = Helper . Instance . SuppressRule ( dscResourceRule . GetName ( ) , ruleSuppressions , dscResourceRule . AnalyzeDSCResource ( ast , filePath ) . ToList ( ) ) ;
535- diagnostics . AddRange ( records . Item2 ) ;
536- suppressed . AddRange ( records . Item1 ) ;
540+ foreach ( var record in records . Item2 )
541+ {
542+ diagnostics . Add ( record ) ;
543+ }
544+ foreach ( var suppressedRec in records . Item1 )
545+ {
546+ suppressed . Add ( suppressedRec ) ;
547+ }
537548 }
538549 catch ( Exception dscResourceRuleException )
539550 {
@@ -573,15 +584,20 @@ private void AnalyzeFile(string filePath)
573584 }
574585 }
575586
576- diagnostics . AddRange ( ScriptAnalyzer . Instance . GetExternalRecord ( ast , tokens , exRules . ToArray ( ) , this , fileName ) ) ;
587+ foreach ( var record in ScriptAnalyzer . Instance . GetExternalRecord ( ast , tokens , exRules . ToArray ( ) , this , fileName ) )
588+ {
589+ diagnostics . Add ( record ) ;
590+ }
577591 }
578592
579593 #endregion
580594
595+ IEnumerable < DiagnosticRecord > diagnosticsList = diagnostics ;
596+
581597 if ( severity != null )
582598 {
583599 var diagSeverity = severity . Select ( item => Enum . Parse ( typeof ( DiagnosticSeverity ) , item , true ) ) ;
584- diagnostics = diagnostics . Where ( item => diagSeverity . Contains ( item . Severity ) ) . ToList ( ) ;
600+ diagnosticsList = diagnostics . Where ( item => diagSeverity . Contains ( item . Severity ) ) ;
585601 }
586602
587603 //Output through loggers
@@ -596,14 +612,73 @@ private void AnalyzeFile(string filePath)
596612 }
597613 else
598614 {
599- foreach ( DiagnosticRecord diagnostic in diagnostics )
615+ foreach ( DiagnosticRecord diagnostic in diagnosticsList )
600616 {
601617 logger . LogObject ( diagnostic , this ) ;
602618 }
603619 }
604620 }
605621 }
606622
623+ void bg_DoWork ( object sender , DoWorkEventArgs e )
624+ {
625+ bool includeRegexMatch = false ;
626+ bool excludeRegexMatch = false ;
627+
628+ object [ ] parameters = e . Argument as object [ ] ;
629+
630+ IScriptRule scriptRule = parameters [ 0 ] as IScriptRule ;
631+
632+ foreach ( Regex include in includeRegexList )
633+ {
634+ if ( include . IsMatch ( scriptRule . GetName ( ) ) )
635+ {
636+ includeRegexMatch = true ;
637+ break ;
638+ }
639+ }
640+
641+ foreach ( Regex exclude in excludeRegexList )
642+ {
643+ if ( exclude . IsMatch ( scriptRule . GetName ( ) ) )
644+ {
645+ excludeRegexMatch = true ;
646+ break ;
647+ }
648+ }
649+
650+ List < object > result = new List < object > ( ) ;
651+
652+ if ( ( includeRule == null || includeRegexMatch ) && ( excludeRule == null || ! excludeRegexMatch ) )
653+ {
654+ //WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName()));
655+ result . Add ( string . Format ( CultureInfo . CurrentCulture , Strings . VerboseRunningMessage , scriptRule . GetName ( ) ) ) ;
656+
657+ // Ensure that any unhandled errors from Rules are converted to non-terminating errors
658+ // We want the Engine to continue functioning even if one or more Rules throws an exception
659+ try
660+ {
661+ var records = Helper . Instance . SuppressRule ( scriptRule . GetName ( ) , ruleSuppressions , scriptRule . AnalyzeScript ( ast , ast . Extent . File ) . ToList ( ) ) ;
662+ foreach ( var record in records . Item2 )
663+ {
664+ diagnostics . Add ( record ) ;
665+ }
666+ foreach ( var suppressedRec in records . Item1 )
667+ {
668+ suppressed . Add ( suppressedRec ) ;
669+ }
670+ }
671+ catch ( Exception scriptRuleException )
672+ {
673+ result . Add ( new ErrorRecord ( scriptRuleException , Strings . RuleErrorMessage , ErrorCategory . InvalidOperation , ast . Extent . File ) ) ;
674+ }
675+ }
676+
677+ ruleDictionary [ scriptRule . GetName ( ) ] = result ;
678+
679+ cde . Signal ( ) ;
680+ }
681+
607682 #endregion
608683 }
609684}
0 commit comments