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,
bool foldConstants, CompilationState& compilationState)
: ParserSharedBase(fc, compilationState, ParserSharedBase::Kind::Parser),
anyChars(fc, options, this),
ss(nullptr),
foldConstants_(foldConstants),
#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,
bool foldConstants, CompilationState& compilationState,
void* internalSyntaxParser)
: ParserBase(fc, options, foldConstants, 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, bool foldConstants,
CompilationState& compilationState, SyntaxParser* syntaxParser)
: Base(fc, options, foldConstants, 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();
}
if (foldConstants_) {
Node 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(), &node, &handler_)) {
return errorResult();
}
}
stmtList = handler_.asListNode(node);
}
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;