/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Globalization;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Management.Automation.Host;
using System.Text;
using System.Reflection;
using System.Management.Automation.Runspaces;
namespace System.Management.Automation.Language
{
#region "AstArgumentPair"
///
/// The types for AstParameterArgumentPair
///
internal enum AstParameterArgumentType
{
AstPair = 0,
Switch = 1,
Fake = 2,
AstArray = 3,
PipeObject = 4
}
///
/// The base class for parameter argument pair
///
internal abstract class AstParameterArgumentPair
{
///
/// The parameter Ast
///
public CommandParameterAst Parameter { get; protected set; }
///
/// The argument type
///
public AstParameterArgumentType ParameterArgumentType { get; protected set; }
///
/// Indicate if the parameter is specified
///
public bool ParameterSpecified { get; protected set; } = false;
///
/// Indicate if the parameter is specified
///
public bool ArgumentSpecified { get; protected set; } = false;
///
/// The parameter name
///
public string ParameterName { get; protected set; }
///
/// The parameter text
///
public string ParameterText { get; protected set; }
///
/// The argument type
///
public Type ArgumentType { get; protected set; }
}
///
/// Represent a parameter argument pair. The argument is a pipeline input object
///
internal sealed class PipeObjectPair : AstParameterArgumentPair
{
internal PipeObjectPair(string parameterName, Type pipeObjType)
{
if (parameterName == null)
throw PSTraceSource.NewArgumentNullException("parameterName");
Parameter = null;
ParameterArgumentType = AstParameterArgumentType.PipeObject;
ParameterSpecified = true;
ArgumentSpecified = true;
ParameterName = parameterName;
ParameterText = parameterName;
ArgumentType = pipeObjType;
}
}
///
/// Represent a parameter argument pair. The argument is an array of ExpressionAst (remaining
/// arguments)
///
internal sealed class AstArrayPair : AstParameterArgumentPair
{
internal AstArrayPair(string parameterName, ICollection arguments)
{
if (parameterName == null)
throw PSTraceSource.NewArgumentNullException("parameterName");
if (arguments == null || arguments.Count == 0)
throw PSTraceSource.NewArgumentNullException("arguments");
Parameter = null;
ParameterArgumentType = AstParameterArgumentType.AstArray;
ParameterSpecified = true;
ArgumentSpecified = true;
ParameterName = parameterName;
ParameterText = parameterName;
ArgumentType = typeof(Array);
Argument = arguments.ToArray();
}
///
/// Get the argument
///
public ExpressionAst[] Argument { get; } = null;
}
///
/// Represent a parameter argument pair. The argument is a fake object.
///
internal sealed class FakePair : AstParameterArgumentPair
{
internal FakePair(CommandParameterAst parameterAst)
{
if (parameterAst == null)
throw PSTraceSource.NewArgumentNullException("parameterAst");
Parameter = parameterAst;
ParameterArgumentType = AstParameterArgumentType.Fake;
ParameterSpecified = true;
ArgumentSpecified = true;
ParameterName = parameterAst.ParameterName;
ParameterText = parameterAst.ParameterName;
ArgumentType = typeof(object);
}
}
///
/// Represent a parameter argument pair. The parameter is a switch parameter.
///
internal sealed class SwitchPair : AstParameterArgumentPair
{
internal SwitchPair(CommandParameterAst parameterAst)
{
if (parameterAst == null)
throw PSTraceSource.NewArgumentNullException("parameterAst");
Parameter = parameterAst;
ParameterArgumentType = AstParameterArgumentType.Switch;
ParameterSpecified = true;
ArgumentSpecified = true;
ParameterName = parameterAst.ParameterName;
ParameterText = parameterAst.ParameterName;
ArgumentType = typeof(bool);
}
///
/// Get the argument
///
public bool Argument
{
get { return true; }
}
}
///
/// Represent a parameter argument pair. It could be a pure argument (no parameter, only argument available);
/// it could be a CommandParameterAst that contains its argument; it also could be a CommandParameterAst with
/// another CommandParameterAst as the argument.
///
internal sealed class AstPair : AstParameterArgumentPair
{
internal AstPair(CommandParameterAst parameterAst)
{
if (parameterAst == null || parameterAst.Argument == null)
throw PSTraceSource.NewArgumentException("parameterAst");
Parameter = parameterAst;
ParameterArgumentType = AstParameterArgumentType.AstPair;
ParameterSpecified = true;
ArgumentSpecified = true;
ParameterName = parameterAst.ParameterName;
ParameterText = "-" + ParameterName + ":";
ArgumentType = parameterAst.Argument.StaticType;
ParameterContainsArgument = true;
Argument = parameterAst.Argument;
}
internal AstPair(CommandParameterAst parameterAst, ExpressionAst argumentAst)
{
if (parameterAst != null && parameterAst.Argument != null)
throw PSTraceSource.NewArgumentException("parameterAst");
if (parameterAst == null && argumentAst == null)
throw PSTraceSource.NewArgumentNullException("argumentAst");
Parameter = parameterAst;
ParameterArgumentType = AstParameterArgumentType.AstPair;
ParameterSpecified = parameterAst != null;
ArgumentSpecified = argumentAst != null;
ParameterName = parameterAst != null ? parameterAst.ParameterName : null;
ParameterText = parameterAst != null ? parameterAst.ParameterName : null;
ArgumentType = argumentAst != null ? argumentAst.StaticType : null;
ParameterContainsArgument = false;
Argument = argumentAst;
}
internal AstPair(CommandParameterAst parameterAst, CommandElementAst argumentAst)
{
if (parameterAst != null && parameterAst.Argument != null)
throw PSTraceSource.NewArgumentException("parameterAst");
if (parameterAst == null || argumentAst == null)
throw PSTraceSource.NewArgumentNullException("argumentAst");
Parameter = parameterAst;
ParameterArgumentType = AstParameterArgumentType.AstPair;
ParameterSpecified = true;
ArgumentSpecified = true;
ParameterName = parameterAst.ParameterName;
ParameterText = parameterAst.ParameterName;
ArgumentType = typeof(string);
ParameterContainsArgument = false;
Argument = argumentAst;
ArgumentIsCommandParameterAst = true;
}
///
/// Indicate if the argument is contained in the CommandParameterAst
///
public bool ParameterContainsArgument { get; } = false;
///
/// Indicate if the argument is of type CommandParameterAst
///
public bool ArgumentIsCommandParameterAst { get; } = false;
///
/// Get the argument
///
public CommandElementAst Argument { get; } = null;
}
#endregion "AstArgumentPair"
///
/// Runs the PowerShell parameter binding algorithm against a CommandAst,
/// returning information about which parameters were bound.
///
///
public static class StaticParameterBinder
{
///
/// Bind a CommandAst to one of PowerShell's built-in commands
///
/// The CommandAst that represents the command invocation.
/// The StaticBindingResult that represents the binding.
public static StaticBindingResult BindCommand(CommandAst commandAst)
{
bool resolve = true;
return BindCommand(commandAst, resolve);
}
///
/// Bind a CommandAst to the specified command
///
/// The CommandAst that represents the command invocation.
/// Boolean to determine whether binding should be syntactic, or should attempt
/// to resolve against an existing command.
///
/// The StaticBindingResult that represents the binding.
public static StaticBindingResult BindCommand(CommandAst commandAst, bool resolve)
{
return BindCommand(commandAst, resolve, null);
}
///
/// Bind a CommandAst to the specified command
///
/// The CommandAst that represents the command invocation.
/// Boolean to determine whether binding should be syntactic, or should attempt
/// to resolve against an existing command.
///
///
/// A string array that represents parameter names of interest. If any of these are specified,
/// then full binding is done.
///
/// The StaticBindingResult that represents the binding.
public static StaticBindingResult BindCommand(CommandAst commandAst, bool resolve, string[] desiredParameters)
{
// If they specified any desired parameters, first quickly check if they are found
if ((desiredParameters != null) && (desiredParameters.Length > 0))
{
bool possiblyHadDesiredParameter = false;
foreach (CommandParameterAst commandParameter in commandAst.CommandElements.OfType())
{
string actualParameterName = commandParameter.ParameterName;
foreach (string actualParameter in desiredParameters)
{
if (actualParameter.StartsWith(actualParameterName, StringComparison.OrdinalIgnoreCase))
{
possiblyHadDesiredParameter = true;
break;
}
}
if (possiblyHadDesiredParameter)
{
break;
}
}
// Quick exit if the desired parameter was not present
if (!possiblyHadDesiredParameter)
{
return null;
}
}
if (!resolve)
{
return new StaticBindingResult(commandAst, null);
}
PseudoBindingInfo pseudoBinding = null;
if (Runspace.DefaultRunspace == null)
{
lock (s_bindCommandlLock)
{
if (s_bindCommandPowerShell == null)
{
// Create a mini runspace by remove the types and formats
InitialSessionState minimalState = InitialSessionState.CreateDefault2();
minimalState.Types.Clear();
minimalState.Formats.Clear();
s_bindCommandPowerShell = PowerShell.Create(minimalState);
}
// Handle static binding from a non-PowerShell / C# application
Runspace.DefaultRunspace = s_bindCommandPowerShell.Runspace;
// Static binding always does argument binding (not argument or parameter completion).
pseudoBinding = new PseudoParameterBinder().DoPseudoParameterBinding(commandAst, null, null, PseudoParameterBinder.BindingType.ArgumentBinding);
Runspace.DefaultRunspace = null;
}
}
else
{
// Static binding always does argument binding (not argument or parameter completion).
pseudoBinding = new PseudoParameterBinder().DoPseudoParameterBinding(commandAst, null, null, PseudoParameterBinder.BindingType.ArgumentBinding);
}
return new StaticBindingResult(commandAst, pseudoBinding);
}
private static Object s_bindCommandlLock = new Object();
private static PowerShell s_bindCommandPowerShell = null;
}
///
/// Represents the results of the PowerShell parameter binding process.
///
public class StaticBindingResult
{
internal StaticBindingResult(CommandAst commandAst, PseudoBindingInfo bindingInfo)
{
BoundParameters = new Dictionary(StringComparer.OrdinalIgnoreCase);
BindingExceptions = new Dictionary(StringComparer.OrdinalIgnoreCase);
if (bindingInfo == null)
{
CreateBindingResultForSyntacticBind(commandAst);
}
else
{
CreateBindingResultForSuccessfulBind(commandAst, bindingInfo);
}
}
private void CreateBindingResultForSuccessfulBind(CommandAst commandAst, PseudoBindingInfo bindingInfo)
{
_bindingInfo = bindingInfo;
// Check if there is exactly one parameter set valid. In that case,
// ValidParameterSetFlags is exactly a power of two. Otherwise,
// add to the binding exceptions.
bool parameterSetSpecified = bindingInfo.ValidParameterSetsFlags != UInt32.MaxValue;
bool remainingParameterSetIncludesDefault =
(bindingInfo.DefaultParameterSetFlag != 0) &&
((bindingInfo.ValidParameterSetsFlags & bindingInfo.DefaultParameterSetFlag) ==
bindingInfo.DefaultParameterSetFlag);
// (x & (x -1 ) == 0) is a bit hack to determine if something is
// exactly a power of two.
bool onlyOneRemainingParameterSet =
(bindingInfo.ValidParameterSetsFlags != 0) &&
(bindingInfo.ValidParameterSetsFlags &
(bindingInfo.ValidParameterSetsFlags - 1)) == 0;
if (parameterSetSpecified &&
(!remainingParameterSetIncludesDefault) &&
(!onlyOneRemainingParameterSet))
{
ParameterBindingException bindingException =
new ParameterBindingException(
ErrorCategory.InvalidArgument,
null,
null,
null,
null,
null,
ParameterBinderStrings.AmbiguousParameterSet,
"AmbiguousParameterSet");
BindingExceptions.Add(commandAst.CommandElements[0].Extent.Text,
new StaticBindingError(commandAst.CommandElements[0], bindingException));
}
// Add error for duplicate parameters
if (bindingInfo.DuplicateParameters != null)
{
foreach (AstParameterArgumentPair duplicateParameter in bindingInfo.DuplicateParameters)
{
AddDuplicateParameterBindingException(duplicateParameter.Parameter);
}
}
// Add error for parameters not found
if (bindingInfo.ParametersNotFound != null)
{
foreach (CommandParameterAst parameterNotFound in bindingInfo.ParametersNotFound)
{
ParameterBindingException bindingException =
new ParameterBindingException(
ErrorCategory.InvalidArgument,
null,
parameterNotFound.ErrorPosition,
parameterNotFound.ParameterName,
null,
null,
ParameterBinderStrings.NamedParameterNotFound,
"NamedParameterNotFound");
BindingExceptions.Add(parameterNotFound.ParameterName, new StaticBindingError(parameterNotFound, bindingException));
}
}
// Add error for ambiguous parameters
if (bindingInfo.AmbiguousParameters != null)
{
foreach (CommandParameterAst ambiguousParameter in bindingInfo.AmbiguousParameters)
{
ParameterBindingException bindingException = bindingInfo.BindingExceptions[ambiguousParameter];
BindingExceptions.Add(ambiguousParameter.ParameterName, new StaticBindingError(ambiguousParameter, bindingException));
}
}
// Add error for unbound positional parameters
if (bindingInfo.UnboundArguments != null)
{
foreach (AstParameterArgumentPair unboundArgument in bindingInfo.UnboundArguments)
{
AstPair argument = unboundArgument as AstPair;
ParameterBindingException bindingException =
new ParameterBindingException(
ErrorCategory.InvalidArgument,
null,
argument.Argument.Extent,
argument.Argument.Extent.Text,
null,
null,
ParameterBinderStrings.PositionalParameterNotFound,
"PositionalParameterNotFound");
BindingExceptions.Add(argument.Argument.Extent.Text, new StaticBindingError(argument.Argument, bindingException));
}
}
// Process the bound parameters
if (bindingInfo.BoundParameters != null)
{
foreach (KeyValuePair item in bindingInfo.BoundParameters)
{
CompiledCommandParameter parameter = item.Value.Parameter;
CommandElementAst value = null;
Object constantValue = null;
// This is a single argument
AstPair argumentAstPair = bindingInfo.BoundArguments[item.Key] as AstPair;
if (argumentAstPair != null)
{
value = argumentAstPair.Argument;
}
// This is a parameter that took an argument, as well as ValueFromRemainingArguments.
// Merge the arguments into a single fake argument.
AstArrayPair argumentAstArrayPair = bindingInfo.BoundArguments[item.Key] as AstArrayPair;
if (argumentAstArrayPair != null)
{
List arguments = new List();
foreach (ExpressionAst expression in argumentAstArrayPair.Argument)
{
ArrayLiteralAst expressionArray = expression as ArrayLiteralAst;
if (expressionArray != null)
{
foreach (ExpressionAst newExpression in expressionArray.Elements)
{
arguments.Add((ExpressionAst)newExpression.Copy());
}
}
else
{
arguments.Add((ExpressionAst)expression.Copy());
}
}
// Define the virtual extent and virtual ArrayLiteral.
IScriptExtent fakeExtent = arguments[0].Extent;
ArrayLiteralAst fakeArguments = new ArrayLiteralAst(fakeExtent, arguments);
value = fakeArguments;
}
// Special handling of switch parameters
if (parameter.Type == typeof(SwitchParameter))
{
if ((value != null) &&
(String.Equals("$false", value.Extent.Text, StringComparison.OrdinalIgnoreCase)))
{
continue;
}
constantValue = true;
}
// We got a parameter and a value
if ((value != null) || (constantValue != null))
{
BoundParameters.Add(item.Key, new ParameterBindingResult(parameter, value, constantValue));
}
else
{
bool takesValueFromPipeline = false;
foreach (ParameterSetSpecificMetadata parameterSet in parameter.GetMatchingParameterSetData(bindingInfo.ValidParameterSetsFlags))
{
if (parameterSet.ValueFromPipeline)
{
takesValueFromPipeline = true;
break;
}
}
if (!takesValueFromPipeline)
{
// We have a parameter with no value that isn't a switch parameter, or input parameter
ParameterBindingException bindingException =
new ParameterBindingException(
ErrorCategory.InvalidArgument,
null,
commandAst.CommandElements[0].Extent,
parameter.Name,
parameter.Type,
null,
ParameterBinderStrings.MissingArgument,
"MissingArgument");
BindingExceptions.Add(commandAst.CommandElements[0].Extent.Text,
new StaticBindingError(commandAst.CommandElements[0], bindingException));
}
}
}
}
}
private void AddDuplicateParameterBindingException(CommandParameterAst duplicateParameter)
{
if (duplicateParameter == null)
{
return;
}
ParameterBindingException bindingException =
new ParameterBindingException(
ErrorCategory.InvalidArgument,
null,
duplicateParameter.ErrorPosition,
duplicateParameter.ParameterName,
null,
null,
ParameterBinderStrings.ParameterAlreadyBound,
"ParameterAlreadyBound");
// if the duplicated Parameter Name appears more than twice, we will ignore as we already have similar bindingException.
if (!BindingExceptions.ContainsKey(duplicateParameter.ParameterName))
{
BindingExceptions.Add(duplicateParameter.ParameterName, new StaticBindingError(duplicateParameter, bindingException));
}
}
private PseudoBindingInfo _bindingInfo = null;
private void CreateBindingResultForSyntacticBind(CommandAst commandAst)
{
bool foundCommand = false;
CommandParameterAst currentParameter = null;
int position = 0;
ParameterBindingResult bindingResult = new ParameterBindingResult();
foreach (CommandElementAst commandElement in commandAst.CommandElements)
{
// Skip the command name
if (!foundCommand)
{
foundCommand = true;
continue;
}
CommandParameterAst parameter = commandElement as CommandParameterAst;
if (parameter != null)
{
if (currentParameter != null)
{
// Assume it was a switch
AddSwitch(currentParameter.ParameterName, bindingResult);
ResetCurrentParameter(ref currentParameter, ref bindingResult);
}
// If this is an actual parameter, get its name.
string parameterName = parameter.ParameterName;
bindingResult.Value = parameter;
// If it's a parameter with argument, add them both to the dictionary
if (parameter.Argument != null)
{
bindingResult.Value = parameter.Argument;
AddBoundParameter(parameter, parameterName, bindingResult);
ResetCurrentParameter(ref currentParameter, ref bindingResult);
}
// Otherwise, it's just a parameter and the argument is to follow.
else
{
// Store our current parameter
currentParameter = parameter;
}
}
else
{
// This isn't a parameter, it's a value for the previous parameter
if (currentParameter != null)
{
bindingResult.Value = commandElement;
AddBoundParameter(currentParameter, currentParameter.ParameterName, bindingResult);
}
else
{
// Assume positional
bindingResult.Value = commandElement;
AddBoundParameter(null, position.ToString(CultureInfo.InvariantCulture), bindingResult);
position++;
}
ResetCurrentParameter(ref currentParameter, ref bindingResult);
}
}
// Catch any hanging parameters at the end of the command
if (currentParameter != null)
{
// Assume it was a switch
AddSwitch(currentParameter.ParameterName, bindingResult);
}
}
private void AddBoundParameter(CommandParameterAst parameter, string parameterName, ParameterBindingResult bindingResult)
{
if (BoundParameters.ContainsKey(parameterName))
{
AddDuplicateParameterBindingException(parameter);
}
else
{
BoundParameters.Add(parameterName, bindingResult);
}
}
private static void ResetCurrentParameter(ref CommandParameterAst currentParameter, ref ParameterBindingResult bindingResult)
{
currentParameter = null;
bindingResult = new ParameterBindingResult();
}
private void AddSwitch(string currentParameter, ParameterBindingResult bindingResult)
{
bindingResult.ConstantValue = true;
AddBoundParameter(null, currentParameter, bindingResult);
}
///
///
///
public Dictionary BoundParameters { get; }
///
///
///
public Dictionary BindingExceptions { get; }
}
///
/// Represents the binding of a parameter to its argument
///
public class ParameterBindingResult
{
internal ParameterBindingResult(CompiledCommandParameter parameter, CommandElementAst value, Object constantValue)
{
this.Parameter = new ParameterMetadata(parameter);
this.Value = value;
this.ConstantValue = constantValue;
}
internal ParameterBindingResult()
{
}
///
///
///
public ParameterMetadata Parameter { get; internal set; }
///
///
///
public Object ConstantValue
{
get { return _constantValue; }
internal set
{
if (value != null)
{
_constantValue = value;
}
}
}
private object _constantValue;
///
///
///
public CommandElementAst Value
{
get { return _value; }
internal set
{
_value = value;
ConstantExpressionAst constantValueAst = value as ConstantExpressionAst;
if (constantValueAst != null)
{
this.ConstantValue = constantValueAst.Value;
}
}
}
private CommandElementAst _value;
}
///
/// Represents the exception generated by the static parameter binding process
///
public class StaticBindingError
{
///
/// Creates a StaticBindingException
///
/// The element associated with the exception
/// The parameter binding exception that got raised
internal StaticBindingError(CommandElementAst commandElement, ParameterBindingException exception)
{
this.CommandElement = commandElement;
this.BindingException = exception;
}
///
/// The command element associated with the exception.
///
public CommandElementAst CommandElement { get; private set; }
///
/// The ParameterBindingException that this command element caused.
///
public ParameterBindingException BindingException { get; private set; }
}
#region "PseudoBindingInfo"
internal enum PseudoBindingInfoType
{
PseudoBindingFail = 0,
PseudoBindingSucceed = 1,
}
internal sealed class PseudoBindingInfo
{
///
/// The pseudo binding succeeded
///
///
///
///
///
///
///
///
///
///
///
///
///
///
internal PseudoBindingInfo(
CommandInfo commandInfo,
uint validParameterSetsFlags,
uint defaultParameterSetFalg,
Dictionary boundParameters,
List unboundParameters,
Dictionary boundArguments,
Collection boundPositionalParameter,
Collection allParsedArguments,
Collection parametersNotFound,
Collection ambiguousParameters,
Dictionary bindingExceptions,
Collection duplicateParameters,
Collection unboundArguments)
{
CommandInfo = commandInfo;
InfoType = PseudoBindingInfoType.PseudoBindingSucceed;
ValidParameterSetsFlags = validParameterSetsFlags;
DefaultParameterSetFlag = defaultParameterSetFalg;
BoundParameters = boundParameters;
UnboundParameters = unboundParameters;
BoundArguments = boundArguments;
BoundPositionalParameter = boundPositionalParameter;
AllParsedArguments = allParsedArguments;
ParametersNotFound = parametersNotFound;
AmbiguousParameters = ambiguousParameters;
BindingExceptions = bindingExceptions;
DuplicateParameters = duplicateParameters;
UnboundArguments = unboundArguments;
}
///
/// The pseudo binding failed with parameter set confliction
///
///
///
///
///
internal PseudoBindingInfo(
CommandInfo commandInfo,
uint defaultParameterSetFlag,
Collection allParsedArguments,
List unboundParameters)
{
CommandInfo = commandInfo;
InfoType = PseudoBindingInfoType.PseudoBindingFail;
DefaultParameterSetFlag = defaultParameterSetFlag;
AllParsedArguments = allParsedArguments;
UnboundParameters = unboundParameters;
}
internal string CommandName
{
get { return CommandInfo.Name; }
}
internal CommandInfo CommandInfo { get; }
internal PseudoBindingInfoType InfoType { get; }
internal uint ValidParameterSetsFlags { get; }
internal uint DefaultParameterSetFlag { get; }
internal Dictionary BoundParameters { get; }
internal List UnboundParameters { get; }
internal Dictionary BoundArguments { get; }
internal Collection UnboundArguments { get; }
internal Collection BoundPositionalParameter { get; }
internal Collection AllParsedArguments { get; }
internal Collection ParametersNotFound { get; }
internal Collection AmbiguousParameters { get; }
internal Dictionary BindingExceptions { get; }
internal Collection DuplicateParameters { get; }
}
#endregion "PseudoBindingInfo"
internal class PseudoParameterBinder
{
/*
///
/// Get the parameter binding metadata
///
///
///
public Dictionary GetPseudoParameterBinding(out Collection possibleParameterSets)
{
ExecutionContext contextFromTls =
System.Management.Automation.Runspaces.LocalPipeline.GetExecutionContextFromTLS();
return GetPseudoParameterBinding(out possibleParameterSets, contextFromTls, null);
}
*/
internal enum BindingType
{
///
/// Caller is binding a parameter argument
///
ArgumentBinding = 0,
///
/// Caller is performing completion on a parameter argument
///
ArgumentCompletion,
///
/// Caller is performing completion on a parameter name
///
ParameterCompletion
}
///
/// Get the parameter binding metadata
///
///
/// Indicate the type of the piped-in argument
/// The CommandParameterAst the cursor is pointing at
/// Indicates whether pseudo binding is for argument binding, argument completion, or parameter completion.
/// PseudoBindingInfo
internal PseudoBindingInfo DoPseudoParameterBinding(CommandAst command, Type pipeArgumentType, CommandParameterAst paramAstAtCursor, BindingType bindingType)
{
if (command == null)
{
throw PSTraceSource.NewArgumentNullException("command");
}
// initialize/reset the private members
InitializeMembers();
_commandAst = command;
_commandElements = command.CommandElements;
Collection unboundArguments = new Collection();
// analyze the command and reparse the arguments
{
ExecutionContext executionContext = LocalPipeline.GetExecutionContextFromTLS();
if (executionContext != null)
{
// WinBlue: 324316. This limits the interaction of pseudoparameterbinder with the actual host.
SetTemporaryDefaultHost(executionContext);
PSLanguageMode? previousLanguageMode = null;
try
{
// Tab expansion is called from a trusted function - we should apply ConstrainedLanguage if necessary.
if (ExecutionContext.HasEverUsedConstrainedLanguage)
{
previousLanguageMode = executionContext.LanguageMode;
executionContext.LanguageMode = PSLanguageMode.ConstrainedLanguage;
}
_bindingEffective = PrepareCommandElements(executionContext);
}
finally
{
if (previousLanguageMode.HasValue)
{
executionContext.LanguageMode = previousLanguageMode.Value;
}
RestoreHost(executionContext);
}
}
}
if (_bindingEffective && (_isPipelineInputExpected || pipeArgumentType != null))
{
_pipelineInputType = pipeArgumentType;
}
_bindingEffective = ParseParameterArguments(paramAstAtCursor);
if (_bindingEffective)
{
// named binding
unboundArguments = BindNamedParameters();
_bindingEffective = _currentParameterSetFlag != 0;
// positional binding
unboundArguments = BindPositionalParameter(
unboundArguments,
_currentParameterSetFlag,
_defaultParameterSetFlag,
bindingType);
// VFRA/pipeline binding if the given command is a binary cmdlet or a script cmdlet
if (!_function)
{
unboundArguments = BindRemainingParameters(unboundArguments);
BindPipelineParameters();
}
// Update available parameter sets based on bound arguments
// (x & (x -1 ) == 0) is a bit hack to determine if something is
// exactly a power of two.
bool parameterSetSpecified = (_currentParameterSetFlag != 0) &&
(_currentParameterSetFlag != UInt32.MaxValue);
bool onlyOneRemainingParameterSet = (_currentParameterSetFlag != 0) &&
(_currentParameterSetFlag & (_currentParameterSetFlag - 1)) == 0;
if ((bindingType != BindingType.ParameterCompletion) && parameterSetSpecified && (!onlyOneRemainingParameterSet))
{
CmdletParameterBinderController.ResolveParameterSetAmbiguityBasedOnMandatoryParameters(
_boundParameters,
_unboundParameters,
null,
ref _currentParameterSetFlag,
null);
}
}
// Binding failed
if (!_bindingEffective)
{
// The command is not a cmdlet, not a script cmdlet, and not a function
if (_bindableParameters == null)
return null;
// get all bindable parameters
_unboundParameters.Clear();
_unboundParameters.AddRange(_bindableParameters.BindableParameters.Values);
return new PseudoBindingInfo(
_commandInfo,
_defaultParameterSetFlag,
_arguments,
_unboundParameters);
}
return new PseudoBindingInfo(
_commandInfo,
_currentParameterSetFlag,
_defaultParameterSetFlag,
_boundParameters,
_unboundParameters,
_boundArguments,
_boundPositionalParameter,
_arguments,
_parametersNotFound,
_ambiguousParameters,
_bindingExceptions,
_duplicateParameters,
unboundArguments
);
}
///
/// Sets a temporary default host on the ExecutionContext
///
/// ExecutionContext
private void SetTemporaryDefaultHost(ExecutionContext executionContext)
{
if (executionContext.EngineHostInterface.IsHostRefSet)
{
// A temporary host is already set so we need to track and restore here, because
// setting the host again will overwrite the current one.
_restoreHost = executionContext.EngineHostInterface.ExternalHost;
// Revert host back to its original state.
executionContext.EngineHostInterface.RevertHostRef();
}
// Temporarily set host to default.
executionContext.EngineHostInterface.SetHostRef(new Microsoft.PowerShell.DefaultHost(
CultureInfo.CurrentCulture,
CultureInfo.CurrentUICulture));
}
///
/// Restores original ExceutionContext host state.
///
/// ExecutionContext
private void RestoreHost(ExecutionContext executionContext)
{
// Remove temporary host and revert to original.
executionContext.EngineHostInterface.RevertHostRef();
// Re-apply saved host if any.
if (_restoreHost != null)
{
executionContext.EngineHostInterface.SetHostRef(_restoreHost);
_restoreHost = null;
}
}
// Host to restore.
private PSHost _restoreHost;
// command ast related states
private CommandAst _commandAst;
private ReadOnlyCollection _commandElements;
// binding realted states
private bool _function = false;
private string _commandName = null;
private CommandInfo _commandInfo = null;
private uint _currentParameterSetFlag = uint.MaxValue;
private uint _defaultParameterSetFlag = 0;
private MergedCommandParameterMetadata _bindableParameters = null;
private Dictionary _boundParameters;
private Dictionary _boundArguments;
private Collection _arguments;
private Collection _boundPositionalParameter;
private List _unboundParameters;
// tab expansion related states
private Type _pipelineInputType = null;
private bool _bindingEffective = true;
private bool _isPipelineInputExpected = false;
private Collection _parametersNotFound;
private Collection _ambiguousParameters;
private Collection _duplicateParameters;
private Dictionary _bindingExceptions;
// A corresponding list is also kept in WorkflowJobConverter.cs.
private List _ignoredWorkflowParameters = null;
///
/// Initialize collection/dictionary members when it's necessary
///
private void InitializeMembers()
{
List supportedCommonParameters = new List() { "Verbose", "Debug", "ErrorAction", "WarningAction", "InformationAction" };
_ignoredWorkflowParameters = new List(Cmdlet.CommonParameters.Concat(Cmdlet.OptionalCommonParameters));
_ignoredWorkflowParameters.RemoveAll(item => supportedCommonParameters.Contains(item, StringComparer.OrdinalIgnoreCase));
// Initializing binding realted members
_function = false;
_commandName = null;
_currentParameterSetFlag = uint.MaxValue;
_defaultParameterSetFlag = 0;
_bindableParameters = null;
// reuse the collections/dictionaries
_arguments = _arguments ?? new Collection();
_boundParameters = _boundParameters ?? new Dictionary(StringComparer.OrdinalIgnoreCase);
_boundArguments = _boundArguments ?? new Dictionary(StringComparer.OrdinalIgnoreCase);
_unboundParameters = _unboundParameters ?? new List();
_boundPositionalParameter = _boundPositionalParameter ?? new Collection();
_bindingExceptions = _bindingExceptions ?? new Dictionary();
_arguments.Clear();
_boundParameters.Clear();
_unboundParameters.Clear();
_boundArguments.Clear();
_boundPositionalParameter.Clear();
_bindingExceptions.Clear();
// Initializing tab expansion related members
_pipelineInputType = null;
_bindingEffective = true;
_isPipelineInputExpected = false;
// reuse the collections
_parametersNotFound = _parametersNotFound ?? new Collection();
_ambiguousParameters = _ambiguousParameters ?? new Collection();
_duplicateParameters = _duplicateParameters ?? new Collection();
_parametersNotFound.Clear();
_ambiguousParameters.Clear();
_duplicateParameters.Clear();
}
private bool PrepareCommandElements(ExecutionContext context)
{
int commandIndex = 0;
bool dotSource = _commandAst.InvocationOperator == TokenKind.Dot;
CommandProcessorBase processor = null;
string commandName = null;
bool psuedoWorkflowCommand = false;
try
{
processor = PrepareFromAst(context, out commandName) ?? context.CreateCommand(commandName, dotSource);
}
catch (RuntimeException rte)
{
// Failed to create the CommandProcessor;
CommandProcessorBase.CheckForSevereException(rte);
if (_commandAst.IsInWorkflow() &&
commandName != null &&
CompletionCompleters.PseudoWorkflowCommands.Contains(commandName, StringComparer.OrdinalIgnoreCase))
{
psuedoWorkflowCommand = true;
}
else
{
return false;
}
}
var commandProcessor = processor as CommandProcessor;
var scriptProcessor = processor as ScriptCommandProcessorBase;
bool implementsDynamicParameters = commandProcessor != null &&
commandProcessor.CommandInfo.ImplementsDynamicParameters;
var argumentsToGetDynamicParameters = implementsDynamicParameters
? new List