X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174..ed1e77d3adeb83d26fd1dfb16dd84cabdcefd250:/parser/Parser.cpp?ds=sidebyside diff --git a/parser/Parser.cpp b/parser/Parser.cpp index d88a9a8..be88dd2 100644 --- a/parser/Parser.cpp +++ b/parser/Parser.cpp @@ -1,7 +1,7 @@ /* * 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 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 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 @@ -26,53 +26,228 @@ #include "ASTBuilder.h" #include "CodeBlock.h" #include "Debugger.h" -#include "JSGlobalData.h" +#include "JSCJSValueInlines.h" #include "Lexer.h" -#include "NodeInfo.h" +#include "JSCInlines.h" #include "SourceProvider.h" +#include "VM.h" #include #include -#include +#include #include + +#define updateErrorMessage(shouldPrintToken, ...) do {\ + propagateError(); \ + logError(shouldPrintToken, __VA_ARGS__); \ +} while (0) + +#define propagateError() do { if (hasError()) return 0; } while (0) +#define internalFailWithMessage(shouldPrintToken, ...) do { updateErrorMessage(shouldPrintToken, __VA_ARGS__); return 0; } while (0) +#define handleErrorToken() do { if (m_token.m_type == EOFTOK || m_token.m_type & ErrorTokenFlag) { failDueToUnexpectedToken(); } } while (0) +#define failWithMessage(...) do { { handleErrorToken(); updateErrorMessage(true, __VA_ARGS__); } return 0; } while (0) +#define failWithStackOverflow() do { updateErrorMessage(false, "Stack exhausted"); m_hasStackOverflow = true; return 0; } while (0) +#define failIfFalse(cond, ...) do { if (!(cond)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define failIfTrue(cond, ...) do { if (cond) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define failIfTrueIfStrict(cond, ...) do { if ((cond) && strictMode()) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define failIfFalseIfStrict(cond, ...) do { if ((!(cond)) && strictMode()) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define consumeOrFail(tokenType, ...) do { if (!consume(tokenType)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define consumeOrFailWithFlags(tokenType, flags, ...) do { if (!consume(tokenType, flags)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define matchOrFail(tokenType, ...) do { if (!match(tokenType)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) +#define failIfStackOverflow() do { if (!canRecurse()) failWithStackOverflow(); } while (0) +#define semanticFail(...) do { internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define semanticFailIfTrue(cond, ...) do { if (cond) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define semanticFailIfFalse(cond, ...) do { if (!(cond)) internalFailWithMessage(false, __VA_ARGS__); } while (0) +#define regexFail(failure) do { setErrorMessage(failure); return 0; } while (0) +#define failDueToUnexpectedToken() do {\ + logError(true);\ + return 0;\ +} while (0) + +#define handleProductionOrFail(token, tokenString, operation, production) do {\ + consumeOrFail(token, "Expected '", tokenString, "' to ", operation, " a ", production);\ +} while (0) + +#define semanticFailureDueToKeyword(...) do { \ + if (strictMode() && m_token.m_type == RESERVED_IF_STRICT) \ + semanticFail("Cannot use the reserved word '", getToken(), "' as a ", __VA_ARGS__, " in strict mode"); \ + if (m_token.m_type == RESERVED || m_token.m_type == RESERVED_IF_STRICT) \ + semanticFail("Cannot use the reserved word '", getToken(), "' as a ", __VA_ARGS__); \ + if (m_token.m_type & KeywordTokenFlag) \ + semanticFail("Cannot use the keyword '", getToken(), "' as a ", __VA_ARGS__); \ +} while (0) + using namespace std; namespace JSC { template -Parser::Parser(JSGlobalData* globalData, const SourceCode& source, FunctionParameters* parameters, JSParserStrictness strictness, JSParserMode parserMode) - : m_globalData(globalData) +void Parser::logError(bool) +{ + if (hasError()) + return; + StringPrintStream stream; + printUnexpectedTokenText(stream); + setErrorMessage(stream.toString()); +} + +template template +void Parser::logError(bool shouldPrintToken, const A& value1) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, "."); + setErrorMessage(stream.toString()); +} + +template template +void Parser::logError(bool shouldPrintToken, const A& value1, const B& value2) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, "."); + setErrorMessage(stream.toString()); +} + +template template +void Parser::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, "."); + setErrorMessage(stream.toString()); +} + +template template +void Parser::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, "."); + setErrorMessage(stream.toString()); +} + +template template +void Parser::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4, const E& value5) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, value5, "."); + setErrorMessage(stream.toString()); +} + +template template +void Parser::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4, const E& value5, const F& value6) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, value5, value6, "."); + setErrorMessage(stream.toString()); +} + +template template +void Parser::logError(bool shouldPrintToken, const A& value1, const B& value2, const C& value3, const D& value4, const E& value5, const F& value6, const G& value7) +{ + if (hasError()) + return; + StringPrintStream stream; + if (shouldPrintToken) { + printUnexpectedTokenText(stream); + stream.print(". "); + } + stream.print(value1, value2, value3, value4, value5, value6, value7, "."); + setErrorMessage(stream.toString()); +} + +template +Parser::Parser( + VM* vm, const SourceCode& source, FunctionParameters* parameters, + const Identifier& name, JSParserBuiltinMode builtinMode, + JSParserStrictMode strictMode, JSParserCodeType codeType, + ConstructorKind defaultConstructorKind, ThisTDZMode thisTDZMode) + : m_vm(vm) , m_source(&source) - , m_stack(wtfThreadData().stack()) - , m_error(false) - , m_errorMessage("Parse error") + , m_hasStackOverflow(false) , m_allowsIn(true) - , m_lastLine(0) - , m_lastTokenEnd(0) , m_assignmentCount(0) , m_nonLHSCount(0) , m_syntaxAlreadyValidated(source.provider()->isValid()) , m_statementDepth(0) , m_nonTrivialExpressionCount(0) , m_lastIdentifier(0) + , m_lastFunctionName(nullptr) , m_sourceElements(0) + , m_parsingBuiltin(builtinMode == JSParserBuiltinMode::Builtin) + , m_defaultConstructorKind(defaultConstructorKind) + , m_thisTDZMode(thisTDZMode) { - m_lexer = adoptPtr(new LexerType(globalData)); - m_arena = m_globalData->parserArena.get(); - m_lexer->setCode(source, m_arena); - - m_functionCache = source.provider()->cache(); + m_lexer = std::make_unique(vm, builtinMode); + m_lexer->setCode(source, &m_parserArena); + m_token.m_location.line = source.firstLine(); + m_token.m_location.startOffset = source.startOffset(); + m_token.m_location.endOffset = source.startOffset(); + m_token.m_location.lineStartOffset = source.startOffset(); + m_functionCache = vm->addSourceProviderCache(source.provider()); ScopeRef scope = pushScope(); - if (parserMode == JSParseFunctionCode) + if (codeType == JSParserCodeType::Function) scope->setIsFunction(); - if (strictness == JSParseStrict) + if (strictMode == JSParserStrictMode::Strict) scope->setStrictMode(); if (parameters) { - for (unsigned i = 0; i < parameters->size(); i++) - scope->declareParameter(¶meters->at(i)); + bool hadBindingParameters = false; + for (unsigned i = 0; i < parameters->size(); i++) { + auto parameter = parameters->at(i); + if (!parameter->isBindingNode()) { + hadBindingParameters = true; + continue; + } + scope->declareParameter(&static_cast(parameter)->boundProperty()); + } + if (hadBindingParameters) { + Vector boundParameterNames; + for (unsigned i = 0; i < parameters->size(); i++) { + auto parameter = parameters->at(i); + if (parameter->isBindingNode()) + continue; + parameter->collectBoundIdentifiers(boundParameterNames); + } + for (auto& boundParameterName : boundParameterNames) + scope->declareVariable(&boundParameterName); + } } + if (!name.isNull()) + scope->declareCallee(&name); next(); - m_lexer->setLastLineNumber(tokenLine()); } template @@ -81,46 +256,83 @@ Parser::~Parser() } template -UString Parser::parseInner() +String Parser::parseInner() { - UString parseError = UString(); + String parseError = String(); - unsigned oldFunctionCacheSize = m_functionCache ? m_functionCache->byteSize() : 0; - ASTBuilder context(const_cast(m_globalData), const_cast(m_source)); + ASTBuilder context(const_cast(m_vm), m_parserArena, const_cast(m_source)); if (m_lexer->isReparsing()) m_statementDepth--; ScopeRef scope = currentScope(); - SourceElements* sourceElements = parseSourceElements(context); - if (!sourceElements || !consume(EOFTOK)) - parseError = m_errorMessage; + SourceElements* sourceElements = parseSourceElements(context, CheckForStrictMode, StandardFunctionParseType); + if (!sourceElements || !consume(EOFTOK)) { + if (hasError()) + parseError = m_errorMessage; + else + parseError = ASCIILiteral("Parser error"); + } IdentifierSet capturedVariables; - scope->getCapturedVariables(capturedVariables); + bool modifiedParameter = false; + bool modifiedArguments = false; + scope->getCapturedVariables(capturedVariables, modifiedParameter, modifiedArguments); + 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); + if (modifiedParameter) + features |= ModifiedParameterFeature; + if (modifiedArguments) + features |= ModifiedArgumentsFeature; + Vector> closedVariables; + if (m_parsingBuiltin) { + IdentifierSet usedVariables; + scope->getUsedVariables(usedVariables); + for (const auto& variable : usedVariables) { + Identifier identifier = Identifier::fromUid(m_vm, variable.get()); + if (scope->hasDeclaredVariable(identifier)) + continue; + + if (scope->hasDeclaredParameter(identifier)) + continue; + + if (variable == m_vm->propertyNames->arguments.impl()) + continue; + + closedVariables.append(variable); + } + + if (!capturedVariables.isEmpty()) { + for (const auto& capturedVariable : capturedVariables) { + Identifier identifier = Identifier::fromUid(m_vm, capturedVariable.get()); + if (scope->hasDeclaredVariable(identifier)) + continue; + + if (scope->hasDeclaredParameter(identifier)) + continue; + RELEASE_ASSERT_NOT_REACHED(); + } + } + } didFinishParsing(sourceElements, context.varDeclarations(), context.funcDeclarations(), features, - m_lastLine, context.numConstants(), capturedVariables); + context.numConstants(), capturedVariables, WTF::move(closedVariables)); return parseError; } template -void Parser::didFinishParsing(SourceElements* sourceElements, ParserArenaData* varStack, - ParserArenaData* funcStack, CodeFeatures features, int lastLine, int numConstants, IdentifierSet& capturedVars) +void Parser::didFinishParsing(SourceElements* sourceElements, DeclarationStacks::VarStack& varStack, + DeclarationStacks::FunctionStack& funcStack, CodeFeatures features, int numConstants, IdentifierSet& capturedVars, const Vector>&& closedVariables) { m_sourceElements = sourceElements; - m_varDeclarations = varStack; - m_funcDeclarations = funcStack; + m_varDeclarations.swap(varStack); + m_funcDeclarations.swap(funcStack); m_capturedVariables.swap(capturedVars); + m_closedVariables = closedVariables; m_features = features; - m_lastLine = lastLine; m_numConstants = numConstants; } @@ -131,30 +343,54 @@ bool Parser::allowAutomaticSemicolon() } template -template TreeSourceElements Parser::parseSourceElements(TreeBuilder& context) +template TreeSourceElements Parser::parseSourceElements(TreeBuilder& context, SourceElementsMode mode, FunctionParseType functionParseType) { const unsigned lengthOfUseStrictLiteral = 12; // "use strict".length TreeSourceElements sourceElements = context.createSourceElements(); bool seenNonDirective = false; const Identifier* directive = 0; unsigned directiveLiteralLength = 0; - unsigned startOffset = m_token.m_info.startOffset; - unsigned oldLastLineNumber = m_lexer->lastLineNumber(); - unsigned oldLineNumber = m_lexer->lineNumber(); + auto savePoint = createSavePoint(); bool hasSetStrict = false; - while (TreeStatement statement = parseStatement(context, directive, &directiveLiteralLength)) { + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (match(ARROWFUNCTION)) { + TreeStatement arrowfunctionStatement = parseArrowFunctionSingleExpressionBody(context, functionParseType); + + if (arrowfunctionStatement) { + context.setEndOffset(arrowfunctionStatement, m_lastTokenEndPosition.offset); + context.appendStatement(sourceElements, arrowfunctionStatement); + } + + propagateError(); + return sourceElements; + } +#else + UNUSED_PARAM(functionParseType); +#endif + + while (TreeStatement statement = parseStatementListItem(context, directive, &directiveLiteralLength)) { if (mode == CheckForStrictMode && !seenNonDirective) { if (directive) { // "use strict" must be the exact literal without escape sequences or line continuation. - if (!hasSetStrict && directiveLiteralLength == lengthOfUseStrictLiteral && m_globalData->propertyNames->useStrictIdentifier == *directive) { + if (!hasSetStrict && directiveLiteralLength == lengthOfUseStrictLiteral && m_vm->propertyNames->useStrictIdentifier == *directive) { setStrictMode(); hasSetStrict = true; - failIfFalse(isValidStrictMode()); - m_lexer->setOffset(startOffset); - next(); - m_lexer->setLastLineNumber(oldLastLineNumber); - m_lexer->setLineNumber(oldLineNumber); - failIfTrue(m_error); + if (!isValidStrictMode()) { + if (m_lastFunctionName) { + if (m_vm->propertyNames->arguments == *m_lastFunctionName) + semanticFail("Cannot name a function 'arguments' in strict mode"); + if (m_vm->propertyNames->eval == *m_lastFunctionName) + semanticFail("Cannot name a function 'eval' in strict mode"); + } + if (hasDeclaredVariable(m_vm->propertyNames->arguments)) + semanticFail("Cannot declare a variable named 'arguments' in strict mode"); + if (hasDeclaredVariable(m_vm->propertyNames->eval)) + semanticFail("Cannot declare a variable named 'eval' in strict mode"); + semanticFailIfFalse(isValidStrictMode(), "Invalid parameters or function name in strict mode"); + } + restoreSavePoint(savePoint); + propagateError(); continue; } } else @@ -162,40 +398,65 @@ template TreeSourceElements Parser< } context.appendStatement(sourceElements, statement); } - - if (m_error) - fail(); + + propagateError(); return sourceElements; } +template +template TreeStatement Parser::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength) +{ + // The grammar is documented here: + // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-statements + TreeStatement result = 0; + switch (m_token.m_type) { + case CONSTTOKEN: + result = parseConstDeclaration(context); + break; +#if ENABLE(ES6_CLASS_SYNTAX) + case CLASSTOKEN: + result = parseClassDeclaration(context); + break; +#endif + default: + // FIXME: This needs to consider 'let' in bug: + // https://bugs.webkit.org/show_bug.cgi?id=142944 + result = parseStatement(context, directive, directiveLiteralLength); + break; + } + + return result; +} template template TreeStatement Parser::parseVarDeclaration(TreeBuilder& context) { ASSERT(match(VAR)); + JSTokenLocation location(tokenLocation()); int start = tokenLine(); int end = 0; int scratch; - const Identifier* scratch1 = 0; + TreeDestructuringPattern scratch1 = 0; TreeExpression scratch2 = 0; - int scratch3 = 0; - TreeExpression varDecls = parseVarDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3); - failIfTrue(m_error); - failIfFalse(autoSemiColon()); + JSTextPosition scratch3; + TreeExpression varDecls = parseVarDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3, VarDeclarationContext); + propagateError(); + failIfFalse(autoSemiColon(), "Expected ';' after var declaration"); - return context.createVarStatement(m_lexer->lastLineNumber(), varDecls, start, end); + return context.createVarStatement(location, varDecls, start, end); } template template TreeStatement Parser::parseConstDeclaration(TreeBuilder& context) { ASSERT(match(CONSTTOKEN)); + JSTokenLocation location(tokenLocation()); int start = tokenLine(); int end = 0; TreeConstDeclList constDecls = parseConstDeclarationList(context); - failIfTrue(m_error); - failIfFalse(autoSemiColon()); + propagateError(); + failIfFalse(autoSemiColon(), "Expected ';' after const declaration"); - return context.createConstStatement(m_lexer->lastLineNumber(), constDecls, start, end); + return context.createConstStatement(location, constDecls, start, end); } template @@ -208,94 +469,376 @@ template TreeStatement Parser::parseDoWhileStatem startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); + failIfFalse(statement, "Expected a statement following 'do'"); int endLine = tokenLine(); - consumeOrFail(WHILE); - consumeOrFail(OPENPAREN); + JSTokenLocation location(tokenLocation()); + handleProductionOrFail(WHILE, "while", "end", "do-while loop"); + handleProductionOrFail(OPENPAREN, "(", "start", "do-while loop condition"); + semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a do-while loop condition"); TreeExpression expr = parseExpression(context); - failIfFalse(expr); - consumeOrFail(CLOSEPAREN); + failIfFalse(expr, "Unable to parse do-while loop condition"); + handleProductionOrFail(CLOSEPAREN, ")", "end", "do-while loop condition"); if (match(SEMICOLON)) next(); // Always performs automatic semicolon insertion. - return context.createDoWhileStatement(m_lexer->lastLineNumber(), statement, expr, startLine, endLine); + return context.createDoWhileStatement(location, statement, expr, startLine, endLine); } template template TreeStatement Parser::parseWhileStatement(TreeBuilder& context) { ASSERT(match(WHILE)); + JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); + + handleProductionOrFail(OPENPAREN, "(", "start", "while loop condition"); + semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a while loop condition"); TreeExpression expr = parseExpression(context); - failIfFalse(expr); + failIfFalse(expr, "Unable to parse while loop condition"); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "end", "while loop condition"); + const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); - return context.createWhileStatement(m_lexer->lastLineNumber(), expr, statement, startLine, endLine); + failIfFalse(statement, "Expected a statement as the body of a while loop"); + return context.createWhileStatement(location, expr, statement, startLine, endLine); } template -template TreeExpression Parser::parseVarDeclarationList(TreeBuilder& context, int& declarations, const Identifier*& lastIdent, TreeExpression& lastInitializer, int& identStart, int& initStart, int& initEnd) +template TreeExpression Parser::parseVarDeclarationList(TreeBuilder& context, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext declarationListContext) { - TreeExpression varDecls = 0; + TreeExpression head = 0; + TreeExpression tail = 0; + const Identifier* lastIdent; + JSToken lastIdentToken; do { - declarations++; + lastIdent = 0; + lastPattern = TreeDestructuringPattern(0); + JSTokenLocation location(tokenLocation()); next(); - matchOrFail(IDENT); + TreeExpression node = 0; + declarations++; + bool hasInitializer = false; + if (match(IDENT)) { + JSTextPosition varStart = tokenStartPosition(); + JSTokenLocation varStartLocation(tokenLocation()); + identStart = varStart; + const Identifier* name = m_token.m_data.ident; + lastIdent = name; + lastIdentToken = m_token; + next(); + hasInitializer = match(EQUAL); + failIfFalseIfStrict(declareVariable(name), "Cannot declare a variable named ", name->impl(), " in strict mode"); + context.addVar(name, (hasInitializer || (!m_allowsIn && (match(INTOKEN) || isofToken()))) ? DeclarationStacks::HasInitializer : 0); + if (hasInitializer) { + JSTextPosition varDivot = tokenStartPosition() + 1; + initStart = tokenStartPosition(); + next(TreeBuilder::DontBuildStrings); // consume '=' + TreeExpression initializer = parseAssignmentExpression(context); + initEnd = lastTokenEndPosition(); + lastInitializer = initializer; + failIfFalse(initializer, "Expected expression as the intializer for the variable '", name->impl(), "'"); + + node = context.createAssignResolve(location, *name, initializer, varStart, varDivot, lastTokenEndPosition()); + } else + node = context.createEmptyVarExpression(varStartLocation, *name); + } else { + lastIdent = 0; + auto pattern = parseDestructuringPattern(context, DestructureToVariables); + failIfFalse(pattern, "Cannot parse this destructuring pattern"); + hasInitializer = match(EQUAL); + failIfTrue(declarationListContext == VarDeclarationContext && !hasInitializer, "Expected an initializer in destructuring variable declaration"); + lastPattern = pattern; + if (hasInitializer) { + next(TreeBuilder::DontBuildStrings); // consume '=' + TreeExpression rhs = parseAssignmentExpression(context); + node = context.createDestructuringAssignment(location, pattern, rhs); + lastInitializer = rhs; + } + } - int varStart = tokenStart(); - identStart = varStart; - const Identifier* name = m_token.m_data.ident; - lastIdent = name; + if (!head) + head = node; + else if (!tail) { + head = context.createCommaExpr(location, head); + tail = context.appendToCommaExpr(location, head, head, node); + } else + tail = context.appendToCommaExpr(location, head, tail, node); + } while (match(COMMA)); + if (lastIdent) + lastPattern = createBindingPattern(context, DestructureToVariables, *lastIdent, 0, lastIdentToken); + return head; +} + +template +template TreeDestructuringPattern Parser::createBindingPattern(TreeBuilder& context, DestructuringKind kind, const Identifier& name, int depth, JSToken token) +{ + ASSERT(!name.isNull()); + + ASSERT(name.impl()->isAtomic() || name.impl()->isSymbol()); + if (depth) { + if (kind == DestructureToVariables) + failIfFalseIfStrict(declareVariable(&name), "Cannot destructure to a variable named '", name.impl(), "' in strict mode"); + if (kind == DestructureToParameters) { + auto bindingResult = declareBoundParameter(&name); + if (bindingResult == Scope::StrictBindingFailed && strictMode()) { + semanticFailIfTrue(m_vm->propertyNames->arguments == name || m_vm->propertyNames->eval == name, "Cannot destructure to a parameter name '", name.impl(), "' in strict mode"); + if (m_lastFunctionName && name == *m_lastFunctionName) + semanticFail("Cannot destructure to '", name.impl(), "' as it shadows the name of a strict mode function"); + semanticFailureDueToKeyword("bound parameter name"); + if (hasDeclaredParameter(name)) + semanticFail("Cannot destructure to '", name.impl(), "' as it has already been declared"); + semanticFail("Cannot bind to a parameter named '", name.impl(), "' in strict mode"); + } + if (bindingResult == Scope::BindingFailed) { + semanticFailureDueToKeyword("bound parameter name"); + if (hasDeclaredParameter(name)) + semanticFail("Cannot destructure to '", name.impl(), "' as it has already been declared"); + semanticFail("Cannot destructure to a parameter named '", name.impl(), "'"); + } + } + if (kind != DestructureToExpressions) + context.addVar(&name, DeclarationStacks::HasInitializer); + + } else { + if (kind == DestructureToVariables) { + failIfFalseIfStrict(declareVariable(&name), "Cannot declare a variable named '", name.impl(), "' in strict mode"); + context.addVar(&name, DeclarationStacks::HasInitializer); + } + + if (kind == DestructureToParameters) { + bool declarationResult = declareParameter(&name); + if (!declarationResult && strictMode()) { + semanticFailIfTrue(m_vm->propertyNames->arguments == name || m_vm->propertyNames->eval == name, "Cannot destructure to a parameter name '", name.impl(), "' in strict mode"); + if (m_lastFunctionName && name == *m_lastFunctionName) + semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function"); + semanticFailureDueToKeyword("parameter name"); + if (hasDeclaredParameter(name)) + semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared"); + semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode"); + } + } + } + return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition); +} + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) +template +template TreeStatement Parser::parseArrowFunctionSingleExpressionBody(TreeBuilder& context, FunctionParseType parseType) +{ + ASSERT(match(ARROWFUNCTION)); + + // When reparsing phase, parseType becomes StandardFunctionParseType even if the function is arrow function. + // This condition considers the following situations. + // (1): If we are in the reparsing phase, this arrow function is already parsed once, so there is no syntax error. + // (2): But if we are not in the reparsing phase, we should check this function is called in the context of the arrow function. + if (!m_lexer->isReparsing() && parseType != ArrowFunctionParseType) + failDueToUnexpectedToken(); + + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + JSTextPosition end = tokenEndPosition(); + + next(); + + failIfStackOverflow(); + TreeExpression expr = parseAssignmentExpression(context); + failIfFalse(expr, "Cannot parse the arrow function expression"); + + context.setEndOffset(expr, m_lastTokenEndPosition.offset); + + failIfFalse(isEndOfArrowFunction(), "Expected a ';', ']', '}', ')', ',', line terminator or EOF following a arrow function statement"); + + end = tokenEndPosition(); + + if (!m_lexer->prevTerminator()) + setEndOfStatement(); + + return context.createReturnStatement(location, expr, start, end); +} +#endif + +template +template TreeDestructuringPattern Parser::tryParseDestructuringPatternExpression(TreeBuilder& context) +{ + return parseDestructuringPattern(context, DestructureToExpressions); +} + +template +template TreeDestructuringPattern Parser::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, int depth) +{ + failIfStackOverflow(); + int nonLHSCount = m_nonLHSCount; + TreeDestructuringPattern pattern; + switch (m_token.m_type) { + case OPENBRACKET: { + JSTextPosition divotStart = tokenStartPosition(); + auto arrayPattern = context.createArrayPattern(m_token.m_location); next(); - bool hasInitializer = match(EQUAL); - failIfFalseIfStrictWithNameAndMessage(declareVariable(name), "Cannot declare a variable named", name->impl(), "in strict mode."); - context.addVar(name, (hasInitializer || (!m_allowsIn && match(INTOKEN))) ? DeclarationStacks::HasInitializer : 0); - if (hasInitializer) { - int varDivot = tokenStart() + 1; - initStart = tokenStart(); - next(TreeBuilder::DontBuildStrings); // consume '=' - int initialAssignments = m_assignmentCount; - TreeExpression initializer = parseAssignmentExpression(context); - initEnd = lastTokenEnd(); - lastInitializer = initializer; - failIfFalse(initializer); - - TreeExpression node = context.createAssignResolve(m_lexer->lastLineNumber(), *name, initializer, initialAssignments != m_assignmentCount, varStart, varDivot, lastTokenEnd()); - if (!varDecls) - varDecls = node; - else - varDecls = context.combineCommaNodes(m_lexer->lastLineNumber(), varDecls, node); + + bool restElementWasFound = false; + + do { + while (match(COMMA)) { + context.appendArrayPatternSkipEntry(arrayPattern, m_token.m_location); + next(); + } + propagateError(); + + if (match(CLOSEBRACKET)) + break; + + if (UNLIKELY(match(DOTDOTDOT))) { + JSTokenLocation location = m_token.m_location; + next(); + auto innerPattern = parseDestructuringPattern(context, kind, depth + 1); + if (kind == DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + + failIfTrue(kind != DestructureToExpressions && !context.isBindingNode(innerPattern), "Expected identifier for a rest element destructuring pattern"); + + context.appendArrayPatternRestEntry(arrayPattern, location, innerPattern); + restElementWasFound = true; + break; + } + + JSTokenLocation location = m_token.m_location; + auto innerPattern = parseDestructuringPattern(context, kind, depth + 1); + if (kind == DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + failIfTrue(kind == DestructureToParameters && defaultValue, "Default values in destructuring parameters are currently not supported"); + context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue); + } while (consume(COMMA)); + + if (kind == DestructureToExpressions && !match(CLOSEBRACKET)) + return 0; + consumeOrFail(CLOSEBRACKET, restElementWasFound ? "Expected a closing ']' following a rest element destructuring pattern" : "Expected either a closing ']' or a ',' following an element destructuring pattern"); + context.finishArrayPattern(arrayPattern, divotStart, divotStart, lastTokenEndPosition()); + pattern = arrayPattern; + break; + } + case OPENBRACE: { + auto objectPattern = context.createObjectPattern(m_token.m_location); + next(); + + do { + bool wasString = false; + + if (match(CLOSEBRACE)) + break; + + Identifier propertyName; + TreeDestructuringPattern innerPattern = 0; + JSTokenLocation location = m_token.m_location; + if (match(IDENT)) { + propertyName = *m_token.m_data.ident; + JSToken identifierToken = m_token; + next(); + if (consume(COLON)) + innerPattern = parseDestructuringPattern(context, kind, depth + 1); + else + innerPattern = createBindingPattern(context, kind, propertyName, depth, identifierToken); + } else { + JSTokenType tokenType = m_token.m_type; + switch (m_token.m_type) { + case DOUBLE: + case INTEGER: + propertyName = Identifier::from(m_vm, m_token.m_data.doubleValue); + break; + case STRING: + propertyName = *m_token.m_data.ident; + wasString = true; + break; + default: + if (m_token.m_type != RESERVED && m_token.m_type != RESERVED_IF_STRICT && !(m_token.m_type & KeywordTokenFlag)) { + if (kind == DestructureToExpressions) + return 0; + failWithMessage("Expected a property name"); + } + propertyName = *m_token.m_data.ident; + break; + } + next(); + if (!consume(COLON)) { + if (kind == DestructureToExpressions) + return 0; + semanticFailIfTrue(tokenType == RESERVED, "Cannot use abbreviated destructuring syntax for reserved name '", propertyName.impl(), "'"); + semanticFailIfTrue(tokenType == RESERVED_IF_STRICT, "Cannot use abbreviated destructuring syntax for reserved name '", propertyName.impl(), "' in strict mode"); + semanticFailIfTrue(tokenType & KeywordTokenFlag, "Cannot use abbreviated destructuring syntax for keyword '", propertyName.impl(), "'"); + + failWithMessage("Expected a ':' prior to a named destructuring property"); + } + innerPattern = parseDestructuringPattern(context, kind, depth + 1); + } + if (kind == DestructureToExpressions && !innerPattern) + return 0; + failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); + TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); + failIfTrue(kind == DestructureToParameters && defaultValue, "Default values in destructuring parameters are currently not supported"); + context.appendObjectPatternEntry(objectPattern, location, wasString, propertyName, innerPattern, defaultValue); + } while (consume(COMMA)); + + if (kind == DestructureToExpressions && !match(CLOSEBRACE)) + return 0; + consumeOrFail(CLOSEBRACE, "Expected either a closing '}' or an ',' after a property destructuring pattern"); + pattern = objectPattern; + break; + } + + default: { + if (!match(IDENT)) { + if (kind == DestructureToExpressions) + return 0; + semanticFailureDueToKeyword("variable name"); + failWithMessage("Expected a parameter pattern or a ')' in parameter list"); } - } while (match(COMMA)); - return varDecls; + pattern = createBindingPattern(context, kind, *m_token.m_data.ident, depth, m_token); + next(); + break; + } + } + m_nonLHSCount = nonLHSCount; + return pattern; +} + +template +template TreeExpression Parser::parseDefaultValueForDestructuringPattern(TreeBuilder& context) +{ + if (!match(EQUAL)) + return 0; + + next(TreeBuilder::DontBuildStrings); // consume '=' + return parseAssignmentExpression(context); } template template TreeConstDeclList Parser::parseConstDeclarationList(TreeBuilder& context) { - failIfTrue(strictMode()); + failIfTrue(strictMode(), "Const declarations are not supported in strict mode"); TreeConstDeclList constDecls = 0; TreeConstDeclList tail = 0; do { + JSTokenLocation location(tokenLocation()); next(); - matchOrFail(IDENT); + matchOrFail(IDENT, "Expected an identifier name in const declaration"); 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(TreeBuilder::DontBuildStrings); // consume '=' initializer = parseAssignmentExpression(context); + failIfFalse(!!initializer, "Unable to parse initializer"); } - tail = context.appendConstDecl(m_lexer->lastLineNumber(), tail, name, initializer); + tail = context.appendConstDecl(location, tail, name, initializer); if (!constDecls) constDecls = tail; } while (match(COMMA)); @@ -306,65 +849,84 @@ template template TreeStatement Parser::parseForStatement(TreeBuilder& context) { ASSERT(match(FOR)); + JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); + handleProductionOrFail(OPENPAREN, "(", "start", "for-loop header"); int nonLHSCount = m_nonLHSCount; int declarations = 0; - int declsStart = 0; - int declsEnd = 0; + JSTextPosition declsStart; + JSTextPosition declsEnd; TreeExpression decls = 0; - bool hasDeclaration = false; + TreeDestructuringPattern pattern = 0; 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; + TreeDestructuringPattern forInTarget = 0; TreeExpression forInInitializer = 0; m_allowsIn = false; - int initStart = 0; - int initEnd = 0; - decls = parseVarDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd); + JSTextPosition initStart; + JSTextPosition initEnd; + decls = parseVarDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd, ForLoopContext); m_allowsIn = true; - if (m_error) - fail(); - + propagateError(); + // Remainder of a standard for loop is handled identically if (match(SEMICOLON)) goto standardForLoop; - failIfFalse(declarations == 1); - + failIfFalse(declarations == 1, "can only declare a single variable in an enumeration"); + failIfTrueIfStrict(forInInitializer, "Cannot use initialiser syntax in a strict mode enumeration"); + + if (forInInitializer) + failIfFalse(context.isBindingNode(forInTarget), "Cannot use initialiser syntax when binding to a pattern during enumeration"); + // Handle for-in with var declaration - int inLocation = tokenStart(); - consumeOrFail(INTOKEN); - + JSTextPosition inLocation = tokenStartPosition(); + bool isOfEnumeration = false; + if (!consume(INTOKEN)) { + failIfFalse(match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of, "Expected either 'in' or 'of' in enumeration syntax"); + isOfEnumeration = true; + failIfTrue(forInInitializer, "Cannot use initialiser syntax in a for-of enumeration"); + next(); + } TreeExpression expr = parseExpression(context); - failIfFalse(expr); - int exprEnd = lastTokenEnd(); + failIfFalse(expr, "Expected expression to enumerate"); + JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + + handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); - - return context.createForInLoop(m_lexer->lastLineNumber(), forInTarget, forInInitializer, expr, statement, declsStart, inLocation, exprEnd, initStart, initEnd, startLine, endLine); + failIfFalse(statement, "Expected statement as body of for-", isOfEnumeration ? "of" : "in", " statement"); + if (isOfEnumeration) + return context.createForOfLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine); + return context.createForInLoop(location, forInTarget, expr, statement, declsStart, inLocation, exprEnd, startLine, endLine); } if (!match(SEMICOLON)) { + if (match(OPENBRACE) || match(OPENBRACKET)) { + SavePoint savePoint = createSavePoint(); + declsStart = tokenStartPosition(); + pattern = tryParseDestructuringPatternExpression(context); + declsEnd = lastTokenEndPosition(); + if (pattern && (match(INTOKEN) || (match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of))) + goto enumerationLoop; + pattern = TreeDestructuringPattern(0); + restoreSavePoint(savePoint); + } m_allowsIn = false; - declsStart = tokenStart(); + declsStart = tokenStartPosition(); decls = parseExpression(context); - declsEnd = lastTokenEnd(); + declsEnd = lastTokenEndPosition(); m_allowsIn = true; - failIfFalse(decls); + failIfFalse(decls, "Cannot parse for loop declarations"); } if (match(SEMICOLON)) { @@ -375,185 +937,198 @@ template TreeStatement Parser::parseForStatement( if (!match(SEMICOLON)) { condition = parseExpression(context); - failIfFalse(condition); + failIfFalse(condition, "Cannot parse for loop condition expression"); } - consumeOrFail(SEMICOLON); + consumeOrFail(SEMICOLON, "Expected a ';' after the for loop condition expression"); TreeExpression increment = 0; if (!match(CLOSEPAREN)) { increment = parseExpression(context); - failIfFalse(increment); + failIfFalse(increment, "Cannot parse for loop iteration expression"); } int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "end", "for-loop header"); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); - return context.createForLoop(m_lexer->lastLineNumber(), decls, condition, increment, statement, hasDeclaration, startLine, endLine); + failIfFalse(statement, "Expected a statement as the body of a for loop"); + return context.createForLoop(location, decls, condition, increment, statement, startLine, endLine); } // For-in loop - failIfFalse(nonLHSCount == m_nonLHSCount); - consumeOrFail(INTOKEN); +enumerationLoop: + failIfFalse(nonLHSCount == m_nonLHSCount, "Expected a reference on the left hand side of an enumeration statement"); + bool isOfEnumeration = false; + if (!consume(INTOKEN)) { + failIfFalse(match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of, "Expected either 'in' or 'of' in enumeration syntax"); + isOfEnumeration = true; + next(); + } TreeExpression expr = parseExpression(context); - failIfFalse(expr); - int exprEnd = lastTokenEnd(); + failIfFalse(expr, "Cannot parse subject for-", isOfEnumeration ? "of" : "in", " statement"); + JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + + handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); const Identifier* unused = 0; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); - failIfFalse(statement); - - return context.createForInLoop(m_lexer->lastLineNumber(), decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); + failIfFalse(statement, "Expected a statement as the body of a for-", isOfEnumeration ? "of" : "in", "loop"); + if (pattern) { + ASSERT(!decls); + if (isOfEnumeration) + return context.createForOfLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); + return context.createForInLoop(location, pattern, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); + } + if (isOfEnumeration) + return context.createForOfLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); + return context.createForInLoop(location, decls, expr, statement, declsStart, declsEnd, exprEnd, startLine, endLine); } template template TreeStatement Parser::parseBreakStatement(TreeBuilder& context) { ASSERT(match(BREAK)); - int startCol = tokenStart(); - int endCol = tokenEnd(); - int startLine = tokenLine(); - int endLine = tokenLine(); + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + JSTextPosition end = tokenEndPosition(); next(); if (autoSemiColon()) { - failIfFalseWithMessage(breakIsValid(), "'break' is only valid inside a switch or loop statement"); - return context.createBreakStatement(m_lexer->lastLineNumber(), startCol, endCol, startLine, endLine); + semanticFailIfFalse(breakIsValid(), "'break' is only valid inside a switch or loop statement"); + return context.createBreakStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); } - matchOrFail(IDENT); + matchOrFail(IDENT, "Expected an identifier as the target for a break statement"); const Identifier* ident = m_token.m_data.ident; - failIfFalseWithNameAndMessage(getLabel(ident), "Label", ident->impl(), "is not defined"); - endCol = tokenEnd(); - endLine = tokenLine(); + semanticFailIfFalse(getLabel(ident), "Cannot use the undeclared label '", ident->impl(), "'"); + end = tokenEndPosition(); next(); - failIfFalse(autoSemiColon()); - return context.createBreakStatement(m_lexer->lastLineNumber(), ident, startCol, endCol, startLine, endLine); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted break statement"); + return context.createBreakStatement(location, ident, start, end); } template template TreeStatement Parser::parseContinueStatement(TreeBuilder& context) { ASSERT(match(CONTINUE)); - int startCol = tokenStart(); - int endCol = tokenEnd(); - int startLine = tokenLine(); - int endLine = tokenLine(); + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); + JSTextPosition end = tokenEndPosition(); next(); if (autoSemiColon()) { - failIfFalseWithMessage(continueIsValid(), "'continue' is only valid inside a loop statement"); - return context.createContinueStatement(m_lexer->lastLineNumber(), startCol, endCol, startLine, endLine); + semanticFailIfFalse(continueIsValid(), "'continue' is only valid inside a loop statement"); + return context.createContinueStatement(location, &m_vm->propertyNames->nullIdentifier, start, end); } - matchOrFail(IDENT); + matchOrFail(IDENT, "Expected an identifier as the target for a continue statement"); const Identifier* ident = m_token.m_data.ident; ScopeLabelInfo* label = getLabel(ident); - failIfFalseWithNameAndMessage(label, "Label", ident->impl(), "is not defined"); - failIfFalseWithMessage(label->m_isLoop, "'continue' is only valid inside a loop statement"); - endCol = tokenEnd(); - endLine = tokenLine(); + semanticFailIfFalse(label, "Cannot use the undeclared label '", ident->impl(), "'"); + semanticFailIfFalse(label->isLoop, "Cannot continue to the label '", ident->impl(), "' as it is not targeting a loop"); + end = tokenEndPosition(); next(); - failIfFalse(autoSemiColon()); - return context.createContinueStatement(m_lexer->lastLineNumber(), ident, startCol, endCol, startLine, endLine); + failIfFalse(autoSemiColon(), "Expected a ';' following a targeted continue statement"); + return context.createContinueStatement(location, ident, start, end); } template template TreeStatement Parser::parseReturnStatement(TreeBuilder& context) { ASSERT(match(RETURN)); - failIfFalse(currentScope()->isFunction()); - int startLine = tokenLine(); - int endLine = startLine; - int start = tokenStart(); - int end = tokenEnd(); + JSTokenLocation location(tokenLocation()); + semanticFailIfFalse(currentScope()->isFunction(), "Return statements are only valid inside functions"); + JSTextPosition start = tokenStartPosition(); + JSTextPosition end = tokenEndPosition(); next(); - // We do the auto semicolon check before attempting to parse an expression + // We do the auto semicolon check before attempting to parse expression // as we need to ensure the a line break after the return correctly terminates // the statement if (match(SEMICOLON)) - endLine = tokenLine(); + end = tokenEndPosition(); + if (autoSemiColon()) - return context.createReturnStatement(m_lexer->lastLineNumber(), 0, start, end, startLine, endLine); + return context.createReturnStatement(location, 0, start, end); TreeExpression expr = parseExpression(context); - failIfFalse(expr); - end = lastTokenEnd(); + failIfFalse(expr, "Cannot parse the return expression"); + end = lastTokenEndPosition(); if (match(SEMICOLON)) - endLine = tokenLine(); - failIfFalse(autoSemiColon()); - return context.createReturnStatement(m_lexer->lastLineNumber(), expr, start, end, startLine, endLine); + end = tokenEndPosition(); + if (!autoSemiColon()) + failWithMessage("Expected a ';' following a return statement"); + return context.createReturnStatement(location, expr, start, end); } template template TreeStatement Parser::parseThrowStatement(TreeBuilder& context) { ASSERT(match(THROW)); - int eStart = tokenStart(); - int startLine = tokenLine(); + JSTokenLocation location(tokenLocation()); + JSTextPosition start = tokenStartPosition(); next(); - - failIfTrue(autoSemiColon()); + failIfTrue(match(SEMICOLON), "Expected expression after 'throw'"); + semanticFailIfTrue(autoSemiColon(), "Cannot have a newline after 'throw'"); TreeExpression expr = parseExpression(context); - failIfFalse(expr); - int eEnd = lastTokenEnd(); - int endLine = tokenLine(); - failIfFalse(autoSemiColon()); + failIfFalse(expr, "Cannot parse expression for throw statement"); + JSTextPosition end = lastTokenEndPosition(); + failIfFalse(autoSemiColon(), "Expected a ';' after a throw statement"); - return context.createThrowStatement(m_lexer->lastLineNumber(), expr, eStart, eEnd, startLine, endLine); + return context.createThrowStatement(location, expr, start, end); } template template TreeStatement Parser::parseWithStatement(TreeBuilder& context) { ASSERT(match(WITH)); - failIfTrueWithMessage(strictMode(), "'with' statements are not valid in strict mode"); + JSTokenLocation location(tokenLocation()); + semanticFailIfTrue(strictMode(), "'with' statements are not valid in strict mode"); currentScope()->setNeedsFullActivation(); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); + + handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'with' statement"); int start = tokenStart(); TreeExpression expr = parseExpression(context); - failIfFalse(expr); - int end = lastTokenEnd(); - + failIfFalse(expr, "Cannot parse 'with' subject expression"); + JSTextPosition end = lastTokenEndPosition(); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "start", "subject of a 'with' statement"); const Identifier* unused = 0; TreeStatement statement = parseStatement(context, unused); - failIfFalse(statement); + failIfFalse(statement, "A 'with' statement must have a body"); - return context.createWithStatement(m_lexer->lastLineNumber(), expr, statement, start, end, startLine, endLine); + return context.createWithStatement(location, expr, statement, start, end, startLine, endLine); } template template TreeStatement Parser::parseSwitchStatement(TreeBuilder& context) { ASSERT(match(SWITCH)); + JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); - consumeOrFail(OPENPAREN); + handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'switch'"); TreeExpression expr = parseExpression(context); - failIfFalse(expr); + failIfFalse(expr, "Cannot parse switch subject expression"); int endLine = tokenLine(); - consumeOrFail(CLOSEPAREN); - consumeOrFail(OPENBRACE); + + handleProductionOrFail(CLOSEPAREN, ")", "end", "subject of a 'switch'"); + handleProductionOrFail(OPENBRACE, "{", "start", "body of a 'switch'"); startSwitch(); TreeClauseList firstClauses = parseSwitchClauses(context); - failIfTrue(m_error); + propagateError(); TreeClause defaultClause = parseSwitchDefaultClause(context); - failIfTrue(m_error); + propagateError(); TreeClauseList secondClauses = parseSwitchClauses(context); - failIfTrue(m_error); + propagateError(); endSwitch(); - consumeOrFail(CLOSEBRACE); + handleProductionOrFail(CLOSEBRACE, "}", "end", "body of a 'switch'"); - return context.createSwitchStatement(m_lexer->lastLineNumber(), expr, firstClauses, defaultClause, secondClauses, startLine, endLine); + return context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine); } @@ -562,24 +1137,28 @@ template TreeClauseList Parser::parseSwitchClause { if (!match(CASE)) return 0; + unsigned startOffset = tokenStart(); next(); TreeExpression condition = parseExpression(context); - failIfFalse(condition); - consumeOrFail(COLON); - TreeSourceElements statements = parseSourceElements(context); - failIfFalse(statements); + failIfFalse(condition, "Cannot parse switch clause"); + consumeOrFail(COLON, "Expected a ':' after switch clause expression"); + TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode, StandardFunctionParseType); + failIfFalse(statements, "Cannot parse the body of a switch clause"); TreeClause clause = context.createClause(condition, statements); + context.setStartOffset(clause, startOffset); TreeClauseList clauseList = context.createClauseList(clause); TreeClauseList tail = clauseList; while (match(CASE)) { + startOffset = tokenStart(); next(); TreeExpression condition = parseExpression(context); - failIfFalse(condition); - consumeOrFail(COLON); - TreeSourceElements statements = parseSourceElements(context); - failIfFalse(statements); + failIfFalse(condition, "Cannot parse switch case expression"); + consumeOrFail(COLON, "Expected a ':' after switch clause expression"); + TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode, StandardFunctionParseType); + failIfFalse(statements, "Cannot parse the body of a switch clause"); clause = context.createClause(condition, statements); + context.setStartOffset(clause, startOffset); tail = context.createClauseList(tail, clause); } return clauseList; @@ -590,84 +1169,103 @@ template TreeClause Parser::parseSwitchDefaultCla { if (!match(DEFAULT)) return 0; + unsigned startOffset = tokenStart(); next(); - consumeOrFail(COLON); - TreeSourceElements statements = parseSourceElements(context); - failIfFalse(statements); - return context.createClause(0, statements); + consumeOrFail(COLON, "Expected a ':' after switch default clause"); + TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode, StandardFunctionParseType); + failIfFalse(statements, "Cannot parse the body of a switch default clause"); + TreeClause result = context.createClause(0, statements); + context.setStartOffset(result, startOffset); + return result; } template template TreeStatement Parser::parseTryStatement(TreeBuilder& context) { ASSERT(match(TRY)); + JSTokenLocation location(tokenLocation()); TreeStatement tryBlock = 0; - const Identifier* ident = &m_globalData->propertyNames->nullIdentifier; + const Identifier* ident = &m_vm->propertyNames->nullIdentifier; TreeStatement catchBlock = 0; TreeStatement finallyBlock = 0; int firstLine = tokenLine(); next(); - matchOrFail(OPENBRACE); + matchOrFail(OPENBRACE, "Expected a block statement as body of a try statement"); tryBlock = parseBlockStatement(context); - failIfFalse(tryBlock); - int lastLine = m_lastLine; + failIfFalse(tryBlock, "Cannot parse the body of try block"); + int lastLine = m_lastTokenEndPosition.line; if (match(CATCH)) { currentScope()->setNeedsFullActivation(); next(); - consumeOrFail(OPENPAREN); - matchOrFail(IDENT); + + handleProductionOrFail(OPENPAREN, "(", "start", "'catch' target"); + if (!match(IDENT)) { + semanticFailureDueToKeyword("catch variable name"); + failWithMessage("Expected identifier name as catch target"); + } ident = m_token.m_data.ident; next(); AutoPopScopeRef catchScope(this, pushScope()); - failIfFalseIfStrictWithNameAndMessage(declareVariable(ident), "Cannot declare a variable named", ident->impl(), "in strict mode"); + failIfFalseIfStrict(declareVariable(ident), "Cannot declare a catch variable named '", ident->impl(), "' in strict mode"); catchScope->preventNewDecls(); - consumeOrFail(CLOSEPAREN); - matchOrFail(OPENBRACE); + handleProductionOrFail(CLOSEPAREN, ")", "end", "'catch' target"); + matchOrFail(OPENBRACE, "Expected exception handler to be a block statement"); catchBlock = parseBlockStatement(context); - failIfFalseWithMessage(catchBlock, "'try' must have a catch or finally block"); - failIfFalse(popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo)); + failIfFalse(catchBlock, "Unable to parse 'catch' block"); + failIfFalse(popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo), "Parse error"); } if (match(FINALLY)) { next(); - matchOrFail(OPENBRACE); + matchOrFail(OPENBRACE, "Expected block statement for finally body"); finallyBlock = parseBlockStatement(context); - failIfFalse(finallyBlock); + failIfFalse(finallyBlock, "Cannot parse finally body"); } - failIfFalse(catchBlock || finallyBlock); - return context.createTryStatement(m_lexer->lastLineNumber(), tryBlock, ident, catchBlock, finallyBlock, firstLine, lastLine); + failIfFalse(catchBlock || finallyBlock, "Try statements must have at least a catch or finally block"); + return context.createTryStatement(location, tryBlock, ident, catchBlock, finallyBlock, firstLine, lastLine); } template template TreeStatement Parser::parseDebuggerStatement(TreeBuilder& context) { ASSERT(match(DEBUGGER)); + JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); int endLine = startLine; next(); if (match(SEMICOLON)) startLine = tokenLine(); - failIfFalse(autoSemiColon()); - return context.createDebugger(m_lexer->lastLineNumber(), startLine, endLine); + failIfFalse(autoSemiColon(), "Debugger keyword must be followed by a ';'"); + return context.createDebugger(location, startLine, endLine); } template template TreeStatement Parser::parseBlockStatement(TreeBuilder& context) { ASSERT(match(OPENBRACE)); + JSTokenLocation location(tokenLocation()); + int startOffset = m_token.m_data.offset; int start = tokenLine(); next(); if (match(CLOSEBRACE)) { + int endOffset = m_token.m_data.offset; next(); - return context.createBlockStatement(m_lexer->lastLineNumber(), 0, start, m_lastLine); + TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line); + context.setStartOffset(result, startOffset); + context.setEndOffset(result, endOffset); + return result; } - TreeSourceElements subtree = parseSourceElements(context); - failIfFalse(subtree); - matchOrFail(CLOSEBRACE); + TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode, StandardFunctionParseType); + failIfFalse(subtree, "Cannot parse the body of the block statement"); + matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement"); + int endOffset = m_token.m_data.offset; next(); - return context.createBlockStatement(m_lexer->lastLineNumber(), subtree, start, m_lastLine); + TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line); + context.setStartOffset(result, startOffset); + context.setEndOffset(result, endOffset); + return result; } template @@ -678,43 +1276,63 @@ template TreeStatement Parser::parseStatement(Tre directive = 0; int nonTrivialExpressionCount = 0; failIfStackOverflow(); + TreeStatement result = 0; + bool shouldSetEndOffset = true; + switch (m_token.m_type) { case OPENBRACE: - return parseBlockStatement(context); + result = parseBlockStatement(context); + shouldSetEndOffset = false; + break; case VAR: - return parseVarDeclaration(context); - case CONSTTOKEN: - return parseConstDeclaration(context); + result = parseVarDeclaration(context); + break; case FUNCTION: - failIfFalseIfStrictWithMessage(m_statementDepth == 1, "Functions cannot be declared in a nested block in strict mode"); - return parseFunctionDeclaration(context); - case SEMICOLON: + failIfFalseIfStrict(m_statementDepth == 1, "Strict mode does not allow function declarations in a lexically nested statement"); + result = parseFunctionDeclaration(context); + break; + case SEMICOLON: { + JSTokenLocation location(tokenLocation()); next(); - return context.createEmptyStatement(m_lexer->lastLineNumber()); + result = context.createEmptyStatement(location); + break; + } case IF: - return parseIfStatement(context); + result = parseIfStatement(context); + break; case DO: - return parseDoWhileStatement(context); + result = parseDoWhileStatement(context); + break; case WHILE: - return parseWhileStatement(context); + result = parseWhileStatement(context); + break; case FOR: - return parseForStatement(context); + result = parseForStatement(context); + break; case CONTINUE: - return parseContinueStatement(context); + result = parseContinueStatement(context); + break; case BREAK: - return parseBreakStatement(context); + result = parseBreakStatement(context); + break; case RETURN: - return parseReturnStatement(context); + result = parseReturnStatement(context); + break; case WITH: - return parseWithStatement(context); + result = parseWithStatement(context); + break; case SWITCH: - return parseSwitchStatement(context); + result = parseSwitchStatement(context); + break; case THROW: - return parseThrowStatement(context); + result = parseThrowStatement(context); + break; case TRY: - return parseTryStatement(context); + result = parseTryStatement(context); + break; case DEBUGGER: - return parseDebuggerStatement(context); + result = parseDebuggerStatement(context); + break; case EOFTOK: case CASE: case CLOSEBRACE: @@ -722,121 +1340,372 @@ template TreeStatement Parser::parseStatement(Tre // These tokens imply the end of a set of source elements return 0; case IDENT: - return parseExpressionOrLabelStatement(context); + result = parseExpressionOrLabelStatement(context); + break; case STRING: directive = m_token.m_data.ident; if (directiveLiteralLength) - *directiveLiteralLength = m_token.m_info.endOffset - m_token.m_info.startOffset; + *directiveLiteralLength = m_token.m_location.endOffset - m_token.m_location.startOffset; nonTrivialExpressionCount = m_nonTrivialExpressionCount; + FALLTHROUGH; default: TreeStatement exprStatement = parseExpressionStatement(context); if (directive && nonTrivialExpressionCount != m_nonTrivialExpressionCount) directive = 0; - return exprStatement; + result = exprStatement; + break; } + + if (result && shouldSetEndOffset) + context.setEndOffset(result, m_lastTokenEndPosition.offset); + return result; } template template TreeFormalParameterList Parser::parseFormalParameters(TreeBuilder& context) { - matchOrFail(IDENT); - failIfFalseIfStrictWithNameAndMessage(declareParameter(m_token.m_data.ident), "Cannot declare a parameter named", m_token.m_data.ident->impl(), " in strict mode"); - TreeFormalParameterList list = context.createFormalParameterList(*m_token.m_data.ident); + auto parameter = parseDestructuringPattern(context, DestructureToParameters); + failIfFalse(parameter, "Cannot parse parameter pattern"); + TreeFormalParameterList list = context.createFormalParameterList(parameter); TreeFormalParameterList tail = list; - next(); - while (match(COMMA)) { - next(); - matchOrFail(IDENT); - const Identifier* ident = m_token.m_data.ident; - failIfFalseIfStrictWithNameAndMessage(declareParameter(ident), "Cannot declare a parameter named", ident->impl(), "in strict mode"); - next(); - tail = context.createFormalParameterList(tail, *ident); + while (consume(COMMA)) { + parameter = parseDestructuringPattern(context, DestructureToParameters); + failIfFalse(parameter, "Cannot parse parameter pattern"); + tail = context.createFormalParameterList(tail, parameter); } return list; } template -template TreeFunctionBody Parser::parseFunctionBody(TreeBuilder& context) +template TreeFunctionBody Parser::parseFunctionBody( + TreeBuilder& context, int functionKeywordStart, int functionNameStart, + int parametersStart, ConstructorKind constructorKind, FunctionParseType parseType) { - if (match(CLOSEBRACE)) - return context.createFunctionBody(m_lexer->lastLineNumber(), strictMode()); + JSTokenLocation startLocation(tokenLocation()); + unsigned startColumn = tokenColumn(); + + if (parseType == StandardFunctionParseType) { + next(); + if (match(CLOSEBRACE)) { + unsigned endColumn = tokenColumn(); + return context.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind); + } + } + DepthManager statementDepth(&m_statementDepth); m_statementDepth = 0; - typename TreeBuilder::FunctionBodyBuilder bodyBuilder(const_cast(m_globalData), m_lexer.get()); - failIfFalse(parseSourceElements(bodyBuilder)); - return context.createFunctionBody(m_lexer->lastLineNumber(), strictMode()); + typename TreeBuilder::FunctionBodyBuilder bodyBuilder(const_cast(m_vm), m_lexer.get()); +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + failIfFalse(parseSourceElements(bodyBuilder, CheckForStrictMode, parseType), parseType == StandardFunctionParseType ? "Cannot parse body of this function" : "Cannot parse body of this arrow function"); +#else + failIfFalse(parseSourceElements(bodyBuilder, CheckForStrictMode, StandardFunctionParseType), "Cannot parse body of this function"); +#endif + unsigned endColumn = tokenColumn(); + return context.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind); +} + +static const char* stringForFunctionMode(FunctionParseMode mode) +{ + switch (mode) { + case GetterMode: + return "getter"; + case SetterMode: + return "setter"; + case FunctionMode: + return "function"; + case MethodMode: + return "method"; +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + case ArrowFunctionMode: + return "arrow function"; +#endif + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; +} + +template template int Parser::parseFunctionParameters(TreeBuilder& context, FunctionParseMode mode, ParserFunctionInfo& info) +{ + int parametersStart = m_token.m_location.startOffset; + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (mode == ArrowFunctionMode) { + if (!match(IDENT) && !match(OPENPAREN)) { + semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); + failWithMessage("Expected an arrow function input parameter"); + } else { + if (match(OPENPAREN)) { + next(); + + if (!match(CLOSEPAREN)) { + info.parameters = parseFormalParameters(context); + failIfFalse(info.parameters, "Cannot parse parameters for this ", stringForFunctionMode(mode)); + } + + consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); + } else { + auto parameter = parseDestructuringPattern(context, DestructureToParameters); + failIfFalse(parameter, "Cannot parse parameter pattern"); + info.parameters = context.createFormalParameterList(parameter); + failIfFalse(info.parameters, "Cannot parse parameters for this ", stringForFunctionMode(mode)); + } + } + + return parametersStart; + } +#endif + + if (!consume(OPENPAREN)) { + semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); + failWithMessage("Expected an opening '(' before a ", stringForFunctionMode(mode), "'s parameter list"); + } + + if (mode == GetterMode) + consumeOrFail(CLOSEPAREN, "getter functions must have no parameters"); + else if (mode == SetterMode) { + failIfTrue(match(CLOSEPAREN), "setter functions must have one parameter"); + auto parameter = parseDestructuringPattern(context, DestructureToParameters); + failIfFalse(parameter, "setter functions must have one parameter"); + info.parameters = context.createFormalParameterList(parameter); + failIfTrue(match(COMMA), "setter functions must have one parameter"); + consumeOrFail(CLOSEPAREN, "Expected a ')' after a parameter declaration"); + } else { + if (!match(CLOSEPAREN)) { + info.parameters = parseFormalParameters(context); + failIfFalse(info.parameters, "Cannot parse parameters for this ", stringForFunctionMode(mode)); + } + consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); + } + + return parametersStart; } template -template bool Parser::parseFunctionInfo(TreeBuilder& context, const Identifier*& name, TreeFormalParameterList& parameters, TreeFunctionBody& body, int& openBracePos, int& closeBracePos, int& bodyStartLine) +template bool Parser::parseFunctionInfo(TreeBuilder& context, FunctionRequirements requirements, FunctionParseMode mode, bool nameIsInContainingScope, ConstructorKind constructorKind, SuperBinding expectedSuperBinding, int functionKeywordStart, ParserFunctionInfo& info, FunctionParseType parseType) { AutoPopScopeRef functionScope(this, pushScope()); functionScope->setIsFunction(); - if (match(IDENT)) { - name = m_token.m_data.ident; + int functionNameStart = m_token.m_location.startOffset; + const Identifier* lastFunctionName = m_lastFunctionName; + m_lastFunctionName = nullptr; + int parametersStart; + + switch (parseType) { + case StandardFunctionParseType: { + if (match(IDENT)) { + info.name = m_token.m_data.ident; + m_lastFunctionName = info.name; + next(); + if (!nameIsInContainingScope) + failIfFalseIfStrict(functionScope->declareVariable(info.name), "'", info.name->impl(), "' is not a valid ", stringForFunctionMode(mode), " name in strict mode"); + } else if (requirements == FunctionNeedsName) { + if (match(OPENPAREN) && mode == FunctionMode) + semanticFail("Function statements must have a name"); + semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); + failDueToUnexpectedToken(); + return false; + } + + parametersStart = parseFunctionParameters(context, mode, info); + propagateError(); + + matchOrFail(OPENBRACE, "Expected an opening '{' at the start of a ", stringForFunctionMode(mode), " body"); + + // BytecodeGenerator emits code to throw TypeError when a class constructor is "call"ed. + // Set ConstructorKind to None for non-constructor methods of classes. + + if (m_defaultConstructorKind != ConstructorKind::None) { + constructorKind = m_defaultConstructorKind; + expectedSuperBinding = m_defaultConstructorKind == ConstructorKind::Derived ? SuperBinding::Needed : SuperBinding::NotNeeded; + } + + info.startFunctionOffset = m_token.m_data.offset; + + break; + } +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + case ArrowFunctionParseType: { + parametersStart = parseFunctionParameters(context, ArrowFunctionMode, info); + propagateError(); + + matchOrFail(ARROWFUNCTION, "Expected a '=>' after arrow function parameter declaration"); + + if (m_lexer->prevTerminator()) + failDueToUnexpectedToken(); + + ASSERT(constructorKind == ConstructorKind::None); + + info.arrowFunctionOffset = m_token.m_data.offset; + // Check if arrow body start with {. If it true it mean that arrow function is Fat arrow function + // and we need use common approach to parse function body + SavePoint savePoint = createSavePoint(); + next(); - if (!nameIsInContainingScope) - failIfFalseIfStrict(functionScope->declareVariable(name)); - } else if (requirements == FunctionNeedsName) - return false; - consumeOrFail(OPENPAREN); - if (!match(CLOSEPAREN)) { - parameters = parseFormalParameters(context); - failIfFalse(parameters); + info.functionBodyType = match(OPENBRACE) ? ArrowFunctionBodyBlock : ArrowFunctionBodyExpression; + info.startFunctionOffset = (info.functionBodyType == ArrowFunctionBodyBlock) ? m_token.m_data.offset : info.arrowFunctionOffset; + + restoreSavePoint(savePoint); + + break; + } +#endif } - consumeOrFail(CLOSEPAREN); - matchOrFail(OPENBRACE); - openBracePos = m_token.m_data.intValue; - bodyStartLine = tokenLine(); + bool isClassConstructor = constructorKind != ConstructorKind::None; + + info.bodyStartLine = tokenLine(); + info.bodyStartColumn = m_token.m_data.offset - m_token.m_data.lineStartOffset; + JSTokenLocation startLocation(tokenLocation()); // If we know about this function already, we can use the cached info and skip the parser to the end of the function. - if (const SourceProviderCacheItem* cachedInfo = TreeBuilder::CanUseFunctionCache ? findCachedFunctionInfo(openBracePos) : 0) { + if (const SourceProviderCacheItem* cachedInfo = TreeBuilder::CanUseFunctionCache ? findCachedFunctionInfo(info.startFunctionOffset) : 0) { // If we're in a strict context, the cached function info must say it was strict too. ASSERT(!strictMode() || cachedInfo->strictMode); - body = context.createFunctionBody(m_lexer->lastLineNumber(), cachedInfo->strictMode); + JSTokenLocation endLocation; + + endLocation.line = cachedInfo->lastTockenLine; + endLocation.startOffset = cachedInfo->lastTockenStartOffset; + endLocation.lineStartOffset = cachedInfo->lastTockenLineStartOffset; + + bool endColumnIsOnStartLine = (endLocation.line == info.bodyStartLine); + ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); + unsigned bodyEndColumn = endColumnIsOnStartLine ? + endLocation.startOffset - m_token.m_data.lineStartOffset : + endLocation.startOffset - endLocation.lineStartOffset; + unsigned currentLineStartOffset = m_token.m_location.lineStartOffset; + + info.body = context.createFunctionBody( + startLocation, endLocation, info.bodyStartColumn, bodyEndColumn, + functionKeywordStart, functionNameStart, parametersStart, + cachedInfo->strictMode, constructorKind); - functionScope->restoreFunctionInfo(cachedInfo); - failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); + functionScope->restoreFromSourceProviderCache(cachedInfo); + failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error"); - closeBracePos = cachedInfo->closeBracePos; - m_token = cachedInfo->closeBraceToken(); - m_lexer->setOffset(m_token.m_info.endOffset); - m_lexer->setLineNumber(m_token.m_info.line); + m_token = cachedInfo->endFunctionToken(); + + if (endColumnIsOnStartLine) + m_token.m_location.lineStartOffset = currentLineStartOffset; + + m_lexer->setOffset(m_token.m_location.endOffset, m_token.m_location.lineStartOffset); + m_lexer->setLineNumber(m_token.m_location.line); + info.endFunctionOffset = cachedInfo->endFunctionOffset; +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (parseType == ArrowFunctionParseType) + info.functionBodyType = cachedInfo->isBodyArrowExpression ? ArrowFunctionBodyExpression : ArrowFunctionBodyBlock; + else + info.functionBodyType = StandardFunctionBodyBlock; + switch (info.functionBodyType) { + case ArrowFunctionBodyExpression: + next(); + context.setEndOffset(info.body, m_lexer->currentOffset()); + break; + case ArrowFunctionBodyBlock: + case StandardFunctionBodyBlock: + context.setEndOffset(info.body, m_lexer->currentOffset()); + next(); + break; + } +#else + context.setEndOffset(info.body, m_lexer->currentOffset()); next(); +#endif + info.bodyEndLine = m_lastTokenEndPosition.line; return true; } - next(); + m_lastFunctionName = lastFunctionName; + ParserState oldState = saveState(); + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + switch (info.functionBodyType) { + case ArrowFunctionBodyBlock: { + // Consume => in case of arrow function block e.g. x => { return x; } + next(); + + info.bodyStartLine = tokenLine(); + info.bodyStartColumn = m_token.m_data.offset - m_token.m_data.lineStartOffset; + + info.body = parseFunctionBody(context, functionKeywordStart, functionNameStart, parametersStart, constructorKind, StandardFunctionParseType); + break; + } + case StandardFunctionBodyBlock: + case ArrowFunctionBodyExpression : { + info.body = parseFunctionBody(context, functionKeywordStart, functionNameStart, parametersStart, constructorKind, parseType); + break; + } + } +#else + info.body = parseFunctionBody(context, functionKeywordStart, functionNameStart, parametersStart, constructorKind, StandardFunctionParseType); +#endif + + restoreState(oldState); + failIfFalse(info.body, "Cannot parse the body of this ", stringForFunctionMode(mode)); + context.setEndOffset(info.body, m_lexer->currentOffset()); + if (functionScope->strictMode() && info.name) { + RELEASE_ASSERT(mode == FunctionMode || mode == MethodMode); + semanticFailIfTrue(m_vm->propertyNames->arguments == *info.name, "'", info.name->impl(), "' is not a valid function name in strict mode"); + semanticFailIfTrue(m_vm->propertyNames->eval == *info.name, "'", info.name->impl(), "' is not a valid function name in strict mode"); + } + if (functionScope->hasDirectSuper()) { + semanticFailIfTrue(!isClassConstructor, "Cannot call super() outside of a class constructor"); + semanticFailIfTrue(constructorKind != ConstructorKind::Derived, "Cannot call super() in a base class constructor"); + } + if (functionScope->needsSuperBinding()) + semanticFailIfTrue(expectedSuperBinding == SuperBinding::NotNeeded, "super can only be used in a method of a derived class"); + + JSTokenLocation location = JSTokenLocation(m_token.m_location); + info.endFunctionOffset = m_token.m_data.offset; - body = parseFunctionBody(context); - failIfFalse(body); - if (functionScope->strictMode() && name) { - failIfTrueWithNameAndMessage(m_globalData->propertyNames->arguments == *name, "Function name", name->impl(), "is not valid in strict mode"); - failIfTrueWithNameAndMessage(m_globalData->propertyNames->eval == *name, "Function name", name->impl(), "is not valid in strict mode"); +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (info.functionBodyType == ArrowFunctionBodyExpression) { + location = locationBeforeLastToken(); + info.endFunctionOffset = location.endOffset; } - closeBracePos = m_token.m_data.intValue; +#endif // 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 newInfo; - int functionLength = closeBracePos - openBracePos; + static const int minimumFunctionLengthToCache = 16; + std::unique_ptr newInfo; + int functionLength = info.endFunctionOffset - info.startFunctionOffset; if (TreeBuilder::CanUseFunctionCache && m_functionCache && functionLength > minimumFunctionLengthToCache) { - newInfo = adoptPtr(new SourceProviderCacheItem(m_token.m_info.line, closeBracePos)); - functionScope->saveFunctionInfo(newInfo.get()); + SourceProviderCacheItemCreationParameters parameters; + parameters.endFunctionOffset = info.endFunctionOffset; + parameters.functionNameStart = functionNameStart; + parameters.lastTockenLine = location.line; + parameters.lastTockenStartOffset = location.startOffset; + parameters.lastTockenEndOffset = location.endOffset; + parameters.lastTockenLineStartOffset = location.lineStartOffset; +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (info.functionBodyType == ArrowFunctionBodyExpression) { + parameters.isBodyArrowExpression = true; + parameters.tokenType = m_token.m_type; + } +#endif + functionScope->fillParametersForSourceProviderCache(parameters); + newInfo = SourceProviderCacheItem::create(parameters); } - failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); - matchOrFail(CLOSEBRACE); + failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error"); - if (newInfo) { - unsigned approximateByteSize = newInfo->approximateByteSize(); - m_functionCache->add(openBracePos, newInfo.release(), approximateByteSize); +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (info.functionBodyType == ArrowFunctionBodyExpression) + failIfFalse(isEndOfArrowFunction(), "Expected the closing ';' ',' ']' ')' '}', line terminator or EOF after arrow function"); + else { + matchOrFail(CLOSEBRACE, "Expected a closing '}' after a ", stringForFunctionMode(mode), " body"); + next(); } - +#else + matchOrFail(CLOSEBRACE, "Expected a closing '}' after a ", stringForFunctionMode(mode), " body"); next(); +#endif + + if (newInfo) + m_functionCache->add(info.startFunctionOffset, WTF::move(newInfo)); + + info.bodyEndLine = m_lastTokenEndPosition.line; return true; } @@ -844,21 +1713,168 @@ template template TreeStatement Parser::parseFunctionDeclaration(TreeBuilder& context) { ASSERT(match(FUNCTION)); + JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); + next(); + ParserFunctionInfo info; + failIfFalse((parseFunctionInfo(context, FunctionNeedsName, FunctionMode, true, ConstructorKind::None, SuperBinding::NotNeeded, + functionKeywordStart, info, StandardFunctionParseType)), "Cannot parse this function"); + failIfFalse(info.name, "Function statements must have a name"); + failIfFalseIfStrict(declareVariable(info.name), "Cannot declare a function named '", info.name->impl(), "' in strict mode"); + return context.createFuncDeclStatement(location, info); +} + +#if ENABLE(ES6_CLASS_SYNTAX) +template +template TreeStatement Parser::parseClassDeclaration(TreeBuilder& context) +{ + ASSERT(match(CLASSTOKEN)); + JSTokenLocation location(tokenLocation()); + JSTextPosition classStart = tokenStartPosition(); + unsigned classStartLine = tokenLine(); + + ParserClassInfo info; + TreeClassExpression classExpr = parseClass(context, FunctionNeedsName, info); + failIfFalse(classExpr, "Failed to parse class"); + declareVariable(info.className); + + // FIXME: This should be like `let`, not `var`. + context.addVar(info.className, DeclarationStacks::HasInitializer); + + JSTextPosition classEnd = lastTokenEndPosition(); + unsigned classEndLine = tokenLine(); + + return context.createClassDeclStatement(location, classExpr, classStart, classEnd, classStartLine, classEndLine); +} + +template +template TreeClassExpression Parser::parseClass(TreeBuilder& context, FunctionRequirements requirements, ParserClassInfo& info) +{ + ASSERT(match(CLASSTOKEN)); + JSTokenLocation location(tokenLocation()); next(); - const Identifier* name = 0; - TreeFormalParameterList parameters = 0; - TreeFunctionBody body = 0; - int openBracePos = 0; - int closeBracePos = 0; - int bodyStartLine = 0; - failIfFalse((parseFunctionInfo(context, name, parameters, body, openBracePos, closeBracePos, bodyStartLine))); - failIfFalse(name); - failIfFalseIfStrict(declareVariable(name)); - return context.createFuncDeclStatement(m_lexer->lastLineNumber(), name, body, parameters, openBracePos, closeBracePos, bodyStartLine, m_lastLine); + + AutoPopScopeRef classScope(this, pushScope()); + classScope->setStrictMode(); + + const Identifier* className = nullptr; + if (match(IDENT)) { + className = m_token.m_data.ident; + info.className = className; + next(); + failIfFalse(classScope->declareVariable(className), "'", className->impl(), "' is not a valid class name"); + } else if (requirements == FunctionNeedsName) { + if (match(OPENBRACE)) + semanticFail("Class statements must have a name"); + semanticFailureDueToKeyword("class name"); + failDueToUnexpectedToken(); + } else + className = &m_vm->propertyNames->nullIdentifier; + ASSERT(className); + + TreeExpression parentClass = 0; + if (consume(EXTENDS)) { + parentClass = parseMemberExpression(context); + failIfFalse(parentClass, "Cannot parse the parent class name"); + } + const ConstructorKind constructorKind = parentClass ? ConstructorKind::Derived : ConstructorKind::Base; + + consumeOrFail(OPENBRACE, "Expected opening '{' at the start of a class body"); + + TreeExpression constructor = 0; + TreePropertyList staticMethods = 0; + TreePropertyList instanceMethods = 0; + TreePropertyList instanceMethodsTail = 0; + TreePropertyList staticMethodsTail = 0; + while (!match(CLOSEBRACE)) { + if (match(SEMICOLON)) { + next(); + continue; + } + + JSTokenLocation methodLocation(tokenLocation()); + unsigned methodStart = tokenStart(); + + // For backwards compatibility, "static" is a non-reserved keyword in non-strict mode. + bool isStaticMethod = match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm->propertyNames->staticKeyword; + if (isStaticMethod) + next(); + + // FIXME: Figure out a way to share more code with parseProperty. + const CommonIdentifiers& propertyNames = *m_vm->propertyNames; + const Identifier* ident = nullptr; + bool isGetter = false; + bool isSetter = false; + switch (m_token.m_type) { + case STRING: + ident = m_token.m_data.ident; + ASSERT(ident); + next(); + break; + case IDENT: + ident = m_token.m_data.ident; + isGetter = *ident == propertyNames.get; + isSetter = *ident == propertyNames.set; + ASSERT(ident); + break; + case DOUBLE: + case INTEGER: + ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast(m_vm), m_token.m_data.doubleValue); + ASSERT(ident); + next(); + break; + default: + failDueToUnexpectedToken(); + } + + TreeProperty property; + const bool alwaysStrictInsideClass = true; + if (isGetter || isSetter) { + nextExpectIdentifier(LexerFlagsIgnoreReservedWords); + property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, methodStart, + ConstructorKind::None, SuperBinding::Needed); + failIfFalse(property, "Cannot parse this method"); + } else { + ParserFunctionInfo methodInfo; + bool isConstructor = !isStaticMethod && *ident == propertyNames.constructor; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, isStaticMethod ? FunctionMode : MethodMode, false, isConstructor ? constructorKind : ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, StandardFunctionParseType)), "Cannot parse this method"); + failIfFalse(ident && declareVariable(ident), "Cannot declare a method named '", methodInfo.name->impl(), "'"); + methodInfo.name = isConstructor ? className : ident; + + TreeExpression method = context.createFunctionExpr(methodLocation, methodInfo); + if (isConstructor) { + semanticFailIfTrue(constructor, "Cannot declare multiple constructors in a single class"); + constructor = method; + continue; + } + + // FIXME: Syntax error when super() is called + semanticFailIfTrue(isStaticMethod && methodInfo.name && *methodInfo.name == propertyNames.prototype, + "Cannot declare a static method named 'prototype'"); + property = context.createProperty(methodInfo.name, method, PropertyNode::Constant, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed); + } + + TreePropertyList& tail = isStaticMethod ? staticMethodsTail : instanceMethodsTail; + if (tail) + tail = context.createPropertyList(methodLocation, property, tail); + else { + tail = context.createPropertyList(methodLocation, property); + if (isStaticMethod) + staticMethods = tail; + else + instanceMethods = tail; + } + } + + failIfFalse(popScope(classScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error"); + consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); + + return context.createClassExpr(location, *className, constructor, parentClass, instanceMethods, staticMethods); } +#endif struct LabelInfo { - LabelInfo(const Identifier* ident, int start, int end) + LabelInfo(const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) : m_ident(ident) , m_start(start) , m_end(end) @@ -866,8 +1882,8 @@ struct LabelInfo { } const Identifier* m_ident; - int m_start; - int m_end; + JSTextPosition m_start; + JSTextPosition m_end; }; template @@ -878,29 +1894,30 @@ template TreeStatement Parser::parseExpressionOrL * special case that looks for a colon as the next character in the input. */ Vector labels; - + JSTokenLocation location; do { - int start = tokenStart(); - int startLine = tokenLine(); + JSTextPosition start = tokenStartPosition(); + location = tokenLocation(); 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(m_lexer->lastLineNumber(), expression, startLine, m_lastLine); + failIfFalse(expression, "Cannot parse expression statement"); + if (!autoSemiColon()) + failDueToUnexpectedToken(); + return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); } const Identifier* ident = m_token.m_data.ident; - int end = tokenEnd(); + JSTextPosition end = tokenEndPosition(); next(); - consumeOrFail(COLON); + consumeOrFail(COLON, "Labels must be followed by a ':'"); 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)); + failIfTrue(ident->impl() == labels[i].m_ident->impl(), "Attempted to redeclare the label '", ident->impl(), "'"); + failIfTrue(getLabel(ident), "Cannot find scope for the label '", ident->impl(), "'"); labels.append(LabelInfo(ident, start, end)); } } while (match(IDENT)); @@ -925,10 +1942,10 @@ template TreeStatement Parser::parseExpressionOrL for (size_t i = 0; i < labels.size(); i++) popLabel(); } - failIfFalse(statement); + failIfFalse(statement, "Cannot parse statement"); for (size_t i = 0; i < labels.size(); i++) { const LabelInfo& info = labels[labels.size() - i - 1]; - statement = context.createLabelStatement(m_lexer->lastLineNumber(), info.m_ident, statement, info.m_start, info.m_end); + statement = context.createLabelStatement(location, info.m_ident, statement, info.m_start, info.m_end); } return statement; } @@ -936,45 +1953,60 @@ template TreeStatement Parser::parseExpressionOrL template template TreeStatement Parser::parseExpressionStatement(TreeBuilder& context) { - int startLine = tokenLine(); + switch (m_token.m_type) { + // Consult: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-expression-statement + // The ES6 spec mandates that we should fail from FUNCTION token here. We handle this case + // in parseStatement() which is the only caller of parseExpressionStatement(). + // We actually allow FUNCTION in situations where it should not be allowed unless we're in strict mode. + case CLASSTOKEN: + failWithMessage("'class' declaration is not directly within a block statement"); + break; + default: + // FIXME: when implementing 'let' we should fail when we see the token sequence "let [". + // https://bugs.webkit.org/show_bug.cgi?id=142944 + break; + } + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); TreeExpression expression = parseExpression(context); - failIfFalse(expression); - failIfFalse(autoSemiColon()); - return context.createExprStatement(m_lexer->lastLineNumber(), expression, startLine, m_lastLine); + failIfFalse(expression, "Cannot parse expression statement"); + failIfFalse(autoSemiColon(), "Parse error"); + return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); } template template TreeStatement Parser::parseIfStatement(TreeBuilder& context) { ASSERT(match(IF)); - + JSTokenLocation ifLocation(tokenLocation()); int start = tokenLine(); next(); - - consumeOrFail(OPENPAREN); - + handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); + TreeExpression condition = parseExpression(context); - failIfFalse(condition); + failIfFalse(condition, "Expected a expression as the condition for an if statement"); int end = tokenLine(); - consumeOrFail(CLOSEPAREN); - + handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); + const Identifier* unused = 0; TreeStatement trueBlock = parseStatement(context, unused); - failIfFalse(trueBlock); - + failIfFalse(trueBlock, "Expected a statement as the body of an if block"); + if (!match(ELSE)) - return context.createIfStatement(m_lexer->lastLineNumber(), condition, trueBlock, start, end); - + return context.createIfStatement(ifLocation, condition, trueBlock, 0, start, end); + Vector exprStack; - Vector > posStack; + Vector> posStack; + Vector tokenLocationStack; Vector statementStack; bool trailingElse = false; do { + JSTokenLocation tempLocation = tokenLocation(); next(); if (!match(IF)) { const Identifier* unused = 0; TreeStatement block = parseStatement(context, unused); - failIfFalse(block); + failIfFalse(block, "Expected a statement as the body of an else block"); statementStack.append(block); trailingElse = true; break; @@ -982,30 +2014,35 @@ template TreeStatement Parser::parseIfStatement(T int innerStart = tokenLine(); next(); - consumeOrFail(OPENPAREN); - + handleProductionOrFail(OPENPAREN, "(", "start", "'if' condition"); + TreeExpression innerCondition = parseExpression(context); - failIfFalse(innerCondition); + failIfFalse(innerCondition, "Expected a expression as the condition for an if statement"); int innerEnd = tokenLine(); - consumeOrFail(CLOSEPAREN); + handleProductionOrFail(CLOSEPAREN, ")", "end", "'if' condition"); const Identifier* unused = 0; TreeStatement innerTrueBlock = parseStatement(context, unused); - failIfFalse(innerTrueBlock); + failIfFalse(innerTrueBlock, "Expected a statement as the body of an if block"); + tokenLocationStack.append(tempLocation); exprStack.append(innerCondition); - posStack.append(make_pair(innerStart, innerEnd)); + posStack.append(std::make_pair(innerStart, innerEnd)); statementStack.append(innerTrueBlock); } while (match(ELSE)); - + if (!trailingElse) { TreeExpression condition = exprStack.last(); exprStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); - pair pos = posStack.last(); + std::pair pos = posStack.last(); posStack.removeLast(); - statementStack.append(context.createIfStatement(m_lexer->lastLineNumber(), condition, trueBlock, pos.first, pos.second)); + JSTokenLocation elseLocation = tokenLocationStack.last(); + tokenLocationStack.removeLast(); + TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, 0, pos.first, pos.second); + context.setEndOffset(ifStatement, context.endOffset(trueBlock)); + statementStack.append(ifStatement); } - + while (!exprStack.isEmpty()) { TreeExpression condition = exprStack.last(); exprStack.removeLast(); @@ -1013,48 +2050,80 @@ template TreeStatement Parser::parseIfStatement(T statementStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); - pair pos = posStack.last(); + std::pair pos = posStack.last(); posStack.removeLast(); - statementStack.append(context.createIfStatement(m_lexer->lastLineNumber(), condition, trueBlock, falseBlock, pos.first, pos.second)); + JSTokenLocation elseLocation = tokenLocationStack.last(); + tokenLocationStack.removeLast(); + TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, falseBlock, pos.first, pos.second); + context.setEndOffset(ifStatement, context.endOffset(falseBlock)); + statementStack.append(ifStatement); } - - return context.createIfStatement(m_lexer->lastLineNumber(), condition, trueBlock, statementStack.last(), start, end); + + return context.createIfStatement(ifLocation, condition, trueBlock, statementStack.last(), start, end); } template template TreeExpression Parser::parseExpression(TreeBuilder& context) { failIfStackOverflow(); + JSTokenLocation location(tokenLocation()); TreeExpression node = parseAssignmentExpression(context); - failIfFalse(node); + failIfFalse(node, "Cannot parse expression"); + context.setEndOffset(node, m_lastTokenEndPosition.offset); if (!match(COMMA)) return node; next(); m_nonTrivialExpressionCount++; m_nonLHSCount++; TreeExpression right = parseAssignmentExpression(context); - failIfFalse(right); - typename TreeBuilder::Comma commaNode = context.createCommaExpr(m_lexer->lastLineNumber(), node, right); + failIfFalse(right, "Cannot parse expression in a comma expression"); + context.setEndOffset(right, m_lastTokenEndPosition.offset); + typename TreeBuilder::Comma head = context.createCommaExpr(location, node); + typename TreeBuilder::Comma tail = context.appendToCommaExpr(location, head, head, right); while (match(COMMA)) { next(TreeBuilder::DontBuildStrings); right = parseAssignmentExpression(context); - failIfFalse(right); - context.appendToComma(commaNode, right); + failIfFalse(right, "Cannot parse expression in a comma expression"); + context.setEndOffset(right, m_lastTokenEndPosition.offset); + tail = context.appendToCommaExpr(location, head, tail, right); } - return commaNode; + context.setEndOffset(head, m_lastTokenEndPosition.offset); + return head; } + template template TreeExpression Parser::parseAssignmentExpression(TreeBuilder& context) { failIfStackOverflow(); - int start = tokenStart(); + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); int initialAssignmentCount = m_assignmentCount; int initialNonLHSCount = m_nonLHSCount; + if (match(OPENBRACE) || match(OPENBRACKET)) { + SavePoint savePoint = createSavePoint(); + auto pattern = tryParseDestructuringPatternExpression(context); + if (pattern && consume(EQUAL)) { + auto rhs = parseAssignmentExpression(context); + if (rhs) + return context.createDestructuringAssignment(location, pattern, rhs); + } + restoreSavePoint(savePoint); + } + +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) + if (isArrowFunctionParamters()) + return parseArrowFunctionExpression(context); +#endif + TreeExpression lhs = parseConditionalExpression(context); - failIfFalse(lhs); - if (initialNonLHSCount != m_nonLHSCount) + failIfFalse(lhs, "Cannot parse expression"); + if (initialNonLHSCount != m_nonLHSCount) { + if (m_token.m_type >= EQUAL && m_token.m_type <= OREQUAL) + semanticFail("Left hand side of operator '", getToken(), "' must be a reference"); + return lhs; + } int assignmentStack = 0; Operator op; @@ -1078,20 +2147,23 @@ template TreeExpression Parser::parseAssignmen } m_nonTrivialExpressionCount++; hadAssignment = true; - context.assignmentStackAppend(assignmentStack, lhs, start, tokenStart(), m_assignmentCount, op); - start = tokenStart(); + context.assignmentStackAppend(assignmentStack, lhs, start, tokenStartPosition(), m_assignmentCount, op); + start = tokenStartPosition(); m_assignmentCount++; next(TreeBuilder::DontBuildStrings); if (strictMode() && m_lastIdentifier && context.isResolve(lhs)) { - failIfTrueIfStrictWithMessage(m_globalData->propertyNames->eval == *m_lastIdentifier, "'eval' cannot be modified in strict mode"); - failIfTrueIfStrictWithMessage(m_globalData->propertyNames->arguments == *m_lastIdentifier, "'arguments' cannot be modified in strict mode"); + failIfTrueIfStrict(m_vm->propertyNames->eval == *m_lastIdentifier, "Cannot modify 'eval' in strict mode"); + failIfTrueIfStrict(m_vm->propertyNames->arguments == *m_lastIdentifier, "Cannot modify 'arguments' in strict mode"); declareWrite(m_lastIdentifier); m_lastIdentifier = 0; } - lhs = parseConditionalExpression(context); - failIfFalse(lhs); - if (initialNonLHSCount != m_nonLHSCount) + lhs = parseAssignmentExpression(context); + failIfFalse(lhs, "Cannot parse the right hand side of an assignment expression"); + if (initialNonLHSCount != m_nonLHSCount) { + if (m_token.m_type >= EQUAL && m_token.m_type <= OREQUAL) + semanticFail("Left hand side of operator '", getToken(), "' must be a reference"); break; + } } end: if (hadAssignment) @@ -1101,7 +2173,7 @@ end: return lhs; while (assignmentStack) - lhs = context.createAssignment(m_lexer->lastLineNumber(), assignmentStack, lhs, initialAssignmentCount, m_assignmentCount, lastTokenEnd()); + lhs = context.createAssignment(location, assignmentStack, lhs, initialAssignmentCount, m_assignmentCount, lastTokenEndPosition()); return lhs; } @@ -1109,19 +2181,23 @@ end: template template TreeExpression Parser::parseConditionalExpression(TreeBuilder& context) { + JSTokenLocation location(tokenLocation()); TreeExpression cond = parseBinaryExpression(context); - failIfFalse(cond); + failIfFalse(cond, "Cannot parse expression"); if (!match(QUESTION)) return cond; m_nonTrivialExpressionCount++; m_nonLHSCount++; next(TreeBuilder::DontBuildStrings); TreeExpression lhs = parseAssignmentExpression(context); - consumeOrFailWithFlags(COLON, TreeBuilder::DontBuildStrings); + failIfFalse(lhs, "Cannot parse left hand side of ternary operator"); + context.setEndOffset(lhs, m_lastTokenEndPosition.offset); + consumeOrFailWithFlags(COLON, TreeBuilder::DontBuildStrings, "Expected ':' in ternary operator"); TreeExpression rhs = parseAssignmentExpression(context); - failIfFalse(rhs); - return context.createConditionalExpr(m_lexer->lastLineNumber(), cond, lhs, rhs); + failIfFalse(rhs, "Cannot parse right hand side of ternary operator"); + context.setEndOffset(rhs, m_lastTokenEndPosition.offset); + return context.createConditionalExpr(location, cond, lhs, rhs); } ALWAYS_INLINE static bool isUnaryOp(JSTokenType token) @@ -1140,17 +2216,17 @@ int Parser::isBinaryOperator(JSTokenType token) template template TreeExpression Parser::parseBinaryExpression(TreeBuilder& context) { - int operandStackDepth = 0; int operatorStackDepth = 0; typename TreeBuilder::BinaryExprContext binaryExprContext(context); + JSTokenLocation location(tokenLocation()); while (true) { - int exprStart = tokenStart(); + JSTextPosition exprStart = tokenStartPosition(); int initialAssignments = m_assignmentCount; TreeExpression current = parseUnaryExpression(context); - failIfFalse(current); + failIfFalse(current, "Cannot parse expression"); - context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEnd(), lastTokenEnd(), initialAssignments != m_assignmentCount); + context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEndPosition(), lastTokenEndPosition(), initialAssignments != m_assignmentCount); int precedence = isBinaryOperator(m_token.m_type); if (!precedence) break; @@ -1165,7 +2241,7 @@ template TreeExpression Parser::parseBinaryExpres typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); context.shrinkOperandStackBy(operandStackDepth, 2); - context.appendBinaryOperation(m_lexer->lastLineNumber(), operandStackDepth, operatorStackDepth, lhs, rhs); + context.appendBinaryOperation(location, operandStackDepth, operatorStackDepth, lhs, rhs); context.operatorStackPop(operatorStackDepth); } context.operatorStackAppend(operatorStackDepth, operatorToken, precedence); @@ -1176,178 +2252,267 @@ template TreeExpression Parser::parseBinaryExpres typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); context.shrinkOperandStackBy(operandStackDepth, 2); - context.appendBinaryOperation(m_lexer->lastLineNumber(), operandStackDepth, operatorStackDepth, lhs, rhs); + context.appendBinaryOperation(location, operandStackDepth, operatorStackDepth, lhs, rhs); context.operatorStackPop(operatorStackDepth); } return context.popOperandStack(operandStackDepth); } template -template TreeProperty Parser::parseProperty(TreeBuilder& context) +template TreeProperty Parser::parseProperty(TreeBuilder& context, bool complete) { bool wasIdent = false; switch (m_token.m_type) { namedProperty: case IDENT: wasIdent = true; + FALLTHROUGH; case STRING: { const Identifier* ident = m_token.m_data.ident; - if (complete || (wasIdent && (*ident == m_globalData->propertyNames->get || *ident == m_globalData->propertyNames->set))) + unsigned getterOrSetterStartOffset = tokenStart(); + if (complete || (wasIdent && (*ident == m_vm->propertyNames->get || *ident == m_vm->propertyNames->set))) nextExpectIdentifier(LexerFlagsIgnoreReservedWords); else nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); - + if (match(COLON)) { next(); TreeExpression node = parseAssignmentExpression(context); - failIfFalse(node); - return context.template createProperty(ident, node, PropertyNode::Constant); + failIfFalse(node, "Cannot parse expression for property declaration"); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(ident, node, PropertyNode::Constant, PropertyNode::Unknown, complete); + } + + if (match(OPENPAREN)) { + auto method = parsePropertyMethod(context, ident); + propagateError(); + return context.createProperty(ident, method, PropertyNode::Constant, PropertyNode::KnownDirect, complete); + } + + failIfFalse(wasIdent, "Expected an identifier as property name"); + + if (match(COMMA) || match(CLOSEBRACE)) { + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); + currentScope()->useVariable(ident, m_vm->propertyNames->eval == *ident); + TreeExpression node = context.createResolve(location, ident, start); + return context.createProperty(ident, node, static_cast(PropertyNode::Constant | PropertyNode::Shorthand), PropertyNode::KnownDirect, complete); } - failIfFalse(wasIdent); - 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) + if (*ident == m_vm->propertyNames->get) type = PropertyNode::Getter; - else if (*ident == m_globalData->propertyNames->set) + else if (*ident == m_vm->propertyNames->set) type = PropertyNode::Setter; else - fail(); - const Identifier* stringPropertyName = 0; - double numericPropertyName = 0; - if (m_token.m_type == IDENT || m_token.m_type == STRING) - stringPropertyName = m_token.m_data.ident; - else if (m_token.m_type == NUMBER) - numericPropertyName = m_token.m_data.doubleValue; - else - fail(); - next(); - failIfFalse((parseFunctionInfo(context, accessorName, parameters, body, openBracePos, closeBracePos, bodyStartLine))); - if (stringPropertyName) - return context.template createGetterOrSetterProperty(m_lexer->lastLineNumber(), type, stringPropertyName, parameters, body, openBracePos, closeBracePos, bodyStartLine, m_lastLine); - return context.template createGetterOrSetterProperty(const_cast(m_globalData), m_lexer->lastLineNumber(), type, numericPropertyName, parameters, body, openBracePos, closeBracePos, bodyStartLine, m_lastLine); + failWithMessage("Expected a ':' following the property name '", ident->impl(), "'"); + return parseGetterSetter(context, complete, type, getterOrSetterStartOffset); } - case NUMBER: { + case DOUBLE: + case INTEGER: { double propertyName = m_token.m_data.doubleValue; next(); - consumeOrFail(COLON); + + if (match(OPENPAREN)) { + const Identifier& ident = m_parserArena.identifierArena().makeNumericIdentifier(const_cast(m_vm), propertyName); + auto method = parsePropertyMethod(context, &ident); + propagateError(); + return context.createProperty(&ident, method, PropertyNode::Constant, PropertyNode::Unknown, complete); + } + + consumeOrFail(COLON, "Expected ':' after property name"); + TreeExpression node = parseAssignmentExpression(context); + failIfFalse(node, "Cannot parse expression for property declaration"); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(const_cast(m_vm), m_parserArena, propertyName, node, PropertyNode::Constant, PropertyNode::Unknown, complete); + } + case OPENBRACKET: { + next(); + auto propertyName = parseAssignmentExpression(context); + failIfFalse(propertyName, "Cannot parse computed property name"); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); + + if (match(OPENPAREN)) { + auto method = parsePropertyMethod(context, &m_vm->propertyNames->nullIdentifier); + propagateError(); + return context.createProperty(propertyName, method, static_cast(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::KnownDirect, complete); + } + + consumeOrFail(COLON, "Expected ':' after property name"); TreeExpression node = parseAssignmentExpression(context); - failIfFalse(node); - return context.template createProperty(const_cast(m_globalData), propertyName, node, PropertyNode::Constant); + failIfFalse(node, "Cannot parse expression for property declaration"); + context.setEndOffset(node, m_lexer->currentOffset()); + return context.createProperty(propertyName, node, static_cast(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::Unknown, complete); } default: - failIfFalse(m_token.m_type & KeywordTokenFlag); + failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name"); goto namedProperty; } } +template +template TreeExpression Parser::parsePropertyMethod(TreeBuilder& context, const Identifier* methodName) +{ + JSTokenLocation methodLocation(tokenLocation()); + unsigned methodStart = tokenStart(); + ParserFunctionInfo methodInfo; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, MethodMode, false, ConstructorKind::None, SuperBinding::NotNeeded, methodStart, methodInfo, StandardFunctionParseType)), "Cannot parse this method"); + methodInfo.name = methodName; + return context.createFunctionExpr(methodLocation, methodInfo); +} + +template +template TreeProperty Parser::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset, + ConstructorKind constructorKind, SuperBinding superBinding) +{ + const Identifier* stringPropertyName = 0; + double numericPropertyName = 0; + if (m_token.m_type == IDENT || m_token.m_type == STRING) { + stringPropertyName = m_token.m_data.ident; + semanticFailIfTrue(superBinding == SuperBinding::Needed && *stringPropertyName == m_vm->propertyNames->prototype, + "Cannot declare a static method named 'prototype'"); + semanticFailIfTrue(superBinding == SuperBinding::Needed && *stringPropertyName == m_vm->propertyNames->constructor, + "Cannot declare a getter or setter named 'constructor'"); + } else if (m_token.m_type == DOUBLE || m_token.m_type == INTEGER) + numericPropertyName = m_token.m_data.doubleValue; + else + failDueToUnexpectedToken(); + JSTokenLocation location(tokenLocation()); + next(); + ParserFunctionInfo info; + if (type & PropertyNode::Getter) { + failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition"); + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, constructorKind, superBinding, + getterOrSetterStartOffset, info, StandardFunctionParseType)), "Cannot parse getter definition"); + } else { + failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition"); + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, constructorKind, superBinding, + getterOrSetterStartOffset, info, StandardFunctionParseType)), "Cannot parse setter definition"); + } + if (stringPropertyName) + return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, superBinding); + return context.createGetterOrSetterProperty(const_cast(m_vm), m_parserArena, location, type, strict, numericPropertyName, info, superBinding); +} + +template +template bool Parser::shouldCheckPropertyForUnderscoreProtoDuplicate(TreeBuilder& context, const TreeProperty& property) +{ + if (m_syntaxAlreadyValidated) + return false; + + if (!context.getName(property)) + return false; + + // A Constant property that is not a Computed or Shorthand Constant property. + return context.getType(property) == PropertyNode::Constant; +} + template template TreeExpression Parser::parseObjectLiteral(TreeBuilder& context) { - int startOffset = m_token.m_data.intValue; - unsigned oldLastLineNumber = m_lexer->lastLineNumber(); - unsigned oldLineNumber = m_lexer->lineNumber(); - consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings); - + auto savePoint = createSavePoint(); + consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings, "Expected opening '{' at the start of an object literal"); + int oldNonLHSCount = m_nonLHSCount; - + + JSTokenLocation location(tokenLocation()); if (match(CLOSEBRACE)) { next(); - return context.createObjectLiteral(m_lexer->lastLineNumber()); + return context.createObjectLiteral(location); } - TreeProperty property = parseProperty(context); - failIfFalse(property); - if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { - m_lexer->setOffset(startOffset); - next(); - m_lexer->setLastLineNumber(oldLastLineNumber); - m_lexer->setLineNumber(oldLineNumber); + TreeProperty property = parseProperty(context, false); + failIfFalse(property, "Cannot parse object literal property"); + + if (!m_syntaxAlreadyValidated && context.getType(property) & (PropertyNode::Getter | PropertyNode::Setter)) { + restoreSavePoint(savePoint); return parseStrictObjectLiteral(context); } - TreePropertyList propertyList = context.createPropertyList(m_lexer->lastLineNumber(), property); + + bool seenUnderscoreProto = false; + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) + seenUnderscoreProto = *context.getName(property) == m_vm->propertyNames->underscoreProto; + + TreePropertyList propertyList = context.createPropertyList(location, property); TreePropertyList tail = propertyList; while (match(COMMA)) { next(TreeBuilder::DontBuildStrings); - // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 if (match(CLOSEBRACE)) break; - property = parseProperty(context); - failIfFalse(property); - if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) { - m_lexer->setOffset(startOffset); - next(); - m_lexer->setLastLineNumber(oldLastLineNumber); - m_lexer->setLineNumber(oldLineNumber); + JSTokenLocation propertyLocation(tokenLocation()); + property = parseProperty(context, false); + failIfFalse(property, "Cannot parse object literal property"); + if (!m_syntaxAlreadyValidated && context.getType(property) & (PropertyNode::Getter | PropertyNode::Setter)) { + restoreSavePoint(savePoint); return parseStrictObjectLiteral(context); } - tail = context.createPropertyList(m_lexer->lastLineNumber(), property, tail); + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) { + if (*context.getName(property) == m_vm->propertyNames->underscoreProto) { + semanticFailIfTrue(seenUnderscoreProto, "Attempted to redefine __proto__ property"); + seenUnderscoreProto = true; + } + } + tail = context.createPropertyList(propertyLocation, property, tail); } - - consumeOrFail(CLOSEBRACE); + + location = tokenLocation(); + handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); m_nonLHSCount = oldNonLHSCount; - return context.createObjectLiteral(m_lexer->lastLineNumber(), propertyList); + return context.createObjectLiteral(location, propertyList); } template template TreeExpression Parser::parseStrictObjectLiteral(TreeBuilder& context) { - consumeOrFail(OPENBRACE); + consumeOrFail(OPENBRACE, "Expected opening '{' at the start of an object literal"); int oldNonLHSCount = m_nonLHSCount; + JSTokenLocation location(tokenLocation()); if (match(CLOSEBRACE)) { next(); - return context.createObjectLiteral(m_lexer->lastLineNumber()); + return context.createObjectLiteral(location); } - TreeProperty property = parseProperty(context); - failIfFalse(property); - - typedef HashMap, unsigned, IdentifierRepHash> ObjectValidationMap; - ObjectValidationMap objectValidator; - // Add the first property - if (!m_syntaxAlreadyValidated) - objectValidator.add(context.getName(property).impl(), context.getType(property)); - - TreePropertyList propertyList = context.createPropertyList(m_lexer->lastLineNumber(), property); + TreeProperty property = parseProperty(context, true); + failIfFalse(property, "Cannot parse object literal property"); + + bool seenUnderscoreProto = false; + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) + seenUnderscoreProto = *context.getName(property) == m_vm->propertyNames->underscoreProto; + + TreePropertyList propertyList = context.createPropertyList(location, property); TreePropertyList tail = propertyList; while (match(COMMA)) { next(); - // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 if (match(CLOSEBRACE)) break; - property = parseProperty(context); - failIfFalse(property); - if (!m_syntaxAlreadyValidated) { - ObjectValidationMap::AddResult propertyEntry = objectValidator.add(context.getName(property).impl(), context.getType(property)); - if (!propertyEntry.isNewEntry) { - failIfTrue(propertyEntry.iterator->second == PropertyNode::Constant); - failIfTrue(context.getType(property) == PropertyNode::Constant); - failIfTrue(context.getType(property) & propertyEntry.iterator->second); - propertyEntry.iterator->second |= context.getType(property); + JSTokenLocation propertyLocation(tokenLocation()); + property = parseProperty(context, true); + failIfFalse(property, "Cannot parse object literal property"); + if (shouldCheckPropertyForUnderscoreProtoDuplicate(context, property)) { + if (*context.getName(property) == m_vm->propertyNames->underscoreProto) { + semanticFailIfTrue(seenUnderscoreProto, "Attempted to redefine __proto__ property"); + seenUnderscoreProto = true; } } - tail = context.createPropertyList(m_lexer->lastLineNumber(), property, tail); + tail = context.createPropertyList(propertyLocation, property, tail); } - - consumeOrFail(CLOSEBRACE); + + location = tokenLocation(); + handleProductionOrFail(CLOSEBRACE, "}", "end", "object literal"); m_nonLHSCount = oldNonLHSCount; - return context.createObjectLiteral(m_lexer->lastLineNumber(), propertyList); + return context.createObjectLiteral(location, propertyList); } template template TreeExpression Parser::parseArrayLiteral(TreeBuilder& context) { - consumeOrFailWithFlags(OPENBRACKET, TreeBuilder::DontBuildStrings); + consumeOrFailWithFlags(OPENBRACKET, TreeBuilder::DontBuildStrings, "Expected an opening '[' at the beginning of an array literal"); int oldNonLHSCount = m_nonLHSCount; @@ -1357,12 +2522,23 @@ template TreeExpression Parser::parseArrayLiteral elisions++; } if (match(CLOSEBRACKET)) { + JSTokenLocation location(tokenLocation()); next(TreeBuilder::DontBuildStrings); - return context.createArray(m_lexer->lastLineNumber(), elisions); + return context.createArray(location, elisions); } - TreeExpression elem = parseAssignmentExpression(context); - failIfFalse(elem); + TreeExpression elem; + if (UNLIKELY(match(DOTDOTDOT))) { + auto spreadLocation = m_token.m_location; + auto start = m_token.m_startPosition; + auto divot = m_token.m_endPosition; + next(); + auto spreadExpr = parseAssignmentExpression(context); + failIfFalse(spreadExpr, "Cannot parse subject of a spread operation"); + elem = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, m_lastTokenEndPosition); + } else + elem = parseAssignmentExpression(context); + failIfFalse(elem, "Cannot parse array literal element"); typename TreeBuilder::ElementList elementList = context.createElementList(elisions, elem); typename TreeBuilder::ElementList tail = elementList; elisions = 0; @@ -1376,26 +2552,117 @@ template TreeExpression Parser::parseArrayLiteral } if (match(CLOSEBRACKET)) { + JSTokenLocation location(tokenLocation()); next(TreeBuilder::DontBuildStrings); - return context.createArray(m_lexer->lastLineNumber(), elisions, elementList); + return context.createArray(location, elisions, elementList); + } + if (UNLIKELY(match(DOTDOTDOT))) { + auto spreadLocation = m_token.m_location; + auto start = m_token.m_startPosition; + auto divot = m_token.m_endPosition; + next(); + TreeExpression elem = parseAssignmentExpression(context); + failIfFalse(elem, "Cannot parse subject of a spread operation"); + auto spread = context.createSpreadExpression(spreadLocation, elem, start, divot, m_lastTokenEndPosition); + tail = context.createElementList(tail, elisions, spread); + continue; } TreeExpression elem = parseAssignmentExpression(context); - failIfFalse(elem); + failIfFalse(elem, "Cannot parse array literal element"); tail = context.createElementList(tail, elisions, elem); } - - consumeOrFail(CLOSEBRACKET); + + JSTokenLocation location(tokenLocation()); + if (!consume(CLOSEBRACKET)) { + failIfFalse(match(DOTDOTDOT), "Expected either a closing ']' or a ',' following an array element"); + semanticFail("The '...' operator should come before a target expression"); + } m_nonLHSCount = oldNonLHSCount; - return context.createArray(m_lexer->lastLineNumber(), elementList); + return context.createArray(location, elementList); +} + +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) +template +template typename TreeBuilder::TemplateString Parser::parseTemplateString(TreeBuilder& context, bool isTemplateHead, typename LexerType::RawStringsBuildMode rawStringsBuildMode, bool& elementIsTail) +{ + if (!isTemplateHead) { + matchOrFail(CLOSEBRACE, "Expected a closing '}' following an expression in template literal"); + // Re-scan the token to recognize it as Template Element. + m_token.m_type = m_lexer->scanTrailingTemplateString(&m_token, rawStringsBuildMode); + } + matchOrFail(TEMPLATE, "Expected an template element"); + const Identifier* cooked = m_token.m_data.cooked; + const Identifier* raw = m_token.m_data.raw; + elementIsTail = m_token.m_data.isTail; + JSTokenLocation location(tokenLocation()); + next(); + return context.createTemplateString(location, *cooked, *raw); +} + +template +template typename TreeBuilder::TemplateLiteral Parser::parseTemplateLiteral(TreeBuilder& context, typename LexerType::RawStringsBuildMode rawStringsBuildMode) +{ + JSTokenLocation location(tokenLocation()); + bool elementIsTail = false; + + auto headTemplateString = parseTemplateString(context, true, rawStringsBuildMode, elementIsTail); + failIfFalse(headTemplateString, "Cannot parse head template element"); + + typename TreeBuilder::TemplateStringList templateStringList = context.createTemplateStringList(headTemplateString); + typename TreeBuilder::TemplateStringList templateStringTail = templateStringList; + + if (elementIsTail) + return context.createTemplateLiteral(location, templateStringList); + + failIfTrue(match(CLOSEBRACE), "Template literal expression cannot be empty"); + TreeExpression expression = parseExpression(context); + failIfFalse(expression, "Cannot parse expression in template literal"); + + typename TreeBuilder::TemplateExpressionList templateExpressionList = context.createTemplateExpressionList(expression); + typename TreeBuilder::TemplateExpressionList templateExpressionTail = templateExpressionList; + + auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail); + failIfFalse(templateString, "Cannot parse template element"); + templateStringTail = context.createTemplateStringList(templateStringTail, templateString); + + while (!elementIsTail) { + failIfTrue(match(CLOSEBRACE), "Template literal expression cannot be empty"); + TreeExpression expression = parseExpression(context); + failIfFalse(expression, "Cannot parse expression in template literal"); + + templateExpressionTail = context.createTemplateExpressionList(templateExpressionTail, expression); + + auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail); + failIfFalse(templateString, "Cannot parse template element"); + templateStringTail = context.createTemplateStringList(templateStringTail, templateString); + } + + return context.createTemplateLiteral(location, templateStringList, templateExpressionList); } +#endif template template TreeExpression Parser::parsePrimaryExpression(TreeBuilder& context) { failIfStackOverflow(); switch (m_token.m_type) { + case FUNCTION: { + JSTokenLocation location(tokenLocation()); + unsigned functionKeywordStart = tokenStart(); + next(); + ParserFunctionInfo info; + info.name = &m_vm->propertyNames->nullIdentifier; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, FunctionMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, info, StandardFunctionParseType)), "Cannot parse function expression"); + return context.createFunctionExpr(location, info); + } +#if ENABLE(ES6_CLASS_SYNTAX) + case CLASSTOKEN: { + ParserClassInfo info; + return parseClass(context, FunctionNoRequirements, info); + } +#endif case OPENBRACE: if (strictMode()) return parseStrictObjectLiteral(context); @@ -1407,43 +2674,55 @@ template TreeExpression Parser::parsePrimaryExpre int oldNonLHSCount = m_nonLHSCount; TreeExpression result = parseExpression(context); m_nonLHSCount = oldNonLHSCount; - consumeOrFail(CLOSEPAREN); - + handleProductionOrFail(CLOSEPAREN, ")", "end", "compound expression"); return result; } case THISTOKEN: { + JSTokenLocation location(tokenLocation()); next(); - return context.thisExpr(m_lexer->lastLineNumber()); + return context.thisExpr(location, m_thisTDZMode); } case IDENT: { - int start = tokenStart(); + JSTextPosition start = tokenStartPosition(); const Identifier* ident = m_token.m_data.ident; + JSTokenLocation location(tokenLocation()); next(); - currentScope()->useVariable(ident, m_globalData->propertyNames->eval == *ident); + currentScope()->useVariable(ident, m_vm->propertyNames->eval == *ident); m_lastIdentifier = ident; - return context.createResolve(m_lexer->lastLineNumber(), ident, start); + return context.createResolve(location, ident, start); } case STRING: { const Identifier* ident = m_token.m_data.ident; + JSTokenLocation location(tokenLocation()); + next(); + return context.createString(location, ident); + } + case DOUBLE: { + double d = m_token.m_data.doubleValue; + JSTokenLocation location(tokenLocation()); next(); - return context.createString(m_lexer->lastLineNumber(), ident); + return context.createDoubleExpr(location, d); } - case NUMBER: { + case INTEGER: { double d = m_token.m_data.doubleValue; + JSTokenLocation location(tokenLocation()); next(); - return context.createNumberExpr(m_lexer->lastLineNumber(), d); + return context.createIntegerExpr(location, d); } case NULLTOKEN: { + JSTokenLocation location(tokenLocation()); next(); - return context.createNull(m_lexer->lastLineNumber()); + return context.createNull(location); } case TRUETOKEN: { + JSTokenLocation location(tokenLocation()); next(); - return context.createBoolean(m_lexer->lastLineNumber(), true); + return context.createBoolean(location, true); } case FALSETOKEN: { + JSTokenLocation location(tokenLocation()); next(); - return context.createBoolean(m_lexer->lastLineNumber(), false); + return context.createBoolean(location, false); } case DIVEQUAL: case DIVIDE: { @@ -1451,45 +2730,70 @@ template TreeExpression Parser::parsePrimaryExpre const Identifier* pattern; const Identifier* flags; if (match(DIVEQUAL)) - failIfFalse(m_lexer->scanRegExp(pattern, flags, '=')); + failIfFalse(m_lexer->scanRegExp(pattern, flags, '='), "Invalid regular expression"); else - failIfFalse(m_lexer->scanRegExp(pattern, flags)); + failIfFalse(m_lexer->scanRegExp(pattern, flags), "Invalid regular expression"); - int start = tokenStart(); + JSTextPosition start = tokenStartPosition(); + JSTokenLocation location(tokenLocation()); next(); - TreeExpression re = context.createRegExp(m_lexer->lastLineNumber(), *pattern, *flags, start); + TreeExpression re = context.createRegExp(location, *pattern, *flags, start); if (!re) { - const char* yarrErrorMsg = Yarr::checkSyntax(pattern->ustring()); - ASSERT(!m_errorMessage.isNull()); - failWithMessage(yarrErrorMsg); + const char* yarrErrorMsg = Yarr::checkSyntax(pattern->string()); + regexFail(yarrErrorMsg); } return re; } +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + case TEMPLATE: + return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings); +#endif default: - fail(); + failDueToUnexpectedToken(); } } template -template TreeArguments Parser::parseArguments(TreeBuilder& context) +template TreeArguments Parser::parseArguments(TreeBuilder& context, SpreadMode mode) { - consumeOrFailWithFlags(OPENPAREN, TreeBuilder::DontBuildStrings); + consumeOrFailWithFlags(OPENPAREN, TreeBuilder::DontBuildStrings, "Expected opening '(' at start of argument list"); + JSTokenLocation location(tokenLocation()); if (match(CLOSEPAREN)) { next(TreeBuilder::DontBuildStrings); return context.createArguments(); } + if (match(DOTDOTDOT) && mode == AllowSpread) { + JSTokenLocation spreadLocation(tokenLocation()); + auto start = m_token.m_startPosition; + auto divot = m_token.m_endPosition; + next(); + auto spreadExpr = parseAssignmentExpression(context); + auto end = m_lastTokenEndPosition; + if (!spreadExpr) + failWithMessage("Cannot parse spread expression"); + if (!consume(CLOSEPAREN)) { + if (match(COMMA)) + semanticFail("Spread operator may only be applied to the last argument passed to a function"); + handleProductionOrFail(CLOSEPAREN, ")", "end", "argument list"); + } + auto spread = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, end); + TreeArgumentsList argList = context.createArgumentsList(location, spread); + return context.createArguments(argList); + } TreeExpression firstArg = parseAssignmentExpression(context); - failIfFalse(firstArg); + failIfFalse(firstArg, "Cannot parse function argument"); - TreeArgumentsList argList = context.createArgumentsList(m_lexer->lastLineNumber(), firstArg); + TreeArgumentsList argList = context.createArgumentsList(location, firstArg); TreeArgumentsList tail = argList; while (match(COMMA)) { + JSTokenLocation argumentLocation(tokenLocation()); next(TreeBuilder::DontBuildStrings); TreeExpression arg = parseAssignmentExpression(context); - failIfFalse(arg); - tail = context.createArgumentsList(m_lexer->lastLineNumber(), tail, arg); + failIfFalse(arg, "Cannot parse function argument"); + tail = context.createArgumentsList(argumentLocation, tail, arg); } - consumeOrFail(CLOSEPAREN); + semanticFailIfTrue(match(DOTDOTDOT), "The '...' operator should come before the target expression"); + handleProductionOrFail(CLOSEPAREN, ")", "end", "argument list"); return context.createArguments(argList); } @@ -1497,40 +2801,43 @@ template template TreeExpression Parser::parseMemberExpression(TreeBuilder& context) { TreeExpression base = 0; - int start = tokenStart(); - int expressionStart = start; + JSTextPosition expressionStart = tokenStartPosition(); int newCount = 0; + JSTokenLocation startLocation = tokenLocation(); + JSTokenLocation location; 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; + +#if ENABLE(ES6_CLASS_SYNTAX) + bool baseIsSuper = match(SUPER); + semanticFailIfTrue(baseIsSuper && newCount, "Cannot use new with super"); +#else + bool baseIsSuper = false; +#endif + + if (baseIsSuper) { + base = context.superExpr(location); next(); - failIfFalse((parseFunctionInfo(context, name, parameters, body, openBracePos, closeBracePos, bodyStartLine))); - base = context.createFunctionExpr(m_lexer->lastLineNumber(), name, body, parameters, openBracePos, closeBracePos, bodyStartLine, m_lastLine); + currentScope()->setNeedsSuperBinding(); } else base = parsePrimaryExpression(context); - - failIfFalse(base); + + failIfFalse(base, "Cannot parse base expression"); while (true) { + location = tokenLocation(); switch (m_token.m_type) { case OPENBRACKET: { m_nonTrivialExpressionCount++; - int expressionEnd = lastTokenEnd(); + JSTextPosition expressionEnd = lastTokenEndPosition(); next(); int nonLHSCount = m_nonLHSCount; int initialAssignments = m_assignmentCount; TreeExpression property = parseExpression(context); - failIfFalse(property); - base = context.createBracketAccess(m_lexer->lastLineNumber(), base, property, initialAssignments != m_assignmentCount, expressionStart, expressionEnd, tokenEnd()); - consumeOrFail(CLOSEBRACKET); + failIfFalse(property, "Cannot parse subscript expression"); + base = context.createBracketAccess(location, base, property, initialAssignments != m_assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); + handleProductionOrFail(CLOSEBRACKET, "]", "end", "subscript expression"); m_nonLHSCount = nonLHSCount; break; } @@ -1539,38 +2846,100 @@ template TreeExpression Parser::parseMemberExpres int nonLHSCount = m_nonLHSCount; if (newCount) { newCount--; - int exprEnd = lastTokenEnd(); - TreeArguments arguments = parseArguments(context); - failIfFalse(arguments); - base = context.createNewExpr(m_lexer->lastLineNumber(), base, arguments, start, exprEnd, lastTokenEnd()); + JSTextPosition expressionEnd = lastTokenEndPosition(); + TreeArguments arguments = parseArguments(context, AllowSpread); + failIfFalse(arguments, "Cannot parse call arguments"); + base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); } else { - int expressionEnd = lastTokenEnd(); - TreeArguments arguments = parseArguments(context); - failIfFalse(arguments); - base = context.makeFunctionCallNode(m_lexer->lastLineNumber(), base, arguments, expressionStart, expressionEnd, lastTokenEnd()); + JSTextPosition expressionEnd = lastTokenEndPosition(); + TreeArguments arguments = parseArguments(context, AllowSpread); + failIfFalse(arguments, "Cannot parse call arguments"); + if (baseIsSuper) + currentScope()->setHasDirectSuper(); + base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); } m_nonLHSCount = nonLHSCount; break; } case DOT: { m_nonTrivialExpressionCount++; - int expressionEnd = lastTokenEnd(); + JSTextPosition expressionEnd = lastTokenEndPosition(); nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); - matchOrFail(IDENT); - base = context.createDotAccess(m_lexer->lastLineNumber(), base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEnd()); + matchOrFail(IDENT, "Expected a property name after '.'"); + base = context.createDotAccess(location, base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEndPosition()); next(); break; } +#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) + case TEMPLATE: { + semanticFailIfTrue(baseIsSuper, "Cannot use super as tag for tagged templates"); + JSTextPosition expressionEnd = lastTokenEndPosition(); + int nonLHSCount = m_nonLHSCount; + typename TreeBuilder::TemplateLiteral templateLiteral = parseTemplateLiteral(context, LexerType::RawStringsBuildMode::BuildRawStrings); + failIfFalse(templateLiteral, "Cannot parse template literal"); + base = context.createTaggedTemplate(location, base, templateLiteral, expressionStart, expressionEnd, lastTokenEndPosition()); + m_nonLHSCount = nonLHSCount; + break; + } +#endif default: goto endMemberExpression; } + baseIsSuper = false; } endMemberExpression: + semanticFailIfTrue(baseIsSuper, "Cannot reference super"); while (newCount--) - base = context.createNewExpr(m_lexer->lastLineNumber(), base, start, lastTokenEnd()); + base = context.createNewExpr(location, base, expressionStart, lastTokenEndPosition()); return base; } +#if ENABLE(ES6_ARROWFUNCTION_SYNTAX) +template +template TreeExpression Parser::parseArrowFunctionExpression(TreeBuilder& context) +{ + JSTokenLocation location; + + unsigned functionKeywordStart = tokenStart(); + location = tokenLocation(); + ParserFunctionInfo info; + info.name = &m_vm->propertyNames->nullIdentifier; + failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, FunctionMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, info, ArrowFunctionParseType)), "Cannot parse arrow function expression"); + + return context.createArrowFunctionExpr(location, info); +} +#endif + +static const char* operatorString(bool prefix, unsigned tok) +{ + switch (tok) { + case MINUSMINUS: + case AUTOMINUSMINUS: + return prefix ? "prefix-decrement" : "decrement"; + + case PLUSPLUS: + case AUTOPLUSPLUS: + return prefix ? "prefix-increment" : "increment"; + + case EXCLAMATION: + return "logical-not"; + + case TILDE: + return "bitwise-not"; + + case TYPEOF: + return "typeof"; + + case VOIDTOKEN: + return "void"; + + case DELETETOKEN: + return "delete"; + } + RELEASE_ASSERT_NOT_REACHED(); + return "error"; +} + template template TreeExpression Parser::parseUnaryExpression(TreeBuilder& context) { @@ -1579,6 +2948,7 @@ template TreeExpression Parser::parseUnaryExpress int tokenStackDepth = 0; bool modifiesExpr = false; bool requiresLExpr = false; + unsigned lastOperator = 0; while (isUnaryOp(m_token.m_type)) { if (strictMode()) { switch (m_token.m_type) { @@ -1586,94 +2956,105 @@ template TreeExpression Parser::parseUnaryExpress case MINUSMINUS: case AUTOPLUSPLUS: case AUTOMINUSMINUS: - failIfTrue(requiresLExpr); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); modifiesExpr = true; requiresLExpr = true; break; case DELETETOKEN: - failIfTrue(requiresLExpr); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); requiresLExpr = true; break; default: - failIfTrue(requiresLExpr); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); break; } } + lastOperator = m_token.m_type; m_nonLHSCount++; - context.appendUnaryToken(tokenStackDepth, m_token.m_type, tokenStart()); + context.appendUnaryToken(tokenStackDepth, m_token.m_type, tokenStartPosition()); next(); m_nonTrivialExpressionCount++; } - int subExprStart = tokenStart(); + JSTextPosition subExprStart = tokenStartPosition(); + ASSERT(subExprStart.offset >= subExprStart.lineStartOffset); + JSTokenLocation location(tokenLocation()); TreeExpression expr = parseMemberExpression(context); - failIfFalse(expr); + if (!expr) { + if (lastOperator) + failWithMessage("Cannot parse subexpression of ", operatorString(true, lastOperator), "operator"); + failWithMessage("Cannot parse member expression"); + } bool isEvalOrArguments = false; if (strictMode() && !m_syntaxAlreadyValidated) { if (context.isResolve(expr)) - isEvalOrArguments = *m_lastIdentifier == m_globalData->propertyNames->eval || *m_lastIdentifier == m_globalData->propertyNames->arguments; + isEvalOrArguments = *m_lastIdentifier == m_vm->propertyNames->eval || *m_lastIdentifier == m_vm->propertyNames->arguments; } - failIfTrueIfStrictWithNameAndMessage(isEvalOrArguments && modifiesExpr, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); + failIfTrueIfStrict(isEvalOrArguments && modifiesExpr, "Cannot modify '", m_lastIdentifier->impl(), "' in strict mode"); switch (m_token.m_type) { case PLUSPLUS: m_nonTrivialExpressionCount++; m_nonLHSCount++; - expr = context.makePostfixNode(m_lexer->lastLineNumber(), expr, OpPlusPlus, subExprStart, lastTokenEnd(), tokenEnd()); + expr = context.makePostfixNode(location, expr, OpPlusPlus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); m_assignmentCount++; - failIfTrueIfStrictWithNameAndMessage(isEvalOrArguments, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); - failIfTrueIfStrict(requiresLExpr); + failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_lastIdentifier->impl(), "' in strict mode"); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(false, lastOperator), " operator requires a reference expression"); + lastOperator = PLUSPLUS; next(); break; case MINUSMINUS: m_nonTrivialExpressionCount++; m_nonLHSCount++; - expr = context.makePostfixNode(m_lexer->lastLineNumber(), expr, OpMinusMinus, subExprStart, lastTokenEnd(), tokenEnd()); + expr = context.makePostfixNode(location, expr, OpMinusMinus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); m_assignmentCount++; - failIfTrueIfStrictWithNameAndMessage(isEvalOrArguments, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); - failIfTrueIfStrict(requiresLExpr); + failIfTrueIfStrict(isEvalOrArguments, "'", m_lastIdentifier->impl(), "' cannot be modified in strict mode"); + semanticFailIfTrue(requiresLExpr, "The ", operatorString(false, lastOperator), " operator requires a reference expression"); + lastOperator = PLUSPLUS; next(); break; default: break; } - int end = lastTokenEnd(); - + JSTextPosition end = lastTokenEndPosition(); + if (!TreeBuilder::CreatesAST && (m_syntaxAlreadyValidated || !strictMode())) return expr; - + + location = tokenLocation(); + location.line = m_lexer->lastLineNumber(); while (tokenStackDepth) { switch (context.unaryTokenStackLastType(tokenStackDepth)) { case EXCLAMATION: - expr = context.createLogicalNot(m_lexer->lastLineNumber(), expr); + expr = context.createLogicalNot(location, expr); break; case TILDE: - expr = context.makeBitwiseNotNode(m_lexer->lastLineNumber(), expr); + expr = context.makeBitwiseNotNode(location, expr); break; case MINUS: - expr = context.makeNegateNode(m_lexer->lastLineNumber(), expr); + expr = context.makeNegateNode(location, expr); break; case PLUS: - expr = context.createUnaryPlus(m_lexer->lastLineNumber(), expr); + expr = context.createUnaryPlus(location, expr); break; case PLUSPLUS: case AUTOPLUSPLUS: - expr = context.makePrefixNode(m_lexer->lastLineNumber(), expr, OpPlusPlus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); + expr = context.makePrefixNode(location, expr, OpPlusPlus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); m_assignmentCount++; break; case MINUSMINUS: case AUTOMINUSMINUS: - expr = context.makePrefixNode(m_lexer->lastLineNumber(), expr, OpMinusMinus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); + expr = context.makePrefixNode(location, expr, OpMinusMinus, context.unaryTokenStackLastStart(tokenStackDepth), subExprStart + 1, end); m_assignmentCount++; break; case TYPEOF: - expr = context.makeTypeOfNode(m_lexer->lastLineNumber(), expr); + expr = context.makeTypeOfNode(location, expr); break; case VOIDTOKEN: - expr = context.createVoid(m_lexer->lastLineNumber(), expr); + expr = context.createVoid(location, expr); break; case DELETETOKEN: - failIfTrueIfStrictWithNameAndMessage(context.isResolve(expr), "Cannot delete unqualified property", m_lastIdentifier->impl(), "in strict mode"); - expr = context.makeDeleteNode(m_lexer->lastLineNumber(), expr, context.unaryTokenStackLastStart(tokenStackDepth), end, end); + failIfTrueIfStrict(context.isResolve(expr), "Cannot delete unqualified property '", m_lastIdentifier->impl(), "' in strict mode"); + expr = context.makeDeleteNode(location, expr, context.unaryTokenStackLastStart(tokenStackDepth), end, end); break; default: // If we get here something has gone horribly horribly wrong @@ -1685,8 +3066,82 @@ template TreeExpression Parser::parseUnaryExpress return expr; } -// Instantiate the two flavors of Parser we need instead of putting most of this file in Parser.h -template class Parser< Lexer >; -template class Parser< Lexer >; +template void Parser::printUnexpectedTokenText(WTF::PrintStream& out) +{ + switch (m_token.m_type) { + case EOFTOK: + out.print("Unexpected end of script"); + return; + case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK: + case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: + out.print("Incomplete unicode escape in identifier: '", getToken(), "'"); + return; + case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK: + out.print("Unterminated multiline comment"); + return; + case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK: + out.print("Unterminated numeric literal '", getToken(), "'"); + return; + case UNTERMINATED_STRING_LITERAL_ERRORTOK: + out.print("Unterminated string literal '", getToken(), "'"); + return; + case INVALID_IDENTIFIER_ESCAPE_ERRORTOK: + out.print("Invalid escape in identifier: '", getToken(), "'"); + return; + case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: + out.print("Invalid unicode escape in identifier: '", getToken(), "'"); + return; + case INVALID_NUMERIC_LITERAL_ERRORTOK: + out.print("Invalid numeric literal: '", getToken(), "'"); + return; + case INVALID_OCTAL_NUMBER_ERRORTOK: + out.print("Invalid use of octal: '", getToken(), "'"); + return; + case INVALID_STRING_LITERAL_ERRORTOK: + out.print("Invalid string literal: '", getToken(), "'"); + return; + case ERRORTOK: + out.print("Unrecognized token '", getToken(), "'"); + return; + case STRING: + out.print("Unexpected string literal ", getToken()); + return; + case INTEGER: + case DOUBLE: + out.print("Unexpected number '", getToken(), "'"); + return; + + case RESERVED_IF_STRICT: + out.print("Unexpected use of reserved word '", getToken(), "' in strict mode"); + return; + + case RESERVED: + out.print("Unexpected use of reserved word '", getToken(), "'"); + return; + + case INVALID_PRIVATE_NAME_ERRORTOK: + out.print("Invalid private name '", getToken(), "'"); + return; + + case IDENT: + out.print("Unexpected identifier '", getToken(), "'"); + return; + + default: + break; + } + + if (m_token.m_type & KeywordTokenFlag) { + out.print("Unexpected keyword '", getToken(), "'"); + return; + } + + out.print("Unexpected token '", getToken(), "'"); +} + +// Instantiate the two flavors of Parser we need instead of putting most of this file in Parser.h +template class Parser>; +template class Parser>; + } // namespace JSC