public void Visit(JsUnaryOperator node)
 {
     // if this is a postfix operator and there is an operand, recurse into it
     if (node != null && node.IsPostfix && node.Operand != null)
     {
         node.Operand.Accept(this);
     }
 }
        public void Visit(JsUnaryOperator node)
        {
            if (node != null)
            {
                if (node.Operand != null)
                {
                    node.Operand.Accept(this);
                }

                node.Index = NextOrderIndex;
            }
        }
Ejemplo n.º 3
0
 public override void Visit(JsUnaryOperator node)
 {
     if (node != null && !node.OperatorInConditionalCompilationComment)
     {
         // if this is a unary logical-not operator, then we will just remove the
         // logical-not operation
         if (node.OperatorToken == JsToken.LogicalNot)
         {
             if (m_measure)
             {
                 // measure
                 // removes the not operator character, but also might remove parens that we would
                 // no longer need.
                 --m_delta;
                 if (node.Operand is JsBinaryOperator || node.Operand is JsConditional || node.Operand is JsGroupingOperator)
                 {
                     // those operators are lesser-precedence than the logical-not coperator and would've
                     // added parens that we now don't need
                     m_delta -= 2;
                 }
             }
             else
             {
                 // convert
                 // just replace the not with its own operand, unless the child
                 // itself is a grouping operator, in which case we will replace it
                 // with the grouping operand to get rid of the parens
                 var grouping = node.Operand as JsGroupingOperator;
                 if (grouping != null)
                 {
                     node.Parent.ReplaceChild(node, grouping.Operand);
                 }
                 else
                 {
                     node.Parent.ReplaceChild(node, node.Operand);
                 }
             }
         }
         else
         {
             // same logic as most nodes for the other operators
             TypicalHandler(node);
         }
     }
 }
Ejemplo n.º 4
0
 public void Visit(JsUnaryOperator node)
 {
     if (node != null)
     {
         // only a negation is allowed -- and even then, I'm not sure
         // if it has already been integrated into the numeric value yet.
         if (node.OperatorToken == JsToken.Minus)
         {
             m_writer.Write('-');
             if (node.Operand != null)
             {
                 node.Operand.Accept(this);
             }
         }
         else
         {
             // invalid! ignore
             IsValid = false;
         }
     }
 }
        public void Visit(JsUnaryOperator node)
        {
            if (node != null)
            {
                if (node.Operand != null)
                {
                    node.Operand.Accept(this);
                }

                node.Index = NextOrderIndex;
            }
        }
 public void Visit(JsUnaryOperator node)
 {
     // lesser precedence than the new operator; use parens
     m_needsParens = true;
 }
Ejemplo n.º 7
0
 public void Visit(JsUnaryOperator node)
 {
     // lesser precedence than the new operator; use parens
     m_needsParens = true;
 }
Ejemplo n.º 8
0
        //---------------------------------------------------------------------------------------
        // ParseUnaryExpression
        //
        //  UnaryExpression :
        //    PostfixExpression |
        //    'delete' UnaryExpression |
        //    'void' UnaryExpression |
        //    'typeof' UnaryExpression |
        //    '++' UnaryExpression |
        //    '--' UnaryExpression |
        //    '+' UnaryExpression |
        //    '-' UnaryExpression |
        //    '~' UnaryExpression |
        //    '!' UnaryExpression
        //
        //---------------------------------------------------------------------------------------
        private JsAstNode ParseUnaryExpression(out bool isLeftHandSideExpr, bool isMinus)
        {
            isLeftHandSideExpr = false;
            bool dummy = false;
            JsContext exprCtx = null;
            JsAstNode expr = null;

            TryItAgain:
            JsAstNode ast = null;
            switch (m_currentToken.Token)
            {
                case JsToken.Void:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.Void
                        };
                    break;
                case JsToken.TypeOf:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.TypeOf
                        };
                    break;
                case JsToken.Plus:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.Plus
                        };
                    break;
                case JsToken.Minus:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, true);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.Minus
                        };
                    break;
                case JsToken.BitwiseNot:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.BitwiseNot
                        };
                    break;
                case JsToken.LogicalNot:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.LogicalNot
                        };
                    break;
                case JsToken.Delete:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.Delete
                        };
                    break;
                case JsToken.Increment:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.Increment
                        };
                    break;
                case JsToken.Decrement:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this)
                        {
                            Operand = expr,
                            OperatorContext = exprCtx,
                            OperatorToken = JsToken.Decrement
                        };
                    break;

                case JsToken.ConditionalCommentStart:
                    // skip past the start to the next token
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    if (m_currentToken.Token == JsToken.ConditionalCommentEnd)
                    {
                        // empty conditional-compilation comment -- ignore
                        GetNextToken();
                        goto TryItAgain;
                    }
                    else if (m_currentToken.Token == JsToken.ConditionalCompilationOn)
                    {
                        // /*@cc_on -- check for @IDENT@*/ or !@*/
                        GetNextToken();
                        if (m_currentToken.Token == JsToken.ConditionalCompilationVariable)
                        {
                            // /*@cc_on@IDENT -- check for @*/
                            ast = new JsConstantWrapperPP(m_currentToken.Clone(), this)
                                {
                                    VarName = m_currentToken.Code,
                                    ForceComments = true
                                };

                            GetNextToken();

                            if (m_currentToken.Token == JsToken.ConditionalCommentEnd)
                            {
                                // skip the close and keep going
                                GetNextToken();
                            }
                            else
                            {
                                // too complicated
                                CCTooComplicated(null);
                                goto TryItAgain;
                            }
                        }
                        else if (m_currentToken.Token == JsToken.LogicalNot)
                        {
                            // /*@cc_on! -- check for @*/
                            var operatorContext = m_currentToken.Clone();
                            GetNextToken();
                            if (m_currentToken.Token == JsToken.ConditionalCommentEnd)
                            {
                                // we have /*@cc_on!@*/
                                GetNextToken();
                                expr = ParseUnaryExpression(out dummy, false);
                                exprCtx.UpdateWith(expr.Context);

                                var unary = new JsUnaryOperator(exprCtx, this)
                                    {
                                        Operand = expr,
                                        OperatorContext = operatorContext,
                                        OperatorToken = JsToken.LogicalNot
                                    };
                                unary.OperatorInConditionalCompilationComment = true;
                                unary.ConditionalCommentContainsOn = true;
                                ast = unary;
                            }
                            else
                            {
                                // too complicated
                                CCTooComplicated(null);
                                goto TryItAgain;
                            }
                        }
                        else
                        {
                            // too complicated
                            CCTooComplicated(null);
                            goto TryItAgain;
                        }
                    }
                    else if (m_currentToken.Token == JsToken.LogicalNot)
                    {
                        // /*@! -- check for @*/
                        var operatorContext = m_currentToken.Clone();
                        GetNextToken();
                        if (m_currentToken.Token == JsToken.ConditionalCommentEnd)
                        {
                            // we have /*@!@*/
                            GetNextToken();
                            expr = ParseUnaryExpression(out dummy, false);
                            exprCtx.UpdateWith(expr.Context);

                            var unary = new JsUnaryOperator(exprCtx, this)
                                {
                                    Operand = expr,
                                    OperatorContext = operatorContext,
                                    OperatorToken = JsToken.LogicalNot
                                };
                            unary.OperatorInConditionalCompilationComment = true;
                            ast = unary;
                        }
                        else
                        {
                            // too complicated
                            CCTooComplicated(null);
                            goto TryItAgain;
                        }
                    }
                    else if (m_currentToken.Token == JsToken.ConditionalCompilationVariable)
                    {
                        // @IDENT -- check for @*/
                        ast = new JsConstantWrapperPP(m_currentToken.Clone(), this)
                            {
                                VarName = m_currentToken.Code,
                                ForceComments = true
                            };
                        GetNextToken();

                        if (m_currentToken.Token == JsToken.ConditionalCommentEnd)
                        {
                            // skip the close and keep going
                            GetNextToken();
                        }
                        else
                        {
                            // too complicated
                            CCTooComplicated(null);
                            goto TryItAgain;
                        }
                    }
                    else
                    {
                        // we ONLY support /*@id@*/ or /*@cc_on@id@*/ or /*@!@*/ or /*@cc_on!@*/ in expressions right now.
                        // throw an error, skip to the end of the comment, then ignore it and start
                        // looking for the next token.
                        CCTooComplicated(null);
                        goto TryItAgain;
                    }
                    break;

                default:
                    m_noSkipTokenSet.Add(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet);
                    try
                    {
                        ast = ParseLeftHandSideExpression(isMinus);
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (IndexOfToken(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet, exc) == -1)
                        {
                            throw;
                        }
                        else
                        {
                            if (exc._partiallyComputedNode == null)
                                SkipTokensAndThrow();
                            else
                                ast = exc._partiallyComputedNode;
                        }
                    }
                    finally
                    {
                        m_noSkipTokenSet.Remove(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet);
                    }
                    ast = ParsePostfixExpression(ast, out isLeftHandSideExpr);
                    break;
            }

            return ast;
        }
        public override void Visit(JsBinaryOperator node)
        {
            if (node != null)
            {
                base.Visit(node);

                // see if this operation is subtracting zero from a lookup -- that is typically done to
                // coerce a value to numeric. There's a simpler way: unary plus operator.
                if (node.OperatorToken == JsToken.Minus
                    && m_parser.Settings.IsModificationAllowed(JsTreeModifications.SimplifyStringToNumericConversion))
                {
                    JsLookup lookup = node.Operand1 as JsLookup;
                    if (lookup != null)
                    {
                        JsConstantWrapper right = node.Operand2 as JsConstantWrapper;
                        if (right != null && right.IsIntegerLiteral && right.ToNumber() == 0)
                        {
                            // okay, so we have "lookup - 0"
                            // this is done frequently to force a value to be numeric.
                            // There is an easier way: apply the unary + operator to it.
                            // transform: lookup - 0   => +lookup
                            var unary = new JsUnaryOperator(node.Context, m_parser)
                                {
                                    Operand = lookup,
                                    OperatorToken = JsToken.Plus
                                };
                            node.Parent.ReplaceChild(node, unary);

                            // because we recursed at the top of this function, we don't need to Analyze
                            // the new Unary node. This visitor's method for UnaryOperator only does something
                            // if the operand is a constant -- and this one is a Lookup. And we already analyzed
                            // the lookup.
                        }
                    }
                }
                else if ((node.OperatorToken == JsToken.StrictEqual || node.OperatorToken == JsToken.StrictNotEqual)
                    && m_parser.Settings.IsModificationAllowed(JsTreeModifications.ReduceStrictOperatorIfTypesAreSame))
                {
                    JsPrimitiveType leftType = node.Operand1.FindPrimitiveType();
                    if (leftType != JsPrimitiveType.Other)
                    {
                        JsPrimitiveType rightType = node.Operand2.FindPrimitiveType();
                        if (leftType == rightType)
                        {
                            // the are the same known types. We can reduce the operators
                            node.OperatorToken = node.OperatorToken == JsToken.StrictEqual ? JsToken.Equal : JsToken.NotEqual;
                        }
                        else if (rightType != JsPrimitiveType.Other)
                        {
                            // they are not the same, but they are both known. We can completely remove the operator
                            // and replace it with true (!==) or false (===).
                            // transform: x !== y   =>   true
                            // transform: x === y   =>   false
                            node.Context.HandleError(JsError.StrictComparisonIsAlwaysTrueOrFalse, false);
                            node.Parent.ReplaceChild(
                                node,
                                new JsConstantWrapper(node.OperatorToken == JsToken.StrictNotEqual, JsPrimitiveType.Boolean, node.Context, m_parser));

                            // because we are essentially removing the node from the AST, be sure to detach any references
                            JsDetachReferences.Apply(node);
                        }
                    }
                }
                else if (node.IsAssign)
                {
                    var lookup = node.Operand1 as JsLookup;
                    if (lookup != null)
                    {
                        if (lookup.VariableField != null && lookup.VariableField.InitializationOnly)
                        {
                            // the field is an initialization-only field -- we should NOT be assigning to it
                            lookup.Context.HandleError(JsError.AssignmentToConstant, true);
                        }
                        else if (m_scopeStack.Peek().UseStrict)
                        {
                            if (lookup.VariableField == null || lookup.VariableField.FieldType == JsFieldType.UndefinedGlobal)
                            {
                                // strict mode cannot assign to undefined fields
                                node.Operand1.Context.HandleError(JsError.StrictModeUndefinedVariable, true);
                            }
                            else if(lookup.VariableField.FieldType == JsFieldType.Arguments
                                || (lookup.VariableField.FieldType == JsFieldType.Predefined && string.CompareOrdinal(lookup.Name, "eval") == 0))
                            {
                                // strict mode cannot assign to lookup "eval" or "arguments"
                                node.Operand1.Context.HandleError(JsError.StrictModeInvalidAssign, true);
                            }
                        }
                    }
                }
                else if ((node.Parent is JsBlock || (node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock))
                    && (node.OperatorToken == JsToken.LogicalOr || node.OperatorToken == JsToken.LogicalAnd))
                {
                    // this is an expression statement where the operator is || or && -- basically
                    // it's a shortcut for an if-statement:
                    // expr1&&expr2; ==> if(expr1)expr2;
                    // expr1||expr2; ==> if(!expr1)expr2;
                    // let's check to see if the not of expr1 is smaller. If so, we can not the expression
                    // and change the operator
                    var logicalNot = new JsLogicalNot(node.Operand1, node.Parser);
                    if (logicalNot.Measure() < 0)
                    {
                        // it would be smaller! Change it.
                        // transform: expr1&&expr2 => !expr1||expr2
                        // transform: expr1||expr2 => !expr1&&expr2
                        logicalNot.Apply();
                        node.OperatorToken = node.OperatorToken == JsToken.LogicalAnd ? JsToken.LogicalOr : JsToken.LogicalAnd;
                    }
                }
            }
        }
Ejemplo n.º 10
0
        public void Visit(JsUnaryOperator node)
        {
            if (node != null)
            {
                var symbol = StartSymbol(node);
                var isNoIn = m_noIn;
                m_noIn = false;

                if (node.IsPostfix)
                {
                    if (node.Operand != null)
                    {
                        AcceptNodeWithParens(node.Operand, node.Operand.Precedence < node.Precedence);
                        SetContextOutputPosition(node.Context, node.Operand.Context);
                    }

                    // the only postfix unary operators are ++ and --, and when in the postfix position,
                    // line breaks are NOT allowed between the operand and the operator.
                    // doesn't seem to need this flag set here, but set it anyways just in case.
                    m_noLineBreaks = true;
                    Output(OperatorString(node.OperatorToken));
                    MarkSegment(node, null, node.OperatorContext);
                    m_startOfStatement = false;
                }
                else
                {
                    if (node.OperatorInConditionalCompilationComment)
                    {
                        // if we haven't output a cc_on yet, we ALWAYS want to do it now, whether or not the
                        // sources had one. Otherwise, we only only want to output one if we had one and we aren't
                        // removing unneccesary ones.
                        if (!m_outputCCOn
                            || (node.ConditionalCommentContainsOn && !m_settings.IsModificationAllowed(JsTreeModifications.RemoveUnnecessaryCCOnStatements)))
                        {
                            // output it now and set the flag that we have output them
                            Output("/*@cc_on");
                            m_outputCCOn = true;
                        }
                        else
                        {
                            Output("/*@");
                        }

                        Output(OperatorString(node.OperatorToken));
                        MarkSegment(node, null, node.OperatorContext);
                        SetContextOutputPosition(node.Context);
                        Output("@*/");
                    }
                    else
                    {
                        Output(OperatorString(node.OperatorToken));
                        MarkSegment(node, null, node.OperatorContext ?? node.Context);
                        SetContextOutputPosition(node.Context);
                    }

                    m_startOfStatement = false;
                    if (node.Operand != null)
                    {
                        AcceptNodeWithParens(node.Operand, node.Operand.Precedence < node.Precedence);
                    }
                }

                m_noIn = isNoIn;
                EndSymbol(symbol);
            }
        }
Ejemplo n.º 11
0
 public override void Visit(JsUnaryOperator node)
 {
     if (node != null && !node.OperatorInConditionalCompilationComment)
     {
         // if this is a unary logical-not operator, then we will just remove the
         // logical-not operation
         if (node.OperatorToken == JsToken.LogicalNot)
         {
             if (m_measure)
             {
                 // measure
                 // removes the not operator character, but also might remove parens that we would
                 // no longer need.
                 --m_delta;
                 if (node.Operand is JsBinaryOperator || node.Operand is JsConditional || node.Operand is JsGroupingOperator)
                 {
                     // those operators are lesser-precedence than the logical-not coperator and would've
                     // added parens that we now don't need
                     m_delta -= 2;
                 }
             }
             else
             {
                 // convert
                 // just replace the not with its own operand, unless the child
                 // itself is a grouping operator, in which case we will replace it
                 // with the grouping operand to get rid of the parens
                 var grouping = node.Operand as JsGroupingOperator;
                 if (grouping != null)
                 {
                     node.Parent.ReplaceChild(node, grouping.Operand);
                 }
                 else
                 {
                     node.Parent.ReplaceChild(node, node.Operand);
                 }
             }
         }
         else
         {
             // same logic as most nodes for the other operators
             TypicalHandler(node);
         }
     }
 }
        private void DoUnaryNode(JsUnaryOperator node)
        {
            if (!node.OperatorInConditionalCompilationComment
                && m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
            {
                var literalOperand = node.Operand as JsConstantWrapper;
                switch(node.OperatorToken)
                {
                    case JsToken.Void:
                        // see if our operand is a ConstantWrapper
                        if (literalOperand != null)
                        {
                            // either number, string, boolean, or null.
                            // the void operator evaluates its operand and returns undefined. Since evaluating a literal
                            // does nothing, then it doesn't matter what the heck it is. Replace it with a zero -- a one-
                            // character literal.
                            node.Operand = new JsConstantWrapper(0, JsPrimitiveType.Number, node.Context, m_parser);
                        }
                        break;

                    case JsToken.TypeOf:
                        if (literalOperand != null)
                        {
                            // either number, string, boolean, or null.
                            // the operand is a literal. Therefore we already know what the typeof
                            // operator will return. Just short-circuit that behavior now and replace the operator
                            // with a string literal of the proper value
                            string typeName = null;
                            if (literalOperand.IsStringLiteral)
                            {
                                // "string"
                                typeName = "string";
                            }
                            else if (literalOperand.IsNumericLiteral)
                            {
                                // "number"
                                typeName = "number";
                            }
                            else if (literalOperand.IsBooleanLiteral)
                            {
                                // "boolean"
                                typeName = "boolean";
                            }
                            else if (literalOperand.Value == null)
                            {
                                // "object"
                                typeName = "object";
                            }

                            if (!string.IsNullOrEmpty(typeName))
                            {
                                ReplaceNodeWithLiteral(node, new JsConstantWrapper(typeName, JsPrimitiveType.String, node.Context, m_parser));
                            }
                        }
                        else if (node.Operand is JsObjectLiteral)
                        {
                            ReplaceNodeWithLiteral(node, new JsConstantWrapper("object", JsPrimitiveType.String, node.Context, m_parser));
                        }
                        break;

                    case JsToken.Plus:
                        if (literalOperand != null)
                        {
                            try
                            {
                                // replace with a constant representing operand.ToNumber,
                                ReplaceNodeWithLiteral(node, new JsConstantWrapper(literalOperand.ToNumber(), JsPrimitiveType.Number, node.Context, m_parser));
                            }
                            catch (InvalidCastException)
                            {
                                // some kind of casting in ToNumber caused a situation where we don't want
                                // to perform the combination on these operands
                            }
                        }
                        break;

                    case JsToken.Minus:
                        if (literalOperand != null)
                        {
                            try
                            {
                                // replace with a constant representing the negative of operand.ToNumber
                                ReplaceNodeWithLiteral(node, new JsConstantWrapper(-literalOperand.ToNumber(), JsPrimitiveType.Number, node.Context, m_parser));
                            }
                            catch (InvalidCastException)
                            {
                                // some kind of casting in ToNumber caused a situation where we don't want
                                // to perform the combination on these operands
                            }
                        }
                        break;

                    case JsToken.BitwiseNot:
                        if (literalOperand != null)
                        {
                            try
                            {
                                // replace with a constant representing the bitwise-not of operant.ToInt32
                                ReplaceNodeWithLiteral(node, new JsConstantWrapper(Convert.ToDouble(~literalOperand.ToInt32()), JsPrimitiveType.Number, node.Context, m_parser));
                            }
                            catch (InvalidCastException)
                            {
                                // some kind of casting in ToNumber caused a situation where we don't want
                                // to perform the combination on these operands
                            }
                        }
                        break;

                    case JsToken.LogicalNot:
                        if (literalOperand != null)
                        {
                            // replace with a constant representing the opposite of operand.ToBoolean
                            try
                            {
                                ReplaceNodeWithLiteral(node, new JsConstantWrapper(!literalOperand.ToBoolean(), JsPrimitiveType.Boolean, node.Context, m_parser));
                            }
                            catch (InvalidCastException)
                            {
                                // ignore any invalid cast exceptions
                            }
                        }
                        break;
                }
            }
        }
        private void DoBinaryOperator(JsBinaryOperator node)
        {
            if (m_parser.Settings.EvalLiteralExpressions)
            {
                // if this is an assign operator, an in, or an instanceof, then we won't
                // try to evaluate it
                if (!node.IsAssign && node.OperatorToken != JsToken.In && node.OperatorToken != JsToken.InstanceOf)
                {
                    if (node.OperatorToken == JsToken.StrictEqual || node.OperatorToken == JsToken.StrictNotEqual)
                    {
                        // the operator is a strict equality (or not-equal).
                        // check the primitive types of the two operands -- if they are known but not the same, we can
                        // shortcut the whole process by just replacing this node with a boolean literal.
                        var leftType = node.Operand1.FindPrimitiveType();
                        if (leftType != JsPrimitiveType.Other)
                        {
                            var rightType = node.Operand2.FindPrimitiveType();
                            if (rightType != JsPrimitiveType.Other)
                            {
                                // both sides are known
                                if (leftType != rightType)
                                {
                                    // they are not the same type -- replace with a boolean and bail
                                    ReplaceNodeWithLiteral(
                                        node,
                                        new JsConstantWrapper(node.OperatorToken == JsToken.StrictEqual ? false : true, JsPrimitiveType.Boolean, node.Context, m_parser));
                                    return;
                                }

                                // they are the same type -- we can change the operator to simple equality/not equality
                                node.OperatorToken = node.OperatorToken == JsToken.StrictEqual ? JsToken.Equal : JsToken.NotEqual;
                            }
                        }
                    }

                    // see if the left operand is a literal number, boolean, string, or null
                    JsConstantWrapper left = node.Operand1 as JsConstantWrapper;
                    if (left != null)
                    {
                        if (node.OperatorToken == JsToken.Comma)
                        {
                            // the comma operator evaluates the left, then evaluates the right and returns it.
                            // but if the left is a literal, evaluating it doesn't DO anything, so we can replace the
                            // entire operation with the right-hand operand
                            JsConstantWrapper rightConstant = node.Operand2 as JsConstantWrapper;
                            if (rightConstant != null)
                            {
                                // we'll replace the operator with the right-hand operand, BUT it's a constant, too.
                                // first check to see if replacing this node with a constant will result in creating
                                // a member-bracket operator that can be turned into a member-dot. If it is, then that
                                // method will handle the replacement. But if it doesn't, then we should just replace
                                // the comma with the right-hand operand.
                                if (!ReplaceMemberBracketWithDot(node, rightConstant))
                                {
                                    ReplaceNodeWithLiteral(node, rightConstant);
                                }
                            }
                            else if (node is JsCommaOperator)
                            {
                                // this is a collection of expression statements that we've joined together as
                                // an extended comma operator.
                                var list = node.Operand2 as JsAstNodeList;
                                if (list == null)
                                {
                                    // not a list, just a single item, so we can just
                                    // replace this entire node with the one element
                                    ReplaceNodeCheckParens(node, node.Operand2);
                                }
                                else if (list.Count == 1)
                                {
                                    // If the list has a single element, then we can just
                                    // replace this entire node with the one element
                                    ReplaceNodeCheckParens(node, list[0]);
                                }
                                else if (list.Count == 0)
                                {
                                    // the recursion ended up emptying the list, so we can just delete
                                    // this node altogether
                                    ReplaceNodeCheckParens(node, null);
                                }
                                else
                                {
                                    // more than one item in the list
                                    // move the first item from the list to the left-hand side
                                    var firstItem = list[0];
                                    list.RemoveAt(0);
                                    node.Operand1 = firstItem;

                                    // if there's only one item left in the list, we can get rid of the
                                    // extra list node and make it just the remaining node
                                    if (list.Count == 1)
                                    {
                                        firstItem = list[0];
                                        list.RemoveAt(0);
                                        node.Operand2 = firstItem;
                                    }
                                }
                            }
                            else
                            {
                                // replace the comma operator with the right-hand operand
                                ReplaceNodeCheckParens(node, node.Operand2);
                            }
                        }
                        else
                        {
                            // see if the right operand is a literal number, boolean, string, or null
                            JsConstantWrapper right = node.Operand2 as JsConstantWrapper;
                            if (right != null)
                            {
                                // then they are both constants and we can evaluate the operation
                                EvalThisOperator(node, left, right);
                            }
                            else
                            {
                                // see if the right is a binary operator that can be combined with ours
                                JsBinaryOperator rightBinary = node.Operand2 as JsBinaryOperator;
                                if (rightBinary != null)
                                {
                                    JsConstantWrapper rightLeft = rightBinary.Operand1 as JsConstantWrapper;
                                    if (rightLeft != null)
                                    {
                                        // eval our left and the right-hand binary's left and put the combined operation as
                                        // the child of the right-hand binary
                                        EvalToTheRight(node, left, rightLeft, rightBinary);
                                    }
                                    else
                                    {
                                        JsConstantWrapper rightRight = rightBinary.Operand2 as JsConstantWrapper;
                                        if (rightRight != null)
                                        {
                                            EvalFarToTheRight(node, left, rightRight, rightBinary);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        // left is not a constantwrapper. See if the right is
                        JsConstantWrapper right = node.Operand2 as JsConstantWrapper;
                        if (right != null)
                        {
                            // the right is a constant. See if the the left is a binary operator...
                            JsBinaryOperator leftBinary = node.Operand1 as JsBinaryOperator;
                            if (leftBinary != null)
                            {
                                // ...with a constant on the right, and the operators can be combined
                                JsConstantWrapper leftRight = leftBinary.Operand2 as JsConstantWrapper;
                                if (leftRight != null)
                                {
                                    EvalToTheLeft(node, right, leftRight, leftBinary);
                                }
                                else
                                {
                                    JsConstantWrapper leftLeft = leftBinary.Operand1 as JsConstantWrapper;
                                    if (leftLeft != null)
                                    {
                                        EvalFarToTheLeft(node, right, leftLeft, leftBinary);
                                    }
                                }
                            }
                            else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.SimplifyStringToNumericConversion))
                            {
                                // see if it's a lookup and this is a minus operation and the constant is a zero
                                JsLookup lookup = node.Operand1 as JsLookup;
                                if (lookup != null && node.OperatorToken == JsToken.Minus && right.IsIntegerLiteral && right.ToNumber() == 0)
                                {
                                    // okay, so we have "lookup - 0"
                                    // this is done frequently to force a value to be numeric.
                                    // There is an easier way: apply the unary + operator to it.
                                    var unary = new JsUnaryOperator(node.Context, m_parser)
                                        {
                                            Operand = lookup,
                                            OperatorToken = JsToken.Plus
                                        };
                                    ReplaceNodeCheckParens(node, unary);
                                }
                            }
                        }
                        // TODO: shouldn't we check if they BOTH are binary operators? (a*6)*(5*b) ==> a*30*b (for instance)
                    }
                }
            }
        }
        public override void Visit(JsUnaryOperator node)
        {
            if (node != null)
            {
                // depth-first
                base.Visit(node);

                DoUnaryNode(node);
            }
        }
 public void Visit(JsUnaryOperator node)
 {
     // not applicable; terminate
 }
Ejemplo n.º 16
0
 public void Visit(JsUnaryOperator node)
 {
     // not applicable; terminate
 }
Ejemplo n.º 17
0
 //---------------------------------------------------------------------------------------
 // ParsePostfixExpression
 //
 //  PostfixExpression:
 //    LeftHandSideExpression |
 //    LeftHandSideExpression '++' |
 //    LeftHandSideExpression  '--'
 //
 //---------------------------------------------------------------------------------------
 private JsAstNode ParsePostfixExpression(JsAstNode ast, out bool isLeftHandSideExpr)
 {
     isLeftHandSideExpr = true;
     JsContext exprCtx = null;
     if (null != ast)
     {
         if (!m_foundEndOfLine)
         {
             if (JsToken.Increment == m_currentToken.Token)
             {
                 isLeftHandSideExpr = false;
                 exprCtx = ast.Context.Clone();
                 exprCtx.UpdateWith(m_currentToken);
                 ast = new JsUnaryOperator(exprCtx, this)
                     {
                         Operand = ast,
                         OperatorToken = m_currentToken.Token,
                         OperatorContext = m_currentToken.Clone(),
                         IsPostfix = true
                     };
                 GetNextToken();
             }
             else if (JsToken.Decrement == m_currentToken.Token)
             {
                 isLeftHandSideExpr = false;
                 exprCtx = ast.Context.Clone();
                 exprCtx.UpdateWith(m_currentToken);
                 ast = new JsUnaryOperator(exprCtx, this)
                     {
                         Operand = ast,
                         OperatorToken = m_currentToken.Token,
                         OperatorContext = m_currentToken.Clone(),
                         IsPostfix = true
                     };
                 GetNextToken();
             }
         }
     }
     return ast;
 }
 public void Visit(JsUnaryOperator node)
 {
     if (node != null)
     {
         // only a negation is allowed -- and even then, I'm not sure
         // if it has already been integrated into the numeric value yet.
         if (node.OperatorToken == JsToken.Minus)
         {
             m_writer.Write('-');
             if (node.Operand != null)
             {
                 node.Operand.Accept(this);
             }
         }
         else
         {
             // invalid! ignore
             IsValid = false;
         }
     }
 }
        public override void Visit(JsUnaryOperator node)
        {
            if (node != null)
            {
                base.Visit(node);

                // strict mode has some restrictions
                if (node.OperatorToken == JsToken.Delete)
                {
                    if (m_scopeStack.Peek().UseStrict)
                    {
                        // operand of a delete operator cannot be a variable name, argument name, or function name
                        // which means it can't be a lookup
                        if (node.Operand is JsLookup)
                        {
                            node.Context.HandleError(JsError.StrictModeInvalidDelete, true);
                        }
                    }
                }
                else if (node.OperatorToken == JsToken.Increment || node.OperatorToken == JsToken.Decrement)
                {
                    var lookup = node.Operand as JsLookup;
                    if (lookup != null)
                    {
                        if (lookup.VariableField != null && lookup.VariableField.InitializationOnly)
                        {
                            // can't increment or decrement a constant!
                            lookup.Context.HandleError(JsError.AssignmentToConstant, true);
                        }

                        // and strict mode has some restrictions we want to check now
                        if (m_scopeStack.Peek().UseStrict)
                        {
                            // the operator cannot be the eval function or arguments object.
                            // that means the operator is a lookup, and the field for that lookup
                            // is the arguments object or the predefined "eval" object.
                            if (lookup.VariableField == null
                                || lookup.VariableField.FieldType == JsFieldType.UndefinedGlobal
                                || lookup.VariableField.FieldType == JsFieldType.Arguments
                                || (lookup.VariableField.FieldType == JsFieldType.Predefined && string.CompareOrdinal(lookup.Name, "eval") == 0))
                            {
                                node.Operand.Context.HandleError(JsError.StrictModeInvalidPreOrPost, true);
                            }
                        }
                    }
                }
                else
                {

                    // if the operand is a numeric literal
                    JsConstantWrapper constantWrapper = node.Operand as JsConstantWrapper;
                    if (constantWrapper != null && constantWrapper.IsNumericLiteral)
                    {
                        // get the value of the constant. We've already screened it for numeric, so
                        // we don't have to worry about catching any errors
                        double doubleValue = constantWrapper.ToNumber();

                        // if this is a unary minus...
                        if (node.OperatorToken == JsToken.Minus
                            && m_parser.Settings.IsModificationAllowed(JsTreeModifications.ApplyUnaryMinusToNumericLiteral))
                        {
                            // negate the value
                            constantWrapper.Value = -doubleValue;

                            // replace us with the negated constant
                            if (node.Parent.ReplaceChild(node, constantWrapper))
                            {
                                // the context for the minus will include the number (its operand),
                                // but the constant will just be the number. Update the context on
                                // the constant to be a copy of the context on the operator
                                constantWrapper.Context = node.Context.Clone();
                            }
                        }
                        else if (node.OperatorToken == JsToken.Plus
                            && m_parser.Settings.IsModificationAllowed(JsTreeModifications.RemoveUnaryPlusOnNumericLiteral))
                        {
                            // +NEG is still negative, +POS is still positive, and +0 is still 0.
                            // so just get rid of the unary operator altogether
                            if (node.Parent.ReplaceChild(node, constantWrapper))
                            {
                                // the context for the unary will include the number (its operand),
                                // but the constant will just be the number. Update the context on
                                // the constant to be a copy of the context on the operator
                                constantWrapper.Context = node.Context.Clone();
                            }
                        }
                    }
                }
            }
        }