public override void Visit()

in AjaxMinDll/JavaScript/AnalyzeNodeVisitor.cs [3537:3754]


        public override void Visit(Switch node)
        {
            if (node != null)
            {
                base.Visit(node);

                if (node.Expression != null && node.Expression.IsDebugOnly)
                {
                    node.Expression = new ConstantWrapper(null, PrimitiveType.Null, node.Expression.Context);
                }

                // if the switch case has a lexical scope, we need to check to make sure anything declared lexically
                // doesn't collide with anything declared as a var underneath (which bubbles up to the variable scope).
                if (node.BlockScope != null)
                {
                    foreach (var lexDecl in node.BlockScope.LexicallyDeclaredNames)
                    {
                        var varDecl = node.BlockScope.VarDeclaredName(lexDecl.Name);
                        if (varDecl != null)
                        {
                            // report the error (lex/const collides with var) or warning (funcdecl collides with var)
                            varDecl.Context.HandleError(JSError.DuplicateLexicalDeclaration, lexDecl is LexicalDeclaration);

                            // mark them both a no-rename to preserve the collision
                            varDecl.VariableField.IfNotNull(v => v.CanCrunch = false);
                            lexDecl.VariableField.IfNotNull(v => v.CanCrunch = false);
                        }
                    }
                }

                // we only want to remove stuff if we are hypercrunching
                if (m_parser.Settings.RemoveUnneededCode)
                {
                    // because we are looking at breaks, we need to know if this
                    // switch statement is labeled
                    string thisLabel = string.Empty;
                    LabeledStatement label = node.Parent as LabeledStatement;
                    if (label != null)
                    {
                        thisLabel = label.Label;
                    }

                    // loop through all the cases, looking for the default.
                    // then, if it's empty (or just doesn't do anything), we can
                    // get rid of it altogether
                    int defaultCase = -1;
                    bool eliminateDefault = false;
                    for (int ndx = 0; ndx < node.Cases.Count; ++ndx)
                    {
                        // it should always be a switch case, but just in case...
                        SwitchCase switchCase = node.Cases[ndx] as SwitchCase;
                        if (switchCase != null)
                        {
                            if (switchCase.IsDefault)
                            {
                                // save the index for later
                                defaultCase = ndx;

                                // set the flag to true unless we can prove that we need it.
                                // we'll prove we need it by finding the statement block executed by
                                // this case and showing that it's neither empty nor containing
                                // just a single break statement.
                                eliminateDefault = true;
                            }

                            // if the default case is empty, then we need to keep going
                            // until we find the very next non-empty case
                            if (eliminateDefault && switchCase.Statements.Count > 0)
                            {
                                // this is the set of statements executed during default processing.
                                // if it does nothing -- one break statement -- then we can get rid
                                // of the default case. Otherwise we need to leave it in.
                                if (switchCase.Statements.Count == 1)
                                {
                                    // see if it's a break
                                    Break lastBreak = switchCase.Statements[0] as Break;

                                    // if the last statement is not a break,
                                    // OR it has a label and it's not this switch statement...
                                    if (lastBreak == null
                                      || (lastBreak.Label != null && lastBreak.Label != thisLabel))
                                    {
                                        // set the flag back to false to indicate that we need to keep it.
                                        eliminateDefault = false;
                                    }
                                }
                                else
                                {
                                    // set the flag back to false to indicate that we need to keep it.
                                    eliminateDefault = false;
                                }

                                // break out of the loop
                                break;
                            }
                        }
                    }

                    // if we get here and the flag is still true, then either the default case is
                    // empty, or it contains only a single break statement. Either way, we can get 
                    // rid of it.
                    if (eliminateDefault && defaultCase >= 0
                        && m_parser.Settings.IsModificationAllowed(TreeModifications.RemoveEmptyDefaultCase))
                    {
                        // remove it and reset the position index
                        node.Cases.RemoveAt(defaultCase);
                        defaultCase = -1;
                    }

                    // if we have no default handling, then we know we can get rid
                    // of any cases that don't do anything either.
                    if (defaultCase == -1
                        && m_parser.Settings.IsModificationAllowed(TreeModifications.RemoveEmptyCaseWhenNoDefault))
                    {
                        // when we delete a case statement, we set this flag to true.
                        // when we hit a non-empty case statement, we set the flag to false.
                        // if we hit an empty case statement when this flag is true, we can delete this case, too.
                        bool emptyStatements = true;
                        Break deletedBreak = null;

                        // walk the tree backwards because we don't know how many we will
                        // be deleting, and if we go backwards, we won't have to adjust the 
                        // index as we go.
                        for (int ndx = node.Cases.Count - 1; ndx >= 0; --ndx)
                        {
                            // should always be a switch case
                            SwitchCase switchCase = node.Cases[ndx] as SwitchCase;
                            if (switchCase != null)
                            {
                                // if the block is empty and the last block was empty, we can delete this case.
                                // OR if there is only one statement and it's a break, we can delete it, too.
                                if (switchCase.Statements.Count == 0 && emptyStatements)
                                {
                                    // remove this case statement because it falls through to a deleted case
                                    DetachReferences.Apply(switchCase.CaseValue);
                                    node.Cases.RemoveAt(ndx);
                                }
                                else
                                {
                                    // onlyBreak will be set to null if this block is not a single-statement break block
                                    Break onlyBreak = (switchCase.Statements.Count == 1 ? switchCase.Statements[0] as Break : null);
                                    if (onlyBreak != null)
                                    {
                                        // we'll only delete this case if the break either doesn't have a label
                                        // OR the label matches the switch statement
                                        if (onlyBreak.Label == null || onlyBreak.Label == thisLabel)
                                        {
                                            // if this is a block with only a break, then we need to keep a hold of the break
                                            // statement in case we need it later
                                            deletedBreak = onlyBreak;

                                            // remove this case statement
                                            DetachReferences.Apply(switchCase.CaseValue);
                                            node.Cases.RemoveAt(ndx);
                                            // make sure the flag is set so we delete any other empty
                                            // cases that fell through to this empty case block
                                            emptyStatements = true;
                                        }
                                        else
                                        {
                                            // the break statement has a label and it's not the switch statement.
                                            // we're going to keep this block
                                            emptyStatements = false;
                                            deletedBreak = null;
                                        }
                                    }
                                    else
                                    {
                                        // either this is a non-empty block, or it's an empty case that falls through
                                        // to a non-empty block. if we have been deleting case statements and this
                                        // is not an empty block....
                                        if (emptyStatements && switchCase.Statements.Count > 0 && deletedBreak != null)
                                        {
                                            // we'll need to append the deleted break statement if it doesn't already have
                                            // a flow-changing statement: break, continue, return, or throw
                                            AstNode lastStatement = switchCase.Statements[switchCase.Statements.Count - 1];
                                            if (!(lastStatement is Break) && !(lastStatement is ContinueNode)
                                              && !(lastStatement is ReturnNode) && !(lastStatement is ThrowNode))
                                            {
                                                switchCase.Statements.Append(deletedBreak);
                                            }
                                        }

                                        // make sure the deletedBreak flag is reset
                                        deletedBreak = null;

                                        // reset the flag
                                        emptyStatements = false;
                                    }
                                }
                            }
                        }
                    }

                    // if the last case's statement list ends in a break, 
                    // we can get rid of the break statement
                    if (node.Cases.Count > 0
                        && m_parser.Settings.IsModificationAllowed(TreeModifications.RemoveBreakFromLastCaseBlock))
                    {
                        SwitchCase lastCase = node.Cases[node.Cases.Count - 1] as SwitchCase;
                        if (lastCase != null)
                        {
                            // get the block of statements making up the last case block
                            Block lastBlock = lastCase.Statements;
                            // if the last statement is not a break, then lastBreak will be null
                            Break lastBreak = (lastBlock.Count > 0 ? lastBlock[lastBlock.Count - 1] as Break : null);
                            // if lastBreak is not null and it either has no label, or the label matches this switch statement...
                            if (lastBreak != null
                              && (lastBreak.Label == null || lastBreak.Label == thisLabel))
                            {
                                // remove the break statement
                                lastBlock.RemoveLast();
                            }
                        }
                    }
                }
            }
        }