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();
}
}
}
}
}
}