MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Globalization;7using System.Linq;8using System.Management.Automation.Language;9using System.Management.Automation.Runspaces;10using System.Reflection;11using System.Text;12using System.Text.RegularExpressions;13 14using Microsoft.PowerShell.Commands;15 16using CimClass = Microsoft.Management.Infrastructure.CimClass;17using CimInstance = Microsoft.Management.Infrastructure.CimInstance;18 19namespace System.Management.Automation20{21 /// <summary>22 /// Enum describing permissions to use runtime evaluation during type inference.23 /// </summary>24 public enum TypeInferenceRuntimePermissions25 {26 /// <summary>27 /// No runtime use is allowed.28 /// </summary>29 None = 0,30 31 /// <summary>32 /// Use of SafeExprEvaluator visitor is allowed.33 /// </summary>34 AllowSafeEval = 1,35 }36 37 /// <summary>38 /// Static class containing methods to work with type inference of abstract syntax trees.39 /// </summary>40 internal static class AstTypeInference41 {42 /// <summary>43 /// Infers the type that the result of executing a statement would have without using runtime safe eval.44 /// </summary>45 /// <param name="ast">The ast to infer the type from.</param>46 /// <returns>List of inferred typenames.</returns>47 public static IList<PSTypeName> InferTypeOf(Ast ast)48 {49 return InferTypeOf(ast, TypeInferenceRuntimePermissions.None);50 }51 52 /// <summary>53 /// Infers the type that the result of executing a statement would have.54 /// </summary>55 /// <param name="ast">The ast to infer the type from.</param>56 /// <param name="evalPermissions">The runtime usage permissions allowed during type inference.</param>57 /// <returns>List of inferred typenames.</returns>58 public static IList<PSTypeName> InferTypeOf(Ast ast, TypeInferenceRuntimePermissions evalPermissions)59 {60 return InferTypeOf(ast, PowerShell.Create(RunspaceMode.CurrentRunspace), evalPermissions);61 }62 63 /// <summary>64 /// Infers the type that the result of executing a statement would have without using runtime safe eval.65 /// </summary>66 /// <param name="ast">The ast to infer the type from.</param>67 /// <param name="powerShell">The instance of powershell to use for expression evaluation needed for type inference.</param>68 /// <returns>List of inferred typenames.</returns>69 public static IList<PSTypeName> InferTypeOf(Ast ast, PowerShell powerShell)70 {71 return InferTypeOf(ast, powerShell, TypeInferenceRuntimePermissions.None);72 }73 74 /// <summary>75 /// Infers the type that the result of executing a statement would have.76 /// </summary>77 /// <param name="ast">The ast to infer the type from.</param>78 /// <param name="powerShell">The instance of powershell to user for expression evaluation needed for type inference.</param>79 /// <param name="evalPersmissions">The runtime usage permissions allowed during type inference.</param>80 /// <returns>List of inferred typenames.</returns>81 public static IList<PSTypeName> InferTypeOf(Ast ast, PowerShell powerShell, TypeInferenceRuntimePermissions evalPersmissions)82 {83 var context = new TypeInferenceContext(powerShell);84 return InferTypeOf(ast, context, evalPersmissions);85 }86 87 /// <summary>88 /// Infers the type that the result of executing a statement would have.89 /// </summary>90 /// <param name="ast">The ast to infer the type from.</param>91 /// <param name="context">The current type inference context.</param>92 /// <param name="evalPersmissions">The runtime usage permissions allowed during type inference.</param>93 /// <returns>List of inferred typenames.</returns>94 internal static IList<PSTypeName> InferTypeOf(95 Ast ast,96 TypeInferenceContext context,97 TypeInferenceRuntimePermissions evalPersmissions = TypeInferenceRuntimePermissions.None)98 {99 var originalRuntimePermissions = context.RuntimePermissions;100 try101 {102 context.RuntimePermissions = evalPersmissions;103 return context.InferType(ast, new TypeInferenceVisitor(context)).Distinct(new PSTypeNameComparer()).ToList();104 }105 finally106 {107 context.RuntimePermissions = originalRuntimePermissions;108 }109 }110 }111 112 internal class PSTypeNameComparer : IEqualityComparer<PSTypeName>113 {114 public bool Equals(PSTypeName x, PSTypeName y)115 {116 return x.Name.Equals(y.Name);117 }118 119 public int GetHashCode(PSTypeName obj)120 {121 return obj.Name.GetHashCode();122 }123 }124 125 internal class TypeInferenceContext126 {127 public static readonly PSTypeName[] EmptyPSTypeNameArray = Array.Empty<PSTypeName>();128 private readonly PowerShell _powerShell;129 130 public TypeInferenceContext() : this(PowerShell.Create(RunspaceMode.CurrentRunspace))131 {132 }133 134 /// <summary>135 /// Initializes a new instance of the <see cref="TypeInferenceContext"/> class.136 /// The powerShell instance passed need to have a non null Runspace.137 /// </summary>138 /// <param name="powerShell">The instance of powershell to use for expression evaluation needed for type inference.</param>139 public TypeInferenceContext(PowerShell powerShell)140 {141 Diagnostics.Assert(powerShell.Runspace != null, "Callers are required to ensure we have a runspace");142 _powerShell = powerShell;143 144 Helper = new PowerShellExecutionHelper(powerShell);145 }146 147 // used to infer types in script properties attached to an object,148 // to be able to determine the type of $this in the scripts properties149 public PSTypeName CurrentThisType { get; set; }150 151 public TypeDefinitionAst CurrentTypeDefinitionAst { get; set; }152 153 public HashSet<IParameterMetadataProvider> AnalyzedCommands { get; } = new HashSet<IParameterMetadataProvider>();154 155 public TypeInferenceRuntimePermissions RuntimePermissions { get; set; }156 157 internal PowerShellExecutionHelper Helper { get; }158 159 internal ExecutionContext ExecutionContext => _powerShell.Runspace.ExecutionContext;160 161 public bool TryGetRepresentativeTypeNameFromExpressionSafeEval(ExpressionAst expression, out PSTypeName typeName)162 {163 typeName = null;164 if (RuntimePermissions != TypeInferenceRuntimePermissions.AllowSafeEval)165 {166 return false;167 }168 169 return expression != null &&170 SafeExprEvaluator.TrySafeEval(expression, ExecutionContext, out var value) &&171 TryGetRepresentativeTypeNameFromValue(value, out typeName);172 }173 174 internal IList<object> GetMembersByInferredType(PSTypeName typename, bool isStatic, Func<object, bool> filter)175 {176 List<object> results = new List<object>();177 178 Func<object, bool> filterToCall = filter;179 if (typename is PSSyntheticTypeName synthetic)180 {181 foreach (var mem in synthetic.Members)182 {183 results.Add(new PSInferredProperty(mem.Name, mem.PSTypeName));184 }185 }186 187 if (typename.Type != null)188 {189 AddMembersByInferredTypesClrType(typename, isStatic, filter, filterToCall, results);190 }191 else if (typename.TypeDefinitionAst != null)192 {193 AddMembersByInferredTypeDefinitionAst(typename, isStatic, filter, filterToCall, results);194 }195 else196 {197 // Look in the type table first.198 if (!isStatic)199 {200 // The Ciminstance type adapter adds the full typename with and without a namespace to the list of type names.201 // So if we see one with a full typename we need to also get the types for the short version.202 // For example: "CimInstance#root/standardcimv2/MSFT_NetFirewallRule" and "CimInstance#MSFT_NetFirewallRule"203 int namespaceSeparator = typename.Name.LastIndexOf('/');204 ConsolidatedString consolidatedString;205 if (namespaceSeparator != -1206 && typename.Name.StartsWith("Microsoft.Management.Infrastructure.CimInstance#", StringComparison.OrdinalIgnoreCase))207 {208 consolidatedString = new ConsolidatedString(new[]209 {210 typename.Name,211 string.Concat("Microsoft.Management.Infrastructure.CimInstance#", typename.Name.AsSpan(namespaceSeparator + 1))212 });213 }214 else215 {216 consolidatedString = new ConsolidatedString(new[] { typename.Name });217 }218 219 results.AddRange(ExecutionContext.TypeTable.GetMembers<PSMemberInfo>(consolidatedString));220 }221 222 AddMembersByInferredTypeCimType(typename, results, filterToCall);223 }224 225 return results;226 }227 228 internal void AddMembersByInferredTypesClrType(PSTypeName typename, bool isStatic, Func<object, bool> filter, Func<object, bool> filterToCall, List<object> results)229 {230 if (CurrentTypeDefinitionAst == null || CurrentTypeDefinitionAst.Type != typename.Type)231 {232 if (filterToCall == null)233 {234 filterToCall = o => !IsMemberHidden(o);235 }236 else237 {238 filterToCall = o => !IsMemberHidden(o) && filter(o);239 }240 }241 242 IEnumerable<Type> elementTypes;243 if (typename.Type.IsArray)244 {245 elementTypes = new[] { typename.Type.GetElementType() };246 }247 else248 {249 var elementList = new List<Type>();250 foreach (var t in typename.Type.GetInterfaces())251 {252 if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))253 {254 elementList.Add(t);255 }256 }257 258 elementTypes = elementList;259 }260 261 foreach (var type in elementTypes.Prepend(typename.Type))262 {263 // Look in the type table first.264 if (!isStatic)265 {266 var consolidatedString = DotNetAdapter.GetInternedTypeNameHierarchy(type);267 results.AddRange(ExecutionContext.TypeTable.GetMembers<PSMemberInfo>(consolidatedString));268 }269 270 var members = isStatic271 ? PSObject.DotNetStaticAdapter.BaseGetMembers<PSMemberInfo>(type)272 : PSObject.DotNetInstanceAdapter.GetPropertiesAndMethods(type, false);273 274 if (filterToCall != null)275 {276 foreach (var member in members)277 {278 if (filterToCall(member))279 {280 results.Add(member);281 }282 }283 }284 else285 {286 results.AddRange(members);287 }288 }289 }290 291 internal void AddMembersByInferredTypeDefinitionAst(292 PSTypeName typename,293 bool isStatic,294 Func<object, bool> filter,295 Func<object, bool> filterToCall,296 List<object> results)297 {298 if (CurrentTypeDefinitionAst != typename.TypeDefinitionAst)299 {300 if (filterToCall == null)301 {302 filterToCall = o => !IsMemberHidden(o);303 }304 else305 {306 filterToCall = o => !IsMemberHidden(o) && filter(o);307 }308 }309 310 bool foundConstructor = false;311 foreach (var member in typename.TypeDefinitionAst.Members)312 {313 bool add;314 if (member is PropertyMemberAst propertyMember)315 {316 add = propertyMember.IsStatic == isStatic;317 }318 else319 {320 var functionMember = (FunctionMemberAst)member;321 add = (functionMember.IsConstructor && isStatic) || (!functionMember.IsConstructor && functionMember.IsStatic == isStatic);322 foundConstructor |= functionMember.IsConstructor;323 }324 325 if (filterToCall != null && add)326 {327 add = filterToCall(member);328 }329 330 if (add)331 {332 results.Add(member);333 }334 }335 336 // iterate through bases/interfaces337 foreach (var baseType in typename.TypeDefinitionAst.BaseTypes)338 {339 if (!(baseType.TypeName is TypeName baseTypeName))340 {341 continue;342 }343 344 var baseTypeDefinitionAst = baseTypeName._typeDefinitionAst;345 if (baseTypeDefinitionAst is null)346 {347 var baseReflectionType = baseTypeName.GetReflectionType();348 if (baseReflectionType is not null)349 {350 results.AddRange(GetMembersByInferredType(new PSTypeName(baseReflectionType), isStatic, filterToCall));351 }352 }353 else354 {355 results.AddRange(GetMembersByInferredType(new PSTypeName(baseTypeDefinitionAst), isStatic, filterToCall));356 }357 }358 359 // Add stuff from our base class System.Object.360 if (isStatic)361 {362 // Don't add base class constructors363 if (filter == null)364 {365 filterToCall = o => !IsConstructor(o);366 }367 else368 {369 filterToCall = o => !IsConstructor(o) && filter(o);370 }371 372 if (!foundConstructor)373 {374 results.Add(375 new CompilerGeneratedMemberFunctionAst(376 PositionUtilities.EmptyExtent,377 typename.TypeDefinitionAst,378 SpecialMemberFunctionType.DefaultConstructor));379 }380 }381 else382 {383 // Reset the filter because the recursive call will add IsHidden back if necessary.384 filterToCall = filter;385 }386 387 PSTypeName baseMembersType;388 if (typename.TypeDefinitionAst.IsEnum)389 {390 if (!isStatic)391 {392 results.Add(new PSInferredProperty("value__", new PSTypeName(typeof(int))));393 }394 395 baseMembersType = new PSTypeName(typeof(Enum));396 }397 else398 {399 baseMembersType = new PSTypeName(typeof(object));400 }401 402 results.AddRange(GetMembersByInferredType(baseMembersType, isStatic, filterToCall));403 }404 405 internal void AddMembersByInferredTypeCimType(PSTypeName typename, List<object> results, Func<object, bool> filterToCall)406 {407 if (ParseCimCommandsTypeName(typename, out var cimNamespace, out var className))408 {409 var powerShellExecutionHelper = Helper;410 powerShellExecutionHelper.AddCommandWithPreferenceSetting("CimCmdlets\\Get-CimClass")411 .AddParameter("Namespace", cimNamespace)412 .AddParameter("Class", className);413 414 var classes = powerShellExecutionHelper.ExecuteCurrentPowerShell(out _);415 var cimClasses = new List<CimClass>();416 foreach (var c in classes)417 {418 if (PSObject.Base(c) is CimClass cc)419 {420 cimClasses.Add(cc);421 }422 }423 424 foreach (var cimClass in cimClasses)425 {426 if (filterToCall == null)427 {428 results.AddRange(cimClass.CimClassProperties);429 }430 else431 {432 foreach (var prop in cimClass.CimClassProperties)433 {434 if (filterToCall(prop))435 {436 results.Add(prop);437 }438 }439 }440 }441 }442 }443 444 internal IEnumerable<PSTypeName> InferType(Ast ast, TypeInferenceVisitor visitor)445 {446 var res = ast.Accept(visitor);447 Diagnostics.Assert(res != null, "Fix visit methods to not return null");448 return (IEnumerable<PSTypeName>)res;449 }450 451 private static bool TryGetRepresentativeTypeNameFromValue(object value, out PSTypeName type)452 {453 type = null;454 if (value != null)455 {456 if (value is IList list457 && list.Count > 0)458 {459 value = list[0];460 }461 462 value = PSObject.Base(value);463 if (value != null)464 {465 var typeObject = value.GetType();466 467 if (typeObject.FullName.Equals("System.Management.Automation.PSObject", StringComparison.Ordinal))468 {469 var psobjectPropertyList = new List<PSMemberNameAndType>();470 foreach (var property in ((PSObject)value).Properties)471 {472 if (property.IsHidden)473 {474 continue;475 }476 477 var propertyTypeName = new PSTypeName(property.TypeNameOfValue);478 psobjectPropertyList.Add(new PSMemberNameAndType(property.Name, propertyTypeName, property.Value));479 }480 481 type = PSSyntheticTypeName.Create(typeObject, psobjectPropertyList);482 }483 else484 {485 type = new PSTypeName(typeObject);486 }487 488 return true;489 }490 }491 492 return false;493 }494 495 internal static bool ParseCimCommandsTypeName(PSTypeName typename, out string cimNamespace, out string className)496 {497 cimNamespace = null;498 className = null;499 if (typename == null)500 {501 return false;502 }503 504 if (typename.Type != null)505 {506 return false;507 }508 509 var match = Regex.Match(typename.Name, "(?<NetTypeName>.*)#(?<CimNamespace>.*)[/\\\\](?<CimClassName>.*)");510 if (!match.Success)511 {512 return false;513 }514 515 if (!match.Groups["NetTypeName"].Value.EqualsOrdinalIgnoreCase(typeof(CimInstance).FullName))516 {517 return false;518 }519 520 cimNamespace = match.Groups["CimNamespace"].Value;521 className = match.Groups["CimClassName"].Value;522 return true;523 }524 525 private static bool IsMemberHidden(object member)526 {527 switch (member)528 {529 case PSMemberInfo psMemberInfo:530 return psMemberInfo.IsHidden;531 case MemberInfo memberInfo:532 return memberInfo.GetCustomAttributes(typeof(HiddenAttribute), false).Length != 0;533 case PropertyMemberAst propertyMember:534 return propertyMember.IsHidden;535 case FunctionMemberAst functionMember:536 return functionMember.IsHidden;537 }538 539 return false;540 }541 542 private static bool IsConstructor(object member)543 {544 var psMethod = member as PSMethod;545 var methodCacheEntry = psMethod?.adapterData as DotNetAdapter.MethodCacheEntry;546 return methodCacheEntry != null && methodCacheEntry.methodInformationStructures[0].method.IsConstructor;547 }548 }549 550 internal class TypeInferenceVisitor : ICustomAstVisitor2551 {552 private readonly TypeInferenceContext _context;553 554 private static readonly PSTypeName StringPSTypeName = new PSTypeName(typeof(string));555 556 public TypeInferenceVisitor(TypeInferenceContext context)557 {558 _context = context;559 }560 561 private IEnumerable<PSTypeName> InferTypes(Ast ast)562 {563 return _context.InferType(ast, this);564 }565 566 object ICustomAstVisitor.VisitTypeExpression(TypeExpressionAst typeExpressionAst)567 {568 return new[] { new PSTypeName(typeExpressionAst.StaticType) };569 }570 571 object ICustomAstVisitor.VisitMemberExpression(MemberExpressionAst memberExpressionAst)572 {573 return InferTypesFrom(memberExpressionAst);574 }575 576 object ICustomAstVisitor.VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst)577 {578 return InferTypesFrom(invokeMemberExpressionAst);579 }580 581 object ICustomAstVisitor.VisitArrayExpression(ArrayExpressionAst arrayExpressionAst)582 {583 if (arrayExpressionAst.SubExpression.Statements.Count == 0)584 {585 return new[] { new PSTypeName(typeof(object[])) };586 }587 588 return new[] { GetArrayType(InferTypes(arrayExpressionAst.SubExpression)) };589 }590 591 object ICustomAstVisitor.VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst)592 {593 var inferredElementTypes = new List<PSTypeName>();594 foreach (ExpressionAst expression in arrayLiteralAst.Elements)595 {596 inferredElementTypes.AddRange(InferTypes(expression));597 }598 599 return new[] { GetArrayType(inferredElementTypes) };600 }601 602 object ICustomAstVisitor.VisitHashtable(HashtableAst hashtableAst)603 {604 if (hashtableAst.KeyValuePairs.Count > 0)605 {606 var properties = new List<PSMemberNameAndType>();607 void AddInferredTypes(Ast ast, string keyName)608 {609 bool foundAnyTypes = false;610 foreach (PSTypeName item in InferTypes(ast))611 {612 foundAnyTypes = true;613 properties.Add(new PSMemberNameAndType(keyName, item));614 }615 616 if (!foundAnyTypes)617 {618 properties.Add(new PSMemberNameAndType(keyName, new PSTypeName("System.Object")));619 }620 }621 622 foreach (var kv in hashtableAst.KeyValuePairs)623 {624 string name = null;625 if (kv.Item1 is StringConstantExpressionAst stringConstantExpressionAst)626 {627 name = stringConstantExpressionAst.Value;628 }629 else if (kv.Item1 is ConstantExpressionAst constantExpressionAst)630 {631 name = constantExpressionAst.Value.ToString();632 }633 else if (SafeExprEvaluator.TrySafeEval(kv.Item1, _context.ExecutionContext, out object nameValue))634 {635 name = nameValue.ToString();636 }637 638 if (name is not null)639 {640 if (kv.Item2 is PipelineAst pipelineAst && pipelineAst.GetPureExpression() is ExpressionAst expression)641 {642 object value;643 if (expression is ConstantExpressionAst constant)644 {645 value = constant.Value;646 }647 else648 {649 _ = SafeExprEvaluator.TrySafeEval(expression, _context.ExecutionContext, out value);650 }651 652 if (value is null)653 {654 AddInferredTypes(expression, name);655 continue;656 }657 658 PSTypeName valueType = new(value.GetType());659 properties.Add(new PSMemberNameAndType(name, valueType, value));660 }661 else662 {663 AddInferredTypes(kv.Item2, name);664 }665 }666 }667 668 return new[] { PSSyntheticTypeName.Create(typeof(Hashtable), properties) };669 }670 671 return new[] { new PSTypeName(typeof(Hashtable)) };672 }673 674 object ICustomAstVisitor.VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst)675 {676 return new[] { new PSTypeName(typeof(ScriptBlock)) };677 }678 679 object ICustomAstVisitor.VisitParenExpression(ParenExpressionAst parenExpressionAst)680 {681 return parenExpressionAst.Pipeline.Accept(this);682 }683 684 object ICustomAstVisitor.VisitExpandableStringExpression(ExpandableStringExpressionAst expandableStringExpressionAst)685 {686 return new[] { StringPSTypeName };687 }688 689 object ICustomAstVisitor.VisitIndexExpression(IndexExpressionAst indexExpressionAst)690 {691 return InferTypeFrom(indexExpressionAst);692 }693 694 object ICustomAstVisitor.VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst)695 {696 return attributedExpressionAst.Child.Accept(this);697 }698 699 object ICustomAstVisitor.VisitBlockStatement(BlockStatementAst blockStatementAst)700 {701 return blockStatementAst.Body.Accept(this);702 }703 704 object ICustomAstVisitor.VisitUsingExpression(UsingExpressionAst usingExpressionAst)705 {706 return usingExpressionAst.SubExpression.Accept(this);707 }708 709 object ICustomAstVisitor.VisitVariableExpression(VariableExpressionAst ast)710 {711 var inferredTypes = new List<PSTypeName>();712 InferTypeFrom(ast, inferredTypes);713 return inferredTypes;714 }715 716 object ICustomAstVisitor.VisitMergingRedirection(MergingRedirectionAst mergingRedirectionAst)717 {718 return TypeInferenceContext.EmptyPSTypeNameArray;719 }720 721 object ICustomAstVisitor.VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)722 {723 switch (binaryExpressionAst.Operator)724 {725 case TokenKind.And:726 case TokenKind.Ccontains:727 case TokenKind.Cin:728 case TokenKind.Cnotcontains:729 case TokenKind.Cnotin:730 case TokenKind.Icontains:731 case TokenKind.Iin:732 case TokenKind.Inotcontains:733 case TokenKind.Inotin:734 case TokenKind.Is:735 case TokenKind.IsNot:736 case TokenKind.Or:737 case TokenKind.Xor:738 // Always returns a bool739 return BinaryExpressionAst.BoolTypeNameArray;740 741 case TokenKind.As:742 // TODO: Handle other kinds of expressions on the right side.743 if (binaryExpressionAst.Right is TypeExpressionAst typeExpression)744 {745 var type = typeExpression.TypeName.GetReflectionType();746 var psTypeName = type != null ? new PSTypeName(type) : new PSTypeName(typeExpression.TypeName.FullName);747 return new[] { psTypeName };748 }749 break;750 751 case TokenKind.Ceq:752 case TokenKind.Cge:753 case TokenKind.Cgt:754 case TokenKind.Cle:755 case TokenKind.Clike:756 case TokenKind.Clt:757 case TokenKind.Cmatch:758 case TokenKind.Cne:759 case TokenKind.Cnotlike:760 case TokenKind.Cnotmatch:761 case TokenKind.Ieq:762 case TokenKind.Ige:763 case TokenKind.Igt:764 case TokenKind.Ile:765 case TokenKind.Ilike:766 case TokenKind.Ilt:767 case TokenKind.Imatch:768 case TokenKind.Ine:769 case TokenKind.Inotlike:770 case TokenKind.Inotmatch:771 // Returns a bool or filtered output from the left hand side if it's enumerable772 var comparisonOutput = new List<PSTypeName>() { new(typeof(bool)) };773 comparisonOutput.AddRange(InferTypes(binaryExpressionAst.Left));774 return comparisonOutput;775 776 case TokenKind.Creplace:777 case TokenKind.Format:778 case TokenKind.Ireplace:779 case TokenKind.Join:780 // Always returns a string781 return BinaryExpressionAst.StringTypeNameArray;782 783 case TokenKind.Csplit:784 case TokenKind.Isplit:785 // Always returns a string array786 return BinaryExpressionAst.StringArrayTypeNameArray;787 788 case TokenKind.QuestionQuestion:789 // Can return left or right hand side790 var nullCoalescingOutput = InferTypes(binaryExpressionAst.Left).ToList();791 nullCoalescingOutput.AddRange(InferTypes(binaryExpressionAst.Right));792 return nullCoalescingOutput.Distinct();793 794 default:795 break;796 }797 798 List<PSTypeName> lhsTypes = InferTypes(binaryExpressionAst.Left).ToList();799 if (lhsTypes.Count == 0)800 {801 return lhsTypes;802 }803 804 string methodName;805 switch (binaryExpressionAst.Operator)806 {807 case TokenKind.Divide:808 methodName = "op_Division";809 break;810 811 case TokenKind.Minus:812 methodName = "op_Subtraction";813 break;814 815 case TokenKind.Multiply:816 methodName = "op_Multiply";817 break;818 819 case TokenKind.Plus:820 methodName = "op_Addition";821 break;822 823 case TokenKind.Rem:824 methodName = "op_Modulus";825 break;826 827 case TokenKind.Shl:828 methodName = "op_LeftShift";829 break;830 831 case TokenKind.Shr:832 methodName = "op_RightShift";833 break;834 835 default:836 return lhsTypes;837 }838 839 List<PSTypeName> rhsTypes = InferTypes(binaryExpressionAst.Right).ToList();840 HashSet<string> addedReturnTypes = new HashSet<string>();841 List<PSTypeName> result = new List<PSTypeName>();842 foreach (PSTypeName lType in lhsTypes)843 {844 if (lType.Type is null)845 {846 continue;847 }848 849 foreach (MethodInfo method in lType.Type.GetMethods(BindingFlags.Public | BindingFlags.Static))850 {851 if (!method.Name.Equals(methodName, StringComparison.Ordinal))852 {853 continue;854 }855 856 if (rhsTypes.Count == 0)857 {858 if (addedReturnTypes.Add(method.ReturnType.FullName))859 {860 result.Add(new PSTypeName(method.ReturnType));861 }862 863 continue;864 }865 866 ParameterInfo[] methodParams = method.GetParameters();867 if (methodParams.Length != 2)868 {869 continue;870 }871 872 foreach (PSTypeName rType in rhsTypes)873 {874 if (rType.Type is not null && rType.Type.IsAssignableTo(methodParams[1].ParameterType))875 {876 if (addedReturnTypes.Add(method.ReturnType.FullName))877 {878 result.Add(new PSTypeName(method.ReturnType));879 }880 881 break;882 }883 }884 }885 }886 887 if (result.Count == 0)888 {889 result.AddRange(lhsTypes);890 }891 892 return result;893 }894 895 object ICustomAstVisitor.VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst)896 {897 var tokenKind = unaryExpressionAst.TokenKind;898 return (tokenKind == TokenKind.Not || tokenKind == TokenKind.Exclaim)899 ? BinaryExpressionAst.BoolTypeNameArray900 : unaryExpressionAst.Child.Accept(this);901 }902 903 object ICustomAstVisitor.VisitConvertExpression(ConvertExpressionAst convertExpressionAst)904 {905 // The reflection type of PSCustomObject is PSObject, so this covers both the906 // [PSObject] @{ Key = "Value" } and the [PSCustomObject] @{ Key = "Value" } case.907 var type = convertExpressionAst.Type.TypeName.GetReflectionType();908 909 if (type is null && convertExpressionAst.Type.TypeName is TypeName unavailableType && unavailableType._typeDefinitionAst is not null)910 {911 return new[] { new PSTypeName(unavailableType._typeDefinitionAst) };912 }913 914 if (type == typeof(PSObject) && convertExpressionAst.Child is HashtableAst hashtableAst)915 {916 if (InferTypes(hashtableAst).FirstOrDefault() is PSSyntheticTypeName syntheticTypeName)917 {918 return new[] { PSSyntheticTypeName.Create(type, syntheticTypeName.Members) };919 }920 }921 922 var psTypeName = type != null ? new PSTypeName(type) : new PSTypeName(convertExpressionAst.Type.TypeName.FullName);923 return new[] { psTypeName };924 }925 926 object ICustomAstVisitor.VisitConstantExpression(ConstantExpressionAst constantExpressionAst)927 {928 var value = constantExpressionAst.Value;929 return value != null ? new[] { new PSTypeName(value.GetType()) } : TypeInferenceContext.EmptyPSTypeNameArray;930 }931 932 object ICustomAstVisitor.VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst)933 {934 return new[] { StringPSTypeName };935 }936 937 object ICustomAstVisitor.VisitSubExpression(SubExpressionAst subExpressionAst)938 {939 return subExpressionAst.SubExpression.Accept(this);940 }941 942 object ICustomAstVisitor.VisitErrorStatement(ErrorStatementAst errorStatementAst)943 {944 var inferredTypes = new List<PSTypeName>();945 if (errorStatementAst.Conditions is not null)946 {947 foreach (var ast in errorStatementAst.Conditions)948 {949 inferredTypes.AddRange(InferTypes(ast));950 }951 }952 953 if (errorStatementAst.Bodies is not null)954 {955 foreach (var ast in errorStatementAst.Bodies)956 {957 inferredTypes.AddRange(InferTypes(ast));958 }959 }960 961 if (errorStatementAst.NestedAst is not null)962 {963 foreach (var ast in errorStatementAst.NestedAst)964 {965 inferredTypes.AddRange(InferTypes(ast));966 }967 }968 969 return inferredTypes;970 }971 972 object ICustomAstVisitor.VisitErrorExpression(ErrorExpressionAst errorExpressionAst)973 {974 var inferredTypes = new List<PSTypeName>();975 foreach (var ast in errorExpressionAst.NestedAst)976 {977 inferredTypes.AddRange(InferTypes(ast));978 }979 980 return inferredTypes;981 }982 983 object ICustomAstVisitor.VisitScriptBlock(ScriptBlockAst scriptBlockAst)984 {985 var res = new List<PSTypeName>(10);986 var beginBlock = scriptBlockAst.BeginBlock;987 var processBlock = scriptBlockAst.ProcessBlock;988 var endBlock = scriptBlockAst.EndBlock;989 990 // The following is used when we don't find OutputType, which is checked elsewhere.991 if (beginBlock != null)992 {993 res.AddRange(InferTypes(beginBlock));994 }995 996 if (processBlock != null)997 {998 res.AddRange(InferTypes(processBlock));999 }1000 1001 if (endBlock != null)1002 {1003 res.AddRange(InferTypes(endBlock));1004 }1005 1006 return res;1007 }1008 1009 object ICustomAstVisitor.VisitParamBlock(ParamBlockAst paramBlockAst)1010 {1011 return TypeInferenceContext.EmptyPSTypeNameArray;1012 }1013 1014 object ICustomAstVisitor.VisitNamedBlock(NamedBlockAst namedBlockAst)1015 {1016 var inferredTypes = new List<PSTypeName>();1017 for (int index = 0; index < namedBlockAst.Statements.Count; index++)1018 {1019 StatementAst ast = namedBlockAst.Statements[index];1020 if (ast is AssignmentStatementAst1021 || (ast is PipelineAst pipe && pipe.PipelineElements.Count == 1 && pipe.PipelineElements[0] is CommandExpressionAst cmd1022 && cmd.Redirections.Count == 0 && cmd.Expression is UnaryExpressionAst unary1023 && unary.TokenKind is TokenKind.PostfixPlusPlus or TokenKind.PlusPlus or TokenKind.PostfixMinusMinus or TokenKind.MinusMinus))1024 {1025 // Assignments don't output anything to the named block unless they are wrapped in parentheses.1026 // When they are wrapped in parentheses, they are seen as PipelineAst.1027 // Increment/decrement operators like $i++ also don't output anything unless there's a redirection, or they are wrapped in parentheses.1028 continue;1029 }1030 1031 inferredTypes.AddRange(InferTypes(ast));1032 }1033 1034 return inferredTypes;1035 }1036 1037 object ICustomAstVisitor.VisitTypeConstraint(TypeConstraintAst typeConstraintAst)1038 {1039 return TypeInferenceContext.EmptyPSTypeNameArray;1040 }1041 1042 object ICustomAstVisitor.VisitAttribute(AttributeAst attributeAst)1043 {1044 return TypeInferenceContext.EmptyPSTypeNameArray;1045 }1046 1047 object ICustomAstVisitor.VisitNamedAttributeArgument(NamedAttributeArgumentAst namedAttributeArgumentAst)1048 {1049 return TypeInferenceContext.EmptyPSTypeNameArray;1050 }1051 1052 object ICustomAstVisitor.VisitParameter(ParameterAst parameterAst)1053 {1054 var res = new List<PSTypeName>();1055 var attributes = parameterAst.Attributes;1056 bool typeConstraintAdded = false;1057 foreach (var attrib in attributes)1058 {1059 switch (attrib)1060 {1061 case TypeConstraintAst typeConstraint:1062 if (!typeConstraintAdded)1063 {1064 res.Add(new PSTypeName(typeConstraint.TypeName));1065 typeConstraintAdded = true;1066 }1067 1068 break;1069 case AttributeAst attributeAst:1070 PSTypeNameAttribute attribute = null;1071 try1072 {1073 attribute = attributeAst.GetAttribute() as PSTypeNameAttribute;1074 }1075 catch (RuntimeException)1076 {1077 }1078 1079 if (attribute != null)1080 {1081 res.Add(new PSTypeName(attribute.PSTypeName));1082 }1083 1084 break;1085 }1086 }1087 1088 return res;1089 }1090 1091 object ICustomAstVisitor.VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)1092 {1093 return TypeInferenceContext.EmptyPSTypeNameArray;1094 }1095 1096 object ICustomAstVisitor.VisitStatementBlock(StatementBlockAst statementBlockAst)1097 {1098 var inferredTypes = new List<PSTypeName>();1099 foreach (StatementAst ast in statementBlockAst.Statements)1100 {1101 if (ast is AssignmentStatementAst1102 || (ast is PipelineAst pipe && pipe.PipelineElements.Count == 1 && pipe.PipelineElements[0] is CommandExpressionAst cmd1103 && cmd.Redirections.Count == 0 && cmd.Expression is UnaryExpressionAst unary1104 && unary.TokenKind is TokenKind.PostfixPlusPlus or TokenKind.PlusPlus or TokenKind.PostfixMinusMinus or TokenKind.MinusMinus))1105 {1106 // Assignments don't output anything to the statement block unless they are wrapped in parentheses.1107 // When they are wrapped in parentheses, they are seen as PipelineAst.1108 // Increment operators like $i++ also don't output anything unless there's a redirection, or they are wrapped in parentheses.1109 continue;1110 }1111 1112 inferredTypes.AddRange(InferTypes(ast));1113 }1114 1115 return inferredTypes;1116 }1117 1118 object ICustomAstVisitor.VisitIfStatement(IfStatementAst ifStmtAst)1119 {1120 var res = new List<PSTypeName>();1121 1122 foreach (var clause in ifStmtAst.Clauses)1123 {1124 res.AddRange(InferTypes(clause.Item2));1125 }1126 1127 var elseClause = ifStmtAst.ElseClause;1128 if (elseClause != null)1129 {1130 res.AddRange(InferTypes(elseClause));1131 }1132 1133 return res;1134 }1135 1136 object ICustomAstVisitor.VisitTrap(TrapStatementAst trapStatementAst)1137 {1138 return trapStatementAst.Body.Accept(this);1139 }1140 1141 object ICustomAstVisitor.VisitSwitchStatement(SwitchStatementAst switchStatementAst)1142 {1143 var res = new List<PSTypeName>(8);1144 var clauses = switchStatementAst.Clauses;1145 var defaultStatement = switchStatementAst.Default;1146 1147 foreach (var clause in clauses)1148 {1149 res.AddRange(InferTypes(clause.Item2));1150 }1151 1152 if (defaultStatement != null)1153 {1154 res.AddRange(InferTypes(defaultStatement));1155 }1156 1157 return res;1158 }1159 1160 object ICustomAstVisitor.VisitDataStatement(DataStatementAst dataStatementAst)1161 {1162 return dataStatementAst.Body.Accept(this);1163 }1164 1165 object ICustomAstVisitor.VisitForEachStatement(ForEachStatementAst forEachStatementAst)1166 {1167 return forEachStatementAst.Body.Accept(this);1168 }1169 1170 object ICustomAstVisitor.VisitDoWhileStatement(DoWhileStatementAst doWhileStatementAst)1171 {1172 return doWhileStatementAst.Body.Accept(this);1173 }1174 1175 object ICustomAstVisitor.VisitForStatement(ForStatementAst forStatementAst)1176 {1177 return forStatementAst.Body.Accept(this);1178 }1179 1180 object ICustomAstVisitor.VisitWhileStatement(WhileStatementAst whileStatementAst)1181 {1182 return whileStatementAst.Body.Accept(this);1183 }1184 1185 object ICustomAstVisitor.VisitCatchClause(CatchClauseAst catchClauseAst)1186 {1187 return catchClauseAst.Body.Accept(this);1188 }1189 1190 object ICustomAstVisitor.VisitTryStatement(TryStatementAst tryStatementAst)1191 {1192 var res = new List<PSTypeName>(5);1193 res.AddRange(InferTypes(tryStatementAst.Body));1194 foreach (var catchClauseAst in tryStatementAst.CatchClauses)1195 {1196 res.AddRange(InferTypes(catchClauseAst));1197 }1198 1199 if (tryStatementAst.Finally != null)1200 {