public void Visit(JsReturnNode node) { if (node != null) { if (node.Operand != null) { node.Operand.Accept(this); } node.Index = NextOrderIndex; // we can stop marking order for subsequent statements in this block, // since this stops execution m_isUnreachable = true; } }
public void Visit(JsReturnNode node) { // invalid! ignore IsValid = false; }
public void Visit(JsReturnNode node) { // starts with 'return', so we don't care }
public void Visit(JsReturnNode node) { // not applicable; terminate }
public void Visit(JsReturnNode node) { Debug.Fail("shouldn't get here"); }
//--------------------------------------------------------------------------------------- // ParseReturnStatement // // ReturnStatement : // 'return' Expression // // This function may return a null AST under error condition. The caller should handle // that case. // Regardless of error conditions, on exit the parser points to the first token after // the return statement. //--------------------------------------------------------------------------------------- private JsReturnNode ParseReturnStatement() { var returnNode = new JsReturnNode(m_currentToken.Clone(), this); GetNextToken(); if (!m_foundEndOfLine) { if (JsToken.Semicolon != m_currentToken.Token && JsToken.RightCurly != m_currentToken.Token) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); try { returnNode.Operand = ParseExpression(); } catch (RecoveryTokenException exc) { returnNode.Operand = exc._partiallyComputedNode; if (IndexOfToken(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = returnNode; throw; } } finally { if (returnNode.Operand != null) { returnNode.UpdateWith(returnNode.Operand.Context); } m_noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); } } if (JsToken.Semicolon == m_currentToken.Token) { returnNode.TerminatingContext = m_currentToken.Clone(); GetNextToken(); } else if (m_foundEndOfLine || m_currentToken.Token == JsToken.RightCurly || m_currentToken.Token == JsToken.EndOfFile) { // semicolon insertion rules // a right-curly or an end of line is something we don't WANT to throw a warning for. // Just too common and doesn't really warrant a warning (in my opinion) if (JsToken.RightCurly != m_currentToken.Token && JsToken.EndOfFile != m_currentToken.Token) { ReportError(JsError.SemicolonInsertion, returnNode.Context.IfNotNull(c => c.FlattenToEnd()), true); } } else { ReportError(JsError.NoSemicolon, false); } } return returnNode; }
public void Visit(JsReturnNode node) { if (node != null) { var symbol = StartSymbol(node); Output("return"); MarkSegment(node, null, node.Context); SetContextOutputPosition(node.Context); m_startOfStatement = false; if (node.Operand != null) { if (m_settings.OutputMode == MinifierOutputMode.MultipleLines) { Output(' '); } // no page breaks allowed here m_noLineBreaks = true; Indent(); node.Operand.Accept(this); Unindent(); } EndSymbol(symbol); } }
private void CombineReturnWithExpression(JsBlock node, int ndx, JsReturnNode returnNode) { // see if the return node has an expression operand if (returnNode.Operand != null && returnNode.Operand.IsExpression) { // check for lookup[ASSIGN]expr2;return expr1. var beforeExpr = node[ndx - 1] as JsBinaryOperator; JsLookup lookup; if (beforeExpr != null && beforeExpr.IsAssign && (lookup = beforeExpr.Operand1 as JsLookup) != null) { if (returnNode.Operand.IsEquivalentTo(lookup)) { // we have lookup[ASSIGN]expr2;return lookup. // if lookup is a local variable in the current scope, we can replace with return expr2; // if lookup is an outer reference, we can replace with return lookup[ASSIGN]expr2 if (beforeExpr.OperatorToken == JsToken.Assign) { // check to see if lookup is in the current scope from which we are returning if (lookup.VariableField == null || lookup.VariableField.OuterField != null || lookup.VariableField.IsReferencedInnerScope) { // transform: lookup[ASSIGN]expr2;return lookup => return lookup[ASSIGN]expr2 // lookup points to outer field (or we don't know) // replace the operand on the return node with the previous expression and // delete the previous node. // first be sure to remove the lookup in the return operand from the references // to field. JsDetachReferences.Apply(returnNode.Operand); returnNode.Operand = beforeExpr; node[ndx - 1] = null; } else { // transform: lookup[ASSIGN]expr2;return lookup => return expr2 // lookup is a variable local to the current scope, so when we return, the // variable won't exists anymore anyway. // replace the operand on the return node oprand with the right-hand operand of the // previous expression and delete the previous node. // we're eliminating the two lookups altogether, so remove them both from the // field's reference table. var varField = lookup.VariableField; JsDetachReferences.Apply(lookup, returnNode.Operand); returnNode.Operand = beforeExpr.Operand2; node[ndx - 1] = null; // now that we've eliminated the two lookups, see if the local variable isn't // referenced anymore. If it isn't, we might be able to remove the variable, too. // (need to pick up those changes to keep track of a field's declarations, though) if (varField.RefCount == 0) { // it's not. if there's only one declaration and it either has no initializer or // is initialized to a constant, get rid of it. var nameDecl = varField.OnlyDeclaration; if (nameDecl != null) { // we only had one declaration. if (nameDecl.Initializer == null || nameDecl.Initializer.IsConstant) { // and it either had no initializer or it was initialized to a constant. // but it has no references, so let's whack it. Actually, only if it was // a var-decl (leave parameter and function decls alone). var varDecl = nameDecl as JsVariableDeclaration; if (varDecl != null) { // save the declaration parent (var, const, or let) and remove the // child vardecl from its list var declStatement = varDecl.Parent as JsDeclaration; declStatement.Remove(varDecl); varField.WasRemoved = true; // if the parent statement is now empty, remove it, too. this will // move everything up one index, but that'll just mean an extra loop. if (declStatement.Count == 0) { declStatement.Parent.ReplaceChild(declStatement, null); } } } } } } } else { // it's an assignment, but it's not =. That means it's one of the OP= operators. // we can't remove the field altogether. But we can move the assignment into the // return statement and get rid of the lone lookup. // transform: lookup OP= expr;return lookup => return lookup OP= expr; if (lookup.VariableField != null) { // we're getting rid of the lookup, so remove it from the field's list of references JsDetachReferences.Apply(returnNode.Operand); } // remove the expression from the block and put it in the operand of // the return statement. node.RemoveAt(ndx - 1); returnNode.Operand = beforeExpr; // is this field scoped only to this function? if (lookup.VariableField != null && lookup.VariableField.OuterField == null && !lookup.VariableField.IsReferencedInnerScope) { // in fact, the lookup is in the current scope, so assigning to it is a waste // because we're going to return (this is a return statement, after all). // we can get rid of the assignment part and just keep the operator: // transform: lookup OP= expr;return lookup => return lookup OP expr; beforeExpr.OperatorToken = JsScanner.StripAssignment(beforeExpr.OperatorToken); } } } else { // transform: expr1;return expr2 to return expr1,expr2 var binOp = JsCommaOperator.CombineWithComma(null, m_parser, node[ndx - 1], returnNode.Operand); // replace the operand on the return node with the new expression and // delete the previous node returnNode.Operand = binOp; node[ndx - 1] = null; } } else { // transform: expr1;return expr2 to return expr1,expr2 var binOp = JsCommaOperator.CombineWithComma(null, m_parser, node[ndx - 1], returnNode.Operand); // replace the operand on the return node with the new expression and // delete the previous node returnNode.Operand = binOp; node[ndx - 1] = null; } } }
public override void Visit(JsReturnNode node) { if (node != null) { // first we want to make sure that we are indeed within a function scope. // it makes no sense to have a return outside of a function JsActivationObject scope = m_scopeStack.Peek(); while (scope != null && !(scope is JsFunctionScope)) { scope = scope.Parent; } if (scope == null) { node.Context.HandleError(JsError.BadReturn); } // recurse the operand if we have one if (node.Operand != null) { node.Operand.Accept(this); // now see if it's a binary op assignment to a variable local to this scope. // if it is, we can get rid of the assignment because we're leaving the scope. var lookup = node.Operand.LeftHandSide as JsLookup; JsBinaryOperator binaryOp; if (lookup != null && lookup.VariableField != null && lookup.VariableField.OuterField == null && (binaryOp = lookup.Parent as JsBinaryOperator) != null && binaryOp.IsAssign && !lookup.VariableField.IsReferencedInnerScope) { if (binaryOp.OperatorToken != JsToken.Assign) { // it's an OP= assignment, so keep the lookup, but convert the operator to a non-assignment binaryOp.OperatorToken = JsScanner.StripAssignment(binaryOp.OperatorToken); } else if (binaryOp.Parent == node) { // straight assignment. But we can only get rid of the assignment if // it's the root operation of the return. If it's buried down in a complex // assignment, then leave it be. lookup.VariableField.References.Remove(lookup); node.Operand = binaryOp.Operand2; } } } } }
public override void Visit(JsIfNode node) { if (node != null) { if (m_parser.Settings.StripDebugStatements && m_parser.Settings.IsModificationAllowed(JsTreeModifications.StripDebugStatements)) { if (node.TrueBlock != null && node.TrueBlock.IsDebuggerStatement) { node.TrueBlock = null; } if (node.FalseBlock != null && node.FalseBlock.IsDebuggerStatement) { node.FalseBlock = null; } } // recurse.... base.Visit(node); // now check to see if the two branches are now empty. // if they are, null them out. if (node.TrueBlock != null && node.TrueBlock.Count == 0) { node.TrueBlock = null; } if (node.FalseBlock != null && node.FalseBlock.Count == 0) { node.FalseBlock = null; } if (node.TrueBlock != null && node.FalseBlock != null) { // neither true block nor false block is null. // if they're both expressions, convert them to a condition operator if (node.TrueBlock.IsExpression && node.FalseBlock.IsExpression && m_parser.Settings.IsModificationAllowed(JsTreeModifications.IfExpressionsToExpression)) { // if this statement has both true and false blocks, and they are both expressions, // then we can simplify this to a conditional expression. // because the blocks are expressions, we know they only have ONE statement in them, // so we can just dereference them directly. JsConditional conditional; var logicalNot = new JsLogicalNot(node.Condition, m_parser); if (logicalNot.Measure() < 0) { // applying a logical-not makes the condition smaller -- reverse the branches logicalNot.Apply(); conditional = new JsConditional(node.Context, m_parser) { Condition = node.Condition, TrueExpression = node.FalseBlock[0], FalseExpression = node.TrueBlock[0] }; } else { // regular order conditional = new JsConditional(node.Context, m_parser) { Condition = node.Condition, TrueExpression = node.TrueBlock[0], FalseExpression = node.FalseBlock[0] }; } node.Parent.ReplaceChild( node, conditional); Optimize(conditional); } else { // see if logical-notting the condition produces something smaller var logicalNot = new JsLogicalNot(node.Condition, m_parser); if (logicalNot.Measure() < 0) { // it does -- not the condition and swap the branches logicalNot.Apply(); node.SwapBranches(); } // see if the true- and false-branches each contain only a single statement if (node.TrueBlock.Count == 1 && node.FalseBlock.Count == 1) { // they do -- see if the true-branch's statement is a return-statement var trueReturn = node.TrueBlock[0] as JsReturnNode; if (trueReturn != null && trueReturn.Operand != null) { // it is -- see if the false-branch is also a return statement var falseReturn = node.FalseBlock[0] as JsReturnNode; if (falseReturn != null && falseReturn.Operand != null) { // transform: if(cond)return expr1;else return expr2 to return cond?expr1:expr2 var conditional = new JsConditional(null, m_parser) { Condition = node.Condition, TrueExpression = trueReturn.Operand, FalseExpression = falseReturn.Operand }; // create a new return node from the conditional and replace // our if-node with it var returnNode = new JsReturnNode(node.Context, m_parser) { Operand = conditional }; node.Parent.ReplaceChild( node, returnNode); Optimize(conditional); } } } } } else if (node.FalseBlock != null) { // true block must be null. // if there is no true branch but a false branch, then // put a not on the condition and move the false branch to the true branch. if (node.FalseBlock.IsExpression && m_parser.Settings.IsModificationAllowed(JsTreeModifications.IfConditionCallToConditionAndCall)) { // if (cond); else expr ==> cond || expr // but first -- which operator to use? if(a);else b --> a||b, and if(!a);else b --> a&&b // so determine which one is smaller: a or !a // assume we'll use the logical-or, since that doesn't require changing the condition var newOperator = JsToken.LogicalOr; var logicalNot = new JsLogicalNot(node.Condition, m_parser); if (logicalNot.Measure() < 0) { // !a is smaller, so apply it and use the logical-or operator logicalNot.Apply(); newOperator = JsToken.LogicalAnd; } var binaryOp = new JsBinaryOperator(node.Context, m_parser) { Operand1 = node.Condition, Operand2 = node.FalseBlock[0], OperatorToken = newOperator, }; // we don't need to analyse this new node because we've already analyzed // the pieces parts as part of the if. And this visitor's method for the BinaryOperator // doesn't really do anything else. Just replace our current node with this // new node node.Parent.ReplaceChild(node, binaryOp); } else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.IfConditionFalseToIfNotConditionTrue)) { // logical-not the condition // if(cond);else stmt ==> if(!cond)stmt var logicalNot = new JsLogicalNot(node.Condition, m_parser); logicalNot.Apply(); // and swap the branches node.SwapBranches(); } } else if (node.TrueBlock != null) { // false block must be null if (node.TrueBlock.IsExpression && m_parser.Settings.IsModificationAllowed(JsTreeModifications.IfConditionCallToConditionAndCall)) { // convert the if-node to an expression IfConditionExpressionToExpression(node, node.TrueBlock[0]); } } else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.IfEmptyToExpression)) { // NEITHER branches have anything now! // as long as the condition doesn't // contain calls or assignments, we should be able to completely delete // the statement altogether rather than changing it to an expression // statement on the condition. // but how do we KNOW there are no side-effects? // if the condition is a constant or operations on constants, delete it. // or if the condition itself is a debugger statement -- a call, lookup, or member. var remove = node.Condition.IsConstant || node.Condition.IsDebuggerStatement; if (remove) { // we're pretty sure there are no side-effects; remove it altogether node.Parent.ReplaceChild(node, null); } else { // We don't know what it is and what the side-effects may be, so // just change this statement into an expression statement by replacing us with // the expression // no need to analyze -- we already recursed node.Parent.ReplaceChild(node, node.Condition); } } if (node.FalseBlock == null && node.TrueBlock != null && node.TrueBlock.Count == 1 && m_parser.Settings.IsModificationAllowed(JsTreeModifications.CombineNestedIfs)) { var nestedIf = node.TrueBlock[0] as JsIfNode; if (nestedIf != null && nestedIf.FalseBlock == null) { // we have nested if-blocks. // transform if(cond1)if(cond2){...} to if(cond1&&cond2){...} // change the first if-statement's condition to be cond1&&cond2 // move the nested if-statement's true block to the outer if-statement node.Condition = new JsBinaryOperator(null, m_parser) { Operand1 = node.Condition, Operand2 = nestedIf.Condition, OperatorToken = JsToken.LogicalAnd }; node.TrueBlock = nestedIf.TrueBlock; } } } }