Example #1
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultSourceSpanBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            foreach (var declaration in this.Declarations)
            {
                if (declaration.InitExpression != null)
                {
                    // Create a new assignment expression and generate code for it.
                    var initializationStatement = new ExpressionStatement(
                        new AssignmentExpression(this.Scope, declaration.VariableName, declaration.InitExpression));
                    initializationStatement.SourceSpan = declaration.SourceSpan;
                    initializationStatement.GenerateCode(generator, optimizationInfo);
                }
            }

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #2
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals();
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            foreach (var declaration in this.Declarations)
            {
                if (declaration.InitExpression != null)
                {
                    // Create a new assignment expression and generate code for it.
                    if (optimizationInfo.DebugDocument != null)
                        generator.MarkSequencePoint(optimizationInfo.DebugDocument, declaration.DebugInfo);
                    var initializationStatement = new ExpressionStatement(
                        new AssignmentExpression(this.Scope, declaration.VariableName, declaration.InitExpression));
                    initializationStatement.GenerateCode(generator, optimizationInfo);
                }
            }

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Example #3
0
        /// <summary>
        /// Parses a for statement or a for-in statement.
        /// </summary>
        /// <returns> A for statement or a for-in statement. </returns>
        private Statement ParseFor()
        {
            // Consume the for keyword.
            this.Expect(KeywordToken.For);

            // Read the left parenthesis.
            this.Expect(PunctuatorToken.LeftParenthesis);

            // The initialization statement.
            Statement initializationStatement = null;

            // The for-in expression needs a variable to assign to.  Is null for a regular for statement.
            IReferenceExpression forInReference = null;

            // Keep track of the start of the statement so that source debugging works correctly.
            var start = this.PositionAfterWhitespace;

            if (this.nextToken == KeywordToken.Var)
            {
                // Read past the var token.
                this.Expect(KeywordToken.Var);

                // There can be multiple initializers (but not for for-in statements).
                var varStatement = new VarStatement(this.labelsForCurrentStatement, this.currentScope);
                initializationStatement = varStatement;

                // Only a simple variable name is allowed for for-in statements.
                bool cannotBeForIn = false;

                while (true)
                {
                    var declaration = new VariableDeclaration();

                    // The next token must be a variable name.
                    declaration.VariableName = this.ExpectIdentifier();
                    ValidateVariableName(declaration.VariableName);

                    // Add the variable to the current function's list of local variables.
                    this.currentScope.DeclareVariable(declaration.VariableName,
                        this.context == CodeContext.Function ? null : new LiteralExpression(Undefined.Value),
                        writable: true, deletable: this.context == CodeContext.Eval);

                    // The next token is either an equals sign (=), a semi-colon, a comma, or the "in" keyword.
                    if (this.nextToken == PunctuatorToken.Assignment)
                    {
                        // Read past the equals token (=).
                        this.Expect(PunctuatorToken.Assignment);

                        // Read the setter expression.
                        declaration.InitExpression = ParseExpression(PunctuatorToken.Semicolon, PunctuatorToken.Comma);

                        // Record the portion of the source document that will be highlighted when debugging.
                        declaration.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace);

                        // This is a regular for statement.
                        cannotBeForIn = true;
                    }

                    // Add the declaration to the initialization statement.
                    varStatement.Declarations.Add(declaration);

                    if (this.nextToken == PunctuatorToken.Semicolon)
                    {
                        // This is a regular for statement.
                        break;
                    }
                    else if (this.nextToken == KeywordToken.In && cannotBeForIn == false)
                    {
                        // This is a for-in statement.
                        forInReference = new NameExpression(this.currentScope, declaration.VariableName);
                        break;
                    }
                    else if (this.nextToken != PunctuatorToken.Comma)
                        throw new JavaScriptException(this.engine, "SyntaxError", string.Format("Unexpected token {0}", Token.ToText(this.nextToken)), this.LineNumber, this.SourcePath);

                    // Read past the comma token.
                    this.Expect(PunctuatorToken.Comma);

                    // Keep track of the start of the statement so that source debugging works correctly.
                    start = this.PositionAfterWhitespace;

                    // Multiple initializers are not allowed in for-in statements.
                    cannotBeForIn = true;
                }

                // Record the portion of the source document that will be highlighted when debugging.
                varStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace);
            }
            else
            {
                // Not a var initializer - can be a simple variable name then "in" or any expression ending with a semi-colon.
                // The expression can be empty.
                if (this.nextToken != PunctuatorToken.Semicolon)
                {
                    // Parse an expression.
                    var initializationExpression = ParseExpression(PunctuatorToken.Semicolon, KeywordToken.In);

                    // Record debug info for the expression.
                    initializationStatement = new ExpressionStatement(initializationExpression);
                    initializationStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace);

                    if (this.nextToken == KeywordToken.In)
                    {
                        // This is a for-in statement.
                        if ((initializationExpression is IReferenceExpression) == false)
                            throw new JavaScriptException(this.engine, "ReferenceError", "Invalid left-hand side in for-in", this.LineNumber, this.SourcePath);
                        forInReference = (IReferenceExpression)initializationExpression;
                    }
                }
            }

            if (forInReference != null)
            {
                // for (x in y)
                // for (var x in y)
                var result = new ForInStatement(this.labelsForCurrentStatement);
                result.Variable = forInReference;
                result.VariableSourceSpan = initializationStatement.SourceSpan;
                
                // Consume the "in".
                this.Expect(KeywordToken.In);

                // Parse the right-hand-side expression.
                start = this.PositionAfterWhitespace;
                result.TargetObject = ParseExpression(PunctuatorToken.RightParenthesis);
                result.TargetObjectSourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace);

                // Read the right parenthesis.
                this.Expect(PunctuatorToken.RightParenthesis);

                // Read the statements that will be executed in the loop body.
                result.Body = ParseStatement();

                return result;
            }
            else
            {
                var result = new ForStatement(this.labelsForCurrentStatement);

                // Set the initialization statement.
                if (initializationStatement != null)
                    result.InitStatement = initializationStatement;

                // Read the semicolon.
                this.Expect(PunctuatorToken.Semicolon);

                // Parse the optional condition expression.
                // Note: if the condition is omitted then it is considered to always be true.
                if (this.nextToken != PunctuatorToken.Semicolon)
                {
                    start = this.PositionAfterWhitespace;
                    result.ConditionStatement = new ExpressionStatement(ParseExpression(PunctuatorToken.Semicolon));
                    result.ConditionStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace);
                }

                // Read the semicolon.
                // Note: automatic semicolon insertion never inserts a semicolon in the header of a
                // for statement.
                this.Expect(PunctuatorToken.Semicolon);

                // Parse the optional increment expression.
                if (this.nextToken != PunctuatorToken.RightParenthesis)
                {
                    start = this.PositionAfterWhitespace;
                    result.IncrementStatement = new ExpressionStatement(ParseExpression(PunctuatorToken.RightParenthesis));
                    result.IncrementStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace);
                }

                // Read the right parenthesis.
                this.Expect(PunctuatorToken.RightParenthesis);

                // Read the statements that will be executed in the loop body.
                result.Body = ParseStatement();

                return result;
            }
        }
Example #4
0
        //     PARSE METHODS
        //_________________________________________________________________________________________

        /// <summary>
        /// Parses javascript source code.
        /// </summary>
        /// <returns> An expression that can be executed to run the program represented by the
        /// source code. </returns>
        public Statement Parse()
        {
            // Read the directive prologue.
            var result = new BlockStatement(new string[0]);
            while (true)
            {
                // Check if we should stop parsing.
                if (this.nextToken == this.endToken)
                    break;

                // A directive must start with a string literal token.  Record it now so that the
                // escape sequence and line continuation information is not lost.
                var directiveToken = this.nextToken as StringLiteralToken;
                if (directiveToken == null)
                    break;

                // Directives cannot have escape sequences or line continuations.
                if (directiveToken.EscapeSequenceCount != 0 || directiveToken.LineContinuationCount != 0)
                    break;

                // If the statement starts with a string literal, it must be an expression.
                var beforeInitialToken = this.PositionAfterWhitespace;
                var expression = ParseExpression(PunctuatorToken.Semicolon);

                // The statement must be added to the AST so that eval("'test'") works.
                var initialStatement = new ExpressionStatement(this.labelsForCurrentStatement, expression);
                initialStatement.SourceSpan = new SourceCodeSpan(beforeInitialToken, this.PositionBeforeWhitespace);
                result.Statements.Add(initialStatement);

                // In order for the expression to be part of the directive prologue, it must
                // consist solely of a string literal.
                if ((expression is LiteralExpression) == false)
                    break;

                // Strict mode directive.
                if (directiveToken.Value == "use strict")
                    this.StrictMode = true;

                // Read the end of the statement.  This must happen last so that the lexer has a
                // chance to act on the strict mode flag.
                ExpectEndOfStatement();
            }

            // Call the directive prologue callback.
            if (this.DirectivePrologueProcessedCallback != null)
                this.DirectivePrologueProcessedCallback(this);

            // Read zero or more regular statements.
            while (true)
            {
                // Check if we should stop parsing.
                if (this.nextToken == this.endToken)
                    break;

                // Parse a single statement.
                result.Statements.Add(ParseStatement());
            }

            return result;
        }
Example #5
0
        /// <summary>
        /// Parses a statement consisting of an expression or starting with a label.  These two
        /// cases are disambiguated here.
        /// </summary>
        /// <returns> A statement. </returns>
        private Statement ParseLabelOrExpressionStatement()
        {
            // Keep track of the start of the statement so that source debugging works correctly.
            var start = this.PositionAfterWhitespace;

            // Parse the statement as though it was an expression - but stop if there is an unexpected colon.
            var expression = ParseExpression(PunctuatorToken.Semicolon, PunctuatorToken.Colon);

            if (this.nextToken == PunctuatorToken.Colon && expression is NameExpression)
            {
                // The expression is actually a label.

                // Extract the label name.
                var labelName = ((NameExpression)expression).Name;
                this.labelsForCurrentStatement.Add(labelName);

                // Read past the colon.
                this.Expect(PunctuatorToken.Colon);

                // Read the rest of the statement.
                return ParseStatementNoNewContext();
            }
            else
            {

                // Consume the end of the statement.
                this.ExpectEndOfStatement();

                // Create a new expression statement.
                var result = new ExpressionStatement(this.labelsForCurrentStatement, expression);

                // Record the portion of the source document that will be highlighted when debugging.
                result.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace);

                return result;
            }
        }