MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Management.Automation.Internal;7 8namespace System.Management.Automation9{10 /// <summary>11 /// This is the interface between the ScriptCommandProcessor and the12 /// parameter binders required to bind parameters to a shell function.13 /// </summary>14 internal class ScriptParameterBinderController : ParameterBinderController15 {16 #region ctor17 18 /// <summary>19 /// Initializes the cmdlet parameter binder controller for20 /// the specified cmdlet and engine context.21 /// </summary>22 /// <param name="script">23 /// The script that contains the parameter metadata.24 /// </param>25 /// <param name="invocationInfo">26 /// The invocation information about the code being run.27 /// </param>28 /// <param name="context">29 /// The engine context the cmdlet is run in.30 /// </param>31 /// <param name="command">32 /// The command that the parameters will be bound to.33 /// </param>34 /// <param name="localScope">35 /// The scope that the parameter binder will use to set parameters.36 /// </param>37 internal ScriptParameterBinderController(38 ScriptBlock script,39 InvocationInfo invocationInfo,40 ExecutionContext context,41 InternalCommand command,42 SessionStateScope localScope)43 : base(invocationInfo, context, new ScriptParameterBinder(script, invocationInfo, context, command, localScope))44 {45 this.DollarArgs = new List<object>();46 47 // Add the script parameter metadata to the bindable parameters48 // And add them to the unbound parameters list49 50 if (script.HasDynamicParameters)51 {52 UnboundParameters = this.BindableParameters.ReplaceMetadata(script.ParameterMetadata);53 }54 else55 {56 _bindableParameters = script.ParameterMetadata;57 UnboundParameters = new List<MergedCompiledCommandParameter>(_bindableParameters.BindableParameters.Values);58 }59 }60 61 #endregion ctor62 63 /// <summary>64 /// Holds the set of parameters that were not bound to any argument (i.e $args)65 /// </summary>66 internal List<object> DollarArgs { get; }67 68 /// <summary>69 /// Binds the command line parameters for shell functions/filters/scripts/scriptblocks.70 /// </summary>71 /// <param name="arguments">72 /// The arguments to be bound.73 /// </param>74 /// <returns>75 /// True if binding was successful or false otherwise.76 /// </returns>77 internal void BindCommandLineParameters(Collection<CommandParameterInternal> arguments)78 {79 // Add the passed in arguments to the unboundArguments collection80 InitUnboundArguments(arguments);81 ReparseUnboundArguments();82 83 UnboundArguments = BindNamedParameters(uint.MaxValue, UnboundArguments);84 85 ParameterBindingException parameterBindingError;86 UnboundArguments =87 BindPositionalParameters(88 UnboundArguments,89 uint.MaxValue,90 uint.MaxValue,91 out parameterBindingError);92 93 try94 {95 this.DefaultParameterBinder.RecordBoundParameters = false;96 97 // If there are any unbound parameters that have default values, then98 // set those default values.99 BindUnboundScriptParameters();100 101 // If there are any unbound arguments, stick them into $args102 HandleRemainingArguments(UnboundArguments);103 }104 finally105 {106 this.DefaultParameterBinder.RecordBoundParameters = true;107 }108 109 return;110 }111 112 /// <summary>113 /// Passes the binding directly through to the parameter binder.114 /// It does no verification against metadata.115 /// </summary>116 /// <param name="argument">117 /// The name and value of the variable to bind.118 /// </param>119 /// <param name="flags">120 /// Ignored.121 /// </param>122 /// <returns>123 /// True if the parameter was successfully bound. Any error condition124 /// produces an exception.125 /// </returns>126 internal override bool BindParameter(CommandParameterInternal argument, ParameterBindingFlags flags)127 {128 // Just pass the binding straight through. No metadata to verify the parameter against.129 DefaultParameterBinder.BindParameter(argument.ParameterName, argument.ArgumentValue, parameterMetadata: null);130 return true;131 }132 133 /// <summary>134 /// Takes the remaining arguments that haven't been bound, and binds135 /// them to $args.136 /// </summary>137 /// <param name="arguments">138 /// The remaining unbound arguments.139 /// </param>140 /// <remarks>141 /// An array containing the values that were bound to $args.142 /// </remarks>143 private void HandleRemainingArguments(Collection<CommandParameterInternal> arguments)144 {145 List<object> args = new List<object>();146 147 foreach (CommandParameterInternal parameter in arguments)148 {149 object argValue = parameter.ArgumentSpecified ? parameter.ArgumentValue : null;150 151 // Proper automatic proxy generation requires the ability to prevent unbound arguments152 // in the proxy from binding to positional parameters in the proxied command. We use153 // a special key ("$args") when splatting @CommandLineArguments to package up $args.154 // This special key is not created automatically because it is useful to splat @args,155 // just not in the automatically generated proxy.156 //157 // Example usage:158 //159 // function foo { param($a, $b) $a; $b; $args }160 // function foo_proxy { param($a) ; $CommandLineArguments.Add('$args', $args); foo @CommandLineArguments }161 // foo_proxy 1 2 3162 //163 // Then in foo, $a=1, $b=, $args=2,3164 //165 // Here, we want $b in foo to be unbound because the proxy doesn't have $b (an Exchange scenario.)166 // So we pass $args (2,3) in the special entry in @CommandLineArguments. If we had instead written:167 //168 // function foo_proxy { param($a) ; foo @CommandLineArguments @args }169 // foo_proxy 1 2 3170 //171 // Then in foo, $a=1, $b=2, $args=3172 //173 // Note that the name $args is chosen to be:174 // * descriptive175 // * obscure (it can't be a property/field name in C#, and is an unlikely variable in script)176 // So we shouldn't have any real conflict. Note that if someone actually puts ${$args} in their177 // param block, then the value will be bound and we won't have an unbound argument for "$args" here.178 if (parameter.ParameterAndArgumentSpecified &&179 parameter.ParameterName.Equals("$args", StringComparison.OrdinalIgnoreCase))180 {181 // $args is normally an object[], but because this feature is accessible from script, it's possible182 // for it to contain anything.183 if (argValue is object[])184 {185 args.AddRange(argValue as object[]);186 }187 else188 {189 args.Add(argValue);190 }191 192 continue;193 }194 195 if (parameter.ParameterNameSpecified)196 {197 // Add a property to the string so we can tell the difference between:198 // foo -abc199 // foo "-abc"200 // This is important when splatting, we reconstruct the parameter if the201 // value is splatted.202 var parameterText = new PSObject(new string(parameter.ParameterText));203 if (parameterText.Properties[NotePropertyNameForSplattingParametersInArgs] == null)204 {205 var noteProperty = new PSNoteProperty(NotePropertyNameForSplattingParametersInArgs,206 parameter.ParameterName)207 { IsHidden = true };208 parameterText.Properties.Add(noteProperty);209 }210 211 args.Add(parameterText);212 }213 214 if (parameter.ArgumentSpecified)215 {216 args.Add(argValue);217 }218 }219 220 object[] argsArray = args.ToArray();221 222 DefaultParameterBinder.BindParameter(SpecialVariables.Args, argsArray, parameterMetadata: null);223 224 DollarArgs.AddRange(argsArray);225 226 return;227 }228 229 internal const string NotePropertyNameForSplattingParametersInArgs = "<CommandParameterName>";230 }231}232 