internal static PowerShell Convert(ScriptBlockAst body,
                                           ReadOnlyCollection <ParameterAst> functionParameters,
                                           bool isTrustedInput,
                                           ExecutionContext context,
                                           Dictionary <string, object> variables,
                                           bool filterNonUsingVariables,
                                           bool?createLocalScope,
                                           object[] args)
        {
            ExecutionContext.CheckStackDepth();

            if (args == null)
            {
                args = Array.Empty <object>();
            }

            // Perform validations on the ScriptBlock.  GetSimplePipeline can allow for more than one
            // pipeline if the first parameter is true, but Invoke-Command doesn't yet support multiple
            // pipelines in a PowerShell (it just grabs the last command directly.)  The rest of this
            // code properly supports multiple pipelines, so it should just work to change the false to true
            // if/when Invoke-Command can support multiple pipelines.
            string errorId;
            string errorMsg;

            body.GetSimplePipeline(true, out errorId, out errorMsg);
            if (errorId != null)
            {
                throw new ScriptBlockToPowerShellNotSupportedException(errorId, null, errorMsg);
            }

            var checker = new ScriptBlockToPowerShellChecker {
                ScriptBeingConverted = body
            };

            if (functionParameters != null)
            {
                foreach (var parameter in functionParameters)
                {
                    parameter.InternalVisit(checker);
                }
            }

            body.InternalVisit(checker);

            // When the context is null (or they haven't supplied any variables), throw, but only if we really need the
            // context (basically, if we have some variable reference to resolve).
            if (context == null && (checker.HasUsingExpr || checker.UsesParameter) && (variables == null))
            {
                throw new PSInvalidOperationException(AutomationExceptions.CantConvertScriptBlockWithNoContext);
            }

            try
            {
                var converter = new ScriptBlockToPowerShellConverter {
                    _context = context, _createLocalScope = createLocalScope
                };

                if (checker.HasUsingExpr)
                {
                    converter._usingValueMap = GetUsingValues(body, isTrustedInput, context, variables, filterNonUsingVariables).Item1;
                }

                if (checker.UsesParameter)
                {
                    // If any parameters are used, we create a new scope and bind the parameters.

                    var newScope = context.EngineSessionState.NewScope(false);
                    context.EngineSessionState.CurrentScope             = newScope;
                    context.EngineSessionState.CurrentScope.ScopeOrigin = CommandOrigin.Internal;

                    var locals =
                        MutableTuple.MakeTuple(Compiler.DottedLocalsTupleType, Compiler.DottedLocalsNameIndexMap);

                    // Get the parameter metadata for the script block.
                    // If 'functionParameters' is not null, then the ScriptBlockAst is actually the body of a FunctionDefinitionAst, and it doesn't have a ParamBlock.
                    // If 'functionParameters' is null, then the ScriptBlockAst may have parameters defined in its ParamBlock.
                    bool usesCmdletBinding = false;
                    var  parameters        = functionParameters != null
                                         ? Compiler.GetParameterMetaData(functionParameters, true, ref usesCmdletBinding)
                                         : ((IParameterMetadataProvider)body).GetParameterMetadata(true, ref usesCmdletBinding);

                    object[] remainingArgs = ScriptBlock.BindArgumentsForScriptblockInvoke(
                        (RuntimeDefinedParameter[])parameters.Data, args, context, false, null, locals);
                    locals.SetAutomaticVariable(AutomaticVariable.Args, remainingArgs, context);
                    newScope.LocalsTuple = locals;
                }

                foreach (var pipeline in body.EndBlock.Statements.OfType <PipelineAst>())
                {
                    converter._powershell.AddStatement();
                    converter.ConvertPipeline(pipeline, isTrustedInput);
                }

                return(converter._powershell);
            }
            finally
            {
                if (checker.UsesParameter)
                {
                    context.EngineSessionState.RemoveScope(context.EngineSessionState.CurrentScope);
                }
            }
        }