Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* JS parser.
*
* This is a recursive-descent parser for the JavaScript language specified by
* "The ECMAScript Language Specification" (Standard ECMA-262). It uses
* lexical and semantic feedback to disambiguate non-LL(1) structures. It
* generates trees of nodes induced by the recursive parsing (not precise
* syntax trees, see Parser.h). After tree construction, it rewrites trees to
* fold constants and evaluate compile-time expressions.
*
* This parser attempts no error recovery.
*/
#include "frontend/Parser.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Casting.h"
#include "mozilla/Range.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Try.h" // MOZ_TRY*
#include "mozilla/Utf8.h"
#include "mozilla/Variant.h"
#include <memory>
#include <new>
#include <type_traits>
#include "jsnum.h"
#include "jstypes.h"
#include "frontend/FoldConstants.h"
#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
#include "frontend/ModuleSharedContext.h"
#include "frontend/ParseNode.h"
#include "frontend/ParseNodeVerify.h"
#include "frontend/Parser-macros.h" // MOZ_TRY_VAR_OR_RETURN
#include "frontend/ParserAtom.h" // TaggedParserAtomIndex, ParserAtomsTable, ParserAtom
#include "frontend/ScriptIndex.h" // ScriptIndex
#include "frontend/TokenStream.h" // IsKeyword, ReservedWordTokenKind, ReservedWordToCharZ, DeprecatedContent, *TokenStream*, CharBuffer, TokenKindToDesc
#include "irregexp/RegExpAPI.h"
#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::ColumnNumberOneOrigin
#include "js/ErrorReport.h" // JSErrorBase
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/HashTable.h"
#include "js/RegExpFlags.h" // JS::RegExpFlags
#include "js/Stack.h" // JS::NativeStackLimit
#include "util/StringBuilder.h" // StringBuilder
#include "vm/BytecodeUtil.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
#include "vm/JSContext.h"
#include "vm/JSScript.h"
#include "vm/ModuleBuilder.h" // js::ModuleBuilder
#include "vm/Scope.h" // GetScopeDataTrailingNames
#include "wasm/AsmJS.h"
#include "frontend/ParseContext-inl.h"
#include "frontend/SharedContext-inl.h"
using namespace js;
using mozilla::AssertedCast;
using mozilla::AsVariant;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::PointerRangeSize;
using mozilla::Some;
using mozilla::Utf8Unit;
using JS::ReadOnlyCompileOptions;
using JS::RegExpFlags;
namespace js::frontend {
using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr;
using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
using BindingIter = ParseContext::Scope::BindingIter;
using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
using ParserBindingNameVector = Vector<ParserBindingName, 6>;
static inline void PropagateTransitiveParseFlags(const FunctionBox* inner,
SharedContext* outer) {
if (inner->bindingsAccessedDynamically()) {
outer->setBindingsAccessedDynamically();
}
if (inner->hasDirectEval()) {
outer->setHasDirectEval();
}
}
static bool StatementKindIsBraced(StatementKind kind) {
return kind == StatementKind::Block || kind == StatementKind::Switch ||
kind == StatementKind::Try || kind == StatementKind::Catch ||
kind == StatementKind::Finally;
}
template <class ParseHandler, typename Unit>
inline typename GeneralParser<ParseHandler, Unit>::FinalParser*
GeneralParser<ParseHandler, Unit>::asFinalParser() {
static_assert(
std::is_base_of_v<GeneralParser<ParseHandler, Unit>, FinalParser>,
"inheritance relationship required by the static_cast<> below");
return static_cast<FinalParser*>(this);
}
template <class ParseHandler, typename Unit>
inline const typename GeneralParser<ParseHandler, Unit>::FinalParser*
GeneralParser<ParseHandler, Unit>::asFinalParser() const {
static_assert(
std::is_base_of_v<GeneralParser<ParseHandler, Unit>, FinalParser>,
"inheritance relationship required by the static_cast<> below");
return static_cast<const FinalParser*>(this);
}
template <class ParseHandler, typename Unit>
template <typename ConditionT, typename ErrorReportT>
bool GeneralParser<ParseHandler, Unit>::mustMatchTokenInternal(
ConditionT condition, ErrorReportT errorReport) {
MOZ_ASSERT(condition(TokenKind::Div) == false);
MOZ_ASSERT(condition(TokenKind::DivAssign) == false);
MOZ_ASSERT(condition(TokenKind::RegExp) == false);
TokenKind actual;
if (!tokenStream.getToken(&actual, TokenStream::SlashIsInvalid)) {
return false;
}
if (!condition(actual)) {
errorReport(actual);
return false;
}
return true;
}
ParserSharedBase::ParserSharedBase(FrontendContext* fc,
CompilationState& compilationState,
Kind kind)
: fc_(fc),
alloc_(compilationState.parserAllocScope.alloc()),
compilationState_(compilationState),
pc_(nullptr),
usedNames_(compilationState.usedNames) {
fc_->nameCollectionPool().addActiveCompilation();
}
ParserSharedBase::~ParserSharedBase() {
fc_->nameCollectionPool().removeActiveCompilation();
}
#if defined(DEBUG) || defined(JS_JITSPEW)
void ParserSharedBase::dumpAtom(TaggedParserAtomIndex index) const {
parserAtoms().dump(index);
}
#endif
ParserBase::ParserBase(FrontendContext* fc,
const ReadOnlyCompileOptions& options,
CompilationState& compilationState)
: ParserSharedBase(fc, compilationState, ParserSharedBase::Kind::Parser),
anyChars(fc, options, this),
ss(nullptr),
#ifdef DEBUG
checkOptionsCalled_(false),
#endif
isUnexpectedEOF_(false),
awaitHandling_(AwaitIsName),
inParametersOfAsyncFunction_(false) {
}
bool ParserBase::checkOptions() {
#ifdef DEBUG
checkOptionsCalled_ = true;
#endif
return anyChars.checkOptions();
}
ParserBase::~ParserBase() { MOZ_ASSERT(checkOptionsCalled_); }
JSAtom* ParserBase::liftParserAtomToJSAtom(TaggedParserAtomIndex index) {
JSContext* cx = fc_->maybeCurrentJSContext();
MOZ_ASSERT(cx);
return parserAtoms().toJSAtom(cx, fc_, index,
compilationState_.input.atomCache);
}
template <class ParseHandler>
PerHandlerParser<ParseHandler>::PerHandlerParser(
FrontendContext* fc, const ReadOnlyCompileOptions& options,
CompilationState& compilationState, void* internalSyntaxParser)
: ParserBase(fc, options, compilationState),
handler_(fc, compilationState),
internalSyntaxParser_(internalSyntaxParser) {
MOZ_ASSERT(compilationState.isInitialStencil() ==
compilationState.input.isInitialStencil());
}
template <class ParseHandler, typename Unit>
GeneralParser<ParseHandler, Unit>::GeneralParser(
FrontendContext* fc, const ReadOnlyCompileOptions& options,
const Unit* units, size_t length, CompilationState& compilationState,
SyntaxParser* syntaxParser)
: Base(fc, options, compilationState, syntaxParser),
tokenStream(fc, &compilationState.parserAtoms, options, units, length) {}
template <typename Unit>
void Parser<SyntaxParseHandler, Unit>::setAwaitHandling(
AwaitHandling awaitHandling) {
this->awaitHandling_ = awaitHandling;
}
template <typename Unit>
void Parser<FullParseHandler, Unit>::setAwaitHandling(
AwaitHandling awaitHandling) {
this->awaitHandling_ = awaitHandling;
if (SyntaxParser* syntaxParser = getSyntaxParser()) {
syntaxParser->setAwaitHandling(awaitHandling);
}
}
template <class ParseHandler, typename Unit>
inline void GeneralParser<ParseHandler, Unit>::setAwaitHandling(
AwaitHandling awaitHandling) {
asFinalParser()->setAwaitHandling(awaitHandling);
}
template <typename Unit>
void Parser<SyntaxParseHandler, Unit>::setInParametersOfAsyncFunction(
bool inParameters) {
this->inParametersOfAsyncFunction_ = inParameters;
}
template <typename Unit>
void Parser<FullParseHandler, Unit>::setInParametersOfAsyncFunction(
bool inParameters) {
this->inParametersOfAsyncFunction_ = inParameters;
if (SyntaxParser* syntaxParser = getSyntaxParser()) {
syntaxParser->setInParametersOfAsyncFunction(inParameters);
}
}
template <class ParseHandler, typename Unit>
inline void GeneralParser<ParseHandler, Unit>::setInParametersOfAsyncFunction(
bool inParameters) {
asFinalParser()->setInParametersOfAsyncFunction(inParameters);
}
template <class ParseHandler>
FunctionBox* PerHandlerParser<ParseHandler>::newFunctionBox(
FunctionNodeType funNode, TaggedParserAtomIndex explicitName,
FunctionFlags flags, uint32_t toStringStart, Directives inheritedDirectives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind) {
MOZ_ASSERT(funNode);
ScriptIndex index = ScriptIndex(compilationState_.scriptData.length());
if (uint32_t(index) >= TaggedScriptThingIndex::IndexLimit) {
ReportAllocationOverflow(fc_);
return nullptr;
}
if (!compilationState_.appendScriptStencilAndData(fc_)) {
return nullptr;
}
bool isInitialStencil = compilationState_.isInitialStencil();
// This source extent will be further filled in during the remainder of parse.
SourceExtent extent;
extent.toStringStart = toStringStart;
FunctionBox* funbox = alloc_.new_<FunctionBox>(
fc_, extent, compilationState_, inheritedDirectives, generatorKind,
asyncKind, isInitialStencil, explicitName, flags, index);
if (!funbox) {
ReportOutOfMemory(fc_);
return nullptr;
}
handler_.setFunctionBox(funNode, funbox);
return funbox;
}
template <class ParseHandler>
FunctionBox* PerHandlerParser<ParseHandler>::newFunctionBox(
FunctionNodeType funNode, const ScriptStencil& cachedScriptData,
const ScriptStencilExtra& cachedScriptExtra) {
MOZ_ASSERT(funNode);
ScriptIndex index = ScriptIndex(compilationState_.scriptData.length());
if (uint32_t(index) >= TaggedScriptThingIndex::IndexLimit) {
ReportAllocationOverflow(fc_);
return nullptr;
}
if (!compilationState_.appendScriptStencilAndData(fc_)) {
return nullptr;
}
FunctionBox* funbox = alloc_.new_<FunctionBox>(
fc_, cachedScriptExtra.extent, compilationState_,
Directives(/* strict = */ false), cachedScriptExtra.generatorKind(),
cachedScriptExtra.asyncKind(), compilationState_.isInitialStencil(),
cachedScriptData.functionAtom, cachedScriptData.functionFlags, index);
if (!funbox) {
ReportOutOfMemory(fc_);
return nullptr;
}
handler_.setFunctionBox(funNode, funbox);
funbox->initFromScriptStencilExtra(cachedScriptExtra);
return funbox;
}
bool ParserBase::setSourceMapInfo() {
// If support for source pragmas have been fully disabled, we can skip
// processing of all of these values.
if (!options().sourcePragmas()) {
return true;
}
// Not all clients initialize ss. Can't update info to an object that isn't
// there.
if (!ss) {
return true;
}
if (anyChars.hasDisplayURL()) {
if (!ss->setDisplayURL(fc_, anyChars.displayURL())) {
return false;
}
}
if (anyChars.hasSourceMapURL()) {
MOZ_ASSERT(!ss->hasSourceMapURL());
if (!ss->setSourceMapURL(fc_, anyChars.sourceMapURL())) {
return false;
}
}
/*
* Source map URLs passed as a compile option (usually via a HTTP source map
* header) override any source map urls passed as comment pragmas.
*/
if (options().sourceMapURL()) {
// Warn about the replacement, but use the new one.
if (ss->hasSourceMapURL()) {
if (!warningNoOffset(JSMSG_ALREADY_HAS_PRAGMA, ss->filename(),
"//# sourceMappingURL")) {
return false;
}
}
if (!ss->setSourceMapURL(fc_, options().sourceMapURL())) {
return false;
}
}
return true;
}
/*
* Parse a top-level JS script.
*/
template <class ParseHandler, typename Unit>
typename ParseHandler::ListNodeResult
GeneralParser<ParseHandler, Unit>::parse() {
MOZ_ASSERT(checkOptionsCalled_);
SourceExtent extent = SourceExtent::makeGlobalExtent(
/* len = */ 0, options().lineno,
JS::LimitedColumnNumberOneOrigin::fromUnlimited(
JS::ColumnNumberOneOrigin(options().column)));
Directives directives(options().forceStrictMode());
GlobalSharedContext globalsc(this->fc_, ScopeKind::Global, options(),
directives, extent);
SourceParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
if (!globalpc.init()) {
return errorResult();
}
ParseContext::VarScope varScope(this);
if (!varScope.init(pc_)) {
return errorResult();
}
ListNodeType stmtList;
MOZ_TRY_VAR(stmtList, statementList(YieldIsName));
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (tt != TokenKind::Eof) {
error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt));
return errorResult();
}
if (!CheckParseTree(this->fc_, alloc_, stmtList)) {
return errorResult();
}
return stmtList;
}
/*
* Strict mode forbids introducing new definitions for 'eval', 'arguments',
* 'let', 'static', 'yield', or for any strict mode reserved word.
*/
bool ParserBase::isValidStrictBinding(TaggedParserAtomIndex name) {
TokenKind tt = ReservedWordTokenKind(name);
if (tt == TokenKind::Limit) {
return name != TaggedParserAtomIndex::WellKnown::eval() &&
name != TaggedParserAtomIndex::WellKnown::arguments();
}
return tt != TokenKind::Let && tt != TokenKind::Static &&
tt != TokenKind::Yield && !TokenKindIsStrictReservedWord(tt);
}
/*
* Returns true if all parameter names are valid strict mode binding names and
* no duplicate parameter names are present.
*/
bool ParserBase::hasValidSimpleStrictParameterNames() {
MOZ_ASSERT(pc_->isFunctionBox() &&
pc_->functionBox()->hasSimpleParameterList());
if (pc_->functionBox()->hasDuplicateParameters) {
return false;
}
for (auto name : pc_->positionalFormalParameterNames()) {
MOZ_ASSERT(name);
if (!isValidStrictBinding(name)) {
return false;
}
}
return true;
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::reportMissingClosing(
unsigned errorNumber, unsigned noteNumber, uint32_t openedPos) {
auto notes = MakeUnique<JSErrorNotes>();
if (!notes) {
ReportOutOfMemory(this->fc_);
return;
}
uint32_t line;
JS::LimitedColumnNumberOneOrigin column;
tokenStream.computeLineAndColumn(openedPos, &line, &column);
const size_t MaxWidth = sizeof("4294967295");
char columnNumber[MaxWidth];
SprintfLiteral(columnNumber, "%" PRIu32, column.oneOriginValue());
char lineNumber[MaxWidth];
SprintfLiteral(lineNumber, "%" PRIu32, line);
if (!notes->addNoteASCII(this->fc_, getFilename().c_str(), 0, line,
JS::ColumnNumberOneOrigin(column), GetErrorMessage,
nullptr, noteNumber, lineNumber, columnNumber)) {
return;
}
errorWithNotes(std::move(notes), errorNumber);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::reportRedeclarationHelper(
TaggedParserAtomIndex& name, DeclarationKind& prevKind, TokenPos& pos,
uint32_t& prevPos, const unsigned& errorNumber,
const unsigned& noteErrorNumber) {
UniqueChars bytes = this->parserAtoms().toPrintableString(name);
if (!bytes) {
ReportOutOfMemory(this->fc_);
return;
}
if (prevPos == DeclaredNameInfo::npos) {
errorAt(pos.begin, errorNumber, DeclarationKindString(prevKind),
bytes.get());
return;
}
auto notes = MakeUnique<JSErrorNotes>();
if (!notes) {
ReportOutOfMemory(this->fc_);
return;
}
uint32_t line;
JS::LimitedColumnNumberOneOrigin column;
tokenStream.computeLineAndColumn(prevPos, &line, &column);
const size_t MaxWidth = sizeof("4294967295");
char columnNumber[MaxWidth];
SprintfLiteral(columnNumber, "%" PRIu32, column.oneOriginValue());
char lineNumber[MaxWidth];
SprintfLiteral(lineNumber, "%" PRIu32, line);
if (!notes->addNoteASCII(this->fc_, getFilename().c_str(), 0, line,
JS::ColumnNumberOneOrigin(column), GetErrorMessage,
nullptr, noteErrorNumber, lineNumber,
columnNumber)) {
return;
}
errorWithNotesAt(std::move(notes), pos.begin, errorNumber,
DeclarationKindString(prevKind), bytes.get());
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::reportRedeclaration(
TaggedParserAtomIndex name, DeclarationKind prevKind, TokenPos pos,
uint32_t prevPos) {
reportRedeclarationHelper(name, prevKind, pos, prevPos, JSMSG_REDECLARED_VAR,
JSMSG_PREV_DECLARATION);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::reportMismatchedPlacement(
TaggedParserAtomIndex name, DeclarationKind prevKind, TokenPos pos,
uint32_t prevPos) {
reportRedeclarationHelper(name, prevKind, pos, prevPos,
JSMSG_MISMATCHED_PLACEMENT, JSMSG_PREV_DECLARATION);
}
// notePositionalFormalParameter is called for both the arguments of a regular
// function definition and the arguments specified by the Function
// constructor.
//
// The 'disallowDuplicateParams' bool indicates whether the use of another
// feature (destructuring or default arguments) disables duplicate arguments.
// (ECMA-262 requires us to support duplicate parameter names, but, for newer
// features, we consider the code to have "opted in" to higher standards and
// forbid duplicates.)
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::notePositionalFormalParameter(
FunctionNodeType funNode, TaggedParserAtomIndex name, uint32_t beginPos,
bool disallowDuplicateParams, bool* duplicatedParam) {
if (AddDeclaredNamePtr p =
pc_->functionScope().lookupDeclaredNameForAdd(name)) {
if (disallowDuplicateParams) {
error(JSMSG_BAD_DUP_ARGS);
return false;
}
// Strict-mode disallows duplicate args. We may not know whether we are
// in strict mode or not (since the function body hasn't been parsed).
// In such cases, report will queue up the potential error and return
// 'true'.
if (pc_->sc()->strict()) {
UniqueChars bytes = this->parserAtoms().toPrintableString(name);
if (!bytes) {
ReportOutOfMemory(this->fc_);
return false;
}
if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.get())) {
return false;
}
}
*duplicatedParam = true;
} else {
DeclarationKind kind = DeclarationKind::PositionalFormalParameter;
if (!pc_->functionScope().addDeclaredName(pc_, p, name, kind, beginPos)) {
return false;
}
}
if (!pc_->positionalFormalParameterNames().append(
TrivialTaggedParserAtomIndex::from(name))) {
ReportOutOfMemory(this->fc_);
return false;
}
NameNodeType paramNode;
MOZ_TRY_VAR_OR_RETURN(paramNode, newName(name), false);
handler_.addFunctionFormalParameter(funNode, paramNode);
return true;
}
template <class ParseHandler>
bool PerHandlerParser<ParseHandler>::noteDestructuredPositionalFormalParameter(
FunctionNodeType funNode, Node destruct) {
// Append an empty name to the positional formals vector to keep track of
// argument slots when making FunctionScope::ParserData.
if (!pc_->positionalFormalParameterNames().append(
TrivialTaggedParserAtomIndex::null())) {
ReportOutOfMemory(fc_);
return false;
}
handler_.addFunctionFormalParameter(funNode, destruct);
return true;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::noteDeclaredName(
TaggedParserAtomIndex name, DeclarationKind kind, TokenPos pos,
ClosedOver isClosedOver) {
// The asm.js validator does all its own symbol-table management so, as an
// optimization, avoid doing any work here.
if (pc_->useAsmOrInsideUseAsm()) {
return true;
}
switch (kind) {
case DeclarationKind::Var:
case DeclarationKind::BodyLevelFunction: {
Maybe<DeclarationKind> redeclaredKind;
uint32_t prevPos;
if (!pc_->tryDeclareVar(name, this, kind, pos.begin, &redeclaredKind,
&prevPos)) {
return false;
}
if (redeclaredKind) {
reportRedeclaration(name, *redeclaredKind, pos, prevPos);
return false;
}
break;
}
case DeclarationKind::ModuleBodyLevelFunction: {
MOZ_ASSERT(pc_->atModuleLevel());
AddDeclaredNamePtr p = pc_->varScope().lookupDeclaredNameForAdd(name);
if (p) {
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
if (!pc_->varScope().addDeclaredName(pc_, p, name, kind, pos.begin,
isClosedOver)) {
return false;
}
// Body-level functions in modules are always closed over.
pc_->varScope().lookupDeclaredName(name)->value()->setClosedOver();
break;
}
case DeclarationKind::FormalParameter: {
// It is an early error if any non-positional formal parameter name
// (e.g., destructuring formal parameter) is duplicated.
AddDeclaredNamePtr p =
pc_->functionScope().lookupDeclaredNameForAdd(name);
if (p) {
error(JSMSG_BAD_DUP_ARGS);
return false;
}
if (!pc_->functionScope().addDeclaredName(pc_, p, name, kind, pos.begin,
isClosedOver)) {
return false;
}
break;
}
case DeclarationKind::LexicalFunction:
case DeclarationKind::PrivateName:
case DeclarationKind::Synthetic:
case DeclarationKind::PrivateMethod: {
ParseContext::Scope* scope = pc_->innermostScope();
AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name);
if (p) {
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
if (!scope->addDeclaredName(pc_, p, name, kind, pos.begin,
isClosedOver)) {
return false;
}
break;
}
case DeclarationKind::SloppyLexicalFunction: {
// Functions in block have complex allowances in sloppy mode for being
// labelled that other lexical declarations do not have. Those checks
// are done in functionStmt.
ParseContext::Scope* scope = pc_->innermostScope();
if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) {
// It is usually an early error if there is another declaration
// with the same name in the same scope.
//
// Sloppy lexical functions may redeclare other sloppy lexical
// functions for web compatibility reasons.
if (p->value()->kind() != DeclarationKind::SloppyLexicalFunction) {
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
} else {
if (!scope->addDeclaredName(pc_, p, name, kind, pos.begin,
isClosedOver)) {
return false;
}
}
break;
}
case DeclarationKind::Let:
case DeclarationKind::Const:
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case DeclarationKind::Using:
case DeclarationKind::AwaitUsing:
#endif
case DeclarationKind::Class:
// The BoundNames of LexicalDeclaration and ForDeclaration must not
// contain 'let'. (CatchParameter is the only lexical binding form
// without this restriction.)
if (name == TaggedParserAtomIndex::WellKnown::let()) {
errorAt(pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET);
return false;
}
// For body-level lexically declared names in a function, it is an
// early error if there is a formal parameter of the same name. This
// needs a special check if there is an extra var scope due to
// parameter expressions.
if (pc_->isFunctionExtraBodyVarScopeInnermost()) {
DeclaredNamePtr p = pc_->functionScope().lookupDeclaredName(name);
if (p && DeclarationKindIsParameter(p->value()->kind())) {
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
}
[[fallthrough]];
case DeclarationKind::Import:
// Module code is always strict, so 'let' is always a keyword and never a
// name.
MOZ_ASSERT(name != TaggedParserAtomIndex::WellKnown::let());
[[fallthrough]];
case DeclarationKind::SimpleCatchParameter:
case DeclarationKind::CatchParameter: {
ParseContext::Scope* scope = pc_->innermostScope();
// It is an early error if there is another declaration with the same
// name in the same scope.
AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name);
if (p) {
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
if (!scope->addDeclaredName(pc_, p, name, kind, pos.begin,
isClosedOver)) {
return false;
}
break;
}
case DeclarationKind::CoverArrowParameter:
// CoverArrowParameter is only used as a placeholder declaration kind.
break;
case DeclarationKind::PositionalFormalParameter:
MOZ_CRASH(
"Positional formal parameter names should use "
"notePositionalFormalParameter");
break;
case DeclarationKind::VarForAnnexBLexicalFunction:
MOZ_CRASH(
"Synthesized Annex B vars should go through "
"addPossibleAnnexBFunctionBox, and "
"propagateAndMarkAnnexBFunctionBoxes");
break;
}
return true;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::noteDeclaredPrivateName(
Node nameNode, TaggedParserAtomIndex name, PropertyType propType,
FieldPlacement placement, TokenPos pos) {
ParseContext::Scope* scope = pc_->innermostScope();
AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name);
DeclarationKind declKind = DeclarationKind::PrivateName;
// Our strategy for enabling debugger functionality is to mark names as closed
// over, even if they don't necessarily need to be, to ensure that they are
// included in the environment object. This allows us to easily look them up
// by name when needed, even if there is no corresponding property on an
// object, as is the case with getter, setters and private methods.
ClosedOver closedOver = ClosedOver::Yes;
PrivateNameKind kind;
switch (propType) {
case PropertyType::Field:
kind = PrivateNameKind::Field;
closedOver = ClosedOver::No;
break;
case PropertyType::FieldWithAccessor:
// In this case, we create a new private field for the underlying storage,
// and use the current name for the getter and setter.
kind = PrivateNameKind::GetterSetter;
break;
case PropertyType::Method:
case PropertyType::GeneratorMethod:
case PropertyType::AsyncMethod:
case PropertyType::AsyncGeneratorMethod:
if (placement == FieldPlacement::Instance) {
// Optimized private method. Non-optimized paths still get
// DeclarationKind::Synthetic.
declKind = DeclarationKind::PrivateMethod;
}
kind = PrivateNameKind::Method;
break;
case PropertyType::Getter:
kind = PrivateNameKind::Getter;
break;
case PropertyType::Setter:
kind = PrivateNameKind::Setter;
break;
default:
MOZ_CRASH("Invalid Property Type for noteDeclarePrivateName");
}
if (p) {
PrivateNameKind prevKind = p->value()->privateNameKind();
if ((prevKind == PrivateNameKind::Getter &&
kind == PrivateNameKind::Setter) ||
(prevKind == PrivateNameKind::Setter &&
kind == PrivateNameKind::Getter)) {
// Private methods demands that
//
// class A {
// static set #x(_) {}
// get #x() { }
// }
//
// Report a SyntaxError.
if (placement == p->value()->placement()) {
p->value()->setPrivateNameKind(PrivateNameKind::GetterSetter);
handler_.setPrivateNameKind(nameNode, PrivateNameKind::GetterSetter);
return true;
}
}
reportMismatchedPlacement(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
if (!scope->addDeclaredName(pc_, p, name, declKind, pos.begin, closedOver)) {
return false;
}
DeclaredNamePtr declared = scope->lookupDeclaredName(name);
declared->value()->setPrivateNameKind(kind);
declared->value()->setFieldPlacement(placement);
handler_.setPrivateNameKind(nameNode, kind);
return true;
}
bool ParserBase::noteUsedNameInternal(TaggedParserAtomIndex name,
NameVisibility visibility,
mozilla::Maybe<TokenPos> tokenPosition) {
// The asm.js validator does all its own symbol-table management so, as an
// optimization, avoid doing any work here.
if (pc_->useAsmOrInsideUseAsm()) {
return true;
}
// Global bindings are properties and not actual bindings; we don't need
// to know if they are closed over. So no need to track used name at the
// global scope. It is not incorrect to track them, this is an
// optimization.
//
// Exceptions:
// (a) Track private name references, as the used names tracker is used to
// provide early errors for undeclared private name references
// (b) If the script has extra bindings, track all references to detect
// references to extra bindings
ParseContext::Scope* scope = pc_->innermostScope();
if (pc_->sc()->isGlobalContext() && scope == &pc_->varScope() &&
visibility == NameVisibility::Public &&
!this->compilationState_.input.hasExtraBindings()) {
return true;
}
return usedNames_.noteUse(fc_, name, visibility, pc_->scriptId(), scope->id(),
tokenPosition);
}
template <class ParseHandler>
bool PerHandlerParser<ParseHandler>::
propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope) {
// Now that we have all the declared names in the scope, check which
// functions should exhibit Annex B semantics.
if (!scope.propagateAndMarkAnnexBFunctionBoxes(pc_, this)) {
return false;
}
if (handler_.reuseClosedOverBindings()) {
MOZ_ASSERT(pc_->isOutermostOfCurrentCompile());
// Closed over bindings for all scopes are stored in a contiguous array, in
// the same order as the order in which scopes are visited, and seprated by
// TaggedParserAtomIndex::null().
uint32_t slotCount = scope.declaredCount();
while (auto parserAtom = handler_.nextLazyClosedOverBinding()) {
scope.lookupDeclaredName(parserAtom)->value()->setClosedOver();
MOZ_ASSERT(slotCount > 0);
slotCount--;
}
if (pc_->isGeneratorOrAsync()) {
scope.setOwnStackSlotCount(slotCount);
}
return true;
}
constexpr bool isSyntaxParser =
std::is_same_v<ParseHandler, SyntaxParseHandler>;
uint32_t scriptId = pc_->scriptId();
uint32_t scopeId = scope.id();
uint32_t slotCount = 0;
for (BindingIter bi = scope.bindings(pc_); bi; bi++) {
bool closedOver = false;
if (UsedNamePtr p = usedNames_.lookup(bi.name())) {
p->value().noteBoundInScope(scriptId, scopeId, &closedOver);
if (closedOver) {
bi.setClosedOver();
if constexpr (isSyntaxParser) {
if (!pc_->closedOverBindingsForLazy().append(
TrivialTaggedParserAtomIndex::from(bi.name()))) {
ReportOutOfMemory(fc_);
return false;
}
}
}
}
if constexpr (!isSyntaxParser) {
if (!closedOver) {
slotCount++;
}
}
}
if constexpr (!isSyntaxParser) {
if (pc_->isGeneratorOrAsync()) {
scope.setOwnStackSlotCount(slotCount);
}
}
// Append a nullptr to denote end-of-scope.
if constexpr (isSyntaxParser) {
if (!pc_->closedOverBindingsForLazy().append(
TrivialTaggedParserAtomIndex::null())) {
ReportOutOfMemory(fc_);
return false;
}
}
return true;
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkStatementsEOF() {
// This is designed to be paired with parsing a statement list at the top
// level.
//
// The statementList() call breaks on TokenKind::RightCurly, so make sure
// we've reached EOF here.
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) {
return false;
}
if (tt != TokenKind::Eof) {
error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
return false;
}
return true;
}
template <typename ScopeT>
typename ScopeT::ParserData* NewEmptyBindingData(FrontendContext* fc,
LifoAlloc& alloc,
uint32_t numBindings) {
using Data = typename ScopeT::ParserData;
size_t allocSize = SizeOfScopeData<Data>(numBindings);
auto* bindings = alloc.newWithSize<Data>(allocSize, numBindings);
if (!bindings) {
ReportOutOfMemory(fc);
}
return bindings;
}
GlobalScope::ParserData* NewEmptyGlobalScopeData(FrontendContext* fc,
LifoAlloc& alloc,
uint32_t numBindings) {
return NewEmptyBindingData<GlobalScope>(fc, alloc, numBindings);
}
LexicalScope::ParserData* NewEmptyLexicalScopeData(FrontendContext* fc,
LifoAlloc& alloc,
uint32_t numBindings) {
return NewEmptyBindingData<LexicalScope>(fc, alloc, numBindings);
}
FunctionScope::ParserData* NewEmptyFunctionScopeData(FrontendContext* fc,
LifoAlloc& alloc,
uint32_t numBindings) {
return NewEmptyBindingData<FunctionScope>(fc, alloc, numBindings);
}
namespace detail {
template <class SlotInfo>
static MOZ_ALWAYS_INLINE ParserBindingName* InitializeIndexedBindings(
SlotInfo& slotInfo, ParserBindingName* start, ParserBindingName* cursor) {
return cursor;
}
template <class SlotInfo, typename UnsignedInteger, typename... Step>
static MOZ_ALWAYS_INLINE ParserBindingName* InitializeIndexedBindings(
SlotInfo& slotInfo, ParserBindingName* start, ParserBindingName* cursor,
UnsignedInteger SlotInfo::* field, const ParserBindingNameVector& bindings,
Step&&... step) {
slotInfo.*field =
AssertedCast<UnsignedInteger>(PointerRangeSize(start, cursor));
ParserBindingName* newCursor =
std::uninitialized_copy(bindings.begin(), bindings.end(), cursor);
return InitializeIndexedBindings(slotInfo, start, newCursor,
std::forward<Step>(step)...);
}
} // namespace detail
// Initialize the trailing name bindings of |data|, then set |data->length| to
// the count of bindings added (which must equal |count|).
//
// First, |firstBindings| are added to the trailing names. Then any
// "steps" present are performed first to last. Each step is 1) a pointer to a
// member of |data| to be set to the current number of bindings added, and 2) a
// vector of |ParserBindingName|s to then copy into |data->trailingNames|.
// (Thus each |data| member field indicates where the corresponding vector's
// names start.)
template <class Data, typename... Step>
static MOZ_ALWAYS_INLINE void InitializeBindingData(
Data* data, uint32_t count, const ParserBindingNameVector& firstBindings,
Step&&... step) {
MOZ_ASSERT(data->length == 0, "data shouldn't be filled yet");
ParserBindingName* start = GetScopeDataTrailingNamesPointer(data);
ParserBindingName* cursor = std::uninitialized_copy(
firstBindings.begin(), firstBindings.end(), start);
#ifdef DEBUG
ParserBindingName* end =
#endif
detail::InitializeIndexedBindings(data->slotInfo, start, cursor,
std::forward<Step>(step)...);
MOZ_ASSERT(PointerRangeSize(start, end) == count);
data->length = count;
}
static Maybe<GlobalScope::ParserData*> NewGlobalScopeData(
FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc,
ParseContext* pc) {
ParserBindingNameVector vars(fc);
ParserBindingNameVector lets(fc);
ParserBindingNameVector consts(fc);
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
bool closedOver = allBindingsClosedOver || bi.closedOver();
switch (bi.kind()) {
case BindingKind::Var: {
bool isTopLevelFunction =
bi.declarationKind() == DeclarationKind::BodyLevelFunction;
ParserBindingName binding(bi.name(), closedOver, isTopLevelFunction);
if (!vars.append(binding)) {
return Nothing();
}
break;
}
case BindingKind::Let: {
ParserBindingName binding(bi.name(), closedOver);
if (!lets.append(binding)) {
return Nothing();
}
break;
}
case BindingKind::Const: {
ParserBindingName binding(bi.name(), closedOver);
if (!consts.append(binding)) {
return Nothing();
}
break;
}
default:
MOZ_CRASH("Bad global scope BindingKind");
}
}
GlobalScope::ParserData* bindings = nullptr;
uint32_t numBindings = vars.length() + lets.length() + consts.length();
if (numBindings > 0) {
bindings = NewEmptyBindingData<GlobalScope>(fc, alloc, numBindings);
if (!bindings) {
return Nothing();
}
// The ordering here is important. See comments in GlobalScope.
InitializeBindingData(bindings, numBindings, vars,
&ParserGlobalScopeSlotInfo::letStart, lets,
&ParserGlobalScopeSlotInfo::constStart, consts);
}
return Some(bindings);
}
Maybe<GlobalScope::ParserData*> ParserBase::newGlobalScopeData(
ParseContext::Scope& scope) {
return NewGlobalScopeData(fc_, scope, stencilAlloc(), pc_);
}
static Maybe<ModuleScope::ParserData*> NewModuleScopeData(
FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc,
ParseContext* pc) {
ParserBindingNameVector imports(fc);
ParserBindingNameVector vars(fc);
ParserBindingNameVector lets(fc);
ParserBindingNameVector consts(fc);
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
ParserBindingNameVector usings(fc);
#endif
bool allBindingsClosedOver =
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
// Imports are indirect bindings and must not be given known slots.
ParserBindingName binding(bi.name(),
(allBindingsClosedOver || bi.closedOver()) &&
bi.kind() != BindingKind::Import);
switch (bi.kind()) {
case BindingKind::Import:
if (!imports.append(binding)) {
return Nothing();
}
break;
case BindingKind::Var:
if (!vars.append(binding)) {
return Nothing();
}
break;
case BindingKind::Let:
if (!lets.append(binding)) {
return Nothing();
}
break;
case BindingKind::Const:
if (!consts.append(binding)) {
return Nothing();
}
break;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case BindingKind::Using:
if (!usings.append(binding)) {
return Nothing();
}
break;
#endif
default:
MOZ_CRASH("Bad module scope BindingKind");
}
}
ModuleScope::ParserData* bindings = nullptr;
uint32_t numBindings = imports.length() + vars.length() + lets.length() +
consts.length()
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
+ usings.length()
#endif
;
if (numBindings > 0) {
bindings = NewEmptyBindingData<ModuleScope>(fc, alloc, numBindings);
if (!bindings) {
return Nothing();
}
// The ordering here is important. See comments in ModuleScope.
InitializeBindingData(bindings, numBindings, imports,
&ParserModuleScopeSlotInfo::varStart, vars,
&ParserModuleScopeSlotInfo::letStart, lets,
&ParserModuleScopeSlotInfo::constStart, consts
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
,
&ParserModuleScopeSlotInfo::usingStart, usings
#endif
);
}
return Some(bindings);
}
Maybe<ModuleScope::ParserData*> ParserBase::newModuleScopeData(
ParseContext::Scope& scope) {
return NewModuleScopeData(fc_, scope, stencilAlloc(), pc_);
}
static Maybe<EvalScope::ParserData*> NewEvalScopeData(
FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc,
ParseContext* pc) {
ParserBindingNameVector vars(fc);
// Treat all bindings as closed over in non-strict eval.
bool allBindingsClosedOver =
!pc->sc()->strict() || pc->sc()->allBindingsClosedOver();
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
// Eval scopes only contain 'var' bindings.
MOZ_ASSERT(bi.kind() == BindingKind::Var);
bool isTopLevelFunction =
bi.declarationKind() == DeclarationKind::BodyLevelFunction;
bool closedOver = allBindingsClosedOver || bi.closedOver();
ParserBindingName binding(bi.name(), closedOver, isTopLevelFunction);
if (!vars.append(binding)) {
return Nothing();
}
}
EvalScope::ParserData* bindings = nullptr;
uint32_t numBindings = vars.length();
if (numBindings > 0) {
bindings = NewEmptyBindingData<EvalScope>(fc, alloc, numBindings);
if (!bindings) {
return Nothing();
}
InitializeBindingData(bindings, numBindings, vars);
}
return Some(bindings);
}
Maybe<EvalScope::ParserData*> ParserBase::newEvalScopeData(
ParseContext::Scope& scope) {
return NewEvalScopeData(fc_, scope, stencilAlloc(), pc_);
}
static Maybe<FunctionScope::ParserData*> NewFunctionScopeData(
FrontendContext* fc, ParseContext::Scope& scope, bool hasParameterExprs,
LifoAlloc& alloc, ParseContext* pc) {
ParserBindingNameVector positionalFormals(fc);
ParserBindingNameVector formals(fc);
ParserBindingNameVector vars(fc);
bool allBindingsClosedOver =
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
bool argumentBindingsClosedOver =
allBindingsClosedOver || pc->isGeneratorOrAsync();
bool hasDuplicateParams = pc->functionBox()->hasDuplicateParameters;
// Positional parameter names must be added in order of appearance as they are
// referenced using argument slots.
for (size_t i = 0; i < pc->positionalFormalParameterNames().length(); i++) {
TaggedParserAtomIndex name = pc->positionalFormalParameterNames()[i];
ParserBindingName bindName;
if (name) {
DeclaredNamePtr p = scope.lookupDeclaredName(name);
// Do not consider any positional formal parameters closed over if
// there are parameter defaults. It is the binding in the defaults
// scope that is closed over instead.
bool closedOver =
argumentBindingsClosedOver || (p && p->value()->closedOver());
// If the parameter name has duplicates, only the final parameter
// name should be on the environment, as otherwise the environment
// object would have multiple, same-named properties.
if (hasDuplicateParams) {
for (size_t j = pc->positionalFormalParameterNames().length() - 1;
j > i; j--) {
if (TaggedParserAtomIndex(pc->positionalFormalParameterNames()[j]) ==
name) {
closedOver = false;
break;
}
}
}
bindName = ParserBindingName(name, closedOver);
}
if (!positionalFormals.append(bindName)) {
return Nothing();
}
}
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
ParserBindingName binding(bi.name(),
allBindingsClosedOver || bi.closedOver());
switch (bi.kind()) {
case BindingKind::FormalParameter:
// Positional parameter names are already handled above.
if (bi.declarationKind() == DeclarationKind::FormalParameter) {
if (!formals.append(binding)) {
return Nothing();
}
}
break;
case BindingKind::Var:
// The only vars in the function scope when there are parameter
// exprs, which induces a separate var environment, should be the
// special bindings.
MOZ_ASSERT_IF(hasParameterExprs,
FunctionScope::isSpecialName(bi.name()));
if (!vars.append(binding)) {
return Nothing();
}
break;
case BindingKind::Let:
case BindingKind::Const:
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case BindingKind::Using:
#endif
break;
default:
MOZ_CRASH("bad function scope BindingKind");
break;
}
}
FunctionScope::ParserData* bindings = nullptr;
uint32_t numBindings =
positionalFormals.length() + formals.length() + vars.length();
if (numBindings > 0) {
bindings = NewEmptyBindingData<FunctionScope>(fc, alloc, numBindings);
if (!bindings) {
return Nothing();
}
// The ordering here is important. See comments in FunctionScope.
InitializeBindingData(
bindings, numBindings, positionalFormals,
&ParserFunctionScopeSlotInfo::nonPositionalFormalStart, formals,
&ParserFunctionScopeSlotInfo::varStart, vars);
}
return Some(bindings);
}
// Compute if `NewFunctionScopeData` would return any binding list with any
// entry marked as closed-over. This is done without the need to allocate the
// binding list. If true, an EnvironmentObject will be needed at runtime.
bool FunctionScopeHasClosedOverBindings(ParseContext* pc) {
bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() ||
pc->functionScope().tooBigToOptimize();
for (BindingIter bi = pc->functionScope().bindings(pc); bi; bi++) {
switch (bi.kind()) {
case BindingKind::FormalParameter:
case BindingKind::Var:
if (allBindingsClosedOver || bi.closedOver()) {
return true;
}
break;
default:
break;
}
}
return false;
}
Maybe<FunctionScope::ParserData*> ParserBase::newFunctionScopeData(
ParseContext::Scope& scope, bool hasParameterExprs) {
return NewFunctionScopeData(fc_, scope, hasParameterExprs, stencilAlloc(),
pc_);
}
VarScope::ParserData* NewEmptyVarScopeData(FrontendContext* fc,
LifoAlloc& alloc,
uint32_t numBindings) {
return NewEmptyBindingData<VarScope>(fc, alloc, numBindings);
}
static Maybe<VarScope::ParserData*> NewVarScopeData(FrontendContext* fc,
ParseContext::Scope& scope,
LifoAlloc& alloc,
ParseContext* pc) {
ParserBindingNameVector vars(fc);
bool allBindingsClosedOver =
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
if (bi.kind() == BindingKind::Var) {
ParserBindingName binding(bi.name(),
allBindingsClosedOver || bi.closedOver());
if (!vars.append(binding)) {
return Nothing();
}
} else {
MOZ_ASSERT(bi.kind() == BindingKind::Let ||
bi.kind() == BindingKind::Const
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|| bi.kind() == BindingKind::Using
#endif
,
"bad var scope BindingKind");
}
}
VarScope::ParserData* bindings = nullptr;
uint32_t numBindings = vars.length();
if (numBindings > 0) {
bindings = NewEmptyBindingData<VarScope>(fc, alloc, numBindings);
if (!bindings) {
return Nothing();
}
InitializeBindingData(bindings, numBindings, vars);
}
return Some(bindings);
}
// Compute if `NewVarScopeData` would return any binding list. This is done
// without allocate the binding list.
static bool VarScopeHasBindings(ParseContext* pc) {
for (BindingIter bi = pc->varScope().bindings(pc); bi; bi++) {
if (bi.kind() == BindingKind::Var) {
return true;
}
}
return false;
}
Maybe<VarScope::ParserData*> ParserBase::newVarScopeData(
ParseContext::Scope& scope) {
return NewVarScopeData(fc_, scope, stencilAlloc(), pc_);
}
static Maybe<LexicalScope::ParserData*> NewLexicalScopeData(
FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc,
ParseContext* pc) {
ParserBindingNameVector lets(fc);
ParserBindingNameVector consts(fc);
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
ParserBindingNameVector usings(fc);
#endif
bool allBindingsClosedOver =
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
ParserBindingName binding(bi.name(),
allBindingsClosedOver || bi.closedOver());
switch (bi.kind()) {
case BindingKind::Let:
if (!lets.append(binding)) {
return Nothing();
}
break;
case BindingKind::Const:
if (!consts.append(binding)) {
return Nothing();
}
break;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case BindingKind::Using:
if (!usings.append(binding)) {
return Nothing();
}
break;
#endif
case BindingKind::Var:
case BindingKind::FormalParameter:
break;
default:
MOZ_CRASH("Bad lexical scope BindingKind");
break;
}
}
LexicalScope::ParserData* bindings = nullptr;
uint32_t numBindings = lets.length() + consts.length()
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
+ usings.length()
#endif
;
if (numBindings > 0) {
bindings = NewEmptyBindingData<LexicalScope>(fc, alloc, numBindings);
if (!bindings) {
return Nothing();
}
// The ordering here is important. See comments in LexicalScope.
InitializeBindingData(bindings, numBindings, lets,
&ParserLexicalScopeSlotInfo::constStart, consts
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
,
&ParserLexicalScopeSlotInfo::usingStart, usings
#endif
);
}
return Some(bindings);
}
// Compute if `NewLexicalScopeData` would return any binding list with any entry
// marked as closed-over. This is done without the need to allocate the binding
// list. If true, an EnvironmentObject will be needed at runtime.
bool LexicalScopeHasClosedOverBindings(ParseContext* pc,
ParseContext::Scope& scope) {
bool allBindingsClosedOver =
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
switch (bi.kind()) {
case BindingKind::Let:
case BindingKind::Const:
if (allBindingsClosedOver || bi.closedOver()) {
return true;
}
break;
default:
break;
}
}
return false;
}
Maybe<LexicalScope::ParserData*> ParserBase::newLexicalScopeData(
ParseContext::Scope& scope) {
return NewLexicalScopeData(fc_, scope, stencilAlloc(), pc_);
}
static Maybe<ClassBodyScope::ParserData*> NewClassBodyScopeData(
FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc,
ParseContext* pc) {
ParserBindingNameVector privateBrand(fc);
ParserBindingNameVector synthetics(fc);
ParserBindingNameVector privateMethods(fc);
bool allBindingsClosedOver =
pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize();
for (BindingIter bi = scope.bindings(pc); bi; bi++) {
ParserBindingName binding(bi.name(),
allBindingsClosedOver || bi.closedOver());
switch (bi.kind()) {
case BindingKind::Synthetic:
if (bi.name() ==
TaggedParserAtomIndex::WellKnown::dot_privateBrand_()) {
MOZ_ASSERT(privateBrand.empty());
if (!privateBrand.append(binding)) {
return Nothing();
}
} else {
if (!synthetics.append(binding)) {
return Nothing();
}
}
break;
case BindingKind::PrivateMethod:
if (!privateMethods.append(binding)) {
return Nothing();
}
break;
default:
MOZ_CRASH("bad class body scope BindingKind");
break;
}
}
// We should have zero or one private brands.
MOZ_ASSERT(privateBrand.length() == 0 || privateBrand.length() == 1);
ClassBodyScope::ParserData* bindings = nullptr;
uint32_t numBindings =
privateBrand.length() + synthetics.length() + privateMethods.length();
if (numBindings > 0) {
bindings = NewEmptyBindingData<ClassBodyScope>(fc, alloc, numBindings);
if (!bindings) {
return Nothing();
}
// To simplify initialization of the bindings, we concatenate the
// synthetics+privateBrand vector such that the private brand is always the
// first element, as ordering is important. See comments in ClassBodyScope.
ParserBindingNameVector brandAndSynthetics(fc);
if (!brandAndSynthetics.appendAll(privateBrand)) {
return Nothing();
}
if (!brandAndSynthetics.appendAll(synthetics)) {
return Nothing();
}
// The ordering here is important. See comments in ClassBodyScope.
InitializeBindingData(bindings, numBindings, brandAndSynthetics,
&ParserClassBodyScopeSlotInfo::privateMethodStart,
privateMethods);
}
// `EmitterScope::lookupPrivate()` requires `.privateBrand` to be stored in a
// predictable slot: the first slot available in the environment object,
// `ClassBodyLexicalEnvironmentObject::privateBrandSlot()`. We assume that
// if `.privateBrand` is first in the scope, it will be stored there.
MOZ_ASSERT_IF(!privateBrand.empty(),
GetScopeDataTrailingNames(bindings)[0].name() ==
TaggedParserAtomIndex::WellKnown::dot_privateBrand_());
return Some(bindings);
}
Maybe<ClassBodyScope::ParserData*> ParserBase::newClassBodyScopeData(
ParseContext::Scope& scope) {
return NewClassBodyScopeData(fc_, scope, stencilAlloc(), pc_);
}
template <>
SyntaxParseHandler::LexicalScopeNodeResult
PerHandlerParser<SyntaxParseHandler>::finishLexicalScope(
ParseContext::Scope& scope, Node body, ScopeKind kind) {
if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) {
return errorResult();
}
return handler_.newLexicalScope(body);
}
template <>
FullParseHandler::LexicalScopeNodeResult
PerHandlerParser<FullParseHandler>::finishLexicalScope(
ParseContext::Scope& scope, ParseNode* body, ScopeKind kind) {
if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) {
return errorResult();
}
Maybe<LexicalScope::ParserData*> bindings = newLexicalScopeData(scope);
if (!bindings) {
return errorResult();
}
return handler_.newLexicalScope(*bindings, body, kind);
}
template <>
SyntaxParseHandler::ClassBodyScopeNodeResult
PerHandlerParser<SyntaxParseHandler>::finishClassBodyScope(
ParseContext::Scope& scope, ListNodeType body) {
if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) {
return errorResult();
}
return handler_.newClassBodyScope(body);
}
template <>
FullParseHandler::ClassBodyScopeNodeResult
PerHandlerParser<FullParseHandler>::finishClassBodyScope(
ParseContext::Scope& scope, ListNode* body) {
if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) {
return errorResult();
}
Maybe<ClassBodyScope::ParserData*> bindings = newClassBodyScopeData(scope);
if (!bindings) {
return errorResult();
}
return handler_.newClassBodyScope(*bindings, body);
}
template <class ParseHandler>
bool PerHandlerParser<ParseHandler>::checkForUndefinedPrivateFields(
EvalSharedContext* evalSc) {
if (!this->compilationState_.isInitialStencil()) {
// We're delazifying -- so we already checked private names during first
// parse.
return true;
}
Vector<UnboundPrivateName, 8> unboundPrivateNames(fc_);
if (!usedNames_.getUnboundPrivateNames(unboundPrivateNames)) {
return false;
}
// No unbound names, let's get out of here!
if (unboundPrivateNames.empty()) {
return true;
}
// It is an early error if there's private name references unbound,
// unless it's an eval, in which case we need to check the scope
// chain.
if (!evalSc) {
// The unbound private names are sorted, so just grab the first one.
UnboundPrivateName minimum = unboundPrivateNames[0];
UniqueChars str = this->parserAtoms().toPrintableString(minimum.atom);
if (!str) {
ReportOutOfMemory(this->fc_);
return false;
}
errorAt(minimum.position.begin, JSMSG_MISSING_PRIVATE_DECL, str.get());
return false;
}
// It's important that the unbound private names are sorted, as we
// want our errors to always be issued to the first textually.
for (UnboundPrivateName unboundName : unboundPrivateNames) {
// If the enclosingScope is non-syntactic, then we are in a
// Debugger.Frame.prototype.eval call. In order to find the declared private
// names, we must use the effective scope that was determined when creating
// the scopeContext.
if (!this->compilationState_.scopeContext
.effectiveScopePrivateFieldCacheHas(unboundName.atom)) {
UniqueChars str = this->parserAtoms().toPrintableString(unboundName.atom);
if (!str) {
ReportOutOfMemory(this->fc_);
return false;
}
errorAt(unboundName.position.begin, JSMSG_MISSING_PRIVATE_DECL,
str.get());
return false;
}
}
return true;
}
template <typename Unit>
FullParseHandler::LexicalScopeNodeResult
Parser<FullParseHandler, Unit>::evalBody(EvalSharedContext* evalsc) {
SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
if (!evalpc.init()) {
return errorResult();
}
ParseContext::VarScope varScope(this);
if (!varScope.init(pc_)) {
return errorResult();
}
LexicalScopeNode* body;
{
// All evals have an implicit non-extensible lexical scope.
ParseContext::Scope lexicalScope(this);
if (!lexicalScope.init(pc_)) {
return errorResult();
}
ListNode* list;
MOZ_TRY_VAR(list, statementList(YieldIsName));
if (!checkStatementsEOF()) {
return errorResult();
}
// Private names not lexically defined must trigger a syntax error.
if (!checkForUndefinedPrivateFields(evalsc)) {
return errorResult();
}
MOZ_TRY_VAR(body, finishLexicalScope(lexicalScope, list));
}
#ifdef DEBUG
if (evalpc.superScopeNeedsHomeObject() &&
!this->compilationState_.input.enclosingScope.isNull()) {
// If superScopeNeedsHomeObject_ is set and we are an entry-point
// ParseContext, then we must be emitting an eval script, and the
// outer function must already be marked as needing a home object
// since it contains an eval.
MOZ_ASSERT(
this->compilationState_.scopeContext.hasFunctionNeedsHomeObjectOnChain,
"Eval must have found an enclosing function box scope that "
"allows super.property");
}
#endif
if (!CheckParseTree(this->fc_, alloc_, body)) {
return errorResult();
}
ParseNode* node = body;
// Don't constant-fold inside "use asm" code, as this could create a parse
// tree that doesn't type-check as asm.js.
if (!pc_->useAsmOrInsideUseAsm()) {
if (!FoldConstants(this->fc_, this->parserAtoms(), this->bigInts(), &node,
&handler_)) {
return errorResult();
}
}
body = handler_.asLexicalScopeNode(node);
if (!this->setSourceMapInfo()) {
return errorResult();
}
if (pc_->sc()->strict()) {
if (!propagateFreeNamesAndMarkClosedOverBindings(varScope)) {
return errorResult();
}
} else {
// For non-strict eval scripts, since all bindings are automatically
// considered closed over, we don't need to call propagateFreeNames-
// AndMarkClosedOverBindings. However, Annex B.3.3 functions still need to
// be marked.
if (!varScope.propagateAndMarkAnnexBFunctionBoxes(pc_, this)) {
return errorResult();
}
}
Maybe<EvalScope::ParserData*> bindings = newEvalScopeData(pc_->varScope());
if (!bindings) {
return errorResult();
}
evalsc->bindings = *bindings;
return body;
}
template <typename Unit>
FullParseHandler::ListNodeResult Parser<FullParseHandler, Unit>::globalBody(
GlobalSharedContext* globalsc) {
SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
if (!globalpc.init()) {
return errorResult();
}
ParseContext::VarScope varScope(this);
if (!varScope.init(pc_)) {
return errorResult();
}
ListNode* body;
MOZ_TRY_VAR(body, statementList(YieldIsName));
if (!checkStatementsEOF()) {
return errorResult();
}
if (!CheckParseTree(this->fc_, alloc_, body)) {
return errorResult();
}
if (!checkForUndefinedPrivateFields()) {
return errorResult();
}
ParseNode* node = body;
// Don't constant-fold inside "use asm" code, as this could create a parse
// tree that doesn't type-check as asm.js.
if (!pc_->useAsmOrInsideUseAsm()) {
if (!FoldConstants(this->fc_, this->parserAtoms(), this->bigInts(), &node,
&handler_)) {
return errorResult();
}
}
body = &node->as<ListNode>();
if (!this->setSourceMapInfo()) {
return errorResult();
}
// For global scripts, whether bindings are closed over or not doesn't
// matter, so no need to call propagateFreeNamesAndMarkClosedOver-
// Bindings. However, Annex B.3.3 functions still need to be marked.
if (!varScope.propagateAndMarkAnnexBFunctionBoxes(pc_, this)) {
return errorResult();
}
Maybe<GlobalScope::ParserData*> bindings =
newGlobalScopeData(pc_->varScope());
if (!bindings) {
return errorResult();
}
globalsc->bindings = *bindings;
return body;
}
template <typename Unit>
FullParseHandler::ModuleNodeResult Parser<FullParseHandler, Unit>::moduleBody(
ModuleSharedContext* modulesc) {
MOZ_ASSERT(checkOptionsCalled_);
this->compilationState_.moduleMetadata =
fc_->getAllocator()->template new_<StencilModuleMetadata>();
if (!this->compilationState_.moduleMetadata) {
return errorResult();
}
SourceParseContext modulepc(this, modulesc, nullptr);
if (!modulepc.init()) {
return errorResult();
}
ParseContext::VarScope varScope(this);
if (!varScope.init(pc_)) {
return errorResult();
}
ModuleNodeType moduleNode;
MOZ_TRY_VAR(moduleNode, handler_.newModule(pos()));
AutoAwaitIsKeyword<FullParseHandler, Unit> awaitIsKeyword(
this, AwaitIsModuleKeyword);
ListNode* stmtList;
MOZ_TRY_VAR(stmtList, statementList(YieldIsName));
MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
moduleNode->setBody(&stmtList->template as<ListNode>());
if (pc_->isAsync()) {
if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_generator_())) {
return errorResult();
}
if (!pc_->declareTopLevelDotGeneratorName()) {
return errorResult();
}
}
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (tt != TokenKind::Eof) {
error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt));
return errorResult();
}
// Set the module to async if an await keyword was found at the top level.
if (pc_->isAsync()) {
pc_->sc()->asModuleContext()->builder.noteAsync(
*this->compilationState_.moduleMetadata);
}
// Generate the Import/Export tables and store in CompilationState.
if (!modulesc->builder.buildTables(*this->compilationState_.moduleMetadata)) {
return errorResult();
}
// Check exported local bindings exist and mark them as closed over.
StencilModuleMetadata& moduleMetadata =
*this->compilationState_.moduleMetadata;
for (auto entry : moduleMetadata.localExportEntries) {
DeclaredNamePtr p = modulepc.varScope().lookupDeclaredName(entry.localName);
if (!p) {
UniqueChars str = this->parserAtoms().toPrintableString(entry.localName);
if (!str) {
ReportOutOfMemory(this->fc_);
return errorResult();
}
errorNoOffset(JSMSG_MISSING_EXPORT, str.get());
return errorResult();
}
p->value()->setClosedOver();
}
// Reserve an environment slot for a "*namespace*" psuedo-binding and mark as
// closed-over. We do not know until module linking if this will be used.
if (!noteDeclaredName(
TaggedParserAtomIndex::WellKnown::star_namespace_star_(),
DeclarationKind::Const, pos())) {
return errorResult();
}
modulepc.varScope()
.lookupDeclaredName(
TaggedParserAtomIndex::WellKnown::star_namespace_star_())
->value()
->setClosedOver();
if (!CheckParseTree(this->fc_, alloc_, stmtList)) {
return errorResult();
}
ParseNode* node = stmtList;
// Don't constant-fold inside "use asm" code, as this could create a parse
// tree that doesn't type-check as asm.js.
if (!pc_->useAsmOrInsideUseAsm()) {
if (!FoldConstants(this->fc_, this->parserAtoms(), this->bigInts(), &node,
&handler_)) {
return errorResult();
}
}
stmtList = &node->as<ListNode>();
if (!this->setSourceMapInfo()) {
return errorResult();
}
// Private names not lexically defined must trigger a syntax error.
if (!checkForUndefinedPrivateFields()) {
return errorResult();
}
if (!propagateFreeNamesAndMarkClosedOverBindings(modulepc.varScope())) {
return errorResult();
}
Maybe<ModuleScope::ParserData*> bindings =
newModuleScopeData(modulepc.varScope());
if (!bindings) {
return errorResult();
}
modulesc->bindings = *bindings;
return moduleNode;
}
template <typename Unit>
SyntaxParseHandler::ModuleNodeResult
Parser<SyntaxParseHandler, Unit>::moduleBody(ModuleSharedContext* modulesc) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return errorResult();
}
template <class ParseHandler>
typename ParseHandler::NameNodeResult
PerHandlerParser<ParseHandler>::newInternalDotName(TaggedParserAtomIndex name) {
NameNodeType nameNode;
MOZ_TRY_VAR(nameNode, newName(name));
if (!noteUsedName(name)) {
return errorResult();
}
return nameNode;
}
template <class ParseHandler>
typename ParseHandler::NameNodeResult
PerHandlerParser<ParseHandler>::newThisName() {
return newInternalDotName(TaggedParserAtomIndex::WellKnown::dot_this_());
}
template <class ParseHandler>
typename ParseHandler::NameNodeResult
PerHandlerParser<ParseHandler>::newNewTargetName() {
return newInternalDotName(TaggedParserAtomIndex::WellKnown::dot_newTarget_());
}
template <class ParseHandler>
typename ParseHandler::NameNodeResult
PerHandlerParser<ParseHandler>::newDotGeneratorName() {
return newInternalDotName(TaggedParserAtomIndex::WellKnown::dot_generator_());
}
template <class ParseHandler>
bool PerHandlerParser<ParseHandler>::finishFunctionScopes(
bool isStandaloneFunction) {
FunctionBox* funbox = pc_->functionBox();
if (funbox->hasParameterExprs) {
if (!propagateFreeNamesAndMarkClosedOverBindings(pc_->functionScope())) {
return false;
}
// Functions with parameter expressions utilize the FunctionScope for vars
// generated by sloppy-direct-evals, as well as arguments (which are
// lexicals bindings). If the function body has var bindings (or has a
// sloppy-direct-eval that might), then an extra VarScope must be created
// for them.
if (VarScopeHasBindings(pc_) ||
funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings()) {
funbox->setFunctionHasExtraBodyVarScope();
}
}
// See: JSFunction::needsCallObject()
if (FunctionScopeHasClosedOverBindings(pc_) ||
funbox->needsCallObjectRegardlessOfBindings()) {
funbox->setNeedsFunctionEnvironmentObjects();
}
if (funbox->isNamedLambda() && !isStandaloneFunction) {
if (!propagateFreeNamesAndMarkClosedOverBindings(pc_->namedLambdaScope())) {
return false;
}
// See: JSFunction::needsNamedLambdaEnvironment()
if (LexicalScopeHasClosedOverBindings(pc_, pc_->namedLambdaScope())) {
funbox->setNeedsFunctionEnvironmentObjects();
}
}
return true;
}
template <>
bool PerHandlerParser<FullParseHandler>::finishFunction(
bool isStandaloneFunction /* = false */) {
if (!finishFunctionScopes(isStandaloneFunction)) {
return false;
}
FunctionBox* funbox = pc_->functionBox();
ScriptStencil& script = funbox->functionStencil();
if (funbox->isInterpreted()) {
// BCE will need to generate bytecode for this.
funbox->emitBytecode = true;
this->compilationState_.nonLazyFunctionCount++;
}
bool hasParameterExprs = funbox->hasParameterExprs;
if (hasParameterExprs) {
Maybe<VarScope::ParserData*> bindings = newVarScopeData(pc_->varScope());
if (!bindings) {
return false;
}
funbox->setExtraVarScopeBindings(*bindings);
MOZ_ASSERT(bool(*bindings) == VarScopeHasBindings(pc_));
MOZ_ASSERT_IF(!funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(),
bool(*bindings) == funbox->functionHasExtraBodyVarScope());
}
{
Maybe<FunctionScope::ParserData*> bindings =
newFunctionScopeData(pc_->functionScope(), hasParameterExprs);
if (!bindings) {
return false;
}
funbox->setFunctionScopeBindings(*bindings);
}
if (funbox->isNamedLambda() && !isStandaloneFunction) {
Maybe<LexicalScope::ParserData*> bindings =
newLexicalScopeData(pc_->namedLambdaScope());
if (!bindings) {
return false;
}
funbox->setNamedLambdaBindings(*bindings);
}
funbox->finishScriptFlags();
funbox->copyFunctionFields(script);
if (this->compilationState_.isInitialStencil()) {
ScriptStencilExtra& scriptExtra = funbox->functionExtraStencil();
funbox->copyFunctionExtraFields(scriptExtra);
funbox->copyScriptExtraFields(scriptExtra);
}
return true;
}
template <>
bool PerHandlerParser<SyntaxParseHandler>::finishFunction(
bool isStandaloneFunction /* = false */) {
// The BaseScript for a lazily parsed function needs to know its set of
// free variables and inner functions so that when it is fully parsed, we
// can skip over any already syntax parsed inner functions and still
// retain correct scope information.
if (!finishFunctionScopes(isStandaloneFunction)) {
return false;
}
FunctionBox* funbox = pc_->functionBox();
ScriptStencil& script = funbox->functionStencil();
funbox->finishScriptFlags();
funbox->copyFunctionFields(script);
ScriptStencilExtra& scriptExtra = funbox->functionExtraStencil();
funbox->copyFunctionExtraFields(scriptExtra);
funbox->copyScriptExtraFields(scriptExtra);
// Elide nullptr sentinels from end of binding list. These are inserted for
// each scope regardless of if any bindings are actually closed over.
{
AtomVector& closedOver = pc_->closedOverBindingsForLazy();
while (!closedOver.empty() && !closedOver.back()) {
closedOver.popBack();
}
}
// Check if we will overflow the `ngcthings` field later.
mozilla::CheckedUint32 ngcthings =
mozilla::CheckedUint32(pc_->innerFunctionIndexesForLazy.length()) +
mozilla::CheckedUint32(pc_->closedOverBindingsForLazy().length());
if (!ngcthings.isValid()) {
ReportAllocationOverflow(fc_);
return false;
}
// If there are no script-things, we can return early without allocating.
if (ngcthings.value() == 0) {
MOZ_ASSERT(!script.hasGCThings());
return true;
}
TaggedScriptThingIndex* cursor = nullptr;
if (!this->compilationState_.allocateGCThingsUninitialized(
fc_, funbox->index(), ngcthings.value(), &cursor)) {
return false;
}
// Copy inner-function and closed-over-binding info for the stencil. The order
// is important here. We emit functions first, followed by the bindings info.
// The bindings list uses nullptr as delimiter to separates the bindings per
// scope.
//
// See: FullParseHandler::nextLazyInnerFunction(),
// FullParseHandler::nextLazyClosedOverBinding()
for (const ScriptIndex& index : pc_->innerFunctionIndexesForLazy) {
void* raw = &(*cursor++);
new (raw) TaggedScriptThingIndex(index);
}
for (auto binding : pc_->closedOverBindingsForLazy()) {
void* raw = &(*cursor++);
if (binding) {
this->parserAtoms().markUsedByStencil(binding, ParserAtom::Atomize::Yes);
new (raw) TaggedScriptThingIndex(binding);
} else {
new (raw) TaggedScriptThingIndex();
}
}
return true;
}
static YieldHandling GetYieldHandling(GeneratorKind generatorKind) {
if (generatorKind == GeneratorKind::NotGenerator) {
return YieldIsName;
}
return YieldIsKeyword;
}
static AwaitHandling GetAwaitHandling(FunctionAsyncKind asyncKind) {
if (asyncKind == FunctionAsyncKind::SyncFunction) {
return AwaitIsName;
}
return AwaitIsKeyword;
}
static FunctionFlags InitialFunctionFlags(FunctionSyntaxKind kind,
GeneratorKind generatorKind,
FunctionAsyncKind asyncKind,
bool isSelfHosting) {
FunctionFlags flags = {};
switch (kind) {
case FunctionSyntaxKind::Expression:
flags = (generatorKind == GeneratorKind::NotGenerator &&
asyncKind == FunctionAsyncKind::SyncFunction
? FunctionFlags::INTERPRETED_LAMBDA
: FunctionFlags::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC);
break;
case FunctionSyntaxKind::Arrow:
flags = FunctionFlags::INTERPRETED_LAMBDA_ARROW;
break;
case FunctionSyntaxKind::Method:
case FunctionSyntaxKind::FieldInitializer:
case FunctionSyntaxKind::StaticClassBlock:
flags = FunctionFlags::INTERPRETED_METHOD;
break;
case FunctionSyntaxKind::ClassConstructor:
case FunctionSyntaxKind::DerivedClassConstructor:
flags = FunctionFlags::INTERPRETED_CLASS_CTOR;
break;
case FunctionSyntaxKind::Getter:
flags = FunctionFlags::INTERPRETED_GETTER;
break;
case FunctionSyntaxKind::Setter:
flags = FunctionFlags::INTERPRETED_SETTER;
break;
default:
MOZ_ASSERT(kind == FunctionSyntaxKind::Statement);
flags = (generatorKind == GeneratorKind::NotGenerator &&
asyncKind == FunctionAsyncKind::SyncFunction
? FunctionFlags::INTERPRETED_NORMAL
: FunctionFlags::INTERPRETED_GENERATOR_OR_ASYNC);
}
if (isSelfHosting) {
flags.setIsSelfHostedBuiltin();
}
return flags;
}
template <typename Unit>
FullParseHandler::FunctionNodeResult
Parser<FullParseHandler, Unit>::standaloneFunction(
const Maybe<uint32_t>& parameterListEnd, FunctionSyntaxKind syntaxKind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
Directives inheritedDirectives, Directives* newDirectives) {
MOZ_ASSERT(checkOptionsCalled_);
// Skip prelude.
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (asyncKind == FunctionAsyncKind::AsyncFunction) {
MOZ_ASSERT(tt == TokenKind::Async);
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
}
MOZ_ASSERT(tt == TokenKind::Function);
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
if (generatorKind == GeneratorKind::Generator) {
MOZ_ASSERT(tt == TokenKind::Mul);
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
}
// Skip function name, if present.
TaggedParserAtomIndex explicitName;
if (TokenKindIsPossibleIdentifierName(tt)) {
explicitName = anyChars.currentName();
} else {
anyChars.ungetToken();
}
FunctionNodeType funNode;
MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos()));
ParamsBodyNodeType argsbody;
MOZ_TRY_VAR(argsbody, handler_.newParamsBody(pos()));
funNode->setBody(argsbody);
bool isSelfHosting = options().selfHostingMode;
FunctionFlags flags =
InitialFunctionFlags(syntaxKind, generatorKind, asyncKind, isSelfHosting);
FunctionBox* funbox =
newFunctionBox(funNode, explicitName, flags, /* toStringStart = */ 0,
inheritedDirectives, generatorKind, asyncKind);
if (!funbox) {
return errorResult();
}
// Function is not syntactically part of another script.
MOZ_ASSERT(funbox->index() == CompilationStencil::TopLevelIndex);
funbox->initStandalone(this->compilationState_.scopeContext, syntaxKind);
SourceParseContext funpc(this, funbox, newDirectives);
if (!funpc.init()) {
return errorResult();
}
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
AwaitHandling awaitHandling = GetAwaitHandling(asyncKind);
AutoAwaitIsKeyword<FullParseHandler, Unit> awaitIsKeyword(this,
awaitHandling);
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode,
syntaxKind, parameterListEnd,
/* isStandaloneFunction = */ true)) {
return errorResult();
}
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (tt != TokenKind::Eof) {
error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt));
return errorResult();
}
if (!CheckParseTree(this->fc_, alloc_, funNode)) {
return errorResult();
}
ParseNode* node = funNode;
// Don't constant-fold inside "use asm" code, as this could create a parse
// tree that doesn't type-check as asm.js.
if (!pc_->useAsmOrInsideUseAsm()) {
if (!FoldConstants(this->fc_, this->parserAtoms(), this->bigInts(), &node,
&handler_)) {
return errorResult();
}
}
funNode = &node->as<FunctionNode>();
if (!checkForUndefinedPrivateFields(nullptr)) {
return errorResult();
}
if (!this->setSourceMapInfo()) {
return errorResult();
}
return funNode;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::LexicalScopeNodeResult
GeneralParser<ParseHandler, Unit>::functionBody(InHandling inHandling,
YieldHandling yieldHandling,
FunctionSyntaxKind kind,
FunctionBodyType type) {
MOZ_ASSERT(pc_->isFunctionBox());
#ifdef DEBUG
uint32_t startYieldOffset = pc_->lastYieldOffset;
#endif
Node body;
if (type == StatementListBody) {
bool inheritedStrict = pc_->sc()->strict();
MOZ_TRY_VAR(body, statementList(yieldHandling));
// When we transitioned from non-strict to strict mode, we need to
// validate that all parameter names are valid strict mode names.
if (!inheritedStrict && pc_->sc()->strict()) {
MOZ_ASSERT(pc_->sc()->hasExplicitUseStrict(),
"strict mode should only change when a 'use strict' directive "
"is present");
if (!hasValidSimpleStrictParameterNames()) {
// Request that this function be reparsed as strict to report
// the invalid parameter name at the correct source location.
pc_->newDirectives->setStrict();
return errorResult();
}
}
} else {
MOZ_ASSERT(type == ExpressionBody);
// Async functions are implemented as generators, and generators are
// assumed to be statement lists, to prepend initial `yield`.
ListNodeType stmtList = null();
if (pc_->isAsync()) {
MOZ_TRY_VAR(stmtList, handler_.newStatementList(pos()));
}
Node kid;
MOZ_TRY_VAR(kid,
assignExpr(inHandling, yieldHandling, TripledotProhibited));
MOZ_TRY_VAR(body, handler_.newExpressionBody(kid));
if (pc_->isAsync()) {
handler_.addStatementToList(stmtList, body);
body = stmtList;
}
}
MOZ_ASSERT_IF(!pc_->isGenerator() && !pc_->isAsync(),
pc_->lastYieldOffset == startYieldOffset);
MOZ_ASSERT_IF(pc_->isGenerator(), kind != FunctionSyntaxKind::Arrow);
MOZ_ASSERT_IF(pc_->isGenerator(), type == StatementListBody);
if (pc_->needsDotGeneratorName()) {
MOZ_ASSERT_IF(!pc_->isAsync(), type == StatementListBody);
if (!pc_->declareDotGeneratorName()) {
return errorResult();
}
if (pc_->isGenerator()) {
NameNodeType generator;
MOZ_TRY_VAR(generator, newDotGeneratorName());
if (!handler_.prependInitialYield(handler_.asListNode(body), generator)) {
return errorResult();
}
}
}
if (pc_->numberOfArgumentsNames > 0 || kind == FunctionSyntaxKind::Arrow) {
MOZ_ASSERT(pc_->isFunctionBox());
pc_->sc()->setIneligibleForArgumentsLength();
}
// Declare the 'arguments', 'this', and 'new.target' bindings if necessary
// before finishing up the scope so these special bindings get marked as
// closed over if necessary. Arrow functions don't have these bindings.
if (kind != FunctionSyntaxKind::Arrow) {
bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings();
if (!pc_->declareFunctionArgumentsObject(usedNames_,
canSkipLazyClosedOverBindings)) {
return errorResult();
}
if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) {
return errorResult();
}
if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) {
return errorResult();
}
}
return finishLexicalScope(pc_->varScope(), body, ScopeKind::FunctionLexical);
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::matchOrInsertSemicolon(
Modifier modifier /* = TokenStream::SlashIsRegExp */) {
TokenKind tt = TokenKind::Eof;
if (!tokenStream.peekTokenSameLine(&tt, modifier)) {
return false;
}
if (tt != TokenKind::Eof && tt != TokenKind::Eol && tt != TokenKind::Semi &&
tt != TokenKind::RightCurly) {
/*
* When current token is `await` and it's outside of async function,
* it's possibly intended to be an await expression.
*
* await f();
* ^
* |
* tried to insert semicolon here
*
* Detect this situation and throw an understandable error. Otherwise
* we'd throw a confusing "unexpected token: (unexpected token)" error.
*/
if (!pc_->isAsync() && anyChars.currentToken().type == TokenKind::Await) {
error(JSMSG_AWAIT_OUTSIDE_ASYNC_OR_MODULE);
return false;
}
if (!yieldExpressionsSupported() &&
anyChars.currentToken().type == TokenKind::Yield) {
error(JSMSG_YIELD_OUTSIDE_GENERATOR);
return false;
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
if (options().explicitResourceManagement() &&
!this->pc_->isUsingSyntaxAllowed() &&
anyChars.currentToken().type == TokenKind::Using) {
error(JSMSG_USING_OUTSIDE_BLOCK_OR_MODULE);
return false;
}
#endif
/* Advance the scanner for proper error location reporting. */
tokenStream.consumeKnownToken(tt, modifier);
error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(tt));
return false;
}
bool matched;
return tokenStream.matchToken(&matched, TokenKind::Semi, modifier);
}
bool ParserBase::leaveInnerFunction(ParseContext* outerpc) {
MOZ_ASSERT(pc_ != outerpc);
MOZ_ASSERT_IF(outerpc->isFunctionBox(),
outerpc->functionBox()->index() < pc_->functionBox()->index());
// If the current function allows super.property but cannot have a home
// object, i.e., it is an arrow function, we need to propagate the flag to
// the outer ParseContext.
if (pc_->superScopeNeedsHomeObject()) {
if (!pc_->isArrowFunction()) {
MOZ_ASSERT(pc_->functionBox()->needsHomeObject());
} else {
outerpc->setSuperScopeNeedsHomeObject();
}
}
// Lazy functions inner to another lazy function need to be remembered by
// the inner function so that if the outer function is eventually parsed
// we do not need any further parsing or processing of the inner function.
//
// Append the inner function index here unconditionally; the vector is only
// used if the Parser using outerpc is a syntax parsing. See
// GeneralParser<SyntaxParseHandler>::finishFunction.
if (!outerpc->innerFunctionIndexesForLazy.append(
pc_->functionBox()->index())) {
return false;
}
PropagateTransitiveParseFlags(pc_->functionBox(), outerpc->sc());
return true;
}
TaggedParserAtomIndex ParserBase::prefixAccessorName(
PropertyType propType, TaggedParserAtomIndex propAtom) {
StringBuilder prefixed(fc_);
if (propType == PropertyType::Setter) {
if (!prefixed.append("set ")) {
return TaggedParserAtomIndex::null();
}
} else {
if (!prefixed.append("get ")) {
return TaggedParserAtomIndex::null();
}
}
if (!prefixed.append(this->parserAtoms(), propAtom)) {
return TaggedParserAtomIndex::null();
}
return prefixed.finishParserAtom(this->parserAtoms(), fc_);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::setFunctionStartAtPosition(
FunctionBox* funbox, TokenPos pos) const {
uint32_t startLine;
JS::LimitedColumnNumberOneOrigin startColumn;
tokenStream.computeLineAndColumn(pos.begin, &startLine, &startColumn);
// NOTE: `Debugger::CallData::findScripts` relies on sourceStart and
// lineno/column referring to the same location.
funbox->setStart(pos.begin, startLine, startColumn);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::setFunctionStartAtCurrentToken(
FunctionBox* funbox) const {
setFunctionStartAtPosition(funbox, anyChars.currentToken().pos);
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::functionArguments(
YieldHandling yieldHandling, FunctionSyntaxKind kind,
FunctionNodeType funNode) {
FunctionBox* funbox = pc_->functionBox();
// Modifier for the following tokens.
// TokenStream::SlashIsDiv for the following cases:
// async a => 1
// ^
//
// (a) => 1
// ^
//
// async (a) => 1
// ^
//
// function f(a) {}
// ^
//
// TokenStream::SlashIsRegExp for the following case:
// a => 1
// ^
Modifier firstTokenModifier =
kind != FunctionSyntaxKind::Arrow || funbox->isAsync()
? TokenStream::SlashIsDiv
: TokenStream::SlashIsRegExp;
TokenKind tt;
if (!tokenStream.getToken(&tt, firstTokenModifier)) {
return false;
}
if (kind == FunctionSyntaxKind::Arrow && TokenKindIsPossibleIdentifier(tt)) {
// Record the start of function source (for FunctionToString).
setFunctionStartAtCurrentToken(funbox);
ParamsBodyNodeType argsbody;
MOZ_TRY_VAR_OR_RETURN(argsbody, handler_.newParamsBody(pos()), false);
handler_.setFunctionFormalParametersAndBody(funNode, argsbody);
TaggedParserAtomIndex name = bindingIdentifier(yieldHandling);
if (!name) {
return false;
}
constexpr bool disallowDuplicateParams = true;
bool duplicatedParam = false;
if (!notePositionalFormalParameter(funNode, name, pos().begin,
disallowDuplicateParams,
&duplicatedParam)) {
return false;
}
MOZ_ASSERT(!duplicatedParam);
MOZ_ASSERT(pc_->positionalFormalParameterNames().length() == 1);
funbox->setLength(1);
funbox->setArgCount(1);
return true;
}
if (tt != TokenKind::LeftParen) {
error(kind == FunctionSyntaxKind::Arrow ? JSMSG_BAD_ARROW_ARGS
: JSMSG_PAREN_BEFORE_FORMAL);
return false;
}
// Record the start of function source (for FunctionToString).
setFunctionStartAtCurrentToken(funbox);
ParamsBodyNodeType argsbody;
MOZ_TRY_VAR_OR_RETURN(argsbody, handler_.newParamsBody(pos()), false);
handler_.setFunctionFormalParametersAndBody(funNode, argsbody);
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::RightParen,
TokenStream::SlashIsRegExp)) {
return false;
}
if (!matched) {
bool hasRest = false;
bool hasDefault = false;
bool duplicatedParam = false;
bool disallowDuplicateParams =
kind == FunctionSyntaxKind::Arrow ||
kind == FunctionSyntaxKind::Method ||
kind == FunctionSyntaxKind::FieldInitializer ||
kind == FunctionSyntaxKind::ClassConstructor;
AtomVector& positionalFormals = pc_->positionalFormalParameterNames();
if (kind == FunctionSyntaxKind::Getter) {
error(JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
return false;
}
while (true) {
if (hasRest) {
error(JSMSG_PARAMETER_AFTER_REST);
return false;
}
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return false;
}
if (tt == TokenKind::TripleDot) {
if (kind == FunctionSyntaxKind::Setter) {
error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
}
disallowDuplicateParams = true;
if (duplicatedParam) {
// Has duplicated args before the rest parameter.
error(JSMSG_BAD_DUP_ARGS);
return false;
}
hasRest = true;
funbox->setHasRest();
if (!tokenStream.getToken(&tt)) {
return false;
}
if (!TokenKindIsPossibleIdentifier(tt) &&
tt != TokenKind::LeftBracket && tt != TokenKind::LeftCurly) {
error(JSMSG_NO_REST_NAME);
return false;
}
}
switch (tt) {
case TokenKind::LeftBracket:
case TokenKind::LeftCurly: {
disallowDuplicateParams = true;
if (duplicatedParam) {
// Has duplicated args before the destructuring parameter.
error(JSMSG_BAD_DUP_ARGS);
return false;
}
funbox->hasDestructuringArgs = true;
Node destruct;
MOZ_TRY_VAR_OR_RETURN(
destruct,
destructuringDeclarationWithoutYieldOrAwait(
DeclarationKind::FormalParameter, yieldHandling, tt),
false);
if (!noteDestructuredPositionalFormalParameter(funNode, destruct)) {
return false;
}
break;
}
default: {
if (!TokenKindIsPossibleIdentifier(tt)) {
error(JSMSG_MISSING_FORMAL);
return false;
}
TaggedParserAtomIndex name = bindingIdentifier(yieldHandling);
if (!name) {
return false;
}
if (!notePositionalFormalParameter(funNode, name, pos().begin,
disallowDuplicateParams,
&duplicatedParam)) {
return false;
}
if (duplicatedParam) {
funbox->hasDuplicateParameters = true;
}
break;
}
}
if (positionalFormals.length() >= ARGNO_LIMIT) {
error(JSMSG_TOO_MANY_FUN_ARGS);
return false;
}
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Assign,
TokenStream::SlashIsRegExp)) {
return false;
}
if (matched) {
if (hasRest) {
error(JSMSG_REST_WITH_DEFAULT);
return false;
}
disallowDuplicateParams = true;
if (duplicatedParam) {
error(JSMSG_BAD_DUP_ARGS);
return false;
}
if (!hasDefault) {
hasDefault = true;
// The Function.length property is the number of formals
// before the first default argument.
funbox->setLength(positionalFormals.length() - 1);
}
funbox->hasParameterExprs = true;
Node def_expr;
MOZ_TRY_VAR_OR_RETURN(
def_expr, assignExprWithoutYieldOrAwait(yieldHandling), false);
if (!handler_.setLastFunctionFormalParameterDefault(funNode,
def_expr)) {
return false;
}
}
// Setter syntax uniquely requires exactly one argument.
if (kind == FunctionSyntaxKind::Setter) {
break;
}
if (!tokenStream.matchToken(&matched, TokenKind::Comma,
TokenStream::SlashIsRegExp)) {
return false;
}
if (!matched) {
break;
}
if (!hasRest) {
if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) {
return false;
}
if (tt == TokenKind::RightParen) {
break;
}
}
}
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return false;
}
if (tt != TokenKind::RightParen) {
if (kind == FunctionSyntaxKind::Setter) {
error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
}
error(JSMSG_PAREN_AFTER_FORMAL);
return false;
}
if (!hasDefault) {
funbox->setLength(positionalFormals.length() - hasRest);
}
funbox->setArgCount(positionalFormals.length());
} else if (kind == FunctionSyntaxKind::Setter) {
error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
}
return true;
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::skipLazyInnerFunction(
FunctionNode* funNode, uint32_t toStringStart, bool tryAnnexB) {
// When a lazily-parsed function is called, we only fully parse (and emit)
// that function, not any of its nested children. The initial syntax-only
// parse recorded the free variables of nested functions and their extents,
// so we can skip over them after accounting for their free variables.
MOZ_ASSERT(pc_->isOutermostOfCurrentCompile());
handler_.nextLazyInnerFunction();
const ScriptStencil& cachedData = handler_.cachedScriptData();
const ScriptStencilExtra& cachedExtra = handler_.cachedScriptExtra();
MOZ_ASSERT(toStringStart == cachedExtra.extent.toStringStart);
FunctionBox* funbox = newFunctionBox(funNode, cachedData, cachedExtra);
if (!funbox) {
return false;
}
ScriptStencil& script = funbox->functionStencil();
funbox->copyFunctionFields(script);
// If the inner lazy function is class constructor, connect it to the class
// statement/expression we are parsing.
if (funbox->isClassConstructor()) {
auto classStmt =
pc_->template findInnermostStatement<ParseContext::ClassStatement>();
MOZ_ASSERT(!classStmt->constructorBox);
classStmt->constructorBox = funbox;
}
MOZ_ASSERT_IF(pc_->isFunctionBox(),
pc_->functionBox()->index() < funbox->index());
PropagateTransitiveParseFlags(funbox, pc_->sc());
if (!tokenStream.advance(funbox->extent().sourceEnd)) {
return false;
}
// Append possible Annex B function box only upon successfully parsing.
if (tryAnnexB &&
!pc_->innermostScope()->addPossibleAnnexBFunctionBox(pc_, funbox)) {
return false;
}
return true;
}
template <typename Unit>
bool Parser<SyntaxParseHandler, Unit>::skipLazyInnerFunction(
FunctionNodeType funNode, uint32_t toStringStart, bool tryAnnexB) {
MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::skipLazyInnerFunction(
FunctionNodeType funNode, uint32_t toStringStart, bool tryAnnexB) {
return asFinalParser()->skipLazyInnerFunction(funNode, toStringStart,
tryAnnexB);
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::addExprAndGetNextTemplStrToken(
YieldHandling yieldHandling, ListNodeType nodeList, TokenKind* ttp) {
Node pn;
MOZ_TRY_VAR_OR_RETURN(pn, expr(InAllowed, yieldHandling, TripledotProhibited),
false);
handler_.addList(nodeList, pn);
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return false;
}
if (tt != TokenKind::RightCurly) {
error(JSMSG_TEMPLSTR_UNTERM_EXPR);
return false;
}
return tokenStream.getTemplateToken(ttp);
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::taggedTemplate(
YieldHandling yieldHandling, ListNodeType tagArgsList, TokenKind tt) {
CallSiteNodeType callSiteObjNode;
MOZ_TRY_VAR_OR_RETURN(callSiteObjNode,
handler_.newCallSiteObject(pos().begin), false);
handler_.addList(tagArgsList, callSiteObjNode);
pc_->sc()->setHasCallSiteObj();
while (true) {
if (!appendToCallSiteObj(callSiteObjNode)) {
return false;
}
if (tt != TokenKind::TemplateHead) {
break;
}
if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt)) {
return false;
}
}
handler_.setEndPosition(tagArgsList, callSiteObjNode);
return true;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::ListNodeResult
GeneralParser<ParseHandler, Unit>::templateLiteral(
YieldHandling yieldHandling) {
NameNodeType literal;
MOZ_TRY_VAR(literal, noSubstitutionUntaggedTemplate());
ListNodeType nodeList;
MOZ_TRY_VAR(nodeList,
handler_.newList(ParseNodeKind::TemplateStringListExpr, literal));
TokenKind tt;
do {
if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) {
return errorResult();
}
MOZ_TRY_VAR(literal, noSubstitutionUntaggedTemplate());
handler_.addList(nodeList, literal);
} while (tt == TokenKind::TemplateHead);
return nodeList;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::FunctionNodeResult
GeneralParser<ParseHandler, Unit>::functionDefinition(
FunctionNodeType funNode, uint32_t toStringStart, InHandling inHandling,
YieldHandling yieldHandling, TaggedParserAtomIndex funName,
FunctionSyntaxKind kind, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind, bool tryAnnexB /* = false */) {
MOZ_ASSERT_IF(kind == FunctionSyntaxKind::Statement, funName);
// If we see any inner function, note it on our current context. The bytecode
// emitter may eliminate the function later, but we use a conservative
// definition for consistency between lazy and full parsing.
pc_->sc()->setHasInnerFunctions();
// When fully parsing a lazy script, we do not fully reparse its inner
// functions, which are also lazy. Instead, their free variables and source
// extents are recorded and may be skipped.
if (handler_.reuseLazyInnerFunctions()) {
if (!skipLazyInnerFunction(funNode, toStringStart, tryAnnexB)) {
return errorResult();
}
return funNode;
}
bool isSelfHosting = options().selfHostingMode;
FunctionFlags flags =
InitialFunctionFlags(kind, generatorKind, asyncKind, isSelfHosting);
// Self-hosted functions with special function names require extended slots
// for various purposes.
bool forceExtended =
isSelfHosting && funName &&
this->parserAtoms().isExtendedUnclonedSelfHostedFunctionName(funName);
if (forceExtended) {
flags.setIsExtended();
}
// Speculatively parse using the directives of the parent parsing context.
// If a directive is encountered (e.g., "use strict") that changes how the
// function should have been parsed, we backup and reparse with the new set
// of directives.
Directives directives(pc_);
Directives newDirectives = directives;
Position start(tokenStream);
auto startObj = this->compilationState_.getPosition();
// Parse the inner function. The following is a loop as we may attempt to
// reparse a function due to failed syntax parsing and encountering new
// "use foo" directives.
while (true) {
if (trySyntaxParseInnerFunction(&funNode, funName, flags, toStringStart,
inHandling, yieldHandling, kind,
generatorKind, asyncKind, tryAnnexB,
directives, &newDirectives)) {
break;
}
// Return on error.
if (anyChars.hadError() || directives == newDirectives) {
return errorResult();
}
// Assignment must be monotonic to prevent infinitely attempting to
// reparse.
MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
directives = newDirectives;
// Rewind to retry parsing with new directives applied.
tokenStream.rewind(start);
this->compilationState_.rewind(startObj);
// functionFormalParametersAndBody may have already set body before failing.
handler_.setFunctionFormalParametersAndBody(funNode, null());
}
return funNode;
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::advancePastSyntaxParsedFunction(
SyntaxParser* syntaxParser) {
MOZ_ASSERT(getSyntaxParser() == syntaxParser);
// Advance this parser over tokens processed by the syntax parser.
Position currentSyntaxPosition(syntaxParser->tokenStream);
if (!tokenStream.fastForward(currentSyntaxPosition, syntaxParser->anyChars)) {
return false;
}
anyChars.adoptState(syntaxParser->anyChars);
tokenStream.adoptState(syntaxParser->tokenStream);
return true;
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::trySyntaxParseInnerFunction(
FunctionNode** funNode, TaggedParserAtomIndex explicitName,
FunctionFlags flags, uint32_t toStringStart, InHandling inHandling,
YieldHandling yieldHandling, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives) {
// Try a syntax parse for this inner function.
do {
// If we're assuming this function is an IIFE, always perform a full
// parse to avoid the overhead of a lazy syntax-only parse. Although
// the prediction may be incorrect, IIFEs are common enough that it
// pays off for lots of code.
if ((*funNode)->isLikelyIIFE() &&
generatorKind == GeneratorKind::NotGenerator &&
asyncKind == FunctionAsyncKind::SyncFunction) {
break;
}
SyntaxParser* syntaxParser = getSyntaxParser();
if (!syntaxParser) {
break;
}
UsedNameTracker::RewindToken token = usedNames_.getRewindToken();
auto statePosition = this->compilationState_.getPosition();
// Move the syntax parser to the current position in the stream. In the
// common case this seeks forward, but it'll also seek backward *at least*
// when arrow functions appear inside arrow function argument defaults
// (because we rewind to reparse arrow functions once we're certain they're
// arrow functions):
//
// var x = (y = z => 2) => q;
// // ^ we first seek to here to syntax-parse this function
// // ^ then we seek back to here to syntax-parse the outer function
Position currentPosition(tokenStream);
if (!syntaxParser->tokenStream.seekTo(currentPosition, anyChars)) {
return false;
}
// Make a FunctionBox before we enter the syntax parser, because |pn|
// still expects a FunctionBox to be attached to it during BCE, and
// the syntax parser cannot attach one to it.
FunctionBox* funbox =
newFunctionBox(*funNode, explicitName, flags, toStringStart,
inheritedDirectives, generatorKind, asyncKind);
if (!funbox) {
return false;
}
funbox->initWithEnclosingParseContext(pc_, kind);
auto syntaxNodeResult = syntaxParser->innerFunctionForFunctionBox(
SyntaxParseHandler::Node::NodeGeneric, pc_, funbox, inHandling,
yieldHandling, kind, newDirectives);
if (syntaxNodeResult.isErr()) {
if (syntaxParser->hadAbortedSyntaxParse()) {
// Try again with a full parse. UsedNameTracker needs to be
// rewound to just before we tried the syntax parse for
// correctness.
syntaxParser->clearAbortedSyntaxParse();
usedNames_.rewind(token);
this->compilationState_.rewind(statePosition);
MOZ_ASSERT(!fc_->hadErrors());
break;
}
return false;
}
if (!advancePastSyntaxParsedFunction(syntaxParser)) {
return false;
}
// Update the end position of the parse node.
(*funNode)->pn_pos.end = anyChars.currentToken().pos.end;
// Append possible Annex B function box only upon successfully parsing.
if (tryAnnexB) {
if (!pc_->innermostScope()->addPossibleAnnexBFunctionBox(pc_, funbox)) {
return false;
}
}
return true;
} while (false);
// We failed to do a syntax parse above, so do the full parse.
FunctionNodeType innerFunc;
MOZ_TRY_VAR_OR_RETURN(
innerFunc,
innerFunction(*funNode, pc_, explicitName, flags, toStringStart,
inHandling, yieldHandling, kind, generatorKind, asyncKind,
tryAnnexB, inheritedDirectives, newDirectives),
false);
*funNode = innerFunc;
return true;
}
template <typename Unit>
bool Parser<SyntaxParseHandler, Unit>::trySyntaxParseInnerFunction(
FunctionNodeType* funNode, TaggedParserAtomIndex explicitName,
FunctionFlags flags, uint32_t toStringStart, InHandling inHandling,
YieldHandling yieldHandling, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives) {
// This is already a syntax parser, so just parse the inner function.
FunctionNodeType innerFunc;
MOZ_TRY_VAR_OR_RETURN(
innerFunc,
innerFunction(*funNode, pc_, explicitName, flags, toStringStart,
inHandling, yieldHandling, kind, generatorKind, asyncKind,
tryAnnexB, inheritedDirectives, newDirectives),
false);
*funNode = innerFunc;
return true;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::trySyntaxParseInnerFunction(
FunctionNodeType* funNode, TaggedParserAtomIndex explicitName,
FunctionFlags flags, uint32_t toStringStart, InHandling inHandling,
YieldHandling yieldHandling, FunctionSyntaxKind kind,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
Directives inheritedDirectives, Directives* newDirectives) {
return asFinalParser()->trySyntaxParseInnerFunction(
funNode, explicitName, flags, toStringStart, inHandling, yieldHandling,
kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives,
newDirectives);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::FunctionNodeResult
GeneralParser<ParseHandler, Unit>::innerFunctionForFunctionBox(
FunctionNodeType funNode, ParseContext* outerpc, FunctionBox* funbox,
InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
Directives* newDirectives) {
// Note that it is possible for outerpc != this->pc_, as we may be
// attempting to syntax parse an inner function from an outer full
// parser. In that case, outerpc is a SourceParseContext from the full parser
// instead of the current top of the stack of the syntax parser.
// Push a new ParseContext.
SourceParseContext funpc(this, funbox, newDirectives);
if (!funpc.init()) {
return errorResult();
}
if (!functionFormalParametersAndBody(inHandling, yieldHandling, &funNode,
kind)) {
return errorResult();
}
if (!leaveInnerFunction(outerpc)) {
return errorResult();
}
return funNode;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::FunctionNodeResult
GeneralParser<ParseHandler, Unit>::innerFunction(
FunctionNodeType funNode, ParseContext* outerpc,
TaggedParserAtomIndex explicitName, FunctionFlags flags,
uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling,
FunctionSyntaxKind kind, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives,
Directives* newDirectives) {
// Note that it is possible for outerpc != this->pc_, as we may be
// attempting to syntax parse an inner function from an outer full
// parser. In that case, outerpc is a SourceParseContext from the full parser
// instead of the current top of the stack of the syntax parser.
FunctionBox* funbox =
newFunctionBox(funNode, explicitName, flags, toStringStart,
inheritedDirectives, generatorKind, asyncKind);
if (!funbox) {
return errorResult();
}
funbox->initWithEnclosingParseContext(outerpc, kind);
FunctionNodeType innerFunc;
MOZ_TRY_VAR(innerFunc,
innerFunctionForFunctionBox(funNode, outerpc, funbox, inHandling,
yieldHandling, kind, newDirectives));
// Append possible Annex B function box only upon successfully parsing.
if (tryAnnexB) {
if (!pc_->innermostScope()->addPossibleAnnexBFunctionBox(pc_, funbox)) {
return errorResult();
}
}
return innerFunc;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::appendToCallSiteObj(
CallSiteNodeType callSiteObj) {
Node cookedNode;
MOZ_TRY_VAR_OR_RETURN(cookedNode, noSubstitutionTaggedTemplate(), false);
auto atom = tokenStream.getRawTemplateStringAtom();
if (!atom) {
return false;
}
NameNodeType rawNode;
MOZ_TRY_VAR_OR_RETURN(rawNode, handler_.newTemplateStringLiteral(atom, pos()),
false);
handler_.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
return true;
}
template <typename Unit>
FullParseHandler::FunctionNodeResult
Parser<FullParseHandler, Unit>::standaloneLazyFunction(
CompilationInput& input, uint32_t toStringStart, bool strict,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind) {
MOZ_ASSERT(checkOptionsCalled_);
FunctionSyntaxKind syntaxKind = input.functionSyntaxKind();
FunctionNodeType funNode;
MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos()));
TaggedParserAtomIndex displayAtom =
this->getCompilationState().previousParseCache.displayAtom();
Directives directives(strict);
FunctionBox* funbox =
newFunctionBox(funNode, displayAtom, input.functionFlags(), toStringStart,
directives, generatorKind, asyncKind);
if (!funbox) {
return errorResult();
}
const ScriptStencilExtra& funExtra =
this->getCompilationState().previousParseCache.funExtra();
funbox->initFromLazyFunction(
funExtra, this->getCompilationState().scopeContext, syntaxKind);
if (funbox->useMemberInitializers()) {
funbox->setMemberInitializers(funExtra.memberInitializers());
}
Directives newDirectives = directives;
SourceParseContext funpc(this, funbox, &newDirectives);
if (!funpc.init()) {
return errorResult();
}
// Our tokenStream has no current token, so funNode's position is garbage.
// Substitute the position of the first token in our source. If the
// function is a not-async arrow, use TokenStream::SlashIsRegExp to keep
// verifyConsistentModifier from complaining (we will use
// TokenStream::SlashIsRegExp in functionArguments).
Modifier modifier = (input.functionFlags().isArrow() &&
asyncKind == FunctionAsyncKind::SyncFunction)
? TokenStream::SlashIsRegExp
: TokenStream::SlashIsDiv;
if (!tokenStream.peekTokenPos(&funNode->pn_pos, modifier)) {
return errorResult();
}
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
if (funbox->isSyntheticFunction()) {
// Currently default class constructors are the only synthetic function that
// supports delazification.
MOZ_ASSERT(funbox->isClassConstructor());
MOZ_ASSERT(funbox->extent().toStringStart == funbox->extent().sourceStart);
HasHeritage hasHeritage = funbox->isDerivedClassConstructor()
? HasHeritage::Yes
: HasHeritage::No;
TokenPos synthesizedBodyPos(funbox->extent().toStringStart,
funbox->extent().toStringEnd);
// Reset pos() to the `class` keyword for predictable results.
tokenStream.consumeKnownToken(TokenKind::Class);
if (!this->synthesizeConstructorBody(synthesizedBodyPos, hasHeritage,
funNode, funbox)) {
return errorResult();
}
} else {
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode,
syntaxKind)) {
MOZ_ASSERT(directives == newDirectives);
return errorResult();
}
}
if (!CheckParseTree(this->fc_, alloc_, funNode)) {
return errorResult();
}
ParseNode* node = funNode;
// Don't constant-fold inside "use asm" code, as this could create a parse
// tree that doesn't type-check as asm.js.
if (!pc_->useAsmOrInsideUseAsm()) {
if (!FoldConstants(this->fc_, this->parserAtoms(), this->bigInts(), &node,
&handler_)) {
return errorResult();
}
}
funNode = &node->as<FunctionNode>();
return funNode;
}
void ParserBase::setFunctionEndFromCurrentToken(FunctionBox* funbox) const {
if (compilationState_.isInitialStencil()) {
MOZ_ASSERT(anyChars.currentToken().type != TokenKind::Eof);
MOZ_ASSERT(anyChars.currentToken().type < TokenKind::Limit);
funbox->setEnd(anyChars.currentToken().pos.end);
} else {
// If we're delazifying an arrow function with expression body and
// the expression is also a function, we arrive here immediately after
// skipping the function by Parser::skipLazyInnerFunction.
//
// a => b => c
// ^
// |
// we're here
//
// In that case, the current token's type field is either Limit or
// poisoned.
// We shouldn't read the value if it's poisoned.
// See TokenStreamSpecific<Unit, AnyCharsAccess>::advance and
// mfbt/MemoryChecking.h for more details.
//
// Also, in delazification, the FunctionBox should already have the
// correct extent, and we shouldn't overwrite it here.
// See ScriptStencil variant of PerHandlerParser::newFunctionBox.
#if !defined(MOZ_ASAN) && !defined(MOZ_MSAN) && !defined(MOZ_VALGRIND)
MOZ_ASSERT(anyChars.currentToken().type != TokenKind::Eof);
#endif
MOZ_ASSERT(funbox->extent().sourceEnd == anyChars.currentToken().pos.end);
}
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::functionFormalParametersAndBody(
InHandling inHandling, YieldHandling yieldHandling,
FunctionNodeType* funNode, FunctionSyntaxKind kind,
const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
bool isStandaloneFunction /* = false */) {
// Given a properly initialized parse context, try to parse an actual
// function without concern for conversion to strict mode, use of lazy
// parsing and such.
FunctionBox* funbox = pc_->functionBox();
if (kind == FunctionSyntaxKind::ClassConstructor ||
kind == FunctionSyntaxKind::DerivedClassConstructor) {
if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) {
return false;
}
#ifdef ENABLE_DECORATORS
if (!noteUsedName(TaggedParserAtomIndex::WellKnown::
dot_instanceExtraInitializers_())) {
return false;
}
#endif
}
// See below for an explanation why arrow function parameters and arrow
// function bodies are parsed with different yield/await settings.
{
AwaitHandling awaitHandling =
kind == FunctionSyntaxKind::StaticClassBlock ? AwaitIsDisallowed
: (funbox->isAsync() ||
(kind == FunctionSyntaxKind::Arrow && awaitIsKeyword()))
? AwaitIsKeyword
: AwaitIsName;
AutoAwaitIsKeyword<ParseHandler, Unit> awaitIsKeyword(this, awaitHandling);
AutoInParametersOfAsyncFunction<ParseHandler, Unit> inParameters(
this, funbox->isAsync());
if (!functionArguments(yieldHandling, kind, *funNode)) {
return false;
}
}
Maybe<ParseContext::VarScope> varScope;
if (funbox->hasParameterExprs) {
varScope.emplace(this);
if (!varScope->init(pc_)) {
return false;
}
} else {
pc_->functionScope().useAsVarScope(pc_);
}
if (kind == FunctionSyntaxKind::Arrow) {
TokenKind tt;
if (!tokenStream.peekTokenSameLine(&tt)) {
return false;
}
if (tt == TokenKind::Eol) {
error(JSMSG_UNEXPECTED_TOKEN,
"'=>' on the same line after an argument list",
TokenKindToDesc(tt));
return false;
}
if (tt != TokenKind::Arrow) {
error(JSMSG_BAD_ARROW_ARGS);
return false;
}
tokenStream.consumeKnownToken(TokenKind::Arrow);
}
// When parsing something for new Function() we have to make sure to
// only treat a certain part of the source as a parameter list.
if (parameterListEnd.isSome() && parameterListEnd.value() != pos().begin) {
error(JSMSG_UNEXPECTED_PARAMLIST_END);
return false;
}
// Parse the function body.
FunctionBodyType bodyType = StatementListBody;
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return false;
}
uint32_t openedPos = 0;
if (tt != TokenKind::LeftCurly) {
if (kind != FunctionSyntaxKind::Arrow) {
error(JSMSG_CURLY_BEFORE_BODY);
return false;
}
anyChars.ungetToken();
bodyType = ExpressionBody;
funbox->setHasExprBody();
} else {
openedPos = pos().begin;
}
// Arrow function parameters inherit yieldHandling from the enclosing
// context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
// |yield| in the parameters is either a name or keyword, depending on
// whether the arrow function is enclosed in a generator function or not.
// Whereas the |yield| in the function body is always parsed as a name.
// The same goes when parsing |await| in arrow functions.
YieldHandling bodyYieldHandling = GetYieldHandling(pc_->generatorKind());
AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc_->asyncKind());
bool inheritedStrict = pc_->sc()->strict();
LexicalScopeNodeType body;
{
AutoAwaitIsKeyword<ParseHandler, Unit> awaitIsKeyword(this,
bodyAwaitHandling);
AutoInParametersOfAsyncFunction<ParseHandler, Unit> inParameters(this,
false);
MOZ_TRY_VAR_OR_RETURN(
body, functionBody(inHandling, bodyYieldHandling, kind, bodyType),
false);
}
// Revalidate the function name when we transitioned to strict mode.
if ((kind == FunctionSyntaxKind::Statement ||
kind == FunctionSyntaxKind::Expression) &&
funbox->explicitName() && !inheritedStrict && pc_->sc()->strict()) {
MOZ_ASSERT(pc_->sc()->hasExplicitUseStrict(),
"strict mode should only change when a 'use strict' directive "
"is present");
auto propertyName = funbox->explicitName();
YieldHandling nameYieldHandling;
if (kind == FunctionSyntaxKind::Expression) {
// Named lambda has binding inside it.
nameYieldHandling = bodyYieldHandling;
} else {
// Otherwise YieldHandling cannot be checked at this point
// because of different context.
// It should already be checked before this point.
nameYieldHandling = YieldIsName;
}
// We already use the correct await-handling at this point, therefore
// we don't need call AutoAwaitIsKeyword here.
uint32_t nameOffset = handler_.getFunctionNameOffset(*funNode, anyChars);
if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling)) {
return false;
}
}
if (bodyType == StatementListBody) {
// Cannot use mustMatchToken here because of internal compiler error on
// gcc 6.4.0, with linux 64 SM hazard build.
TokenKind actual;
if (!tokenStream.getToken(&actual, TokenStream::SlashIsRegExp)) {
return false;
}
if (actual != TokenKind::RightCurly) {
reportMissingClosing(JSMSG_CURLY_AFTER_BODY, JSMSG_CURLY_OPENED,
openedPos);
return false;
}
setFunctionEndFromCurrentToken(funbox);
} else {
MOZ_ASSERT(kind == FunctionSyntaxKind::Arrow);
if (anyChars.hadError()) {
return false;
}
setFunctionEndFromCurrentToken(funbox);
if (kind == FunctionSyntaxKind::Statement) {
if (!matchOrInsertSemicolon()) {
return false;
}
}
}
if (IsMethodDefinitionKind(kind) && pc_->superScopeNeedsHomeObject()) {
funbox->setNeedsHomeObject();
}
if (!finishFunction(isStandaloneFunction)) {
return false;
}
handler_.setEndPosition(body, pos().begin);
handler_.setEndPosition(*funNode, pos().end);
handler_.setFunctionBody(*funNode, body);
return true;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::FunctionNodeResult
GeneralParser<ParseHandler, Unit>::functionStmt(uint32_t toStringStart,
YieldHandling yieldHandling,
DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function));
// In sloppy mode, Annex B.3.2 allows labelled function declarations.
// Otherwise it's a parse error.
ParseContext::Statement* declaredInStmt = pc_->innermostStatement();
if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
MOZ_ASSERT(!pc_->sc()->strict(),
"labeled functions shouldn't be parsed in strict mode");
// Find the innermost non-label statement. Report an error if it's
// unbraced: functions can't appear in it. Otherwise the statement
// (or its absence) determines the scope the function's bound in.
while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
declaredInStmt = declaredInStmt->enclosing();
}
if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) {
error(JSMSG_SLOPPY_FUNCTION_LABEL);
return errorResult();
}
}
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
GeneratorKind generatorKind = GeneratorKind::NotGenerator;
if (tt == TokenKind::Mul) {
generatorKind = GeneratorKind::Generator;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
}
TaggedParserAtomIndex name;
if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name) {
return errorResult();
}
} else if (defaultHandling == AllowDefaultName) {
name = TaggedParserAtomIndex::WellKnown::default_();
anyChars.ungetToken();
} else {
/* Unnamed function expressions are forbidden in statement context. */
error(JSMSG_UNNAMED_FUNCTION_STMT);
return errorResult();
}
// Note the declared name and check for early errors.
DeclarationKind kind;
if (declaredInStmt) {
MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
kind =
(!pc_->sc()->strict() && generatorKind == GeneratorKind::NotGenerator &&
asyncKind == FunctionAsyncKind::SyncFunction)
? DeclarationKind::SloppyLexicalFunction
: DeclarationKind::LexicalFunction;
} else {
kind = pc_->atModuleLevel() ? DeclarationKind::ModuleBodyLevelFunction
: DeclarationKind::BodyLevelFunction;
}
if (!noteDeclaredName(name, kind, pos())) {
return errorResult();
}
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
FunctionNodeType funNode;
MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos()));
// Under sloppy mode, try Annex B.3.3 semantics. If making an additional
// 'var' binding of the same name does not throw an early error, do so.
// This 'var' binding would be assigned the function object when its
// declaration is reached, not at the start of the block.
//
// This semantics is implemented upon Scope exit in
// Scope::propagateAndMarkAnnexBFunctionBoxes.
bool tryAnnexB = kind == DeclarationKind::SloppyLexicalFunction;
YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
return functionDefinition(funNode, toStringStart, InAllowed, newYieldHandling,
name, syntaxKind, generatorKind, asyncKind,
tryAnnexB);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::FunctionNodeResult
GeneralParser<ParseHandler, Unit>::functionExpr(uint32_t toStringStart,
InvokedPrediction invoked,
FunctionAsyncKind asyncKind) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function));
AutoAwaitIsKeyword<ParseHandler, Unit> awaitIsKeyword(
this, GetAwaitHandling(asyncKind));
GeneratorKind generatorKind = GeneratorKind::NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
if (tt == TokenKind::Mul) {
generatorKind = GeneratorKind::Generator;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
}
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
TaggedParserAtomIndex name;
if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name) {
return errorResult();
}
} else {
anyChars.ungetToken();
}
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Expression;
FunctionNodeType funNode;
MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos()));
if (invoked) {
funNode = handler_.setLikelyIIFE(funNode);
}
return functionDefinition(funNode, toStringStart, InAllowed, yieldHandling,
name, syntaxKind, generatorKind, asyncKind);
}
/*
* Return true if this node, known to be an unparenthesized string literal
* that never contain escape sequences, could be the string of a directive in a
* Directive Prologue. Directive strings never contain escape sequences or line
* continuations.
*/
static inline bool IsUseStrictDirective(const TokenPos& pos,
TaggedParserAtomIndex atom) {
// the length of "use strict", including quotation.
static constexpr size_t useStrictLength = 12;
return atom == TaggedParserAtomIndex::WellKnown::use_strict_() &&
pos.begin + useStrictLength == pos.end;
}
static inline bool IsUseAsmDirective(const TokenPos& pos,
TaggedParserAtomIndex atom) {
// the length of "use asm", including quotation.
static constexpr size_t useAsmLength = 9;
return atom == TaggedParserAtomIndex::WellKnown::use_asm_() &&
pos.begin + useAsmLength == pos.end;
}
template <typename Unit>
bool Parser<SyntaxParseHandler, Unit>::asmJS(ListNodeType list) {
// While asm.js could technically be validated and compiled during syntax
// parsing, we have no guarantee that some later JS wouldn't abort the
// syntax parse and cause us to re-parse (and re-compile) the asm.js module.
// For simplicity, unconditionally abort the syntax parse when "use asm" is
// encountered so that asm.js is always validated/compiled exactly once
// during a full parse.
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::asmJS(ListNodeType list) {
// Disable syntax parsing in anything nested inside the asm.js module.
disableSyntaxParser();
// We should be encountering the "use asm" directive for the first time; if
// the directive is already, we must have failed asm.js validation and we're
// reparsing. In that case, don't try to validate again. A non-null
// newDirectives means we're not in a normal function.
if (!pc_->newDirectives || pc_->newDirectives->asmJS()) {
return true;
}
// If there is no ScriptSource, then we are doing a non-compiling parse and
// so we shouldn't (and can't, without a ScriptSource) compile.
if (ss == nullptr) {
return true;
}
pc_->functionBox()->useAsm = true;
// Attempt to validate and compile this asm.js module. On success, the
// tokenStream has been advanced to the closing }. On failure, the
// tokenStream is in an indeterminate state and we must reparse the
// function from the beginning. Reparsing is triggered by marking that a
// new directive has been encountered and returning 'false'.
bool validated;
if (!CompileAsmJS(this->fc_, this->parserAtoms(), *this, list, &validated)) {
return false;
}
if (!validated) {
pc_->newDirectives->setAsmJS();
return false;
}
return true;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::asmJS(ListNodeType list) {
return asFinalParser()->asmJS(list);
}
/*
* Recognize Directive Prologue members and directives. Assuming |pn| is a
* candidate for membership in a directive prologue, recognize directives and
* set |pc_|'s flags accordingly. If |pn| is indeed part of a prologue, set its
* |prologue| flag.
*
* Note that the following is a strict mode function:
*
* function foo() {
* "blah" // inserted semi colon
* "blurgh"
* "use\x20loose"
* "use strict"
* }
*
* That is, even though "use\x20loose" can never be a directive, now or in the
* future (because of the hex escape), the Directive Prologue extends through it
* to the "use strict" statement, which is indeed a directive.
*/
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::maybeParseDirective(
ListNodeType list, Node possibleDirective, bool* cont) {
TokenPos directivePos;
TaggedParserAtomIndex directive =
handler_.isStringExprStatement(possibleDirective, &directivePos);
*cont = !!directive;
if (!*cont) {
return true;
}
if (IsUseStrictDirective(directivePos, directive)) {
// Functions with non-simple parameter lists (destructuring,
// default or rest parameters) must not contain a "use strict"
// directive.
if (pc_->isFunctionBox()) {
FunctionBox* funbox = pc_->functionBox();
if (!funbox->hasSimpleParameterList()) {
const char* parameterKind = funbox->hasDestructuringArgs
? "destructuring"
: funbox->hasParameterExprs ? "default"
: "rest";
errorAt(directivePos.begin, JSMSG_STRICT_NON_SIMPLE_PARAMS,
parameterKind);
return false;
}
}
// We're going to be in strict mode. Note that this scope explicitly
// had "use strict";
pc_->sc()->setExplicitUseStrict();
if (!pc_->sc()->strict()) {
// Some strict mode violations can appear before a Use Strict Directive
// is applied. (See the |DeprecatedContent| enum initializers.) These
// violations can manifest in two ways.
//
// First, the violation can appear *before* the Use Strict Directive.
// Numeric literals (and therefore octal literals) can only precede a
// Use Strict Directive if this function's parameter list is not simple,
// but we reported an error for non-simple parameter lists above, so
// octal literals present no issue. But octal escapes and \8 and \9 can
// appear in the directive prologue before a Use Strict Directive:
//
// function f()
// {
// "hell\157 world"; // octal escape
// "\8"; "\9"; // NonOctalDecimalEscape
// "use strict"; // retroactively makes all the above errors
// }
//
// Second, the violation can appear *after* the Use Strict Directive but
// *before* the directive is recognized as terminated. This only
// happens when a directive is terminated by ASI, and the next token
// contains a violation:
//
// function a()
// {
// "use strict" // ASI
// 0755;
// }
// function b()
// {
// "use strict" // ASI
// "hell\157 world";
// }
// function c()
// {
// "use strict" // ASI
// "\8";
// }
//
// We note such violations when tokenizing. Then, if a violation has
// been observed at the time a "use strict" is applied, we report the
// error.
switch (anyChars.sawDeprecatedContent()) {
case DeprecatedContent::None:
break;
case DeprecatedContent::OctalLiteral:
error(JSMSG_DEPRECATED_OCTAL_LITERAL);
return false;
case DeprecatedContent::OctalEscape:
error(JSMSG_DEPRECATED_OCTAL_ESCAPE);
return false;
case DeprecatedContent::EightOrNineEscape:
error(JSMSG_DEPRECATED_EIGHT_OR_NINE_ESCAPE);
return false;
}
pc_->sc()->setStrictScript();
}
} else if (IsUseAsmDirective(directivePos, directive)) {
if (pc_->isFunctionBox()) {
return asmJS(list);
}
return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL);
}
return true;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::ListNodeResult
GeneralParser<ParseHandler, Unit>::statementList(YieldHandling yieldHandling) {
AutoCheckRecursionLimit recursion(this->fc_);
if (!recursion.check(this->fc_)) {
return errorResult();
}
ListNodeType stmtList;
MOZ_TRY_VAR(stmtList, handler_.newStatementList(pos()));
bool canHaveDirectives = pc_->atBodyLevel();
if (canHaveDirectives) {
// Clear flags for deprecated content that might have been seen in an
// enclosing context.
anyChars.clearSawDeprecatedContent();
}
bool canHaveHashbangComment = pc_->atTopLevel();
if (canHaveHashbangComment) {
tokenStream.consumeOptionalHashbangComment();
}
bool afterReturn = false;
bool warnedAboutStatementsAfterReturn = false;
uint32_t statementBegin = 0;
for (;;) {
TokenKind tt = TokenKind::Eof;
if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) {
if (anyChars.isEOF()) {
isUnexpectedEOF_ = true;
}
return errorResult();
}
if (tt == TokenKind::Eof || tt == TokenKind::RightCurly) {
TokenPos pos;
if (!tokenStream.peekTokenPos(&pos, TokenStream::SlashIsRegExp)) {
return errorResult();
}
handler_.setListEndPosition(stmtList, pos);
break;
}
if (afterReturn) {
if (!tokenStream.peekOffset(&statementBegin,
TokenStream::SlashIsRegExp)) {
return errorResult();
}
}
auto nextResult = statementListItem(yieldHandling, canHaveDirectives);
if (nextResult.isErr()) {
if (anyChars.isEOF()) {
isUnexpectedEOF_ = true;
}
return errorResult();
}
Node next = nextResult.unwrap();
if (!warnedAboutStatementsAfterReturn) {
if (afterReturn) {
if (!handler_.isStatementPermittedAfterReturnStatement(next)) {
if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) {
return errorResult();
}
warnedAboutStatementsAfterReturn = true;
}
} else if (handler_.isReturnStatement(next)) {
afterReturn = true;
}
}
if (canHaveDirectives) {
if (!maybeParseDirective(stmtList, next, &canHaveDirectives)) {
return errorResult();
}
}
handler_.addStatementToList(stmtList, next);
}
return stmtList;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult GeneralParser<ParseHandler, Unit>::condition(
InHandling inHandling, YieldHandling yieldHandling) {
if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_COND)) {
return errorResult();
}
Node pn;
MOZ_TRY_VAR(pn, exprInParens(inHandling, yieldHandling, TripledotProhibited));
if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_COND)) {
return errorResult();
}
return pn;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::matchLabel(
YieldHandling yieldHandling, TaggedParserAtomIndex* labelOut) {
MOZ_ASSERT(labelOut != nullptr);
TokenKind tt = TokenKind::Eof;
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) {
return false;
}
if (TokenKindIsPossibleIdentifier(tt)) {
tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp);
*labelOut = labelIdentifier(yieldHandling);
if (!*labelOut) {
return false;
}
} else {
*labelOut = TaggedParserAtomIndex::null();
}
return true;
}
template <class ParseHandler, typename Unit>
GeneralParser<ParseHandler, Unit>::PossibleError::PossibleError(
GeneralParser<ParseHandler, Unit>& parser)
: parser_(parser) {}
template <class ParseHandler, typename Unit>
typename GeneralParser<ParseHandler, Unit>::PossibleError::Error&
GeneralParser<ParseHandler, Unit>::PossibleError::error(ErrorKind kind) {
if (kind == ErrorKind::Expression) {
return exprError_;
}
if (kind == ErrorKind::Destructuring) {
return destructuringError_;
}
MOZ_ASSERT(kind == ErrorKind::DestructuringWarning);
return destructuringWarning_;
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::PossibleError::setResolved(
ErrorKind kind) {
error(kind).state_ = ErrorState::None;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::PossibleError::hasError(
ErrorKind kind) {
return error(kind).state_ == ErrorState::Pending;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler,
Unit>::PossibleError::hasPendingDestructuringError() {
return hasError(ErrorKind::Destructuring);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::PossibleError::setPending(
ErrorKind kind, const TokenPos& pos, unsigned errorNumber) {
// Don't overwrite a previously recorded error.
if (hasError(kind)) {
return;
}
// If we report an error later, we'll do it from the position where we set
// the state to pending.
Error& err = error(kind);
err.offset_ = pos.begin;
err.errorNumber_ = errorNumber;
err.state_ = ErrorState::Pending;
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::PossibleError::
setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber) {
setPending(ErrorKind::Destructuring, pos, errorNumber);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::PossibleError::
setPendingDestructuringWarningAt(const TokenPos& pos,
unsigned errorNumber) {
setPending(ErrorKind::DestructuringWarning, pos, errorNumber);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::PossibleError::
setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber) {
setPending(ErrorKind::Expression, pos, errorNumber);
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::PossibleError::checkForError(
ErrorKind kind) {
if (!hasError(kind)) {
return true;
}
Error& err = error(kind);
parser_.errorAt(err.offset_, err.errorNumber_);
return false;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler,
Unit>::PossibleError::checkForDestructuringErrorOrWarning() {
// Clear pending expression error, because we're definitely not in an
// expression context.
setResolved(ErrorKind::Expression);
// Report any pending destructuring error.
return checkForError(ErrorKind::Destructuring);
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler,
Unit>::PossibleError::checkForExpressionError() {
// Clear pending destructuring error, because we're definitely not
// in a destructuring context.
setResolved(ErrorKind::Destructuring);
setResolved(ErrorKind::DestructuringWarning);
// Report any pending expression error.
return checkForError(ErrorKind::Expression);
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::PossibleError::transferErrorTo(
ErrorKind kind, PossibleError* other) {
if (hasError(kind) && !other->hasError(kind)) {
Error& err = error(kind);
Error& otherErr = other->error(kind);
otherErr.offset_ = err.offset_;
otherErr.errorNumber_ = err.errorNumber_;
otherErr.state_ = err.state_;
}
}
template <class ParseHandler, typename Unit>
void GeneralParser<ParseHandler, Unit>::PossibleError::transferErrorsTo(
PossibleError* other) {
MOZ_ASSERT(other);
MOZ_ASSERT(this != other);
MOZ_ASSERT(&parser_ == &other->parser_,
"Can't transfer fields to an instance which belongs to a "
"different parser");
transferErrorTo(ErrorKind::Destructuring, other);
transferErrorTo(ErrorKind::Expression, other);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::BinaryNodeResult
GeneralParser<ParseHandler, Unit>::bindingInitializer(
Node lhs, DeclarationKind kind, YieldHandling yieldHandling) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign));
if (kind == DeclarationKind::FormalParameter) {
pc_->functionBox()->hasParameterExprs = true;
}
Node rhs;
MOZ_TRY_VAR(rhs, assignExpr(InAllowed, yieldHandling, TripledotProhibited));
BinaryNodeType assign;
MOZ_TRY_VAR(assign,
handler_.newAssignment(ParseNodeKind::AssignExpr, lhs, rhs));
return assign;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NameNodeResult
GeneralParser<ParseHandler, Unit>::bindingIdentifier(
DeclarationKind kind, YieldHandling yieldHandling) {
TaggedParserAtomIndex name = bindingIdentifier(yieldHandling);
if (!name) {
return errorResult();
}
NameNodeType binding;
MOZ_TRY_VAR(binding, newName(name));
if (!noteDeclaredName(name, kind, pos())) {
return errorResult();
}
return binding;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::bindingIdentifierOrPattern(
DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) {
if (tt == TokenKind::LeftBracket) {
return arrayBindingPattern(kind, yieldHandling);
}
if (tt == TokenKind::LeftCurly) {
return objectBindingPattern(kind, yieldHandling);
}
if (!TokenKindIsPossibleIdentifierName(tt)) {
error(JSMSG_NO_VARIABLE_NAME, TokenKindToDesc(tt));
return errorResult();
}
return bindingIdentifier(kind, yieldHandling);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::ListNodeResult
GeneralParser<ParseHandler, Unit>::objectBindingPattern(
DeclarationKind kind, YieldHandling yieldHandling) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
AutoCheckRecursionLimit recursion(this->fc_);
if (!recursion.check(this->fc_)) {
return errorResult();
}
uint32_t begin = pos().begin;
ListNodeType literal;
MOZ_TRY_VAR(literal, handler_.newObjectLiteral(begin));
Maybe<DeclarationKind> declKind = Some(kind);
TaggedParserAtomIndex propAtom;
for (;;) {
TokenKind tt;
if (!tokenStream.peekToken(&tt)) {
return errorResult();
}
if (tt == TokenKind::RightCurly) {
break;
}
if (tt == TokenKind::TripleDot) {
tokenStream.consumeKnownToken(TokenKind::TripleDot);
uint32_t begin = pos().begin;
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
if (!TokenKindIsPossibleIdentifierName(tt)) {
error(JSMSG_NO_VARIABLE_NAME, TokenKindToDesc(tt));
return errorResult();
}
NameNodeType inner;
MOZ_TRY_VAR(inner, bindingIdentifier(kind, yieldHandling));
if (!handler_.addSpreadProperty(literal, begin, inner)) {
return errorResult();
}
} else {
TokenPos namePos = anyChars.nextToken().pos;
PropertyType propType;
Node propName;
MOZ_TRY_VAR(propName, propertyOrMethodName(
yieldHandling, PropertyNameInPattern, declKind,
literal, &propType, &propAtom));
if (propType == PropertyType::Normal) {
// Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
Node binding;
MOZ_TRY_VAR(binding,
bindingIdentifierOrPattern(kind, yieldHandling, tt));
bool hasInitializer;
if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign,
TokenStream::SlashIsRegExp)) {
return errorResult();
}
Node bindingExpr;
if (hasInitializer) {
MOZ_TRY_VAR(bindingExpr,
bindingInitializer(binding, kind, yieldHandling));
} else {
bindingExpr = binding;
}
if (!handler_.addPropertyDefinition(literal, propName, bindingExpr)) {
return errorResult();
}
} else if (propType == PropertyType::Shorthand) {
// Handle e.g., |var {x, y} = o| as destructuring shorthand
// for |var {x: x, y: y} = o|.
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
NameNodeType binding;
MOZ_TRY_VAR(binding, bindingIdentifier(kind, yieldHandling));
if (!handler_.addShorthand(literal, handler_.asNameNode(propName),
binding)) {
return errorResult();
}
} else if (propType == PropertyType::CoverInitializedName) {
// Handle e.g., |var {x=1, y=2} = o| as destructuring
// shorthand with default values.
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
NameNodeType binding;
MOZ_TRY_VAR(binding, bindingIdentifier(kind, yieldHandling));
tokenStream.consumeKnownToken(TokenKind::Assign);
BinaryNodeType bindingExpr;
MOZ_TRY_VAR(bindingExpr,
bindingInitializer(binding, kind, yieldHandling));
if (!handler_.addPropertyDefinition(literal, propName, bindingExpr)) {
return errorResult();
}
} else {
errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME, TokenKindToDesc(tt));
return errorResult();
}
}
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Comma,
TokenStream::SlashIsInvalid)) {
return errorResult();
}
if (!matched) {
break;
}
if (tt == TokenKind::TripleDot) {
error(JSMSG_REST_WITH_COMMA);
return errorResult();
}
}
if (!mustMatchToken(TokenKind::RightCurly, [this, begin](TokenKind actual) {
this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED,
begin);
})) {
return errorResult();
}
handler_.setEndPosition(literal, pos().end);
return literal;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::ListNodeResult
GeneralParser<ParseHandler, Unit>::arrayBindingPattern(
DeclarationKind kind, YieldHandling yieldHandling) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket));
AutoCheckRecursionLimit recursion(this->fc_);
if (!recursion.check(this->fc_)) {
return errorResult();
}
uint32_t begin = pos().begin;
ListNodeType literal;
MOZ_TRY_VAR(literal, handler_.newArrayLiteral(begin));
uint32_t index = 0;
for (;; index++) {
if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
error(JSMSG_ARRAY_INIT_TOO_BIG);
return errorResult();
}
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
if (tt == TokenKind::RightBracket) {
anyChars.ungetToken();
break;
}
if (tt == TokenKind::Comma) {
if (!handler_.addElision(literal, pos())) {
return errorResult();
}
} else if (tt == TokenKind::TripleDot) {
uint32_t begin = pos().begin;
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
Node inner;
MOZ_TRY_VAR(inner, bindingIdentifierOrPattern(kind, yieldHandling, tt));
if (!handler_.addSpreadElement(literal, begin, inner)) {
return errorResult();
}
} else {
Node binding;
MOZ_TRY_VAR(binding, bindingIdentifierOrPattern(kind, yieldHandling, tt));
bool hasInitializer;
if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign,
TokenStream::SlashIsRegExp)) {
return errorResult();
}
Node element;
if (hasInitializer) {
MOZ_TRY_VAR(element, bindingInitializer(binding, kind, yieldHandling));
} else {
element = binding;
}
handler_.addArrayElement(literal, element);
}
if (tt != TokenKind::Comma) {
// If we didn't already match TokenKind::Comma in above case.
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Comma,
TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (!matched) {
break;
}
if (tt == TokenKind::TripleDot) {
error(JSMSG_REST_WITH_COMMA);
return errorResult();
}
}
}
if (!mustMatchToken(TokenKind::RightBracket, [this, begin](TokenKind actual) {
this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
JSMSG_BRACKET_OPENED, begin);
})) {
return errorResult();
}
handler_.setEndPosition(literal, pos().end);
return literal;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::destructuringDeclaration(
DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) {
MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
MOZ_ASSERT(tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly);
if (tt == TokenKind::LeftBracket) {
return arrayBindingPattern(kind, yieldHandling);
}
return objectBindingPattern(kind, yieldHandling);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::destructuringDeclarationWithoutYieldOrAwait(
DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) {
uint32_t startYieldOffset = pc_->lastYieldOffset;
uint32_t startAwaitOffset = pc_->lastAwaitOffset;
Node res;
MOZ_TRY_VAR(res, destructuringDeclaration(kind, yieldHandling, tt));
if (pc_->lastYieldOffset != startYieldOffset) {
errorAt(pc_->lastYieldOffset, JSMSG_YIELD_IN_PARAMETER);
return errorResult();
}
if (pc_->lastAwaitOffset != startAwaitOffset) {
errorAt(pc_->lastAwaitOffset, JSMSG_AWAIT_IN_PARAMETER);
return errorResult();
}
return res;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::LexicalScopeNodeResult
GeneralParser<ParseHandler, Unit>::blockStatement(YieldHandling yieldHandling,
unsigned errorNumber) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
uint32_t openedPos = pos().begin;
ParseContext::Statement stmt(pc_, StatementKind::Block);
ParseContext::Scope scope(this);
if (!scope.init(pc_)) {
return errorResult();
}
ListNodeType list;
MOZ_TRY_VAR(list, statementList(yieldHandling));
if (!mustMatchToken(TokenKind::RightCurly, [this, errorNumber,
openedPos](TokenKind actual) {
this->reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED, openedPos);
})) {
return errorResult();
}
return finishLexicalScope(scope, list);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::expressionAfterForInOrOf(
ParseNodeKind forHeadKind, YieldHandling yieldHandling) {
MOZ_ASSERT(forHeadKind == ParseNodeKind::ForIn ||
forHeadKind == ParseNodeKind::ForOf);
if (forHeadKind == ParseNodeKind::ForOf) {
return assignExpr(InAllowed, yieldHandling, TripledotProhibited);
}
return expr(InAllowed, yieldHandling, TripledotProhibited);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::declarationPattern(
DeclarationKind declKind, TokenKind tt, bool initialDeclaration,
YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
Node* forInOrOfExpression) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket) ||
anyChars.isCurrentTokenType(TokenKind::LeftCurly));
Node pattern;
MOZ_TRY_VAR(pattern, destructuringDeclaration(declKind, yieldHandling, tt));
if (initialDeclaration && forHeadKind) {
bool isForIn, isForOf;
if (!matchInOrOf(&isForIn, &isForOf)) {
return errorResult();
}
if (isForIn) {
*forHeadKind = ParseNodeKind::ForIn;
} else if (isForOf) {
*forHeadKind = ParseNodeKind::ForOf;
} else {
*forHeadKind = ParseNodeKind::ForHead;
}
if (*forHeadKind != ParseNodeKind::ForHead) {
MOZ_TRY_VAR(*forInOrOfExpression,
expressionAfterForInOrOf(*forHeadKind, yieldHandling));
return pattern;
}
}
if (!mustMatchToken(TokenKind::Assign, JSMSG_BAD_DESTRUCT_DECL)) {
return errorResult();
}
Node init;
MOZ_TRY_VAR(init, assignExpr(forHeadKind ? InProhibited : InAllowed,
yieldHandling, TripledotProhibited));
return handler_.newAssignment(ParseNodeKind::AssignExpr, pattern, init);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::AssignmentNodeResult
GeneralParser<ParseHandler, Unit>::initializerInNameDeclaration(
NameNodeType binding, DeclarationKind declKind, bool initialDeclaration,
YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
Node* forInOrOfExpression) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign));
uint32_t initializerOffset;
if (!tokenStream.peekOffset(&initializerOffset, TokenStream::SlashIsRegExp)) {
return errorResult();
}
Node initializer;
MOZ_TRY_VAR(initializer, assignExpr(forHeadKind ? InProhibited : InAllowed,
yieldHandling, TripledotProhibited));
if (forHeadKind && initialDeclaration) {
bool isForIn, isForOf;
if (!matchInOrOf(&isForIn, &isForOf)) {
return errorResult();
}
// An initialized declaration can't appear in a for-of:
//
// for (var/let/const x = ... of ...); // BAD
if (isForOf) {
errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL);
return errorResult();
}
if (isForIn) {
// Lexical declarations in for-in loops can't be initialized:
//
// for (let/const x = ... in ...); // BAD
if (DeclarationKindIsLexical(declKind)) {
errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL);
return errorResult();
}
// This leaves only initialized for-in |var| declarations. ES6
// forbids these; later ES un-forbids in non-strict mode code.
*forHeadKind = ParseNodeKind::ForIn;
if (!strictModeErrorAt(initializerOffset,
JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) {
return errorResult();
}
MOZ_TRY_VAR(
*forInOrOfExpression,
expressionAfterForInOrOf(ParseNodeKind::ForIn, yieldHandling));
} else {
*forHeadKind = ParseNodeKind::ForHead;
}
}
return handler_.finishInitializerAssignment(binding, initializer);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::declarationName(DeclarationKind declKind,
TokenKind tt,
bool initialDeclaration,
YieldHandling yieldHandling,
ParseNodeKind* forHeadKind,
Node* forInOrOfExpression) {
// Anything other than possible identifier is an error.
if (!TokenKindIsPossibleIdentifier(tt)) {
error(JSMSG_NO_VARIABLE_NAME, TokenKindToDesc(tt));
return errorResult();
}
TaggedParserAtomIndex name = bindingIdentifier(yieldHandling);
if (!name) {
return errorResult();
}
NameNodeType binding;
MOZ_TRY_VAR(binding, newName(name));
TokenPos namePos = pos();
// The '=' context after a variable name in a declaration is an opportunity
// for ASI, and thus for the next token to start an ExpressionStatement:
//
// var foo // VariableDeclaration
// /bar/g; // ExpressionStatement
//
// Therefore get the token here with SlashIsRegExp.
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::Assign,
TokenStream::SlashIsRegExp)) {
return errorResult();
}
Node declaration;
if (matched) {
MOZ_TRY_VAR(declaration,
initializerInNameDeclaration(binding, declKind,
initialDeclaration, yieldHandling,
forHeadKind, forInOrOfExpression));
} else {
declaration = binding;
if (initialDeclaration && forHeadKind) {
bool isForIn, isForOf;
if (!matchInOrOf(&isForIn, &isForOf)) {
return errorResult();
}
if (isForIn) {
*forHeadKind = ParseNodeKind::ForIn;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
if (declKind == DeclarationKind::Using ||
declKind == DeclarationKind::AwaitUsing) {
errorAt(namePos.begin, JSMSG_NO_IN_WITH_USING);
return errorResult();
}
#endif
} else if (isForOf) {
*forHeadKind = ParseNodeKind::ForOf;
} else {
*forHeadKind = ParseNodeKind::ForHead;
}
}
if (forHeadKind && *forHeadKind != ParseNodeKind::ForHead) {
MOZ_TRY_VAR(*forInOrOfExpression,
expressionAfterForInOrOf(*forHeadKind, yieldHandling));
} else {
// Normal const declarations, and const declarations in for(;;)
// heads, must be initialized.
if (declKind == DeclarationKind::Const) {
errorAt(namePos.begin, JSMSG_BAD_CONST_DECL);
return errorResult();
}
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
if (declKind == DeclarationKind::Using ||
declKind == DeclarationKind::AwaitUsing) {
errorAt(namePos.begin, JSMSG_BAD_USING_DECL);
return errorResult();
}
#endif
}
}
// Note the declared name after knowing whether or not we are in a for-of
// loop, due to special early error semantics in Annex B.3.5.
if (!noteDeclaredName(name, declKind, namePos)) {
return errorResult();
}
return declaration;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::DeclarationListNodeResult
GeneralParser<ParseHandler, Unit>::declarationList(
YieldHandling yieldHandling, ParseNodeKind kind,
ParseNodeKind* forHeadKind /* = nullptr */,
Node* forInOrOfExpression /* = nullptr */) {
MOZ_ASSERT(kind == ParseNodeKind::VarStmt || kind == ParseNodeKind::LetDecl ||
kind == ParseNodeKind::ConstDecl
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|| kind == ParseNodeKind::UsingDecl ||
kind == ParseNodeKind::AwaitUsingDecl
#endif
);
DeclarationKind declKind;
switch (kind) {
case ParseNodeKind::VarStmt:
declKind = DeclarationKind::Var;
break;
case ParseNodeKind::ConstDecl:
declKind = DeclarationKind::Const;
break;
case ParseNodeKind::LetDecl:
declKind = DeclarationKind::Let;
break;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case ParseNodeKind::UsingDecl:
declKind = DeclarationKind::Using;
break;
case ParseNodeKind::AwaitUsingDecl:
declKind = DeclarationKind::AwaitUsing;
break;
#endif
default:
MOZ_CRASH("Unknown declaration kind");
}
DeclarationListNodeType decl;
MOZ_TRY_VAR(decl, handler_.newDeclarationList(kind, pos()));
bool moreDeclarations;
bool initialDeclaration = true;
do {
MOZ_ASSERT_IF(!initialDeclaration && forHeadKind,
*forHeadKind == ParseNodeKind::ForHead);
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
Node binding;
if (tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly) {
MOZ_TRY_VAR(binding, declarationPattern(declKind, tt, initialDeclaration,
yieldHandling, forHeadKind,
forInOrOfExpression));
} else {
MOZ_TRY_VAR(binding, declarationName(declKind, tt, initialDeclaration,
yieldHandling, forHeadKind,
forInOrOfExpression));
}
handler_.addList(decl, binding);
// If we have a for-in/of loop, the above call matches the entirety
// of the loop head (up to the closing parenthesis).
if (forHeadKind && *forHeadKind != ParseNodeKind::ForHead) {
break;
}
initialDeclaration = false;
if (!tokenStream.matchToken(&moreDeclarations, TokenKind::Comma,
TokenStream::SlashIsRegExp)) {
return errorResult();
}
} while (moreDeclarations);
return decl;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::DeclarationListNodeResult
GeneralParser<ParseHandler, Unit>::lexicalDeclaration(
YieldHandling yieldHandling, DeclarationKind kind) {
MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|| kind == DeclarationKind::Using ||
kind == DeclarationKind::AwaitUsing
#endif
);
if (options().selfHostingMode) {
error(JSMSG_SELFHOSTED_LEXICAL);
return errorResult();
}
/*
* Parse body-level lets without a new block object. ES6 specs
* that an execution environment's initial lexical environment
* is the VariableEnvironment, i.e., body-level lets are in
* the same environment record as vars.
*
* However, they cannot be parsed exactly as vars, as ES6
* requires that uninitialized lets throw ReferenceError on use.
*
* See 8.1.1.1.6 and the note in 13.2.1.
*/
DeclarationListNodeType decl;
ParseNodeKind pnk;
switch (kind) {
case DeclarationKind::Const:
pnk = ParseNodeKind::ConstDecl;
break;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case DeclarationKind::Using:
pnk = ParseNodeKind::UsingDecl;
break;
case DeclarationKind::AwaitUsing:
pnk = ParseNodeKind::AwaitUsingDecl;
break;
#endif
case DeclarationKind::Let:
pnk = ParseNodeKind::LetDecl;
break;
default:
MOZ_CRASH("unexpected node kind");
}
MOZ_TRY_VAR(decl, declarationList(yieldHandling, pnk));
if (!matchOrInsertSemicolon()) {
return errorResult();
}
return decl;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NameNodeResult
GeneralParser<ParseHandler, Unit>::moduleExportName() {
MOZ_ASSERT(anyChars.currentToken().type == TokenKind::String);
TaggedParserAtomIndex name = anyChars.currentToken().atom();
if (!this->parserAtoms().isModuleExportName(name)) {
error(JSMSG_UNPAIRED_SURROGATE_EXPORT);
return errorResult();
}
return handler_.newStringLiteral(name, pos());
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::withClause(ListNodeType attributesSet) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assert) ||
anyChars.isCurrentTokenType(TokenKind::With));
if (!options().importAttributes()) {
error(JSMSG_IMPORT_ATTRIBUTES_NOT_SUPPORTED);
return false;
}
if (!abortIfSyntaxParser()) {
return false;
}
if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_AFTER_ASSERT)) {
return false;
}
// Handle the form |... assert {}|
TokenKind token;
if (!tokenStream.getToken(&token)) {
return false;
}
if (token == TokenKind::RightCurly) {
return true;
}
js::HashSet<TaggedParserAtomIndex, TaggedParserAtomIndexHasher,
js::SystemAllocPolicy>
usedAssertionKeys;
for (;;) {
TaggedParserAtomIndex keyName;
if (TokenKindIsPossibleIdentifierName(token)) {
keyName = anyChars.currentName();
} else if (token == TokenKind::String) {
keyName = anyChars.currentToken().atom();
} else {
error(JSMSG_ASSERT_KEY_EXPECTED);
return false;
}
auto p = usedAssertionKeys.lookupForAdd(keyName);
if (p) {
UniqueChars str = this->parserAtoms().toPrintableString(keyName);
if (!str) {
ReportOutOfMemory(this->fc_);
return false;
}
error(JSMSG_DUPLICATE_ASSERT_KEY, str.get());
return false;
}
if (!usedAssertionKeys.add(p, keyName)) {
ReportOutOfMemory(this->fc_);
return false;
}
NameNodeType keyNode;
MOZ_TRY_VAR_OR_RETURN(keyNode, newName(keyName), false);
if (!mustMatchToken(TokenKind::Colon, JSMSG_COLON_AFTER_ASSERT_KEY)) {
return false;
}
if (!mustMatchToken(TokenKind::String, JSMSG_ASSERT_STRING_LITERAL)) {
return false;
}
NameNodeType valueNode;
MOZ_TRY_VAR_OR_RETURN(valueNode, stringLiteral(), false);
BinaryNodeType importAttributeNode;
MOZ_TRY_VAR_OR_RETURN(importAttributeNode,
handler_.newImportAttribute(keyNode, valueNode),
false);
handler_.addList(attributesSet, importAttributeNode);
if (!tokenStream.getToken(&token)) {
return false;
}
if (token == TokenKind::Comma) {
if (!tokenStream.getToken(&token)) {
return false;
}
}
if (token == TokenKind::RightCurly) {
break;
}
}
return true;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::namedImports(
ListNodeType importSpecSet) {
if (!abortIfSyntaxParser()) {
return false;
}
while (true) {
// Handle the forms |import {} from 'a'| and
// |import { ..., } from 'a'| (where ... is non empty), by
// escaping the loop early if the next token is }.
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return false;
}
if (tt == TokenKind::RightCurly) {
break;
}
TaggedParserAtomIndex importName;
NameNodeType importNameNode = null();
if (TokenKindIsPossibleIdentifierName(tt)) {
importName = anyChars.currentName();
MOZ_TRY_VAR_OR_RETURN(importNameNode, newName(importName), false);
} else if (tt == TokenKind::String) {
MOZ_TRY_VAR_OR_RETURN(importNameNode, moduleExportName(), false);
} else {
error(JSMSG_NO_IMPORT_NAME);
return false;
}
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::As)) {
return false;
}
if (matched) {
TokenKind afterAs;
if (!tokenStream.getToken(&afterAs)) {
return false;
}
if (!TokenKindIsPossibleIdentifierName(afterAs)) {
error(JSMSG_NO_BINDING_NAME);
return false;
}
} else {
// String export names can't refer to local bindings.
if (tt == TokenKind::String) {
error(JSMSG_AS_AFTER_STRING);
return false;
}
// Keywords cannot be bound to themselves, so an import name
// that is a keyword is a syntax error if it is not followed
// by the keyword 'as'.
// See the ImportSpecifier production in ES6 section 15.2.2.
MOZ_ASSERT(importName);
if (IsKeyword(importName)) {
error(JSMSG_AS_AFTER_RESERVED_WORD, ReservedWordToCharZ(importName));
return false;
}
}
TaggedParserAtomIndex bindingAtom = importedBinding();
if (!bindingAtom) {
return false;
}
NameNodeType bindingName;
MOZ_TRY_VAR_OR_RETURN(bindingName, newName(bindingAtom), false);
if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) {
return false;
}
BinaryNodeType importSpec;
MOZ_TRY_VAR_OR_RETURN(
importSpec, handler_.newImportSpec(importNameNode, bindingName), false);
handler_.addList(importSpecSet, importSpec);
TokenKind next;
if (!tokenStream.getToken(&next)) {
return false;
}
if (next == TokenKind::RightCurly) {
break;
}
if (next != TokenKind::Comma) {
error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
return false;
}
}
return true;
}
template <class ParseHandler, typename Unit>
bool GeneralParser<ParseHandler, Unit>::namespaceImport(
ListNodeType importSpecSet) {
if (!abortIfSyntaxParser()) {
return false;
}
if (!mustMatchToken(TokenKind::As, JSMSG_AS_AFTER_IMPORT_STAR)) {
return false;
}
uint32_t begin = pos().begin;
if (!mustMatchToken(TokenKindIsPossibleIdentifierName,
JSMSG_NO_BINDING_NAME)) {
return false;
}
// Namespace imports are not indirect bindings but lexical
// definitions that hold a module namespace object. They are treated
// as const variables which are initialized during the
// ModuleInstantiate step.
TaggedParserAtomIndex bindingName = importedBinding();
if (!bindingName) {
return false;
}
NameNodeType bindingNameNode;
MOZ_TRY_VAR_OR_RETURN(bindingNameNode, newName(bindingName), false);
if (!noteDeclaredName(bindingName, DeclarationKind::Const, pos())) {
return false;
}
// The namespace import name is currently required to live on the
// environment.
pc_->varScope().lookupDeclaredName(bindingName)->value()->setClosedOver();
UnaryNodeType importSpec;
MOZ_TRY_VAR_OR_RETURN(importSpec,
handler_.newImportNamespaceSpec(begin, bindingNameNode),
false);
handler_.addList(importSpecSet, importSpec);
return true;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::BinaryNodeResult
GeneralParser<ParseHandler, Unit>::importDeclaration() {
if (!abortIfSyntaxParser()) {
return errorResult();
}
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import));
if (!pc_->atModuleLevel()) {
error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
return errorResult();
}
uint32_t begin = pos().begin;
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
ListNodeType importSpecSet;
MOZ_TRY_VAR(importSpecSet,
handler_.newList(ParseNodeKind::ImportSpecList, pos()));
if (tt == TokenKind::String) {
// Handle the form |import 'a'| by leaving the list empty. This is
// equivalent to |import {} from 'a'|.
handler_.setEndPosition(importSpecSet, pos().begin);
} else {
if (tt == TokenKind::LeftCurly) {
if (!namedImports(importSpecSet)) {
return errorResult();
}
} else if (tt == TokenKind::Mul) {
if (!namespaceImport(importSpecSet)) {
return errorResult();
}
} else if (TokenKindIsPossibleIdentifierName(tt)) {
// Handle the form |import a from 'b'|, by adding a single import
// specifier to the list, with 'default' as the import name and
// 'a' as the binding name. This is equivalent to
// |import { default as a } from 'b'|.
NameNodeType importName;
MOZ_TRY_VAR(importName,
newName(TaggedParserAtomIndex::WellKnown::default_()));
TaggedParserAtomIndex bindingAtom = importedBinding();
if (!bindingAtom) {
return errorResult();
}
NameNodeType bindingName;
MOZ_TRY_VAR(bindingName, newName(bindingAtom));
if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) {
return errorResult();
}
BinaryNodeType importSpec;
MOZ_TRY_VAR(importSpec, handler_.newImportSpec(importName, bindingName));
handler_.addList(importSpecSet, importSpec);
if (!tokenStream.peekToken(&tt)) {
return errorResult();
}
if (tt == TokenKind::Comma) {
tokenStream.consumeKnownToken(tt);
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
if (tt == TokenKind::LeftCurly) {
if (!namedImports(importSpecSet)) {
return errorResult();
}
} else if (tt == TokenKind::Mul) {
if (!namespaceImport(importSpecSet)) {
return errorResult();
}
} else {
error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
return errorResult();
}
}
} else {
error(JSMSG_DECLARATION_AFTER_IMPORT);
return errorResult();
}
if (!mustMatchToken(TokenKind::From, JSMSG_FROM_AFTER_IMPORT_CLAUSE)) {
return errorResult();
}
if (!mustMatchToken(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM)) {
return errorResult();
}
}
NameNodeType moduleSpec;
MOZ_TRY_VAR(moduleSpec, stringLiteral());
// The `assert` keyword has a [no LineTerminator here] production before it in
// the grammar -- `with` does not. We need to handle this distinction.
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
// `with` may have an EOL prior, so peek the next token and replace
// EOL if the next token is `with`.
if (tt == TokenKind::Eol) {
// Doing a regular peek won't produce Eol, but the actual next token.
TokenKind peekedToken;
if (!tokenStream.peekToken(&peekedToken, TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (peekedToken == TokenKind::With) {
tt = TokenKind::With;
}
}
ListNodeType importAttributeList;
MOZ_TRY_VAR(importAttributeList,
handler_.newList(ParseNodeKind::ImportAttributeList, pos()));
if (tt == TokenKind::With) {
tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp);
if (!withClause(importAttributeList)) {
return errorResult();
}
}
if (!matchOrInsertSemicolon(TokenStream::SlashIsRegExp)) {
return errorResult();
}
BinaryNodeType moduleRequest;
MOZ_TRY_VAR(moduleRequest,
handler_.newModuleRequest(moduleSpec, importAttributeList,
TokenPos(begin, pos().end)));
BinaryNodeType node;
MOZ_TRY_VAR(node, handler_.newImportDeclaration(importSpecSet, moduleRequest,
TokenPos(begin, pos().end)));
if (!processImport(node)) {
return errorResult();
}
return node;
}
template <class ParseHandler, typename Unit>
inline typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::importDeclarationOrImportExpr(
YieldHandling yieldHandling) {
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import));
TokenKind tt;
if (!tokenStream.peekToken(&tt)) {
return errorResult();
}
if (tt == TokenKind::Dot || tt == TokenKind::LeftParen) {
return expressionStatement(yieldHandling);
}
return importDeclaration();
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkExportedName(
TaggedParserAtomIndex exportName) {
switch (pc_->sc()->asModuleContext()->builder.noteExportedName(exportName)) {
case ModuleBuilder::NoteExportedNameResult::Success:
return true;
case ModuleBuilder::NoteExportedNameResult::OutOfMemory:
return false;
case ModuleBuilder::NoteExportedNameResult::AlreadyDeclared:
break;
}
UniqueChars str = this->parserAtoms().toPrintableString(exportName);
if (!str) {
ReportOutOfMemory(this->fc_);
return false;
}
error(JSMSG_DUPLICATE_EXPORT_NAME, str.get());
return false;
}
template <typename Unit>
inline bool Parser<SyntaxParseHandler, Unit>::checkExportedName(
TaggedParserAtomIndex exportName) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::checkExportedName(
TaggedParserAtomIndex exportName) {
return asFinalParser()->checkExportedName(exportName);
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkExportedNamesForArrayBinding(
ListNode* array) {
MOZ_ASSERT(array->isKind(ParseNodeKind::ArrayExpr));
for (ParseNode* node : array->contents()) {
if (node->isKind(ParseNodeKind::Elision)) {
continue;
}
ParseNode* binding;
if (node->isKind(ParseNodeKind::Spread)) {
binding = node->as<UnaryNode>().kid();
} else if (node->isKind(ParseNodeKind::AssignExpr)) {
binding = node->as<AssignmentNode>().left();
} else {
binding = node;
}
if (!checkExportedNamesForDeclaration(binding)) {
return false;
}
}
return true;
}
template <typename Unit>
inline bool Parser<SyntaxParseHandler, Unit>::checkExportedNamesForArrayBinding(
ListNodeType array) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool
GeneralParser<ParseHandler, Unit>::checkExportedNamesForArrayBinding(
ListNodeType array) {
return asFinalParser()->checkExportedNamesForArrayBinding(array);
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkExportedNamesForObjectBinding(
ListNode* obj) {
MOZ_ASSERT(obj->isKind(ParseNodeKind::ObjectExpr));
for (ParseNode* node : obj->contents()) {
MOZ_ASSERT(node->isKind(ParseNodeKind::MutateProto) ||
node->isKind(ParseNodeKind::PropertyDefinition) ||
node->isKind(ParseNodeKind::Shorthand) ||
node->isKind(ParseNodeKind::Spread));
ParseNode* target;
if (node->isKind(ParseNodeKind::Spread)) {
target = node->as<UnaryNode>().kid();
} else {
if (node->isKind(ParseNodeKind::MutateProto)) {
target = node->as<UnaryNode>().kid();
} else {
target = node->as<BinaryNode>().right();
}
if (target->isKind(ParseNodeKind::AssignExpr)) {
target = target->as<AssignmentNode>().left();
}
}
if (!checkExportedNamesForDeclaration(target)) {
return false;
}
}
return true;
}
template <typename Unit>
inline bool Parser<SyntaxParseHandler,
Unit>::checkExportedNamesForObjectBinding(ListNodeType obj) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool
GeneralParser<ParseHandler, Unit>::checkExportedNamesForObjectBinding(
ListNodeType obj) {
return asFinalParser()->checkExportedNamesForObjectBinding(obj);
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkExportedNamesForDeclaration(
ParseNode* node) {
if (node->isKind(ParseNodeKind::Name)) {
if (!checkExportedName(node->as<NameNode>().atom())) {
return false;
}
} else if (node->isKind(ParseNodeKind::ArrayExpr)) {
if (!checkExportedNamesForArrayBinding(&node->as<ListNode>())) {
return false;
}
} else {
MOZ_ASSERT(node->isKind(ParseNodeKind::ObjectExpr));
if (!checkExportedNamesForObjectBinding(&node->as<ListNode>())) {
return false;
}
}
return true;
}
template <typename Unit>
inline bool Parser<SyntaxParseHandler, Unit>::checkExportedNamesForDeclaration(
Node node) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::checkExportedNamesForDeclaration(
Node node) {
return asFinalParser()->checkExportedNamesForDeclaration(node);
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkExportedNamesForDeclarationList(
DeclarationListNodeType node) {
for (ParseNode* binding : node->contents()) {
if (binding->isKind(ParseNodeKind::AssignExpr)) {
binding = binding->as<AssignmentNode>().left();
} else {
MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
}
if (!checkExportedNamesForDeclaration(binding)) {
return false;
}
}
return true;
}
template <typename Unit>
inline bool
Parser<SyntaxParseHandler, Unit>::checkExportedNamesForDeclarationList(
DeclarationListNodeType node) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool
GeneralParser<ParseHandler, Unit>::checkExportedNamesForDeclarationList(
DeclarationListNodeType node) {
return asFinalParser()->checkExportedNamesForDeclarationList(node);
}
template <typename Unit>
inline bool Parser<FullParseHandler, Unit>::checkExportedNameForClause(
NameNode* nameNode) {
return checkExportedName(nameNode->atom());
}
template <typename Unit>
inline bool Parser<SyntaxParseHandler, Unit>::checkExportedNameForClause(
NameNodeType nameNode) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::checkExportedNameForClause(
NameNodeType nameNode) {
return asFinalParser()->checkExportedNameForClause(nameNode);
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkExportedNameForFunction(
FunctionNode* funNode) {
return checkExportedName(funNode->funbox()->explicitName());
}
template <typename Unit>
inline bool Parser<SyntaxParseHandler, Unit>::checkExportedNameForFunction(
FunctionNodeType funNode) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::checkExportedNameForFunction(
FunctionNodeType funNode) {
return asFinalParser()->checkExportedNameForFunction(funNode);
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkExportedNameForClass(
ClassNode* classNode) {
MOZ_ASSERT(classNode->names());
return checkExportedName(classNode->names()->innerBinding()->atom());
}
template <typename Unit>
inline bool Parser<SyntaxParseHandler, Unit>::checkExportedNameForClass(
ClassNodeType classNode) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::checkExportedNameForClass(
ClassNodeType classNode) {
return asFinalParser()->checkExportedNameForClass(classNode);
}
template <>
inline bool PerHandlerParser<FullParseHandler>::processExport(ParseNode* node) {
return pc_->sc()->asModuleContext()->builder.processExport(node);
}
template <>
inline bool PerHandlerParser<SyntaxParseHandler>::processExport(Node node) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <>
inline bool PerHandlerParser<FullParseHandler>::processExportFrom(
BinaryNodeType node) {
return pc_->sc()->asModuleContext()->builder.processExportFrom(node);
}
template <>
inline bool PerHandlerParser<SyntaxParseHandler>::processExportFrom(
BinaryNodeType node) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <>
inline bool PerHandlerParser<FullParseHandler>::processImport(
BinaryNodeType node) {
return pc_->sc()->asModuleContext()->builder.processImport(node);
}
template <>
inline bool PerHandlerParser<SyntaxParseHandler>::processImport(
BinaryNodeType node) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::BinaryNodeResult
GeneralParser<ParseHandler, Unit>::exportFrom(uint32_t begin, Node specList) {
if (!abortIfSyntaxParser()) {
return errorResult();
}
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::From));
if (!mustMatchToken(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM)) {
return errorResult();
}
NameNodeType moduleSpec;
MOZ_TRY_VAR(moduleSpec, stringLiteral());
TokenKind tt;
// The `assert` keyword has a [no LineTerminator here] production before it in
// the grammar -- `with` does not. We need to handle this distinction.
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) {
return errorResult();
}
// `with` may have an EOL prior, so peek the next token and replace
// EOL if the next token is `with`.
if (tt == TokenKind::Eol) {
// Doing a regular peek won't produce Eol, but the actual next token.
TokenKind peekedToken;
if (!tokenStream.peekToken(&peekedToken, TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (peekedToken == TokenKind::With) {
tt = TokenKind::With;
}
}
uint32_t moduleSpecPos = pos().begin;
ListNodeType importAttributeList;
MOZ_TRY_VAR(importAttributeList,
handler_.newList(ParseNodeKind::ImportAttributeList, pos()));
if (tt == TokenKind::With) {
tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp);
if (!withClause(importAttributeList)) {
return errorResult();
}
}
if (!matchOrInsertSemicolon(TokenStream::SlashIsRegExp)) {
return errorResult();
}
BinaryNodeType moduleRequest;
MOZ_TRY_VAR(moduleRequest,
handler_.newModuleRequest(moduleSpec, importAttributeList,
TokenPos(moduleSpecPos, pos().end)));
BinaryNodeType node;
MOZ_TRY_VAR(
node, handler_.newExportFromDeclaration(begin, specList, moduleRequest));
if (!processExportFrom(node)) {
return errorResult();
}
return node;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::BinaryNodeResult
GeneralParser<ParseHandler, Unit>::exportBatch(uint32_t begin) {
if (!abortIfSyntaxParser()) {
return errorResult();
}
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Mul));
uint32_t beginExportSpec = pos().begin;
ListNodeType kid;
MOZ_TRY_VAR(kid, handler_.newList(ParseNodeKind::ExportSpecList, pos()));
bool foundAs;
if (!tokenStream.matchToken(&foundAs, TokenKind::As)) {
return errorResult();
}
if (foundAs) {
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
NameNodeType exportName = null();
if (TokenKindIsPossibleIdentifierName(tt)) {
MOZ_TRY_VAR(exportName, newName(anyChars.currentName()));
} else if (tt == TokenKind::String) {
MOZ_TRY_VAR(exportName, moduleExportName());
} else {
error(JSMSG_NO_EXPORT_NAME);
return errorResult();
}
if (!checkExportedNameForClause(exportName)) {
return errorResult();
}
UnaryNodeType exportSpec;
MOZ_TRY_VAR(exportSpec,
handler_.newExportNamespaceSpec(beginExportSpec, exportName));
handler_.addList(kid, exportSpec);
} else {
// Handle the form |export *| by adding a special export batch
// specifier to the list.
NullaryNodeType exportSpec;
MOZ_TRY_VAR(exportSpec, handler_.newExportBatchSpec(pos()));
handler_.addList(kid, exportSpec);
}
if (!mustMatchToken(TokenKind::From, JSMSG_FROM_AFTER_EXPORT_STAR)) {
return errorResult();
}
return exportFrom(begin, kid);
}
template <typename Unit>
bool Parser<FullParseHandler, Unit>::checkLocalExportNames(ListNode* node) {
// ES 2017 draft 15.2.3.1.
for (ParseNode* next : node->contents()) {
ParseNode* name = next->as<BinaryNode>().left();
if (name->isKind(ParseNodeKind::StringExpr)) {
errorAt(name->pn_pos.begin, JSMSG_BAD_LOCAL_STRING_EXPORT);
return false;
}
MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
TaggedParserAtomIndex ident = name->as<NameNode>().atom();
if (!checkLocalExportName(ident, name->pn_pos.begin)) {
return false;
}
}
return true;
}
template <typename Unit>
bool Parser<SyntaxParseHandler, Unit>::checkLocalExportNames(
ListNodeType node) {
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template <class ParseHandler, typename Unit>
inline bool GeneralParser<ParseHandler, Unit>::checkLocalExportNames(
ListNodeType node) {
return asFinalParser()->checkLocalExportNames(node);
}
template <class ParseHandler, typename Unit>
typename ParseHandler::NodeResult
GeneralParser<ParseHandler, Unit>::exportClause(uint32_t begin) {
if (!abortIfSyntaxParser()) {
return errorResult();
}
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly));
ListNodeType kid;
MOZ_TRY_VAR(kid, handler_.newList(ParseNodeKind::ExportSpecList, pos()));
TokenKind tt;
while (true) {
// Handle the forms |export {}| and |export { ..., }| (where ... is non
// empty), by escaping the loop early if the next token is }.
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
if (tt == TokenKind::RightCurly) {
break;
}
NameNodeType bindingName = null();
if (TokenKindIsPossibleIdentifierName(tt)) {
MOZ_TRY_VAR(bindingName, newName(anyChars.currentName()));
} else if (tt == TokenKind::String) {
MOZ_TRY_VAR(bindingName, moduleExportName());
} else {
error(JSMSG_NO_BINDING_NAME);
return errorResult();
}
bool foundAs;
if (!tokenStream.matchToken(&foundAs, TokenKind::As)) {
return errorResult();
}
NameNodeType exportName = null();
if (foundAs) {
TokenKind tt;
if (!tokenStream.getToken(&tt)) {
return errorResult();
}
if (TokenKindIsPossibleIdentifierName(tt)) {
MOZ_TRY_VAR(exportName, newName(anyChars.currentName()));
} else if (tt == TokenKind::String) {
MOZ_TRY_VAR(exportName, moduleExportName());
} else {
error(JSMSG_NO_EXPORT_NAME);
return errorResult();
}
} else {
if (tt != TokenKind::String) {
MOZ_TRY_VAR(exportName, newName(anyChars.currentName()));
} else {
MOZ_TRY_VAR(exportName, moduleExportName());
}
}
if (!checkExportedNameForClause(exportName)) {
return errorResult();
}
BinaryNodeType exportSpec;
MOZ_TRY_VAR(exportSpec, handler_.newExportSpec(bindingName, exportName));
handler_.addList(kid, exportSpec);
TokenKind next;
if (!tokenStream.getToken(&next)) {
return errorResult();
}
if (next == TokenKind::RightCurly) {
break;
}
if (next != TokenKind::Comma) {
error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
return errorResult();
}
}
// Careful! If |from| follows, even on a new line, it must start a
// FromClause:
//
// export { x }
// from "foo"; // a single ExportDeclaration
//
// But if it doesn't, we might have an ASI opportunity in SlashIsRegExp
// context:
//
// export { x } // ExportDeclaration, terminated by ASI
// fro\u006D // ExpressionStatement, the name "from"
//
// In that case let matchOrInsertSemicolon sort out ASI or any necessary
// error.
bool matched;
if (!tokenStream.matchToken(&matched, TokenKind::From,
TokenStream::SlashIsRegExp)) {
return errorResult();
}
if (matched) {
return exportFrom(begin, kid);
}
if (!matchOrInsertSemicolon()) {
return errorResult();
}
if (!checkLocalExportNames(kid)) {
return errorResult();
}
UnaryNodeType node;
MOZ_TRY_VAR(node,
handler_.newExportDeclaration(kid, TokenPos(begin, pos().end)));
if (!processExport(node)) {
return errorResult();
}
return node;
}
template <class ParseHandler, typename Unit>
typename ParseHandler::UnaryNodeResult
GeneralParser<ParseHandler, Unit>::exportVariableStatement(uint32_t begin) {
if (!abortIfSyntaxParser()) {
return errorResult();
}
MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Var));
DeclarationListNodeType kid;
MOZ_TRY_VAR(kid, declarationList(YieldIsName, ParseNodeKind::VarStmt));
if (!matchOrInsertSemicolon()) {
return errorResult();
}
if (!checkExportedNamesForDeclarationList(kid)) {
return errorResult();
}
UnaryNodeType node;
MOZ_TRY_VAR(node,
handler_.newExportDeclaration(kid, TokenPos(begin, pos().end)));
if (!processExport(node)) {
return errorResult();
}
return node;
}
template <class ParseHandler, typename Unit>