MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Concurrent;5using System.Linq.Expressions;6using System.Management.Automation.Internal;7using System.Reflection;8 9using Microsoft.PowerShell.Commands;10 11namespace System.Management.Automation12{13 /// <summary>14 /// The parameter binder for real CLR objects that have properties and fields decorated with the parameter attributes.15 /// </summary>16 internal class ReflectionParameterBinder : ParameterBinderBase17 {18 #region ctor19 20 /// <summary>21 /// Constructs the parameter binder with the specified type metadata. The binder is only valid22 /// for a single instance of a bindable object and only for the duration of a command.23 /// </summary>24 /// <param name="target">25 /// The target object that the parameter values will be bound to.26 /// </param>27 /// <param name="command">28 /// An instance of the command so that attributes can access the context.29 /// </param>30 internal ReflectionParameterBinder(31 object target,32 Cmdlet command)33 : base(target, command.MyInvocation, command.Context, command)34 {35 }36 37 /// <summary>38 /// Constructs the parameter binder with the specified type metadata. The binder is only valid39 /// for a single instance of a bindable object and only for the duration of a command.40 /// </summary>41 /// <param name="target">42 /// The target object that the parameter values will be bound to.43 /// </param>44 /// <param name="command">45 /// An instance of the command so that attributes can access the context.46 /// </param>47 /// <param name="commandLineParameters">48 /// The dictionary to use to record the parameters set by this object...49 /// </param>50 internal ReflectionParameterBinder(51 object target,52 Cmdlet command,53 CommandLineParameters commandLineParameters)54 : base(target, command.MyInvocation, command.Context, command)55 {56 this.CommandLineParameters = commandLineParameters;57 }58 59 #endregion ctor60 61 #region internal members62 63 #region Parameter default values64 65 /// <summary>66 /// Gets the default value for the specified parameter.67 /// </summary>68 /// <param name="name">69 /// The name of the parameter to get the default value of.70 /// </param>71 /// <returns>72 /// The default value of the specified parameter.73 /// </returns>74 /// <exception cref="GetValueException">75 /// If the ETS call to get the property value throws an exception.76 /// </exception>77 internal override object GetDefaultParameterValue(string name)78 {79 try80 {81 return GetGetter(Target.GetType(), name)(Target);82 }83 catch (TargetInvocationException ex)84 {85 Exception inner = ex.InnerException ?? ex;86 throw new GetValueInvocationException("CatchFromBaseAdapterGetValueTI",87 inner,88 ExtendedTypeSystem.ExceptionWhenGetting,89 name, inner.Message);90 }91 catch (GetValueException) { throw; }92 catch (Exception e)93 {94 throw new GetValueInvocationException("CatchFromBaseAdapterGetValue",95 e,96 ExtendedTypeSystem.ExceptionWhenGetting,97 name, e.Message);98 }99 }100 #endregion Parameter default values101 102 #region Parameter binding103 104 /// <summary>105 /// Uses ETS to set the property specified by name to the value on106 /// the target bindable object.107 /// </summary>108 /// <param name="name">109 /// The name of the parameter to bind the value to.110 /// </param>111 /// <param name="value">112 /// The value to bind to the parameter. It should be assumed by113 /// derived classes that the proper type coercion has already taken114 /// place and that any prerequisite metadata has been satisfied.115 /// </param>116 /// <param name="parameterMetadata"></param>117 /// <exception cref="SetValueException">118 /// If the setter raises an exception.119 /// </exception>120 internal override void BindParameter(string name, object value, CompiledCommandParameter parameterMetadata)121 {122 Diagnostics.Assert(!string.IsNullOrEmpty(name), "caller to verify name parameter");123 124 try125 {126 var setter = parameterMetadata != null127 ? (parameterMetadata.Setter ??= GetSetter(Target.GetType(), name))128 : GetSetter(Target.GetType(), name);129 setter(Target, value);130 }131 catch (TargetInvocationException ex)132 {133 Exception inner = ex.InnerException ?? ex;134 throw new SetValueInvocationException("CatchFromBaseAdapterSetValueTI",135 inner,136 ExtendedTypeSystem.ExceptionWhenSetting,137 name, inner.Message);138 }139 catch (SetValueException) { throw; }140 catch (Exception e)141 {142 throw new SetValueInvocationException("CatchFromBaseAdapterSetValue",143 e,144 ExtendedTypeSystem.ExceptionWhenSetting,145 name, e.Message);146 }147 }148 149 #endregion Parameter binding150 151 #endregion Internal members152 153 #region Private members154 155 static ReflectionParameterBinder()156 {157 // Statically add delegates that we typically need on startup or every time we run PowerShell - this avoids the JIT158 s_getterMethods.TryAdd(Tuple.Create(typeof(OutDefaultCommand), "InputObject"), static o => ((OutDefaultCommand)o).InputObject);159 s_setterMethods.TryAdd(Tuple.Create(typeof(OutDefaultCommand), "InputObject"), static (o, v) => ((OutDefaultCommand)o).InputObject = (PSObject)v);160 161 s_getterMethods.TryAdd(Tuple.Create(typeof(OutLineOutputCommand), "InputObject"), static o => ((OutLineOutputCommand)o).InputObject);162 s_getterMethods.TryAdd(Tuple.Create(typeof(OutLineOutputCommand), "LineOutput"), static o => ((OutLineOutputCommand)o).LineOutput);163 s_setterMethods.TryAdd(Tuple.Create(typeof(OutLineOutputCommand), "InputObject"), static (o, v) => ((OutLineOutputCommand)o).InputObject = (PSObject)v);164 s_setterMethods.TryAdd(Tuple.Create(typeof(OutLineOutputCommand), "LineOutput"), static (o, v) => ((OutLineOutputCommand)o).LineOutput = v);165 166 s_getterMethods.TryAdd(Tuple.Create(typeof(FormatDefaultCommand), "InputObject"), static o => ((FormatDefaultCommand)o).InputObject);167 s_setterMethods.TryAdd(Tuple.Create(typeof(FormatDefaultCommand), "InputObject"), static (o, v) => ((FormatDefaultCommand)o).InputObject = (PSObject)v);168 169 s_setterMethods.TryAdd(Tuple.Create(typeof(SetStrictModeCommand), "Off"), static (o, v) => ((SetStrictModeCommand)o).Off = (SwitchParameter)v);170 s_setterMethods.TryAdd(Tuple.Create(typeof(SetStrictModeCommand), "Version"), static (o, v) => ((SetStrictModeCommand)o).Version = (Version)v);171 172 s_getterMethods.TryAdd(Tuple.Create(typeof(ForEachObjectCommand), "InputObject"), static o => ((ForEachObjectCommand)o).InputObject);173 s_setterMethods.TryAdd(Tuple.Create(typeof(ForEachObjectCommand), "InputObject"), static (o, v) => ((ForEachObjectCommand)o).InputObject = (PSObject)v);174 s_setterMethods.TryAdd(Tuple.Create(typeof(ForEachObjectCommand), "Process"), static (o, v) => ((ForEachObjectCommand)o).Process = (ScriptBlock[])v);175 176 s_getterMethods.TryAdd(Tuple.Create(typeof(WhereObjectCommand), "InputObject"), static o => ((WhereObjectCommand)o).InputObject);177 s_setterMethods.TryAdd(Tuple.Create(typeof(WhereObjectCommand), "InputObject"), static (o, v) => ((WhereObjectCommand)o).InputObject = (PSObject)v);178 s_setterMethods.TryAdd(Tuple.Create(typeof(WhereObjectCommand), "FilterScript"), static (o, v) => ((WhereObjectCommand)o).FilterScript = (ScriptBlock)v);179 180 s_setterMethods.TryAdd(Tuple.Create(typeof(ImportModuleCommand), "Name"), static (o, v) => ((ImportModuleCommand)o).Name = (string[])v);181 s_setterMethods.TryAdd(Tuple.Create(typeof(ImportModuleCommand), "ModuleInfo"), static (o, v) => ((ImportModuleCommand)o).ModuleInfo = (PSModuleInfo[])v);182 s_setterMethods.TryAdd(Tuple.Create(typeof(ImportModuleCommand), "Scope"), static (o, v) => ((ImportModuleCommand)o).Scope = (string)v);183 s_setterMethods.TryAdd(Tuple.Create(typeof(ImportModuleCommand), "PassThru"), static (o, v) => ((ImportModuleCommand)o).PassThru = (SwitchParameter)v);184 185 s_setterMethods.TryAdd(Tuple.Create(typeof(GetCommandCommand), "Name"), static (o, v) => ((GetCommandCommand)o).Name = (string[])v);186 s_setterMethods.TryAdd(Tuple.Create(typeof(GetCommandCommand), "Module"), static (o, v) => ((GetCommandCommand)o).Module = (string[])v);187 188 s_setterMethods.TryAdd(Tuple.Create(typeof(GetModuleCommand), "Name"), static (o, v) => ((GetModuleCommand)o).Name = (string[])v);189 s_setterMethods.TryAdd(Tuple.Create(typeof(GetModuleCommand), "ListAvailable"), static (o, v) => ((GetModuleCommand)o).ListAvailable = (SwitchParameter)v);190 s_setterMethods.TryAdd(Tuple.Create(typeof(GetModuleCommand), "FullyQualifiedName"), static (o, v) => ((GetModuleCommand)o).FullyQualifiedName = (ModuleSpecification[])v);191 192 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ErrorAction"),193 (o, v) =>194 {195 v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference));196 ((CommonParameters)o).ErrorAction = (ActionPreference)v;197 });198 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "WarningAction"),199 (o, v) =>200 {201 v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference));202 ((CommonParameters)o).WarningAction = (ActionPreference)v;203 });204 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "InformationAction"),205 (o, v) =>206 {207 v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference));208 ((CommonParameters)o).InformationAction = (ActionPreference)v;209 });210 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ProgressAction"),211 (o, v) =>212 {213 v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference));214 ((CommonParameters)o).ProgressAction = (ActionPreference)v;215 });216 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "Verbose"), static (o, v) => ((CommonParameters)o).Verbose = (SwitchParameter)v);217 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "Debug"), static (o, v) => ((CommonParameters)o).Debug = (SwitchParameter)v);218 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ErrorVariable"), static (o, v) => ((CommonParameters)o).ErrorVariable = (string)v);219 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "WarningVariable"), static (o, v) => ((CommonParameters)o).WarningVariable = (string)v);220 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "InformationVariable"), static (o, v) => ((CommonParameters)o).InformationVariable = (string)v);221 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "OutVariable"), static (o, v) => ((CommonParameters)o).OutVariable = (string)v);222 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "OutBuffer"), static (o, v) => ((CommonParameters)o).OutBuffer = (int)v);223 s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "PipelineVariable"), static (o, v) => ((CommonParameters)o).PipelineVariable = (string)v);224 }225 226 private static readonly ConcurrentDictionary<Tuple<Type, string>, Func<object, object>> s_getterMethods227 = new ConcurrentDictionary<Tuple<Type, string>, Func<object, object>>();228 229 private static readonly ConcurrentDictionary<Tuple<Type, string>, Action<object, object>> s_setterMethods =230 new ConcurrentDictionary<Tuple<Type, string>, Action<object, object>>();231 232 private static Func<object, object> GetGetter(Type type, string property)233 {234 return s_getterMethods.GetOrAdd(Tuple.Create(type, property),235 (Tuple<Type, string> _) =>236 {237 var target = Expression.Parameter(typeof(object));238 return Expression.Lambda<Func<object, object>>(239 Expression.Convert(240 GetPropertyOrFieldExpr(type, property, Expression.Convert(target, type)),241 typeof(object)),242 new[] { target }).Compile();243 });244 }245 246 private static Action<object, object> GetSetter(Type type, string property)247 {248 return s_setterMethods.GetOrAdd(Tuple.Create(type, property),249 _ =>250 {251 var target = Expression.Parameter(typeof(object));252 var value = Expression.Parameter(typeof(object));253 var propertyExpr = GetPropertyOrFieldExpr(type, property, Expression.Convert(target, type));254 255 Expression expr = Expression.Assign(propertyExpr, Expression.Convert(value, propertyExpr.Type));256 if (propertyExpr.Type.IsValueType && Nullable.GetUnderlyingType(propertyExpr.Type) == null)257 {258 var throwInvalidCastExceptionExpr =259 Expression.Call(Language.CachedReflectionInfo.LanguagePrimitives_ThrowInvalidCastException,260 Language.ExpressionCache.NullConstant,261 Expression.Constant(propertyExpr.Type, typeof(Type)));262 263 // The return type of 'ThrowInvalidCastException' is System.Object, but the method actually always264 // throws 'PSInvalidCastException' when it's executed. So converting 'throwInvalidCastExceptionExpr'265 // to 'propertyExpr.Type' is fine, because the conversion will never be hit.266 expr = Expression.Condition(Expression.Equal(value, Language.ExpressionCache.NullConstant),267 Expression.Convert(throwInvalidCastExceptionExpr, propertyExpr.Type),268 expr);269 }270 271 return Expression.Lambda<Action<object, object>>(expr, new[] { target, value }).Compile();272 });273 }274 275 private static Expression GetPropertyOrFieldExpr(Type type, string name, Expression target)276 {277 const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy;278 try279 {280 var propertyInfo = type.GetProperty(name, bindingFlags);281 if (propertyInfo != null)282 return Expression.Property(target, propertyInfo);283 }284 catch (AmbiguousMatchException)285 {286 // This is uncommon - in C#, there is "new" property that hides a base property.287 // To get the correct property, get all properties, and assume the first that matches288 // the name we want is the correct one. This seems fragile, but the DotNetAdapter289 // does the same thing290 foreach (var propertyInfo in type.GetProperties(bindingFlags))291 {292 if (propertyInfo.Name.Equals(name, StringComparison.Ordinal))293 {294 return Expression.Property(target, propertyInfo);295 }296 }297 }298 299 try300 {301 var fieldInfo = type.GetField(name, bindingFlags);302 if (fieldInfo != null)303 return Expression.Field(target, fieldInfo);304 }305 catch (AmbiguousMatchException)306 {307 foreach (var fieldInfo in type.GetFields(bindingFlags))308 {309 if (fieldInfo.Name.Equals(name, StringComparison.Ordinal))310 {311 return Expression.Field(target, fieldInfo);312 }313 }314 }315 316 Diagnostics.Assert(false, "Can't find property or field?");317 throw PSTraceSource.NewInvalidOperationException();318 }319 320 #endregion Private members321 }322}323 