/* * 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 "CodeBlock.h" #include "JSGlobalData.h" #include "NodeInfo.h" #include "ASTBuilder.h" #include "SourceProvider.h" #include "SourceProviderCacheItem.h" #include <wtf/HashFunctions.h> #include <wtf/OwnPtr.h> #include <wtf/WTFThreadData.h> #include <utility> 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 failIfTrueIfStrict(cond) do { if ((cond) && strictMode()) fail(); } while (0) #define failIfFalseIfStrict(cond) do { if ((!(cond)) && strictMode()) 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); class JSParser { public: JSParser(Lexer*, JSGlobalData*, FunctionParameters*, bool isStrictContext, bool isFunction, SourceProvider*); const char* 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; }; struct ScopeLabelInfo { ScopeLabelInfo(StringImpl* ident, bool isLoop) : m_ident(ident) , m_isLoop(isLoop) { } StringImpl* m_ident; bool m_isLoop; }; void next(Lexer::LexType lexType = Lexer::IdentifyReservedWords) { m_lastLine = m_token.m_info.line; m_lastTokenEnd = m_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, strictMode()); } bool nextTokenIsColon() { return m_lexer->nextTokenIsColon(); } 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 m_token.m_info.startOffset; } int tokenLine() { return m_token.m_info.line; } int tokenEnd() { return m_token.m_info.endOffset; } void startLoop() { currentScope()->startLoop(); } void endLoop() { currentScope()->endLoop(); } void startSwitch() { currentScope()->startSwitch(); } void endSwitch() { currentScope()->endSwitch(); } void setStrictMode() { currentScope()->setStrictMode(); } bool strictMode() { return currentScope()->strictMode(); } bool isValidStrictMode() { return currentScope()->isValidStrictMode(); } bool declareParameter(const Identifier* ident) { return currentScope()->declareParameter(ident); } bool breakIsValid() { ScopeRef current = currentScope(); while (!current->breakIsValid()) { if (!current.hasContainingScope()) return false; current = current.containingScope(); } return true; } bool continueIsValid() { ScopeRef current = currentScope(); while (!current->continueIsValid()) { if (!current.hasContainingScope()) return false; current = current.containingScope(); } return true; } void pushLabel(const Identifier* label, bool isLoop) { currentScope()->pushLabel(label, isLoop); } void popLabel() { currentScope()->popLabel(); } ScopeLabelInfo* getLabel(const Identifier* label) { ScopeRef current = currentScope(); ScopeLabelInfo* result = 0; while (!(result = current->getLabel(label))) { if (!current.hasContainingScope()) return 0; current = current.containingScope(); } return result; } enum SourceElementsMode { CheckForStrictMode, DontCheckForStrictMode }; template <SourceElementsMode mode, class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&); template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive); template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&); template <class TreeBuilder> TreeStatement parseVarDeclaration(TreeBuilder&); template <class TreeBuilder> TreeStatement parseConstDeclaration(TreeBuilder&); template <class TreeBuilder> TreeStatement parseDoWhileStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseWhileStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseForStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseBreakStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseContinueStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseReturnStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseThrowStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseWithStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseSwitchStatement(TreeBuilder&); template <class TreeBuilder> TreeClauseList parseSwitchClauses(TreeBuilder&); template <class TreeBuilder> TreeClause parseSwitchDefaultClause(TreeBuilder&); template <class TreeBuilder> TreeStatement parseTryStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseDebuggerStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseExpressionStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseExpressionOrLabelStatement(TreeBuilder&); template <class TreeBuilder> TreeStatement parseIfStatement(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeStatement parseBlockStatement(TreeBuilder&); template <class TreeBuilder> TreeExpression parseExpression(TreeBuilder&); template <class TreeBuilder> TreeExpression parseAssignmentExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseConditionalExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseBinaryExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseUnaryExpression(TreeBuilder&); template <class TreeBuilder> TreeExpression parseMemberExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parsePrimaryExpression(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseArrayLiteral(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseObjectLiteral(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseStrictObjectLiteral(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&); template <bool strict, class TreeBuilder> ALWAYS_INLINE TreeProperty parseProperty(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeFormalParameterList parseFormalParameters(TreeBuilder&); template <class TreeBuilder> ALWAYS_INLINE TreeExpression parseVarDeclarationList(TreeBuilder&, int& declarations, const Identifier*& lastIdent, TreeExpression& lastInitializer, int& identStart, int& initStart, int& initEnd); template <class TreeBuilder> ALWAYS_INLINE TreeConstDeclList parseConstDeclarationList(TreeBuilder& context); enum FunctionRequirements { FunctionNoRequirements, FunctionNeedsName }; template <FunctionRequirements, bool nameIsInContainingScope, class TreeBuilder> bool parseFunctionInfo(TreeBuilder&, const Identifier*&, TreeFormalParameterList&, TreeFunctionBody&, int& openBrace, int& closeBrace, int& bodyStartLine); ALWAYS_INLINE int isBinaryOperator(JSTokenType token); bool allowAutomaticSemicolon(); bool autoSemiColon() { if (m_token.m_type == SEMICOLON) { next(); return true; } return allowAutomaticSemicolon(); } bool canRecurse() { return m_stack.recursionCheck(); } int lastTokenEnd() const { return m_lastTokenEnd; } ParserArena m_arena; Lexer* m_lexer; StackBounds m_stack; bool m_error; const char* m_errorMessage; JSGlobalData* m_globalData; JSToken m_token; bool m_allowsIn; int m_lastLine; int m_lastTokenEnd; int m_assignmentCount; int m_nonLHSCount; bool m_syntaxAlreadyValidated; int m_statementDepth; int m_nonTrivialExpressionCount; const Identifier* m_lastIdentifier; struct DepthManager { DepthManager(int* depth) : m_originalDepth(*depth) , m_depth(depth) { } ~DepthManager() { *m_depth = m_originalDepth; } private: int m_originalDepth; int* m_depth; }; struct Scope { Scope(JSGlobalData* globalData, bool isFunction, bool strictMode) : m_globalData(globalData) , m_shadowsArguments(false) , m_usesEval(false) , m_needsFullActivation(false) , m_allowsNewDecls(true) , m_strictMode(strictMode) , m_isFunction(isFunction) , m_isFunctionBoundary(false) , m_isValidStrictMode(true) , m_loopDepth(0) , m_switchDepth(0) , m_labels(0) { } Scope(const Scope& rhs) : m_globalData(rhs.m_globalData) , m_shadowsArguments(rhs.m_shadowsArguments) , m_usesEval(rhs.m_usesEval) , m_needsFullActivation(rhs.m_needsFullActivation) , m_allowsNewDecls(rhs.m_allowsNewDecls) , m_strictMode(rhs.m_strictMode) , m_isFunction(rhs.m_isFunction) , m_isFunctionBoundary(rhs.m_isFunctionBoundary) , m_isValidStrictMode(rhs.m_isValidStrictMode) , m_loopDepth(rhs.m_loopDepth) , m_switchDepth(rhs.m_switchDepth) , m_labels(0) { if (rhs.m_labels) { m_labels = adoptPtr(new LabelStack); typedef LabelStack::const_iterator iterator; iterator end = rhs.m_labels->end(); for (iterator it = rhs.m_labels->begin(); it != end; ++it) m_labels->append(ScopeLabelInfo(it->m_ident, it->m_isLoop)); } } void startSwitch() { m_switchDepth++; } void endSwitch() { m_switchDepth--; } void startLoop() { m_loopDepth++; } void endLoop() { ASSERT(m_loopDepth); m_loopDepth--; } bool inLoop() { return !!m_loopDepth; } bool breakIsValid() { return m_loopDepth || m_switchDepth; } bool continueIsValid() { return m_loopDepth; } void pushLabel(const Identifier* label, bool isLoop) { if (!m_labels) m_labels = adoptPtr(new LabelStack); m_labels->append(ScopeLabelInfo(label->impl(), isLoop)); } void popLabel() { ASSERT(m_labels); ASSERT(m_labels->size()); m_labels->removeLast(); } ScopeLabelInfo* getLabel(const Identifier* label) { if (!m_labels) return 0; for (int i = m_labels->size(); i > 0; i--) { if (m_labels->at(i - 1).m_ident == label->impl()) return &m_labels->at(i - 1); } return 0; } void setIsFunction() { m_isFunction = true; m_isFunctionBoundary = true; } bool isFunction() { return m_isFunction; } bool isFunctionBoundary() { return m_isFunctionBoundary; } bool declareVariable(const Identifier* ident) { bool isValidStrictMode = m_globalData->propertyNames->eval != *ident && m_globalData->propertyNames->arguments != *ident; m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; m_declaredVariables.add(ident->ustring().impl()); return isValidStrictMode; } void declareWrite(const Identifier* ident) { ASSERT(m_strictMode); m_writtenVariables.add(ident->impl()); } void preventNewDecls() { m_allowsNewDecls = false; } bool allowsNewDecls() const { return m_allowsNewDecls; } bool declareParameter(const Identifier* ident) { bool isArguments = m_globalData->propertyNames->arguments == *ident; bool isValidStrictMode = m_declaredVariables.add(ident->ustring().impl()).second && m_globalData->propertyNames->eval != *ident && !isArguments; m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode; if (isArguments) m_shadowsArguments = true; return isValidStrictMode; } void useVariable(const Identifier* ident, bool isEval) { m_usesEval |= isEval; m_usedVariables.add(ident->ustring().impl()); } void setNeedsFullActivation() { m_needsFullActivation = true; } bool collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables) { if (nestedScope->m_usesEval) m_usesEval = true; IdentifierSet::iterator end = nestedScope->m_usedVariables.end(); for (IdentifierSet::iterator ptr = nestedScope->m_usedVariables.begin(); ptr != end; ++ptr) { if (nestedScope->m_declaredVariables.contains(*ptr)) continue; m_usedVariables.add(*ptr); if (shouldTrackClosedVariables) m_closedVariables.add(*ptr); } if (nestedScope->m_writtenVariables.size()) { IdentifierSet::iterator end = nestedScope->m_writtenVariables.end(); for (IdentifierSet::iterator ptr = nestedScope->m_writtenVariables.begin(); ptr != end; ++ptr) { if (nestedScope->m_declaredVariables.contains(*ptr)) continue; m_writtenVariables.add(*ptr); } } return true; } void getUncapturedWrittenVariables(IdentifierSet& writtenVariables) { IdentifierSet::iterator end = m_writtenVariables.end(); for (IdentifierSet::iterator ptr = m_writtenVariables.begin(); ptr != end; ++ptr) { if (!m_declaredVariables.contains(*ptr)) writtenVariables.add(*ptr); } } void getCapturedVariables(IdentifierSet& capturedVariables) { if (m_needsFullActivation || m_usesEval) { capturedVariables.swap(m_declaredVariables); return; } for (IdentifierSet::iterator ptr = m_closedVariables.begin(); ptr != m_closedVariables.end(); ++ptr) { if (!m_declaredVariables.contains(*ptr)) continue; capturedVariables.add(*ptr); } } void setStrictMode() { m_strictMode = true; } bool strictMode() const { return m_strictMode; } bool isValidStrictMode() const { return m_isValidStrictMode; } bool shadowsArguments() const { return m_shadowsArguments; } void copyCapturedVariablesToVector(const IdentifierSet& capturedVariables, Vector<RefPtr<StringImpl> >& vector) { IdentifierSet::iterator end = capturedVariables.end(); for (IdentifierSet::iterator it = capturedVariables.begin(); it != end; ++it) { if (m_declaredVariables.contains(*it)) continue; vector.append(*it); } vector.shrinkToFit(); } void saveFunctionInfo(SourceProviderCacheItem* info) { ASSERT(m_isFunction); info->usesEval = m_usesEval; copyCapturedVariablesToVector(m_writtenVariables, info->writtenVariables); copyCapturedVariablesToVector(m_usedVariables, info->usedVariables); } void restoreFunctionInfo(const SourceProviderCacheItem* info) { ASSERT(m_isFunction); m_usesEval = info->usesEval; unsigned size = info->usedVariables.size(); for (unsigned i = 0; i < size; ++i) m_usedVariables.add(info->usedVariables[i]); size = info->writtenVariables.size(); for (unsigned i = 0; i < size; ++i) m_writtenVariables.add(info->writtenVariables[i]); } private: JSGlobalData* m_globalData; bool m_shadowsArguments : 1; bool m_usesEval : 1; bool m_needsFullActivation : 1; bool m_allowsNewDecls : 1; bool m_strictMode : 1; bool m_isFunction : 1; bool m_isFunctionBoundary : 1; bool m_isValidStrictMode : 1; int m_loopDepth; int m_switchDepth; typedef Vector<ScopeLabelInfo, 2> LabelStack; OwnPtr<LabelStack> m_labels; IdentifierSet m_declaredVariables; IdentifierSet m_usedVariables; IdentifierSet m_closedVariables; IdentifierSet m_writtenVariables; }; typedef Vector<Scope, 10> ScopeStack; struct ScopeRef { ScopeRef(ScopeStack* scopeStack, unsigned index) : m_scopeStack(scopeStack) , m_index(index) { } Scope* operator->() { return &m_scopeStack->at(m_index); } unsigned index() const { return m_index; } bool hasContainingScope() { return m_index && !m_scopeStack->at(m_index).isFunctionBoundary(); } ScopeRef containingScope() { ASSERT(hasContainingScope()); return ScopeRef(m_scopeStack, m_index - 1); } private: ScopeStack* m_scopeStack; unsigned m_index; }; struct AutoPopScopeRef : public ScopeRef { AutoPopScopeRef(JSParser* parser, ScopeRef scope) : ScopeRef(scope) , m_parser(parser) { } ~AutoPopScopeRef() { if (m_parser) m_parser->popScope(*this, false); } void setPopped() { m_parser = 0; } private: JSParser* m_parser; }; ScopeRef currentScope() { return ScopeRef(&m_scopeStack, m_scopeStack.size() - 1); } ScopeRef pushScope() { bool isFunction = false; bool isStrict = false; if (!m_scopeStack.isEmpty()) { isStrict = m_scopeStack.last().strictMode(); isFunction = m_scopeStack.last().isFunction(); } m_scopeStack.append(Scope(m_globalData, isFunction, isStrict)); return currentScope(); } bool popScopeInternal(ScopeRef& scope, bool shouldTrackClosedVariables) { ASSERT_UNUSED(scope, scope.index() == m_scopeStack.size() - 1); ASSERT(m_scopeStack.size() > 1); bool result = m_scopeStack[m_scopeStack.size() - 2].collectFreeVariables(&m_scopeStack.last(), shouldTrackClosedVariables); m_scopeStack.removeLast(); return result; } bool popScope(ScopeRef& scope, bool shouldTrackClosedVariables) { return popScopeInternal(scope, shouldTrackClosedVariables); } bool popScope(AutoPopScopeRef& scope, bool shouldTrackClosedVariables) { scope.setPopped(); return popScopeInternal(scope, shouldTrackClosedVariables); } bool declareVariable(const Identifier* ident) { unsigned i = m_scopeStack.size() - 1; ASSERT(i < m_scopeStack.size()); while (!m_scopeStack[i].allowsNewDecls()) { i--; ASSERT(i < m_scopeStack.size()); } return m_scopeStack[i].declareVariable(ident); } void declareWrite(const Identifier* ident) { if (!m_syntaxAlreadyValidated) m_scopeStack.last().declareWrite(ident); } ScopeStack m_scopeStack; const SourceProviderCacheItem* findCachedFunctionInfo(int openBracePos) { return m_functionCache ? m_functionCache->get(openBracePos) : 0; } SourceProviderCache* m_functionCache; }; const char* jsParse(JSGlobalData* globalData, FunctionParameters* parameters, JSParserStrictness strictness, JSParserMode parserMode, const SourceCode* source) { JSParser parser(globalData->lexer, globalData, parameters, strictness == JSParseStrict, parserMode == JSParseFunctionCode, source->provider()); return parser.parseProgram(); } JSParser::JSParser(Lexer* lexer, JSGlobalData* globalData, FunctionParameters* parameters, bool inStrictContext, bool isFunction, SourceProvider* provider) : m_lexer(lexer) , m_stack(globalData->stack()) , m_error(false) , m_errorMessage("Parse error") , m_globalData(globalData) , m_allowsIn(true) , m_lastLine(0) , m_lastTokenEnd(0) , m_assignmentCount(0) , m_nonLHSCount(0) , m_syntaxAlreadyValidated(provider->isValid()) , m_statementDepth(0) , m_nonTrivialExpressionCount(0) , m_lastIdentifier(0) , m_functionCache(m_lexer->sourceProvider()->cache()) { ScopeRef scope = pushScope(); if (isFunction) scope->setIsFunction(); if (inStrictContext) scope->setStrictMode(); if (parameters) { for (unsigned i = 0; i < parameters->size(); i++) scope->declareParameter(¶meters->at(i)); } next(); m_lexer->setLastLineNumber(tokenLine()); } const char* JSParser::parseProgram() { unsigned oldFunctionCacheSize = m_functionCache ? m_functionCache->byteSize() : 0; ASTBuilder context(m_globalData, m_lexer); if (m_lexer->isReparsing()) m_statementDepth--; ScopeRef scope = currentScope(); SourceElements* sourceElements = parseSourceElements<CheckForStrictMode>(context); if (!sourceElements || !consume(EOFTOK)) return m_errorMessage; IdentifierSet capturedVariables; scope->getCapturedVariables(capturedVariables); CodeFeatures features = context.features(); if (scope->strictMode()) features |= StrictModeFeature; if (scope->shadowsArguments()) features |= ShadowsArgumentsFeature; unsigned functionCacheSize = m_functionCache ? m_functionCache->byteSize() : 0; if (functionCacheSize != oldFunctionCacheSize) m_lexer->sourceProvider()->notifyCacheSizeChanged(functionCacheSize - oldFunctionCacheSize); m_globalData->parser->didFinishParsing(sourceElements, context.varDeclarations(), context.funcDeclarations(), features, m_lastLine, context.numConstants(), capturedVariables); return 0; } bool JSParser::allowAutomaticSemicolon() { return match(CLOSEBRACE) || match(EOFTOK) || m_lexer->prevTerminator(); } template <JSParser::SourceElementsMode mode, class TreeBuilder> TreeSourceElements JSParser::parseSourceElements(TreeBuilder& context) { TreeSourceElements sourceElements = context.createSourceElements(); bool seenNonDirective = false; const Identifier* directive = 0; unsigned startOffset = m_token.m_info.startOffset; bool hasSetStrict = false; while (TreeStatement statement = parseStatement(context, directive)) { if (mode == CheckForStrictMode && !seenNonDirective) { if (directive) { if (!hasSetStrict && m_globalData->propertyNames->useStrictIdentifier == *directive) { setStrictMode(); hasSetStrict = true; failIfFalse(isValidStrictMode()); m_lexer->setOffset(startOffset); next(); failIfTrue(m_error); continue; } } else seenNonDirective = true; } context.appendStatement(sourceElements, statement); } if (m_error) fail(); return sourceElements; } template <class TreeBuilder> 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 <class TreeBuilder> 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 <class TreeBuilder> TreeStatement JSParser::parseDoWhileStatement(TreeBuilder& context) { ASSERT(match(DO)); int startLine = tokenLine(); next(); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); 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 <class TreeBuilder> 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); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement); return context.createWhileStatement(expr, statement, startLine, endLine); } template <class TreeBuilder> 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 = m_token.m_data.ident; lastIdent = name; next(); bool hasInitializer = match(EQUAL); failIfFalseIfStrict(declareVariable(name)); 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 <class TreeBuilder> TreeConstDeclList JSParser::parseConstDeclarationList(TreeBuilder& context) { failIfTrue(strictMode()); TreeConstDeclList constDecls = 0; TreeConstDeclList tail = 0; do { next(); matchOrFail(IDENT); const Identifier* name = m_token.m_data.ident; next(); bool hasInitializer = match(EQUAL); declareVariable(name); 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 <class TreeBuilder> 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 (match(SEMICOLON)) goto standardForLoop; failIfFalse(declarations == 1); // 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); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); 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); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); 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); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement); return context.createForInLoop(decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); } template <class TreeBuilder> TreeStatement JSParser::parseBreakStatement(TreeBuilder& context) { ASSERT(match(BREAK)); int startCol = tokenStart(); int endCol = tokenEnd(); int startLine = tokenLine(); int endLine = tokenLine(); next(); if (autoSemiColon()) { failIfFalse(breakIsValid()); return context.createBreakStatement(startCol, endCol, startLine, endLine); } matchOrFail(IDENT); const Identifier* ident = m_token.m_data.ident; failIfFalse(getLabel(ident)); endCol = tokenEnd(); endLine = tokenLine(); next(); failIfFalse(autoSemiColon()); return context.createBreakStatement(ident, startCol, endCol, startLine, endLine); } template <class TreeBuilder> TreeStatement JSParser::parseContinueStatement(TreeBuilder& context) { ASSERT(match(CONTINUE)); int startCol = tokenStart(); int endCol = tokenEnd(); int startLine = tokenLine(); int endLine = tokenLine(); next(); if (autoSemiColon()) { failIfFalse(continueIsValid()); return context.createContinueStatement(startCol, endCol, startLine, endLine); } matchOrFail(IDENT); const Identifier* ident = m_token.m_data.ident; ScopeLabelInfo* label = getLabel(ident); failIfFalse(label); failIfFalse(label->m_isLoop); endCol = tokenEnd(); endLine = tokenLine(); next(); failIfFalse(autoSemiColon()); return context.createContinueStatement(ident, startCol, endCol, startLine, endLine); } template <class TreeBuilder> TreeStatement JSParser::parseReturnStatement(TreeBuilder& context) { ASSERT(match(RETURN)); failIfFalse(currentScope()->isFunction()); 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 <class TreeBuilder> TreeStatement JSParser::parseThrowStatement(TreeBuilder& context) { ASSERT(match(THROW)); int eStart = tokenStart(); int startLine = tokenLine(); next(); failIfTrue(autoSemiColon()); TreeExpression expr = parseExpression(context); failIfFalse(expr); int eEnd = lastTokenEnd(); int endLine = tokenLine(); failIfFalse(autoSemiColon()); return context.createThrowStatement(expr, eStart, eEnd, startLine, endLine); } template <class TreeBuilder> TreeStatement JSParser::parseWithStatement(TreeBuilder& context) { ASSERT(match(WITH)); failIfTrue(strictMode()); currentScope()->setNeedsFullActivation(); int startLine = tokenLine(); next(); consumeOrFail(OPENPAREN); int start = tokenStart(); TreeExpression expr = parseExpression(context); failIfFalse(expr); int end = lastTokenEnd(); int endLine = tokenLine(); consumeOrFail(CLOSEPAREN); const Identifier* unused = 0; TreeStatement statement = parseStatement(context, unused); failIfFalse(statement); return context.createWithStatement(expr, statement, start, end, startLine, endLine); } template <class TreeBuilder> 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); startSwitch(); TreeClauseList firstClauses = parseSwitchClauses(context); failIfTrue(m_error); TreeClause defaultClause = parseSwitchDefaultClause(context); failIfTrue(m_error); TreeClauseList secondClauses = parseSwitchClauses(context); failIfTrue(m_error); endSwitch(); consumeOrFail(CLOSEBRACE); return context.createSwitchStatement(expr, firstClauses, defaultClause, secondClauses, startLine, endLine); } template <class TreeBuilder> TreeClauseList JSParser::parseSwitchClauses(TreeBuilder& context) { if (!match(CASE)) return 0; next(); TreeExpression condition = parseExpression(context); failIfFalse(condition); consumeOrFail(COLON); TreeSourceElements statements = parseSourceElements<DontCheckForStrictMode>(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<DontCheckForStrictMode>(context); failIfFalse(statements); clause = context.createClause(condition, statements); tail = context.createClauseList(tail, clause); } return clauseList; } template <class TreeBuilder> TreeClause JSParser::parseSwitchDefaultClause(TreeBuilder& context) { if (!match(DEFAULT)) return 0; next(); consumeOrFail(COLON); TreeSourceElements statements = parseSourceElements<DontCheckForStrictMode>(context); failIfFalse(statements); return context.createClause(0, statements); } template <class TreeBuilder> 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)) { currentScope()->setNeedsFullActivation(); next(); consumeOrFail(OPENPAREN); matchOrFail(IDENT); ident = m_token.m_data.ident; next(); AutoPopScopeRef catchScope(this, pushScope()); failIfFalseIfStrict(catchScope->declareVariable(ident)); catchScope->preventNewDecls(); consumeOrFail(CLOSEPAREN); matchOrFail(OPENBRACE); int initialEvalCount = context.evalCount(); catchBlock = parseBlockStatement(context); failIfFalse(catchBlock); catchHasEval = initialEvalCount != context.evalCount(); failIfFalse(popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo)); } 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 <class TreeBuilder> 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 <class TreeBuilder> 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<DontCheckForStrictMode>(context); failIfFalse(subtree); matchOrFail(CLOSEBRACE); next(); return context.createBlockStatement(subtree, start, m_lastLine); } template <class TreeBuilder> TreeStatement JSParser::parseStatement(TreeBuilder& context, const Identifier*& directive) { DepthManager statementDepth(&m_statementDepth); m_statementDepth++; directive = 0; int nonTrivialExpressionCount = 0; failIfStackOverflow(); switch (m_token.m_type) { case OPENBRACE: return parseBlockStatement(context); case VAR: return parseVarDeclaration(context); case CONSTTOKEN: return parseConstDeclaration(context); case FUNCTION: failIfFalseIfStrict(m_statementDepth == 1); 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); case STRING: directive = m_token.m_data.ident; nonTrivialExpressionCount = m_nonTrivialExpressionCount; default: TreeStatement exprStatement = parseExpressionStatement(context); if (directive && nonTrivialExpressionCount != m_nonTrivialExpressionCount) directive = 0; return exprStatement; } } template <class TreeBuilder> TreeFormalParameterList JSParser::parseFormalParameters(TreeBuilder& context) { matchOrFail(IDENT); failIfFalseIfStrict(declareParameter(m_token.m_data.ident)); TreeFormalParameterList list = context.createFormalParameterList(*m_token.m_data.ident); TreeFormalParameterList tail = list; next(); while (match(COMMA)) { next(); matchOrFail(IDENT); const Identifier* ident = m_token.m_data.ident; failIfFalseIfStrict(declareParameter(ident)); next(); tail = context.createFormalParameterList(tail, *ident); } return list; } template <class TreeBuilder> TreeFunctionBody JSParser::parseFunctionBody(TreeBuilder& context) { if (match(CLOSEBRACE)) return context.createFunctionBody(strictMode()); DepthManager statementDepth(&m_statementDepth); m_statementDepth = 0; typename TreeBuilder::FunctionBodyBuilder bodyBuilder(m_globalData, m_lexer); failIfFalse(parseSourceElements<CheckForStrictMode>(bodyBuilder)); return context.createFunctionBody(strictMode()); } template <JSParser::FunctionRequirements requirements, bool nameIsInContainingScope, class TreeBuilder> bool JSParser::parseFunctionInfo(TreeBuilder& context, const Identifier*& name, TreeFormalParameterList& parameters, TreeFunctionBody& body, int& openBracePos, int& closeBracePos, int& bodyStartLine) { AutoPopScopeRef functionScope(this, pushScope()); functionScope->setIsFunction(); if (match(IDENT)) { name = m_token.m_data.ident; failIfTrue(*name == m_globalData->propertyNames->underscoreProto); next(); if (!nameIsInContainingScope) failIfFalseIfStrict(functionScope->declareVariable(name)); } else if (requirements == FunctionNeedsName) return false; consumeOrFail(OPENPAREN); if (!match(CLOSEPAREN)) { parameters = parseFormalParameters(context); failIfFalse(parameters); } consumeOrFail(CLOSEPAREN); matchOrFail(OPENBRACE); openBracePos = m_token.m_data.intValue; bodyStartLine = tokenLine(); if (const SourceProviderCacheItem* cachedInfo = TreeBuilder::CanUseFunctionCache ? findCachedFunctionInfo(openBracePos) : 0) { // If we know about this function already, we can use the cached info and skip the parser to the end of the function. body = context.createFunctionBody(strictMode()); functionScope->restoreFunctionInfo(cachedInfo); failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); closeBracePos = cachedInfo->closeBracePos; m_token = cachedInfo->closeBraceToken(); m_lexer->setOffset(m_token.m_info.endOffset); m_lexer->setLineNumber(m_token.m_info.line); next(); return true; } next(); body = parseFunctionBody(context); failIfFalse(body); if (functionScope->strictMode() && name) { failIfTrue(m_globalData->propertyNames->arguments == *name); failIfTrue(m_globalData->propertyNames->eval == *name); } closeBracePos = m_token.m_data.intValue; // Cache the tokenizer state and the function scope the first time the function is parsed. // Any future reparsing can then skip the function. static const int minimumFunctionLengthToCache = 64; OwnPtr<SourceProviderCacheItem> newInfo; int functionLength = closeBracePos - openBracePos; if (TreeBuilder::CanUseFunctionCache && m_functionCache && functionLength > minimumFunctionLengthToCache) { newInfo = adoptPtr(new SourceProviderCacheItem(m_token.m_info.line, closeBracePos)); functionScope->saveFunctionInfo(newInfo.get()); } failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); matchOrFail(CLOSEBRACE); if (newInfo) { unsigned approximateByteSize = newInfo->approximateByteSize(); m_functionCache->add(openBracePos, newInfo.release(), approximateByteSize); } next(); return true; } template <class TreeBuilder> 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<FunctionNeedsName, true>(context, name, parameters, body, openBracePos, closeBracePos, bodyStartLine))); failIfFalse(name); failIfFalseIfStrict(declareVariable(name)); return context.createFuncDeclStatement(name, body, parameters, openBracePos, closeBracePos, bodyStartLine, m_lastLine); } struct LabelInfo { LabelInfo(const Identifier* ident, int start, int end) : m_ident(ident) , m_start(start) , m_end(end) { } const Identifier* m_ident; int m_start; int m_end; }; template <class TreeBuilder> TreeStatement JSParser::parseExpressionOrLabelStatement(TreeBuilder& context) { /* Expression and Label statements are ambiguous at LL(1), so we have a * special case that looks for a colon as the next character in the input. */ Vector<LabelInfo> labels; do { int start = tokenStart(); int startLine = tokenLine(); if (!nextTokenIsColon()) { // If we hit this path we're making a expression statement, which // by definition can't make use of continue/break so we can just // ignore any labels we might have accumulated. TreeExpression expression = parseExpression(context); failIfFalse(expression); failIfFalse(autoSemiColon()); return context.createExprStatement(expression, startLine, m_lastLine); } const Identifier* ident = m_token.m_data.ident; int end = tokenEnd(); next(); consumeOrFail(COLON); if (!m_syntaxAlreadyValidated) { // This is O(N^2) over the current list of consecutive labels, but I // have never seen more than one label in a row in the real world. for (size_t i = 0; i < labels.size(); i++) failIfTrue(ident->impl() == labels[i].m_ident->impl()); failIfTrue(getLabel(ident)); labels.append(LabelInfo(ident, start, end)); } } while (match(IDENT)); bool isLoop = false; switch (m_token.m_type) { case FOR: case WHILE: case DO: isLoop = true; break; default: break; } const Identifier* unused = 0; if (!m_syntaxAlreadyValidated) { for (size_t i = 0; i < labels.size(); i++) pushLabel(labels[i].m_ident, isLoop); } TreeStatement statement = parseStatement(context, unused); if (!m_syntaxAlreadyValidated) { for (size_t i = 0; i < labels.size(); i++) popLabel(); } failIfFalse(statement); for (size_t i = 0; i < labels.size(); i++) { const LabelInfo& info = labels[labels.size() - i - 1]; statement = context.createLabelStatement(info.m_ident, statement, info.m_start, info.m_end); } return statement; } template <class TreeBuilder> TreeStatement JSParser::parseExpressionStatement(TreeBuilder& context) { int startLine = tokenLine(); TreeExpression expression = parseExpression(context); failIfFalse(expression); failIfFalse(autoSemiColon()); return context.createExprStatement(expression, startLine, m_lastLine); } template <class TreeBuilder> 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); const Identifier* unused = 0; TreeStatement trueBlock = parseStatement(context, unused); failIfFalse(trueBlock); if (!match(ELSE)) return context.createIfStatement(condition, trueBlock, start, end); Vector<TreeExpression> exprStack; Vector<pair<int, int> > posStack; Vector<TreeStatement> statementStack; bool trailingElse = false; do { next(); if (!match(IF)) { const Identifier* unused = 0; TreeStatement block = parseStatement(context, unused); 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); const Identifier* unused = 0; TreeStatement innerTrueBlock = parseStatement(context, unused); 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<int, int> 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<int, int> 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 <class TreeBuilder> TreeExpression JSParser::parseExpression(TreeBuilder& context) { failIfStackOverflow(); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node); if (!match(COMMA)) return node; next(); m_nonTrivialExpressionCount++; 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 <typename TreeBuilder> 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 (m_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; } m_nonTrivialExpressionCount++; hadAssignment = true; context.assignmentStackAppend(assignmentStack, lhs, start, tokenStart(), m_assignmentCount, op); start = tokenStart(); m_assignmentCount++; next(); if (strictMode() && m_lastIdentifier && context.isResolve(lhs)) { failIfTrueIfStrict(m_globalData->propertyNames->eval == *m_lastIdentifier); failIfTrueIfStrict(m_globalData->propertyNames->arguments == *m_lastIdentifier); declareWrite(m_lastIdentifier); m_lastIdentifier = 0; } lhs = parseConditionalExpression(context); failIfFalse(lhs); if (initialNonLHSCount != m_nonLHSCount) break; } end: if (hadAssignment) m_nonLHSCount++; if (!TreeBuilder::CreatesAST) return lhs; while (assignmentStack) lhs = context.createAssignment(assignmentStack, lhs, initialAssignmentCount, m_assignmentCount, lastTokenEnd()); return lhs; } template <class TreeBuilder> TreeExpression JSParser::parseConditionalExpression(TreeBuilder& context) { TreeExpression cond = parseBinaryExpression(context); failIfFalse(cond); if (!match(QUESTION)) return cond; m_nonTrivialExpressionCount++; 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 <class TreeBuilder> TreeExpression JSParser::parseBinaryExpression(TreeBuilder& context) { int operandStackDepth = 0; int operatorStackDepth = 0; typename TreeBuilder::BinaryExprContext binaryExprContext(context); 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(m_token.m_type); if (!precedence) break; m_nonTrivialExpressionCount++; m_nonLHSCount++; int operatorToken = m_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 <bool complete, class TreeBuilder> TreeProperty JSParser::parseProperty(TreeBuilder& context) { bool wasIdent = false; switch (m_token.m_type) { namedProperty: case IDENT: wasIdent = true; case STRING: { const Identifier* ident = m_token.m_data.ident; next(Lexer::IgnoreReservedWords); if (match(COLON)) { next(); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node); return context.template createProperty<complete>(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<FunctionNeedsName, false>(context, accessorName, parameters, body, openBracePos, closeBracePos, bodyStartLine))); return context.template createGetterOrSetterProperty<complete>(type, accessorName, parameters, body, openBracePos, closeBracePos, bodyStartLine, m_lastLine); } case NUMBER: { double propertyName = m_token.m_data.doubleValue; next(); consumeOrFail(COLON); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node); return context.template createProperty<complete>(m_globalData, propertyName, node, PropertyNode::Constant); } default: failIfFalse(m_token.m_type & KeywordTokenFlag); goto namedProperty; } } template <class TreeBuilder> TreeExpression JSParser::parseObjectLiteral(TreeBuilder& context) { int startOffset = m_token.m_data.intValue; consumeOrFail(OPENBRACE); if (match(CLOSEBRACE)) { next(); return context.createObjectLiteral(); } TreeProperty property = parseProperty<false>(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<false>(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 <class TreeBuilder> TreeExpression JSParser::parseStrictObjectLiteral(TreeBuilder& context) { consumeOrFail(OPENBRACE); if (match(CLOSEBRACE)) { next(); return context.createObjectLiteral(); } TreeProperty property = parseProperty<true>(context); failIfFalse(property); typedef HashMap<RefPtr<StringImpl>, unsigned, IdentifierRepHash> ObjectValidationMap; ObjectValidationMap objectValidator; // Add the first property if (!m_syntaxAlreadyValidated) objectValidator.add(context.getName(property).impl(), 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<true>(context); failIfFalse(property); if (!m_syntaxAlreadyValidated) { std::pair<ObjectValidationMap::iterator, bool> propertyEntryIter = objectValidator.add(context.getName(property).impl(), context.getType(property)); if (!propertyEntryIter.second) { failIfTrue(strictMode()); 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 <class TreeBuilder> 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 <class TreeBuilder> TreeExpression JSParser::parsePrimaryExpression(TreeBuilder& context) { switch (m_token.m_type) { case OPENBRACE: if (strictMode()) return parseStrictObjectLiteral(context); 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 = m_token.m_data.ident; next(); currentScope()->useVariable(ident, m_globalData->propertyNames->eval == *ident); m_lastIdentifier = ident; return context.createResolve(ident, start); } case STRING: { const Identifier* ident = m_token.m_data.ident; next(); return context.createString(ident); } case NUMBER: { double d = m_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(); TreeExpression re = context.createRegExp(*pattern, *flags, start); if (!re) { m_errorMessage = Yarr::checkSyntax(pattern->ustring()); ASSERT(m_errorMessage); fail(); } return re; } default: fail(); } } template <class TreeBuilder> 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 <class TreeBuilder> 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<FunctionNoRequirements, false>(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 (m_token.m_type) { case OPENBRACKET: { m_nonTrivialExpressionCount++; 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: { m_nonTrivialExpressionCount++; 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: { m_nonTrivialExpressionCount++; int expressionEnd = lastTokenEnd(); next(Lexer::IgnoreReservedWords); matchOrFail(IDENT); base = context.createDotAccess(base, *m_token.m_data.ident, expressionStart, expressionEnd, tokenEnd()); next(); break; } default: goto endMemberExpression; } } endMemberExpression: while (newCount--) base = context.createNewExpr(base, start, lastTokenEnd()); return base; } template <class TreeBuilder> TreeExpression JSParser::parseUnaryExpression(TreeBuilder& context) { typename TreeBuilder::UnaryExprContext unaryExprContext(context); AllowInOverride allowInOverride(this); int tokenStackDepth = 0; bool modifiesExpr = false; bool requiresLExpr = false; while (isUnaryOp(m_token.m_type)) { if (strictMode()) { switch (m_token.m_type) { case PLUSPLUS: case MINUSMINUS: case AUTOPLUSPLUS: case AUTOMINUSMINUS: failIfTrue(requiresLExpr); modifiesExpr = true; requiresLExpr = true; break; case DELETETOKEN: failIfTrue(requiresLExpr); requiresLExpr = true; break; default: failIfTrue(requiresLExpr); break; } } m_nonLHSCount++; context.appendUnaryToken(tokenStackDepth, m_token.m_type, tokenStart()); next(); m_nonTrivialExpressionCount++; } int subExprStart = tokenStart(); TreeExpression expr = parseMemberExpression(context); failIfFalse(expr); bool isEvalOrArguments = false; if (strictMode() && !m_syntaxAlreadyValidated) { if (context.isResolve(expr)) { isEvalOrArguments = *m_lastIdentifier == m_globalData->propertyNames->eval || *m_lastIdentifier == m_globalData->propertyNames->arguments; } } failIfTrueIfStrict(isEvalOrArguments && modifiesExpr); switch (m_token.m_type) { case PLUSPLUS: m_nonTrivialExpressionCount++; m_nonLHSCount++; expr = context.makePostfixNode(expr, OpPlusPlus, subExprStart, lastTokenEnd(), tokenEnd()); m_assignmentCount++; failIfTrueIfStrict(isEvalOrArguments); failIfTrueIfStrict(requiresLExpr); next(); break; case MINUSMINUS: m_nonTrivialExpressionCount++; m_nonLHSCount++; expr = context.makePostfixNode(expr, OpMinusMinus, subExprStart, lastTokenEnd(), tokenEnd()); m_assignmentCount++; failIfTrueIfStrict(isEvalOrArguments); failIfTrueIfStrict(requiresLExpr); next(); break; default: break; } int end = lastTokenEnd(); if (!TreeBuilder::CreatesAST && (m_syntaxAlreadyValidated || !strictMode())) 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: failIfTrueIfStrict(context.isResolve(expr)); 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; } } namespace WTF { template <> struct VectorTraits<JSC::JSParser::Scope> : SimpleClassVectorTraits { static const bool canInitializeWithMemset = false; // Not all Scope data members initialize to 0. }; }