Adding support for Typeinference based on runtime variable values (#2744)

* Refactored the type inference code in preparation to make it a public api.
* Added type inference tests

Fixes #2567
This commit is contained in:
Staffan Gustafsson 2017-06-08 00:52:33 +02:00 committed by Jason Shirk
parent a164609eea
commit a52adcd3cf
10 changed files with 2732 additions and 1479 deletions

4
.gitignore vendored
View file

@ -65,3 +65,7 @@ gen
# TestsResults
TestsResults*.xml
# Resharper settings
PowerShell.sln.DotSettings.user

View file

@ -641,7 +641,7 @@ namespace System.Management.Automation
char quote;
var lastword = LastWordFinder.FindLastWord(legacyInput, out replacementIndex, out quote);
replacementLength = legacyInput.Length - replacementIndex;
var helper = new CompletionExecutionHelper(powershell);
var helper = new PowerShellExecutionHelper(powershell);
powershell.AddCommand("TabExpansion").AddArgument(legacyInput).AddArgument(lastword);
@ -749,7 +749,7 @@ namespace System.Management.Automation
/// <param name="quote"></param>
/// <param name="completingAtStartOfLine"></param>
/// <returns></returns>
internal static List<CompletionResult> PSv2GenerateMatchSetOfCmdlets(CompletionExecutionHelper helper, string lastWord, string quote, bool completingAtStartOfLine)
internal static List<CompletionResult> PSv2GenerateMatchSetOfCmdlets(PowerShellExecutionHelper helper, string lastWord, string quote, bool completingAtStartOfLine)
{
var results = new List<CompletionResult>();
bool isSnapinSpecified;
@ -864,7 +864,7 @@ namespace System.Management.Automation
#region "Handle File Names"
internal static List<CompletionResult> PSv2GenerateMatchSetOfFiles(CompletionExecutionHelper helper, string lastWord, bool completingAtStartOfLine, string quote)
internal static List<CompletionResult> PSv2GenerateMatchSetOfFiles(PowerShellExecutionHelper helper, string lastWord, bool completingAtStartOfLine, string quote)
{
var results = new List<CompletionResult>();
@ -926,7 +926,7 @@ namespace System.Management.Automation
bool? isContainer = SafeGetProperty<bool?>(combinedMatch.Item, "PSIsContainer");
string childName = SafeGetProperty<string>(combinedMatch.Item, "PSChildName");
string toolTip = CompletionExecutionHelper.SafeToString(combinedMatch.ConvertedPath);
string toolTip = PowerShellExecutionHelper.SafeToString(combinedMatch.ConvertedPath);
if (isContainer != null && childName != null && toolTip != null)
{
@ -1034,7 +1034,7 @@ namespace System.Management.Automation
return default(T);
}
private static bool PSv2ShouldFullyQualifyPathsPath(CompletionExecutionHelper helper, string lastWord)
private static bool PSv2ShouldFullyQualifyPathsPath(PowerShellExecutionHelper helper, string lastWord)
{
// These are special cases, as they represent cases where the user expects to
// see the full path.
@ -1068,7 +1068,7 @@ namespace System.Management.Automation
}
}
private static List<PathItemAndConvertedPath> PSv2FindMatches(CompletionExecutionHelper helper, string path, bool shouldFullyQualifyPaths)
private static List<PathItemAndConvertedPath> PSv2FindMatches(PowerShellExecutionHelper helper, string path, bool shouldFullyQualifyPaths)
{
Diagnostics.Assert(!String.IsNullOrEmpty(path), "path should have a value");
var result = new List<PathItemAndConvertedPath>();
@ -1113,9 +1113,9 @@ namespace System.Management.Automation
}
result.Add(new PathItemAndConvertedPath(
CompletionExecutionHelper.SafeToString(objectPath),
PowerShellExecutionHelper.SafeToString(objectPath),
item,
CompletionExecutionHelper.SafeToString(convertedPath)));
PowerShellExecutionHelper.SafeToString(convertedPath)));
}
}

View file

@ -24,7 +24,7 @@ namespace System.Management.Automation
internal Token TokenBeforeCursor { get; set; }
internal IScriptPosition CursorPosition { get; set; }
internal CompletionExecutionHelper Helper { get; set; }
internal PowerShellExecutionHelper Helper { get; set; }
internal Hashtable Options { get; set; }
internal Dictionary<string, ScriptBlock> CustomArgumentCompleters { get; set; }
internal Dictionary<string, ScriptBlock> NativeArgumentCompleters { get; set; }
@ -33,7 +33,7 @@ namespace System.Management.Automation
internal int ReplacementLength { get; set; }
internal ExecutionContext ExecutionContext { get; set; }
internal PseudoBindingInfo PseudoBindingInfo { get; set; }
internal TypeDefinitionAst CurrentTypeDefinitionAst { get; set; }
internal TypeInferenceContext TypeInferenceContext { get; set; }
internal bool GetOption(string option, bool @default)
{
@ -96,15 +96,26 @@ namespace System.Management.Automation
return cursor.Offset < extent.StartOffset || cursor.Offset > extent.EndOffset;
}
internal CompletionContext CreateCompletionContext(ExecutionContext executionContext)
internal CompletionContext CreateCompletionContext(PowerShell powerShell)
{
var typeInferenceContext = new TypeInferenceContext(powerShell);
return InitializeCompletionContext(typeInferenceContext);
}
internal CompletionContext CreateCompletionContext(TypeInferenceContext typeInferenceContext)
{
return InitializeCompletionContext(typeInferenceContext);
}
private CompletionContext InitializeCompletionContext(TypeInferenceContext typeInferenceContext)
{
Token tokenBeforeCursor = null;
IScriptPosition positionForAstSearch = _cursorPosition;
var adjustLineAndColumn = false;
var tokenAtCursor = _tokens.LastOrDefault(token => IsCursorWithinOrJustAfterExtent(_cursorPosition, token.Extent) && IsInterestingToken(token));
var tokenAtCursor = InterstingTokenAtCursorOrDefault(_tokens, _cursorPosition);
if (tokenAtCursor == null)
{
tokenBeforeCursor = _tokens.LastOrDefault(token => IsCursorAfterExtent(_cursorPosition, token.Extent) && IsInterestingToken(token));
tokenBeforeCursor = InterstingTokenBeforeCursorOrDefault(_tokens, _cursorPosition);
if (tokenBeforeCursor != null)
{
positionForAstSearch = tokenBeforeCursor.Extent.EndScriptPosition;
@ -114,17 +125,22 @@ namespace System.Management.Automation
else
{
var stringExpandableToken = tokenAtCursor as StringExpandableToken;
if (stringExpandableToken != null && stringExpandableToken.NestedTokens != null)
if (stringExpandableToken?.NestedTokens != null)
{
tokenAtCursor =
stringExpandableToken.NestedTokens.LastOrDefault(
token => IsCursorWithinOrJustAfterExtent(_cursorPosition, token.Extent) && IsInterestingToken(token)) ?? stringExpandableToken;
tokenAtCursor = InterstingTokenAtCursorOrDefault(stringExpandableToken.NestedTokens, _cursorPosition) ?? stringExpandableToken;
}
}
var asts = AstSearcher.FindAll(_ast, ast => IsCursorWithinOrJustAfterExtent(positionForAstSearch, ast.Extent), searchNestedScriptBlocks: true).ToList();
Diagnostics.Assert(tokenAtCursor == null || tokenBeforeCursor == null, "Only one of these tokens can be non-null");
if (typeInferenceContext.CurrentTypeDefinitionAst == null)
{
typeInferenceContext.CurrentTypeDefinitionAst = Ast.GetAncestorTypeDefinitionAst(asts.Last());
}
ExecutionContext executionContext = typeInferenceContext.ExecutionContext;
return new CompletionContext
{
TokenAtCursor = tokenAtCursor,
@ -134,12 +150,24 @@ namespace System.Management.Automation
Options = _options,
ExecutionContext = executionContext,
ReplacementIndex = adjustLineAndColumn ? _cursorPosition.Offset : 0,
CurrentTypeDefinitionAst = Ast.GetAncestorTypeDefinitionAst(asts.Last()),
TypeInferenceContext = typeInferenceContext,
Helper = typeInferenceContext.Helper,
CustomArgumentCompleters = executionContext.CustomArgumentCompleters,
NativeArgumentCompleters = executionContext.NativeArgumentCompleters,
};
}
private static Token InterstingTokenAtCursorOrDefault(IEnumerable<Token> tokens, IScriptPosition cursorPosition)
{
return tokens.LastOrDefault(token => IsCursorWithinOrJustAfterExtent(cursorPosition, token.Extent) && IsInterestingToken(token));
}
private static Token InterstingTokenBeforeCursorOrDefault(IEnumerable<Token> tokens, IScriptPosition cursorPosition)
{
return tokens.LastOrDefault(token => IsCursorAfterExtent(cursorPosition, token.Extent) && IsInterestingToken(token));
}
private static Ast GetLastAstAtCursor(ScriptBlockAst scriptBlockAst, IScriptPosition cursorPosition)
{
var asts = AstSearcher.FindAll(scriptBlockAst, ast => IsCursorRightAfterExtent(cursorPosition, ast.Extent), searchNestedScriptBlocks: true);
@ -278,8 +306,7 @@ namespace System.Management.Automation
internal List<CompletionResult> GetResults(PowerShell powerShell, out int replacementIndex, out int replacementLength)
{
var completionContext = CreateCompletionContext(powerShell.GetContextFromTLS());
completionContext.Helper = new CompletionExecutionHelper(powerShell);
var completionContext = CreateCompletionContext(powerShell);
PSLanguageMode? previousLanguageMode = null;
try
@ -1173,8 +1200,7 @@ namespace System.Management.Automation
cursorIndexInString = strValue.Length;
var analysis = new CompletionAnalysis(_ast, _tokens, _cursorPosition, _options);
var subContext = analysis.CreateCompletionContext(completionContext.ExecutionContext);
subContext.Helper = completionContext.Helper;
var subContext = analysis.CreateCompletionContext(completionContext.TypeInferenceContext);
int subReplaceIndex, subReplaceLength;
var subResult = analysis.GetResultHelper(subContext, out subReplaceIndex, out subReplaceLength, true);

View file

@ -75,7 +75,7 @@ namespace System.Management.Automation
return CommandCompletion.EmptyCompletionResult;
}
var helper = new CompletionExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace));
var helper = new PowerShellExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace));
return CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = helper }, moduleName, commandTypes);
}
@ -104,8 +104,8 @@ namespace System.Management.Automation
lastAst = context.RelatedAsts.Last();
}
var powershell = context.Helper.CurrentPowerShell;
AddCommandWithPreferenceSetting(powershell, "Get-Command", typeof(GetCommandCommand))
var powershell = context.Helper
.AddCommandWithPreferenceSetting("Get-Command", typeof(GetCommandCommand))
.AddParameter("All")
.AddParameter("Name", commandName);
@ -169,8 +169,8 @@ namespace System.Management.Automation
moduleName = commandName.Substring(0, indexOfFirstBackslash);
commandName = commandName.Substring(indexOfFirstBackslash + 1);
var powershell = context.Helper.CurrentPowerShell;
AddCommandWithPreferenceSetting(powershell, "Get-Command", typeof(GetCommandCommand))
var powershell = context.Helper
.AddCommandWithPreferenceSetting("Get-Command", typeof(GetCommandCommand))
.AddParameter("All")
.AddParameter("Name", commandName)
.AddParameter("Module", moduleName);
@ -443,8 +443,7 @@ namespace System.Management.Automation
moduleName += "*";
}
var powershell = context.Helper.CurrentPowerShell;
AddCommandWithPreferenceSetting(powershell, "Get-Module", typeof(GetModuleCommand)).AddParameter("Name", moduleName);
var powershell = context.Helper.AddCommandWithPreferenceSetting("Get-Module", typeof(GetModuleCommand)).AddParameter("Name", moduleName);
if (!loadedModulesOnly)
{
powershell.AddParameter("ListAvailable", true);
@ -1893,7 +1892,7 @@ namespace System.Management.Automation
}
}
foreach (PSTypeName typeName in argumentAst.GetInferredType(context))
foreach (PSTypeName typeName in AstTypeInference.InferTypeOf(argumentAst, context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval))
{
yield return typeName;
}
@ -2445,35 +2444,6 @@ namespace System.Management.Automation
}
}
private static bool NativeCompletionCimCommands_ParseTypeName(PSTypeName typename, out string cimNamespace, out string className)
{
cimNamespace = null;
className = null;
if (typename == null)
{
return false;
}
if (typename.Type != null)
{
return false;
}
var match = Regex.Match(typename.Name, "(?<NetTypeName>.*)#(?<CimNamespace>.*)[/\\\\](?<CimClassName>.*)");
if (!match.Success)
{
return false;
}
if (!match.Groups["NetTypeName"].Value.Equals(typeof(CimInstance).FullName, StringComparison.OrdinalIgnoreCase))
{
return false;
}
cimNamespace = match.Groups["CimNamespace"].Value;
className = match.Groups["CimClassName"].Value;
return true;
}
private static void NativeCompletionCimCommands(
string parameter,
Dictionary<string, AstParameterArgumentPair> boundArguments,
@ -2535,7 +2505,7 @@ namespace System.Management.Automation
{
foreach (PSTypeName typeName in cimClassTypeNames)
{
if (NativeCompletionCimCommands_ParseTypeName(typeName, out pseudoboundCimNamespace, out pseudoboundClassName))
if (TypeInferenceContext.ParseCimCommandsTypeName(typeName, out pseudoboundCimNamespace, out pseudoboundClassName))
{
if (parameter.Equals("ResultClassName", StringComparison.OrdinalIgnoreCase))
{
@ -2905,11 +2875,11 @@ namespace System.Management.Automation
}
var pattern = WildcardPattern.Get(logName, WildcardOptions.IgnoreCase);
var powershell = context.Helper.CurrentPowerShell;
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-EventLog").AddParameter("LogName", "*");
var powerShellExecutionHelper = context.Helper;
var powershell = powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-EventLog").AddParameter("LogName", "*");
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects != null)
{
@ -2948,7 +2918,6 @@ namespace System.Management.Automation
wordToComplete = wordToComplete ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref wordToComplete);
var powershell = context.Helper.CurrentPowerShell;
if (!wordToComplete.EndsWith("*", StringComparison.Ordinal))
{
@ -2956,17 +2925,13 @@ namespace System.Management.Automation
}
var pattern = WildcardPattern.Get(wordToComplete, WildcardOptions.IgnoreCase);
if (paramName.Equals("Name", StringComparison.OrdinalIgnoreCase))
{
AddCommandWithPreferenceSetting(powershell, "Get-Job", typeof(GetJobCommand)).AddParameter("Name", wordToComplete);
}
else
{
AddCommandWithPreferenceSetting(powershell, "Get-Job", typeof(GetJobCommand)).AddParameter("IncludeChildJob", true);
}
var paramIsName = paramName.Equals("Name", StringComparison.OrdinalIgnoreCase);
var (parameterName, value) = paramIsName ? ("Name", wordToComplete) : ("IncludeChildJob", (object)true);
var powerShellExecutionHelper = context.Helper;
powerShellExecutionHelper.AddCommandWithPreferenceSetting("Get-Job", typeof(GetJobCommand)).AddParameter(parameterName, value);
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects == null)
return;
@ -3004,7 +2969,7 @@ namespace System.Management.Automation
result.Add(CompletionResult.Null);
}
else if (paramName.Equals("Name", StringComparison.OrdinalIgnoreCase))
else if (paramIsName)
{
RemoveLastNullCompletionResult(result);
@ -3039,7 +3004,6 @@ namespace System.Management.Automation
wordToComplete = wordToComplete ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref wordToComplete);
var powershell = context.Helper.CurrentPowerShell;
if (!wordToComplete.EndsWith("*", StringComparison.Ordinal))
{
@ -3047,17 +3011,18 @@ namespace System.Management.Automation
}
var pattern = WildcardPattern.Get(wordToComplete, WildcardOptions.IgnoreCase);
var powerShellExecutionHelper = context.Helper;
if (paramName.Equals("Name", StringComparison.OrdinalIgnoreCase))
{
AddCommandWithPreferenceSetting(powershell, "PSScheduledJob\\Get-ScheduledJob").AddParameter("Name", wordToComplete);
powerShellExecutionHelper.AddCommandWithPreferenceSetting("PSScheduledJob\\Get-ScheduledJob").AddParameter("Name", wordToComplete);
}
else
{
AddCommandWithPreferenceSetting(powershell, "PSScheduledJob\\Get-ScheduledJob");
powerShellExecutionHelper.AddCommandWithPreferenceSetting("PSScheduledJob\\Get-ScheduledJob");
}
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects == null)
return;
@ -3165,24 +3130,24 @@ namespace System.Management.Automation
wordToComplete = wordToComplete ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref wordToComplete);
var powershell = context.Helper.CurrentPowerShell;
if (!wordToComplete.EndsWith("*", StringComparison.Ordinal))
{
wordToComplete += "*";
}
var powerShellExecutionHelper = context.Helper;
if (paramName.Equals("Id", StringComparison.OrdinalIgnoreCase))
{
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-Process");
powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-Process");
}
else
{
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-Process").AddParameter("Name", wordToComplete);
powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-Process").AddParameter("Name", wordToComplete);
}
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects == null)
return;
@ -3250,16 +3215,16 @@ namespace System.Management.Automation
providerName = providerName ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref providerName);
var powershell = context.Helper.CurrentPowerShell;
if (!providerName.EndsWith("*", StringComparison.Ordinal))
{
providerName += "*";
}
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-PSProvider").AddParameter("PSProvider", providerName);
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var powerShellExecutionHelper = context.Helper;
powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-PSProvider").AddParameter("PSProvider", providerName);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out _);
if (psObjects == null)
return;
@ -3295,19 +3260,20 @@ namespace System.Management.Automation
wordToComplete = wordToComplete ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref wordToComplete);
var powershell = context.Helper.CurrentPowerShell;
if (!wordToComplete.EndsWith("*", StringComparison.Ordinal))
{
wordToComplete += "*";
}
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-PSDrive").AddParameter("Name", wordToComplete);
var powerShellExecutionHelper = context.Helper;
var powershell = powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-PSDrive")
.AddParameter("Name", wordToComplete);
if (psProvider != null)
powershell.AddParameter("PSProvider", psProvider);
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out _);
if (psObjects != null)
{
foreach (dynamic driveInfo in psObjects)
@ -3341,7 +3307,6 @@ namespace System.Management.Automation
wordToComplete = wordToComplete ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref wordToComplete);
var powershell = context.Helper.CurrentPowerShell;
if (!wordToComplete.EndsWith("*", StringComparison.Ordinal))
{
@ -3349,15 +3314,17 @@ namespace System.Management.Automation
}
Exception exceptionThrown;
var powerShellExecutionHelper = context.Helper;
if (paramName.Equals("DisplayName", StringComparison.OrdinalIgnoreCase))
{
RemoveLastNullCompletionResult(result);
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-Service")
.AddParameter("DisplayName", wordToComplete);
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Sort-Object")
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-Service")
.AddParameter("DisplayName", wordToComplete)
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Sort-Object")
.AddParameter("Property", "DisplayName");
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects != null)
{
foreach (dynamic serviceInfo in psObjects)
@ -3387,8 +3354,8 @@ namespace System.Management.Automation
{
RemoveLastNullCompletionResult(result);
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-Service").AddParameter("Name", wordToComplete);
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-Service").AddParameter("Name", wordToComplete);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects != null)
{
foreach (dynamic serviceInfo in psObjects)
@ -3427,16 +3394,14 @@ namespace System.Management.Automation
variableName = variableName ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref variableName);
var powershell = context.Helper.CurrentPowerShell;
if (!variableName.EndsWith("*", StringComparison.Ordinal))
{
variableName += "*";
}
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Get-Variable").AddParameter("Name", variableName);
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var powerShellExecutionHelper = context.Helper;
var powershell = powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Get-Variable").AddParameter("Name", variableName);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out _);
if (psObjects == null)
return;
@ -3483,11 +3448,11 @@ namespace System.Management.Automation
RemoveLastNullCompletionResult(result);
var powerShellExecutionHelper = context.Helper;
if (paramName.Equals("Name", StringComparison.OrdinalIgnoreCase))
{
commandName = commandName ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref commandName);
var powershell = context.Helper.CurrentPowerShell;
if (!commandName.EndsWith("*", StringComparison.Ordinal))
{
@ -3495,8 +3460,8 @@ namespace System.Management.Automation
}
Exception exceptionThrown;
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Get-Alias").AddParameter("Name", commandName);
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var powershell = powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Get-Alias").AddParameter("Name", commandName);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects != null)
{
foreach (dynamic aliasInfo in psObjects)
@ -3525,12 +3490,12 @@ namespace System.Management.Automation
// Complete for the parameter Definition
// Available commands
const CommandTypes commandTypes = CommandTypes.Cmdlet | CommandTypes.Function | CommandTypes.ExternalScript | CommandTypes.Workflow | CommandTypes.Configuration;
var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }, null, commandTypes);
var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = powerShellExecutionHelper }, null, commandTypes);
if (commandResults != null && commandResults.Count > 0)
result.AddRange(commandResults);
// The parameter Definition takes a file
var fileResults = new List<CompletionResult>(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }));
var fileResults = new List<CompletionResult>(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = powerShellExecutionHelper }));
if (fileResults.Count > 0)
result.AddRange(fileResults);
}
@ -3549,16 +3514,16 @@ namespace System.Management.Automation
traceSourceName = traceSourceName ?? string.Empty;
var quote = HandleDoubleAndSingleQuote(ref traceSourceName);
var powershell = context.Helper.CurrentPowerShell;
if (!traceSourceName.EndsWith("*", StringComparison.Ordinal))
{
traceSourceName += "*";
}
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Get-TraceSource").AddParameter("Name", traceSourceName);
var powerShellExecutionHelper = context.Helper;
var powershell = powerShellExecutionHelper.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Get-TraceSource").AddParameter("Name", traceSourceName);
Exception exceptionThrown;
var psObjects = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psObjects = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psObjects == null)
return;
@ -3786,11 +3751,11 @@ namespace System.Management.Automation
{
return;
}
prevType = astPair.Argument.GetInferredType(context);
prevType = AstTypeInference.InferTypeOf(astPair.Argument, context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval);
}
else
{
prevType = pipelineAst.PipelineElements[i - 1].GetInferredType(context);
prevType = AstTypeInference.InferTypeOf(pipelineAst.PipelineElements[i - 1], context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval);
}
CompleteMemberByInferredType(context, prevType, result, wordToComplete + "*", filter: IsPropertyMember, isStatic: false);
@ -4069,7 +4034,7 @@ namespace System.Management.Automation
return CommandCompletion.EmptyCompletionResult;
}
var helper = new CompletionExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace));
var helper = new PowerShellExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace));
return CompleteFilename(new CompletionContext { WordToComplete = fileName, Helper = helper });
}
@ -4109,16 +4074,13 @@ namespace System.Management.Automation
}
else
{
var powershell = context.Helper.CurrentPowerShell;
var executionContext = powershell.GetContextFromTLS();
// We want to prefer relative paths in a completion result unless the user has already
// specified a drive or portion of the path.
string unused;
var executionContext = context.ExecutionContext;
var defaultRelative = string.IsNullOrWhiteSpace(wordToComplete)
|| (wordToComplete.IndexOfAny(Utils.Separators.Directory) != 0 &&
!Regex.Match(wordToComplete, @"^~[\\/]+.*").Success &&
!executionContext.LocationGlobber.IsAbsolutePath(wordToComplete, out unused));
!executionContext.LocationGlobber.IsAbsolutePath(wordToComplete, out _));
var relativePaths = context.GetOption("RelativePaths", @default: defaultRelative);
var useLiteralPath = context.GetOption("LiteralPaths", @default: false);
@ -4127,18 +4089,20 @@ namespace System.Management.Automation
wordToComplete = WildcardPattern.Escape(wordToComplete, Utils.Separators.StarOrQuestion);
}
if (!defaultRelative && wordToComplete.Length >= 2 && wordToComplete[1] == ':' && char.IsLetter(wordToComplete[0]) && context.ExecutionContext != null)
if (!defaultRelative && wordToComplete.Length >= 2 && wordToComplete[1] == ':' && char.IsLetter(wordToComplete[0]) && executionContext != null)
{
// We don't actually need the drive, but the drive must be "mounted" in PowerShell before completion
// can succeed. This call will mount the drive if it wasn't already.
context.ExecutionContext.SessionState.Drive.GetAtScope(wordToComplete.Substring(0, 1), "global");
executionContext.SessionState.Drive.GetAtScope(wordToComplete.Substring(0, 1), "global");
}
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Resolve-Path")
var powerShellExecutionHelper = context.Helper;
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Resolve-Path")
.AddParameter("Path", wordToComplete + "*");
Exception exceptionThrown;
var psobjs = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psobjs = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psobjs != null)
{
@ -4257,11 +4221,12 @@ namespace System.Management.Automation
if (!hiddenFilesAreHandled)
{
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-ChildItem")
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-ChildItem")
.AddParameter("Path", wordToComplete + "*")
.AddParameter("Hidden", true);
var hiddenItems = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var hiddenItems = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (hiddenItems != null && hiddenItems.Count > 0)
{
foreach (var hiddenItem in hiddenItems)
@ -4396,9 +4361,10 @@ namespace System.Management.Automation
}
else
{
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-Item")
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-Item")
.AddParameter("LiteralPath", path);
var items = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var items = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (items != null && items.Count == 1)
{
dynamic item = items[0];
@ -4407,9 +4373,10 @@ namespace System.Management.Automation
if (containerOnly && !isContainer)
continue;
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Convert-Path")
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Convert-Path")
.AddParameter("LiteralPath", item.PSPath);
var tooltips = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var tooltips = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
string tooltip = null, listItemText = item.PSChildName;
if (tooltips != null && tooltips.Count == 1)
{
@ -4516,7 +4483,7 @@ namespace System.Management.Automation
return CommandCompletion.EmptyCompletionResult;
}
var helper = new CompletionExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace));
var helper = new PowerShellExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace));
return CompleteVariable(new CompletionContext { WordToComplete = variableName, Helper = helper });
}
@ -4619,7 +4586,7 @@ namespace System.Management.Automation
var commandAst = ast as CommandAst;
if (commandAst != null)
{
PSTypeName discoveredType = ast.GetInferredType(context).FirstOrDefault<PSTypeName>();
PSTypeName discoveredType = AstTypeInference.InferTypeOf(ast, context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval).FirstOrDefault<PSTypeName>();
if (discoveredType != null)
{
tooltip = StringUtil.Format("[{0}]${1}", discoveredType.Name, userPath);
@ -4654,12 +4621,13 @@ namespace System.Management.Automation
}
}
var powershell = context.Helper.CurrentPowerShell;
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-Item").AddParameter("Path", pattern);
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Sort-Object").AddParameter("Property", "Name");
var powerShellExecutionHelper = context.Helper;
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-Item").AddParameter("Path", pattern)
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Sort-Object").AddParameter("Property", "Name");
Exception exceptionThrown;
var psobjs = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
var psobjs = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psobjs != null)
{
foreach (dynamic psobj in psobjs)
@ -4690,11 +4658,11 @@ namespace System.Management.Automation
if (colon == -1 && "env".StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase))
{
powershell = context.Helper.CurrentPowerShell;
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-Item").AddParameter("Path", "env:*");
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Sort-Object").AddParameter("Property", "Key");
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-Item").AddParameter("Path", "env:*")
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Sort-Object").AddParameter("Property", "Key");
psobjs = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
psobjs = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psobjs != null)
{
foreach (dynamic psobj in psobjs)
@ -4730,9 +4698,10 @@ namespace System.Management.Automation
{
// If no drive was specified, then look for matching drives/scopes
pattern = wordToComplete + "*";
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Management\\Get-PSDrive").AddParameter("Name", pattern);
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Sort-Object").AddParameter("Property", "Name");
psobjs = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Management\\Get-PSDrive").AddParameter("Name", pattern)
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Sort-Object").AddParameter("Property", "Name");
psobjs = powerShellExecutionHelper.ExecuteCurrentPowerShell(out exceptionThrown);
if (psobjs != null)
{
foreach (var psobj in psobjs)
@ -4867,15 +4836,13 @@ namespace System.Management.Automation
if (!matchResult.Success) { return results; }
string wordToComplete = matchResult.Groups[1].Value;
PowerShell powershell = context.Helper.CurrentPowerShell;
Collection<PSObject> psobjs;
Exception exceptionThrown;
int entryId;
if (Regex.IsMatch(wordToComplete, @"^[0-9]+$") && LanguagePrimitives.TryConvertTo(wordToComplete, out entryId))
{
AddCommandWithPreferenceSetting(powershell, "Get-History", typeof(GetHistoryCommand)).AddParameter("Id", entryId);
psobjs = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
context.Helper.AddCommandWithPreferenceSetting("Get-History", typeof(GetHistoryCommand)).AddParameter("Id", entryId);
psobjs = context.Helper.ExecuteCurrentPowerShell(out _);
if (psobjs != null && psobjs.Count == 1)
{
@ -4899,9 +4866,9 @@ namespace System.Management.Automation
}
wordToComplete = "*" + wordToComplete + "*";
AddCommandWithPreferenceSetting(powershell, "Get-History", typeof(GetHistoryCommand));
context.Helper.AddCommandWithPreferenceSetting("Get-History", typeof(GetHistoryCommand));
psobjs = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown);
psobjs = context.Helper.ExecuteCurrentPowerShell(out _);
var pattern = WildcardPattern.Get(wordToComplete, WildcardOptions.IgnoreCase);
if (psobjs != null)
@ -5068,7 +5035,7 @@ namespace System.Management.Automation
}
else
{
inferredTypes = targetExpr.GetInferredType(context).ToArray();
inferredTypes = AstTypeInference.InferTypeOf(targetExpr, context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval).ToArray();
}
if (inferredTypes != null && inferredTypes.Length > 0)
@ -5190,7 +5157,7 @@ namespace System.Management.Automation
continue;
}
typeNameUsed.Add(psTypeName.Name);
var members = GetMembersByInferredType(psTypeName, context, isStatic, filter);
var members = context.TypeInferenceContext.GetMembersByInferredType(psTypeName, isStatic, filter);
foreach (var member in members)
{
AddInferredMember(member, memberNamePattern, results);
@ -5208,11 +5175,13 @@ namespace System.Management.Automation
if (results.Count > 0)
{
// Sort the results
AddCommandWithPreferenceSetting(context.Helper.CurrentPowerShell, "Microsoft.PowerShell.Utility\\Sort-Object")
var powerShellExecutionHelper = context.Helper;
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Sort-Object")
.AddParameter("Property", new[] { "ResultType", "ListItemText" })
.AddParameter("Unique");
Exception unused;
var sortedResults = context.Helper.ExecuteCurrentPowerShell(out unused, results);
var sortedResults = powerShellExecutionHelper.ExecuteCurrentPowerShell(out unused, results);
results.Clear();
results.AddRange(sortedResults.Select(psobj => PSObject.Base(psobj) as CompletionResult));
}
@ -5366,153 +5335,6 @@ namespace System.Management.Automation
return false;
}
internal static IEnumerable<object> GetMembersByInferredType(PSTypeName typename, CompletionContext context, bool @static, Func<object, bool> filter)
{
List<object> results = new List<object>();
Func<object, bool> filterToCall = filter;
if (typename.Type != null)
{
if (context.CurrentTypeDefinitionAst == null || context.CurrentTypeDefinitionAst.Type != typename.Type)
{
if (filterToCall == null)
filterToCall = o => !IsMemberHidden(o);
else
filterToCall = o => !IsMemberHidden(o) && filter(o);
}
IEnumerable<Type> elementTypes;
if (typename.Type.IsArray)
{
elementTypes = new[] { typename.Type.GetElementType() };
}
else
{
elementTypes = typename.Type.GetInterfaces().Where(
t => t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
}
foreach (var type in elementTypes.Prepend(typename.Type))
{
// Look in the type table first.
if (!@static)
{
var consolidatedString = DotNetAdapter.GetInternedTypeNameHierarchy(type);
results.AddRange(context.ExecutionContext.TypeTable.GetMembers<PSMemberInfo>(consolidatedString));
}
var members = @static
? PSObject.dotNetStaticAdapter.BaseGetMembers<PSMemberInfo>(type)
: PSObject.dotNetInstanceAdapter.GetPropertiesAndMethods(type, false);
results.AddRange(filterToCall != null ? members.Where(filterToCall) : members);
}
}
else if (typename.TypeDefinitionAst != null)
{
if (context.CurrentTypeDefinitionAst != typename.TypeDefinitionAst)
{
if (filterToCall == null)
filterToCall = o => !IsMemberHidden(o);
else
filterToCall = o => !IsMemberHidden(o) && filter(o);
}
bool foundConstructor = false;
foreach (var member in typename.TypeDefinitionAst.Members)
{
bool add = false;
var propertyMember = member as PropertyMemberAst;
if (propertyMember != null)
{
if (propertyMember.IsStatic == @static)
{
add = true;
}
}
else
{
var functionMember = (FunctionMemberAst)member;
if (functionMember.IsStatic == @static)
{
add = true;
}
foundConstructor |= functionMember.IsConstructor;
}
if (filterToCall != null && add)
{
add = filterToCall(member);
}
if (add)
{
results.Add(member);
}
}
//iterate through bases/interfaces
foreach (var baseType in typename.TypeDefinitionAst.BaseTypes)
{
TypeName baseTypeName = baseType.TypeName as TypeName;
if (baseTypeName != null)
{
TypeDefinitionAst baseTypeDefinitionAst = baseTypeName._typeDefinitionAst;
results.AddRange(GetMembersByInferredType(new PSTypeName(baseTypeDefinitionAst), context, @static, filterToCall));
}
}
// Add stuff from our base class System.Object.
if (@static)
{
// Don't add base class constructors
if (filter == null)
{
filterToCall = o => !IsConstructor(o);
}
else
{
filterToCall = o => !IsConstructor(o) && filter(o);
}
if (!foundConstructor)
{
results.Add(
new CompilerGeneratedMemberFunctionAst(PositionUtilities.EmptyExtent, typename.TypeDefinitionAst,
SpecialMemberFunctionType.DefaultConstructor));
}
}
else
{
// Reset the filter because the recursive call will add IsHidden back if necessary.
filterToCall = filter;
}
results.AddRange(GetMembersByInferredType(new PSTypeName(typeof(object)), context, @static, filterToCall));
}
else
{
// Look in the type table first.
if (!@static)
{
var consolidatedString = new ConsolidatedString(new string[] { typename.Name });
results.AddRange(context.ExecutionContext.TypeTable.GetMembers<PSMemberInfo>(consolidatedString));
}
string cimNamespace;
string className;
if (NativeCompletionCimCommands_ParseTypeName(typename, out cimNamespace, out className))
{
AddCommandWithPreferenceSetting(context.Helper.CurrentPowerShell, "CimCmdlets\\Get-CimClass")
.AddParameter("Namespace", cimNamespace)
.AddParameter("Class", className);
Exception unused;
var classes = context.Helper.ExecuteCurrentPowerShell(out unused);
foreach (var @class in classes.Select(PSObject.Base).OfType<CimClass>())
{
results.AddRange(filterToCall != null ? @class.CimClassProperties.Where(filterToCall) : @class.CimClassProperties);
}
}
}
return results;
}
#endregion Members
@ -6038,7 +5860,7 @@ namespace System.Management.Automation
? PowerShell.Create()
: PowerShell.Create(RunspaceMode.CurrentRunspace);
var helper = new CompletionExecutionHelper(powershell);
var helper = new PowerShellExecutionHelper(powershell);
return CompleteType(new CompletionContext { WordToComplete = typeName, Helper = helper });
}
@ -6326,7 +6148,7 @@ namespace System.Management.Automation
{
var result = new List<CompletionResult>();
CompleteMemberByInferredType(
completionContext, typeAst.GetInferredType(completionContext),
completionContext, AstTypeInference.InferTypeOf(typeAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval),
result, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false);
return result;
}
@ -6429,7 +6251,7 @@ namespace System.Management.Automation
switch (binding.CommandName)
{
case "New-Object":
var inferredType = commandAst.GetInferredType(completionContext);
var inferredType = AstTypeInference.InferTypeOf(commandAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval);
var result = new List<CompletionResult>();
CompleteMemberByInferredType(
completionContext, inferredType,
@ -6470,30 +6292,6 @@ namespace System.Management.Automation
#region Helpers
internal static PowerShell AddCommandWithPreferenceSetting(PowerShell powershell, string command, Type type = null)
{
Diagnostics.Assert(powershell != null, "the passed-in powershell cannot be null");
Diagnostics.Assert(!String.IsNullOrWhiteSpace(command), "the passed-in command name should not be null or whitespaces");
if (type != null)
{
var cmdletInfo = new CmdletInfo(command, type);
powershell.AddCommand(cmdletInfo);
}
else
{
powershell.AddCommand(command);
}
powershell
.AddParameter("ErrorAction", ActionPreference.Ignore)
.AddParameter("WarningAction", ActionPreference.Ignore)
.AddParameter("InformationAction", ActionPreference.Ignore)
.AddParameter("Verbose", false)
.AddParameter("Debug", false);
return powershell;
}
internal static bool IsPathSafelyExpandable(ExpandableStringExpressionAst expandableStringAst, string extraText, ExecutionContext executionContext, out string expandedString)
{
expandedString = null;
@ -6619,14 +6417,14 @@ namespace System.Management.Automation
value = new[] { value };
}
var powershell = context.Helper.CurrentPowerShell;
// Instead of Get-Member, we access the members directly and send as input to the pipe.
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Core\\Where-Object")
var powerShellExecutionHelper = context.Helper;
powerShellExecutionHelper
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Core\\Where-Object")
.AddParameter("Property", "Name")
.AddParameter("Like")
.AddParameter("Value", memberName);
AddCommandWithPreferenceSetting(powershell, "Microsoft.PowerShell.Utility\\Sort-Object")
.AddParameter("Value", memberName)
.AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Sort-Object")
.AddParameter("Property", new object[] { "MemberType", "Name" });
IEnumerable members;
@ -6643,8 +6441,7 @@ namespace System.Management.Automation
{
members = PSObject.AsPSObject(value).Members;
}
Exception exceptionThrown;
var sortedMembers = context.Helper.ExecuteCurrentPowerShell(out exceptionThrown, members);
var sortedMembers = powerShellExecutionHelper.ExecuteCurrentPowerShell(out _, members);
foreach (var member in sortedMembers)
{

View file

@ -1253,10 +1253,7 @@ namespace System.Management.Automation.Language
var expressionArgument = _commandElements[commandIndex] as ExpressionAst;
if (expressionArgument != null)
{
if (argumentsToGetDynamicParameters != null)
{
argumentsToGetDynamicParameters.Add(expressionArgument.Extent.Text);
}
argumentsToGetDynamicParameters?.Add(expressionArgument.Extent.Text);
_arguments.Add(new AstPair(null, expressionArgument));
}
@ -1338,79 +1335,76 @@ namespace System.Management.Automation.Language
if (_commandAst.IsInWorkflow())
{
var converterType = Type.GetType(Utils.WorkflowType);
if (converterType != null)
var activityParameters = (Dictionary<string, Type>) converterType?.GetMethod("GetActivityParameters").Invoke(null, new object[] { _commandAst });
if (activityParameters != null)
{
var activityParameters = (Dictionary<string, Type>)converterType.GetMethod("GetActivityParameters").Invoke(null, new object[] { _commandAst });
if (activityParameters != null)
bool needToRemoveReplacedProperty = activityParameters.ContainsKey("PSComputerName") &&
!activityParameters.ContainsKey("ComputerName");
var parametersToAdd = new List<MergedCompiledCommandParameter>();
var attrCollection = new Collection<Attribute> { new ParameterAttribute() };
foreach (var pair in activityParameters)
{
bool needToRemoveReplacedProperty = activityParameters.ContainsKey("PSComputerName") &&
!activityParameters.ContainsKey("ComputerName");
var parametersToAdd = new List<MergedCompiledCommandParameter>();
var attrCollection = new Collection<Attribute> { new ParameterAttribute() };
foreach (var pair in activityParameters)
if (psuedoWorkflowCommand || !_bindableParameters.BindableParameters.ContainsKey(pair.Key))
{
if (psuedoWorkflowCommand || !_bindableParameters.BindableParameters.ContainsKey(pair.Key))
{
Type parameterType = GetActualActivityParameterType(pair.Value);
var runtimeDefinedParameter = new RuntimeDefinedParameter(pair.Key, parameterType, attrCollection);
var compiledCommandParameter = new CompiledCommandParameter(runtimeDefinedParameter, false) { IsInAllSets = true };
var mergedCompiledCommandParameter = new MergedCompiledCommandParameter(compiledCommandParameter, ParameterBinderAssociation.DeclaredFormalParameters);
parametersToAdd.Add(mergedCompiledCommandParameter);
}
Type parameterType = GetActualActivityParameterType(pair.Value);
var runtimeDefinedParameter = new RuntimeDefinedParameter(pair.Key, parameterType, attrCollection);
var compiledCommandParameter = new CompiledCommandParameter(runtimeDefinedParameter, false) { IsInAllSets = true };
var mergedCompiledCommandParameter = new MergedCompiledCommandParameter(compiledCommandParameter, ParameterBinderAssociation.DeclaredFormalParameters);
parametersToAdd.Add(mergedCompiledCommandParameter);
}
if (parametersToAdd.Any())
}
if (parametersToAdd.Any())
{
var mergedBindableParameters = new MergedCommandParameterMetadata();
if (!psuedoWorkflowCommand)
{
var mergedBindableParameters = new MergedCommandParameterMetadata();
if (!psuedoWorkflowCommand)
{
mergedBindableParameters.ReplaceMetadata(_bindableParameters);
}
foreach (var p in parametersToAdd)
{
mergedBindableParameters.BindableParameters.Add(p.Parameter.Name, p);
}
_bindableParameters = mergedBindableParameters;
mergedBindableParameters.ReplaceMetadata(_bindableParameters);
}
// Remove common parameters that are supported by all commands, but not
// by workflows
bool fixedReadOnly = false;
foreach (var ignored in _ignoredWorkflowParameters)
foreach (var p in parametersToAdd)
{
if (_bindableParameters.BindableParameters.ContainsKey(ignored))
mergedBindableParameters.BindableParameters.Add(p.Parameter.Name, p);
}
_bindableParameters = mergedBindableParameters;
}
// Remove common parameters that are supported by all commands, but not
// by workflows
bool fixedReadOnly = false;
foreach (var ignored in _ignoredWorkflowParameters)
{
if (_bindableParameters.BindableParameters.ContainsKey(ignored))
{
// However, some ignored parameters are explicitly implemented by
// activities, so keep them.
if (!activityParameters.ContainsKey(ignored))
{
// However, some ignored parameters are explicitly implemented by
// activities, so keep them.
if (!activityParameters.ContainsKey(ignored))
if (!fixedReadOnly)
{
if (!fixedReadOnly)
{
_bindableParameters.ResetReadOnly();
fixedReadOnly = true;
}
_bindableParameters.BindableParameters.Remove(ignored);
_bindableParameters.ResetReadOnly();
fixedReadOnly = true;
}
_bindableParameters.BindableParameters.Remove(ignored);
}
}
}
if (_bindableParameters.BindableParameters.ContainsKey("ComputerName") && needToRemoveReplacedProperty)
if (_bindableParameters.BindableParameters.ContainsKey("ComputerName") && needToRemoveReplacedProperty)
{
if (!fixedReadOnly)
{
if (!fixedReadOnly)
{
_bindableParameters.ResetReadOnly();
fixedReadOnly = true;
}
_bindableParameters.ResetReadOnly();
fixedReadOnly = true;
}
_bindableParameters.BindableParameters.Remove("ComputerName");
string aliasOfComputerName = (from aliasPair in _bindableParameters.AliasedParameters
where String.Equals("ComputerName", aliasPair.Value.Parameter.Name)
select aliasPair.Key).FirstOrDefault();
if (aliasOfComputerName != null)
{
_bindableParameters.AliasedParameters.Remove(aliasOfComputerName);
}
_bindableParameters.BindableParameters.Remove("ComputerName");
string aliasOfComputerName = (from aliasPair in _bindableParameters.AliasedParameters
where String.Equals("ComputerName", aliasPair.Value.Parameter.Name)
select aliasPair.Key).FirstOrDefault();
if (aliasOfComputerName != null)
{
_bindableParameters.AliasedParameters.Remove(aliasOfComputerName);
}
}
}
@ -1451,32 +1445,36 @@ namespace System.Management.Automation.Language
}
ast.Visit(exportVisitor);
resolvedCommandName = _commandAst.GetCommandName();
CommandProcessorBase commandProcessor = null;
string alias;
int resolvedAliasCount = 0;
while (exportVisitor.DiscoveredAliases.TryGetValue(resolvedCommandName, out alias))
resolvedCommandName = _commandAst.GetCommandName();
if (resolvedCommandName != null)
{
resolvedAliasCount += 1;
if (resolvedAliasCount > 5)
break; // give up, assume it's recursive
resolvedCommandName = alias;
}
string alias;
int resolvedAliasCount = 0;
FunctionDefinitionAst functionDefinitionAst;
if (exportVisitor.DiscoveredFunctions.TryGetValue(resolvedCommandName, out functionDefinitionAst))
{
// We could use the IAstToScriptBlockConverter to get the actual script block, but that can be fairly expensive for workflows.
// IAstToScriptBlockConverter is public, so we might consider converting non-workflows, but the interface isn't really designed
// for Intellisense, so we can't really expect good performance, so instead we'll just fall back on the actual
// parameters we see in the ast.
var scriptBlock = functionDefinitionAst.IsWorkflow
? CreateFakeScriptBlockForWorkflow(functionDefinitionAst)
: new ScriptBlock(functionDefinitionAst, functionDefinitionAst.IsFilter);
commandProcessor = CommandDiscovery.CreateCommandProcessorForScript(scriptBlock, context, true, context.EngineSessionState);
}
while (exportVisitor.DiscoveredAliases.TryGetValue(resolvedCommandName, out alias))
{
resolvedAliasCount += 1;
if (resolvedAliasCount > 5)
break; // give up, assume it's recursive
resolvedCommandName = alias;
}
FunctionDefinitionAst functionDefinitionAst;
if (exportVisitor.DiscoveredFunctions.TryGetValue(resolvedCommandName, out functionDefinitionAst))
{
// We could use the IAstToScriptBlockConverter to get the actual script block, but that can be fairly expensive for workflows.
// IAstToScriptBlockConverter is public, so we might consider converting non-workflows, but the interface isn't really designed
// for Intellisense, so we can't really expect good performance, so instead we'll just fall back on the actual
// parameters we see in the ast.
var scriptBlock = functionDefinitionAst.IsWorkflow
? CreateFakeScriptBlockForWorkflow(functionDefinitionAst)
: new ScriptBlock(functionDefinitionAst, functionDefinitionAst.IsFilter);
commandProcessor = CommandDiscovery.CreateCommandProcessorForScript(scriptBlock, context, true, context.EngineSessionState);
}
}
return commandProcessor;
}
@ -1525,7 +1523,7 @@ namespace System.Management.Automation.Language
var paramBlockAst = functionDefinitionAst.Body.ParamBlock;
if (paramBlockAst != null)
{
var outputTypeAttrs = paramBlockAst.Attributes.Where(attribute => typeof(OutputTypeAttribute).Equals(attribute.TypeName.GetReflectionAttributeType()));
var outputTypeAttrs = paramBlockAst.Attributes.Where(attribute => typeof(OutputTypeAttribute) == attribute.TypeName.GetReflectionAttributeType());
foreach (AttributeAst attributeAst in outputTypeAttrs)
{

File diff suppressed because it is too large Load diff

View file

@ -457,12 +457,6 @@ namespace System.Management.Automation.Language
Diagnostics.Assert(false, "This code is unreachable.");
return AstVisitAction.Continue;
}
internal override IEnumerable<PSTypeName> GetInferredType(CompletionContext context)
{
Diagnostics.Assert(false, "This code is unreachable.");
return Ast.EmptyPSTypeNameArray;
}
}
internal static string GetUnaliasedVariableName(string varName)

File diff suppressed because it is too large Load diff

View file

@ -1,34 +1,28 @@
/********************************************************************++
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
namespace System.Management.Automation
{
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Management.Automation.Runspaces;
using System.Collections;
/// <summary>
/// Auxiliary class to the execution of commands as needed by
/// CommandCompletion
/// </summary>
internal class CompletionExecutionHelper
internal class PowerShellExecutionHelper
{
#region Constructors
// Creates a new CompletionExecutionHelper with the PowerShell instance that will be used to execute the tab expansion commands
// Creates a new PowerShellExecutionHelper with the PowerShell instance that will be used to execute the tab expansion commands
// Used by the ISE
internal CompletionExecutionHelper(PowerShell powershell)
internal PowerShellExecutionHelper(PowerShell powershell)
{
if (powershell == null)
{
throw PSTraceSource.NewArgumentNullException("powershell");
}
this.CurrentPowerShell = powershell;
CurrentPowerShell = powershell;
}
#endregion Constructors
@ -44,16 +38,10 @@ namespace System.Management.Automation
internal PowerShell CurrentPowerShell { get; set; }
// Returns true if this instance is currently executing a command
internal bool IsRunning
{
get { return CurrentPowerShell.InvocationStateInfo.State == PSInvocationState.Running; }
}
internal bool IsRunning => CurrentPowerShell.InvocationStateInfo.State == PSInvocationState.Running;
// Returns true if the command executed by this instance was stopped
internal bool IsStopped
{
get { return CurrentPowerShell.InvocationStateInfo.State == PSInvocationState.Stopped; }
}
internal bool IsStopped => CurrentPowerShell.InvocationStateInfo.State == PSInvocationState.Stopped;
#endregion Fields and Properties
@ -62,7 +50,7 @@ namespace System.Management.Automation
internal Collection<PSObject> ExecuteCommand(string command)
{
Exception unused;
return this.ExecuteCommand(command, true, out unused, null);
return ExecuteCommand(command, true, out unused, null);
}
internal bool ExecuteCommandAndGetResultAsBool()
@ -106,7 +94,7 @@ namespace System.Management.Automation
exceptionThrown = null;
// This flag indicates a previous call to this method had its pipeline cancelled
if (this.CancelTabCompletion)
if (CancelTabCompletion)
{
return new Collection<PSObject>();
}
@ -130,10 +118,10 @@ namespace System.Management.Automation
// If this pipeline has been stopped lets set a flag to cancel all future tab completion calls
// untill the next completion
if (this.IsStopped)
if (IsStopped)
{
results = new Collection<PSObject>();
this.CancelTabCompletion = true;
CancelTabCompletion = true;
}
}
catch (Exception e)
@ -149,7 +137,7 @@ namespace System.Management.Automation
exceptionThrown = null;
// This flag indicates a previous call to this method had its pipeline cancelled
if (this.CancelTabCompletion)
if (CancelTabCompletion)
{
return new Collection<PSObject>();
}
@ -161,10 +149,10 @@ namespace System.Management.Automation
// If this pipeline has been stopped lets set a flag to cancel all future tab completion calls
// untill the next completion
if (this.IsStopped)
if (IsStopped)
{
results = new Collection<PSObject>();
this.CancelTabCompletion = true;
CancelTabCompletion = true;
}
}
catch (Exception e)
@ -236,4 +224,39 @@ namespace System.Management.Automation
#endregion Helpers
}
}
internal static class PowerShellExtensionHelpers
{
internal static PowerShell AddCommandWithPreferenceSetting(this PowerShellExecutionHelper helper,
string command, Type type = null)
{
return helper.CurrentPowerShell.AddCommandWithPreferenceSetting(command, type);
}
internal static PowerShell AddCommandWithPreferenceSetting(this PowerShell powershell, string command, Type type = null)
{
Diagnostics.Assert(powershell != null, "the passed-in powershell cannot be null");
Diagnostics.Assert(!String.IsNullOrWhiteSpace(command),
"the passed-in command name should not be null or whitespaces");
if (type != null)
{
var cmdletInfo = new CmdletInfo(command, type);
powershell.AddCommand(cmdletInfo);
}
else
{
powershell.AddCommand(command);
}
powershell
.AddParameter("ErrorAction", ActionPreference.Ignore)
.AddParameter("WarningAction", ActionPreference.Ignore)
.AddParameter("InformationAction", ActionPreference.Ignore)
.AddParameter("Verbose", false)
.AddParameter("Debug", false);
return powershell;
}
}
}

View file

@ -0,0 +1,859 @@
using namespace System.Management.Automation
using namespace System.Collections.Generic
Describe "Type inference Tests" -tags "CI" {
BeforeAll {
$ati = [Cmdlet].Assembly.GetType("System.Management.Automation.AstTypeInference")
$inferType = $ati.GetMethods().Where{$_.Name -ceq "InferTypeOf"}
$m1 = 'System.Collections.Generic.IList`1[System.Management.Automation.PSTypeName] InferTypeOf(System.Management.Automation.Language.Ast)'
$m2 = 'System.Collections.Generic.IList`1[System.Management.Automation.PSTypeName] InferTypeOf(System.Management.Automation.Language.Ast, System.Management.Automation.TypeInferenceRuntimePermissions)'
$m3 = 'System.Collections.Generic.IList`1[System.Management.Automation.PSTypeName] InferTypeOf(System.Management.Automation.Language.Ast, System.Management.Automation.PowerShell)'
$m4 = 'System.Collections.Generic.IList`1[System.Management.Automation.PSTypeName] InferTypeOf(System.Management.Automation.Language.Ast, System.Management.Automation.PowerShell, System.Management.Automation.TypeInferenceRuntimePermissions)'
$inferTypeOf1 = $inferType.Where{$m1 -eq $_}[0]
$inferTypeOf2 = $inferType.Where{$m2 -eq $_}[0]
$inferTypeOf3 = $inferType.Where{$m3 -eq $_}[0]
$inferTypeOf4 = $inferType.Where{$m4 -eq $_}[0]
class AstTypeInference {
static [IList[PSTypeName]] InferTypeOf([Language.Ast] $ast) {
return $script:inferTypeOf1.Invoke($null, $ast)
}
static [IList[PSTypeName]] InferTypeOf([Language.Ast] $ast, [System.Management.Automation.TypeInferenceRuntimePermissions] $runtimePermissions) {
return $script:inferTypeOf2.Invoke($null, @($ast, $runtimePermissions))
}
static [IList[PSTypeName]] InferTypeOf([Language.Ast] $ast, [System.Management.Automation.PowerShell] $powershell) {
return $script:inferTypeOf3.Invoke($null, @($ast, $powershell))
}
static [IList[PSTypeName]] InferTypeOf([Language.Ast] $ast, [PowerShell] $powerShell, [System.Management.Automation.TypeInferenceRuntimePermissions] $runtimePermissions) {
return $script:inferTypeOf4.Invoke($null, @($ast, $powerShell, $runtimePermissions))
}
}
}
It "Infers type from integer" {
$res = [AstTypeInference]::InferTypeOf( { 1 }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Int32'
}
It "Infers type from string literal" {
$res = [AstTypeInference]::InferTypeOf( { "Text" }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.String'
}
It "Infers type from type expression" {
$res = [AstTypeInference]::InferTypeOf( { [int] }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Type'
}
It "Infers type from hashtable" {
$res = [AstTypeInference]::InferTypeOf( { @{} }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Collections.Hashtable'
}
It "Infers type from array expression" {
$res = [AstTypeInference]::InferTypeOf( { @() }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.object[]'
}
It "Infers type from Array literal" {
$res = [AstTypeInference]::InferTypeOf( { , 1 }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.object[]'
}
It "Infers type from array IndexExpresssion" {
$res = [AstTypeInference]::InferTypeOf( { (1, 2, 3)[0] }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.object'
}
It "Infers type from generic container IndexExpression" {
$res = [AstTypeInference]::InferTypeOf( {
[System.Collections.Generic.List[int]]::new()[0]
}.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Int32'
}
It 'Infers type of Index expression on Dictionary' {
$ast = {
[System.Collections.Generic.Dictionary[int, DateTime]]::new()[1]
}.ast.EndBlock.Statements[0].PipelineElements[0].Expression
$res = [AstTypeInference]::InferTypeOf( $ast )
$res.Count | Should be 1
$res.Name | Should be System.DateTime
}
It "Infers type from ScriptblockExpresssion" {
$res = [AstTypeInference]::InferTypeOf( { {} }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Management.Automation.Scriptblock'
}
It "Infers type from paren expression" {
$res = [AstTypeInference]::InferTypeOf( { (1) }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Int32'
}
It "Infers type from expandable string expression" {
$res = [AstTypeInference]::InferTypeOf( { "$(1)" }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.String'
}
It "Infers type from cast expression" {
$res = [AstTypeInference]::InferTypeOf( { [int] '1'}.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Int32'
}
It "Infers type from using namespace" {
$errors = $null
$tokens = $null
$ast = [Language.Parser]::ParseInput("using namespace System", [ref] $tokens, [ref] $errors)
$res = [AstTypeInference]::InferTypeOf( $ast.Find( {param($a) $a -is [System.Management.Automation.Language.UsingStatementAst] }, $true))
$res.Count | Should Be 0
}
It "Infers type from unary expression" {
$res = [AstTypeInference]::InferTypeOf( { !$true }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Boolean'
}
It "Infers type from param block" {
$res = [AstTypeInference]::InferTypeOf( { param() }.Ast)
$res.Count | Should Be 0
}
It "Infers type from using statement" {
$res = [AstTypeInference]::InferTypeOf( { $pid = 1; $using:pid }.Ast.EndBlock.Statements[1].PipelineElements[0].Expression)
$res.Count | Should Be 1
$res.Name | Should Be System.Int32
}
It "Infers type from param block" {
$res = [AstTypeInference]::InferTypeOf( { param([int] $i)}.Ast.ParamBlock)
$res.Count | Should Be 0
}
It "Infers type no type from Attribute" {
$res = [AstTypeInference]::InferTypeOf( {
[OutputType([int])]
param(
)}.Ast.ParamBlock.Attributes[0])
$res.Count | Should Be 0
}
It "Infers type no type from named Attribute argument" {
$res = [AstTypeInference]::InferTypeOf( {
[OutputType(Type = [int])]
param(
)}.Ast.ParamBlock.Attributes[0].NamedArguments[0])
$res.Count | Should Be 0
}
It "Infers type parameter types" {
$res = [AstTypeInference]::InferTypeOf( {
param([int] $i, [string] $s)
}.Ast.ParamBlock.Parameters[0])
$res.Count | Should Be 1
$res.Name | Should be System.Int32
}
It "Infers type parameter from PSTypeNameAttribute type" -Skip:(!$IsWindows) {
$res = [AstTypeInference]::InferTypeOf( {
param([int] $i, [PSTypeName('System.Management.ManagementObject#root\cimv2\Win32_Process')] $s)
}.Ast.ParamBlock.Parameters[1])
$res.Count | Should Be 1
$res.Name | Should be 'System.Management.ManagementObject#root\cimv2\Win32_Process'
}
It "Infers type from DATA statement" {
$res = [AstTypeInference]::InferTypeOf( {
DATA {
"text"
}
}.Ast.EndBlock)
$res.Count | Should Be 1
$res.Name | Should be 'System.String'
}
It "Infers type from named block" {
$res = [AstTypeInference]::InferTypeOf( { begin {1}}.Ast.BeginBlock)
$res.Count | Should Be 1
$res.Name | Should Be System.Int32
}
It "Infers type from function definition" {
$res = [AstTypeInference]::InferTypeOf( {
function foo {
return 1
}
}.Ast.EndBlock)
$res.Count | Should Be 0
}
It "Infers type from convert expression" {
$errors = $null
$tokens = $null
$ast = [Language.Parser]::ParseInput('[int] "4"', [ref] $tokens, [ref] $errors)
$res = [AstTypeInference]::InferTypeOf( $ast.EndBlock.Statements[0])
$res.Count | Should Be 1
$res.Name | Should Be 'System.Int32'
}
It "Infers type from type constraint" {
$errors = $null
$tokens = $null
$ast = [Language.Parser]::ParseInput('[int] $i', [ref] $tokens, [ref] $errors)
$res = [AstTypeInference]::InferTypeOf( $ast.EndBlock.Statements[0].PipelineElements[0].Expression.Attribute)
$res.Count | Should Be 0
}
It "Infers type from instance member property" {
$res = [AstTypeInference]::InferTypeOf( { 'Text'.Length }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Int32'
}
It "Infers type from static member property" {
$res = [AstTypeInference]::InferTypeOf( { [DateTime]::Now }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.DateTime'
}
It "Infers type from instance member method" {
$res = [AstTypeInference]::InferTypeOf( { [int[]].GetElementType() }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Type'
}
It "Infers type from integer * stringliteral" {
$res = [AstTypeInference]::InferTypeOf( { 5 * "5" }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.Int32'
}
It "Infers type from string literal" {
$res = [AstTypeInference]::InferTypeOf( { "Text" }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.String'
}
It "Infers type from stringliteral * integer" {
$res = [AstTypeInference]::InferTypeOf( { "5" * 2 }.Ast)
$res.Count | Should Be 1
$res.Name | Should be 'System.String'
}
It "Infers type from where-object of integer" {
$res = [AstTypeInference]::InferTypeOf( { [int[]] $i = 1..20; $i | Where-Object {$_ -gt 10} }.Ast)
foreach ($r in $res) {
$r.Name -in 'System.Int32', 'System.Int32[]' | Should be $true
}
}
It "Infers type from foreach-object of integer" {
$res = [AstTypeInference]::InferTypeOf( { [int[]] $i = 1..20; $i | ForEach-Object {$_ * 10} }.Ast)
$res.Count | Should Be 2
foreach ($r in $res) {
$r.Name -in 'System.Int32', 'System.Int32[]' | Should be $true
}
}
It "Infers type from generic new" {
$res = [AstTypeInference]::InferTypeOf( { [System.Collections.Generic.List[int]]::new() }.Ast)
$res.Count | Should Be 1
$res.Name | Should Match 'System.Collections.Generic.List`1\[\[System.Int32.*'
}
It "Infers type from cim command" -Skip:(!$IsWindows) {
$res = [AstTypeInference]::InferTypeOf( { Get-CimInstance -Namespace root/CIMV2 -ClassName Win32_Bios }.Ast)
$res.Count | Should Be 2
foreach ($r in $res) {
$r.Name -in 'Microsoft.Management.Infrastructure.CimInstance#root/CIMV2/Win32_Bios',
'Microsoft.Management.Infrastructure.CimInstance' | Should be $true
}
}
It "Infers type from foreach-object with begin/end" {
$res = [AstTypeInference]::InferTypeOf( { [int[]] $i = 1..20; $i | ForEach-Object -Begin {"Hi"} {$_ * 10} -End {[int]} }.Ast)
$res.Count | Should Be 4
foreach ($r in $res) {
$r.Name -in 'System.Int32', 'System.Int32[]', 'System.String', 'System.Type' | Should be $true
}
}
It "Infers type from OutputTypeAttribute" {
$res = [AstTypeInference]::InferTypeOf( { Get-Process -Id 2345 }.Ast)
$gpsOutput = [Microsoft.PowerShell.Commands.GetProcessCommand].GetCustomAttributes([System.Management.Automation.OutputTypeAttribute], $false).Type
$names = $gpsOutput.Name
foreach ($r in $res) {
$r.Name -in $names | Should Be $true
}
}
It "Infers type from variable with AllowSafeEval" {
function Hide-GetProcess { Get-Process }
$p = Hide-GetProcess
$res = [AstTypeInference]::InferTypeOf( { $p }.Ast, [TypeInferenceRuntimePermissions]::AllowSafeEval)
$res.Name | Should Be 'System.Diagnostics.Process'
}
It "Infers type from variable with type in scope" {
$res = [AstTypeInference]::InferTypeOf( {
$p = 1
$p
}.Ast)
$res.Name | Should Be 'System.Int32'
}
It "Infers type from block statement" {
$errors = $null
$tokens = $null
$ast = [Language.Parser]::ParseInput("parallel {1}", [ref] $tokens, [ref] $errors)
$res = [AstTypeInference]::InferTypeOf( $ast.EndBlock.Statements[0])
$res.Name | Should Be 'System.Int32'
}
It 'Infers type from attributed expession' {
$res = [AstTypeInference]::InferTypeOf( {
[ValidateRange(1, 2)]
[int]$i = 1
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type from if statement' {
$res = [AstTypeInference]::InferTypeOf( {
if ($true) { return 1}
else { return 'Text'}
}.Ast)
$res.Count | Should be 2
foreach ($r in $res) {
$r.Name -in 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from switch statement' {
$res = [AstTypeInference]::InferTypeOf( {
switch (1, 2, 3) {
(1) { return 'Hello'}
(2) {return [int]}
default {return 1}
}
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Type', 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from Foreach statement' {
$res = [AstTypeInference]::InferTypeOf( {
foreach ($i in 1, 2, 3) {
if ($i -eq 1) { return 'Hello'}
if ($i -eq 2) {return [int]}
return 1
}
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Type', 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from While statement' {
$res = [AstTypeInference]::InferTypeOf( {
while ($true) {
if ($i -eq 1) { return 'Hello'}
if ($i -eq 2) {return [int]}
return 1
}
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Type', 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from For statement' {
$res = [AstTypeInference]::InferTypeOf( {
for ($i = 0; $i -lt 10; $i++) {
if ($i -eq 1) { return 'Hello'}
if ($i -eq 2) {return [int]}
return 1
}
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Type', 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from Do-While statement' {
$res = [AstTypeInference]::InferTypeOf( {
do {
if ($i -eq 1) { return 'Hello'}
if ($i -eq 2) {return [int]}
return 1
}while ($true)
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Type', 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from Do-Until statement' {
$res = [AstTypeInference]::InferTypeOf( {
do {
if ($i -eq 1) { return 'Hello'}
if ($i -eq 2) {return [int]}
return 1
} until ($true)
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Type', 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from full scriptblock' {
$res = [AstTypeInference]::InferTypeOf( {
begin {1}
process {"text"}
end {[int]}
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Type', 'System.Int32', 'System.String' | Should be $true
}
}
It 'Infers type from sub expression' {
$res = [AstTypeInference]::InferTypeOf( {
$(1)
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type from Throw statement' {
$res = [AstTypeInference]::InferTypeOf( {
throw 'Foo'
}.Ast)
$res.Count | Should be 0
}
It 'Infers type from Return statement' {
$res = [AstTypeInference]::InferTypeOf( {
return 1
}.Ast)
$res.Count | Should be 1
$res.Name | Should be 'System.Int32'
}
It 'Infers type from New-Object statement' {
$res = [AstTypeInference]::InferTypeOf( {
New-Object -TypeName 'System.Diagnostics.Stopwatch'
}.Ast)
$res.Count | Should be 1
$res.Name | Should be 'System.Diagnostics.Stopwatch'
}
It 'Infers type from Continue statement' {
$res = [AstTypeInference]::InferTypeOf( {
continue
}.Ast)
$res.Count | Should be 0
}
It 'Infers type from Break statement' {
$res = [AstTypeInference]::InferTypeOf( {
break
}.Ast)
$res.Count | Should be 0
}
It 'Infers type from Merging redirection' {
$errors = $null
$tokens = $null
$ast = [Language.Parser]::ParseInput("p4 resolve ... 2>&1", [ref] $tokens, [ref] $errors)
$res = [AstTypeInference]::InferTypeOf( $ast.EndBlock.Statements[0].PipelineElements[0].Redirections[0] )
$res.Count | Should be 0
}
It 'Infers type from File redirection' {
$errors = $null
$tokens = $null
$ast = [Language.Parser]::ParseInput("p4 resolve ... > foo.txt", [ref] $tokens, [ref] $errors)
$res = [AstTypeInference]::InferTypeOf( $ast.EndBlock.Statements[0].PipelineElements[0].Redirections[0] )
$res.Count | Should be 0
}
It 'Infers type of alias property' {
class X {
[int] $Length
}
Update-TypeData -Typename X -MemberType AliasProperty -MemberName AliasLength -Value Length -Force
$res = [AstTypeInference]::InferTypeOf( {
[x]::new().AliasLength
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of code property' {
class X {
static [int] CodeProp([psobject] $o) { return 1 }
}
class Y {}
Update-TypeData -TypeName Y -MemberName CodeProp -MemberType CodeProperty -Value ([X].GetMethod("CodeProp")) -Force
$res = [AstTypeInference]::InferTypeOf( {
[Y]::new().CodeProp
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of script property' {
class Y {}
Update-TypeData -TypeName Y -MemberName ScriptProp -MemberType ScriptProperty -Value {1} -Force
$res = [AstTypeInference]::InferTypeOf( {
[Y]::new().ScriptProp
}.Ast)
$res.Count | Should be 0
}
It 'Infers type of script property with outputtype' {
class Y {}
Update-TypeData -TypeName Y -MemberName ScriptProp -MemberType ScriptProperty -Value {[OutputType([int])]param()1} -Force
$res = [AstTypeInference]::InferTypeOf( {
[Y]::new().ScriptProp
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of script method with outputtype' {
class Y {}
Update-TypeData -TypeName Y -MemberName MyScriptMethod -MemberType ScriptMethod -Value {[OutputType([int])]param()1} -Force
$res = [AstTypeInference]::InferTypeOf( {
[Y]::new().MyScriptMethod
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of note property' {
$res = [AstTypeInference]::InferTypeOf( {
[pscustomobject] @{
A = ''
}.A
}.Ast)
$res.Count | Should be 1
$res.Name | Should be 'System.Management.Automation.PSObject'
}
It 'Infers type of try catch finally' {
$res = [AstTypeInference]::InferTypeOf( {
try {
1
}
catch [exception] {
"Text"
}
finally {
[int]
}
}.Ast)
$res.Count | Should be 3
foreach ($r in $res) {
$r.Name -in 'System.Int32', 'System.String', 'System.Type' | Should be $true
}
}
It "Infers type from trap statement" {
$res = [AstTypeInference]::InferTypeOf( {
trap {
"text"
}
}.Ast.EndBlock.Traps[0])
$res.Count | Should Be 1
$res.Name | Should be 'System.String'
}
It "Infers type from exit statement" {
$res = [AstTypeInference]::InferTypeOf( {
exit
}.Ast.EndBlock)
$res.Count | Should Be 0
}
It 'Infers type of Where/Sort/Foreach pipeline' {
$res = [AstTypeInference]::InferTypeOf( {
[int[]](1..10) | Sort-Object -Descending | Where-Object {$_ -gt 3} | ForEach-Object {$_.ToString()}
}.Ast)
$res.Name | Should be System.String
}
It 'Infers type of Method accessed as Property' {
$res = [AstTypeInference]::InferTypeOf( {
''.ToString
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Management.Automation.PSMethod
}
It 'Infers int from List[int] with foreach' {
$res = [AstTypeInference]::InferTypeOf( {
$l = [System.Collections.Generic.List[string]]::new()
$l | ForEach-Object {$_}
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.String
}
It 'Infers class type' {
$res = [AstTypeInference]::InferTypeOf( {
class X {
[int] $I
[bool] Method() {
return $true
}
}
}.Ast)
$res.Count | Should be 0
}
Context "TestDrivePath" {
BeforeAll {
$errors = $null
$tokens = $null
$p = Resolve-path TestDrive:/
}
It 'Infers type of command parameter' {
$ast = [Language.Parser]::ParseInput("Get-ChildItem -Path $p/foo.txt", [ref] $tokens, [ref] $errors)
$cmdParam = $ast.EndBlock.Statements[0].Pipelineelements[0].CommandElements[1]
$res = [AstTypeInference]::InferTypeOf( $cmdParam )
$res.Count | Should be 0
}
It 'Infers type of command parameter - second form' {
$ast = [Language.Parser]::ParseInput("Get-ChildItem -LiteralPath $p/foo.txt", [ref] $tokens, [ref] $errors)
$cmdParam = $ast.EndBlock.Statements[0].Pipelineelements[0].CommandElements[1]
$res = [AstTypeInference]::InferTypeOf( $cmdParam )
$res.Count | Should be 0
}
It 'Infers type of common commands with Path parameter' {
$ast = [Language.Parser]::ParseInput("Get-ChildItem -Path:$p/foo.txt", [ref] $tokens, [ref] $errors)
$cmdAst = $ast.EndBlock.Statements[0].Pipelineelements[0]
$res = [AstTypeInference]::InferTypeOf( $cmdAst )
$res.Count | Should be 2
foreach ($r in $res) {
$r.Name -in 'System.IO.FileInfo', 'System.IO.DirectoryInfo' | Should be $true
}
}
It 'Infers type of common commands with LiteralPath parameter' {
$ast = [Language.Parser]::ParseInput("Get-ChildItem -LiteralPath:$p/foo.txt", [ref] $tokens, [ref] $errors)
$cmdAst = $ast.EndBlock.Statements[0].Pipelineelements[0]
$res = [AstTypeInference]::InferTypeOf( $cmdAst )
$res.Count | Should be 2
foreach ($r in $res) {
$r.Name -in 'System.IO.FileInfo', 'System.IO.DirectoryInfo' | Should be $true
}
}
}
It 'Infers type of variable $_ in hashtable in command parameter' {
$variableAst = {1..10 | Format-table @{n = 'x'; ex = {$_}}}.ast.Find( {param($a) $a -is [System.Management.Automation.Language.VariableExpressionAst]}, $true)
$res = [AstTypeInference]::InferTypeOf( $variableAst)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of variable $_ in hashtable from Array' {
$variableAst = { [int[]]::new(10) | Format-table @{n = 'x'; ex = {$_}}}.ast.Find( {param($a) $a -is [System.Management.Automation.Language.VariableExpressionAst]}, $true)
$res = [AstTypeInference]::InferTypeOf( $variableAst)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of variable $_ in hashtable from generic IEnumerable ' {
$variableAst = { [System.Collections.Generic.List[int]]::new() | Format-table @{n = 'x'; ex = {$_}}}.ast.Find( {param($a) $a -is [System.Management.Automation.Language.VariableExpressionAst]}, $true)
$res = [AstTypeInference]::InferTypeOf( $variableAst)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of variable $_ command parameter' {
$variableAst = { 1..10 | Group-Object {$_.Length}}.ast.Find( {param($a) $a -is [System.Management.Automation.Language.VariableExpressionAst]}, $true)
$res = [AstTypeInference]::InferTypeOf( $variableAst)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of function member' {
$res = [AstTypeInference]::InferTypeOf( {
class X {
[DateTime] GetDate() { return [datetime]::Now }
}
}.Ast.Find( {param($ast) $ast -is [System.Management.Automation.Language.FunctionMemberAst]}, $true))
$res.Count | Should be 0
}
It 'Infers type of MemberExpression on class property' {
class X {
[DateTime] $Date
}
$x = [X]::new()
$res = [AstTypeInference]::InferTypeOf( {
$x.Date
}.Ast.Find( {param($ast) $ast -is [System.Management.Automation.Language.MemberExpressionAst] -and $ast.Member.Value -eq 'Date'}, $true))
$res.Count | Should be 1
$res.Name | Should Be System.DateTime
}
It 'Infers type of MemberExpression on class Method' {
class X {
[DateTime] GetDate() { return [DateTime]::Now }
}
$x = [X]::new()
$res = [AstTypeInference]::InferTypeOf( {
$x.GetDate()
}.Ast.Find( {param($ast) $ast -is [System.Management.Automation.Language.MemberExpressionAst] -and $ast.Member.Value -eq 'GetDate'}, $true))
$res.Count | Should be 1
$res.Name | Should Be System.DateTime
}
It 'Infers type of note property with safe eval' -Skip {
$res = [AstTypeInference]::InferTypeOf( {
[pscustomobject] @{
A = ''
}.A
}.Ast)
$res.Count | Should be 1
$res.Name | Should be 'System.String'
}
It 'Infers type of invoke operator scriptblock' -Skip {
$res = [AstTypeInference]::InferTypeOf( {
& {1}
}.Ast)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of script property with safe eval' -Skip {
class Y {}
Update-TypeData -TypeName Y -MemberName SafeEvalScriptProp -MemberType ScriptProperty -Value {1} -Force
$res = [AstTypeInference]::InferTypeOf( {
[Y]::new().SafeEvalScriptProp
}.Ast, [TypeInferenceRuntimePermissions]::AllowSafeEval)
$res.Count | Should be 1
$res.Name | Should be System.Int32
}
It 'Infers type of base ctor' -Skip {
$res = [AstTypeInference]::InferTypeOf( {
class BaseType {
[string] $s
BaseType([string]$s) {$this.s = $s}
}
class X : BaseType {
X() : base("foo") {}
}
}.Ast.Find( {param($ast) $ast -is [System.Management.Automation.Language.BaseCtorInvokeMemberExpressionAst]}, $true))
$res.Count | Should be BaseType
}
}
Describe "AstTypeInference tests" -Tags CI {
It "Infers type from integer with passed in powershell instance" {
$powerShell = [PowerShell]::Create([RunspaceMode]::CurrentRunspace)
$res = [AstTypeInference]::InferTypeOf( { 1 }.Ast, $powerShell)
$res.Count | Should Be 1
$res.Name | Should be 'System.Int32'
}
It "Infers type from integer with passed in powershell instance and typeinferencespermissions" {
$powerShell = [PowerShell]::Create([RunspaceMode]::CurrentRunspace)
$v = 1
$res = [AstTypeInference]::InferTypeOf( { $v }.Ast, $powerShell, [TypeInferenceRuntimePermissions]::AllowSafeEval)
$res.Name | Should be 'System.Int32'
}
}