/* * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "JSParser.h" using namespace JSC; #include "JSGlobalData.h" #include "NodeInfo.h" #include "ASTBuilder.h" #include #include using namespace std; namespace JSC { #define fail() do { m_error = true; return 0; } while (0) #define failIfFalse(cond) do { if (!(cond)) fail(); } while (0) #define failIfTrue(cond) do { if ((cond)) fail(); } while (0) #define consumeOrFail(tokenType) do { if (!consume(tokenType)) fail(); } while (0) #define matchOrFail(tokenType) do { if (!match(tokenType)) fail(); } while (0) #define failIfStackOverflow() do { failIfFalse(canRecurse()); } while (0) // Macros to make the more common TreeBuilder types a little less verbose #define TreeStatement typename TreeBuilder::Statement #define TreeExpression typename TreeBuilder::Expression #define TreeFormalParameterList typename TreeBuilder::FormalParameterList #define TreeSourceElements typename TreeBuilder::SourceElements #define TreeClause typename TreeBuilder::Clause #define TreeClauseList typename TreeBuilder::ClauseList #define TreeConstDeclList typename TreeBuilder::ConstDeclList #define TreeArguments typename TreeBuilder::Arguments #define TreeArgumentsList typename TreeBuilder::ArgumentsList #define TreeFunctionBody typename TreeBuilder::FunctionBody #define TreeProperty typename TreeBuilder::Property #define TreePropertyList typename TreeBuilder::PropertyList COMPILE_ASSERT(LastUntaggedToken < 64, LessThan64UntaggedTokens); // This matches v8 static const ptrdiff_t kMaxParserStackUsage = 128 * sizeof(void*) * 1024; class JSParser { public: JSParser(Lexer*, JSGlobalData*, SourceProvider*); bool parseProgram(); private: struct AllowInOverride { AllowInOverride(JSParser* parser) : m_parser(parser) , m_oldAllowsIn(parser->m_allowsIn) { parser->m_allowsIn = true; } ~AllowInOverride() { m_parser->m_allowsIn = m_oldAllowsIn; } JSParser* m_parser; bool m_oldAllowsIn; }; const JSToken& token() { return m_token; } void next(Lexer::LexType lexType = Lexer::IdentifyReservedWords) { m_lastLine = token().m_info.line; m_lastTokenEnd = token().m_info.endOffset; m_lexer->setLastLineNumber(m_lastLine); m_token.m_type = m_lexer->lex(&m_token.m_data, &m_token.m_info, lexType); m_tokenCount++; } bool consume(JSTokenType expected) { bool result = m_token.m_type == expected; failIfFalse(result); next(); return result; } bool match(JSTokenType expected) { return m_token.m_type == expected; } int tokenStart() { return token().m_info.startOffset; } int tokenLine() { return token().m_info.line; } int tokenEnd() { return token().m_info.endOffset; } template TreeSourceElements parseSourceElements(TreeBuilder&); template TreeStatement parseStatement(TreeBuilder&); template TreeStatement parseFunctionDeclaration(TreeBuilder&); template TreeStatement parseVarDeclaration(TreeBuilder&); template TreeStatement parseConstDeclaration(TreeBuilder&); template TreeStatement parseDoWhileStatement(TreeBuilder&); template TreeStatement parseWhileStatement(TreeBuilder&); template TreeStatement parseForStatement(TreeBuilder&); template TreeStatement parseBreakStatement(TreeBuilder&); template TreeStatement parseContinueStatement(TreeBuilder&); template TreeStatement parseReturnStatement(TreeBuilder&); template TreeStatement parseThrowStatement(TreeBuilder&); template TreeStatement parseWithStatement(TreeBuilder&); template TreeStatement parseSwitchStatement(TreeBuilder&); template TreeClauseList parseSwitchClauses(TreeBuilder&); template TreeClause parseSwitchDefaultClause(TreeBuilder&); template TreeStatement parseTryStatement(TreeBuilder&); template TreeStatement parseDebuggerStatement(TreeBuilder&); template TreeStatement parseExpressionStatement(TreeBuilder&); template TreeStatement parseExpressionOrLabelStatement(TreeBuilder&); template TreeStatement parseIfStatement(TreeBuilder&); template ALWAYS_INLINE TreeStatement parseBlockStatement(TreeBuilder&); template TreeExpression parseExpression(TreeBuilder&); template TreeExpression parseAssignmentExpression(TreeBuilder&); template ALWAYS_INLINE TreeExpression parseConditionalExpression(TreeBuilder&); template ALWAYS_INLINE TreeExpression parseBinaryExpression(TreeBuilder&); template ALWAYS_INLINE TreeExpression parseUnaryExpression(TreeBuilder&); template TreeExpression parseMemberExpression(TreeBuilder&); template ALWAYS_INLINE TreeExpression parsePrimaryExpression(TreeBuilder&); template ALWAYS_INLINE TreeExpression parseArrayLiteral(TreeBuilder&); template ALWAYS_INLINE TreeExpression parseObjectLiteral(TreeBuilder&); template ALWAYS_INLINE TreeExpression parseStrictObjectLiteral(TreeBuilder&); template ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&); template ALWAYS_INLINE TreeProperty parseProperty(TreeBuilder&); template ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&); template ALWAYS_INLINE TreeFormalParameterList parseFormalParameters(TreeBuilder&, bool& usesArguments); template ALWAYS_INLINE TreeExpression parseVarDeclarationList(TreeBuilder&, int& declarations, const Identifier*& lastIdent, TreeExpression& lastInitializer, int& identStart, int& initStart, int& initEnd); template ALWAYS_INLINE TreeConstDeclList parseConstDeclarationList(TreeBuilder& context); enum FunctionRequirements { FunctionNoRequirements, FunctionNeedsName }; template bool parseFunctionInfo(TreeBuilder&, const Identifier*&, TreeFormalParameterList&, TreeFunctionBody&, int& openBrace, int& closeBrace, int& bodyStartLine); ALWAYS_INLINE int isBinaryOperator(JSTokenType token); bool allowAutomaticSemicolon(); bool autoSemiColon() { if (token().m_type == SEMICOLON) { next(); return true; } return allowAutomaticSemicolon(); } bool canRecurse() { char sample = 0; ASSERT(m_endAddress); return &sample > m_endAddress; } int lastTokenEnd() const { return m_lastTokenEnd; } ParserArena m_arena; Lexer* m_lexer; char* m_endAddress; bool m_error; JSGlobalData* m_globalData; JSToken m_token; bool m_allowsIn; int m_tokenCount; int m_lastLine; int m_lastTokenEnd; int m_assignmentCount; int m_nonLHSCount; bool m_syntaxAlreadyValidated; }; int jsParse(JSGlobalData* globalData, const SourceCode* source) { JSParser parser(globalData->lexer, globalData, source->provider()); return parser.parseProgram(); } JSParser::JSParser(Lexer* lexer, JSGlobalData* globalData, SourceProvider* provider) : m_lexer(lexer) , m_endAddress(0) , m_error(false) , m_globalData(globalData) , m_allowsIn(true) , m_tokenCount(0) , m_lastLine(0) , m_lastTokenEnd(0) , m_assignmentCount(0) , m_nonLHSCount(0) , m_syntaxAlreadyValidated(provider->isValid()) { m_endAddress = *(globalData->stackGuards); if (!m_endAddress) { char sample = 0; m_endAddress = &sample - kMaxParserStackUsage; *(globalData->stackGuards) = m_endAddress; } next(); m_lexer->setLastLineNumber(tokenLine()); } bool JSParser::parseProgram() { ASTBuilder context(m_globalData, m_lexer); SourceElements* sourceElements = parseSourceElements(context); if (!sourceElements || !consume(EOFTOK)) return true; m_globalData->parser->didFinishParsing(sourceElements, context.varDeclarations(), context.funcDeclarations(), context.features(), m_lastLine, context.numConstants()); return false; } bool JSParser::allowAutomaticSemicolon() { return match(CLOSEBRACE) || match(EOFTOK) || m_lexer->prevTerminator(); } template TreeSourceElements JSParser::parseSourceElements(TreeBuilder& context) { TreeSourceElements sourceElements = context.createSourceElements(); while (TreeStatement statement = parseStatement(context)) context.appendStatement(sourceElements, statement); if (m_error) fail(); return sourceElements; } template TreeStatement JSParser::parseVarDeclaration(TreeBuilder& context) { ASSERT(match(VAR)); int start = tokenLine(); int end = 0; int scratch; const Identifier* scratch1 = 0; TreeExpression scratch2 = 0; int scratch3 = 0; TreeExpression varDecls = parseVarDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3); failIfTrue(m_error); failIfFalse(autoSemiColon()); return context.createVarStatement(varDecls, start, end); } template TreeStatement JSParser::parseConstDeclaration(TreeBuilder& context) { ASSERT(match(CONSTTOKEN)); int start = tokenLine(); int end = 0; TreeConstDeclList constDecls = parseConstDeclarationList(context); failIfTrue(m_error); failIfFalse(autoSemiColon()); return context.createConstStatement(constDecls, start, end); } template TreeStatement JSParser::parseDoWhileStatement(TreeBuilder& context) { ASSERT(match(DO)); int startLine = tokenLine(); next(); TreeStatement statement = parseStatement(context); failIfFalse(statement); int endLine = tokenLine(); consumeOrFail(WHILE); consumeOrFail(OPENPAREN); TreeExpression expr = parseExpression(context); failIfFalse(expr); consumeOrFail(CLOSEPAREN); if (match(SEMICOLON)) next(); // Always performs automatic semicolon insertion. return context.createDoWhileStatement(statement, expr, startLine, endLine); } template TreeStatement JSParser::parseWhileStatement(TreeBuilder& context) { ASSERT(match(WHILE)); int startLine = tokenLine(); next(); consumeOrFail(OPENPAREN); TreeExpression expr = parseExpression(context); failIfFalse(expr); int endLine = tokenLine(); consumeOrFail(CLOSEPAREN); TreeStatement statement = parseStatement(context); failIfFalse(statement); return context.createWhileStatement(expr, statement, startLine, endLine); } template TreeExpression JSParser::parseVarDeclarationList(TreeBuilder& context, int& declarations, const Identifier*& lastIdent, TreeExpression& lastInitializer, int& identStart, int& initStart, int& initEnd) { TreeExpression varDecls = 0; do { declarations++; next(); matchOrFail(IDENT); int varStart = tokenStart(); identStart = varStart; const Identifier* name = token().m_data.ident; lastIdent = name; next(); bool hasInitializer = match(EQUAL); context.addVar(name, (hasInitializer || (!m_allowsIn && match(INTOKEN))) ? DeclarationStacks::HasInitializer : 0); if (hasInitializer) { int varDivot = tokenStart() + 1; initStart = tokenStart(); next(); // consume '=' int initialAssignments = m_assignmentCount; TreeExpression initializer = parseAssignmentExpression(context); initEnd = lastTokenEnd(); lastInitializer = initializer; failIfFalse(initializer); TreeExpression node = context.createAssignResolve(*name, initializer, initialAssignments != m_assignmentCount, varStart, varDivot, lastTokenEnd()); if (!varDecls) varDecls = node; else varDecls = context.combineCommaNodes(varDecls, node); } } while (match(COMMA)); return varDecls; } template TreeConstDeclList JSParser::parseConstDeclarationList(TreeBuilder& context) { TreeConstDeclList constDecls = 0; TreeConstDeclList tail = 0; do { next(); matchOrFail(IDENT); const Identifier* name = token().m_data.ident; next(); bool hasInitializer = match(EQUAL); context.addVar(name, DeclarationStacks::IsConstant | (hasInitializer ? DeclarationStacks::HasInitializer : 0)); TreeExpression initializer = 0; if (hasInitializer) { next(); // consume '=' initializer = parseAssignmentExpression(context); } tail = context.appendConstDecl(tail, name, initializer); if (!constDecls) constDecls = tail; } while (match(COMMA)); return constDecls; } template TreeStatement JSParser::parseForStatement(TreeBuilder& context) { ASSERT(match(FOR)); int startLine = tokenLine(); next(); consumeOrFail(OPENPAREN); int nonLHSCount = m_nonLHSCount; int declarations = 0; int declsStart = 0; int declsEnd = 0; TreeExpression decls = 0; bool hasDeclaration = false; if (match(VAR)) { /* for (var IDENT in expression) statement for (var IDENT = expression in expression) statement for (var varDeclarationList; expressionOpt; expressionOpt) */ hasDeclaration = true; const Identifier* forInTarget = 0; TreeExpression forInInitializer = 0; m_allowsIn = false; int initStart = 0; int initEnd = 0; decls = parseVarDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd); m_allowsIn = true; if (m_error) fail(); // Remainder of a standard for loop is handled identically if (declarations > 1 || match(SEMICOLON)) goto standardForLoop; // Handle for-in with var declaration int inLocation = tokenStart(); if (!consume(INTOKEN)) fail(); TreeExpression expr = parseExpression(context); failIfFalse(expr); int exprEnd = lastTokenEnd(); int endLine = tokenLine(); consumeOrFail(CLOSEPAREN); TreeStatement statement = parseStatement(context); failIfFalse(statement); return context.createForInLoop(forInTarget, forInInitializer, expr, statement, declsStart, inLocation, exprEnd, initStart, initEnd, startLine, endLine); } if (!match(SEMICOLON)) { m_allowsIn = false; declsStart = tokenStart(); decls = parseExpression(context); declsEnd = lastTokenEnd(); m_allowsIn = true; failIfFalse(decls); } if (match(SEMICOLON)) { standardForLoop: // Standard for loop next(); TreeExpression condition = 0; if (!match(SEMICOLON)) { condition = parseExpression(context); failIfFalse(condition); } consumeOrFail(SEMICOLON); TreeExpression increment = 0; if (!match(CLOSEPAREN)) { increment = parseExpression(context); failIfFalse(increment); } int endLine = tokenLine(); consumeOrFail(CLOSEPAREN); TreeStatement statement = parseStatement(context); failIfFalse(statement); return context.createForLoop(decls, condition, increment, statement, hasDeclaration, startLine, endLine); } // For-in loop failIfFalse(nonLHSCount == m_nonLHSCount); consumeOrFail(INTOKEN); TreeExpression expr = parseExpression(context); failIfFalse(expr); int exprEnd = lastTokenEnd(); int endLine = tokenLine(); consumeOrFail(CLOSEPAREN); TreeStatement statement = parseStatement(context); failIfFalse(statement); return context.createForInLoop(decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); } template TreeStatement JSParser::parseBreakStatement(TreeBuilder& context) { ASSERT(match(BREAK)); int startCol = tokenStart(); int endCol = tokenEnd(); int startLine = tokenLine(); int endLine = tokenLine(); next(); if (autoSemiColon()) return context.createBreakStatement(startCol, endCol, startLine, endLine); matchOrFail(IDENT); const Identifier* ident = token().m_data.ident; endCol = tokenEnd(); endLine = tokenLine(); next(); failIfFalse(autoSemiColon()); return context.createBreakStatement(ident, startCol, endCol, startLine, endLine); } template TreeStatement JSParser::parseContinueStatement(TreeBuilder& context) { ASSERT(match(CONTINUE)); int startCol = tokenStart(); int endCol = tokenEnd(); int startLine = tokenLine(); int endLine = tokenLine(); next(); if (autoSemiColon()) return context.createContinueStatement(startCol, endCol, startLine, endLine); matchOrFail(IDENT); const Identifier* ident = token().m_data.ident; endCol = tokenEnd(); endLine = tokenLine(); next(); failIfFalse(autoSemiColon()); return context.createContinueStatement(ident, startCol, endCol, startLine, endLine); } template TreeStatement JSParser::parseReturnStatement(TreeBuilder& context) { ASSERT(match(RETURN)); int startLine = tokenLine(); int endLine = startLine; int start = tokenStart(); int end = tokenEnd(); next(); // We do the auto semicolon check before attempting to parse an expression // as we need to ensure the a line break after the return correctly terminates // the statement if (match(SEMICOLON)) endLine = tokenLine(); if (autoSemiColon()) return context.createReturnStatement(0, start, end, startLine, endLine); TreeExpression expr = parseExpression(context); failIfFalse(expr); end = lastTokenEnd(); if (match(SEMICOLON)) endLine = tokenLine(); failIfFalse(autoSemiColon()); return context.createReturnStatement(expr, start, end, startLine, endLine); } template TreeStatement JSParser::parseThrowStatement(TreeBuilder& context) { ASSERT(match(THROW)); int eStart = tokenStart(); int startLine = tokenLine(); next(); TreeExpression expr = parseExpression(context); failIfFalse(expr); int eEnd = lastTokenEnd(); int endLine = tokenLine(); failIfFalse(autoSemiColon()); return context.createThrowStatement(expr, eStart, eEnd, startLine, endLine); } template TreeStatement JSParser::parseWithStatement(TreeBuilder& context) { ASSERT(match(WITH)); int startLine = tokenLine(); next(); consumeOrFail(OPENPAREN); int start = tokenStart(); TreeExpression expr = parseExpression(context); failIfFalse(expr); int end = lastTokenEnd(); int endLine = tokenLine(); consumeOrFail(CLOSEPAREN); TreeStatement statement = parseStatement(context); failIfFalse(statement); return context.createWithStatement(expr, statement, start, end, startLine, endLine); } template TreeStatement JSParser::parseSwitchStatement(TreeBuilder& context) { ASSERT(match(SWITCH)); int startLine = tokenLine(); next(); consumeOrFail(OPENPAREN); TreeExpression expr = parseExpression(context); failIfFalse(expr); int endLine = tokenLine(); consumeOrFail(CLOSEPAREN); consumeOrFail(OPENBRACE); TreeClauseList firstClauses = parseSwitchClauses(context); failIfTrue(m_error); TreeClause defaultClause = parseSwitchDefaultClause(context); failIfTrue(m_error); TreeClauseList secondClauses = parseSwitchClauses(context); failIfTrue(m_error); consumeOrFail(CLOSEBRACE); return context.createSwitchStatement(expr, firstClauses, defaultClause, secondClauses, startLine, endLine); } template TreeClauseList JSParser::parseSwitchClauses(TreeBuilder& context) { if (!match(CASE)) return 0; next(); TreeExpression condition = parseExpression(context); failIfFalse(condition); consumeOrFail(COLON); TreeSourceElements statements = parseSourceElements(context); failIfFalse(statements); TreeClause clause = context.createClause(condition, statements); TreeClauseList clauseList = context.createClauseList(clause); TreeClauseList tail = clauseList; while (match(CASE)) { next(); TreeExpression condition = parseExpression(context); failIfFalse(condition); consumeOrFail(COLON); TreeSourceElements statements = parseSourceElements(context); failIfFalse(statements); clause = context.createClause(condition, statements); tail = context.createClauseList(tail, clause); } return clauseList; } template TreeClause JSParser::parseSwitchDefaultClause(TreeBuilder& context) { if (!match(DEFAULT)) return 0; next(); consumeOrFail(COLON); TreeSourceElements statements = parseSourceElements(context); failIfFalse(statements); return context.createClause(0, statements); } template TreeStatement JSParser::parseTryStatement(TreeBuilder& context) { ASSERT(match(TRY)); TreeStatement tryBlock = 0; const Identifier* ident = &m_globalData->propertyNames->nullIdentifier; bool catchHasEval = false; TreeStatement catchBlock = 0; TreeStatement finallyBlock = 0; int firstLine = tokenLine(); next(); matchOrFail(OPENBRACE); tryBlock = parseBlockStatement(context); failIfFalse(tryBlock); int lastLine = m_lastLine; if (match(CATCH)) { next(); consumeOrFail(OPENPAREN); matchOrFail(IDENT); ident = token().m_data.ident; next(); consumeOrFail(CLOSEPAREN); matchOrFail(OPENBRACE); int initialEvalCount = context.evalCount(); catchBlock = parseBlockStatement(context); failIfFalse(catchBlock); catchHasEval = initialEvalCount != context.evalCount(); } if (match(FINALLY)) { next(); matchOrFail(OPENBRACE); finallyBlock = parseBlockStatement(context); failIfFalse(finallyBlock); } failIfFalse(catchBlock || finallyBlock); return context.createTryStatement(tryBlock, ident, catchHasEval, catchBlock, finallyBlock, firstLine, lastLine); } template TreeStatement JSParser::parseDebuggerStatement(TreeBuilder& context) { ASSERT(match(DEBUGGER)); int startLine = tokenLine(); int endLine = startLine; next(); if (match(SEMICOLON)) startLine = tokenLine(); failIfFalse(autoSemiColon()); return context.createDebugger(startLine, endLine); } template TreeStatement JSParser::parseBlockStatement(TreeBuilder& context) { ASSERT(match(OPENBRACE)); int start = tokenLine(); next(); if (match(CLOSEBRACE)) { next(); return context.createBlockStatement(0, start, m_lastLine); } TreeSourceElements subtree = parseSourceElements(context); failIfFalse(subtree); matchOrFail(CLOSEBRACE); next(); return context.createBlockStatement(subtree, start, m_lastLine); } template TreeStatement JSParser::parseStatement(TreeBuilder& context) { failIfStackOverflow(); switch (token().m_type) { case OPENBRACE: return parseBlockStatement(context); case VAR: return parseVarDeclaration(context); case CONSTTOKEN: return parseConstDeclaration(context); case FUNCTION: return parseFunctionDeclaration(context); case SEMICOLON: next(); return context.createEmptyStatement(); case IF: return parseIfStatement(context); case DO: return parseDoWhileStatement(context); case WHILE: return parseWhileStatement(context); case FOR: return parseForStatement(context); case CONTINUE: return parseContinueStatement(context); case BREAK: return parseBreakStatement(context); case RETURN: return parseReturnStatement(context); case WITH: return parseWithStatement(context); case SWITCH: return parseSwitchStatement(context); case THROW: return parseThrowStatement(context); case TRY: return parseTryStatement(context); case DEBUGGER: return parseDebuggerStatement(context); case EOFTOK: case CASE: case CLOSEBRACE: case DEFAULT: // These tokens imply the end of a set of source elements return 0; case IDENT: return parseExpressionOrLabelStatement(context); default: return parseExpressionStatement(context); } } template TreeFormalParameterList JSParser::parseFormalParameters(TreeBuilder& context, bool& usesArguments) { matchOrFail(IDENT); usesArguments = m_globalData->propertyNames->arguments == *token().m_data.ident; TreeFormalParameterList list = context.createFormalParameterList(*token().m_data.ident); TreeFormalParameterList tail = list; next(); while (match(COMMA)) { next(); matchOrFail(IDENT); const Identifier* ident = token().m_data.ident; next(); usesArguments = usesArguments || m_globalData->propertyNames->arguments == *ident; tail = context.createFormalParameterList(tail, *ident); } return list; } template TreeFunctionBody JSParser::parseFunctionBody(TreeBuilder& context) { if (match(CLOSEBRACE)) return context.createFunctionBody(); typename TreeBuilder::FunctionBodyBuilder bodyBuilder(m_globalData, m_lexer); failIfFalse(parseSourceElements(bodyBuilder)); return context.createFunctionBody(); } template bool JSParser::parseFunctionInfo(TreeBuilder& context, const Identifier*& name, TreeFormalParameterList& parameters, TreeFunctionBody& body, int& openBracePos, int& closeBracePos, int& bodyStartLine) { if (match(IDENT)) { name = token().m_data.ident; next(); } else if (requirements == FunctionNeedsName) return false; consumeOrFail(OPENPAREN); bool usesArguments = false; if (!match(CLOSEPAREN)) { parameters = parseFormalParameters(context, usesArguments); failIfFalse(parameters); } consumeOrFail(CLOSEPAREN); matchOrFail(OPENBRACE); openBracePos = token().m_data.intValue; bodyStartLine = tokenLine(); next(); body = parseFunctionBody(context); failIfFalse(body); if (usesArguments) context.setUsesArguments(body); matchOrFail(CLOSEBRACE); closeBracePos = token().m_data.intValue; next(); return true; } template TreeStatement JSParser::parseFunctionDeclaration(TreeBuilder& context) { ASSERT(match(FUNCTION)); next(); const Identifier* name = 0; TreeFormalParameterList parameters = 0; TreeFunctionBody body = 0; int openBracePos = 0; int closeBracePos = 0; int bodyStartLine = 0; failIfFalse(parseFunctionInfo(context, name, parameters, body, openBracePos, closeBracePos, bodyStartLine)); failIfFalse(name); return context.createFuncDeclStatement(name, body, parameters, openBracePos, closeBracePos, bodyStartLine, m_lastLine); } template TreeStatement JSParser::parseExpressionOrLabelStatement(TreeBuilder& context) { /* Expression and Label statements are ambiguous at LL(1), to avoid * the cost of having a token buffer to support LL(2) we simply assume * we have an expression statement, and then only look for a label if that * parse fails. */ int start = tokenStart(); int startLine = tokenLine(); const Identifier* ident = token().m_data.ident; int currentToken = m_tokenCount; TreeExpression expression = parseExpression(context); failIfFalse(expression); if (autoSemiColon()) return context.createExprStatement(expression, startLine, m_lastLine); failIfFalse(currentToken + 1 == m_tokenCount); int end = tokenEnd(); consumeOrFail(COLON); TreeStatement statement = parseStatement(context); failIfFalse(statement); return context.createLabelStatement(ident, statement, start, end); } template TreeStatement JSParser::parseExpressionStatement(TreeBuilder& context) { int startLine = tokenLine(); TreeExpression expression = parseExpression(context); failIfFalse(expression); failIfFalse(autoSemiColon()); return context.createExprStatement(expression, startLine, m_lastLine); } template TreeStatement JSParser::parseIfStatement(TreeBuilder& context) { ASSERT(match(IF)); int start = tokenLine(); next(); consumeOrFail(OPENPAREN); TreeExpression condition = parseExpression(context); failIfFalse(condition); int end = tokenLine(); consumeOrFail(CLOSEPAREN); TreeStatement trueBlock = parseStatement(context); failIfFalse(trueBlock); if (!match(ELSE)) return context.createIfStatement(condition, trueBlock, start, end); Vector exprStack; Vector > posStack; Vector statementStack; bool trailingElse = false; do { next(); if (!match(IF)) { TreeStatement block = parseStatement(context); failIfFalse(block); statementStack.append(block); trailingElse = true; break; } int innerStart = tokenLine(); next(); consumeOrFail(OPENPAREN); TreeExpression innerCondition = parseExpression(context); failIfFalse(innerCondition); int innerEnd = tokenLine(); consumeOrFail(CLOSEPAREN); TreeStatement innerTrueBlock = parseStatement(context); failIfFalse(innerTrueBlock); exprStack.append(innerCondition); posStack.append(make_pair(innerStart, innerEnd)); statementStack.append(innerTrueBlock); } while (match(ELSE)); if (!trailingElse) { TreeExpression condition = exprStack.last(); exprStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); pair pos = posStack.last(); posStack.removeLast(); statementStack.append(context.createIfStatement(condition, trueBlock, pos.first, pos.second)); } while (!exprStack.isEmpty()) { TreeExpression condition = exprStack.last(); exprStack.removeLast(); TreeStatement falseBlock = statementStack.last(); statementStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); pair pos = posStack.last(); posStack.removeLast(); statementStack.append(context.createIfStatement(condition, trueBlock, falseBlock, pos.first, pos.second)); } return context.createIfStatement(condition, trueBlock, statementStack.last(), start, end); } template TreeExpression JSParser::parseExpression(TreeBuilder& context) { failIfStackOverflow(); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node); if (!match(COMMA)) return node; next(); m_nonLHSCount++; TreeExpression right = parseAssignmentExpression(context); failIfFalse(right); typename TreeBuilder::Comma commaNode = context.createCommaExpr(node, right); while (match(COMMA)) { next(); right = parseAssignmentExpression(context); failIfFalse(right); context.appendToComma(commaNode, right); } return commaNode; } template TreeExpression JSParser::parseAssignmentExpression(TreeBuilder& context) { failIfStackOverflow(); int start = tokenStart(); int initialAssignmentCount = m_assignmentCount; int initialNonLHSCount = m_nonLHSCount; TreeExpression lhs = parseConditionalExpression(context); failIfFalse(lhs); if (initialNonLHSCount != m_nonLHSCount) return lhs; int assignmentStack = 0; Operator op; bool hadAssignment = false; while (true) { switch (token().m_type) { case EQUAL: op = OpEqual; break; case PLUSEQUAL: op = OpPlusEq; break; case MINUSEQUAL: op = OpMinusEq; break; case MULTEQUAL: op = OpMultEq; break; case DIVEQUAL: op = OpDivEq; break; case LSHIFTEQUAL: op = OpLShift; break; case RSHIFTEQUAL: op = OpRShift; break; case URSHIFTEQUAL: op = OpURShift; break; case ANDEQUAL: op = OpAndEq; break; case XOREQUAL: op = OpXOrEq; break; case OREQUAL: op = OpOrEq; break; case MODEQUAL: op = OpModEq; break; default: goto end; } hadAssignment = true; context.assignmentStackAppend(assignmentStack, lhs, start, tokenStart(), m_assignmentCount, op); start = tokenStart(); m_assignmentCount++; next(); lhs = parseConditionalExpression(context); failIfFalse(lhs); if (initialNonLHSCount != m_nonLHSCount) break; } end: if (hadAssignment) m_nonLHSCount++; if (!ASTBuilder::CreatesAST) return lhs; while (assignmentStack) lhs = context.createAssignment(assignmentStack, lhs, initialAssignmentCount, m_assignmentCount, lastTokenEnd()); return lhs; } template TreeExpression JSParser::parseConditionalExpression(TreeBuilder& context) { TreeExpression cond = parseBinaryExpression(context); failIfFalse(cond); if (!match(QUESTION)) return cond; m_nonLHSCount++; next(); TreeExpression lhs = parseAssignmentExpression(context); consumeOrFail(COLON); TreeExpression rhs = parseAssignmentExpression(context); failIfFalse(rhs); return context.createConditionalExpr(cond, lhs, rhs); } ALWAYS_INLINE static bool isUnaryOp(JSTokenType token) { return token & UnaryOpTokenFlag; } int JSParser::isBinaryOperator(JSTokenType token) { if (m_allowsIn) return token & (BinaryOpTokenPrecedenceMask << BinaryOpTokenAllowsInPrecedenceAdditionalShift); return token & BinaryOpTokenPrecedenceMask; } template TreeExpression JSParser::parseBinaryExpression(TreeBuilder& context) { int operandStackDepth = 0; int operatorStackDepth = 0; while (true) { int exprStart = tokenStart(); int initialAssignments = m_assignmentCount; TreeExpression current = parseUnaryExpression(context); failIfFalse(current); context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEnd(), lastTokenEnd(), initialAssignments != m_assignmentCount); int precedence = isBinaryOperator(token().m_type); if (!precedence) break; m_nonLHSCount++; int operatorToken = token().m_type; next(); while (operatorStackDepth && context.operatorStackHasHigherPrecedence(operatorStackDepth, precedence)) { ASSERT(operandStackDepth > 1); typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); context.shrinkOperandStackBy(operandStackDepth, 2); context.appendBinaryOperation(operandStackDepth, operatorStackDepth, lhs, rhs); context.operatorStackPop(operatorStackDepth); } context.operatorStackAppend(operatorStackDepth, operatorToken, precedence); } while (operatorStackDepth) { ASSERT(operandStackDepth > 1); typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); context.shrinkOperandStackBy(operandStackDepth, 2); context.appendBinaryOperation(operandStackDepth, operatorStackDepth, lhs, rhs); context.operatorStackPop(operatorStackDepth); } return context.popOperandStack(operandStackDepth); } template TreeProperty JSParser::parseProperty(TreeBuilder& context) { bool wasIdent = false; switch (token().m_type) { namedProperty: case IDENT: wasIdent = true; case STRING: { const Identifier* ident = token().m_data.ident; next(Lexer::IgnoreReservedWords); if (match(COLON)) { next(); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node); return context.template createProperty(ident, node, PropertyNode::Constant); } failIfFalse(wasIdent); matchOrFail(IDENT); const Identifier* accessorName = 0; TreeFormalParameterList parameters = 0; TreeFunctionBody body = 0; int openBracePos = 0; int closeBracePos = 0; int bodyStartLine = 0; PropertyNode::Type type; if (*ident == m_globalData->propertyNames->get) type = PropertyNode::Getter; else if (*ident == m_globalData->propertyNames->set) type = PropertyNode::Setter; else fail(); failIfFalse(parseFunctionInfo(context, accessorName, parameters, body, openBracePos, closeBracePos, bodyStartLine)); return context.template createGetterOrSetterProperty(type, accessorName, parameters, body, openBracePos, closeBracePos, bodyStartLine, m_lastLine); } case NUMBER: { double propertyName = token().m_data.doubleValue; next(); consumeOrFail(COLON); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node); return context.template createProperty(m_globalData, propertyName, node, PropertyNode::Constant); } default: failIfFalse(token().m_type & KeywordTokenFlag); goto namedProperty; } } template TreeExpression JSParser::parseObjectLiteral(TreeBuilder& context) { int startOffset = token().m_data.intValue; consumeOrFail(OPENBRACE); if (match(CLOSEBRACE)) { next(); return context.createObjectLiteral(); } TreeProperty property = parseProperty(context); failIfFalse(property); if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { m_lexer->setOffset(startOffset); next(); return parseStrictObjectLiteral(context); } TreePropertyList propertyList = context.createPropertyList(property); TreePropertyList tail = propertyList; while (match(COMMA)) { next(); // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 if (match(CLOSEBRACE)) break; property = parseProperty(context); failIfFalse(property); if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { m_lexer->setOffset(startOffset); next(); return parseStrictObjectLiteral(context); } tail = context.createPropertyList(property, tail); } consumeOrFail(CLOSEBRACE); return context.createObjectLiteral(propertyList); } template TreeExpression JSParser::parseStrictObjectLiteral(TreeBuilder& context) { consumeOrFail(OPENBRACE); if (match(CLOSEBRACE)) { next(); return context.createObjectLiteral(); } TreeProperty property = parseProperty(context); failIfFalse(property); typedef HashMap, unsigned, IdentifierRepHash> ObjectValidationMap; ObjectValidationMap objectValidator; // Add the first property if (!m_syntaxAlreadyValidated) objectValidator.add(context.getName(property).ustring().rep(), context.getType(property)); TreePropertyList propertyList = context.createPropertyList(property); TreePropertyList tail = propertyList; while (match(COMMA)) { next(); // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 if (match(CLOSEBRACE)) break; property = parseProperty(context); failIfFalse(property); if (!m_syntaxAlreadyValidated) { std::pair propertyEntryIter = objectValidator.add(context.getName(property).ustring().rep(), context.getType(property)); if (!propertyEntryIter.second) { if ((context.getType(property) & propertyEntryIter.first->second) != PropertyNode::Constant) { // Can't have multiple getters or setters with the same name, nor can we define // a property as both an accessor and a constant value failIfTrue(context.getType(property) & propertyEntryIter.first->second); failIfTrue((context.getType(property) | propertyEntryIter.first->second) & PropertyNode::Constant); } } } tail = context.createPropertyList(property, tail); } consumeOrFail(CLOSEBRACE); return context.createObjectLiteral(propertyList); } template TreeExpression JSParser::parseArrayLiteral(TreeBuilder& context) { consumeOrFail(OPENBRACKET); int elisions = 0; while (match(COMMA)) { next(); elisions++; } if (match(CLOSEBRACKET)) { next(); return context.createArray(elisions); } TreeExpression elem = parseAssignmentExpression(context); failIfFalse(elem); typename TreeBuilder::ElementList elementList = context.createElementList(elisions, elem); typename TreeBuilder::ElementList tail = elementList; elisions = 0; while (match(COMMA)) { next(); elisions = 0; while (match(COMMA)) { next(); elisions++; } if (match(CLOSEBRACKET)) { next(); return context.createArray(elisions, elementList); } TreeExpression elem = parseAssignmentExpression(context); failIfFalse(elem); tail = context.createElementList(tail, elisions, elem); } consumeOrFail(CLOSEBRACKET); return context.createArray(elementList); } template TreeExpression JSParser::parsePrimaryExpression(TreeBuilder& context) { switch (token().m_type) { case OPENBRACE: return parseObjectLiteral(context); case OPENBRACKET: return parseArrayLiteral(context); case OPENPAREN: { next(); int oldNonLHSCount = m_nonLHSCount; TreeExpression result = parseExpression(context); m_nonLHSCount = oldNonLHSCount; consumeOrFail(CLOSEPAREN); return result; } case THISTOKEN: { next(); return context.thisExpr(); } case IDENT: { int start = tokenStart(); const Identifier* ident = token().m_data.ident; next(); return context.createResolve(ident, start); } case STRING: { const Identifier* ident = token().m_data.ident; next(); return context.createString(ident); } case NUMBER: { double d = token().m_data.doubleValue; next(); return context.createNumberExpr(d); } case NULLTOKEN: { next(); return context.createNull(); } case TRUETOKEN: { next(); return context.createBoolean(true); } case FALSETOKEN: { next(); return context.createBoolean(false); } case DIVEQUAL: case DIVIDE: { /* regexp */ const Identifier* pattern; const Identifier* flags; if (match(DIVEQUAL)) failIfFalse(m_lexer->scanRegExp(pattern, flags, '=')); else failIfFalse(m_lexer->scanRegExp(pattern, flags)); int start = tokenStart(); next(); return context.createRegex(*pattern, *flags, start); } default: fail(); } } template TreeArguments JSParser::parseArguments(TreeBuilder& context) { consumeOrFail(OPENPAREN); if (match(CLOSEPAREN)) { next(); return context.createArguments(); } TreeExpression firstArg = parseAssignmentExpression(context); failIfFalse(firstArg); TreeArgumentsList argList = context.createArgumentsList(firstArg); TreeArgumentsList tail = argList; while (match(COMMA)) { next(); TreeExpression arg = parseAssignmentExpression(context); failIfFalse(arg); tail = context.createArgumentsList(tail, arg); } consumeOrFail(CLOSEPAREN); return context.createArguments(argList); } template TreeExpression JSParser::parseMemberExpression(TreeBuilder& context) { TreeExpression base = 0; int start = tokenStart(); int expressionStart = start; int newCount = 0; while (match(NEW)) { next(); newCount++; } if (match(FUNCTION)) { const Identifier* name = &m_globalData->propertyNames->nullIdentifier; TreeFormalParameterList parameters = 0; TreeFunctionBody body = 0; int openBracePos = 0; int closeBracePos = 0; int bodyStartLine = 0; next(); failIfFalse(parseFunctionInfo(context, name, parameters, body, openBracePos, closeBracePos, bodyStartLine)); base = context.createFunctionExpr(name, body, parameters, openBracePos, closeBracePos, bodyStartLine, m_lastLine); } else base = parsePrimaryExpression(context); failIfFalse(base); while (true) { switch (token().m_type) { case OPENBRACKET: { int expressionEnd = lastTokenEnd(); next(); int nonLHSCount = m_nonLHSCount; int initialAssignments = m_assignmentCount; TreeExpression property = parseExpression(context); failIfFalse(property); base = context.createBracketAccess(base, property, initialAssignments != m_assignmentCount, expressionStart, expressionEnd, tokenEnd()); if (!consume(CLOSEBRACKET)) fail(); m_nonLHSCount = nonLHSCount; break; } case OPENPAREN: { if (newCount) { newCount--; if (match(OPENPAREN)) { int exprEnd = lastTokenEnd(); TreeArguments arguments = parseArguments(context); failIfFalse(arguments); base = context.createNewExpr(base, arguments, start, exprEnd, lastTokenEnd()); } else base = context.createNewExpr(base, start, lastTokenEnd()); } else { int nonLHSCount = m_nonLHSCount; int expressionEnd = lastTokenEnd(); TreeArguments arguments = parseArguments(context); failIfFalse(arguments); base = context.makeFunctionCallNode(base, arguments, expressionStart, expressionEnd, lastTokenEnd()); m_nonLHSCount = nonLHSCount; } break; } case DOT: { int expressionEnd = lastTokenEnd(); next(Lexer::IgnoreReservedWords); matchOrFail(IDENT); base = context.createDotAccess(base, *token().m_data.ident, expressionStart, expressionEnd, tokenEnd()); next(); break; } default: goto endMemberExpression; } } endMemberExpression: while (newCount--) base = context.createNewExpr(base, start, lastTokenEnd()); return base; } template TreeExpression JSParser::parseUnaryExpression(TreeBuilder& context) { AllowInOverride allowInOverride(this); int tokenStackDepth = 0; while (isUnaryOp(token().m_type)) { m_nonLHSCount++; context.appendUnaryToken(tokenStackDepth, token().m_type, tokenStart()); next(); } int subExprStart = tokenStart(); TreeExpression expr = parseMemberExpression(context); failIfFalse(expr); switch (token().m_type) { case PLUSPLUS: m_nonLHSCount++; expr = context.makePostfixNode(expr, OpPlusPlus, subExprStart, lastTokenEnd(), tokenEnd()); m_assignmentCount++; next(); break; case MINUSMINUS: m_nonLHSCount++; expr = context.makePostfixNode(expr, OpMinusMinus, subExprStart, lastTokenEnd(), tokenEnd()); m_assignmentCount++; next(); break; default: break; } int end = lastTokenEnd(); if (!TreeBuilder::CreatesAST) return expr; while (tokenStackDepth) { switch (context.unaryTokenStackLastType(tokenStackDepth)) { case EXCLAMATION: expr = context.createLogicalNot(expr); break; case TILDE: expr = context.makeBitwiseNotNode(expr); break; case MINUS: expr = context.makeNegateNode(expr); break; case PLUS: expr = context.createUnaryPlus(expr); break; case PLUSPLUS: case AUTOPLUSPLUS: expr = context.makePrefixNode(expr, OpPlusPlus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); m_assignmentCount++; break; case MINUSMINUS: case AUTOMINUSMINUS: expr = context.makePrefixNode(expr, OpMinusMinus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); m_assignmentCount++; break; case TYPEOF: expr = context.makeTypeOfNode(expr); break; case VOIDTOKEN: expr = context.createVoid(expr); break; case DELETETOKEN: expr = context.makeDeleteNode(expr, context.unaryTokenStackLastStart(tokenStackDepth), end, end); break; default: // If we get here something has gone horribly horribly wrong CRASH(); } subExprStart = context.unaryTokenStackLastStart(tokenStackDepth); context.unaryTokenStackRemoveLast(tokenStackDepth); } return expr; } }