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