/// <summary> /// Get using values as dictionary for the Foreach-Object parallel cmdlet. /// Ignore any using expressions that are associated with inner nested Foreach-Object parallel calls, /// since they are only effective in the nested call scope and not the current outer scope. /// </summary> /// <param name = "scriptBlock">Scriptblock to search.</param> /// <param name = "isTrustedInput">True when input is trusted.</param> /// <param name = "context">Execution context.</param> /// <param name = "foreachNames">List of foreach command names and aliases.</param> /// <returns>Dictionary of using variable map.</returns> internal static Dictionary <string, object> GetUsingValuesForEachParallel( ScriptBlock scriptBlock, bool isTrustedInput, ExecutionContext context, string[] foreachNames) { // Using variables for Foreach-Object -Parallel use are restricted to be within the // Foreach-Object -Parallel call scope. This will filter the using variable map to variables // only within the current (outer) Foreach-Object -Parallel call scope. var usingAsts = UsingExpressionAstSearcher.FindAllUsingExpressions(scriptBlock.Ast).ToList(); UsingExpressionAst usingAst = null; var usingValueMap = new Dictionary <string, object>(usingAsts.Count); Version oldStrictVersion = null; try { if (context != null) { oldStrictVersion = context.EngineSessionState.CurrentScope.StrictModeVersion; context.EngineSessionState.CurrentScope.StrictModeVersion = PSVersionInfo.PSVersion; } for (int i = 0; i < usingAsts.Count; ++i) { usingAst = (UsingExpressionAst)usingAsts[i]; if (IsInForeachParallelCallingScope(usingAst, foreachNames)) { var value = Compiler.GetExpressionValue(usingAst.SubExpression, isTrustedInput, context); string usingAstKey = PsUtils.GetUsingExpressionKey(usingAst); usingValueMap.TryAdd(usingAstKey, value); } } } catch (RuntimeException rte) { if (rte.ErrorRecord.FullyQualifiedErrorId.Equals("VariableIsUndefined", StringComparison.Ordinal)) { throw InterpreterError.NewInterpreterException( targetObject: null, exceptionType: typeof(RuntimeException), errorPosition: usingAst.Extent, resourceIdAndErrorId: "UsingVariableIsUndefined", resourceString: AutomationExceptions.UsingVariableIsUndefined, args: rte.ErrorRecord.TargetObject); } } finally { if (context != null) { context.EngineSessionState.CurrentScope.StrictModeVersion = oldStrictVersion; } } return(usingValueMap); }
private void ConvertCommand(CommandAst commandAst, bool isTrustedInput) { // First need command name. var commandName = GetCommandName(commandAst.CommandElements[0], isTrustedInput); var command = new Command(commandName, isScript: false, useLocalScope: _createLocalScope); // Handle redirections, if any (there can really be just 0 or 1). if (commandAst.Redirections.Count > 0) { Diagnostics.Assert(commandAst.Redirections.Count == 1, "only 1 kind of redirection is supported"); Diagnostics.Assert(commandAst.Redirections[0] is MergingRedirectionAst, "unexpected redirection type"); PipelineResultTypes toType = PipelineResultTypes.Output; PipelineResultTypes fromType; switch (commandAst.Redirections[0].FromStream) { case RedirectionStream.Error: fromType = PipelineResultTypes.Error; break; case RedirectionStream.Warning: fromType = PipelineResultTypes.Warning; break; case RedirectionStream.Verbose: fromType = PipelineResultTypes.Verbose; break; case RedirectionStream.Debug: fromType = PipelineResultTypes.Debug; break; case RedirectionStream.Information: fromType = PipelineResultTypes.Information; break; case RedirectionStream.All: fromType = PipelineResultTypes.All; break; default: // Default to Error->Output to be compatible with V2. fromType = PipelineResultTypes.Error; break; } command.MergeMyResults(fromType, toType); } _powershell.AddCommand(command); // Now the parameters and arguments. foreach (var ast in commandAst.CommandElements.Skip(1)) { var exprAst = ast as ExpressionAst; if (exprAst != null) { VariableExpressionAst variableAst = null; var usingExprAst = ast as UsingExpressionAst; if (usingExprAst != null) { string usingAstKey = PsUtils.GetUsingExpressionKey(usingExprAst); object usingValue = _usingValueMap[usingAstKey]; variableAst = usingExprAst.SubExpression as VariableExpressionAst; if (variableAst != null && variableAst.Splatted) { // Support the splatting of a dictionary var parameters = usingValue as System.Collections.IDictionary; if (parameters != null) { _powershell.AddParameters(parameters); } else { // Support the splatting of an array var arguments = usingValue as System.Collections.IEnumerable; if (arguments != null) { foreach (object argument in arguments) { _powershell.AddArgument(argument); } } else { // Splat the object directly. _powershell.AddArgument(usingValue); } } } else { _powershell.AddArgument(usingValue); } continue; } variableAst = ast as VariableExpressionAst; if (variableAst != null && variableAst.Splatted) { GetSplattedVariable(variableAst); } else { var constantExprAst = ast as ConstantExpressionAst; object argument; if (constantExprAst != null && LanguagePrimitives.IsNumeric(LanguagePrimitives.GetTypeCode(constantExprAst.StaticType))) { var commandArgumentText = constantExprAst.Extent.Text; argument = constantExprAst.Value; if (!commandArgumentText.Equals(constantExprAst.Value.ToString(), StringComparison.Ordinal)) { // The wrapped number will actually return a PSObject which could end holding a reference to // a typetable, making the object runspace specific. We should find a better way to avoid // any possibility of sharing problems, though this is unlikely to cause problems. argument = ParserOps.WrappedNumber(argument, commandArgumentText); } } else { if (!isTrustedInput) { try { argument = GetSafeValueVisitor.GetSafeValue(exprAst, _context, GetSafeValueVisitor.SafeValueContext.GetPowerShell); } catch (System.Exception) { throw new ScriptBlockToPowerShellNotSupportedException( "CantConvertWithDynamicExpression", null, AutomationExceptions.CantConvertWithDynamicExpression, exprAst.Extent.Text); } } else { argument = GetExpressionValue(exprAst, isTrustedInput); } } _powershell.AddArgument(argument); } } else { AddParameter((CommandParameterAst)ast, isTrustedInput); } } }
/// <summary> /// Collect values for UsingExpressions, in the form of a dictionary and an array. /// - The dictionary form is used when the remote server is PSv5 and later version for handling UsingExpression in Invoke-Command/Start-Job /// - The array form is used when the remote server is PSv3 and PSv4 for handling UsingExpression in Invoke-Command. /// </summary> /// <remarks> /// We still keep the array-form using values because we want to avoid any breaking changes when running Invoke-Command /// targeting PSv3 or PSv4 remote end -- if UsingExpressions are all in the same scope, then we still pass an array of using /// values to the remote end; otherwise, we will handle UsingExpression as if the remote end is PSv2. /// </remarks> /// <returns> /// A tuple of the dictionary-form and the array-form using values. /// If the array-form using value is null, then there are UsingExpressions used in different scopes. /// </returns> private static Tuple <Dictionary <string, object>, object[]> GetUsingValues(Ast body, bool isTrustedInput, ExecutionContext context, Dictionary <string, object> variables, bool filterNonUsingVariables) { Diagnostics.Assert(context != null || variables != null, "can't retrieve variables with no context and no variables"); var usingAsts = UsingExpressionAstSearcher.FindAllUsingExpressionExceptForWorkflow(body).ToList(); var usingValueArray = new object[usingAsts.Count]; var usingValueMap = new Dictionary <string, object>(usingAsts.Count); HashSet <string> usingVarNames = (variables != null && filterNonUsingVariables) ? new HashSet <string>() : null; // Used to check if the PSv3/PSv4 way of handling UsingExpression can continue to be used. bool hasUsingExprInDifferentScope = false; ScriptBlockAst sbClosestToPreUsingExpr = null; UsingExpressionAst usingAst = null; Version oldStrictVersion = null; try { if (context != null) { oldStrictVersion = context.EngineSessionState.CurrentScope.StrictModeVersion; context.EngineSessionState.CurrentScope.StrictModeVersion = PSVersionInfo.PSVersion; } for (int i = 0; i < usingAsts.Count; ++i) { usingAst = (UsingExpressionAst)usingAsts[i]; object value = null; // This happens only when GetUsingValues gets called outside the ScriptBlockToPowerShellConverter class if (!hasUsingExprInDifferentScope && HasUsingExpressionsInDifferentScopes(usingAst, body, ref sbClosestToPreUsingExpr)) { // If there are UsingExpressions in different scopes, the array-form using values will not be useful // even if the remote end is PSv3 or PSv4, because the way we handle using expression in PSv3 and PSv4 // doesn't support UsingExpression in different scopes. In this case, we will set the array-form using // value to be null before return. // // Note that this check only affect array-form using value. In PSv5, we change the way to handle UsingExpression // on both client and server sides. The dictionary-form using values is used and UsingExpression in different // scope is supported. hasUsingExprInDifferentScope = true; } if (variables != null) { var variableAst = usingAst.SubExpression as VariableExpressionAst; if (variableAst == null) { throw InterpreterError.NewInterpreterException(null, typeof(RuntimeException), usingAst.Extent, "CantGetUsingExpressionValueWithSpecifiedVariableDictionary", AutomationExceptions.CantGetUsingExpressionValueWithSpecifiedVariableDictionary, usingAst.Extent.Text); } string varName = variableAst.VariablePath.UserPath; if (varName != null && variables.TryGetValue(varName, out value) && usingVarNames != null) { usingVarNames.Add(varName); } } else { value = Compiler.GetExpressionValue(usingAst.SubExpression, isTrustedInput, context); } // Collect UsingExpression value as an array usingValueArray[i] = value; // Collect UsingExpression value as a dictionary string usingAstKey = PsUtils.GetUsingExpressionKey(usingAst); usingValueMap.TryAdd(usingAstKey, value); } } catch (RuntimeException rte) { if (rte.ErrorRecord.FullyQualifiedErrorId.Equals("VariableIsUndefined", StringComparison.Ordinal)) { throw InterpreterError.NewInterpreterException(null, typeof(RuntimeException), usingAst.Extent, "UsingVariableIsUndefined", AutomationExceptions.UsingVariableIsUndefined, rte.ErrorRecord.TargetObject); } else if (rte.ErrorRecord.FullyQualifiedErrorId.Equals("CantGetUsingExpressionValueWithSpecifiedVariableDictionary", StringComparison.Ordinal)) { throw; } } finally { if (context != null) { context.EngineSessionState.CurrentScope.StrictModeVersion = oldStrictVersion; } } if (usingVarNames != null) { string[] keys = variables.Keys.ToArray(); foreach (string key in keys) { if (!usingVarNames.Contains(key)) { variables.Remove(key); } } } if (hasUsingExprInDifferentScope) { usingValueArray = null; } return(Tuple.Create(usingValueMap, usingValueArray)); }