internal void InvokeWithPipeImpl(ScriptBlockClauseToInvoke clauseToInvoke,
                                                bool createLocalScope,
                                                Dictionary<string, ScriptBlock> functionsToDefine,
                                                List<PSVariable> variablesToDefine,
                                                ErrorHandlingBehavior errorHandlingBehavior,
                                                object dollarUnder,
                                                object input,
                                                object scriptThis,
                                                Pipe outputPipe,
                                                InvocationInfo invocationInfo,
                                                params object[] args)
        {
            if (clauseToInvoke == ScriptBlockClauseToInvoke.Begin && !HasBeginBlock)
            {
                return;
            }
            else if (clauseToInvoke == ScriptBlockClauseToInvoke.Process && !HasProcessBlock)
            {
                return;
            }
            else if (clauseToInvoke == ScriptBlockClauseToInvoke.End && !HasEndBlock)
            {
                return;
            }

            ExecutionContext context = GetContextFromTLS();
            Diagnostics.Assert(SessionStateInternal == null || SessionStateInternal.ExecutionContext == context,
                               "The scriptblock is being invoked in a runspace different than the one where it was created");

            if (context.CurrentPipelineStopping)
            {
                throw new PipelineStoppedException();
            }

            // Validate at the arguments are consistent. The only public API that gets you here never sets createLocalScope to false...
            Diagnostics.Assert(createLocalScope == true || functionsToDefine == null, "When calling ScriptBlock.InvokeWithContext(), if 'functionsToDefine' != null then 'createLocalScope' must be true");
            Diagnostics.Assert(createLocalScope == true || variablesToDefine == null, "When calling ScriptBlock.InvokeWithContext(), if 'variablesToDefine' != null then 'createLocalScope' must be true");

            if (args == null)
            {
                args = Utils.EmptyArray<object>();
            }

            bool runOptimized = context._debuggingMode > 0 ? false : createLocalScope;
            var codeToInvoke = GetCodeToInvoke(ref runOptimized, clauseToInvoke);
            if (codeToInvoke == null)
                return;

            if (outputPipe == null)
            {
                // If we don't have a pipe to write to, we need to discard all results.
                outputPipe = new Pipe { NullPipe = true };
            }

            var locals = MakeLocalsTuple(runOptimized);

            if (dollarUnder != AutomationNull.Value)
            {
                locals.SetAutomaticVariable(AutomaticVariable.Underbar, dollarUnder, context);
            }
            if (input != AutomationNull.Value)
            {
                locals.SetAutomaticVariable(AutomaticVariable.Input, input, context);
            }
            if (scriptThis != AutomationNull.Value)
            {
                locals.SetAutomaticVariable(AutomaticVariable.This, scriptThis, context);
            }
            SetPSScriptRootAndPSCommandPath(locals, context);

            var oldShellFunctionErrorOutputPipe = context.ShellFunctionErrorOutputPipe;
            var oldExternalErrorOutput = context.ExternalErrorOutput;
            var oldScopeOrigin = context.EngineSessionState.CurrentScope.ScopeOrigin;
            var oldSessionState = context.EngineSessionState;

            // If the script block has a different language mode than the current,
            // change the language mode.
            PSLanguageMode? oldLanguageMode = null;
            PSLanguageMode? newLanguageMode = null;
            if ((this.LanguageMode.HasValue) &&
                (this.LanguageMode != context.LanguageMode))
            {
                oldLanguageMode = context.LanguageMode;
                newLanguageMode = this.LanguageMode;
            }

            Dictionary<string, PSVariable> backupWhenDotting = null;
            try
            {
                var myInvocationInfo = invocationInfo;
                if (myInvocationInfo == null)
                {
                    var callerFrame = context.Debugger.GetCallStack().LastOrDefault();
                    var extent = (callerFrame != null)
                        ? callerFrame.FunctionContext.CurrentPosition
                        : Ast.Extent;
                    myInvocationInfo = new InvocationInfo(null, extent, context);
                }

                locals.SetAutomaticVariable(AutomaticVariable.MyInvocation, myInvocationInfo, context);

                if (SessionStateInternal != null)
                    context.EngineSessionState = SessionStateInternal;

                // If we don't want errors written, hide the error pipe.
                switch (errorHandlingBehavior)
                {
                    case ErrorHandlingBehavior.WriteToCurrentErrorPipe:
                        // no need to do anything
                        break;
                    case ErrorHandlingBehavior.WriteToExternalErrorPipe:
                        context.ShellFunctionErrorOutputPipe = null;
                        break;
                    case ErrorHandlingBehavior.SwallowErrors:
                        context.ShellFunctionErrorOutputPipe = null;
                        context.ExternalErrorOutput = new DiscardingPipelineWriter();
                        break;
                }

                if (createLocalScope)
                {
                    var newScope = context.EngineSessionState.NewScope(false);
                    context.EngineSessionState.CurrentScope = newScope;
                    newScope.LocalsTuple = locals;
                    // Inject passed in functions into the scope
                    if (functionsToDefine != null)
                    {
                        foreach (var def in functionsToDefine)
                        {
                            if (string.IsNullOrWhiteSpace(def.Key))
                            {
                                PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(
                                    ParserStrings.EmptyFunctionNameInFunctionDefinitionDictionary);

                                e.SetErrorId("EmptyFunctionNameInFunctionDefinitionDictionary");
                                throw e;
                            }
                            if (def.Value == null)
                            {
                                PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(
                                    ParserStrings.NullFunctionBodyInFunctionDefinitionDictionary, def.Key);

                                e.SetErrorId("NullFunctionBodyInFunctionDefinitionDictionary");
                                throw e;
                            }
                            newScope.FunctionTable.Add(def.Key, new FunctionInfo(def.Key, def.Value, context));
                        }
                    }
                    // Inject passed in variables into the scope
                    if (variablesToDefine != null)
                    {
                        int index = 0;
                        foreach (var psvar in variablesToDefine)
                        {
                            // Check for null entries.
                            if (psvar == null)
                            {
                                PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(
                                    ParserStrings.NullEntryInVariablesDefinitionList, index);

                                e.SetErrorId("NullEntryInVariablesDefinitionList");
                                throw e;
                            }
                            string name = psvar.Name;
                            Diagnostics.Assert(!(string.Equals(name, "this") || string.Equals(name, "_") || string.Equals(name, "input")),
                                "The list of variables to set in the scriptblock's scope cannot contain 'this', '_' or 'input'. These variables should be removed before passing the collection to this routine.");
                            index++;
                            newScope.Variables.Add(name, psvar);
                        }
                    }
                }
                else
                {
                    if (context.EngineSessionState.CurrentScope.LocalsTuple == null)
                    {
                        // If the locals tuple is, that means either:
                        //     * we're invoking a script block for a module
                        //     * something unexpected
                        context.EngineSessionState.CurrentScope.LocalsTuple = locals;
                    }
                    else
                    {
                        context.EngineSessionState.CurrentScope.DottedScopes.Push(locals);
                        backupWhenDotting = new Dictionary<string, PSVariable>();
                    }
                }

                // Set the language mode
                if (newLanguageMode.HasValue)
                {
                    context.LanguageMode = newLanguageMode.Value;
                }

                args = BindArgumentsForScriptblockInvoke(
                    (RuntimeDefinedParameter[])RuntimeDefinedParameters.Data,
                    args, context, !createLocalScope, backupWhenDotting, locals);
                locals.SetAutomaticVariable(AutomaticVariable.Args, args, context);

                context.EngineSessionState.CurrentScope.ScopeOrigin = CommandOrigin.Internal;

                var functionContext = new FunctionContext
                {
                    _executionContext = context,
                    _outputPipe = outputPipe,
                    _localsTuple = locals,
                    _scriptBlock = this,
                    _file = this.File,
                    _debuggerHidden = this.DebuggerHidden,
                    _debuggerStepThrough = this.DebuggerStepThrough,
                    _sequencePoints = SequencePoints,
                };

                ScriptBlock.LogScriptBlockStart(this, context.CurrentRunspace.InstanceId);

                try
                {
                    codeToInvoke(functionContext);
                }
                finally
                {
                    ScriptBlock.LogScriptBlockEnd(this, context.CurrentRunspace.InstanceId);
                }
            }
            catch (TargetInvocationException tie)
            {
                // DynamicInvoke always wraps, so unwrap here.
                throw tie.InnerException;
            }
            finally
            {
                // Restore the language mode
                if (oldLanguageMode.HasValue)
                {
                    context.LanguageMode = oldLanguageMode.Value;
                }

                // Now restore the output pipe...
                context.ShellFunctionErrorOutputPipe = oldShellFunctionErrorOutputPipe;
                context.ExternalErrorOutput = oldExternalErrorOutput;

                // Restore the interactive command state...
                context.EngineSessionState.CurrentScope.ScopeOrigin = oldScopeOrigin;

                if (createLocalScope)
                {
                    context.EngineSessionState.RemoveScope(context.EngineSessionState.CurrentScope);
                }
                else if (backupWhenDotting != null)
                {
                    context.EngineSessionState.CurrentScope.DottedScopes.Pop();

                    Diagnostics.Assert(backupWhenDotting != null, "when dotting, this dictionary isn't null");
                    foreach (var pair in backupWhenDotting)
                    {
                        if (pair.Value != null)
                        {
                            context.EngineSessionState.SetVariable(pair.Value, false, CommandOrigin.Internal);
                        }
                        else
                        {
                            context.EngineSessionState.RemoveVariable(pair.Key);
                        }
                    }
                }

                // Restore session state...
                context.EngineSessionState = oldSessionState;
            }
        }
        private Action<FunctionContext> GetCodeToInvoke(ref bool optimized, ScriptBlockClauseToInvoke clauseToInvoke)
        {
            if (clauseToInvoke == ScriptBlockClauseToInvoke.ProcessBlockOnly && (HasBeginBlock || (HasEndBlock && HasProcessBlock)))
            {
                throw PSTraceSource.NewInvalidOperationException(AutomationExceptions.ScriptBlockInvokeOnOneClauseOnly);
            }

            optimized = _scriptBlockData.Compile(optimized);

            if (optimized)
            {
                switch (clauseToInvoke)
                {
                    case ScriptBlockClauseToInvoke.Begin:
                        return _scriptBlockData.BeginBlock;
                    case ScriptBlockClauseToInvoke.Process:
                        return _scriptBlockData.ProcessBlock;
                    case ScriptBlockClauseToInvoke.End:
                        return _scriptBlockData.EndBlock;
                    default:
                        return HasProcessBlock ? _scriptBlockData.ProcessBlock : _scriptBlockData.EndBlock;
                }
            }
            switch (clauseToInvoke)
            {
                case ScriptBlockClauseToInvoke.Begin:
                    return _scriptBlockData.UnoptimizedBeginBlock;
                case ScriptBlockClauseToInvoke.Process:
                    return _scriptBlockData.UnoptimizedProcessBlock;
                case ScriptBlockClauseToInvoke.End:
                    return _scriptBlockData.UnoptimizedEndBlock;
                default:
                    return HasProcessBlock ? _scriptBlockData.UnoptimizedProcessBlock : _scriptBlockData.UnoptimizedEndBlock;
            }
        }