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 }
public void Visit(JsSwitch node) { // not applicable; terminate }
public void Visit(JsSwitch node) { Debug.Fail("shouldn't get here"); }
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(); } } } } } }