public JsDirectivePrologue(string value, JsContext context, JsParser parser)
     : base(value, JsPrimitiveType.String, context, parser)
 {
     // this is a "use strict" directive if the source context is EXACTLY "use strict"
     // don't consider the quotes so it can be " or ' delimiters
     UseStrict = string.CompareOrdinal(Context.Code, 1, "use strict", 0, 10) == 0;
 }
        /// <summary>
        /// Creates an instance of the JSParser class that can be used to parse the given source code.
        /// </summary>
        /// <param name="source">Source code to parse.</param>
        public JsParser(string source)
        {
            m_severity = 5;
            m_blockType = new List<BlockType>(16);
            m_labelTable = new Dictionary<string, LabelInfo>();
            m_noSkipTokenSet = new NoSkipTokenSet();
            m_importantComments = new List<JsContext>();

            m_document = new JsDocumentContext(this, source);
            m_scanner = new JsScanner(new JsContext(m_document));
            m_currentToken = new JsContext(m_document);

            // if the scanner encounters a special "globals" comment, it'll fire this event
            // at which point we will define a field with that name in the global scope.
            m_scanner.GlobalDefine += (sender, ea) =>
                {
                    var globalScope = GlobalScope;
                    if (globalScope[ea.Name] == null)
                    {
                        var field = globalScope.CreateField(ea.Name, null, FieldAttributes.SpecialName);
                        globalScope.AddField(field);
                    }
                };

            // this event is fired whenever a ///#SOURCE comment is encountered
            m_scanner.NewModule += (sender, ea) =>
                {
                    m_newModule = true;

                    // we also want to assume that we found a newline character after
                    // the comment
                    m_foundEndOfLine = true;
                };
        }
        public JsConstantWrapper(Object value, JsPrimitiveType primitiveType, JsContext context, JsParser parser)
            : base(context, parser)
        {
            PrimitiveType = primitiveType;

            // force numerics to be of type double
            Value = (primitiveType == JsPrimitiveType.Number ? System.Convert.ToDouble(value, CultureInfo.InvariantCulture) : value);
        }
 internal JsException(JsError errorNumber, JsContext context)
 {
     Value = null;
     m_context = (context == null ? null : context.Clone());
     FileContext = (context == null ? null : context.Document.FileContext);
     ErrorCode = errorNumber;
     CanRecover = true;
 }
        public void OutputNumber(double numericValue, JsContext originalContext)
        {
            // numerics are doubles in JavaScript, so force it now as a shortcut
            if (double.IsNaN(numericValue) || double.IsInfinity(numericValue))
            {
                // weird number -- just return the original source code as-is.
                if (originalContext != null && !string.IsNullOrEmpty(originalContext.Code)
                    && !originalContext.Document.IsGenerated)
                {
                    m_writer.Write(originalContext.Code);
                    return;
                }

                // Hmmm... don't have an original source.
                // Must be generated. Just generate the proper JS literal.
                //
                // DANGER! If we just output NaN and Infinity and -Infinity blindly, that assumes
                // that there aren't any local variables in this scope chain with that
                // name, and we're pulling the GLOBAL properties. Might want to use properties
                // on the Number object -- which, of course, assumes that Number doesn't
                // resolve to a local variable...
                string objectName = double.IsNaN(numericValue) ? "NaN" : "Infinity";

                // we're good to go -- just return the name because it will resolve to the
                // global properties (make a special case for negative infinity)
                m_writer.Write(double.IsNegativeInfinity(numericValue) ? "-Infinity" : objectName);
            }
            else if (numericValue == 0)
            {
                // special case zero because we don't need to go through all those
                // gyrations to get a "0" -- and because negative zero is different
                // than a positive zero
                m_writer.Write(1 / numericValue < 0 ? "-0" : "0");
            }
            else
            {
                // normal string representations
                m_writer.Write(GetSmallestRep(numericValue.ToString("R", CultureInfo.InvariantCulture)));
            }
        }
 public JsTryNode(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #7
0
 public JsConditionalCompilationComment(JsContext context, JsParser parser)
     : base(context, parser)
 {
     Statements = new JsBlock(null, parser);
 }
 public JsConditionalCompilationComment(JsContext context, JsParser parser)
     : base(context, parser)
 {
     Statements = new JsBlock(null, parser);
 }
 public JsWithScope(JsActivationObject parent, JsContext context, JsSettings settings)
     : base(parent, context, settings)
 {
     IsInWithScope = true;
 }
 public JsObjectLiteral(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
        //---------------------------------------------------------------------------------------
        // ReportError
        //
        //  Generate a parser error.
        //  The function is told whether or not next call to GetToken() should return the same
        //  token or not
        //---------------------------------------------------------------------------------------
        private void ReportError(JsError errorId, JsContext context, bool skipToken)
        {
            Debug.Assert(context != null);
            int previousSeverity = m_severity;
            m_severity = JsException.GetSeverity(errorId);
            // EOF error is special and it's the last error we can possibly get
            if (JsToken.EndOfFile == context.Token)
                EOFError(errorId); // EOF context is special
            else
            {
                // report the error if not in error condition and the
                // error for this token is not worse than the one for the
                // previous token
                if (m_goodTokensProcessed > 0 || m_severity < previousSeverity)
                    context.HandleError(errorId);

                // reset proper info
                if (skipToken)
                    m_goodTokensProcessed = -1;
                else
                {
                    m_useCurrentForNext = true;
                    m_goodTokensProcessed = 0;
                }
            }
        }
Exemple #12
0
 public JsArrayLiteral(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #13
0
 public JsBreak(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #14
0
 public JsRegExpLiteral(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsCustomNode(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #16
0
 public JsVariableDeclaration(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #17
0
 public JsWithNode(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsLookup(JsContext context, JsParser parser)
     : base(context, parser)
 {
     RefType = JsReferenceType.Variable;
 }
Exemple #19
0
 public JsForIn(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsReturnNode(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsRegExpLiteral(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
        private void CCTooComplicated(JsContext context)
        {
            // we ONLY support /*@id@*/ or /*@cc_on@id@*/ or /*@!@*/ or /*@cc_on!@*/ in expressions right now.
            // throw an error, skip to the end of the comment, then ignore it and start
            // looking for the next token.
            (context ?? m_currentToken).HandleError(JsError.ConditionalCompilationTooComplex);

            // skip to end of conditional comment
            while (m_currentToken.Token != JsToken.EndOfFile && m_currentToken.Token != JsToken.ConditionalCommentEnd)
            {
                GetNextToken();
            }
            GetNextToken();
        }
 internal UndefinedReferenceException(JsLookup lookup, JsContext context)
 {
     m_lookup = lookup;
     m_name = lookup.Name;
     m_type = lookup.RefType;
     m_context = context;
 }
        //---------------------------------------------------------------------------------------
        // CreateExpressionNode
        //
        //  Create the proper AST object according to operator
        //---------------------------------------------------------------------------------------
        private JsAstNode CreateExpressionNode(JsContext op, JsAstNode operand1, JsAstNode operand2)
        {
            JsContext context = operand1.Context.CombineWith(operand2.Context);
            switch (op.Token)
            {
                case JsToken.Assign:
                case JsToken.BitwiseAnd:
                case JsToken.BitwiseAndAssign:
                case JsToken.BitwiseOr:
                case JsToken.BitwiseOrAssign:
                case JsToken.BitwiseXor:
                case JsToken.BitwiseXorAssign:
                case JsToken.Divide:
                case JsToken.DivideAssign:
                case JsToken.Equal:
                case JsToken.GreaterThan:
                case JsToken.GreaterThanEqual:
                case JsToken.In:
                case JsToken.InstanceOf:
                case JsToken.LeftShift:
                case JsToken.LeftShiftAssign:
                case JsToken.LessThan:
                case JsToken.LessThanEqual:
                case JsToken.LogicalAnd:
                case JsToken.LogicalOr:
                case JsToken.Minus:
                case JsToken.MinusAssign:
                case JsToken.Modulo:
                case JsToken.ModuloAssign:
                case JsToken.Multiply:
                case JsToken.MultiplyAssign:
                case JsToken.NotEqual:
                case JsToken.Plus:
                case JsToken.PlusAssign:
                case JsToken.RightShift:
                case JsToken.RightShiftAssign:
                case JsToken.StrictEqual:
                case JsToken.StrictNotEqual:
                case JsToken.UnsignedRightShift:
                case JsToken.UnsignedRightShiftAssign:
                    // regular binary operator
                    return new JsBinaryOperator(context, this)
                        {
                            Operand1 = operand1,
                            Operand2 = operand2,
                            OperatorContext = op,
                            OperatorToken = op.Token
                        };

                case JsToken.Comma:
                    // use the special comma-operator class derived from binary operator.
                    // it has special logic to combine adjacent comma operators into a single
                    // node with an ast node list rather than nested binary operators
                    return JsCommaOperator.CombineWithComma(context, this, operand1, operand2);

                default:
                    // shouldn't get here!
                    Debug.Assert(false);
                    return null;
            }
        }
 public JsLexicalDeclaration(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
        //---------------------------------------------------------------------------------------
        // GetNextToken
        //
        //  Return the next token or peeked token if this.errorToken is not null.
        //  Usually this.errorToken is set by AddError even though any code can look ahead
        //  by assigning this.errorToken.
        //  At this point the context is not saved so if position information is needed
        //  they have to be saved explicitely
        //---------------------------------------------------------------------------------------
        private void GetNextToken()
        {
            if (m_useCurrentForNext)
            {
                // we just want to keep using the current token.
                // but don't get into an infinite loop -- after a while,
                // give up and grab the next token from the scanner anyway
                m_useCurrentForNext = false;
                if (m_breakRecursion++ > 10)
                {
                    m_currentToken = ScanNextToken();
                }
            }
            else
            {
                m_goodTokensProcessed++;
                m_breakRecursion = 0;

                // the scanner reuses the same context object for performance,
                // so if we ever mean to hold onto it later, we need to clone it.
                m_currentToken = ScanNextToken();
            }
        }
Exemple #27
0
 public JsBinaryOperator(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsSwitch(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsLookup(JsContext context, JsParser parser)
     : base(context, parser)
 {
     RefType = JsReferenceType.Variable;
 }
 public JsCustomNode(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #31
0
 public JsDoWhile(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #32
0
 public JsVar(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
        public void MarkSegment(JsAstNode node, int startLine, int startColumn, string name, JsContext context)
        {
            if (node == null || string.IsNullOrEmpty(name))
            {
                return;
            }

            // see if this is within a function object node,
            // AND if this segment has the same name as the function name
            // AND this context isn't the same as the entire function context.
            // this should only be true for the function NAME segment.
            var functionObject = node as JsFunctionObject;
            if (functionObject != null
                && string.CompareOrdinal(name, functionObject.Name) == 0
                && context != functionObject.Context)
            {
                // adjust the offsets
                startLine += m_lineOffset;
                startColumn += m_columnOffset;

                // it does -- so this is the segment that corresponds to the function object's name, which
                // for this format we want to output a separate segment for. It used to be its own Lookup
                // node child of the function object, so we need to create a fake node here, start a new
                // symbol from it, end the symbol, then write it.
                var fakeLookup = new JsLookup(context, functionObject.Parser) { Name = name };
                var nameSymbol = JavaScriptSymbol.StartNew(fakeLookup, startLine, startColumn, GetSourceFileIndex(functionObject.Context.Document.FileContext));

                // the name will never end on a different line -- it's a single unbreakable token. The length is just
                // the length of the name, so add that number to the column start. And the parent context is the function
                // name (again)
                nameSymbol.End(startLine, startColumn + name.Length, name);
                nameSymbol.WriteTo(m_writer);
            }
        }
 public JsVariableDeclaration(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
Exemple #35
0
 public JsBlock(JsContext context, JsParser parser)
     : base(context, parser)
 {
     m_list = new List<JsAstNode>();
 }
 public JsReturnNode(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsGroupingOperator(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsConditionalCompilationElseIf(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 public JsGetterSetter(String identifier, bool isGetter, JsContext context, JsParser parser)
     : base(identifier, JsPrimitiveType.String, context, parser)
 {
     IsGetter = isGetter;
 }
 public JsFunctionObject(JsContext functionContext, JsParser parser)
     : base(functionContext, parser)
 {
 }
        private JsFunctionObject ParseFunction(JsFunctionType functionType, JsContext fncCtx)
        {
            JsLookup name = null;
            JsAstNodeList formalParameters = null;
            JsBlock body = null;
            bool inExpression = (functionType == JsFunctionType.Expression);
            JsContext paramsContext = null;

            GetNextToken();

            // get the function name or make an anonymous function if in expression "position"
            if (JsToken.Identifier == m_currentToken.Token)
            {
                name = new JsLookup(m_currentToken.Clone(), this)
                    {
                        Name = m_scanner.Identifier
                    };
                GetNextToken();
            }
            else
            {
                string identifier = JsKeyword.CanBeIdentifier(m_currentToken.Token);
                if (null != identifier)
                {
                    name = new JsLookup(m_currentToken.Clone(), this)
                        {
                            Name = identifier
                        };
                    GetNextToken();
                }
                else
                {
                    if (!inExpression)
                    {
                        // if this isn't a function expression, then we need to throw an error because
                        // function DECLARATIONS always need a valid identifier name
                        ReportError(JsError.NoIdentifier, m_currentToken.Clone(), true);

                        // BUT if the current token is a left paren, we don't want to use it as the name.
                        // (fix for issue #14152)
                        if (m_currentToken.Token != JsToken.LeftParenthesis
                            && m_currentToken.Token != JsToken.LeftCurly)
                        {
                            identifier = m_currentToken.Code;
                            name = new JsLookup(CurrentPositionContext(), this)
                                {
                                    Name = identifier
                                };
                            GetNextToken();
                        }
                    }
                }
            }

            // make a new state and save the old one
            List<BlockType> blockType = m_blockType;
            m_blockType = new List<BlockType>(16);
            Dictionary<string, LabelInfo> labelTable = m_labelTable;
            m_labelTable = new Dictionary<string, LabelInfo>();

            try
            {
                // get the formal parameters
                if (JsToken.LeftParenthesis != m_currentToken.Token)
                {
                    // we expect a left paren at this point for standard cross-browser support.
                    // BUT -- some versions of IE allow an object property expression to be a function name, like window.onclick.
                    // we still want to throw the error, because it syntax errors on most browsers, but we still want to
                    // be able to parse it and return the intended results.
                    // Skip to the open paren and use whatever is in-between as the function name. Doesn't matter that it's
                    // an invalid identifier; it won't be accessible as a valid field anyway.
                    bool expandedIndentifier = false;
                    while (m_currentToken.Token != JsToken.LeftParenthesis
                        && m_currentToken.Token != JsToken.LeftCurly
                        && m_currentToken.Token != JsToken.Semicolon
                        && m_currentToken.Token != JsToken.EndOfFile)
                    {
                        name.Context.UpdateWith(m_currentToken);
                        GetNextToken();
                        expandedIndentifier = true;
                    }

                    // if we actually expanded the identifier context, then we want to report that
                    // the function name needs to be an identifier. Otherwise we didn't expand the
                    // name, so just report that we expected an open paren at this point.
                    if (expandedIndentifier)
                    {
                        name.Name = name.Context.Code;
                        name.Context.HandleError(JsError.FunctionNameMustBeIdentifier, false);
                    }
                    else
                    {
                        ReportError(JsError.NoLeftParenthesis, true);
                    }
                }

                if (m_currentToken.Token == JsToken.LeftParenthesis)
                {
                    // create the parameter list
                    formalParameters = new JsAstNodeList(m_currentToken.Clone(), this);
                    paramsContext = m_currentToken.Clone();

                    // skip the open paren
                    GetNextToken();

                    // create the list of arguments and update the context
                    while (JsToken.RightParenthesis != m_currentToken.Token)
                    {
                        String id = null;
                        m_noSkipTokenSet.Add(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet);
                        try
                        {
                            JsParameterDeclaration paramDecl = null;
                            if (JsToken.Identifier != m_currentToken.Token && (id = JsKeyword.CanBeIdentifier(m_currentToken.Token)) == null)
                            {
                                if (JsToken.LeftCurly == m_currentToken.Token)
                                {
                                    ReportError(JsError.NoRightParenthesis);
                                    break;
                                }
                                else if (JsToken.Comma == m_currentToken.Token)
                                {
                                    // We're missing an argument (or previous argument was malformed and
                                    // we skipped to the comma.)  Keep trying to parse the argument list --
                                    // we will skip the comma below.
                                    ReportError(JsError.SyntaxError, true);
                                }
                                else
                                {
                                    ReportError(JsError.SyntaxError, true);
                                    SkipTokensAndThrow();
                                }
                            }
                            else
                            {
                                if (null == id)
                                {
                                    id = m_scanner.Identifier;
                                }

                                paramDecl = new JsParameterDeclaration(m_currentToken.Clone(), this)
                                    {
                                        Name = id,
                                        Position = formalParameters.Count
                                    };
                                formalParameters.Append(paramDecl);
                                GetNextToken();
                            }

                            // got an arg, it should be either a ',' or ')'
                            if (JsToken.RightParenthesis == m_currentToken.Token)
                            {
                                break;
                            }
                            else if (JsToken.Comma == m_currentToken.Token)
                            {
                                // append the comma context as the terminator for the parameter
                                paramDecl.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone());
                            }
                            else
                            {
                                // deal with error in some "intelligent" way
                                if (JsToken.LeftCurly == m_currentToken.Token)
                                {
                                    ReportError(JsError.NoRightParenthesis);
                                    break;
                                }
                                else
                                {
                                    if (JsToken.Identifier == m_currentToken.Token)
                                    {
                                        // it's possible that the guy was writing the type in C/C++ style (i.e. int x)
                                        ReportError(JsError.NoCommaOrTypeDefinitionError);
                                    }
                                    else
                                        ReportError(JsError.NoComma);
                                }
                            }

                            GetNextToken();
                        }
                        catch (RecoveryTokenException exc)
                        {
                            if (IndexOfToken(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet, exc) == -1)
                                throw;
                        }
                        finally
                        {
                            m_noSkipTokenSet.Remove(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet);
                        }
                    }

                    fncCtx.UpdateWith(m_currentToken);
                    GetNextToken();
                }

                // read the function body of non-abstract functions.
                if (JsToken.LeftCurly != m_currentToken.Token)
                    ReportError(JsError.NoLeftCurly, true);

                m_blockType.Add(BlockType.Block);
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                try
                {
                    // parse the block locally to get the exact end of function
                    body = new JsBlock(m_currentToken.Clone(), this);
                    body.BraceOnNewLine = m_foundEndOfLine;
                    GetNextToken();

                    var possibleDirectivePrologue = true;
                    while (JsToken.RightCurly != m_currentToken.Token)
                    {
                        try
                        {
                            // function body's are SourceElements (Statements + FunctionDeclarations)
                            var statement = ParseStatement(true);
                            if (possibleDirectivePrologue)
                            {
                                var constantWrapper = statement as JsConstantWrapper;
                                if (constantWrapper != null && constantWrapper.PrimitiveType == JsPrimitiveType.String)
                                {
                                    // if it's already a directive prologues, we're good to go
                                    if (!(constantWrapper is JsDirectivePrologue))
                                    {
                                        // make the statement a directive prologue instead of a constant wrapper
                                        statement = new JsDirectivePrologue(constantWrapper.Value.ToString(), constantWrapper.Context, constantWrapper.Parser)
                                            {
                                                MayHaveIssues = constantWrapper.MayHaveIssues
                                            };
                                    }
                                }
                                else if (!m_newModule)
                                {
                                    // no longer considering constant wrappers
                                    possibleDirectivePrologue = false;
                                }
                            }
                            else if (m_newModule)
                            {
                                // we scanned into a new module -- we might find directive prologues again
                                possibleDirectivePrologue = true;
                            }

                            // add it to the body
                            body.Append(statement);
                        }
                        catch (RecoveryTokenException exc)
                        {
                            if (exc._partiallyComputedNode != null)
                            {
                                body.Append(exc._partiallyComputedNode);
                            }
                            if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1)
                                throw;
                        }
                    }

                    // make sure any important comments before the closing brace are kept
                    AppendImportantComments(body);

                    body.Context.UpdateWith(m_currentToken);
                    fncCtx.UpdateWith(m_currentToken);
                }
                catch (EndOfStreamException)
                {
                    // if we get an EOF here, we never had a chance to find the closing curly-brace
                    fncCtx.HandleError(JsError.UnclosedFunction, true);
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1)
                    {
                        exc._partiallyComputedNode = new JsFunctionObject(fncCtx, this)
                            {
                                FunctionType = (inExpression ? JsFunctionType.Expression : JsFunctionType.Declaration),
                                IdContext = name.IfNotNull(n => n.Context),
                                Name = name.IfNotNull(n => n.Name),
                                ParameterDeclarations = formalParameters,
                                ParametersContext = paramsContext,
                                Body = body
                            };
                        throw;
                    }
                }
                finally
                {
                    m_blockType.RemoveAt(m_blockType.Count - 1);
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                }

                GetNextToken();
            }
            finally
            {
                // restore state
                m_blockType = blockType;
                m_labelTable = labelTable;
            }

            return new JsFunctionObject(fncCtx, this)
                {
                    FunctionType = functionType,
                    IdContext = name.IfNotNull(n => n.Context),
                    Name = name.IfNotNull(n => n.Name),
                    ParameterDeclarations = formalParameters,
                    ParametersContext = paramsContext,
                    Body = body
                };
        }
 public JsConstStatement(JsContext context, JsParser parser)
     : base(context, parser)
 {
 }
 internal JsCatchScope(JsActivationObject parent, JsContext catchContext, JsSettings settings, JsParameterDeclaration catchParameter)
     : base(parent, catchContext, settings)
 {
     CatchParameter = catchParameter;
 }