X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
abaed2b
Initial feature work
rjmholt May 10, 2019
7357091
More tests
Jun 4, 2019
0067fef
Turn operators into experimental feature
Jun 5, 2019
d7f3786
Add control flow statement possibility
Jun 5, 2019
8ff4a2d
Deduplicate rule logic, fix tests
Jun 7, 2019
fcabd6a
Add experimental feature test setting
Jun 10, 2019
7c6bc3c
Fix assignment grammar
Jun 19, 2019
f0c690c
Use experimental test mechanisms
Jun 19, 2019
1edd8e3
Some simplification of compile logic
Jul 5, 2019
eaad264
Use new assignment implementation
Jul 15, 2019
10b5860
Change name to PipelineChainOperators
Jul 16, 2019
01e48ea
Remove experimental feature implementation to make tests work
Jul 16, 2019
eeb8592
Remove redundant rule logic
Jul 16, 2019
42f9337
Add output type tests
Jul 16, 2019
afdd43e
Fix array assignment
rjmholt Jul 17, 2019
e9d5590
Remove char in compiler;
rjmholt Jul 17, 2019
8d8c058
Correct throw tests
Jul 17, 2019
ec1ad46
Fix bad error handling in parser
Jul 17, 2019
1258133
Add reason to tests
Jul 17, 2019
e6f59cd
Add assignment flattening test
Jul 17, 2019
f8e82b6
Remove superfluous enum
Jul 18, 2019
476f48e
Reinstate experimental feature
Aug 8, 2019
b2590e5
Style
Aug 8, 2019
c50561e
Change AST inheritance chain
rjmholt Sep 23, 2019
d4a393a
Revert to simple pipeline chains - tests broken
rjmholt Sep 26, 2019
a428706
Fix AST and compiler behaviour
Oct 15, 2019
b1635d6
Add finally tests
Oct 15, 2019
b3d7ea6
Address @daxian-dbw's feedback
Oct 15, 2019
3159e6d
Change AstVisitor implementation
Oct 15, 2019
f225b6e
Remove inapplicable comment
Oct 15, 2019
f0b4322
Remove redundant variable
Oct 16, 2019
fb4c92c
Revert accidental addition of debugging
Oct 16, 2019
9836b23
Fix parse error handling, improve test
Oct 16, 2019
a6300f0
Address a codefactor issue
Oct 16, 2019
94957f3
Put expfeat test metadata back in
Oct 16, 2019
4f27ae2
Fix comment
Oct 17, 2019
512921e
Fix brace style from CodeFactor
Oct 17, 2019
32f7768
Change CIMInstance tests to -Skip
Oct 17, 2019
3e374f9
Move to Pending on request of @adityapatwardhan
Oct 17, 2019
0b48a8a
Revert "Move to Pending on request of @adityapatwardhan"
Oct 17, 2019
430555f
Move back to pending on @JamesWTruher's advice
Oct 17, 2019
b57b374
Fix other pending defaults
Oct 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion build.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ function Start-PSBuild {
[switch]$NoPSModuleRestore,
[switch]$CI,

# Skips the step where the pwsh that's been built is used to create a configuration
# Useful when changing parsing/compilation, since bugs there can mean we can't get past this step
[switch]$SkipExperimentalFeatureGeneration,

# this switch will re-build only System.Management.Automation.dll
# it's useful for development, to do a quick changes in the engine
[switch]$SMAOnly,
Expand Down Expand Up @@ -499,8 +503,13 @@ Fix steps:
$config = @{ "Microsoft.PowerShell:ExecutionPolicy" = "RemoteSigned" }
}

# When building preview, we want the configuration to enable all experiemental features by default
# ARM is cross compiled, so we can't run pwsh to enumerate Experimental Features
if ((Test-IsPreview $psVersion) -and -not $Runtime.Contains("arm") -and -not ($Runtime -like 'fxdependent*')) {
if (-not $SkipExperimentalFeatureGeneration -and
(Test-IsPreview $psVersion) -and
-not $Runtime.Contains("arm") -and
-not ($Runtime -like 'fxdependent*')) {

$json = & $publishPath\pwsh -noprofile -command {
$expFeatures = [System.Collections.Generic.List[string]]::new()
Get-ExperimentalFeature | ForEach-Object { $expFeatures.Add($_.Name) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6966,16 +6966,15 @@ internal static bool TrySafeEval(ExpressionAst ast, ExecutionContext executionCo

public object VisitUsingStatement(UsingStatementAst usingStatementAst) { return false; }

public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordStatementAst) { return false; }

public object VisitPipelineChain(PipelineChainAst pipelineChainAst) { return false; }

public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
{
return configurationDefinitionAst.Body.Accept(this);
}

public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordStatementAst)
{
return false;
}

public object VisitStatementBlock(StatementBlockAst statementBlockAst)
{
if (statementBlockAst.Traps != null) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ static ExperimentalFeature()
description: "Print notification message when new releases are available"),
new ExperimentalFeature(
name: "PSCoalescingOperators",
description: "Support the null coalescing operator and null coalescing assignment operator in PowerShell language")
description: "Support the null coalescing operator and null coalescing assignment operator in PowerShell language"),
new ExperimentalFeature(
name: "PSBashCommandOperators",
description: "Allow use of && and || as operators between pipeline invocations"),
};
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ public interface ICustomAstVisitor2 : ICustomAstVisitor

/// <summary/>
object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst) => DefaultVisit(ternaryExpressionAst);

/// <summary/>
object VisitPipelineChain(PipelineChainAst statementChainAst) => DefaultVisit(statementChainAst);
}

#if DEBUG
Expand Down Expand Up @@ -318,6 +321,8 @@ internal AstVisitAction CheckParent(Ast ast)
public override AstVisitAction VisitDynamicKeywordStatement(DynamicKeywordStatementAst ast) { return CheckParent(ast); }

public override AstVisitAction VisitTernaryExpression(TernaryExpressionAst ast) => CheckParent(ast);

public override AstVisitAction VisitPipelineChain(PipelineChainAst ast) => CheckParent(ast);
}

/// <summary>
Expand Down Expand Up @@ -552,6 +557,8 @@ protected AstVisitAction CheckScriptBlock(Ast ast)
public override AstVisitAction VisitDynamicKeywordStatement(DynamicKeywordStatementAst ast) { return Check(ast); }

public override AstVisitAction VisitTernaryExpression(TernaryExpressionAst ast) { return Check(ast); }

public override AstVisitAction VisitPipelineChain(PipelineChainAst ast) { return Check(ast); }
}

/// <summary>
Expand Down Expand Up @@ -690,5 +697,7 @@ public abstract class DefaultCustomAstVisitor2 : DefaultCustomAstVisitor, ICusto
public virtual object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return null; }
/// <summary/>
public virtual object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst) { return null; }
/// <summary/>
public virtual object VisitPipelineChain(PipelineChainAst statementChainAst) { return null; }
}
}
168 changes: 165 additions & 3 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ internal static class ExpressionCache
internal static readonly Expression InvariantCulture = Expression.Constant(CultureInfo.InvariantCulture);
internal static readonly Expression OrdinalIgnoreCaseComparer = Expression.Constant(StringComparer.OrdinalIgnoreCase, typeof(StringComparer));
internal static readonly Expression CatchAllType = Expression.Constant(typeof(ExceptionHandlingOps.CatchAll), typeof(Type));
// Empty expression is used at the end of blocks to give them the void expression result
internal static readonly Expression Empty = Expression.Empty();
internal static Expression GetExecutionContextFromTLS =
Expression.Call(CachedReflectionInfo.LocalPipeline_GetExecutionContextFromTLS);
Expand Down Expand Up @@ -648,6 +649,8 @@ internal class Compiler : ICustomAstVisitor2
internal static readonly ParameterExpression _executionContextParameter;
internal static readonly ParameterExpression _functionContext;
internal static readonly ParameterExpression _returnPipe;
private static readonly Expression s_notDollarQuestion;
private static readonly Expression s_getDollarQuestion;
private static readonly Expression s_setDollarQuestionToTrue;
private static readonly Expression s_callCheckForInterrupts;
private static readonly Expression s_getCurrentPipe;
Expand All @@ -667,8 +670,12 @@ static Compiler()
_functionContext = Expression.Parameter(typeof(FunctionContext), "funcContext");
_executionContextParameter = Expression.Variable(typeof(ExecutionContext), "context");

s_getDollarQuestion = Expression.Property(_executionContextParameter, CachedReflectionInfo.ExecutionContext_QuestionMarkVariableValue);

s_notDollarQuestion = Expression.Not(s_getDollarQuestion);

s_setDollarQuestionToTrue = Expression.Assign(
Expression.Property(_executionContextParameter, CachedReflectionInfo.ExecutionContext_QuestionMarkVariableValue),
s_getDollarQuestion,
ExpressionCache.TrueConstant);

s_callCheckForInterrupts = Expression.Call(CachedReflectionInfo.PipelineOps_CheckForInterrupts,
Expand Down Expand Up @@ -2903,8 +2910,9 @@ private void CompileStatementListWithTraps(ReadOnlyCollection<StatementAst> stat
Compiler._functionContext, exception);
var catchAll = Expression.Catch(
exception,
Expression.Block(callCheckActionPreference,
Expression.Goto(dispatchNextStatementTarget)));
Expression.Block(
callCheckActionPreference,
Expression.Goto(dispatchNextStatementTarget)));

var expr = Expression.TryCatch(Expression.Block(tryBodyExprs),
new CatchBlock[] { s_catchFlowControl, catchAll });
Expand Down Expand Up @@ -3115,6 +3123,160 @@ private Expression CompileAssignment(
return Expression.Block(exprs);
}

public object VisitPipelineChain(PipelineChainAst pipelineChainAst)
{
// If the statement chain is backgrounded,
// we defer that to the background operation call
if (pipelineChainAst.Background)
{
return Expression.Call(
CachedReflectionInfo.PipelineOps_InvokePipelineInBackground,
Expression.Constant(pipelineChainAst),
_functionContext);
}

// We want to generate code like:
//
// dispatchIndex = 0;
// DispatchNextStatementTarget:
// try {
// switch (dispatchIndex) {
// case 0: goto L0;
// case 1: goto L1;
// case 2: goto L2;
// }
// L0: dispatchIndex = 1;
// pipeline1;
// L1: dispatchIndex = 2;
// if ($?) pipeline2;
// L2: dispatchIndex = 3;
// if ($?) pipeline3;
// ...
// } catch (FlowControlException) {
// throw;
// } catch (Exception e) {
// ExceptionHandlingOps.CheckActionPreference(functionContext, e);
// goto DispatchNextStatementTarget;
// }
// LN:
//
// Note that we deliberately do not push trap handlers
// so that those can be handled by the enclosing statement block instead.

var exprs = new List<Expression>();

// A pipeline chain is left-hand-side deep,
// so to compile from left to right, we need to start from the leaf
// and roll back up to the top, being the right-most element in the chain
PipelineChainAst currentChain = pipelineChainAst;
while (currentChain.LhsPipelineChain is PipelineChainAst lhsPipelineChain)
{
currentChain = lhsPipelineChain;
}

// int chainIndex = 0;
ParameterExpression dispatchIndex = Expression.Variable(typeof(int), nameof(dispatchIndex));
var temps = new ParameterExpression[] { dispatchIndex };
exprs.Add(Expression.Assign(dispatchIndex, ExpressionCache.Constant(0)));

// DispatchNextTargetStatement:
LabelTarget dispatchNextStatementTargetLabel = Expression.Label();
exprs.Add(Expression.Label(dispatchNextStatementTargetLabel));

// try statement body
var switchCases = new List<SwitchCase>();
var dispatchTargets = new List<LabelTarget>();
var tryBodyExprs = new List<Expression>()
{
null, // Add a slot for the inital switch/case that we'll come back to
};

// L0: dispatchIndex = 1; pipeline1
LabelTarget label0 = Expression.Label();
dispatchTargets.Add(label0);
switchCases.Add(
Expression.SwitchCase(
Expression.Goto(label0),
ExpressionCache.Constant(0)));
tryBodyExprs.Add(Expression.Label(label0));
tryBodyExprs.Add(Expression.Assign(dispatchIndex, ExpressionCache.Constant(1)));
tryBodyExprs.Add(Compile(currentChain.LhsPipelineChain));

// Remainder of try statement body
// L1: dispatchIndex = 2; if ($?) pipeline2;
// ...
int chainIndex = 1;
do
{
// Record label and switch case for later use
LabelTarget currentLabel = Expression.Label();
dispatchTargets.Add(currentLabel);
switchCases.Add(
Expression.SwitchCase(
Expression.Goto(currentLabel),
ExpressionCache.Constant(chainIndex)));

// Add label and dispatchIndex for current pipeline
tryBodyExprs.Add(Expression.Label(currentLabel));
tryBodyExprs.Add(
Expression.Assign(
dispatchIndex,
ExpressionCache.Constant(chainIndex + 1)));

// Increment chain index for next iteration
chainIndex++;

Diagnostics.Assert(
currentChain.Operator == TokenKind.AndAnd || currentChain.Operator == TokenKind.OrOr,
"Chain operators must be either && or ||");

Expression dollarQuestionCheck = currentChain.Operator == TokenKind.AndAnd
? s_getDollarQuestion
: s_notDollarQuestion;

tryBodyExprs.Add(Expression.IfThen(dollarQuestionCheck, Compile(currentChain.RhsPipeline)));

currentChain = currentChain.Parent as PipelineChainAst;
}
while (currentChain != null);

// Add empty expression to make the block value void
tryBodyExprs.Add(ExpressionCache.Empty);

// Create the final label that follows the entire try/catch
LabelTarget afterLabel = Expression.Label();
switchCases.Add(
Expression.SwitchCase(
Expression.Goto(afterLabel),
ExpressionCache.Constant(chainIndex)));

// Now set the switch/case that belongs at the top
tryBodyExprs[0] = Expression.Switch(dispatchIndex, switchCases.ToArray());

// Create the catch block for flow control and action preference
ParameterExpression exception = Expression.Variable(typeof(Exception), nameof(exception));
MethodCallExpression callCheckActionPreference = Expression.Call(
CachedReflectionInfo.ExceptionHandlingOps_CheckActionPreference,
Compiler._functionContext,
exception);
CatchBlock catchAll = Expression.Catch(
exception,
Expression.Block(
callCheckActionPreference,
Expression.Goto(dispatchNextStatementTargetLabel)));

TryExpression expr = Expression.TryCatch(
Expression.Block(tryBodyExprs),
new CatchBlock[] { s_catchFlowControl, catchAll });

exprs.Add(expr);
exprs.Add(Expression.Label(afterLabel));

BlockExpression fullyExpandedBlock = Expression.Block(typeof(void), temps, exprs);

return fullyExpandedBlock;
}

public object VisitPipeline(PipelineAst pipelineAst)
{
var temps = new List<ParameterExpression>();
Expand Down
Loading
X Tutup