/// <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); }
/// <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.FindAllUsingExpressions(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) { if (!(usingAst.SubExpression is VariableExpressionAst variableAst)) { 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)); }