예제 #1
0
        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)
 {
     // starts with 'return', so we don't care
 }
 public void Visit(JsReturnNode node)
 {
     // starts with 'return', so we don't care
 }
 public void Visit(JsReturnNode node)
 {
     // invalid! ignore
     IsValid = false;
 }
 public void Visit(JsReturnNode node)
 {
     // invalid! ignore
     IsValid = false;
 }
예제 #6
0
 public void Visit(JsReturnNode node)
 {
     // not applicable; terminate
 }
 public void Visit(JsReturnNode node)
 {
     // not applicable; terminate
 }
 public void Visit(JsReturnNode node)
 {
     Debug.Fail("shouldn't get here");
 }
예제 #9
0
        //---------------------------------------------------------------------------------------
        // 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)
            {
                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;
            }
        }
예제 #11
0
 public void Visit(JsReturnNode node)
 {
     Debug.Fail("shouldn't get here");
 }
예제 #12
0
        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;
                    }
                }
            }
        }