This commit is contained in:
Bernie White 2020-08-30 17:53:45 +10:00 коммит произвёл GitHub
Родитель a1ea08f10e
Коммит 76b184999e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
38 изменённых файлов: 325 добавлений и 191 удалений

Просмотреть файл

@ -0,0 +1,24 @@
``` ini
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18363.778 (1909/November2018Update/19H2)
Intel Core i7-6600U CPU 2.60GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=3.1.201
[Host] : .NET Core 2.1.17 (CoreCLR 4.6.28619.01, CoreFX 4.6.28619.01), X64 RyuJIT
DefaultJob : .NET Core 2.1.17 (CoreCLR 4.6.28619.01, CoreFX 4.6.28619.01), X64 RyuJIT
```
| Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated |
|------------------------- |-----------:|----------:|----------:|-----------:|-----------:|------:|------:|------------:|
| Invoke | 111.140 ms | 2.1935 ms | 4.5786 ms | 109.312 ms | 8200.0000 | - | - | 16839.42 KB |
| InvokeIf | 117.141 ms | 2.2703 ms | 2.2298 ms | 116.398 ms | 9600.0000 | - | - | 19980.62 KB |
| InvokeType | 108.648 ms | 0.7983 ms | 0.7467 ms | 108.584 ms | 8200.0000 | - | - | 16870.67 KB |
| InvokeSummary | 107.300 ms | 0.8612 ms | 0.8056 ms | 107.115 ms | 8000.0000 | - | - | 16784.76 KB |
| Get | 9.003 ms | 0.0643 ms | 0.0602 ms | 9.010 ms | 140.6250 | - | - | 307.96 KB |
| GetHelp | 8.902 ms | 0.0831 ms | 0.0649 ms | 8.899 ms | 140.6250 | - | - | 306.34 KB |
| Within | 179.522 ms | 1.5483 ms | 1.4483 ms | 179.981 ms | 15666.6667 | - | - | 32400.38 KB |
| WithinBulk | 247.883 ms | 2.6279 ms | 2.1944 ms | 248.124 ms | 28500.0000 | - | - | 59306.73 KB |
| WithinLike | 238.815 ms | 2.5538 ms | 1.9939 ms | 239.245 ms | 29333.3333 | - | - | 60580.58 KB |
| DefaultTargetNameBinding | 2.124 ms | 0.0214 ms | 0.0200 ms | 2.129 ms | 85.9375 | - | - | 179.69 KB |
| CustomTargetNameBinding | 2.463 ms | 0.0483 ms | 0.0452 ms | 2.458 ms | 179.6875 | - | - | 375 KB |
| NestedTargetNameBinding | 2.433 ms | 0.0370 ms | 0.0328 ms | 2.420 ms | 179.6875 | - | - | 375 KB |

Просмотреть файл

@ -0,0 +1,24 @@
``` ini
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1)
Intel Core i7-1065G7 CPU 1.30GHz, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.401
[Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT
DefaultJob : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT
```
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|------------------------- |-------------:|------------:|------------:|-----------:|---------:|------:|------------:|
| Invoke | 40,943.5 μs | 581.23 μs | 515.25 μs | 4000.0000 | 500.0000 | - | 16452.28 KB |
| InvokeIf | 42,806.0 μs | 477.29 μs | 423.11 μs | 4500.0000 | 500.0000 | - | 18703.12 KB |
| InvokeType | 40,470.1 μs | 484.16 μs | 429.19 μs | 4000.0000 | 538.4615 | - | 16452.27 KB |
| InvokeSummary | 39,768.8 μs | 462.14 μs | 385.91 μs | 4000.0000 | 153.8462 | - | 16397.82 KB |
| Get | 11,145.4 μs | 402.59 μs | 1,187.03 μs | 46.8750 | - | - | 252.11 KB |
| GetHelp | 10,169.1 μs | 625.02 μs | 1,842.88 μs | 46.8750 | - | - | 250.51 KB |
| Within | 78,993.5 μs | 799.51 μs | 667.63 μs | 8000.0000 | 400.0000 | - | 32791.83 KB |
| WithinBulk | 118,800.8 μs | 1,637.36 μs | 1,531.59 μs | 14333.3333 | 333.3333 | - | 59817.29 KB |
| WithinLike | 106,796.3 μs | 2,067.20 μs | 2,538.71 μs | 11333.3333 | - | - | 47311.07 KB |
| DefaultTargetNameBinding | 698.2 μs | 7.51 μs | 7.02 μs | 38.0859 | - | - | 156.25 KB |
| CustomTargetNameBinding | 884.7 μs | 7.11 μs | 6.65 μs | 85.9375 | - | - | 351.56 KB |
| NestedTargetNameBinding | 883.9 μs | 14.44 μs | 12.80 μs | 85.9375 | - | - | 351.56 KB |

Просмотреть файл

@ -0,0 +1,24 @@
``` ini
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1)
Intel Core i7-1065G7 CPU 1.30GHz, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.401
[Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT
DefaultJob : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT
```
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|------------------------- |-------------:|------------:|------------:|-----------:|----------:|------:|------------:|
| Invoke | 42,162.8 μs | 827.36 μs | 1,263.47 μs | 3833.3333 | - | - | 15952 KB |
| InvokeIf | 45,646.4 μs | 912.31 μs | 1,924.38 μs | 4416.6667 | 416.6667 | - | 18202.98 KB |
| InvokeType | 41,825.5 μs | 810.73 μs | 901.12 μs | 3833.3333 | - | - | 15952 KB |
| InvokeSummary | 41,133.3 μs | 777.97 μs | 895.91 μs | 3833.3333 | 500.0000 | - | 15897.56 KB |
| Get | 10,054.3 μs | 396.83 μs | 1,170.07 μs | 46.8750 | - | - | 252.11 KB |
| GetHelp | 10,581.4 μs | 448.15 μs | 1,321.38 μs | 46.8750 | - | - | 250.51 KB |
| Within | 81,215.1 μs | 1,532.85 μs | 1,433.83 μs | 7750.0000 | 250.0000 | - | 32290.62 KB |
| WithinBulk | 123,301.6 μs | 2,451.51 μs | 3,958.73 μs | 14000.0000 | 1000.0000 | - | 59317.29 KB |
| WithinLike | 109,738.9 μs | 1,933.95 μs | 1,809.02 μs | 11333.3333 | 1000.0000 | - | 46811.07 KB |
| DefaultTargetNameBinding | 696.0 μs | 12.06 μs | 10.69 μs | 38.0859 | - | - | 156.25 KB |
| CustomTargetNameBinding | 845.6 μs | 11.75 μs | 10.42 μs | 85.9375 | - | - | 351.56 KB |
| NestedTargetNameBinding | 856.0 μs | 12.29 μs | 10.90 μs | 85.9375 | - | - | 351.56 KB |

Просмотреть файл

@ -294,7 +294,7 @@ task Rules {
task Benchmark {
if ($Benchmark -or $BuildTask -eq 'Benchmark') {
dotnet run -p src/PSRule.Benchmark -f netcoreapp2.1 -c Release -- benchmark --output $PWD;
dotnet run -p src/PSRule.Benchmark -f netcoreapp3.1 -c Release -- benchmark --output $PWD;
}
}

Просмотреть файл

@ -12,6 +12,8 @@ using BenchmarkDotNet.Running;
#endif
using Microsoft.Extensions.CommandLineUtils;
using System;
using System.Diagnostics;
namespace PSRule.Benchmark
{
@ -27,7 +29,8 @@ namespace PSRule.Benchmark
#if !BENCHMARK
// Do profiling
DebugProfile();
DebugProfile(app);
app.Execute(args);
#endif
#if BENCHMARK
@ -71,34 +74,64 @@ namespace PSRule.Benchmark
#endif
private static void DebugProfile()
private const int DebugIterations = 100;
private static void DebugProfile(CommandLineApplication app)
{
app.Command("benchmark", cmd =>
{
cmd.OnExecute(() =>
{
Console.WriteLine("Press ENTER to start.");
Console.ReadLine();
RunDebug();
return 0;
});
});
}
private static void RunDebug()
{
var profile = new PSRule();
profile.Prepare();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.Invoke();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.InvokeIf();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.InvokeType();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.InvokeSummary();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.Get();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.DefaultTargetNameBinding();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.CustomTargetNameBinding();
for (var i = 0; i < 100; i++)
ProfileBlock();
for (var i = 0; i < DebugIterations; i++)
profile.NestedTargetNameBinding();
}
[DebuggerStepThrough]
private static void ProfileBlock()
{
// Do nothing
}
}
}

Просмотреть файл

@ -2,10 +2,8 @@
// Licensed under the MIT License.
using PSRule.Pipeline;
using PSRule.Resources;
using PSRule.Runtime;
using System.Management.Automation;
using System.Threading;
namespace PSRule.Commands
{
@ -21,9 +19,9 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsConditionScope())
throw new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.KeywordConditionScope, LanguageKeywords.AllOf));
throw ConditionScopeException(LanguageKeywords.AllOf);
var invokeResult = RuleConditionResult.Create(Body.Invoke());
var invokeResult = RuleConditionHelper.Create(Body.Invoke());
var result = invokeResult.AllOf();
RunspaceContext.CurrentThread.VerboseConditionResult(condition: RuleLanguageNouns.AllOf, pass: invokeResult.Pass, count: invokeResult.Count, outcome: result);

Просмотреть файл

@ -2,10 +2,8 @@
// Licensed under the MIT License.
using PSRule.Pipeline;
using PSRule.Resources;
using PSRule.Runtime;
using System.Management.Automation;
using System.Threading;
namespace PSRule.Commands
{
@ -21,9 +19,9 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsConditionScope())
throw new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.KeywordConditionScope, LanguageKeywords.AnyOf));
throw ConditionScopeException(LanguageKeywords.AnyOf);
var invokeResult = RuleConditionResult.Create(Body.Invoke());
var invokeResult = RuleConditionHelper.Create(Body.Invoke());
var result = invokeResult.AnyOf();
RunspaceContext.CurrentThread.VerboseConditionResult(condition: RuleLanguageNouns.AnyOf, pass: invokeResult.Pass, count: invokeResult.Count, outcome: result);

Просмотреть файл

@ -47,7 +47,7 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsRuleScope())
throw new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.KeywordRuleScope, RuleLanguageNouns.Exists));
throw RuleScopeException(LanguageKeywords.Exists);
var targetObject = InputObject ?? GetTargetObject();
var foundFields = new List<string>();
@ -57,7 +57,7 @@ namespace PSRule.Commands
for (var i = 0; i < Field.Length && found < required; i++)
{
if (ObjectHelper.GetField(bindingContext: PipelineContext.CurrentThread, targetObject: targetObject, name: Field[i], caseSensitive: CaseSensitive, value: out object fieldValue))
if (ObjectHelper.GetField(bindingContext: PipelineContext.CurrentThread, targetObject: targetObject, name: Field[i], caseSensitive: CaseSensitive, value: out _))
{
RunspaceContext.CurrentThread.VerboseConditionMessage(condition: RuleLanguageNouns.Exists, message: PSRuleResources.ExistsTrue, args: Field[i]);
foundFields.Add(Field[i]);

Просмотреть файл

@ -59,9 +59,7 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsRuleScope())
{
throw new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.KeywordRuleScope, RuleLanguageNouns.Match));
}
throw RuleScopeException(LanguageKeywords.Match);
var targetObject = InputObject ?? GetTargetObject();
bool expected = !Not;

Просмотреть файл

@ -5,6 +5,7 @@ using PSRule.Pipeline;
using PSRule.Resources;
using System.Linq;
using System.Management.Automation;
using System.Threading;
namespace PSRule.Commands
{
@ -26,9 +27,7 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsRuleScope())
{
throw new RuleRuntimeException(string.Format(PSRuleResources.KeywordRuleScope, RuleLanguageNouns.TypeOf));
}
throw RuleScopeException(LanguageKeywords.TypeOf);
var inputObject = InputObject ?? GetTargetObject();
var result = false;
@ -42,7 +41,7 @@ namespace PSRule.Commands
RunspaceContext.CurrentThread.VerboseConditionResult(condition: RuleLanguageNouns.TypeOf, outcome: result);
if (!(result || TryReason(Reason)))
{
WriteReason(string.Format(ReasonStrings.TypeOf, string.Join(", ", TypeName)));
WriteReason(string.Format(Thread.CurrentThread.CurrentCulture, ReasonStrings.TypeOf, string.Join(", ", TypeName)));
}
WriteObject(result);
}

Просмотреть файл

@ -6,6 +6,7 @@ using PSRule.Resources;
using PSRule.Runtime;
using System;
using System.Management.Automation;
using System.Threading;
namespace PSRule.Commands
{
@ -59,7 +60,7 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsRuleScope())
throw new RuleRuntimeException(string.Format(PSRuleResources.KeywordRuleScope, LanguageKeywords.Within));
throw RuleScopeException(LanguageKeywords.Within);
var targetObject = InputObject ?? GetTargetObject();
bool expected = !Not;
@ -109,7 +110,7 @@ namespace PSRule.Commands
RunspaceContext.CurrentThread.VerboseConditionResult(condition: RuleLanguageNouns.Within, outcome: result);
if (!(result || TryReason(Reason)))
{
WriteReason(Not ? string.Format(ReasonStrings.WithinNot, found) : ReasonStrings.Within);
WriteReason(Not ? string.Format(Thread.CurrentThread.CurrentCulture, ReasonStrings.WithinNot, found) : ReasonStrings.Within);
}
WriteObject(result);
}

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT License.
using PSRule.Pipeline;
using PSRule.Resources;
using PSRule.Runtime;
using System;
using System.Management.Automation;
@ -32,7 +33,7 @@ namespace PSRule.Commands
// Evalute type pre-condition
if (!AcceptsType())
{
RunspaceContext.CurrentThread.Writer.DebugMessage("Target failed Type precondition");
RunspaceContext.CurrentThread.Writer.DebugMessage(PSRuleResources.DebugTargetTypeMismatch);
return;
}
@ -40,17 +41,17 @@ namespace PSRule.Commands
if (If != null)
{
PipelineContext.CurrentThread.ExecutionScope = ExecutionScope.Precondition;
var ifResult = RuleConditionResult.Create(If.Invoke());
var ifResult = RuleConditionHelper.Create(If.Invoke());
if (!ifResult.AllOf())
{
RunspaceContext.CurrentThread.Writer.DebugMessage("Target failed If precondition");
RunspaceContext.CurrentThread.Writer.DebugMessage(PSRuleResources.DebugTargetIfMismatch);
return;
}
}
// Evaluate script block
PipelineContext.CurrentThread.ExecutionScope = ExecutionScope.Condition;
var invokeResult = RuleConditionResult.Create(Body.Invoke());
var invokeResult = RuleConditionHelper.Create(Body.Invoke());
WriteObject(invokeResult);
}
finally

Просмотреть файл

@ -15,17 +15,17 @@ namespace PSRule.Commands
/// </summary>
internal abstract class LanguageBlock : PSCmdlet
{
protected CommentMetadata GetMetadata(string path, int lineNumber, int offset)
protected static CommentMetadata GetMetadata(string path, int lineNumber, int offset)
{
return HostHelper.GetCommentMeta(path, lineNumber - 2, offset);
}
protected TagSet GetTag(Hashtable hashtable)
protected static TagSet GetTag(Hashtable hashtable)
{
return TagSet.FromHashtable(hashtable);
}
protected bool IsScriptScope()
protected static bool IsScriptScope()
{
return PipelineContext.CurrentThread.ExecutionScope == ExecutionScope.Script;
}

Просмотреть файл

@ -2,10 +2,12 @@
// Licensed under the MIT License.
using PSRule.Pipeline;
using PSRule.Resources;
using PSRule.Rules;
using System;
using System.Collections;
using System.Management.Automation;
using System.Threading;
namespace PSRule.Commands
{
@ -14,12 +16,12 @@ namespace PSRule.Commands
/// </summary>
internal abstract class RuleKeyword : PSCmdlet
{
protected RuleRecord GetResult()
protected static RuleRecord GetResult()
{
return RunspaceContext.CurrentThread.RuleRecord;
}
protected PSObject GetTargetObject()
protected static PSObject GetTargetObject()
{
return RunspaceContext.CurrentThread.TargetObject;
}
@ -80,50 +82,52 @@ namespace PSRule.Commands
return false;
}
protected object GetBaseObject(object value)
protected static object GetBaseObject(object value)
{
if (value == null)
{
return null;
}
if (value is PSObject pso)
{
var baseObject = pso.BaseObject;
if (baseObject != null)
{
return baseObject;
}
}
return value;
}
protected void WriteReason(string text)
protected static void WriteReason(string text)
{
RunspaceContext.CurrentThread.WriteReason(text);
}
protected bool TryReason(string text)
protected static bool TryReason(string text)
{
if (string.IsNullOrEmpty(text))
{
return false;
}
WriteReason(text);
return true;
}
protected bool IsRuleScope()
protected static bool IsRuleScope()
{
return PipelineContext.CurrentThread.ExecutionScope == ExecutionScope.Condition || PipelineContext.CurrentThread.ExecutionScope == ExecutionScope.Precondition;
}
protected bool IsConditionScope()
protected static bool IsConditionScope()
{
return PipelineContext.CurrentThread.ExecutionScope == ExecutionScope.Condition;
}
protected static RuleRuntimeException RuleScopeException(string keyword)
{
return new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.KeywordRuleScope, keyword));
}
protected static RuleRuntimeException ConditionScopeException(string keyword)
{
return new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.KeywordConditionScope, keyword));
}
}
}

Просмотреть файл

@ -2,7 +2,6 @@
// Licensed under the MIT License.
using PSRule.Pipeline;
using PSRule.Resources;
using System.Management.Automation;
namespace PSRule.Commands
@ -19,7 +18,8 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsConditionScope())
throw new RuleRuntimeException(string.Format(PSRuleResources.KeywordConditionScope, LanguageKeywords.Reason));
throw ConditionScopeException(LanguageKeywords.Reason);
if (MyInvocation.BoundParameters.ContainsKey(nameof(Text)))
RunspaceContext.CurrentThread.WriteReason(text: Text);

Просмотреть файл

@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using PSRule.Pipeline;
using PSRule.Resources;
using System.Management.Automation;
namespace PSRule.Commands
@ -20,9 +18,7 @@ namespace PSRule.Commands
protected override void ProcessRecord()
{
if (!IsConditionScope())
{
throw new RuleRuntimeException(string.Format(PSRuleResources.KeywordConditionScope, LanguageKeywords.Recommend));
}
throw ConditionScopeException(LanguageKeywords.Recommend);
var result = GetResult();

Просмотреть файл

@ -6,7 +6,7 @@ using System.Security;
namespace PSRule
{
internal sealed class EnvironmentHelper
internal static class EnvironmentHelper
{
private const char UNDERSCORE = '_';

Просмотреть файл

@ -12,7 +12,7 @@ namespace PSRule.Host
private readonly DependencyTarget[] _Targets;
// Track whether Dispose has been called.
private bool _Disposed = false;
private bool _Disposed;
public DependencyGraph(T[] targets)
{
@ -40,12 +40,12 @@ namespace PSRule.Host
DependencyFail = 3
}
public sealed class DependencyTarget
internal sealed class DependencyTarget
{
public readonly DependencyGraph<T> Graph;
public readonly T Value;
internal DependencyTargetState State;
private DependencyTargetState State;
public DependencyTarget(DependencyGraph<T> graph, T value)
{
@ -58,6 +58,16 @@ namespace PSRule.Host
get { return State == DependencyTargetState.DependencyFail; }
}
public bool Failed
{
get { return State == DependencyTargetState.Fail || State == DependencyTargetState.DependencyFail; }
}
public bool Passed
{
get { return State == DependencyTargetState.Pass; }
}
public void Pass()
{
State = DependencyTargetState.Pass;
@ -67,34 +77,38 @@ namespace PSRule.Host
{
State = DependencyTargetState.Fail;
}
public void DependencyFail()
{
State = DependencyTargetState.DependencyFail;
}
}
public IEnumerable<DependencyTarget> GetSingleTarget()
{
foreach (var target in _Targets)
for (var t = 0; t < _Targets.Length; t++)
{
var target = _Targets[t];
if (target.Value.DependsOn != null && target.Value.DependsOn.Length > 0)
{
// Process each dependency
foreach (var d in target.Value.DependsOn)
for (var d = 0; d < target.Value.DependsOn.Length; d++)
{
var dTarget = _Index[d];
var dTarget = _Index[target.Value.DependsOn[d]];
// Check if dependency was already completed
if (dTarget.State == DependencyTargetState.Pass)
if (dTarget.Passed)
{
continue;
}
else if (dTarget.State == DependencyTargetState.Fail || dTarget.State == DependencyTargetState.DependencyFail)
else if (dTarget.Failed)
{
target.State = DependencyTargetState.DependencyFail;
target.DependencyFail();
break;
}
yield return dTarget;
}
}
yield return target;
}
}

Просмотреть файл

@ -6,6 +6,7 @@ using PSRule.Resources;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace PSRule.Host
{
@ -32,7 +33,7 @@ namespace PSRule.Host
foreach (var item in items)
{
if (index.ContainsKey(item.RuleId))
throw new RuleRuntimeException(message: string.Format(PSRuleResources.DuplicateRuleId, item.RuleId));
throw new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DuplicateRuleId, item.RuleId));
index.Add(item.RuleId, item);
}
@ -63,7 +64,7 @@ namespace PSRule.Host
// Check for circular dependencies
if (_Stack.Contains(value: ruleId, comparer: _Comparer))
throw new RuleRuntimeException(message: string.Format(PSRuleResources.DependencyCircularReference, parentId, ruleId));
throw new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DependencyCircularReference, parentId, ruleId));
try
{
@ -76,7 +77,7 @@ namespace PSRule.Host
foreach (var d in item.DependsOn)
{
if (!index.ContainsKey(d))
throw new RuleRuntimeException(message: string.Format(PSRuleResources.DependencyNotFound, d, ruleId));
throw new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DependencyNotFound, d, ruleId));
// Handle dependencies
if (!_Targets.ContainsKey(d))

Просмотреть файл

@ -88,10 +88,10 @@ namespace PSRule.Host
{
foreach (var comment in comments)
{
if (comment.StartsWith("# Description: "))
if (comment.StartsWith("# Description: ", StringComparison.OrdinalIgnoreCase))
metadata.Synopsis = comment.Substring(15);
if (comment.StartsWith("# Synopsis: "))
if (comment.StartsWith("# Synopsis: ", StringComparison.OrdinalIgnoreCase))
metadata.Synopsis = comment.Substring(12);
}
}
@ -156,11 +156,8 @@ namespace PSRule.Host
foreach (var ir in invokeResults)
{
if (ir.BaseObject is RuleBlock)
{
var block = ir.BaseObject as RuleBlock;
if (ir.BaseObject is RuleBlock block)
results.Add(block);
}
}
}
}
@ -250,7 +247,7 @@ namespace PSRule.Host
{
ruleRecord.OutcomeReason = RuleOutcomeReason.Inconclusive;
ruleRecord.Outcome = RuleOutcome.Fail;
context.WarnRuleInconclusive(ruleId: ruleRecord.RuleId);
context.WarnRuleInconclusive(ruleRecord.RuleId);
}
else
{

Просмотреть файл

@ -9,10 +9,8 @@ namespace PSRule.Parser
{
public Dictionary<string, string> Process(TokenStream stream)
{
stream.MoveTo(0);
// Look for yaml header
stream.MoveTo(0);
return YamlHeader(stream);
}
}

Просмотреть файл

@ -6,6 +6,7 @@ using PSRule.Resources;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace PSRule.Parser
{
@ -26,11 +27,9 @@ namespace PSRule.Parser
public RuleDocument Process(TokenStream stream)
{
stream.MoveTo(0);
// Look for yaml header
stream.MoveTo(0);
var metadata = YamlHeader(stream);
RuleDocument doc = null;
// Process sections
@ -151,7 +150,7 @@ namespace PSRule.Parser
return true;
}
private TextBlock TextBlock(TokenStream stream)
private static TextBlock TextBlock(TokenStream stream)
{
var useBreak = stream.Current.IsDoubleLineEnding();
stream.Next();
@ -177,7 +176,7 @@ namespace PSRule.Parser
AppendEnding(sb, stream.Peak(-1));
sb.Append(stream.Current.Meta);
if (!string.IsNullOrEmpty(stream.Current.Text))
sb.AppendFormat(" ({0})", stream.Current.Text);
sb.AppendFormat(Thread.CurrentThread.CurrentCulture, " ({0})", stream.Current.Text);
}
else if (stream.IsTokenType(MarkdownTokenType.LinkReference))
{

Просмотреть файл

@ -127,15 +127,13 @@ namespace PSRule.Pipeline
internal GetTargetPipeline(PipelineContext context, PipelineReader reader, PipelineWriter writer)
: base(context, null, reader, writer) { }
public override void Process(PSObject targetObject)
public override void Process(PSObject sourceObject)
{
try
{
Reader.Enqueue(targetObject);
Reader.Enqueue(sourceObject);
while (Reader.TryDequeue(out PSObject next))
{
Writer.WriteObject(next, false);
}
}
catch (Exception)
{

Просмотреть файл

@ -25,9 +25,7 @@ namespace PSRule.Pipeline
if (commandRuntime != null)
ShouldProcess = commandRuntime.ShouldProcess;
InSession = executionContext == null ? false : executionContext.SessionState.PSVariable.GetValue("PSSenderInfo") != null;
InSession = executionContext != null && executionContext.SessionState.PSVariable.GetValue("PSSenderInfo") != null;
}
}
}

Просмотреть файл

@ -179,7 +179,7 @@ namespace PSRule.Pipeline
private readonly RuleSuppressionFilter _SuppressionFilter;
// Track whether Dispose has been called.
private bool _Disposed = false;
private bool _Disposed;
internal InvokeRulePipeline(PipelineContext context, Source[] source, PipelineReader reader, PipelineWriter writer, RuleOutcome outcome)
: base(context, source, reader, writer)
@ -199,11 +199,11 @@ namespace PSRule.Pipeline
public int RuleCount { get; private set; }
public override void Process(PSObject targetObject)
public override void Process(PSObject sourceObject)
{
try
{
Reader.Enqueue(targetObject);
Reader.Enqueue(sourceObject);
while (Reader.TryDequeue(out PSObject next))
{
var result = ProcessTargetObject(next);

Просмотреть файл

@ -41,7 +41,7 @@ namespace PSRule.Pipeline
private readonly string _DefaultSearchPattern;
private readonly PathFilter _GlobalFilter;
internal PathBuilder(ILogger logger, string basePath, string searchPattern, PathFilter filter)
protected PathBuilder(ILogger logger, string basePath, string searchPattern, PathFilter filter)
{
_Logger = logger;
_Files = new List<InputFileInfo>();
@ -171,11 +171,6 @@ namespace PSRule.Pipeline
if (string.IsNullOrEmpty(searchPattern))
searchPattern = _DefaultSearchPattern;
// If a path separator is within the pattern use a resursive search
//if (relativeAnchor || !string.IsNullOrEmpty(pathLiteral))
//if (relativeAnchor && string.IsNullOrEmpty(searchPattern))
// searchOption = SearchOption.TopDirectoryOnly;
return GetRootedPath(pathLiteral);
}

Просмотреть файл

@ -62,12 +62,9 @@ namespace PSRule.Pipeline
private const char Hash = '#'; // Comment
private const char Exclamation = '!'; // Include a previously excluded path
private const string GitIgnoreFileName = ".gitignore";
private readonly string _BasePath;
private readonly PathFilterExpression[] _Expression;
private bool _MatchResult;
private readonly bool _MatchResult;
private PathFilter(string basePath, PathFilterExpression[] expression, bool matchResult)
{

Просмотреть файл

@ -16,7 +16,7 @@ namespace PSRule.Pipeline
protected readonly PipelineWriter Writer;
// Track whether Dispose has been called.
private bool _Disposed = false;
private bool _Disposed;
protected RulePipeline(PipelineContext context, Source[] source, PipelineReader reader, PipelineWriter writer)
{

Просмотреть файл

@ -43,7 +43,7 @@ namespace PSRule.Pipeline
private readonly OutcomeLogStream _FailStream;
private readonly OutcomeLogStream _PassStream;
private bool _RaisedUsingInvariantCulture = false;
private bool _RaisedUsingInvariantCulture;
// Pipeline logging
private string _LogPrefix;
@ -53,7 +53,7 @@ namespace PSRule.Pipeline
private readonly List<string> _Reason;
// Track whether Dispose has been called.
private bool _Disposed = false;
private bool _Disposed;
internal RunspaceContext(PipelineContext pipeline, PipelineWriter writer)
{
@ -77,13 +77,13 @@ namespace PSRule.Pipeline
return;
if (_PassStream == OutcomeLogStream.Warning && Writer.ShouldWriteWarning())
Writer.WriteWarning(string.Format(PSRuleResources.OutcomeRulePass, RuleRecord.RuleName, Pipeline.Binder.TargetName));
Writer.WriteWarning(PSRuleResources.OutcomeRulePass, RuleRecord.RuleName, Pipeline.Binder.TargetName);
if (_PassStream == OutcomeLogStream.Error && Writer.ShouldWriteError())
Writer.WriteError(new ErrorRecord(new RuleRuntimeException(string.Format(PSRuleResources.OutcomeRulePass, RuleRecord.RuleName, Pipeline.Binder.TargetName)), SOURCE_OUTCOME_PASS, ErrorCategory.InvalidData, null));
Writer.WriteError(new ErrorRecord(new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.OutcomeRulePass, RuleRecord.RuleName, Pipeline.Binder.TargetName)), SOURCE_OUTCOME_PASS, ErrorCategory.InvalidData, null));
if (_PassStream == OutcomeLogStream.Information && Writer.ShouldWriteInformation())
Writer.WriteInformation(new InformationRecord(messageData: string.Format(PSRuleResources.OutcomeRulePass, RuleRecord.RuleName, Pipeline.Binder.TargetName), source: SOURCE_OUTCOME_PASS));
Writer.WriteInformation(new InformationRecord(messageData: string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.OutcomeRulePass, RuleRecord.RuleName, Pipeline.Binder.TargetName), source: SOURCE_OUTCOME_PASS));
}
public void Fail()
@ -92,13 +92,13 @@ namespace PSRule.Pipeline
return;
if (_FailStream == OutcomeLogStream.Warning && Writer.ShouldWriteWarning())
Writer.WriteWarning(string.Format(PSRuleResources.OutcomeRuleFail, RuleRecord.RuleName, Pipeline.Binder.TargetName));
Writer.WriteWarning(PSRuleResources.OutcomeRuleFail, RuleRecord.RuleName, Pipeline.Binder.TargetName);
if (_FailStream == OutcomeLogStream.Error && Writer.ShouldWriteError())
Writer.WriteError(new ErrorRecord(new RuleRuntimeException(string.Format(PSRuleResources.OutcomeRuleFail, RuleRecord.RuleName, Pipeline.Binder.TargetName)), SOURCE_OUTCOME_FAIL, ErrorCategory.InvalidData, null));
Writer.WriteError(new ErrorRecord(new RuleRuntimeException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.OutcomeRuleFail, RuleRecord.RuleName, Pipeline.Binder.TargetName)), SOURCE_OUTCOME_FAIL, ErrorCategory.InvalidData, null));
if (_FailStream == OutcomeLogStream.Information && Writer.ShouldWriteInformation())
Writer.WriteInformation(new InformationRecord(messageData: string.Format(PSRuleResources.OutcomeRuleFail, RuleRecord.RuleName, Pipeline.Binder.TargetName), source: SOURCE_OUTCOME_FAIL));
Writer.WriteInformation(new InformationRecord(messageData: string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.OutcomeRuleFail, RuleRecord.RuleName, Pipeline.Binder.TargetName), source: SOURCE_OUTCOME_FAIL));
}
public void WarnRuleInconclusive(string ruleId)
@ -106,7 +106,7 @@ namespace PSRule.Pipeline
if (Writer == null || !Writer.ShouldWriteWarning() || !_InconclusiveWarning)
return;
Writer.WriteWarning(string.Format(PSRuleResources.RuleInconclusive, ruleId, Pipeline.Binder.TargetName));
Writer.WriteWarning(PSRuleResources.RuleInconclusive, ruleId, Pipeline.Binder.TargetName);
}
public void WarnObjectNotProcessed()
@ -114,7 +114,7 @@ namespace PSRule.Pipeline
if (Writer == null || !Writer.ShouldWriteWarning() || !_NotProcessedWarning)
return;
Writer.WriteWarning(string.Format(PSRuleResources.ObjectNotProcessed, Pipeline.Binder.TargetName));
Writer.WriteWarning(PSRuleResources.ObjectNotProcessed, Pipeline.Binder.TargetName);
}
public void WarnRuleNotFound()
@ -335,7 +335,7 @@ namespace PSRule.Pipeline
return string.Concat(
record.ScriptStackTrace,
Environment.NewLine,
string.Format(PSRuleResources.RuleStackTrace, RuleBlock.RuleName, RuleBlock.Extent.File, RuleBlock.Extent.StartLineNumber)
string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.RuleStackTrace, RuleBlock.RuleName, RuleBlock.Extent.File, RuleBlock.Extent.StartLineNumber)
);
}
@ -385,7 +385,7 @@ namespace PSRule.Pipeline
{
_ObjectNumber++;
TargetObject = targetObject;
Pipeline.Binder.Bind(Pipeline.Baseline, targetObject);
Pipeline.Binder.Bind(Pipeline.Baseline, TargetObject);
if (Pipeline.ContentCache.Count > 0)
Pipeline.ContentCache.Clear();
}
@ -395,7 +395,6 @@ namespace PSRule.Pipeline
/// </summary>
public RuleRecord EnterRuleBlock(RuleBlock ruleBlock)
{
Pipeline.Binder.Bind(Pipeline.Baseline, TargetObject);
RuleBlock = ruleBlock;
RuleRecord = new RuleRecord(
ruleId: ruleBlock.RuleId,

Просмотреть файл

@ -119,10 +119,10 @@ namespace PSRule.Pipeline
/// </summary>
private void BindField(FieldMap[] map, bool caseSensitive, PSObject targetObject)
{
var hashtable = new ImmutableHashtable();
if (map == null || map.Length == 0)
return;
var hashtable = new ImmutableHashtable();
for (var i = 0; i < map.Length; i++)
{
if (map[i] == null || map[i].Count == 0)

18
src/PSRule/Resources/PSRuleResources.Designer.cs сгенерированный
Просмотреть файл

@ -78,6 +78,24 @@ namespace PSRule.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Target failed If precondition.
/// </summary>
internal static string DebugTargetIfMismatch {
get {
return ResourceManager.GetString("DebugTargetIfMismatch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Target failed Type precondition.
/// </summary>
internal static string DebugTargetTypeMismatch {
get {
return ResourceManager.GetString("DebugTargetTypeMismatch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to A circular rule dependency was detected. The rule &apos;{0}&apos; depends on &apos;{1}&apos; which also depend on &apos;{0}&apos;..
/// </summary>

Просмотреть файл

@ -124,6 +124,12 @@
<data name="ConstrainedTargetBinding" xml:space="preserve">
<value>Binding functions are not supported in this language mode.</value>
</data>
<data name="DebugTargetIfMismatch" xml:space="preserve">
<value>Target failed If precondition</value>
</data>
<data name="DebugTargetTypeMismatch" xml:space="preserve">
<value>Target failed Type precondition</value>
</data>
<data name="DependencyCircularReference" xml:space="preserve">
<value>A circular rule dependency was detected. The rule '{0}' depends on '{1}' which also depend on '{0}'.</value>
<comment>Occurs when rules interdepend on each other.</comment>

Просмотреть файл

@ -34,7 +34,8 @@ namespace PSRule.Rules
if (field != null && field.Count > 0)
Field = field;
Data = new Hashtable();
// Limit allocations for most scenarios. Runtime calls GetData().
Data = null;
}
/// <summary>
@ -143,5 +144,16 @@ namespace PSRule.Rules
return sb.ToString();
}
/// <summary>
/// Safe call to Data.
/// </summary>
internal Hashtable GetData()
{
if (Data == null)
Data = new Hashtable();
return Data;
}
}
}

Просмотреть файл

@ -286,7 +286,6 @@ namespace PSRule.Runtime
value = null;
var bindingFlags = caseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase;
var fieldInfo = baseType.GetField(fieldName, bindingAttr: bindingFlags | BindingFlags.Instance | BindingFlags.Public);
if (fieldInfo == null)
return false;

Просмотреть файл

@ -32,7 +32,7 @@ namespace PSRule.Runtime
{
get
{
return _Context.RuleRecord.Data;
return _Context.RuleRecord.GetData();
}
}

Просмотреть файл

@ -7,13 +7,76 @@ using System.Management.Automation;
namespace PSRule.Runtime
{
internal static class RuleConditionHelper
{
private readonly static RuleConditionResult Empty = new RuleConditionResult(pass: 0, count: 0, hadErrors: false);
internal static RuleConditionResult Create(IEnumerable<object> value)
{
if (value == null)
return Empty;
var count = 0;
var pass = 0;
var hasErrors = false;
foreach (var v in value)
{
count++;
if (v == null)
continue;
var baseObject = GetBaseObject(v);
if (!(TryAssertResult(baseObject, out bool result) || TryBoolean(baseObject, out result)))
{
RunspaceContext.CurrentThread.ErrorInvaildRuleResult();
hasErrors = true;
}
else if (result)
{
pass++;
}
}
return new RuleConditionResult(pass, count, hasErrors);
}
private static object GetBaseObject(object o)
{
return o is PSObject pso ? pso.BaseObject : o;
}
private static bool TryBoolean(object o, out bool result)
{
result = false;
if (o == null || !(o is bool bresult))
return false;
result = bresult;
return true;
}
private static bool TryAssertResult(object o, out bool result)
{
result = false;
if (o == null || !(o is AssertResult assert))
return false;
result = assert.Result;
// Complete results
if (PipelineContext.CurrentThread.ExecutionScope == ExecutionScope.Condition)
assert.Complete();
return true;
}
}
internal sealed class RuleConditionResult
{
public readonly int Pass;
public readonly int Count;
public readonly bool HadErrors;
private RuleConditionResult(int pass, int count, bool hadErrors)
internal RuleConditionResult(int pass, int count, bool hadErrors)
{
Pass = pass;
Count = count;
@ -29,65 +92,5 @@ namespace PSRule.Runtime
{
return Pass > 0;
}
internal static RuleConditionResult Create(IEnumerable<object> value)
{
if (value == null)
return new RuleConditionResult(pass: 0, count: 0, hadErrors: false);
var count = 0;
var pass = 0;
var hasError = false;
foreach (var v in value)
{
count++;
if (v == null)
continue;
if (!(TryAssertResult(v, out bool result) || TryBoolean(v, out result)))
{
RunspaceContext.CurrentThread.ErrorInvaildRuleResult();
hasError = true;
}
else if (result)
{
pass++;
}
}
return new RuleConditionResult(pass: pass, count: count, hadErrors: hasError);
}
private static bool TryBoolean(object o, out bool result)
{
result = false;
if (o == null)
return false;
var baseObject = o is PSObject pso ? pso.BaseObject : o;
if (!(baseObject is bool bresult))
return false;
result = bresult;
return true;
}
private static bool TryAssertResult(object o, out bool result)
{
result = false;
if (o == null)
return false;
var baseObject = o is PSObject pso ? pso.BaseObject : o;
if (!(baseObject is AssertResult assert))
return false;
result = assert.Result;
// Complete results
if (PipelineContext.CurrentThread.ExecutionScope == ExecutionScope.Condition)
assert.Complete();
return true;
}
}
}

Просмотреть файл

@ -47,11 +47,11 @@ namespace PSRule
{
SetContext();
var assert = GetAssertionHelper();
var actual1 = PSRule.Runtime.RuleConditionResult.Create(new object[] { PSObject.AsPSObject(assert.Create(true, "Test reason")), PSObject.AsPSObject(assert.Create(false, "Test reason")) });
var actual1 = PSRule.Runtime.RuleConditionHelper.Create(new object[] { PSObject.AsPSObject(assert.Create(true, "Test reason")), PSObject.AsPSObject(assert.Create(false, "Test reason")) });
Assert.True(actual1.AnyOf());
Assert.False(actual1.AllOf());
var actual2 = PSRule.Runtime.RuleConditionResult.Create(new object[] { assert.Create(true, "Test reason"), assert.Create(false, "Test reason") });
var actual2 = PSRule.Runtime.RuleConditionHelper.Create(new object[] { assert.Create(true, "Test reason"), assert.Create(false, "Test reason") });
Assert.True(actual2.AnyOf());
Assert.False(actual2.AllOf());
}