Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 10 additions & 2 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,16 @@ priorities.
design-corpus categories are delivered. The shared projector,
compatibility flattener, depth guard, decoded-wrapper cloning, and
executable-corpus DTOs now preserve direct and command-owned regions in
the locked substitution-host-region order. No parser emits a region yet,
and automated execution-region oracle coverage remains in task 7.7.
the locked substitution-host-region order. The PowerShell structural
parser now emits command-argument script blocks as conservative unknown,
incomplete regions, recursively exposes supported body commands, retains
pure output expressions without inventing command occurrences, and fails
atomically on unsupported execution-bearing expressions. Until a receiver
contract proves scope and timing, an unknown region also poisons
subsequent observing cwd, variable, and command-resolution facts so a
continuation cannot reuse stale authorization evidence. Receiver-aware
typed facts and shell-specific state flow remain in tasks 7.5b-7.5e, and
automated execution-region oracle coverage remains in task 7.7.
The PowerShell 7.6.4 receiver and parameter-binding catalog is now
implemented with command-resolution proof as an explicit input. It pins
aliases, supported module qualification, exact and abbreviated/inline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ private static bool NextIsAssignmentOperator(IReadOnlyList<PwshToken> tokens, in
}

var v = tokens[j].Value;
return v is "=" or "+=" or "-=" or "*=" or "/=" or "%=";
return v is "=" or "+=" or "-=" or "*=" or "/=" or "%=" or "??=";
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,8 @@ internal sealed class PwshForEachValueAnalyzer
new(ClauseReferenceComparer.Instance);
private bool _isComplete = true;
private int _remainingLoopAnalysisTransitions = MaxLoopAnalysisTransitions;
private long _executionRegionEffectCount;
private long _nonRegionStateMutationCount;

private PwshForEachValueAnalyzer(
PwshParserOptions options,
Expand Down Expand Up @@ -861,9 +863,15 @@ private PwshFlowResult AnalyzeNode(ShellSyntaxNode node, AnalysisContext input)
GroupSyntax group => AnalyzeGroup(group, input),
ForEachSyntax forEach => AnalyzeForEach(forEach, input),
CommandSubstitutionSyntax substitution => AnalyzeSubstitution(substitution, input),
_ => PwshFlowResult.Both(input.Invalidate(unknownCwd: true)),
_ => AnalyzeUnsupportedNode(input),
};

private PwshFlowResult AnalyzeUnsupportedNode(AnalysisContext input)
{
_nonRegionStateMutationCount++;
return PwshFlowResult.Both(input.Invalidate(unknownCwd: true));
}

private PwshFlowResult AnalyzeBlock(ShellBlockSyntax block, AnalysisContext input)
{
var flow = PwshFlowResult.Both(input);
Expand Down Expand Up @@ -899,7 +907,8 @@ private PwshFlowResult AnalyzeSimple(SimpleCommandSyntax simple, AnalysisContext
var effective = CreateEffectiveArguments(
source.ValueProvenance,
current,
includeUnresolved: isForEachIncomplete);
includeUnresolved: isForEachIncomplete ||
current.CommandResolutionInvalidated);
var mayPromote = current.CanPromote &&
source.HasCompleteValueProvenance &&
simple.Substitutions.Count == 0 &&
Expand All @@ -916,38 +925,64 @@ private PwshFlowResult AnalyzeSimple(SimpleCommandSyntax simple, AnalysisContext
var location = AnalyzeSetLocation(simple, current);
if (location is not null)
{
_nonRegionStateMutationCount++;
if (PwshPersistentStateMutation.TryGetEffect(
simple.Clause,
effective,
out var locationEffectUnknownCwd))
{
var flow = location.Value;
return new PwshFlowResult(
return ApplyExecutionRegionEffect(simple, new PwshFlowResult(
flow.OnSuccess is AnalysisContext success
? success.Invalidate(locationEffectUnknownCwd)
: null,
flow.OnFailure is AnalysisContext failure
? failure.Invalidate(locationEffectUnknownCwd)
: null);
: null));
}

return location.Value;
return ApplyExecutionRegionEffect(simple, location.Value);
}

if (simple.Clause.Verb.IsDynamic)
{
return PwshFlowResult.Both(current.Invalidate(unknownCwd: true));
_nonRegionStateMutationCount++;
return ApplyExecutionRegionEffect(
simple,
PwshFlowResult.Both(current.Invalidate(unknownCwd: true)));
}

if (PwshPersistentStateMutation.TryGetEffect(
simple.Clause,
effective,
out var unknownCwd))
{
return PwshFlowResult.Both(current.Invalidate(unknownCwd));
_nonRegionStateMutationCount++;
return ApplyExecutionRegionEffect(
simple,
PwshFlowResult.Both(current.Invalidate(unknownCwd)));
}

return ApplyExecutionRegionEffect(simple, PwshFlowResult.Both(current));
}

private PwshFlowResult ApplyExecutionRegionEffect(
SimpleCommandSyntax simple,
PwshFlowResult flow)
{
if (simple.ExecutionRegions.Count == 0)
{
return flow;
}

return PwshFlowResult.Both(current);
_executionRegionEffectCount++;
return new PwshFlowResult(
flow.OnSuccess is AnalysisContext success
? success.Invalidate(unknownCwd: true)
: null,
flow.OnFailure is AnalysisContext failure
? failure.Invalidate(unknownCwd: true)
: null);
}

private void RecordFacts(
Expand Down Expand Up @@ -1044,22 +1079,34 @@ private PwshFlowResult AnalyzeList(CommandListSyntax list, AnalysisContext input

private PwshFlowResult AnalyzePipeline(PipelineSyntax pipeline, AnalysisContext input)
{
var stageInput = input;
foreach (var stage in pipeline.Stages)
{
var stageFlow = AnalyzeNode(stage, input);
var executionRegionEffectsBefore = _executionRegionEffectCount;
var nonRegionMutationsBefore = _nonRegionStateMutationCount;
var stageFlow = AnalyzeNode(stage, stageInput);
if (stageFlow.JoinedState is not AnalysisContext stageExit)
{
return new PwshFlowResult(null, null);
}

if (!input.StateEquals(stageExit))
if (!stageInput.StateEquals(stageExit))
{
_isComplete = false;
return new PwshFlowResult(null, null);
var regionCausedTransition =
_executionRegionEffectCount > executionRegionEffectsBefore;
var unrelatedMutationCausedTransition =
_nonRegionStateMutationCount > nonRegionMutationsBefore;
if (!regionCausedTransition || unrelatedMutationCausedTransition)
{
_isComplete = false;
return new PwshFlowResult(null, null);
}

stageInput = stageInput.Invalidate(unknownCwd: true);
}
}

return PwshFlowResult.Both(input);
return PwshFlowResult.Both(stageInput);
}

private PwshFlowResult AnalyzeGroup(GroupSyntax group, AnalysisContext input)
Expand All @@ -1069,11 +1116,15 @@ private PwshFlowResult AnalyzeGroup(GroupSyntax group, AnalysisContext input)
return AnalyzeBlock(group.Body, input);
}

var executionRegionEffectCount = _executionRegionEffectCount;
var nonRegionStateMutationCount = _nonRegionStateMutationCount;
AnalyzeBlock(
group.Body,
input.WithoutBindings().Invalidate(
unknownCwd: false,
invalidateCommandResolution: false));
_executionRegionEffectCount = executionRegionEffectCount;
_nonRegionStateMutationCount = nonRegionStateMutationCount;
return PwshFlowResult.Both(input);
}

Expand All @@ -1086,6 +1137,7 @@ private PwshFlowResult AnalyzeSubstitution(

private PwshFlowResult AnalyzeForEach(ForEachSyntax forEach, AnalysisContext input)
{
_nonRegionStateMutationCount++;
var plan = _planFactory(forEach);
if (plan is null)
{
Expand Down Expand Up @@ -1446,6 +1498,16 @@ private SimpleCommandSyntax RewriteSimple(
facts);
}

var executionRegions = new ExecutionRegionSyntax[simple.ExecutionRegions.Count];
for (var index = 0; index < executionRegions.Length; index++)
{
var region = simple.ExecutionRegions[index];
executionRegions[index] = region with
{
Body = RewriteBlock(region.Body, facts),
};
}

var source = GetFacts(simple);
var clause = RewriteCwdCompatibility(
simple.Clause,
Expand All @@ -1465,6 +1527,7 @@ private SimpleCommandSyntax RewriteSimple(
{
Clause = clause,
Substitutions = substitutions,
ExecutionRegions = executionRegions,
};
}

Expand Down
Loading