Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
Binders.cs7952 linesDownload Raw Back to Binding
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Buffers;5using System.Collections;6using System.Collections.Concurrent;7using System.Collections.Generic;8using System.Collections.Specialized;9using System.Data;10using System.Dynamic;11using System.Globalization;12using System.Linq;13using System.Linq.Expressions;14using System.Management.Automation.Internal;15using System.Management.Automation.Runspaces;16using System.Management.Automation.Security;17using System.Reflection;18using System.Runtime.CompilerServices;19using System.Runtime.InteropServices;20using System.Text;21using System.Text.RegularExpressions;22using System.Threading;23using System.Xml;24 25namespace System.Management.Automation.Language26{27    // Item1 - member name28    // Item2 - class containing dynamic site (for protected/private member access)29    // Item3 - static (true) or instance (false)30    // Item4 - enumerating (true) or not (false)31    using PSGetMemberBinderKeyType = Tuple<string, Type, bool, bool>;32 33    // Item1 - member name34    // Item2 - class containing dynamic site (for protected/private member access)35    // Item3 - enumerating (true) or not (false)36    using PSSetMemberBinderKeyType = Tuple<string, Type, bool>;37 38    // Item1 - member name39    // Item2 - callinfo (# of args and (not used) named arguments)40    // Item3 - property setter (true) or not (false)41    // Item4 - enumerating (true) or not (false)42    // Item5 - invocation constraints (casts used in the invocation expression used to guide overload resolution)43    // Item6 - static (true) or instance (false)44    // Item7 - class containing dynamic site (for protected/private member access)45    using PSInvokeMemberBinderKeyType = Tuple<string, CallInfo, bool, bool, PSMethodInvocationConstraints, bool, Type>;46 47    // Item1 - callinfo (# of args and (not used) named arguments)48    // Item2 - invocation constraints (casts used in the invocation expression used to guide overload resolution)49    // Item3 - property setter (true) or not (false)50    // Item4 - static (true) or instance (false)51    // Item5 - class containing dynamic site (for protected/private member access)52    using PSInvokeDynamicMemberBinderKeyType = Tuple<CallInfo, PSMethodInvocationConstraints, bool, bool, Type>;53 54    // Item1 - class containing dynamic site (for protected/private member access)55    // Item2 - static (true) or instance (false)56    using PSGetOrSetDynamicMemberBinderKeyType = Tuple<Type, bool>;57 58    /// <summary>59    /// Extension methods for DynamicMetaObject.  Some of these extensions help handle PSObject, both in terms of60    /// getting the type or value from a DynamicMetaObject, but also help to generate binding restrictions that61    /// account for values that are optionally wrapped in PSObject.62    /// </summary>63    internal static class DynamicMetaObjectExtensions64    {65        internal static readonly DynamicMetaObject FakeError66            = new DynamicMetaObject(ExpressionCache.NullConstant, BindingRestrictions.Empty);67 68        internal static DynamicMetaObject WriteToDebugLog(this DynamicMetaObject obj, DynamicMetaObjectBinder binder)69        {70#if ENABLE_BINDER_DEBUG_LOGGING71            if (obj != FakeError)72            {73                System.Diagnostics.Debug.WriteLine("Binder: {0}\r\n    Restrictions: {2}\r\n    Target: {1}",74                                                   binder.ToString(),75                                                   obj.Expression.ToDebugString(),76                                                   obj.Restrictions.ToDebugString());77            }78#endif79            return obj;80        }81 82        internal static BindingRestrictions GetSimpleTypeRestriction(this DynamicMetaObject obj)83        {84            if (obj.Value == null)85            {86                return BindingRestrictions.GetInstanceRestriction(obj.Expression, obj.Value);87            }88 89            return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.Value.GetType());90        }91 92        internal static BindingRestrictions PSGetMethodArgumentRestriction(this DynamicMetaObject obj)93        {94            var baseValue = PSObject.Base(obj.Value);95            if (baseValue != null && baseValue.GetType() == typeof(object[]))96            {97                var effectiveArgType = Adapter.EffectiveArgumentType(obj.Value);98                var methodInfo = effectiveArgType != typeof(object[])99                    ? CachedReflectionInfo.PSInvokeMemberBinder_IsHomogeneousArray.MakeGenericMethod(effectiveArgType.GetElementType())100                    : CachedReflectionInfo.PSInvokeMemberBinder_IsHeterogeneousArray;101 102                BindingRestrictions restrictions;103                Expression test;104                if (obj.Value != baseValue)105                {106                    // Need PSObject...107                    restrictions = BindingRestrictions.GetTypeRestriction(obj.Expression, typeof(PSObject));108 109                    var temp = Expression.Variable(typeof(object[]));110                    test = Expression.Block(111                        new[] { temp },112                        Expression.Assign(temp, Expression.TypeAs(Expression.Call(CachedReflectionInfo.PSObject_Base, obj.Expression), typeof(object[]))),113                        Expression.AndAlso(114                            Expression.NotEqual(temp, ExpressionCache.NullObjectArray),115                            Expression.Call(methodInfo, temp)));116                }117                else118                {119                    restrictions = BindingRestrictions.GetTypeRestriction(obj.Expression, typeof(object[]));120                    var arrayExpr = obj.Expression.Cast(typeof(object[]));121                    test = Expression.Call(methodInfo, arrayExpr);122                }123 124                return restrictions.Merge(BindingRestrictions.GetExpressionRestriction(test));125            }126 127            return obj.PSGetTypeRestriction();128        }129 130        internal static BindingRestrictions PSGetStaticMemberRestriction(this DynamicMetaObject obj)131        {132            if (obj.Restrictions != BindingRestrictions.Empty)133            {134                return obj.Restrictions;135            }136 137            if (obj.Value == null)138            {139                return BindingRestrictions.GetInstanceRestriction(obj.Expression, obj.Value);140            }141 142            var baseValue = PSObject.Base(obj.Value);143            if (baseValue == null)144            {145                Diagnostics.Assert(obj.Value == AutomationNull.Value, "PSObject.Base should only return null for AutomationNull.Value");146                return BindingRestrictions.GetExpressionRestriction(Expression.Equal(obj.Expression, Expression.Constant(AutomationNull.Value)));147            }148 149            BindingRestrictions restrictions;150 151            if (baseValue is Type)152            {153                if (obj.Value == baseValue)154                {155                    //     newObj == oldObj  (if not wrapped in PSObject) or156                    restrictions = BindingRestrictions.GetInstanceRestriction(obj.Expression, obj.Value);157                }158                else159                {160                    //     newObj.GetType() == typeof(PSObject) && PSObject.Base(newObj) == oldObj161                    restrictions = BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);162                    restrictions = restrictions.Merge(163                        BindingRestrictions.GetInstanceRestriction(164                            Expression.Call(CachedReflectionInfo.PSObject_Base, obj.Expression),165                            baseValue));166                }167            }168            else if (obj.Value != baseValue)169            {170                // Binding restriction will look like:171                //     newObj.GetType() == typeof(PSObject) && PSObject.Base(newObj).GetType() == typeof(oldType)172 173                restrictions = BindingRestrictions.GetTypeRestriction(174                    Expression.Call(CachedReflectionInfo.PSObject_Base, obj.Expression),175                    baseValue.GetType());176            }177            else178            {179                restrictions = BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);180            }181 182            return restrictions;183        }184 185        internal static BindingRestrictions PSGetTypeRestriction(this DynamicMetaObject obj)186        {187            if (obj.Restrictions != BindingRestrictions.Empty)188            {189                return obj.Restrictions;190            }191 192            if (obj.Value == null)193            {194                return BindingRestrictions.GetInstanceRestriction(obj.Expression, obj.Value);195            }196 197            var baseValue = PSObject.Base(obj.Value);198            if (baseValue == null)199            {200                Diagnostics.Assert(obj.Value == AutomationNull.Value, "PSObject.Base should only return null for AutomationNull.Value");201                return BindingRestrictions.GetExpressionRestriction(Expression.Equal(obj.Expression, Expression.Constant(AutomationNull.Value)));202            }203 204            // The default restriction is a simple type test.  We use this type test even if the object is a PSObject,205            // this way we can avoid calling PSObject.Base in all restriction checks.206            var restrictions = BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);207 208            if (obj.Value != baseValue)209            {210                // Binding restriction will look like:211                //     newObj.GetType() == typeof(PSObject) && PSObject.Base(newObj).GetType() == typeof(oldType)212 213                restrictions = restrictions.Merge(214                    BindingRestrictions.GetTypeRestriction(Expression.Call(CachedReflectionInfo.PSObject_Base, obj.Expression),215                                                           baseValue.GetType()));216            }217            else if (baseValue is PSObject)218            {219                // We have an empty custom object.  The restrictions must check this explicitly, otherwise we have220                // a simple type test on PSObject, which obviously tests true for many objects.221                // So we end up with:222                //     newObj.GetType() == typeof(PSObject) && PSObject.Base(newObj) == newObj223 224                restrictions = restrictions.Merge(225                    BindingRestrictions.GetExpressionRestriction(226                        Expression.Equal(Expression.Call(CachedReflectionInfo.PSObject_Base, obj.Expression), obj.Expression)));227            }228 229            return restrictions;230        }231 232        internal static BindingRestrictions CombineRestrictions(this DynamicMetaObject target, params DynamicMetaObject[] args)233        {234            var restrictions = target.Restrictions == BindingRestrictions.Empty ? target.PSGetTypeRestriction() : target.Restrictions;235 236            for (int index = 0; index < args.Length; index++)237            {238                var r = args[index];239                restrictions =240                    restrictions.Merge(r.Restrictions == BindingRestrictions.Empty241                                           ? r.PSGetTypeRestriction()242                                           : r.Restrictions);243            }244 245            return restrictions;246        }247 248        internal static Expression CastOrConvertMethodArgument(this DynamicMetaObject target,249                                                               Type parameterType,250                                                               string parameterName,251                                                               string methodName,252                                                               bool allowCastingToByRefLikeType,253                                                               List<ParameterExpression> temps,254                                                               List<Expression> initTemps)255        {256            if (target.Value == AutomationNull.Value)257            {258                return Expression.Constant(null, parameterType);259            }260 261            var argType = target.LimitType;262            if (parameterType == typeof(object) && argType == typeof(PSObject))263            {264                return Expression.Call(CachedReflectionInfo.PSObject_Base, target.Expression.Cast(typeof(PSObject)));265            }266 267            // If the conversion can't fail, skip wrapping the conversion in try/catch so we generate less code.268            if (parameterType.IsAssignableFrom(argType))269            {270                return target.Expression.Cast(parameterType);271            }272 273            ConversionRank? rank = null;274            if (parameterType.IsByRefLike && allowCastingToByRefLikeType)275            {276                var conversionResult = PSConvertBinder.ConvertToByRefLikeTypeViaCasting(target, parameterType);277                if (conversionResult != null)278                {279                    return conversionResult;280                }281 282                rank = ConversionRank.None;283            }284 285            var exceptionParam = Expression.Variable(typeof(Exception));286            var targetTemp = Expression.Variable(target.Expression.Type);287            bool debase = false;288 289            // ConstrainedLanguage note - calls to this conversion are covered by the method resolution algorithm290            // (which ignores method arguments with disallowed types)291            var conversion = rank == ConversionRank.None292                ? LanguagePrimitives.NoConversion293                : LanguagePrimitives.FigureConversion(target.Value, parameterType, out debase);294            var invokeConverter = PSConvertBinder.InvokeConverter(conversion, targetTemp, parameterType, debase, ExpressionCache.InvariantCulture);295            var expr =296                Expression.Block(new[] { targetTemp },297                    Expression.TryCatch(298                        Expression.Block(299                            Expression.Assign(targetTemp, target.Expression),300                            invokeConverter),301                        Expression.Catch(exceptionParam,302                            Expression.Block(303                                Expression.Call(CachedReflectionInfo.ExceptionHandlingOps_ConvertToArgumentConversionException,304                                                exceptionParam,305                                                Expression.Constant(parameterName),306                                                targetTemp.Cast(typeof(object)),307                                                Expression.Constant(methodName),308                                                Expression.Constant(parameterType, typeof(Type))),309                                Expression.Default(invokeConverter.Type)))));310            var tmp = Expression.Variable(expr.Type);311            temps.Add(tmp);312            initTemps.Add(Expression.Assign(tmp, expr));313            return tmp;314        }315 316        internal static Expression CastOrConvert(this DynamicMetaObject target, Type type)317        {318            if (target.LimitType == type)319            {320                return target.Expression.Cast(type);321            }322 323            bool debase;324 325            // ConstrainedLanguage note - calls to this conversion are done by:326            // Switch statements (always to Object), method invocation (protected by InvokeMember binder),327            // and hard-coded casts to integral types.328            var conversion = LanguagePrimitives.FigureConversion(target.Value, type, out debase);329            return PSConvertBinder.InvokeConverter(conversion, target.Expression, type, debase, ExpressionCache.InvariantCulture);330        }331 332        internal static DynamicMetaObject ThrowRuntimeError(this DynamicMetaObject target, DynamicMetaObject[] args,333                                                            BindingRestrictions moreTests, string errorID,334                                                            string resourceString, params Expression[] exceptionArgs)335        {336            return new DynamicMetaObject(Compiler.ThrowRuntimeError(errorID, resourceString, exceptionArgs),337                                         target.CombineRestrictions(args).Merge(moreTests));338        }339 340        internal static DynamicMetaObject ThrowRuntimeError(this DynamicMetaObject target, BindingRestrictions bindingRestrictions,341                                                            string errorID, string resourceString, params Expression[] exceptionArgs)342        {343            return new DynamicMetaObject(Compiler.ThrowRuntimeError(errorID, resourceString, exceptionArgs),344                                         bindingRestrictions);345        }346 347#if ENABLE_BINDER_DEBUG_LOGGING348        internal static string ToDebugString(this BindingRestrictions restrictions)349        {350            return restrictions.ToExpression().ToDebugString();351        }352#endif353    }354 355    internal static class DynamicMetaObjectBinderExtensions356    {357        internal static DynamicMetaObject DeferForPSObject(this DynamicMetaObjectBinder binder, DynamicMetaObject target, bool targetIsComObject = false)358        {359            Diagnostics.Assert(target.Value is PSObject, "target must be a psobject");360 361            BindingRestrictions restrictions = BindingRestrictions.Empty;362            Expression expr = ProcessOnePSObject(target, ref restrictions, argIsComObject: targetIsComObject);363            return new DynamicMetaObject(DynamicExpression.Dynamic(binder, binder.ReturnType, expr), restrictions);364        }365 366        internal static DynamicMetaObject DeferForPSObject(this DynamicMetaObjectBinder binder, DynamicMetaObject target, DynamicMetaObject arg, bool targetIsComObject = false)367        {368            Diagnostics.Assert(target.Value is PSObject || arg.Value is PSObject, "At least one arg must be a psobject");369 370            BindingRestrictions restrictions = BindingRestrictions.Empty;371            Expression expr1 = ProcessOnePSObject(target, ref restrictions, argIsComObject: targetIsComObject);372            Expression expr2 = ProcessOnePSObject(arg, ref restrictions, argIsComObject: false);373            return new DynamicMetaObject(DynamicExpression.Dynamic(binder, binder.ReturnType, expr1, expr2), restrictions);374        }375 376        internal static DynamicMetaObject DeferForPSObject(this DynamicMetaObjectBinder binder, DynamicMetaObject[] args, bool targetIsComObject = false)377        {378            Diagnostics.Assert(args != null && args.Length > 0, "args should not be null or empty");379            Diagnostics.Assert(args.Any(mo => mo.Value is PSObject), "At least one arg must be a psobject");380 381            Expression[] exprs = new Expression[args.Length];382            BindingRestrictions restrictions = BindingRestrictions.Empty;383 384            // Target maps to arg[0] of the binder.385            exprs[0] = ProcessOnePSObject(args[0], ref restrictions, targetIsComObject);386            for (int i = 1; i < args.Length; i++)387            {388                exprs[i] = ProcessOnePSObject(args[i], ref restrictions, argIsComObject: false);389            }390 391            return new DynamicMetaObject(DynamicExpression.Dynamic(binder, binder.ReturnType, exprs), restrictions);392        }393 394        private static Expression ProcessOnePSObject(DynamicMetaObject arg, ref BindingRestrictions restrictions, bool argIsComObject = false)395        {396            Expression expr = null;397            object baseValue = PSObject.Base(arg.Value);398            if (baseValue != arg.Value)399            {400                expr = Expression.Call(CachedReflectionInfo.PSObject_Base, arg.Expression.Cast(typeof(object)));401 402                if (argIsComObject)403                {404                    // The 'base' is a COM object, so bake that in the rule.405                    restrictions = restrictions406                        .Merge(arg.GetSimpleTypeRestriction())407                        .Merge(BindingRestrictions.GetExpressionRestriction(Expression.Call(CachedReflectionInfo.Utils_IsComObject, expr)));408                }409                else410                {411                    // Use a more general condition for the rule: 'arg' is a PSObject and 'base != arg'.412                    restrictions = restrictions413                        .Merge(arg.GetSimpleTypeRestriction())414                        .Merge(BindingRestrictions.GetExpressionRestriction(Expression.NotEqual(expr, arg.Expression)));415                }416            }417            else418            {419                expr = arg.Expression;420                restrictions = restrictions.Merge(arg.PSGetTypeRestriction());421            }422 423            return expr;424        }425 426        internal static DynamicMetaObject UpdateComRestrictionsForPsObject(this DynamicMetaObject binder, DynamicMetaObject[] args)427        {428            // Add a restriction that prevents PSObject arguments (so that they get based)429            BindingRestrictions newRestrictions = binder.Restrictions;430            newRestrictions = args.Aggregate(newRestrictions, (current, arg) =>431            {432                if (arg.LimitType.IsValueType)433                {434                    return current.Merge(arg.GetSimpleTypeRestriction());435                }436                else437                {438                    return current.Merge(BindingRestrictions.GetExpressionRestriction(439                        Expression.Equal(440                            Expression.Call(CachedReflectionInfo.PSObject_Base, arg.Expression),441                            arg.Expression)));442                }443            });444 445            return new DynamicMetaObject(binder.Expression, newRestrictions);446        }447    }448 449    internal static class BinderUtils450    {451        internal static BindingRestrictions GetVersionCheck(DynamicMetaObjectBinder binder, int expectedVersionNumber)452        {453            return BindingRestrictions.GetExpressionRestriction(454                Expression.Equal(Expression.Field(Expression.Constant(binder), "_version"),455                                 ExpressionCache.Constant(expectedVersionNumber)));456        }457 458        internal static BindingRestrictions GetLanguageModeCheckIfHasEverUsedConstrainedLanguage()459        {460            // Also add a language mode check to detect toggling between language modes461            if (ExecutionContext.HasEverUsedConstrainedLanguage)462            {463                var context = LocalPipeline.GetExecutionContextFromTLS();464 465                var tmp = Expression.Variable(typeof(ExecutionContext));466                var langModeFromContext = Expression.Property(tmp, CachedReflectionInfo.ExecutionContext_LanguageMode);467                var constrainedLanguageMode = Expression.Constant(PSLanguageMode.ConstrainedLanguage);468 469                // Execution context might be null if we're called from a thread with no runspace (e.g. a PSObject470                // is used in some C# w/ dynamic). This is sometimes fine, we don't always need a runspace to access471                // properties.472                Expression test = context?.LanguageMode == PSLanguageMode.ConstrainedLanguage473                    ? Expression.AndAlso(474                          Expression.NotEqual(tmp, ExpressionCache.NullExecutionContext),475                          Expression.Equal(langModeFromContext, constrainedLanguageMode))476                    : Expression.OrElse(477                          Expression.Equal(tmp, ExpressionCache.NullExecutionContext),478                          Expression.NotEqual(langModeFromContext, constrainedLanguageMode));479 480                return BindingRestrictions.GetExpressionRestriction(481                    Expression.Block(482                        new[] { tmp },483                        Expression.Assign(tmp, ExpressionCache.GetExecutionContextFromTLS),484                        test));485            }486 487            return BindingRestrictions.Empty;488        }489 490        internal static BindingRestrictions GetOptionalVersionAndLanguageCheckForType(DynamicMetaObjectBinder binder, Type targetType, int expectedVersionNumber)491        {492            BindingRestrictions additionalBindingRestrictions = BindingRestrictions.Empty;493 494            // If this uses a potentially unsafe type, we also need a version check495            if (!CoreTypes.Contains(targetType))496            {497                if (expectedVersionNumber != -1)498                {499                    additionalBindingRestrictions = additionalBindingRestrictions.Merge(BinderUtils.GetVersionCheck(binder, expectedVersionNumber));500                }501 502                additionalBindingRestrictions = additionalBindingRestrictions.Merge(GetLanguageModeCheckIfHasEverUsedConstrainedLanguage());503            }504 505            return additionalBindingRestrictions;506        }507    }508 509    #region PowerShell non-standard language binders510 511    /// <summary>512    /// Some classes that implement IEnumerable are not considered as enumerable from the perspective of pipelines,513    /// this binder implements those semantics.514    ///515    /// The standard interop ConvertBinder is used to allow third party dynamic objects to get the first chance516    /// at the conversion in case they do support enumeration, but do not implement IEnumerable directly.517    /// </summary>518    internal sealed class PSEnumerableBinder : ConvertBinder519    {520        private static readonly PSEnumerableBinder s_binder = new PSEnumerableBinder();521 522        internal static PSEnumerableBinder Get()523        {524            return s_binder;525        }526 527        private PSEnumerableBinder()528            : base(typeof(IEnumerator), false)529        {530            CacheTarget((Func<CallSite, object, IEnumerator>)(PSObjectStringRule));531            CacheTarget((Func<CallSite, object, IEnumerator>)(ArrayRule));532            CacheTarget((Func<CallSite, object, IEnumerator>)(StringRule));533            CacheTarget((Func<CallSite, object, IEnumerator>)(NotEnumerableRule));534            CacheTarget((Func<CallSite, object, IEnumerator>)(PSObjectNotEnumerableRule));535            CacheTarget((Func<CallSite, object, IEnumerator>)(AutomationNullRule));536        }537 538        public override string ToString()539        {540            return "ToEnumerable";541        }542 543        internal static BindingRestrictions GetRestrictions(DynamicMetaObject target)544        {545            return (target.Value is PSObject)546                ? BindingRestrictions.GetTypeRestriction(target.Expression, target.Value.GetType())547                : target.PSGetTypeRestriction();548        }549 550        private DynamicMetaObject NullResult(DynamicMetaObject target)551        {552            // The object is not enumerable from PowerShell's perspective.  Rather than raise an exception, we let the553            // caller check for null and take the appropriate action.554            return new DynamicMetaObject(555                MaybeDebase(this, static e => ExpressionCache.NullEnumerator, target),556                GetRestrictions(target));557        }558 559        internal static Expression MaybeDebase(DynamicMetaObjectBinder binder, Func<Expression, Expression> generator, DynamicMetaObject target)560        {561            if (target.Value is not PSObject)562            {563                return generator(target.Expression);564            }565 566            object targetValue = PSObject.Base(target.Value);567 568            var tmp = Expression.Parameter(typeof(object), "value");569            return Expression.Block(570                new ParameterExpression[] { tmp },571                Expression.Assign(tmp, Expression.Call(CachedReflectionInfo.PSObject_Base, target.Expression)),572                Expression.Condition(573                    targetValue == null ?574                        (Expression)Expression.AndAlso(Expression.Equal(tmp, ExpressionCache.NullConstant),575                                                       Expression.Not(Expression.Equal(target.Expression, ExpressionCache.AutomationNullConstant)))576                        : Expression.TypeEqual(tmp, targetValue.GetType()),577                    generator(tmp), binder.GetUpdateExpression(binder.ReturnType)));578        }579 580        public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion)581        {582            if (!target.HasValue)583            {584                return Defer(target).WriteToDebugLog(this);585            }586 587            if (target.Value == AutomationNull.Value)588            {589                return new DynamicMetaObject(590                    Expression.Call(Expression.Constant(Array.Empty<object>()), typeof(Array).GetMethod("GetEnumerator")),591                    BindingRestrictions.GetInstanceRestriction(target.Expression, AutomationNull.Value)).WriteToDebugLog(this);592            }593 594            var targetValue = PSObject.Base(target.Value);595 596            if (targetValue == null || targetValue is string || targetValue is PSObject)597            {598                return (errorSuggestion ?? NullResult(target)).WriteToDebugLog(this);599            }600 601            if (targetValue.GetType().IsArray)602            {603                return (new DynamicMetaObject(604                    MaybeDebase(this, static e => Expression.Call(Expression.Convert(e, typeof(Array)), typeof(Array).GetMethod("GetEnumerator")),605                        target),606                    GetRestrictions(target))).WriteToDebugLog(this);607            }608 609            if (targetValue is IDictionary || targetValue is XmlNode)610            {611                return (errorSuggestion ?? NullResult(target)).WriteToDebugLog(this);612            }613 614            if (targetValue is DataTable)615            {616                // Generate:617                //618                //  DataRowCollection rows;619                //  DataTable table = (DataTable)obj;620                //  if ((rows = table.Rows) != null)621                //      return rows.GetEnumerator();622                //  else623                //      return null;624 625                return (new DynamicMetaObject(626                    MaybeDebase(this, e =>627                        {628                            var table = Expression.Parameter(typeof(DataTable), "table");629                            var rows = Expression.Parameter(typeof(DataRowCollection), "rows");630                            return Expression.Block(new ParameterExpression[] { table, rows },631                                Expression.Assign(table, e.Cast(typeof(DataTable))),632                                Expression.Condition(633                                    Expression.NotEqual(Expression.Assign(rows, Expression.Property(table, "Rows")), ExpressionCache.NullConstant),634                                    Expression.Call(rows, typeof(DataRowCollection).GetMethod("GetEnumerator")),635                                    ExpressionCache.NullEnumerator));636                        },637                        target),638                    GetRestrictions(target))).WriteToDebugLog(this);639            }640 641            if (Marshal.IsComObject(targetValue))642            {643                // Pretend that all com objects are enumerable, even if they aren't.  We do this because it's technically impossible644                // to know if a com object is enumerable without just trying to cast it to IEnumerable.  We could generate a rule like:645                //646                //     if (IsComObject(obj)) { return obj as IEnumerable; } else { UpdateSite; }647                //648                // But code that calls PSEnumerableBinder.IsEnumerable and generate code based on the true/false result of that649                // function wouldn't work properly.  Instead, we'll fix things up after the binding decisions are made, see650                // EnumerableOps.NonEnumerableObjectEnumerator for more comments on how this works.651 652                var bindingRestrictions = BindingRestrictions.GetExpressionRestriction(653                    Expression.Call(CachedReflectionInfo.Utils_IsComObject,654                                    Expression.Call(CachedReflectionInfo.PSObject_Base, target.Expression)));655                return new DynamicMetaObject(656                    Expression.Call(CachedReflectionInfo.EnumerableOps_GetCOMEnumerator, target.Expression), bindingRestrictions).WriteToDebugLog(this);657            }658 659            var enumerable = targetValue as IEnumerable;660            if (enumerable != null)661            {662                // Normally it's safe to just call IEnumerable.GetEnumerator, but in some rare cases, the663                // non-generic implementation throws or returns null, so we'll just avoid that problem and664                // call the generic version if it exists.665 666                foreach (var i in targetValue.GetType().GetInterfaces())667                {668                    if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))669                    {670                        return (new DynamicMetaObject(671                            MaybeDebase(this, e => Expression.Call(672                                CachedReflectionInfo.EnumerableOps_GetGenericEnumerator.MakeGenericMethod(i.GetGenericArguments()[0]), Expression.Convert(e, i)),673                                target),674                            GetRestrictions(target))).WriteToDebugLog(this);675                    }676                }677 678                return (new DynamicMetaObject(679                    MaybeDebase(this, static e => Expression.Call(CachedReflectionInfo.EnumerableOps_GetEnumerator, Expression.Convert(e, typeof(IEnumerable))),680                        target),681                    GetRestrictions(target))).WriteToDebugLog(this);682            }683 684            var enumerator = targetValue as IEnumerator;685            if (enumerator != null)686            {687                return (new DynamicMetaObject(688                    MaybeDebase(this, static e => e.Cast(typeof(IEnumerator)), target),689                    GetRestrictions(target))).WriteToDebugLog(this);690            }691 692            return (errorSuggestion ?? NullResult(target)).WriteToDebugLog(this);693        }694 695        /// <summary>696        /// Check if the statically known type is potentially enumerable.  We can avoid some dynamic sites if we know the697        /// type is never enumerable.698        /// </summary>699        internal static bool IsStaticTypePossiblyEnumerable(Type type)700        {701            if (type == typeof(object) || type == typeof(PSObject) || type.IsArray)702            {703                return true;704            }705 706            if (type == typeof(string) || typeof(IDictionary).IsAssignableFrom(type) || typeof(XmlNode).IsAssignableFrom(type))707            {708                return false;709            }710 711            if (type.IsSealed && !typeof(IEnumerable).IsAssignableFrom(type) && !typeof(IEnumerator).IsAssignableFrom(type))712            {713                return false;714            }715 716            return true;717        }718 719        // Binders normally cannot return null, but we want a way to detect if something is enumerable,720        // so we return null if the target is not enumerable.721        internal static DynamicMetaObject IsEnumerable(DynamicMetaObject target)722        {723            var binder = PSEnumerableBinder.Get();724            var result = binder.FallbackConvert(target, DynamicMetaObjectExtensions.FakeError);725            return (result == DynamicMetaObjectExtensions.FakeError) ? null : result;726        }727 728        private static IEnumerator AutomationNullRule(CallSite site, object obj)729        {730            return obj == AutomationNull.Value731                ? Array.Empty<object>().GetEnumerator()732                : ((CallSite<Func<CallSite, object, IEnumerator>>)site).Update(site, obj);733        }734 735        private static IEnumerator NotEnumerableRule(CallSite site, object obj)736        {737            if (obj == null)738            {739                return null;740            }741 742            if (obj is not PSObject743                && obj is not IEnumerable744                && obj is not IEnumerator745                && obj is not DataTable746                && !Marshal.IsComObject(obj))747            {748                return null;749            }750 751            return ((CallSite<Func<CallSite, object, IEnumerator>>)site).Update(site, obj);752        }753 754        private static IEnumerator PSObjectNotEnumerableRule(CallSite site, object obj)755        {756            var psobj = obj as PSObject;757            return psobj != null && obj != AutomationNull.Value758                ? NotEnumerableRule(site, PSObject.Base(obj))759                : ((CallSite<Func<CallSite, object, IEnumerator>>)site).Update(site, obj);760        }761 762        private static IEnumerator ArrayRule(CallSite site, object obj)763        {764            var array = obj as Array;765            if (array != null) return array.GetEnumerator();766            return ((CallSite<Func<CallSite, object, IEnumerator>>)site).Update(site, obj);767        }768 769        private static IEnumerator StringRule(CallSite site, object obj)770        {771            return obj is string ? null : ((CallSite<Func<CallSite, object, IEnumerator>>)site).Update(site, obj);772        }773 774        private static IEnumerator PSObjectStringRule(CallSite site, object obj)775        {776            var psobj = obj as PSObject;777            if (psobj != null && PSObject.Base(psobj) is string) return null;778            return ((CallSite<Func<CallSite, object, IEnumerator>>)site).Update(site, obj);779        }780    }781 782    /// <summary>783    /// This binder is used for the @() operator.784    /// </summary>785    internal sealed class PSToObjectArrayBinder : DynamicMetaObjectBinder786    {787        private static readonly PSToObjectArrayBinder s_binder = new PSToObjectArrayBinder();788 789        internal static PSToObjectArrayBinder Get()790        {791            return s_binder;792        }793 794        private PSToObjectArrayBinder()795        {796        }797 798        public override string ToString()799        {800            return "ToObjectArray";801        }802 803        public override Type ReturnType { get { return typeof(object[]); } }804 805        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)806        {807            if (!target.HasValue)808            {809                return Defer(target, args);810            }811 812            if (target.Value == AutomationNull.Value)813            {814                return new DynamicMetaObject(Expression.Constant(Array.Empty<object>()),815                    BindingRestrictions.GetInstanceRestriction(target.Expression, AutomationNull.Value)).WriteToDebugLog(this);816            }817 818            var enumerable = PSEnumerableBinder.IsEnumerable(target);819            if (enumerable == null)820            {821                return new DynamicMetaObject(822                    Expression.NewArrayInit(typeof(object), target.Expression.Cast(typeof(object))),823                    target.PSGetTypeRestriction()).WriteToDebugLog(this);824            }825 826            var value = PSObject.Base(target.Value);827            if (value is List<object>)828            {829                return new DynamicMetaObject(830                    Expression.Call(PSEnumerableBinder.MaybeDebase(this, static e => e.Cast(typeof(List<object>)), target), CachedReflectionInfo.ObjectList_ToArray),831                    PSEnumerableBinder.GetRestrictions(target)).WriteToDebugLog(this);832            }833 834            // It's enumerable, but not an List<object>.  Call EnumerableOps.ToArray835            return new DynamicMetaObject(836                Expression.Call(CachedReflectionInfo.EnumerableOps_ToArray, enumerable.Expression),837                target.PSGetTypeRestriction()).WriteToDebugLog(this);838        }839    }840 841    internal sealed class PSPipeWriterBinder : DynamicMetaObjectBinder842    {843        private static readonly PSPipeWriterBinder s_binder = new PSPipeWriterBinder();844 845        internal static PSPipeWriterBinder Get()846        {847            return s_binder;848        }849 850        private PSPipeWriterBinder()851        {852            CacheTarget((Action<CallSite, object, Pipe, ExecutionContext>)StringRule);853            CacheTarget((Action<CallSite, object, Pipe, ExecutionContext>)AutomationNullRule);854            CacheTarget((Action<CallSite, object, Pipe, ExecutionContext>)BoolRule);855            CacheTarget((Action<CallSite, object, Pipe, ExecutionContext>)IntRule);856        }857 858        public override string ToString()859        {860            return "PipelineWriter";861        }862 863        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)864        {865            // args[0] is the pipe866            // args[1] is the execution context, only used if we're enumerating867 868            if (!target.HasValue)869            {870                return Defer(target, args);871            }872 873            if (target.Value == AutomationNull.Value)874            {875                return (new DynamicMetaObject(876                    Expression.Block(typeof(void), Expression.Call(CachedReflectionInfo.PipelineOps_Nop)),877                    BindingRestrictions.GetInstanceRestriction(target.Expression, AutomationNull.Value))).WriteToDebugLog(this);878            }879 880            var enumerable = PSEnumerableBinder.IsEnumerable(target);881            if (enumerable == null)882            {883                var bindingResult = PSVariableAssignmentBinder.Get().Bind(target, Array.Empty<DynamicMetaObject>());884                var restrictions = target.LimitType.IsValueType885                    ? bindingResult.Restrictions886                    : target.PSGetTypeRestriction();887                return (new DynamicMetaObject(888                    Expression.Call(args[0].Expression,889                                    CachedReflectionInfo.Pipe_Add,890                                    bindingResult.Expression.Cast(typeof(object))),891                    restrictions)).WriteToDebugLog(this);892            }893 894            bool needsToDispose = PSObject.Base(target.Value) is not IEnumerator;895            return (new DynamicMetaObject(896                Expression.Call(CachedReflectionInfo.EnumerableOps_WriteEnumerableToPipe,897                                enumerable.Expression,898                                args[0].Expression,899                                args[1].Expression,900                                ExpressionCache.Constant(needsToDispose)),901                enumerable.Restrictions)).WriteToDebugLog(this);902        }903 904        private static void BoolRule(CallSite site, object obj, Pipe pipe, ExecutionContext context)905        {906            if (obj is bool) { pipe.Add(obj); }907            else { ((CallSite<Action<CallSite, object, Pipe, ExecutionContext>>)site).Update(site, obj, pipe, context); }908        }909 910        private static void IntRule(CallSite site, object obj, Pipe pipe, ExecutionContext context)911        {912            if (obj is int) { pipe.Add(obj); }913            else { ((CallSite<Action<CallSite, object, Pipe, ExecutionContext>>)site).Update(site, obj, pipe, context); }914        }915 916        private static void StringRule(CallSite site, object obj, Pipe pipe, ExecutionContext context)917        {918            if (obj is string) { pipe.Add(obj); }919            else { ((CallSite<Action<CallSite, object, Pipe, ExecutionContext>>)site).Update(site, obj, pipe, context); }920        }921 922        private static void AutomationNullRule(CallSite site, object obj, Pipe pipe, ExecutionContext context)923        {924            if (obj != AutomationNull.Value) { ((CallSite<Action<CallSite, object, Pipe, ExecutionContext>>)site).Update(site, obj, pipe, context); }925        }926    }927 928    /// <summary>929    /// This binder creates the collection we use to do multi-assignments, e.g.:930    ///     $x,$y = $z931    ///     $x,$y,$z = 1,2,3,4,5932    /// The target in this binder is the RHS, the result expression is an IList where the Count matches the933    /// number of values assigned (_elements) on the left hand side of the assign.934    /// </summary>935    internal sealed class PSArrayAssignmentRHSBinder : DynamicMetaObjectBinder936    {937        private static readonly List<PSArrayAssignmentRHSBinder> s_binders = new List<PSArrayAssignmentRHSBinder>();938        private readonly int _elements;939 940        internal static PSArrayAssignmentRHSBinder Get(int i)941        {942            lock (s_binders)943            {944                while (s_binders.Count <= i)945                {946                    s_binders.Add(null);947                }948 949                return s_binders[i] ?? (s_binders[i] = new PSArrayAssignmentRHSBinder(i));950            }951        }952 953        private PSArrayAssignmentRHSBinder(int elements)954        {955            _elements = elements;956        }957 958        public override string ToString()959        {960            return string.Create(CultureInfo.InvariantCulture, $"MultiAssignRHSBinder {_elements}");961        }962 963        public override Type ReturnType { get { return typeof(IList); } }964 965        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)966        {967            Diagnostics.Assert(args.Length == 0, "binder doesn't expect any arguments");968 969            if (!target.HasValue)970            {971                return Defer(target).WriteToDebugLog(this);972            }973 974            if (target.Value is PSObject && (PSObject.Base(target.Value) != target.Value))975            {976                return this.DeferForPSObject(target).WriteToDebugLog(this);977            }978 979            var iList = target.Value as IList;980            if (iList != null)981            {982                // 3 possibilities - too few, exact, or too many elements.983 984                var getListCountExpr = Expression.Property(target.Expression.Cast(typeof(ICollection)), CachedReflectionInfo.ICollection_Count);985 986                var restrictions = target.PSGetTypeRestriction().Merge(987                    BindingRestrictions.GetExpressionRestriction(Expression.Equal(getListCountExpr,988                                                                                  ExpressionCache.Constant(iList.Count))));989 990                if (iList.Count == _elements)991                {992                    // Exact, we can use the value as is.993                    return new DynamicMetaObject(target.Expression.Cast(typeof(IList)), restrictions).WriteToDebugLog(this);994                }995 996                int i;997                Expression[] newArrayElements = new Expression[_elements];998                var temp = Expression.Variable(typeof(IList));999                if (iList.Count < _elements)1000                {1001                    // Too few, create an array with the correct number assigned, fill in null for the extras1002 1003                    for (i = 0; i < iList.Count; ++i)1004                    {1005                        newArrayElements[i] =1006                            Expression.Call(temp, CachedReflectionInfo.IList_get_Item, ExpressionCache.Constant(i));1007                    }1008 1009                    for (; i < _elements; ++i)1010                    {1011                        newArrayElements[i] = ExpressionCache.NullConstant;1012                    }1013                }1014                else1015                {1016                    // Too many, create an array with the correct number assigned, and the last element contains1017                    // all the extras.1018                    for (i = 0; i < _elements - 1; ++i)1019                    {1020                        newArrayElements[i] =1021                            Expression.Call(temp, CachedReflectionInfo.IList_get_Item, ExpressionCache.Constant(i));1022                    }1023 1024                    newArrayElements[_elements - 1] =1025                        Expression.Call(CachedReflectionInfo.EnumerableOps_GetSlice, temp, ExpressionCache.Constant(_elements - 1)).Cast(typeof(object));1026                }1027 1028                return (new DynamicMetaObject(1029                    Expression.Block(1030                        new[] { temp },1031                        Expression.Assign(temp, target.Expression.Cast(typeof(IList))),1032                        Expression.NewArrayInit(typeof(object), newArrayElements)),1033                    restrictions)).WriteToDebugLog(this);1034            }1035 1036            // We have a single element, the rest must be null.1037            return (new DynamicMetaObject(1038                Expression.NewArrayInit(typeof(object), Enumerable.Repeat(ExpressionCache.NullConstant, _elements - 1)1039                                                .Prepend(target.Expression.Cast(typeof(object)))),1040                target.PSGetTypeRestriction())).WriteToDebugLog(this);1041        }1042    }1043 1044    /// <summary>1045    /// This binder is used to convert objects to string in specific circumstances, including:1046    ///1047    ///     * The LHS of a format expression.  The arguments (the RHS objects) of the format1048    ///       expression are not converted to string here, that is deferred to string.Format which1049    ///       may have some custom formatting to apply.1050    ///     * The objects passed to the format expression as part of an expandable string.  In this1051    ///       case, the format string is generated by the parser, so we know that there is no custom1052    ///       formatting to consider.1053    /// </summary>1054    internal sealed class PSToStringBinder : DynamicMetaObjectBinder1055    {1056        private static readonly PSToStringBinder s_binder = new PSToStringBinder();1057 1058        internal static PSToStringBinder Get()1059        {1060            return s_binder;1061        }1062 1063        public override Type ReturnType { get { return typeof(string); } }1064 1065        private PSToStringBinder()1066        {1067        }1068 1069        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)1070        {1071            // target is the object to convert1072            // args[0] is the ExecutionContext, needed to get $OFS, and possibly the type table.1073 1074            if (!target.HasValue || !args[0].HasValue)1075            {1076                return Defer(target, args).WriteToDebugLog(this);1077            }1078 1079            if (target.Value is PSObject && (PSObject.Base(target.Value) != target.Value))1080            {1081                return this.DeferForPSObject(target, args[0]).WriteToDebugLog(this);1082            }1083 1084            var restrictions = target.PSGetTypeRestriction();1085            if (target.LimitType == typeof(string))1086            {1087                return (new DynamicMetaObject(1088                    target.Expression.Cast(typeof(string)),1089                    restrictions)).WriteToDebugLog(this);1090            }1091 1092            // PSObject.ToStringParser will handle everything, but if you want to speed up conversion to string,1093            // add special cases here.1094 1095            return (new DynamicMetaObject(1096                InvokeToString(args[0].Expression, target.Expression),1097                restrictions)).WriteToDebugLog(this);1098        }1099 1100        internal static Expression InvokeToString(Expression context, Expression target)1101        {1102            if (target.Type == typeof(string))1103            {1104                return target;1105            }1106 1107            // PSObject.ToStringParser will handle everything, but if you want to speed up conversion to string,1108            // add special cases here.1109 1110            return Expression.Call(CachedReflectionInfo.PSObject_ToStringParser,1111                                   context.Cast(typeof(ExecutionContext)),1112                                   target.Cast(typeof(object)));1113        }1114    }1115 1116    /// <summary>1117    /// This binder is used to optimize the conversion of the result.1118    /// </summary>1119    internal sealed class PSPipelineResultToBoolBinder : DynamicMetaObjectBinder1120    {1121        private static readonly PSPipelineResultToBoolBinder s_binder = new PSPipelineResultToBoolBinder();1122 1123        internal static PSPipelineResultToBoolBinder Get()1124        {1125            return s_binder;1126        }1127 1128        private PSPipelineResultToBoolBinder()1129        {1130        }1131 1132        public override Type ReturnType { get { return typeof(bool); } }1133 1134        public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args)1135        {1136            if (!target.HasValue)1137            {1138                return Defer(target).WriteToDebugLog(this);1139            }1140 1141            IList pipelineResult = target.Value as IList;1142            Diagnostics.Assert(pipelineResult != null, "Pipeline result is always an IList");1143 1144            var ilistExpr = target.Expression;1145            if (typeof(IList) != ilistExpr.Type)1146            {1147                ilistExpr = Expression.Convert(ilistExpr, typeof(IList));1148            }1149 1150            var countExpr = Expression.Property(1151                Expression.Convert(ilistExpr, typeof(ICollection)), CachedReflectionInfo.ICollection_Count);1152 1153            Expression result;1154            BindingRestrictions restrictions;1155            switch (pipelineResult.Count)1156            {1157                case 0:1158                    result = ExpressionCache.Constant(false);1159                    restrictions = BindingRestrictions.GetExpressionRestriction(1160                        Expression.Equal(countExpr, ExpressionCache.Constant(0)));1161                    break;1162                case 1:1163                    result = Expression.Call(ilistExpr,1164                                             CachedReflectionInfo.IList_get_Item,1165                                             ExpressionCache.Constant(0)).Convert(typeof(bool));1166                    restrictions = BindingRestrictions.GetExpressionRestriction(1167                        Expression.Equal(countExpr, ExpressionCache.Constant(1)));1168                    break;1169                default:1170                    result = ExpressionCache.Constant(true);1171                    restrictions = BindingRestrictions.GetExpressionRestriction(1172                        Expression.GreaterThan(countExpr, ExpressionCache.Constant(1)));1173                    break;1174            }1175 1176            return (new DynamicMetaObject(result, restrictions)).WriteToDebugLog(this);1177        }1178    }1179 1180    internal sealed class PSInvokeDynamicMemberBinder : DynamicMetaObjectBinder1181    {1182        private sealed class KeyComparer : IEqualityComparer<PSInvokeDynamicMemberBinderKeyType>1183        {1184            public bool Equals(PSInvokeDynamicMemberBinderKeyType x, PSInvokeDynamicMemberBinderKeyType y)1185            {1186                return x.Item1.Equals(y.Item1) &&1187                       ((x.Item2 == null) ? y.Item2 == null : x.Item2.Equals(y.Item2)) &&1188                       x.Item3 == y.Item3 &&1189                       x.Item4 == y.Item4 &&1190                       x.Item5 == y.Item5;1191            }1192 1193            public int GetHashCode(PSInvokeDynamicMemberBinderKeyType obj)1194            {1195                return obj.GetHashCode();1196            }1197        }1198 1199        private static readonly1200            Dictionary<PSInvokeDynamicMemberBinderKeyType, PSInvokeDynamicMemberBinder> s_binderCache

Showing the first 1,200 of 7952 lines. Download the file for the rest.