public void Visit(JsConstantWrapper node)
        {
            if (node != null)
            {
                // allow string, number, true, false, and null.
                switch (node.PrimitiveType)
                {
                case JsPrimitiveType.Boolean:
                    m_writer.Write((bool)node.Value ? "true" : "false");
                    break;

                case JsPrimitiveType.Null:
                    m_writer.Write("null");
                    break;

                case JsPrimitiveType.Number:
                    OutputNumber((double)node.Value, node.Context);
                    break;

                case JsPrimitiveType.String:
                case JsPrimitiveType.Other:
                    // string -- or treat it like a string
                    OutputString(node.Value.ToString());
                    break;
                }
            }
        }
Example #2
0
 public override void Visit(JsConstantWrapper node)
 {
     if (node != null)
     {
         // measure
         if (node.PrimitiveType == JsPrimitiveType.Boolean)
         {
             if (m_measure)
             {
                 // if we are converting true/false literals to !0/!1, then
                 // a logical-not doesn't add or subtract anything. But if we aren't,
                 // we need to add/subtract the difference in the length between the
                 // "true" and "false" strings
                 if (!m_parser.Settings.MinifyCode ||
                     !m_parser.Settings.IsModificationAllowed(JsTreeModifications.BooleanLiteralsToNotOperators))
                 {
                     // converting true to false adds a character, false to true subtracts
                     m_delta += node.ToBoolean() ? 1 : -1;
                 }
             }
             else
             {
                 // convert - just flip the boolean value
                 node.Value = !node.ToBoolean();
             }
         }
         else
         {
             // just the same typical operation as most other nodes for other types
             TypicalHandler(node);
         }
     }
 }
Example #3
0
 public void Visit(JsConstantWrapper node)
 {
     if (node != null)
     {
         node.Index = NextOrderIndex;
     }
 }
        private static bool IsMinificationHint(JsConstantWrapper node)
        {
            var isHint = false;

            if (node.PrimitiveType == JsPrimitiveType.String)
            {
                // try splitting on commas and removing empty items
                var sections = node.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var section in sections)
                {
                    // valid hints are:
                    //      name:nomunge    don't automatically rename the field defined in this scope named "name"
                    //                      if name is missing (colon is the first character) or "*", then don't rename ANY
                    //                      fields defined in the current scope.
                    var ndxColon = section.IndexOf(':');
                    if (ndxColon >= 0)
                    {
                        // make sure this is a "nomunge" hint. If it is, then the entire node is treated as a hint and
                        // will be removed from the AST.
                        if (string.Compare(section.Substring(ndxColon + 1).Trim(), "nomunge", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // it is.
                            isHint = true;

                            // get the name that we don't want to munge. Null means all. Convert "*"
                            // to null.
                            var identifier = section.Substring(0, ndxColon).Trim();
                            if (string.IsNullOrEmpty(identifier) || string.CompareOrdinal(identifier, "*") == 0)
                            {
                                identifier = null;
                            }

                            // get the current scope and iterate over all the fields within it
                            // looking for just the ones that are defined here (outer is null)
                            var currentScope = node.EnclosingScope;
                            foreach (var field in currentScope.NameTable.Values)
                            {
                                if (field.OuterField == null)
                                {
                                    // if the identifier is null or matches exactly, mark it as not crunchable
                                    if (identifier == null || string.CompareOrdinal(identifier, field.Name) == 0)
                                    {
                                        field.CanCrunch = false;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(isHint);
        }
 public bool IsSingleConstantArgument(string argumentValue)
 {
     if (m_list.Count == 1)
     {
         JsConstantWrapper constantWrapper = m_list[0] as JsConstantWrapper;
         if (constantWrapper != null &&
             string.CompareOrdinal(constantWrapper.Value.ToString(), argumentValue) == 0)
         {
             return(true);
         }
     }
     return(false);
 }
 public override void Visit(JsConstantWrapper node)
 {
     if (node != null)
     {
         // no children, so don't bother calling the base.
         if (node.PrimitiveType == JsPrimitiveType.Boolean
             && m_parser.Settings.IsModificationAllowed(JsTreeModifications.BooleanLiteralsToNotOperators))
         {
             node.Parent.ReplaceChild(node, new JsUnaryOperator(node.Context, m_parser)
                 {
                     Operand = new JsConstantWrapper(node.ToBoolean() ? 0 : 1, JsPrimitiveType.Number, node.Context, m_parser),
                     OperatorToken = JsToken.LogicalNot
                 });
         }
     }
 }
Example #7
0
 public override void Visit(JsConstantWrapper node)
 {
     if (node != null)
     {
         // no children, so don't bother calling the base.
         if (node.PrimitiveType == JsPrimitiveType.Boolean &&
             m_parser.Settings.IsModificationAllowed(JsTreeModifications.BooleanLiteralsToNotOperators))
         {
             node.Parent.ReplaceChild(node, new JsUnaryOperator(node.Context, m_parser)
             {
                 Operand       = new JsConstantWrapper(node.ToBoolean() ? 0 : 1, JsPrimitiveType.Number, node.Context, m_parser),
                 OperatorToken = JsToken.LogicalNot
             });
         }
     }
 }
 public override void Visit(JsConstantWrapper node)
 {
     // by default this node has nothing to do and no children to recurse.
     // but if this node's parent is a block, then this is an expression statement
     // consisting of a single string literal. Normally we would ignore these -- if
     // they occured at the top of the block they would be DirectivePrologues. So because
     // this exists, it must not be at the top. But we still want to check it for the nomunge
     // hints and respect them if that's what it is.
     if (node != null && node.Parent is JsBlock)
     {
         // if this is a hint, process it as such.
         if (IsMinificationHint(node))
         {
             // and then remove it. We can do that here, because blocks are processed
             // in reverse order.
             node.Parent.ReplaceChild(node, null);
         }
     }
 }
        private JsConstantWrapper LessThanOrEqual(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;

            if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
            {
                if (left.IsStringLiteral && right.IsStringLiteral)
                {
                    if (left.IsOkayToCombine && right.IsOkayToCombine)
                    {
                        // do a straight ordinal comparison of the strings
                        newLiteral = new JsConstantWrapper(string.CompareOrdinal(left.ToString(), right.ToString()) <= 0, JsPrimitiveType.Boolean, null, m_parser);
                    }
                }
                else
                {
                    try
                    {
                        // either one or both are NOT a string -- numeric comparison
                        if (left.IsOkayToCombine && right.IsOkayToCombine)
                        {
                            newLiteral = new JsConstantWrapper(left.ToNumber() <= right.ToNumber(), JsPrimitiveType.Boolean, null, 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
                    }
                }
            }

            return newLiteral;
        }
        private void EvalToTheLeft(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator leftOperator)
        {
            if (leftOperator.OperatorToken == JsToken.Plus && node.OperatorToken == JsToken.Plus)
            {
                // plus-plus
                // the other operation goes first, so if the other constant is a string, then we know that
                // operation will do a string concatenation, which will force our operation to be a string
                // concatenation. If the other constant is not a string, then we won't know until runtime and
                // we can't combine them.
                if (otherConstant.IsStringLiteral)
                {
                    // the other constant is a string -- so we can do the string concat and combine them
                    JsConstantWrapper newLiteral = StringConcat(otherConstant, thisConstant);
                    if (newLiteral != null)
                    {
                        RotateFromLeft(node, leftOperator, newLiteral);
                    }
                }
            }
            else if (leftOperator.OperatorToken == JsToken.Minus)
            {
                if (node.OperatorToken == JsToken.Plus)
                {
                    // minus-plus
                    // the minus operator goes first and will always convert to number.
                    // if our constant is not a string, then it will be a numeric addition and we can combine them.
                    // if our constant is a string, then we'll end up doing a string concat, so we can't combine
                    if (!thisConstant.IsStringLiteral)
                    {
                        // two numeric operators. a-n1+n2 is the same as a-(n1-n2)
                        JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            // a-(-n) is numerically equivalent as a+n -- and takes fewer characters to represent.
                            // BUT we can't do that because that might change a numeric operation (the original minus)
                            // to a string concatenation if the unknown operand turns out to be a string!

                            RotateFromLeft(node, leftOperator, newLiteral);
                        }
                        else
                        {
                            // if the left-left is a constant, then we can try combining with it
                            JsConstantWrapper leftLeft = leftOperator.Operand1 as JsConstantWrapper;
                            if (leftLeft != null)
                            {
                                EvalFarToTheLeft(node, thisConstant, leftLeft, leftOperator);
                            }
                        }
                    }
                }
                else if (node.OperatorToken == JsToken.Minus)
                {
                    // minus-minus. Both operations are numeric.
                    // (a-n1)-n2 => a-(n1+n2), so we can add the two constants and subtract from
                    // the left-hand non-constant.
                    JsConstantWrapper newLiteral = NumericAddition(otherConstant, thisConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        // make it the new right-hand literal for the left-hand operator
                        // and make the left-hand operator replace our operator
                        RotateFromLeft(node, leftOperator, newLiteral);
                    }
                    else
                    {
                        // if the left-left is a constant, then we can try combining with it
                        JsConstantWrapper leftLeft = leftOperator.Operand1 as JsConstantWrapper;
                        if (leftLeft != null)
                        {
                            EvalFarToTheLeft(node, thisConstant, leftLeft, leftOperator);
                        }
                    }
                }
            }
            else if (leftOperator.OperatorToken == node.OperatorToken
                && (node.OperatorToken == JsToken.Multiply || node.OperatorToken == JsToken.Divide))
            {
                // either multiply-multiply or divide-divide
                // either way, we use the other operand and the product of the two constants.
                // if the product blows up to an infinte value, then don't combine them because that
                // could change the way the program goes at runtime, depending on the unknown value.
                JsConstantWrapper newLiteral = Multiply(otherConstant, thisConstant);
                if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, newLiteral))
                {
                    RotateFromLeft(node, leftOperator, newLiteral);
                }
            }
            else if ((leftOperator.OperatorToken == JsToken.Multiply && node.OperatorToken == JsToken.Divide)
                || (leftOperator.OperatorToken == JsToken.Divide && node.OperatorToken == JsToken.Multiply))
            {
                if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
                {
                    // get the two division operators
                    JsConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    JsConstantWrapper thisOverOther = Divide(thisConstant, otherConstant);

                    // get the lengths
                    int otherOverThisLength = otherOverThis != null ? otherOverThis.ToCode().Length : int.MaxValue;
                    int thisOverOtherLength = thisOverOther != null ? thisOverOther.ToCode().Length : int.MaxValue;

                    // we'll want to use whichever one is shorter, and whichever one does NOT involve an overflow
                    // or possible underflow
                    if (otherOverThis != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, otherOverThis)
                        && (thisOverOther == null || otherOverThisLength < thisOverOtherLength))
                    {
                        // but only if it's smaller than the original expression
                        if (otherOverThisLength <= otherConstant.ToCode().Length + thisConstant.ToCode().Length + 1)
                        {
                            // same operator
                            RotateFromLeft(node, leftOperator, otherOverThis);
                        }
                    }
                    else if (thisOverOther != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, thisOverOther))
                    {
                        // but only if it's smaller than the original expression
                        if (thisOverOtherLength <= otherConstant.ToCode().Length + thisConstant.ToCode().Length + 1)
                        {
                            // opposite operator
                            leftOperator.OperatorToken = leftOperator.OperatorToken == JsToken.Multiply ? JsToken.Divide : JsToken.Multiply;
                            RotateFromLeft(node, leftOperator, thisOverOther);
                        }
                    }
                }
            }
            else if (node.OperatorToken == leftOperator.OperatorToken
                && (node.OperatorToken == JsToken.BitwiseAnd || node.OperatorToken == JsToken.BitwiseOr || node.OperatorToken == JsToken.BitwiseXor))
            {
                // identical bitwise operators can be combined
                JsConstantWrapper newLiteral = null;
                switch (node.OperatorToken)
                {
                    case JsToken.BitwiseAnd:
                        newLiteral = BitwiseAnd(otherConstant, thisConstant);
                        break;

                    case JsToken.BitwiseOr:
                        newLiteral = BitwiseOr(otherConstant, thisConstant);
                        break;

                    case JsToken.BitwiseXor:
                        newLiteral = BitwiseXor(otherConstant, thisConstant);
                        break;
                }
                if (newLiteral != null)
                {
                    RotateFromLeft(node, leftOperator, newLiteral);
                }
            }
        }
        private void EvalToTheRight(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator rightOperator)
        {
            if (node.OperatorToken == JsToken.Plus)
            {
                if (rightOperator.OperatorToken == JsToken.Plus && otherConstant.IsStringLiteral)
                {
                    // plus-plus, and the other constant is a string. So the right operator will be a string-concat
                    // that generates a string. And since this is a plus-operator, then this operator will be a string-
                    // concat as well. So we can just combine the strings now and replace our node with the right-hand
                    // operation
                    JsConstantWrapper newLiteral = StringConcat(thisConstant, otherConstant);
                    if (newLiteral != null)
                    {
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Minus && !thisConstant.IsStringLiteral)
                {
                    // plus-minus. Now, the minus operation happens first, and it will perform a numeric
                    // operation. The plus is NOT string, so that means it will also be a numeric operation
                    // and we can combine the operators numericly.
                    JsConstantWrapper newLiteral = NumericAddition(thisConstant, otherConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                    else
                    {
                        JsConstantWrapper rightRight = rightOperator.Operand2 as JsConstantWrapper;
                        if (rightRight != null)
                        {
                            EvalFarToTheRight(node, thisConstant, rightRight, rightOperator);
                        }
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Minus && rightOperator.OperatorToken == JsToken.Minus)
            {
                // minus-minus
                // both operations are numeric, so we can combine the constant operands. However, we
                // can't combine them into a plus, so make sure we do the minus in the opposite direction
                JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                if (newLiteral != null && NoOverflow(newLiteral))
                {
                    rightOperator.SwapOperands();
                    RotateFromLeft(node, rightOperator, newLiteral);
                }
                else
                {
                    JsConstantWrapper rightRight = rightOperator.Operand2 as JsConstantWrapper;
                    if (rightRight != null)
                    {
                        EvalFarToTheRight(node, thisConstant, rightRight, rightOperator);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Multiply
                && (rightOperator.OperatorToken == JsToken.Multiply || rightOperator.OperatorToken == JsToken.Divide))
            {
                // multiply-divide or multiply-multiply
                // multiply the operands and use the right-hand operator
                JsConstantWrapper newLiteral = Multiply(thisConstant, otherConstant);
                if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral))
                {
                    RotateFromRight(node, rightOperator, newLiteral);
                }
            }
            else if (node.OperatorToken == JsToken.Divide)
            {
                if (rightOperator.OperatorToken == JsToken.Multiply)
                {
                    // divide-multiply
                    JsConstantWrapper newLiteral = Divide(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral)
                        && newLiteral.ToCode().Length < thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                    {
                        // flip the operator: multiply becomes divide; devide becomes multiply
                        rightOperator.OperatorToken = JsToken.Divide;

                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Divide)
                {
                    // divide-divide
                    // get constants for left/right and for right/left
                    JsConstantWrapper leftOverRight = Divide(thisConstant, otherConstant);
                    JsConstantWrapper rightOverLeft = Divide(otherConstant, thisConstant);

                    // get the lengths of the resulting code
                    int leftOverRightLength = leftOverRight != null ? leftOverRight.ToCode().Length : int.MaxValue;
                    int rightOverLeftLength = rightOverLeft != null ? rightOverLeft.ToCode().Length : int.MaxValue;

                    // try whichever is smaller
                    if (leftOverRight != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, leftOverRight)
                        && (rightOverLeft == null || leftOverRightLength < rightOverLeftLength))
                    {
                        // use left-over-right.
                        // but only if the resulting value is smaller than the original expression
                        if (leftOverRightLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // We don't need to swap the operands, but we do need to switch the operator
                            rightOperator.OperatorToken = JsToken.Multiply;
                            RotateFromRight(node, rightOperator, leftOverRight);
                        }
                    }
                    else if (rightOverLeft != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, rightOverLeft))
                    {
                        // but only if the resulting value is smaller than the original expression
                        if (rightOverLeftLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // use right-over-left. Keep the operator, but swap the operands
                            rightOperator.SwapOperands();
                            RotateFromLeft(node, rightOperator, rightOverLeft);
                        }
                    }
                }
            }
        }
 public void Visit(JsConstantWrapper node)
 {
     // it's a constant, so we don't care
 }
Example #13
0
        private JsAstNode ParseSwitchStatement()
        {
            JsContext switchCtx = m_currentToken.Clone();
            JsAstNode expr = null;
            JsAstNodeList cases = null;
            var braceOnNewLine = false;
            JsContext braceContext = null;
            m_blockType.Add(BlockType.Switch);
            try
            {
                // read switch(expr)
                GetNextToken();
                if (JsToken.LeftParenthesis != m_currentToken.Token)
                    ReportError(JsError.NoLeftParenthesis);
                GetNextToken();
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                try
                {
                    expr = ParseExpression();

                    if (JsToken.RightParenthesis != m_currentToken.Token)
                    {
                        ReportError(JsError.NoRightParenthesis);
                    }

                    GetNextToken();
                    if (JsToken.LeftCurly != m_currentToken.Token)
                    {
                        ReportError(JsError.NoLeftCurly);
                    }

                    braceOnNewLine = m_foundEndOfLine;
                    braceContext = m_currentToken.Clone();
                    GetNextToken();

                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1
                          && IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1)
                    {
                        // give up
                        exc._partiallyComputedNode = null;
                        throw;
                    }
                    else
                    {
                        if (exc._partiallyComputedNode == null)
                            expr = new JsConstantWrapper(true, JsPrimitiveType.Boolean, CurrentPositionContext(), this);
                        else
                            expr = exc._partiallyComputedNode;

                        if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) != -1)
                        {
                            if (exc._token == JsToken.RightParenthesis)
                                GetNextToken();

                            if (JsToken.LeftCurly != m_currentToken.Token)
                            {
                                ReportError(JsError.NoLeftCurly);
                            }
                            braceOnNewLine = m_foundEndOfLine;
                            braceContext = m_currentToken.Clone();
                            GetNextToken();
                        }

                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                }

                // parse the switch body
                cases = new JsAstNodeList(CurrentPositionContext(), this);
                bool defaultStatement = false;
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                try
                {
                    while (JsToken.RightCurly != m_currentToken.Token)
                    {
                        JsSwitchCase caseClause = null;
                        JsAstNode caseValue = null;
                        var caseCtx = m_currentToken.Clone();
                        JsContext colonContext = null;
                        m_noSkipTokenSet.Add(NoSkipTokenSet.s_CaseNoSkipTokenSet);
                        try
                        {
                            if (JsToken.Case == m_currentToken.Token)
                            {
                                // get the case
                                GetNextToken();
                                caseValue = ParseExpression();
                            }
                            else if (JsToken.Default == m_currentToken.Token)
                            {
                                // get the default
                                if (defaultStatement)
                                {
                                    // we report an error but we still accept the default
                                    ReportError(JsError.DupDefault, true);
                                }
                                else
                                {
                                    defaultStatement = true;
                                }
                                GetNextToken();
                            }
                            else
                            {
                                // This is an error, there is no case or default. Assume a default was missing and keep going
                                defaultStatement = true;
                                ReportError(JsError.BadSwitch);
                            }

                            if (JsToken.Colon != m_currentToken.Token)
                            {
                                ReportError(JsError.NoColon);
                            }
                            else
                            {
                                colonContext = m_currentToken.Clone();
                            }

                            // read the statements inside the case or default
                            GetNextToken();
                        }
                        catch (RecoveryTokenException exc)
                        {
                            // right now we can only get here for the 'case' statement
                            if (IndexOfToken(NoSkipTokenSet.s_CaseNoSkipTokenSet, exc) == -1)
                            {
                                // ignore the current case or default
                                exc._partiallyComputedNode = null;
                                throw;
                            }
                            else
                            {
                                caseValue = exc._partiallyComputedNode;

                                if (exc._token == JsToken.Colon)
                                {
                                    GetNextToken();
                                }
                            }
                        }
                        finally
                        {
                            m_noSkipTokenSet.Remove(NoSkipTokenSet.s_CaseNoSkipTokenSet);
                        }

                        m_blockType.Add(BlockType.Block);
                        try
                        {
                            var statements = new JsBlock(m_currentToken.Clone(), this);
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                            try
                            {
                                while (JsToken.RightCurly != m_currentToken.Token && JsToken.Case != m_currentToken.Token && JsToken.Default != m_currentToken.Token)
                                {
                                    try
                                    {
                                        // parse a Statement, not a SourceElement
                                        statements.Append(ParseStatement(false));
                                    }
                                    catch (RecoveryTokenException exc)
                                    {
                                        if (exc._partiallyComputedNode != null)
                                        {
                                            statements.Append(exc._partiallyComputedNode);
                                            exc._partiallyComputedNode = null;
                                        }

                                        if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1)
                                        {
                                            throw;
                                        }
                                    }
                                }
                            }
                            catch (RecoveryTokenException exc)
                            {
                                if (IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1)
                                {
                                    caseClause = new JsSwitchCase(caseCtx, this)
                                        {
                                            CaseValue = caseValue,
                                            ColonContext = colonContext,
                                            Statements = statements
                                        };
                                    cases.Append(caseClause);
                                    throw;
                                }
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                            }

                            caseCtx.UpdateWith(statements.Context);
                            caseClause = new JsSwitchCase(caseCtx, this)
                                {
                                    CaseValue = caseValue,
                                    ColonContext = colonContext,
                                    Statements = statements
                                };
                            cases.Append(caseClause);
                        }
                        finally
                        {
                            m_blockType.RemoveAt(m_blockType.Count - 1);
                        }
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1)
                    {
                        //save what you can a rethrow
                        switchCtx.UpdateWith(CurrentPositionContext());
                        exc._partiallyComputedNode = new JsSwitch(switchCtx, this)
                            {
                                Expression = expr,
                                BraceContext = braceContext,
                                Cases = cases,
                                BraceOnNewLine = braceOnNewLine
                            };
                        throw;
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                }
                switchCtx.UpdateWith(m_currentToken);
                GetNextToken();
            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

            return new JsSwitch(switchCtx, this)
                {
                    Expression = expr,
                    BraceContext = braceContext,
                    Cases = cases,
                    BraceOnNewLine = braceOnNewLine
                };
        }
 /// <summary>
 /// Return true if the result isn't an overflow condition
 /// </summary>
 /// <param name="result">result constant</param>
 /// <returns>true is not an overflow; false if it is</returns>
 private static bool NoOverflow(JsConstantWrapper result)
 {
     return !result.IsInfinity;
 }
        private JsConstantWrapper BitwiseXor(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;

            if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
            {
                try
                {
                    Int32 lValue = left.ToInt32();
                    Int32 rValue = right.ToInt32();
                    newLiteral = new JsConstantWrapper(Convert.ToDouble(lValue ^ rValue), JsPrimitiveType.Number, null, 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
                }
            }

            return newLiteral;
        }
Example #16
0
        //---------------------------------------------------------------------------------------
        // ParseWhileStatement
        //
        //  WhileStatement :
        //    'while' '(' Expression ')' Statement
        //---------------------------------------------------------------------------------------
        private JsWhileNode ParseWhileStatement()
        {
            JsContext whileCtx = m_currentToken.Clone();
            JsAstNode condition = null;
            JsAstNode body = null;
            m_blockType.Add(BlockType.Loop);
            try
            {
                GetNextToken();
                if (JsToken.LeftParenthesis != m_currentToken.Token)
                {
                    ReportError(JsError.NoLeftParenthesis);
                }
                GetNextToken();
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                try
                {
                    condition = ParseExpression();
                    if (JsToken.RightParenthesis != m_currentToken.Token)
                    {
                        ReportError(JsError.NoRightParenthesis);
                        whileCtx.UpdateWith(condition.Context);
                    }
                    else
                        whileCtx.UpdateWith(m_currentToken);

                    GetNextToken();
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                    {
                        // abort the while there is really no much to do here
                        exc._partiallyComputedNode = null;
                        throw;
                    }
                    else
                    {
                        // make up a condition
                        if (exc._partiallyComputedNode != null)
                            condition = exc._partiallyComputedNode;
                        else
                            condition = new JsConstantWrapper(false, JsPrimitiveType.Boolean, CurrentPositionContext(), this);

                        if (JsToken.RightParenthesis == m_currentToken.Token)
                            GetNextToken();
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                }

                // if this is an assignment, throw a warning in case the developer
                // meant to use == instead of =
                // but no warning if the condition is wrapped in parens.
                var binOp = condition as JsBinaryOperator;
                if (binOp != null && binOp.OperatorToken == JsToken.Assign)
                {
                    condition.Context.HandleError(JsError.SuspectAssignment);
                }

                // if the statements aren't withing curly-braces, throw a possible error
                if (JsToken.LeftCurly != m_currentToken.Token)
                {
                    ReportError(JsError.StatementBlockExpected, CurrentPositionContext(), true);
                }
                try
                {
                    // parse a Statement, not a SourceElement
                    // and ignore any important comments that spring up right here.
                    body = ParseStatement(false, true);
                }
                catch (RecoveryTokenException exc)
                {
                    if (exc._partiallyComputedNode != null)
                        body = exc._partiallyComputedNode;
                    else
                        body = new JsBlock(CurrentPositionContext(), this);

                    exc._partiallyComputedNode = new JsWhileNode(whileCtx, this)
                        {
                            Condition = condition,
                            Body = JsAstNode.ForceToBlock(body)
                        };
                    throw;
                }

            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

            return new JsWhileNode(whileCtx, this)
                {
                    Condition = condition,
                    Body = JsAstNode.ForceToBlock(body)
                };
        }
        private static string ComputeJoin(JsArrayLiteral arrayLiteral, JsConstantWrapper separatorNode)
        {
            // if the separator node is null, then the separator is a single comma character.
            // otherwise it's just the string value of the separator.
            var separator = separatorNode == null ? "," : separatorNode.ToString();

            var sb = new StringBuilder();
            for (var ndx = 0; ndx < arrayLiteral.Elements.Count; ++ndx)
            {
                // add the separator between items (if we have one)
                if (ndx > 0 && !string.IsNullOrEmpty(separator))
                {
                    sb.Append(separator);
                }

                // the element is a constant wrapper (we wouldn't get this far if it wasn't),
                // but we've overloaded the virtual ToString method on ConstantWrappers to convert the
                // constant value to a string value.
                sb.Append(arrayLiteral.Elements[ndx].ToString());
            }

            return sb.ToString();
        }
        private JsConstantWrapper LogicalOr(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;
            if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
            {
                try
                {
                    // if the left-hand side evaluates to true, return the left-hand side.
                    // if the left-hand side is false, return the right-hand side.
                    newLiteral = left.ToBoolean() ? left : right;
                }
                catch (InvalidCastException)
                {
                    // if we couldn't cast to bool, ignore
                }
            }

            return newLiteral;
        }
        private JsConstantWrapper Plus(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;

            if (left.IsStringLiteral || right.IsStringLiteral)
            {
                // one or both are strings -- this is a strng concat operation
                newLiteral = StringConcat(left, right);
            }
            else
            {
                // neither are strings -- this is a numeric addition operation
                newLiteral = NumericAddition(left, right);
            }
            return newLiteral;
        }
Example #20
0
        //---------------------------------------------------------------------------------------
        // ParseWithStatement
        //
        //  WithStatement :
        //    'with' '(' Expression ')' Statement
        //---------------------------------------------------------------------------------------
        private JsWithNode ParseWithStatement()
        {
            JsContext withCtx = m_currentToken.Clone();
            JsAstNode obj = null;
            JsBlock block = null;
            m_blockType.Add(BlockType.Block);
            try
            {
                GetNextToken();
                if (JsToken.LeftParenthesis != m_currentToken.Token)
                    ReportError(JsError.NoLeftParenthesis);
                GetNextToken();
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                try
                {
                    obj = ParseExpression();
                    if (JsToken.RightParenthesis != m_currentToken.Token)
                    {
                        withCtx.UpdateWith(obj.Context);
                        ReportError(JsError.NoRightParenthesis);
                    }
                    else
                        withCtx.UpdateWith(m_currentToken);
                    GetNextToken();
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                    {
                        // give up
                        exc._partiallyComputedNode = null;
                        throw;
                    }
                    else
                    {
                        if (exc._partiallyComputedNode == null)
                            obj = new JsConstantWrapper(true, JsPrimitiveType.Boolean, CurrentPositionContext(), this);
                        else
                            obj = exc._partiallyComputedNode;
                        withCtx.UpdateWith(obj.Context);

                        if (exc._token == JsToken.RightParenthesis)
                            GetNextToken();
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                }

                // if the statements aren't withing curly-braces, throw a possible error
                if (JsToken.LeftCurly != m_currentToken.Token)
                {
                    ReportError(JsError.StatementBlockExpected, CurrentPositionContext(), true);
                }

                try
                {
                    // parse a Statement, not a SourceElement
                    // and ignore any important comments that spring up right here.
                    JsAstNode statement = ParseStatement(false, true);

                    // but make sure we save it as a block
                    block = statement as JsBlock;
                    if (block == null)
                    {
                        block = new JsBlock(statement.Context, this);
                        block.Append(statement);
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    if (exc._partiallyComputedNode == null)
                    {
                        block = new JsBlock(CurrentPositionContext(), this);
                    }
                    else
                    {
                        block = exc._partiallyComputedNode as JsBlock;
                        if (block == null)
                        {
                            block = new JsBlock(exc._partiallyComputedNode.Context, this);
                            block.Append(exc._partiallyComputedNode);
                        }
                    }
                    exc._partiallyComputedNode = new JsWithNode(withCtx, this)
                        {
                            WithObject = obj,
                            Body = block
                        };
                    throw;
                }
            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

            return new JsWithNode(withCtx, this)
                {
                    WithObject = obj,
                    Body = block
                };
        }
Example #21
0
 public void Visit(JsConstantWrapper node)
 {
     // not applicable; terminate
 }
 public void Visit(JsConstantWrapper node)
 {
     // not applicable; terminate
 }
 public void Visit(JsConstantWrapper node)
 {
     // we're good
 }
Example #24
0
        //---------------------------------------------------------------------------------------
        // MemberExpression
        //
        // Accessor :
        //  <empty> |
        //  Arguments Accessor
        //  '[' Expression ']' Accessor |
        //  '.' Identifier Accessor |
        //
        //  Don't have this function throwing an exception without checking all the calling sites.
        //  There is state in instance variable that is saved on the calling stack in some function
        //  (i.e ParseFunction and ParseClass) and you don't want to blow up the stack
        //---------------------------------------------------------------------------------------
        private JsAstNode MemberExpression(JsAstNode expression, List<JsContext> newContexts)
        {
            for (; ; )
            {
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_MemberExprNoSkipTokenSet);
                try
                {
                    switch (m_currentToken.Token)
                    {
                        case JsToken.LeftParenthesis:
                            JsAstNodeList args = null;
                            RecoveryTokenException callError = null;
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenToken);
                            try
                            {
                                args = ParseExpressionList(JsToken.RightParenthesis);
                            }
                            catch (RecoveryTokenException exc)
                            {
                                args = (JsAstNodeList)exc._partiallyComputedNode;
                                if (IndexOfToken(NoSkipTokenSet.s_ParenToken, exc) == -1)
                                    callError = exc; // thrown later on
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenToken);
                            }

                            expression = new JsCallNode(expression.Context.CombineWith(args.Context), this)
                                {
                                    Function = expression,
                                    Arguments = args,
                                    InBrackets = false
                                };

                            if (null != newContexts && newContexts.Count > 0)
                            {
                                (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context);
                                if (!(expression is JsCallNode))
                                {
                                    expression = new JsCallNode(newContexts[newContexts.Count - 1], this)
                                        {
                                            Function = expression,
                                            Arguments = new JsAstNodeList(CurrentPositionContext(), this)
                                        };
                                }
                                else
                                {
                                    expression.Context = newContexts[newContexts.Count - 1];
                                }

                                ((JsCallNode)expression).IsConstructor = true;
                                newContexts.RemoveAt(newContexts.Count - 1);
                            }

                            if (callError != null)
                            {
                                callError._partiallyComputedNode = expression;
                                throw callError;
                            }

                            GetNextToken();
                            break;

                        case JsToken.LeftBracket:
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_BracketToken);
                            try
                            {
                                //
                                // ROTOR parses a[b,c] as a call to a, passing in the arguments b and c.
                                // the correct parse is a member lookup on a of c -- the "b,c" should be
                                // a single expression with a comma operator that evaluates b but only
                                // returns c.
                                // So we'll change the default behavior from parsing an expression list to
                                // parsing a single expression, but returning a single-item list (or an empty
                                // list if there is no expression) so the rest of the code will work.
                                //
                                //args = ParseExpressionList(JSToken.RightBracket);
                                GetNextToken();
                                args = new JsAstNodeList(CurrentPositionContext(), this);

                                JsAstNode accessor = ParseExpression();
                                if (accessor != null)
                                {
                                    args.Append(accessor);
                                }
                            }
                            catch (RecoveryTokenException exc)
                            {
                                if (IndexOfToken(NoSkipTokenSet.s_BracketToken, exc) == -1)
                                {
                                    if (exc._partiallyComputedNode != null)
                                    {
                                        exc._partiallyComputedNode =
                                           new JsCallNode(expression.Context.CombineWith(m_currentToken.Clone()), this)
                                            {
                                                Function = expression,
                                                Arguments = (JsAstNodeList)exc._partiallyComputedNode,
                                                InBrackets = true
                                            };
                                    }
                                    else
                                    {
                                        exc._partiallyComputedNode = expression;
                                    }
                                    throw;
                                }
                                else
                                    args = (JsAstNodeList)exc._partiallyComputedNode;
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BracketToken);
                            }
                            expression = new JsCallNode(expression.Context.CombineWith(m_currentToken.Clone()), this)
                                {
                                    Function = expression,
                                    Arguments = args,
                                    InBrackets = true
                                };

                            // there originally was code here in the ROTOR sources that checked the new context list and
                            // changed this member call to a constructor call, effectively combining the two. I believe they
                            // need to remain separate.

                            // remove the close bracket token
                            GetNextToken();
                            break;

                        case JsToken.AccessField:
                            JsConstantWrapper id = null;
                            JsContext nameContext = m_currentToken.Clone();
                            GetNextToken();
                            if (JsToken.Identifier != m_currentToken.Token)
                            {
                                string identifier = JsKeyword.CanBeIdentifier(m_currentToken.Token);
                                if (null != identifier)
                                {
                                    // don't report an error here -- it's actually okay to have a property name
                                    // that is a keyword which is okay to be an identifier. For instance,
                                    // jQuery has a commonly-used method named "get" to make an ajax request
                                    //ForceReportInfo(JSError.KeywordUsedAsIdentifier);
                                    id = new JsConstantWrapper(identifier, JsPrimitiveType.String, m_currentToken.Clone(), this);
                                }
                                else if (JsScanner.IsValidIdentifier(m_currentToken.Code))
                                {
                                    // it must be a keyword, because it can't technically be an identifier,
                                    // but it IS a valid identifier format. Throw a warning but still
                                    // create the constant wrapper so we can output it as-is
                                    ReportError(JsError.KeywordUsedAsIdentifier, m_currentToken.Clone(), true);
                                    id = new JsConstantWrapper(m_currentToken.Code, JsPrimitiveType.String, m_currentToken.Clone(), this);
                                }
                                else
                                {
                                    ReportError(JsError.NoIdentifier);
                                    SkipTokensAndThrow(expression);
                                }
                            }
                            else
                            {
                                id = new JsConstantWrapper(m_scanner.Identifier, JsPrimitiveType.String, m_currentToken.Clone(), this);
                            }
                            GetNextToken();
                            expression = new JsMember(expression.Context.CombineWith(id.Context), this)
                                {
                                    Root = expression,
                                    Name = id.Context.Code,
                                    NameContext = nameContext.CombineWith(id.Context)
                                };
                            break;
                        default:
                            if (null != newContexts)
                            {
                                while (newContexts.Count > 0)
                                {
                                    (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context);
                                    expression = new JsCallNode(newContexts[newContexts.Count - 1], this)
                                        {
                                            Function = expression,
                                            Arguments = new JsAstNodeList(CurrentPositionContext(), this)
                                        };
                                    ((JsCallNode)expression).IsConstructor = true;
                                    newContexts.RemoveAt(newContexts.Count - 1);
                                }
                            }
                            return expression;
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_MemberExprNoSkipTokenSet, exc) != -1)
                        expression = exc._partiallyComputedNode;
                    else
                    {
                        Debug.Assert(exc._partiallyComputedNode == expression);
                        throw;
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_MemberExprNoSkipTokenSet);
                }
            }
        }
        public override void Visit(JsMember node)
        {
            if (node != null)
            {
                // depth-first
                base.Visit(node);

                if (string.CompareOrdinal(node.Name, "length") == 0
                    && m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateLiteralLengths))
                {
                    // if we create a constant, we'll replace the current node with it
                    JsConstantWrapper length = null;

                    JsArrayLiteral arrayLiteral;
                    var constantWrapper = node.Root as JsConstantWrapper;
                    if (constantWrapper != null)
                    {
                        if (constantWrapper.PrimitiveType == JsPrimitiveType.String && !constantWrapper.MayHaveIssues)
                        {
                            length = new JsConstantWrapper(constantWrapper.ToString().Length, JsPrimitiveType.Number, node.Context, node.Parser);
                        }
                    }
                    else if ((arrayLiteral = node.Root as JsArrayLiteral) != null && !arrayLiteral.MayHaveIssues)
                    {
                        // get the count of items in the array literal, create a constant wrapper from it, and
                        // replace this node with it
                        length = new JsConstantWrapper(arrayLiteral.Elements.Count, JsPrimitiveType.Number, node.Context, node.Parser);
                    }

                    if (length != null)
                    {
                        node.Parent.ReplaceChild(node, length);
                    }
                }
            }
        }
        /// <summary>
        /// We have determined that our right-hand operand is another binary operator, and its
        /// left-hand operand is a constant that can be combined with our left-hand operand.
        /// Now we want to set the left-hand operand of that other operator to the newly-
        /// combined constant value, and then rotate it up -- replace our binary operator
        /// with this newly-modified binary operator, and then attempt to re-evaluate it.
        /// </summary>
        /// <param name="binaryOp">the binary operator that is our right-hand operand</param>
        /// <param name="newLiteral">the newly-combined literal</param>
        /// <param name="node"></param>
        private void RotateFromRight(JsBinaryOperator node, JsBinaryOperator binaryOp, JsConstantWrapper newLiteral)
        {
            // replace our node with the binary operator
            binaryOp.Operand1 = newLiteral;
            node.Parent.ReplaceChild(node, binaryOp);

            // and just for good measure.. revisit the node that's taking our place, since
            // we just changed a constant value. Assuming the other operand is a constant, too.
            JsConstantWrapper otherConstant = binaryOp.Operand2 as JsConstantWrapper;
            if (otherConstant != null)
            {
                EvalThisOperator(binaryOp, newLiteral, otherConstant);
            }
        }
        /// <summary>
        /// If the new literal is a string literal, then we need to check to see if our
        /// parent is a CallNode. If it is, and if the string literal can be an identifier,
        /// we'll replace it with a Member-Dot operation.
        /// </summary>
        /// <param name="newLiteral">newLiteral we intend to replace this binaryop node with</param>
        /// <returns>true if we replaced the parent callnode with a member-dot operation</returns>
        /// <param name="node"></param>
        private bool ReplaceMemberBracketWithDot(JsBinaryOperator node, JsConstantWrapper newLiteral)
        {
            if (newLiteral.IsStringLiteral)
            {
                // see if this newly-combined string is the sole argument to a
                // call-brackets node. If it is and the combined string is a valid
                // identifier (and not a keyword), then we can replace the call
                // with a member operator.
                // remember that the parent of the argument won't be the call node -- it
                // will be the ast node list representing the arguments, whose parent will
                // be the node list.
                JsCallNode parentCall = (node.Parent is JsAstNodeList ? node.Parent.Parent as JsCallNode : null);
                if (parentCall != null && parentCall.InBrackets)
                {
                    // get the newly-combined string
                    string combinedString = newLiteral.ToString();

                    // see if this new string is the target of a replacement operation
                    string newName;
                    if (m_parser.Settings.HasRenamePairs && m_parser.Settings.ManualRenamesProperties
                        && m_parser.Settings.IsModificationAllowed(JsTreeModifications.PropertyRenaming)
                        && !string.IsNullOrEmpty(newName = m_parser.Settings.GetNewName(combinedString)))
                    {
                        // yes, it is. Now see if the new name is safe to be converted to a dot-operation.
                        if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.BracketMemberToDotMember)
                            && JsScanner.IsSafeIdentifier(newName)
                            && !JsScanner.IsKeyword(newName, parentCall.EnclosingScope.UseStrict))
                        {
                            // we want to replace the call with operator with a new member dot operation, and
                            // since we won't be analyzing it (we're past the analyze phase, we're going to need
                            // to use the new string value
                            JsMember replacementMember = new JsMember(parentCall.Context, m_parser)
                                {
                                    Root = parentCall.Function,
                                    Name = newName,
                                    NameContext = parentCall.Arguments[0].Context
                                };
                            parentCall.Parent.ReplaceChild(parentCall, replacementMember);
                            return true;
                        }
                        else
                        {
                            // nope, can't be changed to a dot-operator for whatever reason.
                            // just replace the value on this new literal. The old operation will
                            // get replaced with this new literal
                            newLiteral.Value = newName;

                            // and make sure it's type is string
                            newLiteral.PrimitiveType = JsPrimitiveType.String;
                        }
                    }
                    else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.BracketMemberToDotMember))
                    {
                        // our parent is a call-bracket -- now we just need to see if the newly-combined
                        // string can be an identifier
                        if (JsScanner.IsSafeIdentifier(combinedString) && !JsScanner.IsKeyword(combinedString, parentCall.EnclosingScope.UseStrict))
                        {
                            // yes -- replace the parent call with a new member node using the newly-combined string
                            JsMember replacementMember = new JsMember(parentCall.Context, m_parser)
                                {
                                    Root = parentCall.Function,
                                    Name = combinedString,
                                    NameContext = parentCall.Arguments[0].Context
                                };
                            parentCall.Parent.ReplaceChild(parentCall, replacementMember);
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        private JsConstantWrapper StringConcat(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;

            // if we don't want to combine adjacent string literals, then we know we don't want to do
            // anything here.
            if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.CombineAdjacentStringLiterals))
            {
                // if either one of the operands is not a string literal, then check to see if we allow
                // evaluation of numeric expression; if not, then no-go. IF they are both string literals,
                // then it doesn't matter what the numeric flag says.
                if ((left.IsStringLiteral && right.IsStringLiteral)
                    || m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
                {
                    // if either value is a floating-point number (a number, not NaN, not Infinite, not an Integer),
                    // then we won't do the string concatenation because different browsers may have subtle differences
                    // in their double-to-string conversion algorithms.
                    // so if neither is a numeric literal, or if one or both are, if they are both integer literals
                    // in the range that we can EXACTLY represent them in a double, then we can proceed.
                    // NaN, +Infinity and -Infinity are also acceptable
                    if (left.IsOkayToCombine && right.IsOkayToCombine)
                    {
                        newLiteral = new JsConstantWrapper(left.ToString() + right.ToString(), JsPrimitiveType.String, null, m_parser);
                    }
                }
            }

            return newLiteral;
        }
        private JsConstantWrapper StrictNotEqual(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;

            if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
            {
                JsPrimitiveType leftType = left.PrimitiveType;
                if (leftType == right.PrimitiveType)
                {
                    // the values are the same type
                    switch (leftType)
                    {
                        case JsPrimitiveType.Null:
                            // null !== null is false
                            newLiteral = new JsConstantWrapper(false, JsPrimitiveType.Boolean, null, m_parser);
                            break;

                        case JsPrimitiveType.Boolean:
                            // compare boolean values
                            newLiteral = new JsConstantWrapper(left.ToBoolean() != right.ToBoolean(), JsPrimitiveType.Boolean, null, m_parser);
                            break;

                        case JsPrimitiveType.String:
                            // compare string ordinally
                            if (left.IsOkayToCombine && right.IsOkayToCombine)
                            {
                                newLiteral = new JsConstantWrapper(string.CompareOrdinal(left.ToString(), right.ToString()) != 0, JsPrimitiveType.Boolean, null, m_parser);
                            }
                            break;

                        case JsPrimitiveType.Number:
                            try
                            {
                                // compare the values
                                // +0 and -0 are treated as "equal" in C#, so we don't need to test them separately.
                                // and NaN is always unequal to everything else, including itself.
                                if (left.IsOkayToCombine && right.IsOkayToCombine)
                                {
                                    newLiteral = new JsConstantWrapper(left.ToNumber() != right.ToNumber(), JsPrimitiveType.Boolean, null, 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;
                    }
                }
                else
                {
                    // if they aren't the same type, they are not equal
                    newLiteral = new JsConstantWrapper(true, JsPrimitiveType.Boolean, null, m_parser);
                }
            }

            return newLiteral;
        }
        public void Visit(JsConstantWrapper node)
        {
            if (node != null)
            {
                // allow string, number, true, false, and null.
                switch (node.PrimitiveType)
                {
                    case JsPrimitiveType.Boolean:
                        m_writer.Write((bool)node.Value ? "true" : "false");
                        break;

                    case JsPrimitiveType.Null:
                        m_writer.Write("null");
                        break;

                    case JsPrimitiveType.Number:
                        OutputNumber((double)node.Value, node.Context);
                        break;

                    case JsPrimitiveType.String:
                    case JsPrimitiveType.Other:
                        // string -- or treat it like a string
                        OutputString(node.Value.ToString());
                        break;
                }
            }
        }
        private JsConstantWrapper UnsignedRightShift(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;

            if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
            {
                try
                {
                    // left-hand value is a 32-bit signed integer
                    UInt32 lvalue = left.ToUInt32();

                    // mask only the bottom 5 bits of the right-hand value
                    int rvalue = (int)(right.ToUInt32() & 0x1F);

                    // convert the result to a double
                    double result = Convert.ToDouble(lvalue >> rvalue);
                    newLiteral = new JsConstantWrapper(result, JsPrimitiveType.Number, null, 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
                }
            }

            return newLiteral;
        }
 public override void Visit(JsConstantWrapper node)
 {
     if (node != null)
     {
         // measure
         if (node.PrimitiveType == JsPrimitiveType.Boolean)
         {
             if (m_measure)
             {
                 // if we are converting true/false literals to !0/!1, then
                 // a logical-not doesn't add or subtract anything. But if we aren't,
                 // we need to add/subtract the difference in the length between the
                 // "true" and "false" strings
                 if (!m_parser.Settings.MinifyCode
                     || !m_parser.Settings.IsModificationAllowed(JsTreeModifications.BooleanLiteralsToNotOperators))
                 {
                     // converting true to false adds a character, false to true subtracts
                     m_delta += node.ToBoolean() ? 1 : -1;
                 }
             }
             else
             {
                 // convert - just flip the boolean value
                 node.Value = !node.ToBoolean();
             }
         }
         else
         {
             // just the same typical operation as most other nodes for other types
             TypicalHandler(node);
         }
     }
 }
        /// <summary>
        /// Return true is not an overflow or underflow, for multiplication operations
        /// </summary>
        /// <param name="left">left operand</param>
        /// <param name="right">right operand</param>
        /// <param name="result">result</param>
        /// <returns>true if result not overflow or underflow; false if it is</returns>
        private static bool NoMultiplicativeOverOrUnderFlow(JsConstantWrapper left, JsConstantWrapper right, JsConstantWrapper result)
        {
            // check for overflow
            bool okayToProceed = !result.IsInfinity;

            // if we still might be good, check for possible underflow
            if (okayToProceed)
            {
                // if the result is zero, we might have an underflow. But if one of the operands
                // was zero, then it's okay.
                // Inverse: if neither operand is zero, then a zero result is not okay
                okayToProceed = !result.IsZero || (left.IsZero || right.IsZero);
            }
            return okayToProceed;
        }
        private void EvalFarToTheLeft(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator leftOperator)
        {
            if (leftOperator.OperatorToken == JsToken.Minus)
            {
                if (node.OperatorToken == JsToken.Plus)
                {
                    // minus-plus
                    // the minus will be a numeric operator, but if this constant is a string, it will be a
                    // string concatenation and we can't combine it.
                    if (thisConstant.PrimitiveType != JsPrimitiveType.String && thisConstant.PrimitiveType != JsPrimitiveType.Other)
                    {
                        JsConstantWrapper newLiteral = NumericAddition(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            RotateFromRight(node, leftOperator, newLiteral);
                        }
                    }
                }
                else if (node.OperatorToken == JsToken.Minus)
                {
                    // minus-minus
                    JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        RotateFromRight(node, leftOperator, newLiteral);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Multiply)
            {
                if (leftOperator.OperatorToken == JsToken.Multiply || leftOperator.OperatorToken == JsToken.Divide)
                {
                    JsConstantWrapper newLiteral = Multiply(otherConstant, thisConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, newLiteral))
                    {
                        RotateFromRight(node, leftOperator, newLiteral);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Divide)
            {
                if (leftOperator.OperatorToken == JsToken.Divide)
                {
                    // divide-divide
                    JsConstantWrapper newLiteral = Divide(otherConstant, thisConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, newLiteral)
                        && newLiteral.ToCode().Length <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                    {
                        RotateFromRight(node, leftOperator, newLiteral);
                    }
                }
                else if (leftOperator.OperatorToken == JsToken.Multiply)
                {
                    // mult-divide
                    JsConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    JsConstantWrapper thisOverOther = Divide(thisConstant, otherConstant);

                    int otherOverThisLength = otherOverThis != null ? otherOverThis.ToCode().Length : int.MaxValue;
                    int thisOverOtherLength = thisOverOther != null ? thisOverOther.ToCode().Length : int.MaxValue;

                    if (otherOverThis != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, otherOverThis)
                        && (thisOverOther == null || otherOverThisLength < thisOverOtherLength))
                    {
                        if (otherOverThisLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            RotateFromRight(node, leftOperator, otherOverThis);
                        }
                    }
                    else if (thisOverOther != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, thisOverOther))
                    {
                        if (thisOverOtherLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // swap the operands
                            leftOperator.SwapOperands();

                            // operator is the opposite
                            leftOperator.OperatorToken = JsToken.Divide;
                            RotateFromLeft(node, leftOperator, thisOverOther);
                        }
                    }
                }
            }
        }
 /// <summary>
 /// replace the node with a literal. If the node was wrapped in a grouping operator
 /// before (parentheses around it), then we can get rid of the parentheses too, since
 /// we are replacing the node with a single literal entity.
 /// </summary>
 /// <param name="node">node to replace</param>
 /// <param name="newLiteral">literal to replace the node with</param>
 private static void ReplaceNodeWithLiteral(JsAstNode node, JsConstantWrapper newLiteral)
 {
     var grouping = node.Parent as JsGroupingOperator;
     if (grouping != null)
     {
         // because we are replacing the operator with a literal, the parentheses
         // the grouped this operator are now superfluous. Replace them, too
         grouping.Parent.ReplaceChild(grouping, newLiteral);
     }
     else
     {
         // just replace the node with the literal
         node.Parent.ReplaceChild(node, newLiteral);
     }
 }
        private void EvalFarToTheRight(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator rightOperator)
        {
            if (rightOperator.OperatorToken == JsToken.Minus)
            {
                if (node.OperatorToken == JsToken.Plus)
                {
                    // plus-minus
                    // our constant cannot be a string, though
                    if (!thisConstant.IsStringLiteral)
                    {
                        JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            RotateFromLeft(node, rightOperator, newLiteral);
                        }
                    }
                }
                else if (node.OperatorToken == JsToken.Minus)
                {
                    // minus-minus
                    JsConstantWrapper newLiteral = NumericAddition(thisConstant, otherConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        // but we need to swap the left and right operands first
                        rightOperator.SwapOperands();

                        // then rotate the node up after replacing old with new
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Multiply)
            {
                if (rightOperator.OperatorToken == JsToken.Multiply)
                {
                    // mult-mult
                    JsConstantWrapper newLiteral = Multiply(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral))
                    {
                        RotateFromLeft(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Divide)
                {
                    // mult-divide
                    JsConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    JsConstantWrapper thisOverOther = Divide(thisConstant, otherConstant);

                    int otherOverThisLength = otherOverThis != null ? otherOverThis.ToCode().Length : int.MaxValue;
                    int thisOverOtherLength = thisOverOther != null ? thisOverOther.ToCode().Length : int.MaxValue;

                    if (otherOverThis != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, otherOverThis)
                        && (thisOverOther == null || otherOverThisLength < thisOverOtherLength))
                    {
                        if (otherOverThisLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // swap the operands, but keep the operator
                            RotateFromLeft(node, rightOperator, otherOverThis);
                        }
                    }
                    else if (thisOverOther != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, thisOverOther))
                    {
                        if (thisOverOtherLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // swap the operands and opposite operator
                            rightOperator.SwapOperands();
                            rightOperator.OperatorToken = JsToken.Multiply;
                            RotateFromRight(node, rightOperator, thisOverOther);
                        }
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Divide)
            {
                if (rightOperator.OperatorToken == JsToken.Multiply)
                {
                    // divide-mult
                    JsConstantWrapper newLiteral = Divide(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral)
                        && newLiteral.ToCode().Length <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                    {
                        // swap the operands
                        rightOperator.SwapOperands();

                        // change the operator
                        rightOperator.OperatorToken = JsToken.Divide;
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Divide)
                {
                    // divide-divide
                    JsConstantWrapper newLiteral = Multiply(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral))
                    {
                        // but we need to swap the left and right operands first
                        rightOperator.SwapOperands();

                        // then rotate the node up after replacing old with new
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
            }
        }
        private JsConstantWrapper Divide(JsConstantWrapper left, JsConstantWrapper right)
        {
            JsConstantWrapper newLiteral = null;

            if (left.IsOkayToCombine && right.IsOkayToCombine
                && m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
            {
                try
                {
                    double leftValue = left.ToNumber();
                    double rightValue = right.ToNumber();
                    double result = leftValue / rightValue;

                    if (JsConstantWrapper.NumberIsOkayToCombine(result))
                    {
                        newLiteral = new JsConstantWrapper(result, JsPrimitiveType.Number, null, m_parser);
                    }
                    else
                    {
                        if (!left.IsNumericLiteral && JsConstantWrapper.NumberIsOkayToCombine(leftValue))
                        {
                            left.Parent.ReplaceChild(left, new JsConstantWrapper(leftValue, JsPrimitiveType.Number, left.Context, m_parser));
                        }
                        if (!right.IsNumericLiteral && JsConstantWrapper.NumberIsOkayToCombine(rightValue))
                        {
                            right.Parent.ReplaceChild(right, new JsConstantWrapper(rightValue, JsPrimitiveType.Number, right.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
                }
            }

            return newLiteral;
        }
        private void EvalThisOperator(JsBinaryOperator node, JsConstantWrapper left, JsConstantWrapper right)
        {
            // we can evaluate these operators if we know both operands are literal
            // number, boolean, string or null
            JsConstantWrapper newLiteral = null;
            switch (node.OperatorToken)
            {
                case JsToken.Multiply:
                    newLiteral = Multiply(left, right);
                    break;

                case JsToken.Divide:
                    newLiteral = Divide(left, right);
                    if (newLiteral != null && newLiteral.ToCode().Length > node.ToCode().Length)
                    {
                        // the result is bigger than the expression.
                        // eg: 1/3 is smaller than .333333333333333
                        // never mind.
                        newLiteral = null;
                    }
                    break;

                case JsToken.Modulo:
                    newLiteral = Modulo(left, right);
                    if (newLiteral != null && newLiteral.ToCode().Length > node.ToCode().Length)
                    {
                        // the result is bigger than the expression.
                        // eg: 46.5%6.3 is smaller than 2.4000000000000012
                        // never mind.
                        newLiteral = null;
                    }
                    break;

                case JsToken.Plus:
                    newLiteral = Plus(left, right);
                    break;

                case JsToken.Minus:
                    newLiteral = Minus(left, right);
                    break;

                case JsToken.LeftShift:
                    newLiteral = LeftShift(left, right);
                    break;

                case JsToken.RightShift:
                    newLiteral = RightShift(left, right);
                    break;

                case JsToken.UnsignedRightShift:
                    newLiteral = UnsignedRightShift(left, right);
                    break;

                case JsToken.LessThan:
                    newLiteral = LessThan(left, right);
                    break;

                case JsToken.LessThanEqual:
                    newLiteral = LessThanOrEqual(left, right);
                    break;

                case JsToken.GreaterThan:
                    newLiteral = GreaterThan(left, right);
                    break;

                case JsToken.GreaterThanEqual:
                    newLiteral = GreaterThanOrEqual(left, right);
                    break;

                case JsToken.Equal:
                    newLiteral = Equal(left, right);
                    break;

                case JsToken.NotEqual:
                    newLiteral = NotEqual(left, right);
                    break;

                case JsToken.StrictEqual:
                    newLiteral = StrictEqual(left, right);
                    break;

                case JsToken.StrictNotEqual:
                    newLiteral = StrictNotEqual(left, right);
                    break;

                case JsToken.BitwiseAnd:
                    newLiteral = BitwiseAnd(left, right);
                    break;

                case JsToken.BitwiseOr:
                    newLiteral = BitwiseOr(left, right);
                    break;

                case JsToken.BitwiseXor:
                    newLiteral = BitwiseXor(left, right);
                    break;

                case JsToken.LogicalAnd:
                    newLiteral = LogicalAnd(left, right);
                    break;

                case JsToken.LogicalOr:
                    newLiteral = LogicalOr(left, right);
                    break;

                default:
                    // an operator we don't want to evaluate
                    break;
            }

            // if we can combine them...
            if (newLiteral != null)
            {
                // first we want to check if the new combination is a string literal, and if so, whether
                // it's now the sole parameter of a member-bracket call operator. If so, instead of replacing our
                // binary operation with the new constant, we'll replace the entire call with a member-dot
                // expression
                if (!ReplaceMemberBracketWithDot(node, newLiteral))
                {
                    ReplaceNodeWithLiteral(node, newLiteral);
                }
            }
        }
 public void Visit(JsConstantWrapper node)
 {
     // it's a constant, so we don't care
 }
Example #40
0
 public void Visit(JsConstantWrapper node)
 {
     // we're good
 }