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/StringBuffer.h" // StringBuffer
#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);
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;
default:
MOZ_CRASH("Bad module scope BindingKind");
}
}
ModuleScope::ParserData* bindings = nullptr;
uint32_t numBindings =
imports.length() + vars.length() + lets.length() + consts.length();
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);
}
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:
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,
"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);
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;
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();
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);
}
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.