public void Visit(JsConstantWrapper node)
 {
     if (node != null)
     {
         node.Index = NextOrderIndex;
     }
 }
 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)
            {
                // 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 #4
0
        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
             });
         }
     }
 }
 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 #8
0
 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 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;
        }
        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 void Visit(JsConstantWrapper node)
 {
     // we're good
 }
        /// <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);
            }
        }
Example #13
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);
                }
            }
        }
Example #14
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)
                };
        }
 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);
         }
     }
 }
        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 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;
        }
 /// <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);
     }
 }
 /// <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;
 }
        /// <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 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 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;
        }
Example #23
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
                };
        }
        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;
        }
Example #25
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
                };
        }
        public void Visit(JsConstantWrapper node)
        {
            if (node != null)
            {
                var symbol = StartSymbol(node);

                var isNoIn = m_noIn;
                m_noIn = false;

                switch (node.PrimitiveType)
                {
                    case JsPrimitiveType.Boolean:
                        Output(node.ToBoolean() ? "true" : "false");
                        break;

                    case JsPrimitiveType.Null:
                        Output("null");
                        break;

                    case JsPrimitiveType.Number:
                        if (node.Context == null || !node.Context.HasCode
                            || (!node.MayHaveIssues && m_settings.IsModificationAllowed(JsTreeModifications.MinifyNumericLiterals)))
                        {
                            // apply minification to the literal to get it as small as possible
                            Output(NormalizeNumber(node.ToNumber(), node.Context));
                        }
                        else
                        {
                            // context is not null but we don't want to minify numeric literals.
                            // just use the original literal from the context.
                            Output(node.Context.Code);
                        }
                        break;

                    case JsPrimitiveType.Other:
                        Output(node.Value.ToString());
                        break;

                    case JsPrimitiveType.String:
                        if (node.Context == null || !node.Context.HasCode)
                        {
                            // escape the string value because we don't have a raw context value
                            // to show anyways
                            Output(InlineSafeString(EscapeString(node.Value.ToString())));
                        }
                        else if (!m_settings.IsModificationAllowed(JsTreeModifications.MinifyStringLiterals))
                        {
                            // we don't want to modify the strings at all!
                            Output(node.Context.Code);
                        }
                        else if (node.MayHaveIssues
                            || (m_settings.AllowEmbeddedAspNetBlocks && node.StringContainsAspNetReplacement))
                        {
                            // we'd rather show the raw string, but make sure it's safe for inlining
                            Output(InlineSafeString(node.Context.Code));
                        }
                        else
                        {
                            // we'd rather show the escaped string
                            Output(InlineSafeString(EscapeString(node.Value.ToString())));
                        }

                        break;
                }

                MarkSegment(node, null, node.Context);
                SetContextOutputPosition(node.Context);
                m_startOfStatement = false;
                m_noIn = isNoIn;

                EndSymbol(symbol);
            }
        }
Example #27
0
 public void Visit(JsConstantWrapper node)
 {
     // we're good
 }
 public void Visit(JsConstantWrapper node)
 {
     // not applicable; terminate
 }
        /// <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;
        }
Example #30
0
        //---------------------------------------------------------------------------------------
        // ParseDoStatement
        //
        //  DoStatement:
        //    'do' Statement 'while' '(' Expression ')'
        //---------------------------------------------------------------------------------------
        private JsDoWhile ParseDoStatement()
        {
            var doCtx = m_currentToken.Clone();
            JsContext whileContext = null;
            JsContext terminatorContext = null;
            JsAstNode body = null;
            JsAstNode condition = null;
            m_blockType.Add(BlockType.Loop);
            try
            {
                GetNextToken();
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_DoWhileBodyNoSkipTokenSet);
                // 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)
                {
                    // make up a block for the do while
                    if (exc._partiallyComputedNode != null)
                        body = exc._partiallyComputedNode;
                    else
                        body = new JsBlock(CurrentPositionContext(), this);
                    if (IndexOfToken(NoSkipTokenSet.s_DoWhileBodyNoSkipTokenSet, exc) == -1)
                    {
                        // we have to pass the exception to someone else, make as much as you can from the 'do while'
                        exc._partiallyComputedNode = new JsDoWhile(doCtx.UpdateWith(CurrentPositionContext()), this)
                            {
                                Body = JsAstNode.ForceToBlock(body),
                                Condition = new JsConstantWrapper(false, JsPrimitiveType.Boolean, CurrentPositionContext(), this)
                            };
                        throw;
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_DoWhileBodyNoSkipTokenSet);
                }

                if (JsToken.While != m_currentToken.Token)
                {
                    ReportError(JsError.NoWhile);
                }

                whileContext = m_currentToken.Clone();
                doCtx.UpdateWith(whileContext);
                GetNextToken();

                if (JsToken.LeftParenthesis != m_currentToken.Token)
                {
                    ReportError(JsError.NoLeftParenthesis);
                }

                GetNextToken();
                // catch here so the body of the do_while is not thrown away
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                try
                {
                    condition = ParseExpression();
                    if (JsToken.RightParenthesis != m_currentToken.Token)
                    {
                        ReportError(JsError.NoRightParenthesis);
                        doCtx.UpdateWith(condition.Context);
                    }
                    else
                    {
                        doCtx.UpdateWith(m_currentToken);
                    }

                    GetNextToken();
                }
                catch (RecoveryTokenException exc)
                {
                    // make up a condition
                    if (exc._partiallyComputedNode != null)
                        condition = exc._partiallyComputedNode;
                    else
                        condition = new JsConstantWrapper(false, JsPrimitiveType.Boolean, CurrentPositionContext(), this);

                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                    {
                        exc._partiallyComputedNode = new JsDoWhile(doCtx, this)
                            {
                                Body = JsAstNode.ForceToBlock(body),
                                WhileContext = whileContext,
                                Condition = condition
                            };
                        throw;
                    }
                    else
                    {
                        if (JsToken.RightParenthesis == m_currentToken.Token)
                            GetNextToken();
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                }
                if (JsToken.Semicolon == m_currentToken.Token)
                {
                    // JScript 5 allowed statements like
                    //   do{print(++x)}while(x<10) print(0)
                    // even though that does not strictly follow the automatic semicolon insertion
                    // rules for the required semi after the while().  For backwards compatibility
                    // we should continue to support this.
                    terminatorContext = m_currentToken.Clone();
                    GetNextToken();
                }
            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

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

            return new JsDoWhile(doCtx, this)
                {
                    Body = JsAstNode.ForceToBlock(body),
                    WhileContext = whileContext,
                    Condition = condition,
                    TerminatingContext = terminatorContext
                };
        }
 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);
         }
     }
 }
 public void Visit(JsConstantWrapper node)
 {
     // it's a constant, so we don't care
 }
 public void Visit(JsConstantWrapper node)
 {
     if (node != null)
     {
         node.Index = NextOrderIndex;
     }
 }
Example #34
0
        //---------------------------------------------------------------------------------------
        // ParseIfStatement
        //
        //  IfStatement :
        //    'if' '(' Expression ')' Statement ElseStatement
        //
        //  ElseStatement :
        //    <empty> |
        //    'else' Statement
        //---------------------------------------------------------------------------------------
        private JsIfNode ParseIfStatement()
        {
            JsContext ifCtx = m_currentToken.Clone();
            JsAstNode condition = null;
            JsAstNode trueBranch = null;
            JsAstNode falseBranch = null;
            JsContext elseCtx = null;

            m_blockType.Add(BlockType.Block);
            try
            {
                // parse condition
                GetNextToken();
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                try
                {
                    if (JsToken.LeftParenthesis != m_currentToken.Token)
                        ReportError(JsError.NoLeftParenthesis);
                    GetNextToken();
                    condition = ParseExpression();

                    // parse statements
                    if (JsToken.RightParenthesis != m_currentToken.Token)
                    {
                        ifCtx.UpdateWith(condition.Context);
                        ReportError(JsError.NoRightParenthesis);
                    }
                    else
                        ifCtx.UpdateWith(m_currentToken);

                    GetNextToken();
                }
                catch (RecoveryTokenException exc)
                {
                    // make up an if condition
                    if (exc._partiallyComputedNode != null)
                        condition = exc._partiallyComputedNode;
                    else
                        condition = new JsConstantWrapper(true, JsPrimitiveType.Boolean, CurrentPositionContext(), this);

                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                    {
                        exc._partiallyComputedNode = null; // really not much to pass up
                        // the if condition was so bogus we do not have a chance to make an If node, give up
                        throw;
                    }
                    else
                    {
                        if (exc._token == JsToken.RightParenthesis)
                            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);
                }

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

                try
                {
                    // parse a Statement, not a SourceElement
                    // and ignore any important comments that spring up right here.
                    trueBranch = ParseStatement(false, true);
                }
                catch (RecoveryTokenException exc)
                {
                    // make up a block for the if part
                    if (exc._partiallyComputedNode != null)
                        trueBranch = exc._partiallyComputedNode;
                    else
                        trueBranch = new JsBlock(CurrentPositionContext(), this);
                    if (IndexOfToken(NoSkipTokenSet.s_IfBodyNoSkipTokenSet, exc) == -1)
                    {
                        // we have to pass the exception to someone else, make as much as you can from the if
                        exc._partiallyComputedNode = new JsIfNode(ifCtx, this)
                            {
                                Condition = condition,
                                TrueBlock = JsAstNode.ForceToBlock(trueBranch)
                            };
                        throw;
                    }
                }
                finally
                {
                    if (trueBranch != null)
                    {
                        ifCtx.UpdateWith(trueBranch.Context);
                    }

                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_IfBodyNoSkipTokenSet);
                }

                // parse else, if any
                if (JsToken.Else == m_currentToken.Token)
                {
                    elseCtx = m_currentToken.Clone();
                    GetNextToken();
                    if (JsToken.Semicolon == m_currentToken.Token)
                    {
                        m_currentToken.HandleError(JsError.SuspectSemicolon);
                    }
                    else if (JsToken.LeftCurly != m_currentToken.Token
                      && JsToken.If != m_currentToken.Token)
                    {
                        // if the statements aren't withing curly-braces (or start another if-statement), throw a possible error
                        ReportError(JsError.StatementBlockExpected, CurrentPositionContext(), true);
                    }

                    try
                    {
                        // parse a Statement, not a SourceElement
                        // and ignore any important comments that spring up right here.
                        falseBranch = ParseStatement(false, true);
                    }
                    catch (RecoveryTokenException exc)
                    {
                        // make up a block for the else part
                        if (exc._partiallyComputedNode != null)
                            falseBranch = exc._partiallyComputedNode;
                        else
                            falseBranch = new JsBlock(CurrentPositionContext(), this);
                        exc._partiallyComputedNode = new JsIfNode(ifCtx, this)
                            {
                                Condition = condition,
                                TrueBlock = JsAstNode.ForceToBlock(trueBranch),
                                ElseContext = elseCtx,
                                FalseBlock = JsAstNode.ForceToBlock(falseBranch)
                            };
                        throw;
                    }
                    finally
                    {
                        if (falseBranch != null)
                        {
                            ifCtx.UpdateWith(falseBranch.Context);
                        }
                    }
                }
            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

            return new JsIfNode(ifCtx, this)
                {
                    Condition = condition,
                    TrueBlock = JsAstNode.ForceToBlock(trueBranch),
                    ElseContext = elseCtx,
                    FalseBlock = JsAstNode.ForceToBlock(falseBranch)
                };
        }
Example #35
0
 public void Visit(JsConstantWrapper node)
 {
     // not applicable; terminate
 }
Example #36
0
        private JsAstNode ParseLeftHandSideExpression(bool isMinus)
        {
            JsAstNode ast = null;
            bool skipToken = true;
            List<JsContext> newContexts = null;

            TryItAgain:

            // new expression
            while (JsToken.New == m_currentToken.Token)
            {
                if (null == newContexts)
                    newContexts = new List<JsContext>(4);
                newContexts.Add(m_currentToken.Clone());
                GetNextToken();
            }
            JsToken token = m_currentToken.Token;
            switch (token)
            {
                // primary expression
                case JsToken.Identifier:
                    ast = new JsLookup(m_currentToken.Clone(), this)
                        {
                            Name = m_scanner.Identifier
                        };
                    break;

                case JsToken.ConditionalCommentStart:
                    // skip past the start to the next token
                    GetNextToken();
                    if (m_currentToken.Token == JsToken.ConditionalCompilationVariable)
                    {
                        // we have /*@id
                        ast = new JsConstantWrapperPP(m_currentToken.Clone(), this)
                            {
                                VarName = m_currentToken.Code,
                                ForceComments = true
                            };

                        GetNextToken();

                        if (m_currentToken.Token == JsToken.ConditionalCommentEnd)
                        {
                            // skip past the closing comment
                            GetNextToken();
                        }
                        else
                        {
                            // we ONLY support /*@id@*/ in expressions right now. If there's not
                            // a closing comment after the ID, then we don't support it.
                            // throw an error, skip to the end of the comment, then ignore it and start
                            // looking for the next token.
                            CCTooComplicated(null);
                            goto TryItAgain;
                        }
                    }
                    else if (m_currentToken.Token == JsToken.ConditionalCommentEnd)
                    {
                        // empty conditional comment! Ignore.
                        GetNextToken();
                        goto TryItAgain;
                    }
                    else
                    {
                        // we DON'T have "/*@IDENT". We only support "/*@IDENT @*/", so since this isn't
                        // and id, throw the error, skip to the end of the comment, and ignore it
                        // by looping back and looking for the NEXT token.
                        m_currentToken.HandleError(JsError.ConditionalCompilationTooComplex);

                        // skip to end of conditional comment
                        while (m_currentToken.Token != JsToken.EndOfFile && m_currentToken.Token != JsToken.ConditionalCommentEnd)
                        {
                            GetNextToken();
                        }
                        GetNextToken();
                        goto TryItAgain;
                    }
                    break;

                case JsToken.This:
                    ast = new JsThisLiteral(m_currentToken.Clone(), this);
                    break;

                case JsToken.StringLiteral:
                    ast = new JsConstantWrapper(m_scanner.StringLiteralValue, JsPrimitiveType.String, m_currentToken.Clone(), this)
                        {
                            MayHaveIssues = m_scanner.LiteralHasIssues
                        };
                    break;

                case JsToken.IntegerLiteral:
                case JsToken.NumericLiteral:
                    {
                        JsContext numericContext = m_currentToken.Clone();
                        double doubleValue;
                        if (ConvertNumericLiteralToDouble(m_currentToken.Code, (token == JsToken.IntegerLiteral), out doubleValue))
                        {
                            // conversion worked fine
                            // check for some boundary conditions
                            var mayHaveIssues = m_scanner.LiteralHasIssues;
                            if (doubleValue == double.MaxValue)
                            {
                                ReportError(JsError.NumericMaximum, numericContext, true);
                            }
                            else if (isMinus && -doubleValue == double.MinValue)
                            {
                                ReportError(JsError.NumericMinimum, numericContext, true);
                            }

                            // create the constant wrapper from the value
                            ast = new JsConstantWrapper(doubleValue, JsPrimitiveType.Number, numericContext, this)
                                {
                                    MayHaveIssues = mayHaveIssues
                                };
                        }
                        else
                        {
                            // if we went overflow or are not a number, then we will use the "Other"
                            // primitive type so we don't try doing any numeric calcs with it.
                            if (double.IsInfinity(doubleValue))
                            {
                                // overflow
                                // and if we ARE an overflow, report it
                                ReportError(JsError.NumericOverflow, numericContext, true);
                            }

                            // regardless, we're going to create a special constant wrapper
                            // that simply echos the input as-is
                            ast = new JsConstantWrapper(m_currentToken.Code, JsPrimitiveType.Other, numericContext, this)
                            {
                                MayHaveIssues = true
                            };
                        }
                        break;
                    }

                case JsToken.True:
                    ast = new JsConstantWrapper(true, JsPrimitiveType.Boolean, m_currentToken.Clone(), this);
                    break;

                case JsToken.False:
                    ast = new JsConstantWrapper(false, JsPrimitiveType.Boolean, m_currentToken.Clone(), this);
                    break;

                case JsToken.Null:
                    ast = new JsConstantWrapper(null, JsPrimitiveType.Null, m_currentToken.Clone(), this);
                    break;

                case JsToken.ConditionalCompilationVariable:
                    ast = new JsConstantWrapperPP(m_currentToken.Clone(), this)
                        {
                            VarName = m_currentToken.Code,
                            ForceComments = false
                        };
                    break;

                case JsToken.DivideAssign:
                // normally this token is not allowed on the left-hand side of an expression.
                // BUT, this might be the start of a regular expression that begins with an equals sign!
                // we need to test to see if we can parse a regular expression, and if not, THEN
                // we can fail the parse.

                case JsToken.Divide:
                    // could it be a regexp?
                    ast = ScanRegularExpression();
                    if (ast != null)
                    {
                        // yup -- we're done here
                        break;
                    }

                    // nope -- go to the default branch
                    goto default;

                // expression
                case JsToken.LeftParenthesis:
                    {
                        var groupingOp = new JsGroupingOperator(m_currentToken.Clone(), this);
                        ast = groupingOp;
                        GetNextToken();
                        m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenExpressionNoSkipToken);
                        try
                        {
                            // parse an expression
                            groupingOp.Operand = ParseExpression();
                            if (JsToken.RightParenthesis != m_currentToken.Token)
                            {
                                ReportError(JsError.NoRightParenthesis);
                            }
                            else
                            {
                                // add the closing paren to the expression context
                                ast.Context.UpdateWith(m_currentToken);
                            }
                        }
                        catch (RecoveryTokenException exc)
                        {
                            if (IndexOfToken(NoSkipTokenSet.s_ParenExpressionNoSkipToken, exc) == -1)
                                throw;
                            else
                                groupingOp.Operand = exc._partiallyComputedNode;
                        }
                        finally
                        {
                            m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenExpressionNoSkipToken);
                        }
                    }
                    break;

                // array initializer
                case JsToken.LeftBracket:
                    JsContext listCtx = m_currentToken.Clone();
                    GetNextToken();
                    JsAstNodeList list = new JsAstNodeList(CurrentPositionContext(), this);
                    var hasTrailingCommas = false;
                    while (JsToken.RightBracket != m_currentToken.Token)
                    {
                        if (JsToken.Comma != m_currentToken.Token)
                        {
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet);
                            try
                            {
                                var expression = ParseExpression(true);
                                list.Append(expression);
                                if (JsToken.Comma != m_currentToken.Token)
                                {
                                    if (JsToken.RightBracket != m_currentToken.Token)
                                    {
                                        ReportError(JsError.NoRightBracket);
                                    }

                                    break;
                                }
                                else
                                {
                                    // we have a comma -- skip it after adding it as a terminator
                                    // on the previous expression
                                    var commaContext = m_currentToken.Clone();
                                    expression.IfNotNull(e => e.TerminatingContext = commaContext);
                                    GetNextToken();

                                    // if the next token is the closing brackets, then we need to
                                    // add a missing value to the array because we end in a comma and
                                    // we need to keep it for cross-platform compat.
                                    // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL.
                                    if (m_currentToken.Token == JsToken.RightBracket)
                                    {
                                        hasTrailingCommas = true;
                                        list.Append(new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this));

                                        // throw a cross-browser warning about trailing commas
                                        commaContext.HandleError(JsError.ArrayLiteralTrailingComma);
                                        break;
                                    }
                                }
                            }
                            catch (RecoveryTokenException exc)
                            {
                                if (exc._partiallyComputedNode != null)
                                    list.Append(exc._partiallyComputedNode);
                                if (IndexOfToken(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet, exc) == -1)
                                {
                                    listCtx.UpdateWith(CurrentPositionContext());
                                    exc._partiallyComputedNode = new JsArrayLiteral(listCtx, this)
                                        {
                                            Elements = list,
                                            MayHaveIssues = true
                                        };
                                    throw;
                                }
                                else
                                {
                                    if (JsToken.RightBracket == m_currentToken.Token)
                                        break;
                                }
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet);
                            }
                        }
                        else
                        {
                            // comma -- missing array item in the list
                            var commaContext = m_currentToken.Clone();
                            list.Append(new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this)
                                {
                                    TerminatingContext = commaContext
                                });

                            // skip over the comma
                            GetNextToken();

                            // if the next token is the closing brace, then we end with a comma -- and we need to
                            // add ANOTHER missing value to make sure this last comma doesn't get left off.
                            // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL.
                            if (m_currentToken.Token == JsToken.RightBracket)
                            {
                                hasTrailingCommas = true;
                                list.Append(new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this));

                                // throw a cross-browser warning about trailing commas
                                commaContext.HandleError(JsError.ArrayLiteralTrailingComma);
                                break;
                            }
                        }
                    }

                    listCtx.UpdateWith(m_currentToken);
                    ast = new JsArrayLiteral(listCtx, this)
                        {
                            Elements = list,
                            MayHaveIssues = hasTrailingCommas
                        };
                    break;

                // object initializer
                case JsToken.LeftCurly:
                    JsContext objCtx = m_currentToken.Clone();
                    GetNextToken();

                    var propertyList = new JsAstNodeList(CurrentPositionContext(), this);

                    if (JsToken.RightCurly != m_currentToken.Token)
                    {
                        for (; ; )
                        {
                            JsObjectLiteralField field = null;
                            JsAstNode value = null;
                            bool getterSetter = false;
                            string ident;

                            switch (m_currentToken.Token)
                            {
                                case JsToken.Identifier:
                                    field = new JsObjectLiteralField(m_scanner.Identifier, JsPrimitiveType.String, m_currentToken.Clone(), this);
                                    break;

                                case JsToken.StringLiteral:
                                    field = new JsObjectLiteralField(m_scanner.StringLiteralValue, JsPrimitiveType.String, m_currentToken.Clone(), this)
                                        {
                                            MayHaveIssues = m_scanner.LiteralHasIssues
                                        };
                                    break;

                                case JsToken.IntegerLiteral:
                                case JsToken.NumericLiteral:
                                    {
                                        double doubleValue;
                                        if (ConvertNumericLiteralToDouble(m_currentToken.Code, (m_currentToken.Token == JsToken.IntegerLiteral), out doubleValue))
                                        {
                                            // conversion worked fine
                                            field = new JsObjectLiteralField(
                                              doubleValue,
                                              JsPrimitiveType.Number,
                                              m_currentToken.Clone(),
                                              this
                                              );
                                        }
                                        else
                                        {
                                            // something went wrong and we're not sure the string representation in the source is
                                            // going to convert to a numeric value well
                                            if (double.IsInfinity(doubleValue))
                                            {
                                                ReportError(JsError.NumericOverflow, m_currentToken.Clone(), true);
                                            }

                                            // use the source as the field name, not the numeric value
                                            field = new JsObjectLiteralField(
                                                m_currentToken.Code,
                                                JsPrimitiveType.Other,
                                                m_currentToken.Clone(),
                                                this);
                                        }
                                        break;
                                    }

                                case JsToken.Get:
                                case JsToken.Set:
                                    if (PeekToken() == JsToken.Colon)
                                    {
                                        // the field is either "get" or "set" and isn't the special Mozilla getter/setter
                                        field = new JsObjectLiteralField(m_currentToken.Code, JsPrimitiveType.String, m_currentToken.Clone(), this);
                                    }
                                    else
                                    {
                                        // ecma-script get/set property construct
                                        getterSetter = true;
                                        bool isGet = (m_currentToken.Token == JsToken.Get);
                                        value = ParseFunction(
                                          (JsToken.Get == m_currentToken.Token ? JsFunctionType.Getter : JsFunctionType.Setter),
                                          m_currentToken.Clone()
                                          );
                                        JsFunctionObject funcExpr = value as JsFunctionObject;
                                        if (funcExpr != null)
                                        {
                                            // getter/setter is just the literal name with a get/set flag
                                            field = new JsGetterSetter(
                                              funcExpr.Name,
                                              isGet,
                                              funcExpr.IdContext.Clone(),
                                              this
                                              );
                                        }
                                        else
                                        {
                                            ReportError(JsError.FunctionExpressionExpected);
                                        }
                                    }
                                    break;

                                default:
                                    // NOT: identifier token, string, number, or getter/setter.
                                    // see if it's a token that COULD be an identifierName.
                                    ident = m_scanner.Identifier;
                                    if (JsScanner.IsValidIdentifier(ident))
                                    {
                                        // BY THE SPEC, if it's a valid identifierName -- which includes reserved words -- then it's
                                        // okay for object literal syntax. However, reserved words here won't work in all browsers,
                                        // so if it is a reserved word, let's throw a low-sev cross-browser warning on the code.
                                        if (JsKeyword.CanBeIdentifier(m_currentToken.Token) == null)
                                        {
                                            ReportError(JsError.ObjectLiteralKeyword, m_currentToken.Clone(), true);
                                        }

                                        field = new JsObjectLiteralField(ident, JsPrimitiveType.String, m_currentToken.Clone(), this);
                                    }
                                    else
                                    {
                                        // throw an error but use it anyway, since that's what the developer has going on
                                        ReportError(JsError.NoMemberIdentifier, m_currentToken.Clone(), true);
                                        field = new JsObjectLiteralField(m_currentToken.Code, JsPrimitiveType.String, m_currentToken.Clone(), this);
                                    }
                                    break;
                            }

                            if (field != null)
                            {
                                if (!getterSetter)
                                {
                                    GetNextToken();
                                }

                                m_noSkipTokenSet.Add(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet);
                                try
                                {
                                    if (!getterSetter)
                                    {
                                        // get the value
                                        if (JsToken.Colon != m_currentToken.Token)
                                        {
                                            ReportError(JsError.NoColon, true);
                                            value = ParseExpression(true);
                                        }
                                        else
                                        {
                                            field.ColonContext = m_currentToken.Clone();
                                            GetNextToken();
                                            value = ParseExpression(true);
                                        }
                                    }

                                    // put the pair into the list of fields
                                    var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context));
                                    var property = new JsObjectLiteralProperty(propCtx, this)
                                        {
                                            Name = field,
                                            Value = value
                                        };

                                    propertyList.Append(property);

                                    if (JsToken.RightCurly == m_currentToken.Token)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (JsToken.Comma == m_currentToken.Token)
                                        {
                                            // skip the comma after adding it to the property as a terminating context
                                            property.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone());
                                            GetNextToken();

                                            // if the next token is the right-curly brace, then we ended
                                            // the list with a comma, which is perfectly fine
                                            if (m_currentToken.Token == JsToken.RightCurly)
                                            {
                                                break;
                                            }
                                        }
                                        else
                                        {
                                            if (m_foundEndOfLine)
                                            {
                                                ReportError(JsError.NoRightCurly);
                                            }
                                            else
                                                ReportError(JsError.NoComma, true);
                                            SkipTokensAndThrow();
                                        }
                                    }
                                }
                                catch (RecoveryTokenException exc)
                                {
                                    if (exc._partiallyComputedNode != null)
                                    {
                                        // the problem was in ParseExpression trying to determine value
                                        value = exc._partiallyComputedNode;

                                        var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context));
                                        var property = new JsObjectLiteralProperty(propCtx, this)
                                        {
                                            Name = field,
                                            Value = value
                                        };

                                        propertyList.Append(property);
                                    }

                                    if (IndexOfToken(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet, exc) == -1)
                                    {
                                        exc._partiallyComputedNode = new JsObjectLiteral(objCtx, this)
                                            {
                                                Properties = propertyList
                                            };
                                        throw;
                                    }
                                    else
                                    {
                                        if (JsToken.Comma == m_currentToken.Token)
                                            GetNextToken();
                                        if (JsToken.RightCurly == m_currentToken.Token)
                                            break;
                                    }
                                }
                                finally
                                {
                                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet);
                                }
                            }
                        }
                    }
                    objCtx.UpdateWith(m_currentToken);
                    ast = new JsObjectLiteral(objCtx, this)
                        {
                            Properties = propertyList
                        };
                    break;

                // function expression
                case JsToken.Function:
                    ast = ParseFunction(JsFunctionType.Expression, m_currentToken.Clone());
                    skipToken = false;
                    break;

                case JsToken.AspNetBlock:
                    ast = new JsAspNetBlockNode(m_currentToken.Clone(), this)
                        {
                            AspNetBlockText = m_currentToken.Code
                        };
                    break;

                default:
                    string identifier = JsKeyword.CanBeIdentifier(m_currentToken.Token);
                    if (null != identifier)
                    {
                        ast = new JsLookup(m_currentToken.Clone(), this)
                            {
                                Name = identifier
                            };
                    }
                    else
                    {
                        ReportError(JsError.ExpressionExpected);
                        SkipTokensAndThrow();
                    }
                    break;
            }

            // can be a CallExpression, that is, followed by '.' or '(' or '['
            if (skipToken)
                GetNextToken();

            return MemberExpression(ast, newContexts);
        }
Example #37
0
        //---------------------------------------------------------------------------------------
        // ParseExpressionList
        //
        //  Given a starting this.currentToken '(' or '[', parse a list of expression separated by
        //  ',' until matching ')' or ']'
        //---------------------------------------------------------------------------------------
        private JsAstNodeList ParseExpressionList(JsToken terminator)
        {
            JsContext listCtx = m_currentToken.Clone();
            GetNextToken();
            JsAstNodeList list = new JsAstNodeList(listCtx, this);
            if (terminator != m_currentToken.Token)
            {
                for (; ; )
                {
                    m_noSkipTokenSet.Add(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet);
                    try
                    {
                        JsAstNode item;
                        if (JsToken.Comma == m_currentToken.Token)
                        {
                            item = new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this);
                            list.Append(item);
                        }
                        else if (terminator == m_currentToken.Token)
                        {
                            break;
                        }
                        else
                        {
                            item = ParseExpression(true);
                            list.Append(item);
                        }

                        if (terminator == m_currentToken.Token)
                        {
                            break;
                        }
                        else
                        {
                            if (JsToken.Comma == m_currentToken.Token)
                            {
                                item.IfNotNull(n => n.TerminatingContext = m_currentToken.Clone());
                            }
                            else
                            {
                                if (terminator == JsToken.RightParenthesis)
                                {
                                    //  in ASP+ it's easy to write a semicolon at the end of an expression
                                    //  not realizing it is going to go inside a function call
                                    //  (ie. Response.Write()), so make a special check here
                                    if (JsToken.Semicolon == m_currentToken.Token)
                                    {
                                        if (JsToken.RightParenthesis == PeekToken())
                                        {
                                            ReportError(JsError.UnexpectedSemicolon, true);
                                            GetNextToken();
                                            break;
                                        }
                                    }

                                    ReportError(JsError.NoRightParenthesisOrComma);
                                }
                                else
                                {
                                    ReportError(JsError.NoRightBracketOrComma);
                                }

                                SkipTokensAndThrow();
                            }
                        }
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (exc._partiallyComputedNode != null)
                            list.Append(exc._partiallyComputedNode);
                        if (IndexOfToken(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet, exc) == -1)
                        {
                            exc._partiallyComputedNode = list;
                            throw;
                        }
                    }
                    finally
                    {
                        m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet);
                    }
                    GetNextToken();
                }
            }
            listCtx.UpdateWith(m_currentToken);
            return list;
        }
        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;
        }
        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 #40
0
        private JsAstNode ParseForStatement()
        {
            m_blockType.Add(BlockType.Loop);
            JsAstNode forNode = null;
            try
            {
                JsContext forCtx = m_currentToken.Clone();
                GetNextToken();
                if (JsToken.LeftParenthesis != m_currentToken.Token)
                {
                    ReportError(JsError.NoLeftParenthesis);
                }

                GetNextToken();
                bool isForIn = false, recoveryInForIn = false;
                JsAstNode lhs = null, initializer = null, condOrColl = null, increment = null;
                JsContext operatorContext = null;
                JsContext separator1Context = null;
                JsContext separator2Context = null;

                try
                {
                    if (JsToken.Var == m_currentToken.Token
                        || JsToken.Let == m_currentToken.Token
                        || JsToken.Const == m_currentToken.Token)
                    {
                        isForIn = true;
                        JsDeclaration declaration;
                        if (m_currentToken.Token == JsToken.Var)
                        {
                            declaration = new JsVar(m_currentToken.Clone(), this);
                        }
                        else
                        {
                            declaration = new JsLexicalDeclaration(m_currentToken.Clone(), this)
                                {
                                    StatementToken = m_currentToken.Token
                                };
                        }

                        declaration.Append(ParseIdentifierInitializer(JsToken.In));

                        // a list of variable initializers is allowed only in a for(;;)
                        while (JsToken.Comma == m_currentToken.Token)
                        {
                            isForIn = false;
                            declaration.Append(ParseIdentifierInitializer(JsToken.In));
                            //initializer = new Comma(initializer.context.CombineWith(var.context), initializer, var);
                        }

                        initializer = declaration;

                        // if it could still be a for..in, now it's time to get the 'in'
                        // TODO: for ES6 might be 'of'
                        if (isForIn)
                        {
                            if (JsToken.In == m_currentToken.Token
                                || (m_currentToken.Token == JsToken.Identifier && string.CompareOrdinal(m_currentToken.Code, "of") == 0))
                            {
                                operatorContext = m_currentToken.Clone();
                                GetNextToken();
                                condOrColl = ParseExpression();
                            }
                            else
                            {
                                isForIn = false;
                            }
                        }
                    }
                    else
                    {
                        if (JsToken.Semicolon != m_currentToken.Token)
                        {
                            bool isLHS;
                            initializer = ParseUnaryExpression(out isLHS, false);
                            if (isLHS && (JsToken.In == m_currentToken.Token
                                || (m_currentToken.Token == JsToken.Identifier && string.CompareOrdinal(m_currentToken.Code, "of") == 0)))
                            {
                                isForIn = true;
                                operatorContext = m_currentToken.Clone();

                                lhs = initializer;
                                initializer = null;
                                GetNextToken();
                                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                                try
                                {
                                    condOrColl = ParseExpression();
                                }
                                catch (RecoveryTokenException exc)
                                {
                                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                                    {
                                        exc._partiallyComputedNode = null;
                                        throw;
                                    }
                                    else
                                    {
                                        if (exc._partiallyComputedNode == null)
                                            condOrColl = new JsConstantWrapper(true, JsPrimitiveType.Boolean, CurrentPositionContext(), this); // what could we put here?
                                        else
                                            condOrColl = exc._partiallyComputedNode;
                                    }
                                    if (exc._token == JsToken.RightParenthesis)
                                    {
                                        GetNextToken();
                                        recoveryInForIn = true;
                                    }
                                }
                                finally
                                {
                                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                                }
                            }
                            else
                            {
                                initializer = ParseExpression(initializer, false, isLHS, JsToken.In);
                            }
                        }
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    // error is too early abort for
                    exc._partiallyComputedNode = null;
                    throw;
                }

                // at this point we know whether or not is a for..in
                if (isForIn)
                {
                    if (!recoveryInForIn)
                    {
                        if (JsToken.RightParenthesis != m_currentToken.Token)
                            ReportError(JsError.NoRightParenthesis);
                        forCtx.UpdateWith(m_currentToken);
                        GetNextToken();
                    }

                    JsAstNode body = null;
                    // 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 = new JsBlock(CurrentPositionContext(), this);
                        else
                            body = exc._partiallyComputedNode;
                        exc._partiallyComputedNode = new JsForIn(forCtx, this)
                            {
                                Variable = (lhs != null ? lhs : initializer),
                                OperatorContext = operatorContext,
                                Collection = condOrColl,
                                Body = JsAstNode.ForceToBlock(body),
                            };
                        throw;
                    }

                    // for (a in b)
                    //      lhs = a, initializer = null
                    // for (var a in b)
                    //      lhs = null, initializer = var a
                    forNode = new JsForIn(forCtx, this)
                        {
                            Variable = (lhs != null ? lhs : initializer),
                            OperatorContext = operatorContext,
                            Collection = condOrColl,
                            Body = JsAstNode.ForceToBlock(body),
                        };
                }
                else
                {
                    m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                    try
                    {
                        if (JsToken.Semicolon == m_currentToken.Token)
                        {
                            separator1Context = m_currentToken.Clone();
                        }
                        else
                        {
                            ReportError(JsError.NoSemicolon);
                            if (JsToken.Colon == m_currentToken.Token)
                            {
                                m_noSkipTokenSet.Add(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet);
                                try
                                {
                                    SkipTokensAndThrow();
                                }
                                catch (RecoveryTokenException)
                                {
                                    if (JsToken.Semicolon == m_currentToken.Token)
                                    {
                                        m_useCurrentForNext = false;
                                    }
                                    else
                                    {
                                        throw;
                                    }
                                }
                                finally
                                {
                                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet);
                                }
                            }
                        }

                        GetNextToken();
                        if (JsToken.Semicolon != m_currentToken.Token)
                        {
                            condOrColl = ParseExpression();
                            if (JsToken.Semicolon != m_currentToken.Token)
                            {
                                ReportError(JsError.NoSemicolon);
                            }
                        }

                        separator2Context = m_currentToken.Clone();
                        GetNextToken();

                        if (JsToken.RightParenthesis != m_currentToken.Token)
                        {
                            increment = ParseExpression();
                        }

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

                        forCtx.UpdateWith(m_currentToken);
                        GetNextToken();
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                        {
                            exc._partiallyComputedNode = null;
                            throw;
                        }
                        else
                        {
                            // discard any partial info, just genrate empty condition and increment and keep going
                            exc._partiallyComputedNode = null;
                            if (condOrColl == null)
                                condOrColl = new JsConstantWrapper(true, JsPrimitiveType.Boolean, CurrentPositionContext(), this);
                        }
                        if (exc._token == JsToken.RightParenthesis)
                        {
                            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 = condOrColl as JsBinaryOperator;
                    if (binOp != null && binOp.OperatorToken == JsToken.Assign)
                    {
                        condOrColl.Context.HandleError(JsError.SuspectAssignment);
                    }

                    JsAstNode body = null;
                    // 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 = new JsBlock(CurrentPositionContext(), this);
                        else
                            body = exc._partiallyComputedNode;
                        exc._partiallyComputedNode = new JsForNode(forCtx, this)
                            {
                                Initializer = initializer,
                                Separator1Context = separator1Context,
                                Condition = condOrColl,
                                Separator2Context = separator2Context,
                                Incrementer = increment,
                                Body = JsAstNode.ForceToBlock(body)
                            };
                        throw;
                    }
                    forNode = new JsForNode(forCtx, this)
                        {
                            Initializer = initializer,
                            Separator1Context = separator1Context,
                            Condition = condOrColl,
                            Separator2Context = separator2Context,
                            Incrementer = increment,
                            Body = JsAstNode.ForceToBlock(body)
                        };
                }
            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

            return forNode;
        }