blob: 29fe518e0fa1c164a94c08b9cfaf3e56c7684c1a [file] [log] [blame]
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
* Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef Parser_h
#define Parser_h
#include "Debugger.h"
#include "ExceptionHelpers.h"
#include "Executable.h"
#include "JSGlobalObject.h"
#include "Lexer.h"
#include "Nodes.h"
#include "ParserArena.h"
#include "ParserError.h"
#include "ParserTokens.h"
#include "SourceProvider.h"
#include "SourceProviderCache.h"
#include "SourceProviderCacheItem.h"
#include "VMStackBounds.h"
#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
#include <wtf/OwnPtr.h>
#include <wtf/RefPtr.h>
namespace JSC {
struct Scope;
}
namespace WTF {
template <> struct VectorTraits<JSC::Scope> : SimpleClassVectorTraits {
static const bool canInitializeWithMemset = false; // Not all Scope data members initialize to 0.
};
}
namespace JSC {
class ExecState;
class FunctionBodyNode;
class FunctionParameters;
class Identifier;
class VM;
class ProgramNode;
class SourceCode;
// 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
#define TreeDeconstructionPattern typename TreeBuilder::DeconstructionPattern
COMPILE_ASSERT(LastUntaggedToken < 64, LessThan64UntaggedTokens);
enum SourceElementsMode { CheckForStrictMode, DontCheckForStrictMode };
enum FunctionRequirements { FunctionNoRequirements, FunctionNeedsName };
enum DeconstructionKind {
DeconstructToVariables,
DeconstructToParameters,
DeconstructToExpressions
};
template <typename T> inline bool isEvalNode() { return false; }
template <> inline bool isEvalNode<EvalNode>() { return true; }
struct DepthManager {
DepthManager(int* depth)
: m_originalDepth(*depth)
, m_depth(depth)
{
}
~DepthManager()
{
*m_depth = m_originalDepth;
}
private:
int m_originalDepth;
int* m_depth;
};
struct ScopeLabelInfo {
ScopeLabelInfo(StringImpl* ident, bool isLoop)
: m_ident(ident)
, m_isLoop(isLoop)
{
}
StringImpl* m_ident;
bool m_isLoop;
};
struct Scope {
Scope(const VM* vm, bool isFunction, bool strictMode)
: m_vm(vm)
, 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)
{
}
Scope(const Scope& rhs)
: m_vm(rhs.m_vm)
, 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)
{
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; }
void declareCallee(const Identifier* ident)
{
m_declaredVariables.add(ident->string().impl());
}
bool declareVariable(const Identifier* ident)
{
bool isValidStrictMode = m_vm->propertyNames->eval != *ident && m_vm->propertyNames->arguments != *ident;
m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
m_declaredVariables.add(ident->string().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_vm->propertyNames->arguments == *ident;
bool isValidStrictMode = m_declaredVariables.add(ident->string().impl()).isNewEntry && m_vm->propertyNames->eval != *ident && !isArguments;
m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
m_declaredParameters.add(ident->string().impl());
if (isArguments)
m_shadowsArguments = true;
return isValidStrictMode;
}
enum BindingResult {
BindingFailed,
StrictBindingFailed,
BindingSucceeded
};
BindingResult declareBoundParameter(const Identifier* ident)
{
bool isArguments = m_vm->propertyNames->arguments == *ident;
bool newEntry = m_declaredVariables.add(ident->string().impl()).isNewEntry;
bool isValidStrictMode = newEntry && m_vm->propertyNames->eval != *ident && !isArguments;
m_isValidStrictMode = m_isValidStrictMode && isValidStrictMode;
if (isArguments)
m_shadowsArguments = true;
if (!newEntry)
return BindingFailed;
return isValidStrictMode ? BindingSucceeded : StrictBindingFailed;
}
void useVariable(const Identifier* ident, bool isEval)
{
m_usesEval |= isEval;
m_usedVariables.add(ident->string().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 getCapturedVariables(IdentifierSet& capturedVariables, bool& modifiedParameter)
{
if (m_needsFullActivation || m_usesEval) {
modifiedParameter = true;
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);
}
modifiedParameter = false;
if (m_declaredParameters.size()) {
IdentifierSet::iterator end = m_writtenVariables.end();
for (IdentifierSet::iterator ptr = m_writtenVariables.begin(); ptr != end; ++ptr) {
if (!m_declaredParameters.contains(*ptr))
continue;
modifiedParameter = true;
break;
}
}
}
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);
}
}
void fillParametersForSourceProviderCache(SourceProviderCacheItemCreationParameters& parameters)
{
ASSERT(m_isFunction);
parameters.usesEval = m_usesEval;
parameters.strictMode = m_strictMode;
parameters.needsFullActivation = m_needsFullActivation;
copyCapturedVariablesToVector(m_writtenVariables, parameters.writtenVariables);
copyCapturedVariablesToVector(m_usedVariables, parameters.usedVariables);
}
void restoreFromSourceProviderCache(const SourceProviderCacheItem* info)
{
ASSERT(m_isFunction);
m_usesEval = info->usesEval;
m_strictMode = info->strictMode;
m_needsFullActivation = info->needsFullActivation;
for (unsigned i = 0; i < info->usedVariablesCount; ++i)
m_usedVariables.add(info->usedVariables()[i]);
for (unsigned i = 0; i < info->writtenVariablesCount; ++i)
m_writtenVariables.add(info->writtenVariables()[i]);
}
private:
const VM* m_vm;
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_declaredParameters;
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;
};
template <typename LexerType>
class Parser {
WTF_MAKE_NONCOPYABLE(Parser);
WTF_MAKE_FAST_ALLOCATED;
public:
Parser(VM*, const SourceCode&, FunctionParameters*, const Identifier&, JSParserStrictness, JSParserMode);
~Parser();
template <class ParsedNode>
PassRefPtr<ParsedNode> parse(ParserError&);
private:
struct AllowInOverride {
AllowInOverride(Parser* parser)
: m_parser(parser)
, m_oldAllowsIn(parser->m_allowsIn)
{
parser->m_allowsIn = true;
}
~AllowInOverride()
{
m_parser->m_allowsIn = m_oldAllowsIn;
}
Parser* m_parser;
bool m_oldAllowsIn;
};
struct AutoPopScopeRef : public ScopeRef {
AutoPopScopeRef(Parser* parser, ScopeRef scope)
: ScopeRef(scope)
, m_parser(parser)
{
}
~AutoPopScopeRef()
{
if (m_parser)
m_parser->popScope(*this, false);
}
void setPopped()
{
m_parser = 0;
}
private:
Parser* 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_vm, 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 || strictMode())
m_scopeStack.last().declareWrite(ident);
}
ScopeStack m_scopeStack;
const SourceProviderCacheItem* findCachedFunctionInfo(int openBracePos)
{
return m_functionCache ? m_functionCache->get(openBracePos) : 0;
}
Parser();
String parseInner();
void didFinishParsing(SourceElements*, ParserArenaData<DeclarationStacks::VarStack>*,
ParserArenaData<DeclarationStacks::FunctionStack>*, CodeFeatures, int, IdentifierSet&);
// Used to determine type of error to report.
bool isFunctionBodyNode(ScopeNode*) { return false; }
bool isFunctionBodyNode(FunctionBodyNode*) { return true; }
ALWAYS_INLINE void next(unsigned lexerFlags = 0)
{
int lastLine = m_token.m_location.line;
int lastTokenEnd = m_token.m_location.endOffset;
int lastTokenLineStart = m_token.m_location.lineStartOffset;
m_lastTokenEndPosition = JSTextPosition(lastLine, lastTokenEnd, lastTokenLineStart);
m_lexer->setLastLineNumber(lastLine);
m_token.m_type = m_lexer->lex(&m_token, lexerFlags, strictMode());
}
ALWAYS_INLINE void nextExpectIdentifier(unsigned lexerFlags = 0)
{
int lastLine = m_token.m_location.line;
int lastTokenEnd = m_token.m_location.endOffset;
int lastTokenLineStart = m_token.m_location.lineStartOffset;
m_lastTokenEndPosition = JSTextPosition(lastLine, lastTokenEnd, lastTokenLineStart);
m_lexer->setLastLineNumber(lastLine);
m_token.m_type = m_lexer->lexExpectIdentifier(&m_token, lexerFlags, strictMode());
}
ALWAYS_INLINE bool nextTokenIsColon()
{
return m_lexer->nextTokenIsColon();
}
ALWAYS_INLINE bool consume(JSTokenType expected, unsigned flags = 0)
{
bool result = m_token.m_type == expected;
if (result)
next(flags);
return result;
}
ALWAYS_INLINE String getToken() {
SourceProvider* sourceProvider = m_source->provider();
return sourceProvider->getRange(tokenStart(), tokenEndPosition().offset);
}
ALWAYS_INLINE bool match(JSTokenType expected)
{
return m_token.m_type == expected;
}
ALWAYS_INLINE bool isofToken()
{
return m_token.m_type == IDENT && *m_token.m_data.ident == m_vm->propertyNames->of;
}
ALWAYS_INLINE unsigned tokenStart()
{
return m_token.m_location.startOffset;
}
ALWAYS_INLINE const JSTextPosition& tokenStartPosition()
{
return m_token.m_startPosition;
}
ALWAYS_INLINE int tokenLine()
{
return m_token.m_location.line;
}
ALWAYS_INLINE int tokenColumn()
{
return tokenStart() - tokenLineStart();
}
ALWAYS_INLINE const JSTextPosition& tokenEndPosition()
{
return m_token.m_endPosition;
}
ALWAYS_INLINE unsigned tokenLineStart()
{
return m_token.m_location.lineStartOffset;
}
ALWAYS_INLINE const JSTokenLocation& tokenLocation()
{
return m_token.m_location;
}
const char* getTokenName(JSTokenType tok)
{
switch (tok) {
case NULLTOKEN:
return "null";
case TRUETOKEN:
return "true";
case FALSETOKEN:
return "false";
case BREAK:
return "break";
case CASE:
return "case";
case DEFAULT:
return "default";
case FOR:
return "for";
case NEW:
return "new";
case VAR:
return "var";
case CONSTTOKEN:
return "const";
case CONTINUE:
return "continue";
case FUNCTION:
return "function";
case IF:
return "if";
case THISTOKEN:
return "this";
case DO:
return "do";
case WHILE:
return "while";
case SWITCH:
return "switch";
case WITH:
return "with";
case THROW:
return "throw";
case TRY:
return "try";
case CATCH:
return "catch";
case FINALLY:
return "finally";
case DEBUGGER:
return "debugger";
case ELSE:
return "else";
case OPENBRACE:
return "{";
case CLOSEBRACE:
return "}";
case OPENPAREN:
return "(";
case CLOSEPAREN:
return ")";
case OPENBRACKET:
return "[";
case CLOSEBRACKET:
return "]";
case COMMA:
return ",";
case QUESTION:
return "?";
case SEMICOLON:
return ";";
case COLON:
return ":";
case DOT:
return ".";
case EQUAL:
return "=";
case PLUSEQUAL:
return "+=";
case MINUSEQUAL:
return "-=";
case MULTEQUAL:
return "*=";
case DIVEQUAL:
return "/=";
case LSHIFTEQUAL:
return "<<=";
case RSHIFTEQUAL:
return ">>=";
case URSHIFTEQUAL:
return ">>>=";
case ANDEQUAL:
return "&=";
case MODEQUAL:
return "%=";
case XOREQUAL:
return "^=";
case OREQUAL:
return "|=";
case AUTOPLUSPLUS:
case PLUSPLUS:
return "++";
case AUTOMINUSMINUS:
case MINUSMINUS:
return "--";
case EXCLAMATION:
return "!";
case TILDE:
return "~";
case TYPEOF:
return "typeof";
case VOIDTOKEN:
return "void";
case DELETETOKEN:
return "delete";
case OR:
return "||";
case AND:
return "&&";
case BITOR:
return "|";
case BITXOR:
return "^";
case BITAND:
return "&";
case EQEQ:
return "==";
case NE:
return "!=";
case STREQ:
return "===";
case STRNEQ:
return "!==";
case LT:
return "<";
case GT:
return ">";
case LE:
return "<=";
case GE:
return ">=";
case INSTANCEOF:
return "instanceof";
case INTOKEN:
return "in";
case LSHIFT:
return "<<";
case RSHIFT:
return ">>";
case URSHIFT:
return ">>>";
case PLUS:
return "+";
case MINUS:
return "-";
case TIMES:
return "*";
case DIVIDE:
return "/";
case MOD:
return "%";
case DOTDOTDOT:
return "...";
case RETURN:
case RESERVED_IF_STRICT:
case RESERVED:
case NUMBER:
case IDENT:
case STRING:
case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK:
case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK:
case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK:
case UNTERMINATED_STRING_LITERAL_ERRORTOK:
case INVALID_IDENTIFIER_ESCAPE_ERRORTOK:
case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
case INVALID_NUMERIC_LITERAL_ERRORTOK:
case INVALID_OCTAL_NUMBER_ERRORTOK:
case INVALID_STRING_LITERAL_ERRORTOK:
case ERRORTOK:
case EOFTOK:
return 0;
case LastUntaggedToken:
break;
}
RELEASE_ASSERT_NOT_REACHED();
return "internal error";
}
ALWAYS_INLINE void updateErrorMessageSpecialCase(JSTokenType expectedToken)
{
switch (expectedToken) {
case RESERVED_IF_STRICT:
m_errorMessage = "Use of reserved word '" + getToken() + "' in strict mode";
return;
case RESERVED:
m_errorMessage = "Use of reserved word '" + getToken() + '\'';
return;
case NUMBER:
m_errorMessage = "Unexpected number '" + getToken() + '\'';
return;
case IDENT:
m_errorMessage = "Expected an identifier but found '" + getToken() + "' instead";
return;
case STRING:
m_errorMessage = "Unexpected string " + getToken();
return;
case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK:
case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
m_errorMessage = "Incomplete unicode escape in identifier: '" + getToken() + '\'';
return;
case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK:
m_errorMessage = "Unterminated multiline comment";
return;
case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK:
m_errorMessage = "Unterminated numeric literal '" + getToken() + '\'';
return;
case UNTERMINATED_STRING_LITERAL_ERRORTOK:
m_errorMessage = "Unterminated string literal '" + getToken() + '\'';
return;
case INVALID_IDENTIFIER_ESCAPE_ERRORTOK:
m_errorMessage = "Invalid escape in identifier: '" + getToken() + '\'';
return;
case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK:
m_errorMessage = "Invalid unicode escape in identifier: '" + getToken() + '\'';
return;
case INVALID_NUMERIC_LITERAL_ERRORTOK:
m_errorMessage = "Invalid numeric literal: '" + getToken() + '\'';
return;
case INVALID_OCTAL_NUMBER_ERRORTOK:
m_errorMessage = "Invalid use of octal: '" + getToken() + '\'';
return;
case INVALID_STRING_LITERAL_ERRORTOK:
m_errorMessage = "Invalid string literal: '" + getToken() + '\'';
return;
case ERRORTOK:
m_errorMessage = "Unrecognized token '" + getToken() + '\'';
return;
case EOFTOK:
m_errorMessage = ASCIILiteral("Unexpected EOF");
return;
case RETURN:
m_errorMessage = ASCIILiteral("Return statements are only valid inside functions");
return;
case DOTDOTDOT:
m_errorMessage = ASCIILiteral("Spread operator is not supported in this context");
return;
default:
RELEASE_ASSERT_NOT_REACHED();
m_errorMessage = ASCIILiteral("internal error");
return;
}
}
NEVER_INLINE void updateErrorMessage()
{
const char* name = getTokenName(m_token.m_type);
if (!name)
updateErrorMessageSpecialCase(m_token.m_type);
else
m_errorMessage = String::format("Unexpected token '%s'", name);
ASSERT(!m_errorMessage.isNull());
}
NEVER_INLINE void updateErrorMessage(JSTokenType expectedToken)
{
const char* name = getTokenName(expectedToken);
if (name)
m_errorMessage = String::format("Expected token '%s'", name);
else {
if (!getTokenName(m_token.m_type))
updateErrorMessageSpecialCase(m_token.m_type);
else
updateErrorMessageSpecialCase(expectedToken);
}
ASSERT(!m_errorMessage.isNull());
}
NEVER_INLINE void updateErrorWithNameAndMessage(const char* beforeMsg, String name, const char* afterMsg)
{
m_errorMessage = makeString(beforeMsg, " '", name, "' ", afterMsg);
}
NEVER_INLINE void updateErrorMessage(const char* msg)
{
ASSERT(msg);
m_errorMessage = String(msg);
ASSERT(!m_errorMessage.isNull());
}
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); }
Scope::BindingResult declareBoundParameter(const Identifier* ident) { return currentScope()->declareBoundParameter(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;
}
template <SourceElementsMode mode, class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&);
template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0);
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&);
enum SpreadMode { AllowSpread, DontAllowSpread };
template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&, SpreadMode);
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, TreeDeconstructionPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd);
template <class TreeBuilder> ALWAYS_INLINE TreeConstDeclList parseConstDeclarationList(TreeBuilder&);
template <DeconstructionKind, class TreeBuilder> ALWAYS_INLINE TreeDeconstructionPattern createBindingPattern(TreeBuilder&, const Identifier&, int depth);
template <DeconstructionKind, class TreeBuilder> TreeDeconstructionPattern parseDeconstructionPattern(TreeBuilder&, int depth = 0);
template <FunctionRequirements, bool nameIsInContainingScope, class TreeBuilder> bool parseFunctionInfo(TreeBuilder&, const Identifier*&, TreeFormalParameterList&, TreeFunctionBody&, unsigned& openBraceOffset, unsigned& closeBraceOffset, int& bodyStartLine, unsigned& bodyStartColumn);
ALWAYS_INLINE int isBinaryOperator(JSTokenType);
bool allowAutomaticSemicolon();
bool autoSemiColon()
{
if (m_token.m_type == SEMICOLON) {
next();
return true;
}
return allowAutomaticSemicolon();
}
bool canRecurse()
{
return m_stack.isSafeToRecurse();
}
const JSTextPosition& lastTokenEndPosition() const
{
return m_lastTokenEndPosition;
}
bool hasError() const
{
return !m_errorMessage.isNull();
}
VM* m_vm;
const SourceCode* m_source;
ParserArena* m_arena;
OwnPtr<LexerType> m_lexer;
VMStackBounds m_stack;
bool m_hasStackOverflow;
String m_errorMessage;
JSToken m_token;
bool m_allowsIn;
JSTextPosition m_lastTokenEndPosition;
int m_assignmentCount;
int m_nonLHSCount;
bool m_syntaxAlreadyValidated;
int m_statementDepth;
int m_nonTrivialExpressionCount;
const Identifier* m_lastIdentifier;
RefPtr<SourceProviderCache> m_functionCache;
SourceElements* m_sourceElements;
ParserArenaData<DeclarationStacks::VarStack>* m_varDeclarations;
ParserArenaData<DeclarationStacks::FunctionStack>* m_funcDeclarations;
IdentifierSet m_capturedVariables;
CodeFeatures m_features;
int m_numConstants;
struct DepthManager {
DepthManager(int* depth)
: m_originalDepth(*depth)
, m_depth(depth)
{
}
~DepthManager()
{
*m_depth = m_originalDepth;
}
private:
int m_originalDepth;
int* m_depth;
};
};
template <typename LexerType>
template <class ParsedNode>
PassRefPtr<ParsedNode> Parser<LexerType>::parse(ParserError& error)
{
int errLine;
String errMsg;
if (ParsedNode::scopeIsFunction)
m_lexer->setIsReparsing();
m_sourceElements = 0;
errLine = -1;
errMsg = String();
JSTokenLocation startLocation(tokenLocation());
unsigned startColumn = m_source->startColumn();
String parseError = parseInner();
int lineNumber = m_lexer->lineNumber();
bool lexError = m_lexer->sawError();
String lexErrorMessage = lexError ? m_lexer->getErrorMessage() : String();
ASSERT(lexErrorMessage.isNull() != lexError);
m_lexer->clear();
if (!parseError.isNull() || lexError) {
errLine = lineNumber;
errMsg = !lexErrorMessage.isNull() ? lexErrorMessage : parseError;
m_sourceElements = 0;
}
RefPtr<ParsedNode> result;
if (m_sourceElements) {
JSTokenLocation endLocation;
endLocation.line = m_lexer->lastLineNumber();
endLocation.lineStartOffset = m_lexer->currentLineStartOffset();
endLocation.startOffset = m_lexer->currentOffset();
result = ParsedNode::create(m_vm,
startLocation,
endLocation,
startColumn,
m_sourceElements,
m_varDeclarations ? &m_varDeclarations->data : 0,
m_funcDeclarations ? &m_funcDeclarations->data : 0,
m_capturedVariables,
*m_source,
m_features,
m_numConstants);
result->setLoc(m_source->firstLine(), m_lastTokenEndPosition.line, m_lexer->currentOffset(), m_lexer->currentLineStartOffset());
} else {
// We can never see a syntax error when reparsing a function, since we should have
// reported the error when parsing the containing program or eval code. So if we're
// parsing a function body node, we assume that what actually happened here is that
// we ran out of stack while parsing. If we see an error while parsing eval or program
// code we assume that it was a syntax error since running out of stack is much less
// likely, and we are currently unable to distinguish between the two cases.
if (isFunctionBodyNode(static_cast<ParsedNode*>(0)) || m_hasStackOverflow)
error = ParserError(ParserError::StackOverflow, ParserError::SyntaxErrorNone, m_token);
else {
ParserError::SyntaxErrorType errorType = ParserError::SyntaxErrorIrrecoverable;
if (m_token.m_type == EOFTOK)
errorType = ParserError::SyntaxErrorRecoverable;
else if (m_token.m_type & UnterminatedErrorTokenFlag)
errorType = ParserError::SyntaxErrorUnterminatedLiteral;
if (isEvalNode<ParsedNode>())
error = ParserError(ParserError::EvalError, errorType, m_token, errMsg, errLine);
else
error = ParserError(ParserError::SyntaxError, errorType, m_token, errMsg, errLine);
}
}
m_arena->reset();
return result.release();
}
template <class ParsedNode>
PassRefPtr<ParsedNode> parse(VM* vm, const SourceCode& source, FunctionParameters* parameters, const Identifier& name, JSParserStrictness strictness, JSParserMode parserMode, ParserError& error)
{
SamplingRegion samplingRegion("Parsing");
ASSERT(!source.provider()->source().isNull());
if (source.provider()->source().is8Bit()) {
Parser<Lexer<LChar>> parser(vm, source, parameters, name, strictness, parserMode);
return parser.parse<ParsedNode>(error);
}
Parser<Lexer<UChar>> parser(vm, source, parameters, name, strictness, parserMode);
return parser.parse<ParsedNode>(error);
}
} // namespace
#endif