public void Visit(JsSwitch node)
        {
            if (node != null)
            {
                node.Index = NextOrderIndex;
                if (node.Expression != null)
                {
                    node.Expression.Accept(this);
                }

                // the switch has its own block scope to use for all the blocks that are under
                // its child switch-case nodes
                node.BlockScope = new JsBlockScope(CurrentLexicalScope, node.Context, m_settings)
                {
                    IsInWithScope = m_withDepth > 0
                };
                m_lexicalStack.Push(node.BlockScope);

                try
                {
                    if (node.Cases != null)
                    {
                        node.Cases.Accept(this);
                    }
                }
                finally
                {
                    Debug.Assert(CurrentLexicalScope == node.BlockScope);
                    m_lexicalStack.Pop();
                }

                // if the block has no lex-decls, we really don't need a separate scope.
                if (node.BlockScope.LexicallyDeclaredNames.Count == 0)
                {
                    CollapseBlockScope(node.BlockScope);
                    node.BlockScope = null;
                }
            }
        }
 public void Visit(JsSwitch node)
 {
     // invalid! ignore
     IsValid = false;
 }
 public void Visit(JsSwitch node)
 {
     // starts with 'switch', so we don't care
 }
Example #4
0
 public void Visit(JsSwitch node)
 {
     // not applicable; terminate
 }
Example #5
0
 public void Visit(JsSwitch node)
 {
     // invalid! ignore
     IsValid = false;
 }
        public void Visit(JsSwitch node)
        {
            if (node != null)
            {
                node.Index = NextOrderIndex;
                if (node.Expression != null)
                {
                    node.Expression.Accept(this);
                }

                // the switch has its own block scope to use for all the blocks that are under
                // its child switch-case nodes
                node.BlockScope = new JsBlockScope(CurrentLexicalScope, node.Context, m_settings)
                {
                    IsInWithScope = m_withDepth > 0
                };
                m_lexicalStack.Push(node.BlockScope);

                try
                {
                    if (node.Cases != null)
                    {
                        node.Cases.Accept(this);
                    }
                }
                finally
                {
                    Debug.Assert(CurrentLexicalScope == node.BlockScope);
                    m_lexicalStack.Pop();
                }

                // if the block has no lex-decls, we really don't need a separate scope.
                if (node.BlockScope.LexicallyDeclaredNames.Count == 0)
                {
                    CollapseBlockScope(node.BlockScope);
                    node.BlockScope = null;
                }
            }
        }
 public void Visit(JsSwitch node)
 {
     Debug.Fail("shouldn't get here");
 }
Example #8
0
 public void Visit(JsSwitch node)
 {
     Debug.Fail("shouldn't get here");
 }
 public void Visit(JsSwitch node)
 {
     // not applicable; terminate
 }
        public void Visit(JsSwitch node)
        {
            if (node != null)
            {
                var symbol = StartSymbol(node);

                Output("switch");
                MarkSegment(node, null, node.Context);
                SetContextOutputPosition(node.Context);
                if (m_settings.OutputMode == MinifierOutputMode.MultipleLines)
                {
                    OutputPossibleLineBreak(' ');
                }

                OutputPossibleLineBreak('(');
                m_startOfStatement = false;
                if (node.Expression != null)
                {
                    node.Expression.Accept(this);
                }
                OutputPossibleLineBreak(')');
                if (m_settings.BlocksStartOnSameLine == MinifierBlockStart.NewLine
                    || (m_settings.BlocksStartOnSameLine == MinifierBlockStart.UseSource && node.BraceOnNewLine))
                {
                    NewLine();
                }
                else if (m_settings.OutputMode == MinifierOutputMode.MultipleLines)
                {
                    OutputPossibleLineBreak(' ');
                }

                OutputPossibleLineBreak('{');
                MarkSegment(node, null, node.BraceContext);
                Indent();

                JsAstNode prevSwitchCase = null;
                for (var ndx = 0; ndx < node.Cases.Count; ++ndx)
                {
                    var switchCase = node.Cases[ndx];
                    if (switchCase != null)
                    {
                        if (prevSwitchCase != null && prevSwitchCase.RequiresSeparator)
                        {
                            // because the next switch-case will always start with either the case or default
                            // keyword, if the semicolon we are about the output would be at the end of a newline,
                            // we can omit the semicolon altogether and just let the semicolon-insertion rules
                            // kick in.
                            if (ReplaceableSemicolon())
                            {
                                MarkSegment(prevSwitchCase, null, prevSwitchCase.TerminatingContext);
                            }
                        }

                        NewLine();
                        switchCase.Accept(this);
                        prevSwitchCase = switchCase;
                    }
                }

                Unindent();
                NewLine();
                OutputPossibleLineBreak('}');
                MarkSegment(node, null, node.BraceContext);

                EndSymbol(symbol);
            }
        }
        public override void Visit(JsSwitch node)
        {
            if (node != null)
            {
                base.Visit(node);

                // 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.NameContext.HandleError(JsError.DuplicateLexicalDeclaration, lexDecl is JsLexicalDeclaration);

                            // 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;
                    JsLabeledStatement label = node.Parent as JsLabeledStatement;
                    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...
                        JsSwitchCase switchCase = node.Cases[ndx] as JsSwitchCase;
                        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
                                    JsBreak lastBreak = switchCase.Statements[0] as JsBreak;

                                    // 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(JsTreeModifications.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(JsTreeModifications.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;
                        JsBreak 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
                            JsSwitchCase switchCase = node.Cases[ndx] as JsSwitchCase;
                            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
                                    JsDetachReferences.Apply(switchCase.CaseValue);
                                    node.Cases.RemoveAt(ndx);
                                }
                                else
                                {
                                    // onlyBreak will be set to null if this block is not a single-statement break block
                                    JsBreak onlyBreak = (switchCase.Statements.Count == 1 ? switchCase.Statements[0] as JsBreak : 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
                                            JsDetachReferences.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
                                            JsAstNode lastStatement = switchCase.Statements[switchCase.Statements.Count - 1];
                                            if (!(lastStatement is JsBreak) && !(lastStatement is JsContinueNode)
                                              && !(lastStatement is JsReturnNode) && !(lastStatement is JsThrowNode))
                                            {
                                                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(JsTreeModifications.RemoveBreakFromLastCaseBlock))
                    {
                        JsSwitchCase lastCase = node.Cases[node.Cases.Count - 1] as JsSwitchCase;
                        if (lastCase != null)
                        {
                            // get the block of statements making up the last case block
                            JsBlock lastBlock = lastCase.Statements;
                            // if the last statement is not a break, then lastBreak will be null
                            JsBreak lastBreak = (lastBlock.Count > 0 ? lastBlock[lastBlock.Count - 1] as JsBreak : 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();
                            }
                        }
                    }
                }
            }
        }