summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/Parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/Parser.cpp')
-rw-r--r--js/src/frontend/Parser.cpp9627
1 files changed, 9627 insertions, 0 deletions
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
new file mode 100644
index 000000000..49fef2bf9
--- /dev/null
+++ b/js/src/frontend/Parser.cpp
@@ -0,0 +1,9627 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jsapi.h"
+#include "jsatom.h"
+#include "jscntxt.h"
+#include "jsfun.h"
+#include "jsopcode.h"
+#include "jsscript.h"
+#include "jstypes.h"
+
+#include "builtin/ModuleObject.h"
+#include "builtin/SelfHostingDefines.h"
+#include "frontend/BytecodeCompiler.h"
+#include "frontend/FoldConstants.h"
+#include "frontend/TokenStream.h"
+#include "wasm/AsmJS.h"
+
+#include "jsatominlines.h"
+#include "jsscriptinlines.h"
+
+#include "frontend/ParseNode-inl.h"
+#include "vm/EnvironmentObject-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+using mozilla::Maybe;
+using mozilla::Move;
+using mozilla::Nothing;
+using mozilla::PodCopy;
+using mozilla::PodZero;
+using mozilla::Some;
+
+using JS::AutoGCRooter;
+
+namespace js {
+namespace frontend {
+
+using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr;
+using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
+using BindingIter = ParseContext::Scope::BindingIter;
+using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
+
+/* Read a token. Report an error and return null() if that token isn't of type tt. */
+#define MUST_MATCH_TOKEN_MOD(tt, modifier, errno) \
+ JS_BEGIN_MACRO \
+ TokenKind token; \
+ if (!tokenStream.getToken(&token, modifier)) \
+ return null(); \
+ if (token != tt) { \
+ report(ParseError, false, null(), errno); \
+ return null(); \
+ } \
+ JS_END_MACRO
+
+#define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errno)
+
+template <class T, class U>
+static inline void
+PropagateTransitiveParseFlags(const T* inner, U* outer)
+{
+ if (inner->bindingsAccessedDynamically())
+ outer->setBindingsAccessedDynamically();
+ if (inner->hasDebuggerStatement())
+ outer->setHasDebuggerStatement();
+ if (inner->hasDirectEval())
+ outer->setHasDirectEval();
+}
+
+static const char*
+DeclarationKindString(DeclarationKind kind)
+{
+ switch (kind) {
+ case DeclarationKind::PositionalFormalParameter:
+ case DeclarationKind::FormalParameter:
+ return "formal parameter";
+ case DeclarationKind::CoverArrowParameter:
+ return "cover arrow parameter";
+ case DeclarationKind::Var:
+ return "var";
+ case DeclarationKind::Let:
+ return "let";
+ case DeclarationKind::Const:
+ return "const";
+ case DeclarationKind::Import:
+ return "import";
+ case DeclarationKind::BodyLevelFunction:
+ case DeclarationKind::LexicalFunction:
+ return "function";
+ case DeclarationKind::VarForAnnexBLexicalFunction:
+ return "annex b var";
+ case DeclarationKind::ForOfVar:
+ return "var in for-of";
+ case DeclarationKind::SimpleCatchParameter:
+ case DeclarationKind::CatchParameter:
+ return "catch parameter";
+ }
+
+ MOZ_CRASH("Bad DeclarationKind");
+}
+
+static bool
+StatementKindIsBraced(StatementKind kind)
+{
+ return kind == StatementKind::Block ||
+ kind == StatementKind::Switch ||
+ kind == StatementKind::Try ||
+ kind == StatementKind::Catch ||
+ kind == StatementKind::Finally;
+}
+
+void
+ParseContext::Scope::dump(ParseContext* pc)
+{
+ ExclusiveContext* cx = pc->sc()->context;
+
+ fprintf(stdout, "ParseScope %p", this);
+
+ fprintf(stdout, "\n decls:\n");
+ for (DeclaredNameMap::Range r = declared_->all(); !r.empty(); r.popFront()) {
+ JSAutoByteString bytes;
+ if (!AtomToPrintableString(cx, r.front().key(), &bytes))
+ return;
+ DeclaredNameInfo& info = r.front().value().wrapped;
+ fprintf(stdout, " %s %s%s\n",
+ DeclarationKindString(info.kind()),
+ bytes.ptr(),
+ info.closedOver() ? " (closed over)" : "");
+ }
+
+ fprintf(stdout, "\n");
+}
+
+/* static */ void
+ParseContext::Scope::removeVarForAnnexBLexicalFunction(ParseContext* pc, JSAtom* name)
+{
+ // Local strict mode is allowed, e.g., a class binding removing a
+ // synthesized Annex B binding.
+ MOZ_ASSERT(!pc->sc()->strictScript);
+
+ for (ParseContext::Scope* scope = pc->innermostScope();
+ scope != pc->varScope().enclosing();
+ scope = scope->enclosing())
+ {
+ if (DeclaredNamePtr p = scope->declared_->lookup(name)) {
+ if (p->value()->kind() == DeclarationKind::VarForAnnexBLexicalFunction)
+ scope->declared_->remove(p);
+ }
+ }
+
+ // Annex B semantics no longer applies to any functions with this name, as
+ // an early error would have occurred.
+ pc->removeInnerFunctionBoxesForAnnexB(name);
+}
+
+static bool
+DeclarationKindIsCatchParameter(DeclarationKind kind)
+{
+ return kind == DeclarationKind::SimpleCatchParameter ||
+ kind == DeclarationKind::CatchParameter;
+}
+
+bool
+ParseContext::Scope::addCatchParameters(ParseContext* pc, Scope& catchParamScope)
+{
+ if (pc->useAsmOrInsideUseAsm())
+ return true;
+
+ for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty(); r.popFront()) {
+ DeclarationKind kind = r.front().value()->kind();
+ MOZ_ASSERT(DeclarationKindIsCatchParameter(kind));
+ JSAtom* name = r.front().key();
+ AddDeclaredNamePtr p = lookupDeclaredNameForAdd(name);
+ MOZ_ASSERT(!p);
+ if (!addDeclaredName(pc, p, name, kind))
+ return false;
+ }
+
+ return true;
+}
+
+void
+ParseContext::Scope::removeCatchParameters(ParseContext* pc, Scope& catchParamScope)
+{
+ if (pc->useAsmOrInsideUseAsm())
+ return;
+
+ for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty(); r.popFront()) {
+ DeclaredNamePtr p = declared_->lookup(r.front().key());
+ MOZ_ASSERT(p);
+
+ // This check is needed because the catch body could have declared
+ // vars, which would have been added to catchParamScope.
+ if (DeclarationKindIsCatchParameter(r.front().value()->kind()))
+ declared_->remove(p);
+ }
+}
+
+void
+SharedContext::computeAllowSyntax(Scope* scope)
+{
+ for (ScopeIter si(scope); si; si++) {
+ if (si.kind() == ScopeKind::Function) {
+ JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
+ if (fun->isArrow())
+ continue;
+ allowNewTarget_ = true;
+ allowSuperProperty_ = fun->allowSuperProperty();
+ allowSuperCall_ = fun->isDerivedClassConstructor();
+ return;
+ }
+ }
+}
+
+void
+SharedContext::computeThisBinding(Scope* scope)
+{
+ for (ScopeIter si(scope); si; si++) {
+ if (si.kind() == ScopeKind::Module) {
+ thisBinding_ = ThisBinding::Module;
+ return;
+ }
+
+ if (si.kind() == ScopeKind::Function) {
+ JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
+
+ // Arrow functions and generator expression lambdas don't have
+ // their own `this` binding.
+ if (fun->isArrow() || fun->nonLazyScript()->isGeneratorExp())
+ continue;
+
+ // Derived class constructors (including nested arrow functions and
+ // eval) need TDZ checks when accessing |this|.
+ if (fun->isDerivedClassConstructor())
+ needsThisTDZChecks_ = true;
+
+ thisBinding_ = ThisBinding::Function;
+ return;
+ }
+ }
+
+ thisBinding_ = ThisBinding::Global;
+}
+
+void
+SharedContext::computeInWith(Scope* scope)
+{
+ for (ScopeIter si(scope); si; si++) {
+ if (si.kind() == ScopeKind::With) {
+ inWith_ = true;
+ break;
+ }
+ }
+}
+
+EvalSharedContext::EvalSharedContext(ExclusiveContext* cx, JSObject* enclosingEnv,
+ Scope* enclosingScope, Directives directives,
+ bool extraWarnings)
+ : SharedContext(cx, Kind::Eval, directives, extraWarnings),
+ enclosingScope_(cx, enclosingScope),
+ bindings(cx)
+{
+ computeAllowSyntax(enclosingScope);
+ computeInWith(enclosingScope);
+ computeThisBinding(enclosingScope);
+
+ // Like all things Debugger, Debugger.Frame.eval needs special
+ // handling. Since the environment chain of such evals are non-syntactic
+ // (DebuggerEnvironmentProxy is not an EnvironmentObject), computing the
+ // this binding with respect to enclosingScope is incorrect if the
+ // Debugger.Frame is a function frame. Recompute the this binding if we
+ // are such an eval.
+ if (enclosingEnv && enclosingScope->hasOnChain(ScopeKind::NonSyntactic)) {
+ // For Debugger.Frame.eval with bindings, the environment chain may
+ // have more than the DebugEnvironmentProxy.
+ JSObject* env = enclosingEnv;
+ while (env) {
+ if (env->is<DebugEnvironmentProxy>())
+ env = &env->as<DebugEnvironmentProxy>().environment();
+
+ if (env->is<CallObject>()) {
+ computeThisBinding(env->as<CallObject>().callee().nonLazyScript()->bodyScope());
+ break;
+ }
+
+ env = env->enclosingEnvironment();
+ }
+ }
+}
+
+bool
+ParseContext::init()
+{
+ if (scriptId_ == UINT32_MAX) {
+ tokenStream_.reportError(JSMSG_NEED_DIET, js_script_str);
+ return false;
+ }
+
+ ExclusiveContext* cx = sc()->context;
+
+ if (isFunctionBox()) {
+ // Named lambdas always need a binding for their own name. If this
+ // binding is closed over when we finish parsing the function in
+ // finishExtraFunctionScopes, the function box needs to be marked as
+ // needing a dynamic DeclEnv object.
+ RootedFunction fun(cx, functionBox()->function());
+ if (fun->isNamedLambda()) {
+ if (!namedLambdaScope_->init(this))
+ return false;
+ AddDeclaredNamePtr p = namedLambdaScope_->lookupDeclaredNameForAdd(fun->name());
+ MOZ_ASSERT(!p);
+ if (!namedLambdaScope_->addDeclaredName(this, p, fun->name(), DeclarationKind::Const))
+ return false;
+ }
+
+ if (!functionScope_->init(this))
+ return false;
+
+ if (!positionalFormalParameterNames_.acquire(cx))
+ return false;
+ }
+
+ if (!closedOverBindingsForLazy_.acquire(cx))
+ return false;
+
+ if (!sc()->strict()) {
+ if (!innerFunctionBoxesForAnnexB_.acquire(cx))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ParseContext::addInnerFunctionBoxForAnnexB(FunctionBox* funbox)
+{
+ for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) {
+ if (!innerFunctionBoxesForAnnexB_[i]) {
+ innerFunctionBoxesForAnnexB_[i] = funbox;
+ return true;
+ }
+ }
+ return innerFunctionBoxesForAnnexB_->append(funbox);
+}
+
+void
+ParseContext::removeInnerFunctionBoxesForAnnexB(JSAtom* name)
+{
+ for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) {
+ if (FunctionBox* funbox = innerFunctionBoxesForAnnexB_[i]) {
+ if (funbox->function()->name() == name)
+ innerFunctionBoxesForAnnexB_[i] = nullptr;
+ }
+ }
+}
+
+void
+ParseContext::finishInnerFunctionBoxesForAnnexB()
+{
+ // Strict mode doesn't have wack Annex B function semantics. Or we
+ // could've failed to initialize ParseContext.
+ if (sc()->strict() || !innerFunctionBoxesForAnnexB_)
+ return;
+
+ for (uint32_t i = 0; i < innerFunctionBoxesForAnnexB_->length(); i++) {
+ if (FunctionBox* funbox = innerFunctionBoxesForAnnexB_[i])
+ funbox->isAnnexB = true;
+ }
+}
+
+ParseContext::~ParseContext()
+{
+ // Any funboxes still in the list at the end of parsing means no early
+ // error would have occurred for declaring a binding in the nearest var
+ // scope. Mark them as needing extra assignments to this var binding.
+ finishInnerFunctionBoxesForAnnexB();
+}
+
+bool
+UsedNameTracker::noteUse(ExclusiveContext* cx, JSAtom* name, uint32_t scriptId, uint32_t scopeId)
+{
+ if (UsedNameMap::AddPtr p = map_.lookupForAdd(name)) {
+ if (!p->value().noteUsedInScope(scriptId, scopeId))
+ return false;
+ } else {
+ UsedNameInfo info(cx);
+ if (!info.noteUsedInScope(scriptId, scopeId))
+ return false;
+ if (!map_.add(p, name, Move(info)))
+ return false;
+ }
+
+ return true;
+}
+
+void
+UsedNameTracker::UsedNameInfo::resetToScope(uint32_t scriptId, uint32_t scopeId)
+{
+ while (!uses_.empty()) {
+ Use& innermost = uses_.back();
+ if (innermost.scopeId < scopeId)
+ break;
+ MOZ_ASSERT(innermost.scriptId >= scriptId);
+ uses_.popBack();
+ }
+}
+
+void
+UsedNameTracker::rewind(RewindToken token)
+{
+ scriptCounter_ = token.scriptId;
+ scopeCounter_ = token.scopeId;
+
+ for (UsedNameMap::Range r = map_.all(); !r.empty(); r.popFront())
+ r.front().value().resetToScope(token.scriptId, token.scopeId);
+}
+
+FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead,
+ JSFunction* fun, Directives directives, bool extraWarnings,
+ GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
+ : ObjectBox(fun, traceListHead),
+ SharedContext(cx, Kind::ObjectBox, directives, extraWarnings),
+ enclosingScope_(nullptr),
+ namedLambdaBindings_(nullptr),
+ functionScopeBindings_(nullptr),
+ extraVarScopeBindings_(nullptr),
+ functionNode(nullptr),
+ bufStart(0),
+ bufEnd(0),
+ startLine(1),
+ startColumn(0),
+ length(0),
+ generatorKindBits_(GeneratorKindAsBits(generatorKind)),
+ asyncKindBits_(AsyncKindAsBits(asyncKind)),
+ isGenexpLambda(false),
+ hasDestructuringArgs(false),
+ hasParameterExprs(false),
+ hasDirectEvalInParameterExpr(false),
+ hasDuplicateParameters(false),
+ useAsm(false),
+ insideUseAsm(false),
+ isAnnexB(false),
+ wasEmitted(false),
+ declaredArguments(false),
+ usesArguments(false),
+ usesApply(false),
+ usesThis(false),
+ usesReturn(false),
+ funCxFlags()
+{
+ // Functions created at parse time may be set singleton after parsing and
+ // baked into JIT code, so they must be allocated tenured. They are held by
+ // the JSScript so cannot be collected during a minor GC anyway.
+ MOZ_ASSERT(fun->isTenured());
+}
+
+void
+FunctionBox::initFromLazyFunction()
+{
+ JSFunction* fun = function();
+ length = fun->nargs() - fun->hasRest();
+ if (fun->lazyScript()->isDerivedClassConstructor())
+ setDerivedClassConstructor();
+ if (fun->lazyScript()->needsHomeObject())
+ setNeedsHomeObject();
+ enclosingScope_ = fun->lazyScript()->enclosingScope();
+ initWithEnclosingScope(enclosingScope_);
+}
+
+void
+FunctionBox::initStandaloneFunction(Scope* enclosingScope)
+{
+ // Standalone functions are Function or Generator constructors and are
+ // always scoped to the global.
+ MOZ_ASSERT(enclosingScope->is<GlobalScope>());
+ JSFunction* fun = function();
+ length = fun->nargs() - fun->hasRest();
+ enclosingScope_ = enclosingScope;
+ allowNewTarget_ = true;
+ thisBinding_ = ThisBinding::Function;
+}
+
+void
+FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind)
+{
+ SharedContext* sc = enclosing->sc();
+ useAsm = sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm();
+
+ JSFunction* fun = function();
+
+ // Arrow functions and generator expression lambdas don't have
+ // their own `this` binding.
+ if (fun->isArrow()) {
+ allowNewTarget_ = sc->allowNewTarget();
+ allowSuperProperty_ = sc->allowSuperProperty();
+ allowSuperCall_ = sc->allowSuperCall();
+ needsThisTDZChecks_ = sc->needsThisTDZChecks();
+ thisBinding_ = sc->thisBinding();
+ } else {
+ allowNewTarget_ = true;
+ allowSuperProperty_ = fun->allowSuperProperty();
+
+ if (kind == DerivedClassConstructor) {
+ setDerivedClassConstructor();
+ allowSuperCall_ = true;
+ needsThisTDZChecks_ = true;
+ }
+
+ if (isGenexpLambda)
+ thisBinding_ = sc->thisBinding();
+ else
+ thisBinding_ = ThisBinding::Function;
+ }
+
+ if (sc->inWith()) {
+ inWith_ = true;
+ } else {
+ auto isWith = [](ParseContext::Statement* stmt) {
+ return stmt->kind() == StatementKind::With;
+ };
+
+ inWith_ = enclosing->findInnermostStatement(isWith);
+ }
+}
+
+void
+FunctionBox::initWithEnclosingScope(Scope* enclosingScope)
+{
+ if (!function()->isArrow()) {
+ allowNewTarget_ = true;
+ allowSuperProperty_ = function()->allowSuperProperty();
+
+ if (isDerivedClassConstructor()) {
+ setDerivedClassConstructor();
+ allowSuperCall_ = true;
+ needsThisTDZChecks_ = true;
+ }
+
+ thisBinding_ = ThisBinding::Function;
+ } else {
+ computeAllowSyntax(enclosingScope);
+ computeThisBinding(enclosingScope);
+ }
+
+ computeInWith(enclosingScope);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
+ unsigned errorNumber, va_list args)
+{
+ bool result = false;
+ switch (kind) {
+ case ParseError:
+ result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
+ break;
+ case ParseWarning:
+ result =
+ tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args);
+ break;
+ case ParseExtraWarning:
+ result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args);
+ break;
+ case ParseStrictError:
+ result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args);
+ break;
+ }
+ return result;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
+{
+ uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin;
+
+ va_list args;
+ va_start(args, errorNumber);
+ bool result = reportHelper(kind, strict, offset, errorNumber, args);
+ va_end(args);
+ return result;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args);
+ va_end(args);
+ return result;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset,
+ unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ bool result = reportHelper(kind, strict, offset, errorNumber, args);
+ va_end(args);
+ return result;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::abortIfSyntaxParser()
+{
+ handler.disableSyntaxParser();
+ return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::abortIfSyntaxParser()
+{
+ abortedSyntaxParse = true;
+ return false;
+}
+
+template <typename ParseHandler>
+Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc& alloc,
+ const ReadOnlyCompileOptions& options,
+ const char16_t* chars, size_t length,
+ bool foldConstants,
+ UsedNameTracker& usedNames,
+ Parser<SyntaxParseHandler>* syntaxParser,
+ LazyScript* lazyOuterFunction)
+ : AutoGCRooter(cx, PARSER),
+ context(cx),
+ alloc(alloc),
+ tokenStream(cx, options, chars, length, thisForCtor()),
+ traceListHead(nullptr),
+ pc(nullptr),
+ usedNames(usedNames),
+ sct(nullptr),
+ ss(nullptr),
+ keepAtoms(cx->perThreadData),
+ foldConstants(foldConstants),
+#ifdef DEBUG
+ checkOptionsCalled(false),
+#endif
+ abortedSyntaxParse(false),
+ isUnexpectedEOF_(false),
+ handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction)
+{
+ cx->perThreadData->frontendCollectionPool.addActiveCompilation();
+
+ // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
+ // which are not generated if functions are parsed lazily. Note that the
+ // standard "use strict" does not inhibit lazy parsing.
+ if (options.extraWarningsOption)
+ handler.disableSyntaxParser();
+
+ tempPoolMark = alloc.mark();
+}
+
+template<typename ParseHandler>
+bool
+Parser<ParseHandler>::checkOptions()
+{
+#ifdef DEBUG
+ checkOptionsCalled = true;
+#endif
+
+ if (!tokenStream.checkOptions())
+ return false;
+
+ return true;
+}
+
+template <typename ParseHandler>
+Parser<ParseHandler>::~Parser()
+{
+ MOZ_ASSERT(checkOptionsCalled);
+ alloc.release(tempPoolMark);
+
+ /*
+ * The parser can allocate enormous amounts of memory for large functions.
+ * Eagerly free the memory now (which otherwise won't be freed until the
+ * next GC) to avoid unnecessary OOMs.
+ */
+ alloc.freeAllIfHugeAndUnused();
+
+ context->perThreadData->frontendCollectionPool.removeActiveCompilation();
+}
+
+template <typename ParseHandler>
+ObjectBox*
+Parser<ParseHandler>::newObjectBox(JSObject* obj)
+{
+ MOZ_ASSERT(obj);
+
+ /*
+ * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
+ * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
+ * arenas containing the entries must be alive until we are done with
+ * scanning, parsing and code generation for the whole script or top-level
+ * function.
+ */
+
+ ObjectBox* objbox = alloc.new_<ObjectBox>(obj, traceListHead);
+ if (!objbox) {
+ ReportOutOfMemory(context);
+ return nullptr;
+ }
+
+ traceListHead = objbox;
+
+ return objbox;
+}
+
+template <typename ParseHandler>
+FunctionBox*
+Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, Directives inheritedDirectives,
+ GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+ bool tryAnnexB)
+{
+ MOZ_ASSERT(fun);
+ MOZ_ASSERT_IF(tryAnnexB, !pc->sc()->strict());
+
+ /*
+ * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
+ * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
+ * arenas containing the entries must be alive until we are done with
+ * scanning, parsing and code generation for the whole script or top-level
+ * function.
+ */
+ FunctionBox* funbox =
+ alloc.new_<FunctionBox>(context, alloc, traceListHead, fun, inheritedDirectives,
+ options().extraWarningsOption, generatorKind, asyncKind);
+ if (!funbox) {
+ ReportOutOfMemory(context);
+ return nullptr;
+ }
+
+ traceListHead = funbox;
+ if (fn)
+ handler.setFunctionBox(fn, funbox);
+
+ if (tryAnnexB && !pc->addInnerFunctionBoxForAnnexB(funbox))
+ return nullptr;
+
+ return funbox;
+}
+
+ModuleSharedContext::ModuleSharedContext(ExclusiveContext* cx, ModuleObject* module,
+ Scope* enclosingScope, ModuleBuilder& builder)
+ : SharedContext(cx, Kind::Module, Directives(true), false),
+ module_(cx, module),
+ enclosingScope_(cx, enclosingScope),
+ bindings(cx),
+ builder(builder)
+{
+ thisBinding_ = ThisBinding::Module;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::trace(JSTracer* trc)
+{
+ ObjectBox::TraceList(trc, traceListHead);
+}
+
+void
+MarkParser(JSTracer* trc, AutoGCRooter* parser)
+{
+ static_cast<Parser<FullParseHandler>*>(parser)->trace(trc);
+}
+
+/*
+ * Parse a top-level JS script.
+ */
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::parse()
+{
+ MOZ_ASSERT(checkOptionsCalled);
+
+ Directives directives(options().strictOption);
+ GlobalSharedContext globalsc(context, ScopeKind::Global,
+ directives, options().extraWarningsOption);
+ ParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
+ if (!globalpc.init())
+ return null();
+
+ ParseContext::VarScope varScope(this);
+ if (!varScope.init(pc))
+ return null();
+
+ Node pn = statementList(YieldIsName);
+ if (!pn)
+ return null();
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt != TOK_EOF) {
+ report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
+ "script", TokenKindToDesc(tt));
+ return null();
+ }
+ if (foldConstants) {
+ if (!FoldConstants(context, &pn, this))
+ return null();
+ }
+
+ return pn;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
+ unsigned errnum, unsigned anonerrnum)
+{
+ JSAutoByteString name;
+ if (JSAtom* atom = pc->functionBox()->function()->name()) {
+ if (!AtomToPrintableString(context, atom, &name))
+ return false;
+ } else {
+ errnum = anonerrnum;
+ }
+ return report(kind, pc->sc()->strict(), pn, errnum, name.ptr());
+}
+
+/*
+ * Strict mode forbids introducing new definitions for 'eval', 'arguments', or
+ * for any strict mode reserved keyword.
+ */
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::isValidStrictBinding(PropertyName* name)
+{
+ return name != context->names().eval &&
+ name != context->names().arguments &&
+ name != context->names().let &&
+ name != context->names().static_ &&
+ !(IsKeyword(name) && name != context->names().await);
+}
+
+/*
+ * Check that it is permitted to introduce a binding for |name|. Use |pos| for
+ * reporting error locations.
+ */
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos)
+{
+ if (!pc->sc()->needStrictChecks())
+ return true;
+
+ if (!isValidStrictBinding(name)) {
+ JSAutoByteString bytes;
+ if (!AtomToPrintableString(context, name, &bytes))
+ return false;
+ return reportWithOffset(ParseStrictError, pc->sc()->strict(), pos.begin,
+ JSMSG_BAD_BINDING, bytes.ptr());
+ }
+
+ return true;
+}
+
+/*
+ * Returns true if all parameter names are valid strict mode binding names and
+ * no duplicate parameter names are present.
+ */
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::hasValidSimpleStrictParameterNames()
+{
+ MOZ_ASSERT(pc->isFunctionBox() && pc->functionBox()->hasSimpleParameterList());
+
+ if (pc->functionBox()->hasDuplicateParameters)
+ return false;
+
+ for (size_t i = 0; i < pc->positionalFormalParameterNames().length(); i++) {
+ JSAtom* name = pc->positionalFormalParameterNames()[i];
+ MOZ_ASSERT(name);
+ if (!isValidStrictBinding(name->asPropertyName()))
+ return false;
+ }
+ return true;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind kind,
+ TokenPos pos)
+{
+ JSAutoByteString bytes;
+ if (!AtomToPrintableString(context, name, &bytes))
+ return;
+ reportWithOffset(ParseError, false, pos.begin, JSMSG_REDECLARED_VAR,
+ DeclarationKindString(kind), bytes.ptr());
+}
+
+// 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 <typename ParseHandler>
+bool
+Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
+ bool disallowDuplicateParams,
+ bool* duplicatedParam)
+{
+ if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
+ if (disallowDuplicateParams) {
+ report(ParseError, false, null(), 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()->needStrictChecks()) {
+ JSAutoByteString bytes;
+ if (!AtomToPrintableString(context, name, &bytes))
+ return false;
+ if (!report(ParseStrictError, pc->sc()->strict(), null(),
+ JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
+ {
+ return false;
+ }
+ }
+
+ *duplicatedParam = true;
+ } else {
+ DeclarationKind kind = DeclarationKind::PositionalFormalParameter;
+ if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
+ return false;
+ }
+
+ if (!pc->positionalFormalParameterNames().append(name)) {
+ ReportOutOfMemory(context);
+ return false;
+ }
+
+ Node paramNode = newName(name);
+ if (!paramNode)
+ return false;
+
+ if (!checkStrictBinding(name, pos()))
+ return false;
+
+ handler.addFunctionFormalParameter(fn, paramNode);
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::noteDestructuredPositionalFormalParameter(Node fn, Node destruct)
+{
+ // Append an empty name to the positional formals vector to keep track of
+ // argument slots when making FunctionScope::Data.
+ if (!pc->positionalFormalParameterNames().append(nullptr)) {
+ ReportOutOfMemory(context);
+ return false;
+ }
+
+ handler.addFunctionFormalParameter(fn, destruct);
+ return true;
+}
+
+static bool
+DeclarationKindIsVar(DeclarationKind kind)
+{
+ return kind == DeclarationKind::Var ||
+ kind == DeclarationKind::BodyLevelFunction ||
+ kind == DeclarationKind::VarForAnnexBLexicalFunction ||
+ kind == DeclarationKind::ForOfVar;
+}
+
+template <typename ParseHandler>
+Maybe<DeclarationKind>
+Parser<ParseHandler>::isVarRedeclaredInEval(HandlePropertyName name, DeclarationKind kind)
+{
+ MOZ_ASSERT(DeclarationKindIsVar(kind));
+ MOZ_ASSERT(pc->sc()->isEvalContext());
+
+ // In the case of eval, we also need to check enclosing VM scopes to see
+ // if the var declaration is allowed in the context.
+ //
+ // This check is necessary in addition to
+ // js::CheckEvalDeclarationConflicts because we only know during parsing
+ // if a var is bound by for-of.
+ Scope* enclosingScope = pc->sc()->compilationEnclosingScope();
+ Scope* varScope = EvalScope::nearestVarScopeForDirectEval(enclosingScope);
+ MOZ_ASSERT(varScope);
+ for (ScopeIter si(enclosingScope); si; si++) {
+ for (js::BindingIter bi(si.scope()); bi; bi++) {
+ if (bi.name() != name)
+ continue;
+
+ switch (bi.kind()) {
+ case BindingKind::Let: {
+ // Annex B.3.5 allows redeclaring simple (non-destructured)
+ // catch parameters with var declarations, except when it
+ // appears in a for-of.
+ bool annexB35Allowance = si.kind() == ScopeKind::SimpleCatch &&
+ kind != DeclarationKind::ForOfVar;
+ if (!annexB35Allowance) {
+ return Some(ScopeKindIsCatch(si.kind())
+ ? DeclarationKind::CatchParameter
+ : DeclarationKind::Let);
+ }
+ break;
+ }
+
+ case BindingKind::Const:
+ return Some(DeclarationKind::Const);
+
+ case BindingKind::Import:
+ case BindingKind::FormalParameter:
+ case BindingKind::Var:
+ case BindingKind::NamedLambdaCallee:
+ break;
+ }
+ }
+
+ if (si.scope() == varScope)
+ break;
+ }
+
+ return Nothing();
+}
+
+static bool
+DeclarationKindIsParameter(DeclarationKind kind)
+{
+ return kind == DeclarationKind::PositionalFormalParameter ||
+ kind == DeclarationKind::FormalParameter;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
+ Maybe<DeclarationKind>* redeclaredKind)
+{
+ MOZ_ASSERT(DeclarationKindIsVar(kind));
+
+ // It is an early error if a 'var' declaration appears inside a
+ // scope contour that has a lexical declaration of the same name. For
+ // example, the following are early errors:
+ //
+ // { let x; var x; }
+ // { { var x; } let x; }
+ //
+ // And the following are not:
+ //
+ // { var x; var x; }
+ // { { let x; } var x; }
+
+ for (ParseContext::Scope* scope = pc->innermostScope();
+ scope != pc->varScope().enclosing();
+ scope = scope->enclosing())
+ {
+ if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) {
+ DeclarationKind declaredKind = p->value()->kind();
+ if (DeclarationKindIsVar(declaredKind)) {
+ // Any vars that are redeclared as body-level functions must
+ // be recorded as body-level functions.
+ //
+ // In the case of global and eval scripts, GlobalDeclaration-
+ // Instantiation [1] and EvalDeclarationInstantiation [2]
+ // check for the declarability of global var and function
+ // bindings via CanDeclareVar [3] and CanDeclareGlobal-
+ // Function [4]. CanDeclareGlobalFunction is strictly more
+ // restrictive than CanDeclareGlobalVar, so record the more
+ // restrictive kind. These semantics are implemented in
+ // CheckCanDeclareGlobalBinding.
+ //
+ // For a var previously declared as ForOfVar, this previous
+ // DeclarationKind is used only to check for if the
+ // 'arguments' binding should be declared. Since body-level
+ // functions shadow 'arguments' [5], it is correct to alter
+ // the kind to BodyLevelFunction. See
+ // declareFunctionArgumentsObject.
+ //
+ // For a var previously declared as
+ // VarForAnnexBLexicalFunction, this previous DeclarationKind
+ // is used so that vars synthesized solely for Annex B.3.3 may
+ // be removed if an early error would occur. If a synthesized
+ // Annex B.3.3 var has the same name as a body-level function,
+ // this is not a redeclaration, and indeed, because the
+ // body-level function binds the name, this name should not be
+ // removed should a redeclaration occur in the future. Thus it
+ // is also correct to alter the kind to BodyLevelFunction.
+ //
+ // [1] ES 15.1.11
+ // [2] ES 18.2.1.3
+ // [3] ES 8.1.1.4.15
+ // [4] ES 8.1.1.4.16
+ // [5] ES 9.2.12
+ if (kind == DeclarationKind::BodyLevelFunction)
+ p->value()->alterKind(kind);
+ } else if (!DeclarationKindIsParameter(declaredKind)) {
+ // Annex B.3.5 allows redeclaring simple (non-destructured)
+ // catch parameters with var declarations, except when it
+ // appears in a for-of.
+ bool annexB35Allowance = declaredKind == DeclarationKind::SimpleCatchParameter &&
+ kind != DeclarationKind::ForOfVar;
+
+ // Annex B.3.3 allows redeclaring functions in the same block.
+ bool annexB33Allowance = declaredKind == DeclarationKind::LexicalFunction &&
+ kind == DeclarationKind::VarForAnnexBLexicalFunction &&
+ scope == pc->innermostScope();
+
+ if (!annexB35Allowance && !annexB33Allowance) {
+ *redeclaredKind = Some(declaredKind);
+ return true;
+ }
+ }
+ } else {
+ if (!scope->addDeclaredName(pc, p, name, kind))
+ return false;
+ }
+ }
+
+ if (!pc->sc()->strict() && pc->sc()->isEvalContext())
+ *redeclaredKind = isVarRedeclaredInEval(name, kind);
+
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name,
+ bool* tryAnnexB)
+{
+ Maybe<DeclarationKind> redeclaredKind;
+ if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, &redeclaredKind))
+ return false;
+
+ if (redeclaredKind) {
+ // If an early error would have occurred, undo all the
+ // VarForAnnexBLexicalFunction declarations.
+ *tryAnnexB = false;
+ ParseContext::Scope::removeVarForAnnexBLexicalFunction(pc, name);
+ } else {
+ *tryAnnexB = true;
+ }
+
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
+ DeclarationKind kind,
+ TokenPos pos)
+{
+ MOZ_ASSERT(DeclarationKindIsLexical(kind));
+
+ // It is an early error to declare a lexical binding not directly
+ // within a block.
+ if (!StatementKindIsBraced(stmt.kind()) &&
+ stmt.kind() != StatementKind::ForLoopLexicalHead)
+ {
+ reportWithOffset(ParseError, false, pos.begin,
+ stmt.kind() == StatementKind::Label
+ ? JSMSG_LEXICAL_DECL_LABEL
+ : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
+ DeclarationKindString(kind));
+ return false;
+ }
+
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind,
+ TokenPos pos)
+{
+ // 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;
+
+ if (!checkStrictBinding(name, pos))
+ return false;
+
+ switch (kind) {
+ case DeclarationKind::Var:
+ case DeclarationKind::BodyLevelFunction:
+ case DeclarationKind::ForOfVar: {
+ Maybe<DeclarationKind> redeclaredKind;
+ if (!tryDeclareVar(name, kind, &redeclaredKind))
+ return false;
+
+ if (redeclaredKind) {
+ reportRedeclaration(name, *redeclaredKind, pos);
+ return false;
+ }
+
+ 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) {
+ report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ return false;
+ }
+
+ if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
+ return false;
+
+ break;
+ }
+
+ case DeclarationKind::LexicalFunction: {
+ // Functions in block have complex allowances in sloppy mode for being
+ // labelled that other lexical declarations do not have. Those checks
+ // are more complex than calling checkLexicalDeclarationDirectlyWithin-
+ // Block and are done in checkFunctionDefinition.
+
+ 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.
+ //
+ // In sloppy mode, lexical functions may redeclare other lexical
+ // functions for web compatibility reasons.
+ if (pc->sc()->strict() ||
+ (p->value()->kind() != DeclarationKind::LexicalFunction &&
+ p->value()->kind() != DeclarationKind::VarForAnnexBLexicalFunction))
+ {
+ reportRedeclaration(name, p->value()->kind(), pos);
+ return false;
+ }
+
+ // Update the DeclarationKind to make a LexicalFunction
+ // declaration that shadows the VarForAnnexBLexicalFunction.
+ p->value()->alterKind(kind);
+ } else {
+ if (!scope->addDeclaredName(pc, p, name, kind))
+ return false;
+ }
+
+ break;
+ }
+
+ case DeclarationKind::Let:
+ case DeclarationKind::Const:
+ // The BoundNames of LexicalDeclaration and ForDeclaration must not
+ // contain 'let'. (CatchParameter is the only lexical binding form
+ // without this restriction.)
+ if (name == context->names().let) {
+ reportWithOffset(ParseError, false, pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET);
+ return false;
+ }
+
+ MOZ_FALLTHROUGH;
+
+ case DeclarationKind::Import:
+ // Module code is always strict, so 'let' is always a keyword and never a name.
+ MOZ_ASSERT(name != context->names().let);
+ MOZ_FALLTHROUGH;
+
+ case DeclarationKind::SimpleCatchParameter:
+ case DeclarationKind::CatchParameter: {
+ if (ParseContext::Statement* stmt = pc->innermostStatement()) {
+ if (!checkLexicalDeclarationDirectlyWithinBlock(*stmt, kind, pos))
+ return false;
+ }
+
+ ParseContext::Scope* scope = pc->innermostScope();
+
+ // 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);
+ return false;
+ }
+ }
+
+ // 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) {
+ // If the early error would have occurred due to Annex B.3.3
+ // semantics, remove the synthesized Annex B var declaration, do
+ // not report the redeclaration, and declare the lexical name.
+ if (p->value()->kind() == DeclarationKind::VarForAnnexBLexicalFunction) {
+ ParseContext::Scope::removeVarForAnnexBLexicalFunction(pc, name);
+ p = scope->lookupDeclaredNameForAdd(name);
+ MOZ_ASSERT(!p);
+ } else {
+ reportRedeclaration(name, p->value()->kind(), pos);
+ return false;
+ }
+ }
+
+ if (!p && !scope->addDeclaredName(pc, p, name, kind))
+ 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 "
+ "tryDeclareVarForAnnexBLexicalFunction");
+ break;
+ }
+
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::noteUsedName(HandlePropertyName name)
+{
+ // If the we are delazifying, the LazyScript already has all the
+ // closed-over info for bindings and there's no need to track used names.
+ if (handler.canSkipLazyClosedOverBindings())
+ return true;
+
+ // 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.
+ ParseContext::Scope* scope = pc->innermostScope();
+ if (pc->sc()->isGlobalContext() && scope == &pc->varScope())
+ return true;
+
+ return usedNames.noteUse(context, name, pc->scriptId(), scope->id());
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::hasUsedName(HandlePropertyName name)
+{
+ if (UsedNamePtr p = usedNames.lookup(name))
+ return p->value().isUsedInScript(pc->scriptId());
+ return false;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
+{
+ if (handler.canSkipLazyClosedOverBindings()) {
+ // Scopes are nullptr-delimited in the LazyScript closed over bindings
+ // array.
+ while (JSAtom* name = handler.nextLazyClosedOverBinding())
+ scope.lookupDeclaredName(name)->value()->setClosedOver();
+ return true;
+ }
+
+ bool isSyntaxParser = mozilla::IsSame<ParseHandler, SyntaxParseHandler>::value;
+ uint32_t scriptId = pc->scriptId();
+ uint32_t scopeId = scope.id();
+ for (BindingIter bi = scope.bindings(pc); bi; bi++) {
+ if (UsedNamePtr p = usedNames.lookup(bi.name())) {
+ bool closedOver;
+ p->value().noteBoundInScope(scriptId, scopeId, &closedOver);
+ if (closedOver) {
+ bi.setClosedOver();
+
+ if (isSyntaxParser && !pc->closedOverBindingsForLazy().append(bi.name())) {
+ ReportOutOfMemory(context);
+ return false;
+ }
+ }
+ }
+ }
+
+ // Append a nullptr to denote end-of-scope.
+ if (isSyntaxParser && !pc->closedOverBindingsForLazy().append(nullptr)) {
+ ReportOutOfMemory(context);
+ return false;
+ }
+
+ return true;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::checkStatementsEOF()
+{
+ // This is designed to be paired with parsing a statement list at the top
+ // level.
+ //
+ // The statementList() call breaks on TOK_RC, so make sure we've
+ // reached EOF here.
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return false;
+ if (tt != TOK_EOF) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "expression", TokenKindToDesc(tt));
+ return false;
+ }
+ return true;
+}
+
+template <typename Scope>
+static typename Scope::Data*
+NewEmptyBindingData(ExclusiveContext* cx, LifoAlloc& alloc, uint32_t numBindings)
+{
+ size_t allocSize = Scope::sizeOfData(numBindings);
+ typename Scope::Data* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize));
+ if (!bindings) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ PodZero(bindings);
+ return bindings;
+}
+
+template <>
+Maybe<GlobalScope::Data*>
+Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope)
+{
+ Vector<BindingName> funs(context);
+ Vector<BindingName> vars(context);
+ Vector<BindingName> lets(context);
+ Vector<BindingName> consts(context);
+
+ bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
+ for (BindingIter bi = scope.bindings(pc); bi; bi++) {
+ BindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver());
+ switch (bi.kind()) {
+ case BindingKind::Var:
+ if (bi.declarationKind() == DeclarationKind::BodyLevelFunction) {
+ if (!funs.append(binding))
+ return Nothing();
+ } else {
+ 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 global scope BindingKind");
+ }
+ }
+
+ GlobalScope::Data* bindings = nullptr;
+ uint32_t numBindings = funs.length() + vars.length() + lets.length() + consts.length();
+
+ if (numBindings > 0) {
+ bindings = NewEmptyBindingData<GlobalScope>(context, alloc, numBindings);
+ if (!bindings)
+ return Nothing();
+
+ // The ordering here is important. See comments in GlobalScope.
+ BindingName* start = bindings->names;
+ BindingName* cursor = start;
+
+ PodCopy(cursor, funs.begin(), funs.length());
+ cursor += funs.length();
+
+ bindings->varStart = cursor - start;
+ PodCopy(cursor, vars.begin(), vars.length());
+ cursor += vars.length();
+
+ bindings->letStart = cursor - start;
+ PodCopy(cursor, lets.begin(), lets.length());
+ cursor += lets.length();
+
+ bindings->constStart = cursor - start;
+ PodCopy(cursor, consts.begin(), consts.length());
+ bindings->length = numBindings;
+ }
+
+ return Some(bindings);
+}
+
+template <>
+Maybe<ModuleScope::Data*>
+Parser<FullParseHandler>::newModuleScopeData(ParseContext::Scope& scope)
+{
+ Vector<BindingName> imports(context);
+ Vector<BindingName> vars(context);
+ Vector<BindingName> lets(context);
+ Vector<BindingName> consts(context);
+
+ bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
+ for (BindingIter bi = scope.bindings(pc); bi; bi++) {
+ // Imports are indirect bindings and must not be given known slots.
+ BindingName 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::Data* bindings = nullptr;
+ uint32_t numBindings = imports.length() + vars.length() + lets.length() + consts.length();
+
+ if (numBindings > 0) {
+ bindings = NewEmptyBindingData<ModuleScope>(context, alloc, numBindings);
+ if (!bindings)
+ return Nothing();
+
+ // The ordering here is important. See comments in ModuleScope.
+ BindingName* start = bindings->names;
+ BindingName* cursor = start;
+
+ PodCopy(cursor, imports.begin(), imports.length());
+ cursor += imports.length();
+
+ bindings->varStart = cursor - start;
+ PodCopy(cursor, vars.begin(), vars.length());
+ cursor += vars.length();
+
+ bindings->letStart = cursor - start;
+ PodCopy(cursor, lets.begin(), lets.length());
+ cursor += lets.length();
+
+ bindings->constStart = cursor - start;
+ PodCopy(cursor, consts.begin(), consts.length());
+ bindings->length = numBindings;
+ }
+
+ return Some(bindings);
+}
+
+template <>
+Maybe<EvalScope::Data*>
+Parser<FullParseHandler>::newEvalScopeData(ParseContext::Scope& scope)
+{
+ Vector<BindingName> funs(context);
+ Vector<BindingName> vars(context);
+
+ for (BindingIter bi = scope.bindings(pc); bi; bi++) {
+ // Eval scopes only contain 'var' bindings. Make all bindings aliased
+ // for now.
+ MOZ_ASSERT(bi.kind() == BindingKind::Var);
+ BindingName binding(bi.name(), true);
+ if (bi.declarationKind() == DeclarationKind::BodyLevelFunction) {
+ if (!funs.append(binding))
+ return Nothing();
+ } else {
+ if (!vars.append(binding))
+ return Nothing();
+ }
+ }
+
+ EvalScope::Data* bindings = nullptr;
+ uint32_t numBindings = funs.length() + vars.length();
+
+ if (numBindings > 0) {
+ bindings = NewEmptyBindingData<EvalScope>(context, alloc, numBindings);
+ if (!bindings)
+ return Nothing();
+
+ BindingName* start = bindings->names;
+ BindingName* cursor = start;
+
+ // Keep track of what vars are functions. This is only used in BCE to omit
+ // superfluous DEFVARs.
+ PodCopy(cursor, funs.begin(), funs.length());
+ cursor += funs.length();
+
+ bindings->varStart = cursor - start;
+ PodCopy(cursor, vars.begin(), vars.length());
+ bindings->length = numBindings;
+ }
+
+ return Some(bindings);
+}
+
+template <>
+Maybe<FunctionScope::Data*>
+Parser<FullParseHandler>::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
+{
+ Vector<BindingName> positionalFormals(context);
+ Vector<BindingName> formals(context);
+ Vector<BindingName> vars(context);
+
+ bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
+ 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++) {
+ JSAtom* name = pc->positionalFormalParameterNames()[i];
+
+ BindingName 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 = allBindingsClosedOver ||
+ (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 (pc->positionalFormalParameterNames()[j] == name) {
+ closedOver = false;
+ break;
+ }
+ }
+ }
+
+ bindName = BindingName(name, closedOver);
+ }
+
+ if (!positionalFormals.append(bindName))
+ return Nothing();
+ }
+
+ for (BindingIter bi = scope.bindings(pc); bi; bi++) {
+ BindingName 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 internal bindings.
+ MOZ_ASSERT_IF(hasParameterExprs,
+ bi.name() == context->names().arguments ||
+ bi.name() == context->names().dotThis ||
+ bi.name() == context->names().dotGenerator);
+ if (!vars.append(binding))
+ return Nothing();
+ break;
+ default:
+ break;
+ }
+ }
+
+ FunctionScope::Data* bindings = nullptr;
+ uint32_t numBindings = positionalFormals.length() + formals.length() + vars.length();
+
+ if (numBindings > 0) {
+ bindings = NewEmptyBindingData<FunctionScope>(context, alloc, numBindings);
+ if (!bindings)
+ return Nothing();
+
+ // The ordering here is important. See comments in FunctionScope.
+ BindingName* start = bindings->names;
+ BindingName* cursor = start;
+
+ PodCopy(cursor, positionalFormals.begin(), positionalFormals.length());
+ cursor += positionalFormals.length();
+
+ bindings->nonPositionalFormalStart = cursor - start;
+ PodCopy(cursor, formals.begin(), formals.length());
+ cursor += formals.length();
+
+ bindings->varStart = cursor - start;
+ PodCopy(cursor, vars.begin(), vars.length());
+ bindings->length = numBindings;
+ }
+
+ return Some(bindings);
+}
+
+template <>
+Maybe<VarScope::Data*>
+Parser<FullParseHandler>::newVarScopeData(ParseContext::Scope& scope)
+{
+ Vector<BindingName> vars(context);
+
+ bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
+
+ for (BindingIter bi = scope.bindings(pc); bi; bi++) {
+ BindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver());
+ if (!vars.append(binding))
+ return Nothing();
+ }
+
+ VarScope::Data* bindings = nullptr;
+ uint32_t numBindings = vars.length();
+
+ if (numBindings > 0) {
+ bindings = NewEmptyBindingData<VarScope>(context, alloc, numBindings);
+ if (!bindings)
+ return Nothing();
+
+ // The ordering here is important. See comments in FunctionScope.
+ BindingName* start = bindings->names;
+ BindingName* cursor = start;
+
+ PodCopy(cursor, vars.begin(), vars.length());
+ bindings->length = numBindings;
+ }
+
+ return Some(bindings);
+}
+
+template <>
+Maybe<LexicalScope::Data*>
+Parser<FullParseHandler>::newLexicalScopeData(ParseContext::Scope& scope)
+{
+ Vector<BindingName> lets(context);
+ Vector<BindingName> consts(context);
+
+ // Unlike other scopes with bindings which are body-level, it is unknown
+ // if pc->sc()->allBindingsClosedOver() is correct at the time of
+ // finishing parsing a lexical scope.
+ //
+ // Instead, pc->sc()->allBindingsClosedOver() is checked in
+ // EmitterScope::enterLexical. Also see comment there.
+ for (BindingIter bi = scope.bindings(pc); bi; bi++) {
+ BindingName binding(bi.name(), 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;
+ default:
+ break;
+ }
+ }
+
+ LexicalScope::Data* bindings = nullptr;
+ uint32_t numBindings = lets.length() + consts.length();
+
+ if (numBindings > 0) {
+ bindings = NewEmptyBindingData<LexicalScope>(context, alloc, numBindings);
+ if (!bindings)
+ return Nothing();
+
+ // The ordering here is important. See comments in LexicalScope.
+ BindingName* cursor = bindings->names;
+ BindingName* start = cursor;
+
+ PodCopy(cursor, lets.begin(), lets.length());
+ cursor += lets.length();
+
+ bindings->constStart = cursor - start;
+ PodCopy(cursor, consts.begin(), consts.length());
+ bindings->length = numBindings;
+ }
+
+ return Some(bindings);
+}
+
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::finishLexicalScope(ParseContext::Scope& scope, Node body)
+{
+ if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
+ return null();
+ return body;
+}
+
+template <>
+ParseNode*
+Parser<FullParseHandler>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
+{
+ if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
+ return nullptr;
+ Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(scope);
+ if (!bindings)
+ return nullptr;
+ return handler.newLexicalScope(*bindings, body);
+}
+
+static bool
+IsArgumentsUsedInLegacyGenerator(ExclusiveContext* cx, Scope* scope)
+{
+ JSAtom* argumentsName = cx->names().arguments;
+ for (ScopeIter si(scope); si; si++) {
+ if (si.scope()->is<LexicalScope>()) {
+ // Using a shadowed lexical 'arguments' is okay.
+ for (::BindingIter bi(si.scope()); bi; bi++) {
+ if (bi.name() == argumentsName)
+ return false;
+ }
+ } else if (si.scope()->is<FunctionScope>()) {
+ // It's an error to use 'arguments' in a legacy generator expression.
+ JSScript* script = si.scope()->as<FunctionScope>().script();
+ return script->isGeneratorExp() && script->isLegacyGenerator();
+ }
+ }
+
+ return false;
+}
+
+template <>
+ParseNode*
+Parser<FullParseHandler>::evalBody(EvalSharedContext* evalsc)
+{
+ ParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
+ if (!evalpc.init())
+ return nullptr;
+
+ ParseContext::VarScope varScope(this);
+ if (!varScope.init(pc))
+ return nullptr;
+
+ // All evals have an implicit non-extensible lexical scope.
+ ParseContext::Scope lexicalScope(this);
+ if (!lexicalScope.init(pc))
+ return nullptr;
+
+ ParseNode* body = statementList(YieldIsName);
+ if (!body)
+ return nullptr;
+
+ if (!checkStatementsEOF())
+ return nullptr;
+
+ body = finishLexicalScope(lexicalScope, body);
+ if (!body)
+ return nullptr;
+
+ // It's an error to use 'arguments' in a legacy generator expression.
+ //
+ // If 'arguments' appears free (i.e. not a declared name) or if the
+ // declaration does not shadow the enclosing script's 'arguments'
+ // binding (i.e. not a lexical declaration), check the enclosing
+ // script.
+ if (hasUsedName(context->names().arguments)) {
+ if (IsArgumentsUsedInLegacyGenerator(context, pc->sc()->compilationEnclosingScope())) {
+ report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
+ return nullptr;
+ }
+ }
+
+#ifdef DEBUG
+ if (evalpc.superScopeNeedsHomeObject() && evalsc->compilationEnclosingScope()) {
+ // 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.
+ ScopeIter si(evalsc->compilationEnclosingScope());
+ for (; si; si++) {
+ if (si.kind() == ScopeKind::Function) {
+ JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
+ if (fun->isArrow())
+ continue;
+ MOZ_ASSERT(fun->allowSuperProperty());
+ MOZ_ASSERT(fun->nonLazyScript()->needsHomeObject());
+ break;
+ }
+ }
+ MOZ_ASSERT(!si.done(),
+ "Eval must have found an enclosing function box scope that allows super.property");
+ }
+#endif
+
+ if (!FoldConstants(context, &body, this))
+ return nullptr;
+
+ Maybe<EvalScope::Data*> bindings = newEvalScopeData(pc->varScope());
+ if (!bindings)
+ return nullptr;
+ evalsc->bindings = *bindings;
+
+ return body;
+}
+
+template <>
+ParseNode*
+Parser<FullParseHandler>::globalBody(GlobalSharedContext* globalsc)
+{
+ ParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
+ if (!globalpc.init())
+ return nullptr;
+
+ ParseContext::VarScope varScope(this);
+ if (!varScope.init(pc))
+ return nullptr;
+
+ ParseNode* body = statementList(YieldIsName);
+ if (!body)
+ return nullptr;
+
+ if (!checkStatementsEOF())
+ return nullptr;
+
+ if (!FoldConstants(context, &body, this))
+ return nullptr;
+
+ Maybe<GlobalScope::Data*> bindings = newGlobalScopeData(pc->varScope());
+ if (!bindings)
+ return nullptr;
+ globalsc->bindings = *bindings;
+
+ return body;
+}
+
+template <>
+ParseNode*
+Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
+{
+ MOZ_ASSERT(checkOptionsCalled);
+
+ ParseContext modulepc(this, modulesc, nullptr);
+ if (!modulepc.init())
+ return null();
+
+ ParseContext::VarScope varScope(this);
+ if (!varScope.init(pc))
+ return nullptr;
+
+ Node mn = handler.newModule();
+ if (!mn)
+ return null();
+
+ AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true);
+ ParseNode* pn = statementList(YieldIsKeyword);
+ if (!pn)
+ return null();
+
+ MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
+ mn->pn_body = pn;
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt != TOK_EOF) {
+ report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt));
+ return null();
+ }
+
+ if (!modulesc->builder.buildTables())
+ return null();
+
+ // Check exported local bindings exist and mark them as closed over.
+ for (auto entry : modulesc->builder.localExportEntries()) {
+ JSAtom* name = entry->localName();
+ MOZ_ASSERT(name);
+
+ DeclaredNamePtr p = modulepc.varScope().lookupDeclaredName(name);
+ if (!p) {
+ JSAutoByteString str;
+ if (!str.encodeLatin1(context, name))
+ return null();
+
+ JS_ReportErrorNumberLatin1(context->asJSContext(), GetErrorMessage, nullptr,
+ JSMSG_MISSING_EXPORT, str.ptr());
+ return null();
+ }
+
+ p->value()->setClosedOver();
+ }
+
+ if (!FoldConstants(context, &pn, this))
+ return null();
+
+ if (!propagateFreeNamesAndMarkClosedOverBindings(modulepc.varScope()))
+ return null();
+
+ Maybe<ModuleScope::Data*> bindings = newModuleScopeData(modulepc.varScope());
+ if (!bindings)
+ return nullptr;
+
+ modulesc->bindings = *bindings;
+ return mn;
+}
+
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::moduleBody(ModuleSharedContext* modulesc)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return SyntaxParseHandler::NodeFailure;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::hasUsedFunctionSpecialName(HandlePropertyName name)
+{
+ MOZ_ASSERT(name == context->names().arguments || name == context->names().dotThis);
+ return hasUsedName(name) || pc->functionBox()->bindingsAccessedDynamically();
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::declareFunctionThis()
+{
+ // 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;
+
+ // Derived class constructors emit JSOP_CHECKRETURN, which requires
+ // '.this' to be bound.
+ FunctionBox* funbox = pc->functionBox();
+ HandlePropertyName dotThis = context->names().dotThis;
+
+ bool declareThis;
+ if (handler.canSkipLazyClosedOverBindings())
+ declareThis = funbox->function()->lazyScript()->hasThisBinding();
+ else
+ declareThis = hasUsedFunctionSpecialName(dotThis) || funbox->isDerivedClassConstructor();
+
+ if (declareThis) {
+ ParseContext::Scope& funScope = pc->functionScope();
+ AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis);
+ MOZ_ASSERT(!p);
+ if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var))
+ return false;
+ funbox->setHasThisBinding();
+ }
+
+ return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newInternalDotName(HandlePropertyName name)
+{
+ Node nameNode = newName(name);
+ if (!nameNode)
+ return null();
+ if (!noteUsedName(name))
+ return null();
+ return nameNode;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newThisName()
+{
+ return newInternalDotName(context->names().dotThis);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newDotGeneratorName()
+{
+ return newInternalDotName(context->names().dotGenerator);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::declareDotGeneratorName()
+{
+ // The special '.generator' binding must be on the function scope, as
+ // generators expect to find it on the CallObject.
+ ParseContext::Scope& funScope = pc->functionScope();
+ HandlePropertyName dotGenerator = context->names().dotGenerator;
+ AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
+ if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var))
+ return false;
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::finishFunctionScopes()
+{
+ FunctionBox* funbox = pc->functionBox();
+
+ if (funbox->hasParameterExprs) {
+ if (!propagateFreeNamesAndMarkClosedOverBindings(pc->functionScope()))
+ return false;
+ }
+
+ if (funbox->function()->isNamedLambda()) {
+ if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope()))
+ return false;
+ }
+
+ return true;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::finishFunction()
+{
+ if (!finishFunctionScopes())
+ return false;
+
+ FunctionBox* funbox = pc->functionBox();
+ bool hasParameterExprs = funbox->hasParameterExprs;
+
+ if (hasParameterExprs) {
+ Maybe<VarScope::Data*> bindings = newVarScopeData(pc->varScope());
+ if (!bindings)
+ return false;
+ funbox->extraVarScopeBindings().set(*bindings);
+ }
+
+ {
+ Maybe<FunctionScope::Data*> bindings = newFunctionScopeData(pc->functionScope(),
+ hasParameterExprs);
+ if (!bindings)
+ return false;
+ funbox->functionScopeBindings().set(*bindings);
+ }
+
+ if (funbox->function()->isNamedLambda()) {
+ Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(pc->namedLambdaScope());
+ if (!bindings)
+ return false;
+ funbox->namedLambdaBindings().set(*bindings);
+ }
+
+ return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::finishFunction()
+{
+ // The LazyScript 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())
+ return false;
+
+ // There are too many bindings or inner functions to be saved into the
+ // LazyScript. Do a full parse.
+ if (pc->closedOverBindingsForLazy().length() >= LazyScript::NumClosedOverBindingsLimit ||
+ pc->innerFunctionsForLazy.length() >= LazyScript::NumInnerFunctionsLimit)
+ {
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+ }
+
+ FunctionBox* funbox = pc->functionBox();
+ RootedFunction fun(context, funbox->function());
+ LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(),
+ pc->innerFunctionsForLazy, versionNumber(),
+ funbox->bufStart, funbox->bufEnd,
+ funbox->startLine, funbox->startColumn);
+ if (!lazy)
+ return false;
+
+ // Flags that need to be copied into the JSScript when we do the full
+ // parse.
+ if (pc->sc()->strict())
+ lazy->setStrict();
+ lazy->setGeneratorKind(funbox->generatorKind());
+ lazy->setAsyncKind(funbox->asyncKind());
+ if (funbox->isLikelyConstructorWrapper())
+ lazy->setLikelyConstructorWrapper();
+ if (funbox->isDerivedClassConstructor())
+ lazy->setIsDerivedClassConstructor();
+ if (funbox->needsHomeObject())
+ lazy->setNeedsHomeObject();
+ if (funbox->declaredArguments)
+ lazy->setShouldDeclareArguments();
+ if (funbox->hasThisBinding())
+ lazy->setHasThisBinding();
+
+ // Flags that need to copied back into the parser when we do the full
+ // parse.
+ PropagateTransitiveParseFlags(funbox, lazy);
+
+ fun->initLazyScript(lazy);
+ return true;
+}
+
+static YieldHandling
+GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
+{
+ if (asyncKind == AsyncFunction)
+ return YieldIsName;
+ if (generatorKind == NotGenerator)
+ return YieldIsName;
+ return YieldIsKeyword;
+}
+
+template <>
+ParseNode*
+Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
+ HandleScope enclosingScope,
+ Handle<PropertyNameVector> formals,
+ GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind,
+ Directives inheritedDirectives,
+ Directives* newDirectives)
+{
+ MOZ_ASSERT(checkOptionsCalled);
+
+ Node fn = handler.newFunctionDefinition();
+ if (!fn)
+ return null();
+
+ ParseNode* argsbody = handler.newList(PNK_PARAMSBODY);
+ if (!argsbody)
+ return null();
+ fn->pn_body = argsbody;
+
+ FunctionBox* funbox = newFunctionBox(fn, fun, inheritedDirectives, generatorKind,
+ asyncKind, /* tryAnnexB = */ false);
+ if (!funbox)
+ return null();
+ funbox->initStandaloneFunction(enclosingScope);
+
+ ParseContext funpc(this, funbox, newDirectives);
+ if (!funpc.init())
+ return null();
+ funpc.setIsStandaloneFunctionBody();
+ funpc.functionScope().useAsVarScope(&funpc);
+
+ if (formals.length() >= ARGNO_LIMIT) {
+ report(ParseError, false, null(), JSMSG_TOO_MANY_FUN_ARGS);
+ return null();
+ }
+
+ bool duplicatedParam = false;
+ for (uint32_t i = 0; i < formals.length(); i++) {
+ if (!notePositionalFormalParameter(fn, formals[i], false, &duplicatedParam))
+ return null();
+ }
+ funbox->hasDuplicateParameters = duplicatedParam;
+
+ YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+ AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
+ ParseNode* pn = functionBody(InAllowed, yieldHandling, Statement, StatementListBody);
+ if (!pn)
+ return null();
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt != TOK_EOF) {
+ report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
+ "function body", TokenKindToDesc(tt));
+ return null();
+ }
+
+ if (!FoldConstants(context, &pn, this))
+ return null();
+
+ fn->pn_pos.end = pos().end;
+
+ MOZ_ASSERT(fn->pn_body->isKind(PNK_PARAMSBODY));
+ fn->pn_body->append(pn);
+
+ if (!finishFunction())
+ return null();
+
+ return fn;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::declareFunctionArgumentsObject()
+{
+ FunctionBox* funbox = pc->functionBox();
+ ParseContext::Scope& funScope = pc->functionScope();
+ ParseContext::Scope& varScope = pc->varScope();
+
+ bool hasExtraBodyVarScope = &funScope != &varScope;
+
+ // Time to implement the odd semantics of 'arguments'.
+ HandlePropertyName argumentsName = context->names().arguments;
+
+ bool tryDeclareArguments;
+ if (handler.canSkipLazyClosedOverBindings())
+ tryDeclareArguments = funbox->function()->lazyScript()->shouldDeclareArguments();
+ else
+ tryDeclareArguments = hasUsedFunctionSpecialName(argumentsName);
+
+ // ES 9.2.12 steps 19 and 20 say formal parameters, lexical bindings,
+ // and body-level functions named 'arguments' shadow the arguments
+ // object.
+ //
+ // So even if there wasn't a free use of 'arguments' but there is a var
+ // binding of 'arguments', we still might need the arguments object.
+ //
+ // If we have an extra var scope due to parameter expressions and the body
+ // declared 'var arguments', we still need to declare 'arguments' in the
+ // function scope.
+ DeclaredNamePtr p = varScope.lookupDeclaredName(argumentsName);
+ if (p && (p->value()->kind() == DeclarationKind::Var ||
+ p->value()->kind() == DeclarationKind::ForOfVar))
+ {
+ if (hasExtraBodyVarScope)
+ tryDeclareArguments = true;
+ else
+ funbox->usesArguments = true;
+ }
+
+ if (tryDeclareArguments) {
+ AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName);
+ if (!p) {
+ if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var))
+ return false;
+ funbox->declaredArguments = true;
+ funbox->usesArguments = true;
+ } else if (hasExtraBodyVarScope) {
+ // Formal parameters shadow the arguments object.
+ return true;
+ }
+ }
+
+ // Compute if we need an arguments object.
+ if (funbox->usesArguments) {
+ // There is an 'arguments' binding. Is the arguments object definitely
+ // needed?
+ //
+ // Also see the flags' comments in ContextFlags.
+ funbox->setArgumentsHasLocalBinding();
+
+ // Dynamic scope access destroys all hope of optimization.
+ if (pc->sc()->bindingsAccessedDynamically())
+ funbox->setDefinitelyNeedsArgsObj();
+
+ // If a script contains the debugger statement either directly or
+ // within an inner function, the arguments object should be created
+ // eagerly so the Debugger API may observe bindings.
+ if (pc->sc()->hasDebuggerStatement())
+ funbox->setDefinitelyNeedsArgsObj();
+ }
+
+ return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHandling,
+ FunctionSyntaxKind kind, FunctionBodyType type)
+{
+ MOZ_ASSERT(pc->isFunctionBox());
+ MOZ_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
+
+#ifdef DEBUG
+ uint32_t startYieldOffset = pc->lastYieldOffset;
+#endif
+
+ Node pn;
+ if (type == StatementListBody) {
+ bool inheritedStrict = pc->sc()->strict();
+ pn = statementList(yieldHandling);
+ if (!pn)
+ return null();
+
+ // 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 null();
+ }
+ }
+ } else {
+ MOZ_ASSERT(type == ExpressionBody);
+
+ // Async functions are implemented as star generators, and star
+ // generators are assumed to be statement lists, to prepend initial
+ // `yield`.
+ Node stmtList = null();
+ if (pc->isAsync()) {
+ stmtList = handler.newStatementList(pos());
+ if (!stmtList)
+ return null();
+ }
+
+ Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+ if (!kid)
+ return null();
+
+ pn = handler.newReturnStatement(kid, handler.getPosition(kid));
+ if (!pn)
+ return null();
+
+ if (pc->isAsync()) {
+ handler.addStatementToList(stmtList, pn);
+ pn = stmtList;
+ }
+ }
+
+ switch (pc->generatorKind()) {
+ case NotGenerator:
+ MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
+ break;
+
+ case LegacyGenerator:
+ MOZ_ASSERT(pc->lastYieldOffset != startYieldOffset);
+
+ // These should throw while parsing the yield expression.
+ MOZ_ASSERT(kind != Arrow);
+ MOZ_ASSERT(!IsGetterKind(kind));
+ MOZ_ASSERT(!IsSetterKind(kind));
+ MOZ_ASSERT(!IsConstructorKind(kind));
+ MOZ_ASSERT(kind != Method);
+ MOZ_ASSERT(type != ExpressionBody);
+ break;
+
+ case StarGenerator:
+ MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow);
+ MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
+ break;
+ }
+
+ if (pc->isGenerator()) {
+ MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
+ if (!declareDotGeneratorName())
+ return null();
+ Node generator = newDotGeneratorName();
+ if (!generator)
+ return null();
+ if (!handler.prependInitialYield(pn, generator))
+ return null();
+ }
+
+ // Declare the 'arguments' and 'this' 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 != Arrow) {
+ if (!declareFunctionArgumentsObject())
+ return null();
+ if (!declareFunctionThis())
+ return null();
+ }
+
+ return finishLexicalScope(pc->varScope(), pn);
+}
+
+template <typename ParseHandler>
+JSFunction*
+Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
+ GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+ HandleObject proto)
+{
+ MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
+
+ RootedFunction fun(context);
+
+ gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
+ JSFunction::Flags flags;
+#ifdef DEBUG
+ bool isGlobalSelfHostedBuiltin = false;
+#endif
+ switch (kind) {
+ case Expression:
+ flags = (generatorKind == NotGenerator
+ ? JSFunction::INTERPRETED_LAMBDA
+ : JSFunction::INTERPRETED_LAMBDA_GENERATOR);
+ break;
+ case Arrow:
+ flags = JSFunction::INTERPRETED_LAMBDA_ARROW;
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+ break;
+ case Method:
+ MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
+ flags = (generatorKind == NotGenerator
+ ? JSFunction::INTERPRETED_METHOD
+ : JSFunction::INTERPRETED_METHOD_GENERATOR);
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+ break;
+ case ClassConstructor:
+ case DerivedClassConstructor:
+ flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR;
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+ break;
+ case Getter:
+ case GetterNoExpressionClosure:
+ flags = JSFunction::INTERPRETED_GETTER;
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+ break;
+ case Setter:
+ case SetterNoExpressionClosure:
+ flags = JSFunction::INTERPRETED_SETTER;
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+ break;
+ default:
+ MOZ_ASSERT(kind == Statement);
+#ifdef DEBUG
+ if (options().selfHostingMode && !pc->isFunctionBox()) {
+ isGlobalSelfHostedBuiltin = true;
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+ }
+#endif
+ flags = (generatorKind == NotGenerator
+ ? JSFunction::INTERPRETED_NORMAL
+ : JSFunction::INTERPRETED_GENERATOR);
+ }
+
+ // We store the async wrapper in a slot for later access.
+ if (asyncKind == AsyncFunction)
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+
+ fun = NewFunctionWithProto(context, nullptr, 0, flags, nullptr, atom, proto,
+ allocKind, TenuredObject);
+ if (!fun)
+ return nullptr;
+ if (options().selfHostingMode) {
+ fun->setIsSelfHostedBuiltin();
+#ifdef DEBUG
+ if (isGlobalSelfHostedBuiltin)
+ fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(false));
+#endif
+ }
+ return fun;
+}
+
+/*
+ * WARNING: Do not call this function directly.
+ * Call either MatchOrInsertSemicolonAfterExpression or
+ * MatchOrInsertSemicolonAfterNonExpression instead, depending on context.
+ */
+static bool
+MatchOrInsertSemicolonHelper(TokenStream& ts, TokenStream::Modifier modifier)
+{
+ TokenKind tt = TOK_EOF;
+ if (!ts.peekTokenSameLine(&tt, modifier))
+ return false;
+ if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
+ /* Advance the scanner for proper error location reporting. */
+ ts.consumeKnownToken(tt, modifier);
+ ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
+ return false;
+ }
+ bool matched;
+ if (!ts.matchToken(&matched, TOK_SEMI, modifier))
+ return false;
+ if (!matched && modifier == TokenStream::None)
+ ts.addModifierException(TokenStream::OperandIsNone);
+ return true;
+}
+
+static bool
+MatchOrInsertSemicolonAfterExpression(TokenStream& ts)
+{
+ return MatchOrInsertSemicolonHelper(ts, TokenStream::None);
+}
+
+static bool
+MatchOrInsertSemicolonAfterNonExpression(TokenStream& ts)
+{
+ return MatchOrInsertSemicolonHelper(ts, TokenStream::Operand);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::leaveInnerFunction(ParseContext* outerpc)
+{
+ MOZ_ASSERT(pc != outerpc);
+
+ // 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 here unconditionally; the vector is only used
+ // if the Parser using outerpc is a syntax parsing. See
+ // Parser<SyntaxParseHandler>::finishFunction.
+ if (!outerpc->innerFunctionsForLazy.append(pc->functionBox()->function()))
+ return false;
+
+ PropagateTransitiveParseFlags(pc->functionBox(), outerpc->sc());
+
+ return true;
+}
+
+template <typename ParseHandler>
+JSAtom*
+Parser<ParseHandler>::prefixAccessorName(PropertyType propType, HandleAtom propAtom)
+{
+ RootedAtom prefix(context);
+ if (propType == PropertyType::Setter || propType == PropertyType::SetterNoExpressionClosure) {
+ prefix = context->names().setPrefix;
+ } else {
+ MOZ_ASSERT(propType == PropertyType::Getter || propType == PropertyType::GetterNoExpressionClosure);
+ prefix = context->names().getPrefix;
+ }
+
+ RootedString str(context, ConcatStrings<CanGC>(context, prefix, propAtom));
+ if (!str)
+ return nullptr;
+
+ return AtomizeString(context, str);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
+ Node funcpn)
+{
+ FunctionBox* funbox = pc->functionBox();
+
+ bool parenFreeArrow = false;
+ // Modifier for the following tokens.
+ // TokenStream::None for the following cases:
+ // async a => 1
+ // ^
+ //
+ // (a) => 1
+ // ^
+ //
+ // async (a) => 1
+ // ^
+ //
+ // function f(a) {}
+ // ^
+ //
+ // TokenStream::Operand for the following case:
+ // a => 1
+ // ^
+ TokenStream::Modifier firstTokenModifier = TokenStream::None;
+
+ // Modifier for the the first token in each argument.
+ // can be changed to TokenStream::None for the following case:
+ // async a => 1
+ // ^
+ TokenStream::Modifier argModifier = TokenStream::Operand;
+ if (kind == Arrow) {
+ TokenKind tt;
+ // In async function, the first token after `async` is already gotten
+ // with TokenStream::None.
+ // In sync function, the first token is already gotten with
+ // TokenStream::Operand.
+ firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
+ if (!tokenStream.peekToken(&tt, firstTokenModifier))
+ return false;
+ if (tt == TOK_NAME || tt == TOK_YIELD) {
+ parenFreeArrow = true;
+ argModifier = firstTokenModifier;
+ }
+ }
+ if (!parenFreeArrow) {
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, firstTokenModifier))
+ return false;
+ if (tt != TOK_LP) {
+ report(ParseError, false, null(),
+ kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
+ return false;
+ }
+
+ // Record the start of function source (for FunctionToString). If we
+ // are parenFreeArrow, we will set this below, after consuming the NAME.
+ funbox->setStart(tokenStream);
+ }
+
+ Node argsbody = handler.newList(PNK_PARAMSBODY);
+ if (!argsbody)
+ return false;
+ handler.setFunctionFormalParametersAndBody(funcpn, argsbody);
+
+ bool hasArguments = false;
+ if (parenFreeArrow) {
+ hasArguments = true;
+ } else {
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
+ return false;
+ if (!matched)
+ hasArguments = true;
+ }
+ if (hasArguments) {
+ bool hasRest = false;
+ bool hasDefault = false;
+ bool duplicatedParam = false;
+ bool disallowDuplicateParams = kind == Arrow || kind == Method || kind == ClassConstructor;
+ AtomVector& positionalFormals = pc->positionalFormalParameterNames();
+
+ if (IsGetterKind(kind)) {
+ report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
+ return false;
+ }
+
+ while (true) {
+ if (hasRest) {
+ report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
+ return false;
+ }
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, argModifier))
+ return false;
+ argModifier = TokenStream::Operand;
+ MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD);
+
+ if (tt == TOK_TRIPLEDOT) {
+ if (IsSetterKind(kind)) {
+ report(ParseError, false, null(),
+ JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
+ return false;
+ }
+
+ disallowDuplicateParams = true;
+ if (duplicatedParam) {
+ // Has duplicated args before the rest parameter.
+ report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ return false;
+ }
+
+ hasRest = true;
+ funbox->function()->setHasRest();
+
+ if (!tokenStream.getToken(&tt))
+ return false;
+
+ if (tt != TOK_NAME && tt != TOK_YIELD && tt != TOK_LB && tt != TOK_LC) {
+ report(ParseError, false, null(), JSMSG_NO_REST_NAME);
+ return false;
+ }
+ }
+
+ switch (tt) {
+ case TOK_LB:
+ case TOK_LC: {
+ disallowDuplicateParams = true;
+ if (duplicatedParam) {
+ // Has duplicated args before the destructuring parameter.
+ report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
+ return false;
+ }
+
+ funbox->hasDestructuringArgs = true;
+
+ Node destruct = destructuringDeclarationWithoutYieldOrAwait(
+ DeclarationKind::FormalParameter,
+ yieldHandling, tt);
+ if (!destruct)
+ return false;
+
+ if (!noteDestructuredPositionalFormalParameter(funcpn, destruct))
+ return false;
+
+ break;
+ }
+
+ case TOK_NAME:
+ case TOK_YIELD: {
+ if (parenFreeArrow)
+ funbox->setStart(tokenStream);
+
+ if (funbox->isAsync() && tokenStream.currentName() == context->names().await) {
+ // `await` is already gotten as TOK_NAME for the following
+ // case:
+ //
+ // async await => 1
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, "await");
+ return false;
+ }
+
+ RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+ if (!name)
+ return false;
+
+ if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
+ &duplicatedParam))
+ {
+ return false;
+ }
+ if (duplicatedParam)
+ funbox->hasDuplicateParameters = true;
+
+ break;
+ }
+
+ default:
+ report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
+ return false;
+ }
+
+ if (positionalFormals.length() >= ARGNO_LIMIT) {
+ report(ParseError, false, null(), JSMSG_TOO_MANY_FUN_ARGS);
+ return false;
+ }
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
+ return false;
+ if (matched) {
+ // A default argument without parentheses would look like:
+ // a = expr => body, but both operators are right-associative, so
+ // that would have been parsed as a = (expr => body) instead.
+ // Therefore it's impossible to get here with parenFreeArrow.
+ MOZ_ASSERT(!parenFreeArrow);
+
+ if (hasRest) {
+ report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
+ return false;
+ }
+ disallowDuplicateParams = true;
+ if (duplicatedParam) {
+ report(ParseError, false, null(), 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->length = positionalFormals.length() - 1;
+ }
+ funbox->hasParameterExprs = true;
+
+ Node def_expr = assignExprWithoutYieldOrAwait(yieldHandling);
+ if (!def_expr)
+ return false;
+ if (!handler.setLastFunctionFormalParameterDefault(funcpn, def_expr))
+ return false;
+ }
+
+ if (parenFreeArrow || IsSetterKind(kind))
+ break;
+
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return false;
+ if (!matched)
+ break;
+
+ if (!hasRest) {
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt == TOK_RP) {
+ tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ break;
+ }
+ }
+ }
+
+ if (!parenFreeArrow) {
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return false;
+ if (tt != TOK_RP) {
+ if (IsSetterKind(kind)) {
+ report(ParseError, false, null(),
+ JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
+ return false;
+ }
+
+ report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
+ return false;
+ }
+ }
+
+ if (!hasDefault)
+ funbox->length = positionalFormals.length() - hasRest;
+
+ if (funbox->hasParameterExprs && funbox->hasDirectEval())
+ funbox->hasDirectEvalInParameterExpr = true;
+
+ funbox->function()->setArgCount(positionalFormals.length());
+ } else if (IsSetterKind(kind)) {
+ report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
+ return false;
+ }
+
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind,
+ GeneratorKind generatorKind, bool* tryAnnexB)
+{
+ if (kind == Statement) {
+ TokenPos pos = handler.getPosition(pn);
+ RootedPropertyName funName(context, funAtom->asPropertyName());
+
+ // In sloppy mode, Annex B.3.2 allows labelled function
+ // declarations. Otherwise it is 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())) {
+ reportWithOffset(ParseError, false, pos.begin, JSMSG_SLOPPY_FUNCTION_LABEL);
+ return false;
+ }
+ }
+
+ if (declaredInStmt) {
+ MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
+ MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
+
+ if (!pc->sc()->strict() && generatorKind == NotGenerator) {
+ // 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.
+
+ if (!tryDeclareVarForAnnexBLexicalFunction(funName, tryAnnexB))
+ return false;
+ }
+
+ if (!noteDeclaredName(funName, DeclarationKind::LexicalFunction, pos))
+ return false;
+ } else {
+ if (!noteDeclaredName(funName, DeclarationKind::BodyLevelFunction, pos))
+ return false;
+
+ // Body-level functions in modules are always closed over.
+ if (pc->atModuleLevel())
+ pc->varScope().lookupDeclaredName(funName)->value()->setClosedOver();
+ }
+ } else {
+ // A function expression does not introduce any binding.
+ handler.setOp(pn, kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
+ }
+
+ return true;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKind kind,
+ 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.
+
+ RootedFunction fun(context, handler.nextLazyInnerFunction());
+ MOZ_ASSERT(!fun->isLegacyGenerator());
+ FunctionBox* funbox = newFunctionBox(pn, fun, Directives(/* strict = */ false),
+ fun->generatorKind(), fun->asyncKind(), tryAnnexB);
+ if (!funbox)
+ return false;
+
+ LazyScript* lazy = fun->lazyScript();
+ if (lazy->needsHomeObject())
+ funbox->setNeedsHomeObject();
+
+ PropagateTransitiveParseFlags(lazy, pc->sc());
+
+ // The position passed to tokenStream.advance() is an offset of the sort
+ // returned by userbuf.offset() and expected by userbuf.rawCharPtrAt(),
+ // while LazyScript::{begin,end} offsets are relative to the outermost
+ // script source.
+ Rooted<LazyScript*> lazyOuter(context, handler.lazyOuterFunction());
+ uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
+ if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase))
+ return false;
+
+ if (kind == Statement && fun->isExprBody()) {
+ if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return false;
+ }
+
+ return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind,
+ bool tryAnnexB)
+{
+ MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
+ TokenKind* ttp)
+{
+ Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!pn)
+ return false;
+ handler.addList(nodeList, pn);
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return false;
+ if (tt != TOK_RC) {
+ report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
+ return false;
+ }
+
+ return tokenStream.getToken(ttp, TokenStream::TemplateTail);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt)
+{
+ Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
+ if (!callSiteObjNode)
+ return false;
+ handler.addList(nodeList, callSiteObjNode);
+
+ while (true) {
+ if (!appendToCallSiteObj(callSiteObjNode))
+ return false;
+ if (tt != TOK_TEMPLATE_HEAD)
+ break;
+
+ if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
+ return false;
+ }
+ handler.setEndPosition(nodeList, callSiteObjNode);
+ return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
+{
+ Node pn = noSubstitutionTemplate();
+ if (!pn)
+ return null();
+
+ Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
+ if (!nodeList)
+ return null();
+
+ TokenKind tt;
+ do {
+ if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
+ return null();
+
+ pn = noSubstitutionTemplate();
+ if (!pn)
+ return null();
+
+ handler.addList(nodeList, pn);
+ } while (tt == TOK_TEMPLATE_HEAD);
+ return nodeList;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionDefinition(InHandling inHandling, YieldHandling yieldHandling,
+ HandleAtom funName, FunctionSyntaxKind kind,
+ GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+ InvokedPrediction invoked)
+{
+ MOZ_ASSERT_IF(kind == Statement, funName);
+ MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator);
+
+ Node pn = handler.newFunctionDefinition();
+ if (!pn)
+ return null();
+
+ if (invoked)
+ pn = handler.setLikelyIIFE(pn);
+
+ // Note the declared name and check for early errors.
+ bool tryAnnexB = false;
+ if (!checkFunctionDefinition(funName, pn, kind, generatorKind, &tryAnnexB))
+ return null();
+
+ // When fully parsing a LazyScript, 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.canSkipLazyInnerFunctions()) {
+ if (!skipLazyInnerFunction(pn, kind, tryAnnexB))
+ return null();
+ return pn;
+ }
+
+ RootedObject proto(context);
+ if (generatorKind == StarGenerator) {
+ // If we are off the main thread, the generator meta-objects have
+ // already been created by js::StartOffThreadParseScript, so cx will not
+ // be necessary.
+ JSContext* cx = context->maybeJSContext();
+ proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
+ if (!proto)
+ return null();
+ }
+ RootedFunction fun(context, newFunction(funName, kind, generatorKind, asyncKind, proto));
+ if (!fun)
+ return null();
+
+ // 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;
+
+ TokenStream::Position start(keepAtoms);
+ tokenStream.tell(&start);
+
+ // 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(pn, fun, inHandling, yieldHandling, kind, generatorKind,
+ asyncKind, tryAnnexB, directives, &newDirectives))
+ {
+ break;
+ }
+
+ // Return on error.
+ if (tokenStream.hadError() || directives == newDirectives)
+ return null();
+
+ // 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;
+
+ tokenStream.seek(start);
+
+ // functionFormalParametersAndBody may have already set pn->pn_body before failing.
+ handler.setFunctionFormalParametersAndBody(pn, null());
+ }
+
+ return pn;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
+ 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 (pn->isLikelyIIFE() && generatorKind == NotGenerator)
+ break;
+
+ Parser<SyntaxParseHandler>* parser = handler.syntaxParser;
+ if (!parser)
+ break;
+
+ UsedNameTracker::RewindToken token = usedNames.getRewindToken();
+
+ // Move the syntax parser to the current position in the stream.
+ TokenStream::Position position(keepAtoms);
+ tokenStream.tell(&position);
+ if (!parser->tokenStream.seek(position, tokenStream))
+ 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(pn, fun, inheritedDirectives, generatorKind,
+ asyncKind, tryAnnexB);
+ if (!funbox)
+ return false;
+ funbox->initWithEnclosingParseContext(pc, kind);
+
+ if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, inHandling,
+ yieldHandling, kind, inheritedDirectives, newDirectives))
+ {
+ if (parser->hadAbortedSyntaxParse()) {
+ // Try again with a full parse. UsedNameTracker needs to be
+ // rewound to just before we tried the syntax parse for
+ // correctness.
+ parser->clearAbortedSyntaxParse();
+ usedNames.rewind(token);
+ MOZ_ASSERT_IF(parser->context->isJSContext(),
+ !parser->context->asJSContext()->isExceptionPending());
+ break;
+ }
+ return false;
+ }
+
+ // Advance this parser over tokens processed by the syntax parser.
+ parser->tokenStream.tell(&position);
+ if (!tokenStream.seek(position, parser->tokenStream))
+ return false;
+
+ // Update the end position of the parse node.
+ pn->pn_pos.end = tokenStream.currentToken().pos.end;
+ return true;
+ } while (false);
+
+ // We failed to do a syntax parse above, so do the full parse.
+ return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind,
+ tryAnnexB, inheritedDirectives, newDirectives);
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
+ 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.
+ return innerFunction(pn, pc, fun, inHandling, yieldHandling, kind, generatorKind, asyncKind,
+ tryAnnexB, inheritedDirectives, newDirectives);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
+ InHandling inHandling, YieldHandling yieldHandling,
+ FunctionSyntaxKind kind, 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 ParseContext from the full parser
+ // instead of the current top of the stack of the syntax parser.
+
+ // Push a new ParseContext.
+ ParseContext funpc(this, funbox, newDirectives);
+ if (!funpc.init())
+ return false;
+
+ if (!functionFormalParametersAndBody(inHandling, yieldHandling, pn, kind))
+ return false;
+
+ return leaveInnerFunction(outerpc);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
+ 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 ParseContext from the full parser
+ // instead of the current top of the stack of the syntax parser.
+
+ FunctionBox* funbox = newFunctionBox(pn, fun, inheritedDirectives, generatorKind,
+ asyncKind, tryAnnexB);
+ if (!funbox)
+ return false;
+ funbox->initWithEnclosingParseContext(outerpc, kind);
+
+ return innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, inheritedDirectives,
+ newDirectives);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
+{
+ Node cookedNode = noSubstitutionTemplate();
+ if (!cookedNode)
+ return false;
+
+ JSAtom* atom = tokenStream.getRawTemplateStringAtom();
+ if (!atom)
+ return false;
+ Node rawNode = handler.newTemplateStringLiteral(atom, pos());
+ if (!rawNode)
+ return false;
+
+ handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
+ return true;
+}
+
+template <>
+ParseNode*
+Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict,
+ GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind)
+{
+ MOZ_ASSERT(checkOptionsCalled);
+
+ Node pn = handler.newFunctionDefinition();
+ if (!pn)
+ return null();
+
+ Directives directives(strict);
+ FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, asyncKind,
+ /* tryAnnexB = */ false);
+ if (!funbox)
+ return null();
+ funbox->initFromLazyFunction();
+
+ Directives newDirectives = directives;
+ ParseContext funpc(this, funbox, &newDirectives);
+ if (!funpc.init())
+ return null();
+
+ // Our tokenStream has no current token, so pn's position is garbage.
+ // Substitute the position of the first token in our source. If the function
+ // is a not-async arrow, use TokenStream::Operand to keep
+ // verifyConsistentModifier from complaining (we will use
+ // TokenStream::Operand in functionArguments).
+ TokenStream::Modifier modifier = (fun->isArrow() && asyncKind == SyncFunction)
+ ? TokenStream::Operand : TokenStream::None;
+ if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier))
+ return null();
+
+ YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+ FunctionSyntaxKind syntaxKind = Statement;
+ if (fun->isClassConstructor())
+ syntaxKind = ClassConstructor;
+ else if (fun->isMethod())
+ syntaxKind = Method;
+ else if (fun->isGetter())
+ syntaxKind = Getter;
+ else if (fun->isSetter())
+ syntaxKind = Setter;
+ else if (fun->isArrow())
+ syntaxKind = Arrow;
+
+ if (!functionFormalParametersAndBody(InAllowed, yieldHandling, pn, syntaxKind)) {
+ MOZ_ASSERT(directives == newDirectives);
+ return null();
+ }
+
+ if (!FoldConstants(context, &pn, this))
+ return null();
+
+ return pn;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
+ YieldHandling yieldHandling,
+ Node pn, FunctionSyntaxKind kind)
+{
+ // 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();
+ RootedFunction fun(context, funbox->function());
+
+ AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync());
+ if (!functionArguments(yieldHandling, kind, pn))
+ 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 == Arrow) {
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_ARROW))
+ return false;
+ if (!matched) {
+ report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
+ return false;
+ }
+ }
+
+ // Parse the function body.
+ FunctionBodyType bodyType = StatementListBody;
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return false;
+ if (tt != TOK_LC) {
+ if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
+ kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
+ IsConstructorKind(kind)) {
+ report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
+ return false;
+ }
+
+ if (kind != Arrow) {
+#if JS_HAS_EXPR_CLOSURES
+ addTelemetry(JSCompartment::DeprecatedExpressionClosure);
+ if (!warnOnceAboutExprClosure())
+ return false;
+#else
+ report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
+ return false;
+#endif
+ }
+
+ tokenStream.ungetToken();
+ bodyType = ExpressionBody;
+#if JS_HAS_EXPR_CLOSURES
+ fun->setIsExprBody();
+#endif
+ }
+
+ // 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.
+ YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind());
+ Node body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
+ if (!body)
+ return false;
+
+ if ((kind != Method && !IsConstructorKind(kind)) && fun->name()) {
+ RootedPropertyName propertyName(context, fun->name()->asPropertyName());
+ if (!checkStrictBinding(propertyName, handler.getPosition(pn)))
+ return false;
+ }
+
+ if (bodyType == StatementListBody) {
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand))
+ return false;
+ if (!matched) {
+ report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY);
+ return false;
+ }
+ funbox->bufEnd = pos().begin + 1;
+ } else {
+#if !JS_HAS_EXPR_CLOSURES
+ MOZ_ASSERT(kind == Arrow);
+#endif
+ if (tokenStream.hadError())
+ return false;
+ funbox->bufEnd = pos().end;
+ if (kind == Statement && !MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return false;
+ }
+
+ if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
+ funbox->setNeedsHomeObject();
+
+ if (!finishFunction())
+ return false;
+
+ handler.setEndPosition(body, pos().begin);
+ handler.setEndPosition(pn, pos().end);
+ handler.setFunctionBody(pn, body);
+
+ return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
+ FunctionAsyncKind asyncKind)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+
+ // Annex B.3.4 says we can parse function declarations unbraced under if
+ // or else as if it were braced. That is, |if (x) function f() {}| is
+ // parsed as |if (x) { function f() {} }|.
+ Maybe<ParseContext::Statement> synthesizedStmtForAnnexB;
+ Maybe<ParseContext::Scope> synthesizedScopeForAnnexB;
+ if (!pc->sc()->strict()) {
+ ParseContext::Statement* stmt = pc->innermostStatement();
+ if (stmt && stmt->kind() == StatementKind::If) {
+ synthesizedStmtForAnnexB.emplace(pc, StatementKind::Block);
+ synthesizedScopeForAnnexB.emplace(this);
+ if (!synthesizedScopeForAnnexB->init(pc))
+ return null();
+ }
+ }
+
+ RootedPropertyName name(context);
+ GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ if (tt == TOK_MUL) {
+ if (asyncKind != SyncFunction) {
+ report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
+ return null();
+ }
+ generatorKind = StarGenerator;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ }
+
+ if (tt == TOK_NAME || tt == TOK_YIELD) {
+ name = bindingIdentifier(yieldHandling);
+ if (!name)
+ return null();
+ } else if (defaultHandling == AllowDefaultName) {
+ name = context->names().starDefaultStar;
+ tokenStream.ungetToken();
+ } else {
+ /* Unnamed function expressions are forbidden in statement context. */
+ report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
+ return null();
+ }
+
+ YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
+ Node fun = functionDefinition(InAllowed, newYieldHandling, name, Statement, generatorKind,
+ asyncKind, PredictUninvoked);
+ if (!fun)
+ return null();
+
+ if (synthesizedStmtForAnnexB) {
+ Node synthesizedStmtList = handler.newStatementList(handler.getPosition(fun));
+ if (!synthesizedStmtList)
+ return null();
+ handler.addStatementToList(synthesizedStmtList, fun);
+ return finishLexicalScope(*synthesizedScopeForAnnexB, synthesizedStmtList);
+ }
+
+ return fun;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+
+ AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
+ GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ if (tt == TOK_MUL) {
+ if (asyncKind != SyncFunction) {
+ report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
+ return null();
+ }
+ generatorKind = StarGenerator;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ }
+
+ YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+
+ RootedPropertyName name(context);
+ if (tt == TOK_NAME || tt == TOK_YIELD) {
+ name = bindingIdentifier(yieldHandling);
+ if (!name)
+ return null();
+ } else {
+ tokenStream.ungetToken();
+ }
+
+ return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind,
+ asyncKind, invoked);
+}
+
+/*
+ * Return true if this node, known to be an unparenthesized string literal,
+ * could be the string of a directive in a Directive Prologue. Directive
+ * strings never contain escape sequences or line continuations.
+ * isEscapeFreeStringLiteral, below, checks whether the node itself could be
+ * a directive.
+ */
+static inline bool
+IsEscapeFreeStringLiteral(const TokenPos& pos, JSAtom* str)
+{
+ /*
+ * If the string's length in the source code is its length as a value,
+ * accounting for the quotes, then it must not contain any escape
+ * sequences or line continuations.
+ */
+ return pos.begin + str->length() + 2 == pos.end;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkUnescapedName()
+{
+ if (!tokenStream.currentToken().nameContainsEscape())
+ return true;
+
+ report(ParseError, false, null(), JSMSG_ESCAPED_KEYWORD);
+ return false;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::asmJS(Node 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.
+ JS_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::asmJS(Node list)
+{
+ // Disable syntax parsing in anything nested inside the asm.js module.
+ handler.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(context, *this, list, &validated))
+ return false;
+ if (!validated) {
+ pc->newDirectives->setAsmJS();
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * 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
+ * |pn_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 <typename ParseHandler>
+bool
+Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
+{
+ TokenPos directivePos;
+ JSAtom* directive = handler.isStringExprStatement(pn, &directivePos);
+
+ *cont = !!directive;
+ if (!*cont)
+ return true;
+
+ if (IsEscapeFreeStringLiteral(directivePos, directive)) {
+ // Mark this statement as being a possibly legitimate part of a
+ // directive prologue, so the bytecode emitter won't warn about it being
+ // useless code. (We mustn't just omit the statement entirely yet, as it
+ // could be producing the value of an eval or JSScript execution.)
+ //
+ // Note that even if the string isn't one we recognize as a directive,
+ // the emitter still shouldn't flag it as useless, as it could become a
+ // directive in the future. We don't want to interfere with people
+ // taking advantage of directive-prologue-enabled features that appear
+ // in other browsers first.
+ handler.setPrologue(pn);
+
+ if (directive == context->names().useStrict) {
+ // 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";
+ reportWithOffset(ParseError, false, 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()) {
+ // We keep track of the one possible strict violation that could
+ // occur in the directive prologue -- octal escapes -- and
+ // complain now.
+ if (tokenStream.sawOctalEscape()) {
+ report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
+ return false;
+ }
+ pc->sc()->strictScript = true;
+ }
+ } else if (directive == context->names().useAsm) {
+ if (pc->isFunctionBox())
+ return asmJS(list);
+ return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
+ }
+ }
+ return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::statementList(YieldHandling yieldHandling)
+{
+ JS_CHECK_RECURSION(context, return null());
+
+ Node pn = handler.newStatementList(pos());
+ if (!pn)
+ return null();
+
+ bool canHaveDirectives = pc->atBodyLevel();
+ if (canHaveDirectives)
+ tokenStream.clearSawOctalEscape();
+ bool afterReturn = false;
+ bool warnedAboutStatementsAfterReturn = false;
+ uint32_t statementBegin = 0;
+ for (;;) {
+ TokenKind tt = TOK_EOF;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
+ if (tokenStream.isEOF())
+ isUnexpectedEOF_ = true;
+ return null();
+ }
+ if (tt == TOK_EOF || tt == TOK_RC)
+ break;
+ if (afterReturn) {
+ TokenPos pos(0, 0);
+ if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
+ return null();
+ statementBegin = pos.begin;
+ }
+ Node next = statementListItem(yieldHandling, canHaveDirectives);
+ if (!next) {
+ if (tokenStream.isEOF())
+ isUnexpectedEOF_ = true;
+ return null();
+ }
+ if (!warnedAboutStatementsAfterReturn) {
+ if (afterReturn) {
+ if (!handler.isStatementPermittedAfterReturnStatement(next)) {
+ if (!reportWithOffset(ParseWarning, false, statementBegin,
+ JSMSG_STMT_AFTER_RETURN))
+ {
+ return null();
+ }
+ warnedAboutStatementsAfterReturn = true;
+ }
+ } else if (handler.isReturnStatement(next)) {
+ afterReturn = true;
+ }
+ }
+
+ if (canHaveDirectives) {
+ if (!maybeParseDirective(pn, next, &canHaveDirectives))
+ return null();
+ }
+
+ handler.addStatementToList(pn, next);
+ }
+
+ return pn;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::condition(InHandling inHandling, YieldHandling yieldHandling)
+{
+ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
+ Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
+ if (!pn)
+ return null();
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
+
+ /* Check for (a = b) and warn about possible (a == b) mistype. */
+ if (handler.isUnparenthesizedAssignment(pn)) {
+ if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+ return null();
+ }
+ return pn;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label)
+{
+ TokenKind tt = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+ return false;
+
+ if (tt == TOK_NAME || tt == TOK_YIELD) {
+ tokenStream.consumeKnownToken(tt, TokenStream::Operand);
+
+ label.set(labelIdentifier(yieldHandling));
+ if (!label)
+ return false;
+ } else {
+ label.set(nullptr);
+ }
+ return true;
+}
+
+template <typename ParseHandler>
+Parser<ParseHandler>::PossibleError::PossibleError(Parser<ParseHandler>& parser)
+ : parser_(parser)
+{}
+
+template <typename ParseHandler>
+typename Parser<ParseHandler>::PossibleError::Error&
+Parser<ParseHandler>::PossibleError::error(ErrorKind kind)
+{
+ if (kind == ErrorKind::Expression)
+ return exprError_;
+ MOZ_ASSERT(kind == ErrorKind::Destructuring);
+ return destructuringError_;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setResolved(ErrorKind kind)
+{
+ error(kind).state_ = ErrorState::None;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::hasError(ErrorKind kind)
+{
+ return error(kind).state_ == ErrorState::Pending;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, Node pn, 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_ = (pn ? parser_.handler.getPosition(pn) : parser_.pos()).begin;
+ err.errorNumber_ = errorNumber;
+ err.state_ = ErrorState::Pending;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setPendingDestructuringError(Node pn, unsigned errorNumber)
+{
+ setPending(ErrorKind::Destructuring, pn, errorNumber);
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setPendingExpressionError(Node pn, unsigned errorNumber)
+{
+ setPending(ErrorKind::Expression, pn, errorNumber);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::checkForError(ErrorKind kind)
+{
+ if (!hasError(kind))
+ return true;
+
+ Error& err = error(kind);
+ parser_.reportWithOffset(ParseError, false, err.offset_, err.errorNumber_);
+ return false;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::checkForDestructuringError()
+{
+ // 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 <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::checkForExpressionError()
+{
+ // Clear pending destructuring error, because we're definitely not in a
+ // destructuring context.
+ setResolved(ErrorKind::Destructuring);
+
+ // Report any pending expression error.
+ return checkForError(ErrorKind::Expression);
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::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 <typename ParseHandler>
+void
+Parser<ParseHandler>::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 <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkAssignmentToCall(Node target, unsigned msg)
+{
+ MOZ_ASSERT(handler.isFunctionCall(target));
+
+ // Assignment to function calls is forbidden in ES6. We're still somewhat
+ // concerned about sites using this in dead code, so forbid it only in
+ // strict mode code (or if the werror option has been set), and otherwise
+ // warn.
+ return report(ParseStrictError, pc->sc()->strict(), target, msg);
+}
+
+template <>
+bool
+Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<DeclarationKind> maybeDecl)
+{
+ MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr));
+
+ // Parentheses are forbidden around destructuring *patterns* (but allowed
+ // around names). Use our nicer error message for parenthesized, nested
+ // patterns.
+ if (handler.isParenthesizedDestructuringPattern(expr)) {
+ report(ParseError, false, expr, JSMSG_BAD_DESTRUCT_PARENS);
+ return false;
+ }
+
+ // This expression might be in a variable-binding pattern where only plain,
+ // unparenthesized names are permitted.
+ if (maybeDecl) {
+ // Destructuring patterns in declarations must only contain
+ // unparenthesized names.
+ if (!handler.isUnparenthesizedName(expr)) {
+ report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
+ return false;
+ }
+
+ RootedPropertyName name(context, expr->name());
+ return noteDeclaredName(name, *maybeDecl, handler.getPosition(expr));
+ }
+
+ // Otherwise this is an expression in destructuring outside a declaration.
+ if (!reportIfNotValidSimpleAssignmentTarget(expr, KeyedDestructuringAssignment))
+ return false;
+
+ MOZ_ASSERT(!handler.isFunctionCall(expr),
+ "function calls shouldn't be considered valid targets in "
+ "destructuring patterns");
+
+ if (handler.isNameAnyParentheses(expr)) {
+ // The arguments/eval identifiers are simple in non-strict mode code.
+ // Warn to discourage their use nonetheless.
+ return reportIfArgumentsEvalTarget(expr);
+ }
+
+ // Nothing further to do for property accesses.
+ MOZ_ASSERT(handler.isPropertyAccess(expr));
+ return true;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
+ Maybe<DeclarationKind> maybeDecl,
+ PossibleError* possibleError /* = nullptr */);
+
+template <>
+bool
+Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
+ Maybe<DeclarationKind> maybeDecl)
+{
+ MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
+
+ for (ParseNode* member = objectPattern->pn_head; member; member = member->pn_next) {
+ ParseNode* target;
+ if (member->isKind(PNK_MUTATEPROTO)) {
+ target = member->pn_kid;
+ } else {
+ MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
+ MOZ_ASSERT_IF(member->isKind(PNK_SHORTHAND),
+ member->pn_left->isKind(PNK_OBJECT_PROPERTY_NAME) &&
+ member->pn_right->isKind(PNK_NAME) &&
+ member->pn_left->pn_atom == member->pn_right->pn_atom);
+
+ target = member->pn_right;
+ }
+ if (handler.isUnparenthesizedAssignment(target))
+ target = target->pn_left;
+
+ if (handler.isUnparenthesizedDestructuringPattern(target)) {
+ if (!checkDestructuringPattern(target, maybeDecl))
+ return false;
+ } else {
+ if (!checkDestructuringName(target, maybeDecl))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
+ Maybe<DeclarationKind> maybeDecl)
+{
+ MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
+
+ for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) {
+ if (element->isKind(PNK_ELISION))
+ continue;
+
+ ParseNode* target;
+ if (element->isKind(PNK_SPREAD)) {
+ if (element->pn_next) {
+ report(ParseError, false, element->pn_next, JSMSG_PARAMETER_AFTER_REST);
+ return false;
+ }
+ target = element->pn_kid;
+ } else if (handler.isUnparenthesizedAssignment(element)) {
+ target = element->pn_left;
+ } else {
+ target = element;
+ }
+
+ if (handler.isUnparenthesizedDestructuringPattern(target)) {
+ if (!checkDestructuringPattern(target, maybeDecl))
+ return false;
+ } else {
+ if (!checkDestructuringName(target, maybeDecl))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Destructuring patterns can appear in two kinds of contexts:
+ *
+ * - assignment-like: assignment expressions and |for| loop heads. In
+ * these cases, the patterns' property value positions can be
+ * arbitrary lvalue expressions; the destructuring is just a fancy
+ * assignment.
+ *
+ * - binding-like: |var| and |let| declarations, functions' formal
+ * parameter lists, |catch| clauses, and comprehension tails. In
+ * these cases, the patterns' property value positions must be
+ * simple names; the destructuring defines them as new variables.
+ *
+ * In both cases, other code parses the pattern as an arbitrary
+ * primaryExpr, and then, here in checkDestructuringPattern, verify
+ * that the tree is a valid AssignmentPattern or BindingPattern.
+ *
+ * In assignment-like contexts, we parse the pattern with
+ * pc->inDestructuringDecl clear, so the lvalue expressions in the
+ * pattern are parsed normally. primaryExpr links variable references
+ * into the appropriate use chains; creates placeholder definitions;
+ * and so on. checkDestructuringPattern won't bind any new names and
+ * we specialize lvalues as appropriate.
+ *
+ * In declaration-like contexts, the normal variable reference
+ * processing would just be an obstruction, because we're going to
+ * define the names that appear in the property value positions as new
+ * variables anyway. In this case, we parse the pattern with
+ * pc->inDestructuringDecl set, which directs primaryExpr to leave
+ * whatever name nodes it creates unconnected. Then, here in
+ * checkDestructuringPattern, we require the pattern's property value
+ * positions to be simple names, and define them as appropriate to the
+ * context.
+ */
+template <>
+bool
+Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
+ Maybe<DeclarationKind> maybeDecl,
+ PossibleError* possibleError /* = nullptr */)
+{
+ if (pattern->isKind(PNK_ARRAYCOMP)) {
+ report(ParseError, false, pattern, JSMSG_ARRAY_COMP_LEFTSIDE);
+ return false;
+ }
+
+ bool isDestructuring = pattern->isKind(PNK_ARRAY)
+ ? checkDestructuringArray(pattern, maybeDecl)
+ : checkDestructuringObject(pattern, maybeDecl);
+
+ // Report any pending destructuring error.
+ if (isDestructuring && possibleError && !possibleError->checkForDestructuringError())
+ return false;
+
+ return isDestructuring;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::checkDestructuringPattern(Node pattern,
+ Maybe<DeclarationKind> maybeDecl,
+ PossibleError* possibleError /* = nullptr */)
+{
+ return abortIfSyntaxParser();
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
+ TokenKind tt)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+ MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
+
+ PossibleError possibleError(*this);
+ Node pattern;
+ {
+ pc->inDestructuringDecl = Some(kind);
+ pattern = primaryExpr(yieldHandling, TripledotProhibited, tt, &possibleError);
+ pc->inDestructuringDecl = Nothing();
+ }
+
+ if (!pattern || !checkDestructuringPattern(pattern, Some(kind), &possibleError))
+ return null();
+
+ return pattern;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
+ YieldHandling yieldHandling,
+ TokenKind tt)
+{
+ uint32_t startYieldOffset = pc->lastYieldOffset;
+ uint32_t startAwaitOffset = pc->lastAwaitOffset;
+ Node res = destructuringDeclaration(kind, yieldHandling, tt);
+ if (res) {
+ if (pc->lastYieldOffset != startYieldOffset) {
+ reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
+ return null();
+ }
+ if (pc->lastAwaitOffset != startAwaitOffset) {
+ reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
+ return null();
+ }
+ }
+ return res;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+
+ ParseContext::Statement stmt(pc, StatementKind::Block);
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ Node list = statementList(yieldHandling);
+ if (!list)
+ return null();
+
+ MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber);
+
+ return finishLexicalScope(scope, list);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
+ YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(forHeadKind == PNK_FORIN || forHeadKind == PNK_FOROF);
+ Node pn = forHeadKind == PNK_FOROF
+ ? assignExpr(InAllowed, yieldHandling, TripledotProhibited)
+ : expr(InAllowed, yieldHandling, TripledotProhibited);
+ return pn;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, TokenKind tt,
+ bool initialDeclaration, YieldHandling yieldHandling,
+ ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) ||
+ tokenStream.isCurrentTokenType(TOK_LC));
+
+ Node pattern = destructuringDeclaration(declKind, yieldHandling, tt);
+ if (!pattern)
+ return null();
+
+ if (initialDeclaration && forHeadKind) {
+ bool isForIn, isForOf;
+ if (!matchInOrOf(&isForIn, &isForOf))
+ return null();
+
+ if (isForIn) {
+ *forHeadKind = PNK_FORIN;
+ } else if (isForOf) {
+ *forHeadKind = PNK_FOROF;
+
+ // Annex B.3.5 has different early errors for vars in for-of loops.
+ if (declKind == DeclarationKind::Var)
+ declKind = DeclarationKind::ForOfVar;
+ } else {
+ *forHeadKind = PNK_FORHEAD;
+ }
+
+ if (*forHeadKind != PNK_FORHEAD) {
+ *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
+ if (!*forInOrOfExpression)
+ return null();
+
+ return pattern;
+ }
+ }
+
+ TokenKind token;
+ if (!tokenStream.getToken(&token, TokenStream::None))
+ return null();
+
+ if (token != TOK_ASSIGN) {
+ report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_DECL);
+ return null();
+ }
+
+ Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
+ yieldHandling, TripledotProhibited);
+ if (!init)
+ return null();
+
+ if (forHeadKind) {
+ // For for(;;) declarations, consistency with |for (;| parsing requires
+ // that the ';' first be examined as Operand, even though absence of a
+ // binary operator (examined with modifier None) terminated |init|.
+ // For all other declarations, through ASI's infinite majesty, a next
+ // token on a new line would begin an expression.
+ tokenStream.addModifierException(TokenStream::OperandIsNone);
+ }
+
+ return handler.newBinary(PNK_ASSIGN, pattern, init);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding,
+ Handle<PropertyName*> name,
+ DeclarationKind declKind,
+ bool initialDeclaration,
+ YieldHandling yieldHandling,
+ ParseNodeKind* forHeadKind,
+ Node* forInOrOfExpression)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+
+ Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
+ yieldHandling, TripledotProhibited);
+ if (!initializer)
+ return false;
+
+ if (forHeadKind) {
+ if (initialDeclaration) {
+ bool isForIn, isForOf;
+ if (!matchInOrOf(&isForIn, &isForOf))
+ return false;
+
+ // An initialized declaration can't appear in a for-of:
+ //
+ // for (var/let/const x = ... of ...); // BAD
+ if (isForOf) {
+ report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE);
+ return false;
+ }
+
+ if (isForIn) {
+ // Lexical declarations in for-in loops can't be initialized:
+ //
+ // for (let/const x = ... in ...); // BAD
+ if (DeclarationKindIsLexical(declKind)) {
+ report(ParseError, false, binding, JSMSG_BAD_FOR_LEFTSIDE);
+ return false;
+ }
+
+ // This leaves only initialized for-in |var| declarations. ES6
+ // forbids these; later ES un-forbids in non-strict mode code.
+ *forHeadKind = PNK_FORIN;
+ if (!report(ParseStrictError, pc->sc()->strict(), initializer,
+ JSMSG_INVALID_FOR_IN_DECL_WITH_INIT))
+ {
+ return false;
+ }
+
+ *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling);
+ if (!*forInOrOfExpression)
+ return false;
+ } else {
+ *forHeadKind = PNK_FORHEAD;
+ }
+ } else {
+ MOZ_ASSERT(*forHeadKind == PNK_FORHEAD);
+
+ // In the very rare case of Parser::assignExpr consuming an
+ // ArrowFunction with block body, when full-parsing with the arrow
+ // function being a skipped lazy inner function, we don't have
+ // lookahead for the next token. Do a one-off peek here to be
+ // consistent with what Parser::matchForInOrOf does in the other
+ // arm of this |if|.
+ //
+ // If you think this all sounds pretty code-smelly, you're almost
+ // certainly correct.
+ TokenKind ignored;
+ if (!tokenStream.peekToken(&ignored))
+ return false;
+ }
+
+ if (*forHeadKind == PNK_FORHEAD) {
+ // Per Parser::forHeadStart, the semicolon in |for (;| is
+ // ultimately gotten as Operand. But initializer expressions
+ // terminate with the absence of an operator gotten as None,
+ // so we need an exception.
+ tokenStream.addModifierException(TokenStream::OperandIsNone);
+ }
+ }
+
+ return handler.finishInitializerAssignment(binding, initializer);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, TokenKind tt,
+ bool initialDeclaration, YieldHandling yieldHandling,
+ ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
+{
+ // Anything other than TOK_YIELD or TOK_NAME is an error.
+ if (tt != TOK_NAME && tt != TOK_YIELD) {
+ report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
+ return null();
+ }
+
+ RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+ if (!name)
+ return null();
+
+ Node binding = newName(name);
+ if (!binding)
+ return null();
+
+ 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 as Operand.
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_ASSIGN, TokenStream::Operand))
+ return null();
+
+ if (matched) {
+ if (!initializerInNameDeclaration(decl, binding, name, declKind, initialDeclaration,
+ yieldHandling, forHeadKind, forInOrOfExpression))
+ {
+ return null();
+ }
+ } else {
+ tokenStream.addModifierException(TokenStream::NoneIsOperand);
+
+ if (initialDeclaration && forHeadKind) {
+ bool isForIn, isForOf;
+ if (!matchInOrOf(&isForIn, &isForOf))
+ return null();
+
+ if (isForIn) {
+ *forHeadKind = PNK_FORIN;
+ } else if (isForOf) {
+ *forHeadKind = PNK_FOROF;
+
+ // Annex B.3.5 has different early errors for vars in for-of loops.
+ if (declKind == DeclarationKind::Var)
+ declKind = DeclarationKind::ForOfVar;
+ } else {
+ *forHeadKind = PNK_FORHEAD;
+ }
+ }
+
+ if (forHeadKind && *forHeadKind != PNK_FORHEAD) {
+ *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
+ if (!*forInOrOfExpression)
+ return null();
+ } else {
+ // Normal const declarations, and const declarations in for(;;)
+ // heads, must be initialized.
+ if (declKind == DeclarationKind::Const) {
+ report(ParseError, false, binding, JSMSG_BAD_CONST_DECL);
+ return null();
+ }
+ }
+ }
+
+ // 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 null();
+
+ return binding;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::declarationList(YieldHandling yieldHandling,
+ ParseNodeKind kind,
+ ParseNodeKind* forHeadKind /* = nullptr */,
+ Node* forInOrOfExpression /* = nullptr */)
+{
+ MOZ_ASSERT(kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST);
+
+ JSOp op;
+ DeclarationKind declKind;
+ switch (kind) {
+ case PNK_VAR:
+ op = JSOP_DEFVAR;
+ declKind = DeclarationKind::Var;
+ break;
+ case PNK_CONST:
+ op = JSOP_DEFCONST;
+ declKind = DeclarationKind::Const;
+ break;
+ case PNK_LET:
+ op = JSOP_DEFLET;
+ declKind = DeclarationKind::Let;
+ break;
+ default:
+ MOZ_CRASH("Unknown declaration kind");
+ }
+
+ Node decl = handler.newDeclarationList(kind, op);
+ if (!decl)
+ return null();
+
+ bool matched;
+ bool initialDeclaration = true;
+ do {
+ MOZ_ASSERT_IF(!initialDeclaration && forHeadKind,
+ *forHeadKind == PNK_FORHEAD);
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ Node binding = (tt == TOK_LB || tt == TOK_LC)
+ ? declarationPattern(decl, declKind, tt, initialDeclaration, yieldHandling,
+ forHeadKind, forInOrOfExpression)
+ : declarationName(decl, declKind, tt, initialDeclaration, yieldHandling,
+ forHeadKind, forInOrOfExpression);
+ if (!binding)
+ return null();
+
+ handler.addList(decl, binding);
+
+ if (forHeadKind && *forHeadKind != PNK_FORHEAD)
+ break;
+
+ initialDeclaration = false;
+
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ } while (matched);
+
+ return decl;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isConst)
+{
+ /*
+ * 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.
+ */
+ Node decl = declarationList(yieldHandling, isConst ? PNK_CONST : PNK_LET);
+ if (!decl || !MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return null();
+
+ return decl;
+}
+
+template <>
+bool
+Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
+{
+ if (tt == TOK_LC) {
+ TokenStream::Modifier modifier = TokenStream::KeywordIsName;
+ 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 }.
+ if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
+ return false;
+
+ if (tt == TOK_RC)
+ break;
+
+ // If the next token is a keyword, the previous call to
+ // peekToken matched it as a TOK_NAME, and put it in the
+ // lookahead buffer, so this call will match keywords as well.
+ MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_IMPORT_NAME);
+ Rooted<PropertyName*> importName(context, tokenStream.currentName());
+ TokenPos importNamePos = pos();
+
+ TokenKind maybeAs;
+ if (!tokenStream.peekToken(&maybeAs))
+ return null();
+
+ if (maybeAs == TOK_NAME &&
+ tokenStream.nextName() == context->names().as)
+ {
+ tokenStream.consumeKnownToken(TOK_NAME);
+
+ if (!checkUnescapedName())
+ return false;
+
+ TokenKind afterAs;
+ if (!tokenStream.getToken(&afterAs))
+ return false;
+
+ if (afterAs != TOK_NAME && afterAs != TOK_YIELD) {
+ report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
+ return false;
+ }
+ } else {
+ // 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.
+ if (IsKeyword(importName)) {
+ JSAutoByteString bytes;
+ if (!AtomToPrintableString(context, importName, &bytes))
+ return false;
+ report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
+ return false;
+ }
+ }
+
+ RootedPropertyName bindingAtom(context, importedBinding());
+ if (!bindingAtom)
+ return false;
+
+ Node bindingName = newName(bindingAtom);
+ if (!bindingName)
+ return false;
+ if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
+ return false;
+
+ Node importNameNode = newName(importName, importNamePos);
+ if (!importNameNode)
+ return false;
+
+ Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importNameNode, bindingName);
+ if (!importSpec)
+ return false;
+
+ handler.addList(importSpecSet, importSpec);
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return false;
+
+ if (!matched) {
+ modifier = TokenStream::None;
+ break;
+ }
+ }
+
+ MUST_MATCH_TOKEN_MOD(TOK_RC, modifier, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
+ } else {
+ MOZ_ASSERT(tt == TOK_MUL);
+ if (!tokenStream.getToken(&tt))
+ return false;
+
+ if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) {
+ report(ParseError, false, null(), JSMSG_AS_AFTER_IMPORT_STAR);
+ return false;
+ }
+
+ if (!checkUnescapedName())
+ return false;
+
+ MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
+
+ Node importName = newName(context->names().star);
+ if (!importName)
+ return false;
+
+ // Namespace imports are are not indirect bindings but lexical
+ // definitions that hold a module namespace object. They are treated
+ // as const variables which are initialized during the
+ // ModuleDeclarationInstantiation step.
+ RootedPropertyName bindingName(context, importedBinding());
+ if (!bindingName)
+ return false;
+ Node bindingNameNode = newName(bindingName);
+ if (!bindingNameNode)
+ return 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();
+
+ Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingNameNode);
+ if (!importSpec)
+ return false;
+
+ handler.addList(importSpecSet, importSpec);
+ }
+
+ return true;
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template<>
+ParseNode*
+Parser<FullParseHandler>::importDeclaration()
+{
+ MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
+
+ if (!pc->atModuleLevel()) {
+ report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
+ return null();
+ }
+
+ uint32_t begin = pos().begin;
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
+ if (!importSpecSet)
+ return null();
+
+ if (tt == TOK_NAME || tt == TOK_LC || tt == TOK_MUL) {
+ if (tt == TOK_NAME) {
+ // 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'|.
+ Node importName = newName(context->names().default_);
+ if (!importName)
+ return null();
+
+ RootedPropertyName bindingAtom(context, importedBinding());
+ if (!bindingAtom)
+ return null();
+
+ Node bindingName = newName(bindingAtom);
+ if (!bindingName)
+ return null();
+
+ if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
+ return null();
+
+ Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
+ if (!importSpec)
+ return null();
+
+ handler.addList(importSpecSet, importSpec);
+
+ if (!tokenStream.peekToken(&tt))
+ return null();
+
+ if (tt == TOK_COMMA) {
+ tokenStream.consumeKnownToken(tt);
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ if (tt != TOK_LC && tt != TOK_MUL) {
+ report(ParseError, false, null(), JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
+ return null();
+ }
+
+ if (!namedImportsOrNamespaceImport(tt, importSpecSet))
+ return null();
+ }
+ } else {
+ if (!namedImportsOrNamespaceImport(tt, importSpecSet))
+ return null();
+ }
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
+ report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_CLAUSE);
+ return null();
+ }
+
+ if (!checkUnescapedName())
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+ } else if (tt == TOK_STRING) {
+ // Handle the form |import 'a'| by leaving the list empty. This is
+ // equivalent to |import {} from 'a'|.
+ importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
+ } else {
+ report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
+ return null();
+ }
+
+ Node moduleSpec = stringLiteral();
+ if (!moduleSpec)
+ return null();
+
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+
+ ParseNode* node =
+ handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
+ if (!node || !pc->sc()->asModuleContext()->builder.processImport(node))
+ return null();
+
+ return node;
+}
+
+template<>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::importDeclaration()
+{
+ JS_ALWAYS_FALSE(abortIfSyntaxParser());
+ return SyntaxParseHandler::NodeFailure;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::checkExportedName(JSAtom* exportName)
+{
+ if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
+ return true;
+
+ JSAutoByteString str;
+ if (!AtomToPrintableString(context, exportName, &str))
+ return false;
+
+ report(ParseError, false, null(), JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
+ return false;
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedName(JSAtom* exportName)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template<>
+bool
+Parser<FullParseHandler>::checkExportedNamesForDeclaration(ParseNode* node)
+{
+ MOZ_ASSERT(node->isArity(PN_LIST));
+ for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
+ if (binding->isKind(PNK_ASSIGN))
+ binding = binding->pn_left;
+ MOZ_ASSERT(binding->isKind(PNK_NAME));
+ if (!checkExportedName(binding->pn_atom))
+ return false;
+ }
+
+ return true;
+}
+
+template<>
+bool
+Parser<SyntaxParseHandler>::checkExportedNamesForDeclaration(Node node)
+{
+ MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+ return false;
+}
+
+template<>
+ParseNode*
+Parser<FullParseHandler>::exportDeclaration()
+{
+ MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
+
+ if (!pc->atModuleLevel()) {
+ report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
+ return null();
+ }
+
+ uint32_t begin = pos().begin;
+
+ Node kid;
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ switch (tt) {
+ case TOK_LC: {
+ kid = handler.newList(PNK_EXPORT_SPEC_LIST);
+ if (!kid)
+ return null();
+
+ while (true) {
+ // Handle the forms |export {}| and |export { ..., }| (where ...
+ // is non empty), by escaping the loop early if the next token
+ // is }.
+ if (!tokenStream.peekToken(&tt))
+ return null();
+ if (tt == TOK_RC)
+ break;
+
+ MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
+ Node bindingName = newName(tokenStream.currentName());
+ if (!bindingName)
+ return null();
+
+ bool foundAs;
+ if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as))
+ return null();
+ if (foundAs) {
+ if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ return null();
+ if (tt != TOK_NAME) {
+ report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME);
+ return null();
+ }
+ }
+
+ Node exportName = newName(tokenStream.currentName());
+ if (!exportName)
+ return null();
+
+ if (!checkExportedName(exportName->pn_atom))
+ return null();
+
+ Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
+ if (!exportSpec)
+ return null();
+
+ handler.addList(kid, exportSpec);
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ if (!matched)
+ break;
+ }
+
+ MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
+
+ // 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 Operand
+ // context, so simply matching a contextual keyword won't work:
+ //
+ // export { x } // ExportDeclaration, terminated by ASI
+ // fro\u006D // ExpressionStatement, the name "from"
+ //
+ // In that case let MatchOrInsertSemicolonAfterNonExpression sort out
+ // ASI or any necessary error.
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ if (tt == TOK_NAME &&
+ tokenStream.currentToken().name() == context->names().from &&
+ !tokenStream.currentToken().nameContainsEscape())
+ {
+ MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+
+ Node moduleSpec = stringLiteral();
+ if (!moduleSpec)
+ return null();
+
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+
+ ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
+ if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
+ return null();
+
+ return node;
+ }
+
+ tokenStream.ungetToken();
+
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+ break;
+ }
+
+ case TOK_MUL: {
+ kid = handler.newList(PNK_EXPORT_SPEC_LIST);
+ if (!kid)
+ return null();
+
+ // Handle the form |export *| by adding a special export batch
+ // specifier to the list.
+ Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
+ if (!exportSpec)
+ return null();
+
+ handler.addList(kid, exportSpec);
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
+ report(ParseError, false, null(), JSMSG_FROM_AFTER_EXPORT_STAR);
+ return null();
+ }
+
+ if (!checkUnescapedName())
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+
+ Node moduleSpec = stringLiteral();
+ if (!moduleSpec)
+ return null();
+
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+
+ ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
+ if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
+ return null();
+
+ return node;
+
+ }
+
+ case TOK_FUNCTION:
+ kid = functionStmt(YieldIsKeyword, NameRequired);
+ if (!kid)
+ return null();
+
+ if (!checkExportedName(kid->pn_funbox->function()->name()))
+ return null();
+ break;
+
+ case TOK_CLASS: {
+ kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
+ if (!kid)
+ return null();
+
+ const ClassNode& cls = kid->as<ClassNode>();
+ MOZ_ASSERT(cls.names());
+ if (!checkExportedName(cls.names()->innerBinding()->pn_atom))
+ return null();
+ break;
+ }
+
+ case TOK_VAR:
+ kid = declarationList(YieldIsName, PNK_VAR);
+ if (!kid)
+ return null();
+ if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return null();
+ if (!checkExportedNamesForDeclaration(kid))
+ return null();
+ break;
+
+ case TOK_DEFAULT: {
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ if (!checkExportedName(context->names().default_))
+ return null();
+
+ ParseNode* nameNode = nullptr;
+ switch (tt) {
+ case TOK_FUNCTION:
+ kid = functionStmt(YieldIsKeyword, AllowDefaultName);
+ if (!kid)
+ return null();
+ break;
+ case TOK_CLASS:
+ kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
+ if (!kid)
+ return null();
+ break;
+ default: {
+ if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ if (nextSameLine == TOK_FUNCTION) {
+ tokenStream.consumeKnownToken(nextSameLine);
+ kid = functionStmt(YieldIsName, AllowDefaultName, AsyncFunction);
+ if (!kid)
+ return null();
+ break;
+ }
+ }
+
+ tokenStream.ungetToken();
+ RootedPropertyName name(context, context->names().starDefaultStar);
+ nameNode = newName(name);
+ if (!nameNode)
+ return null();
+ if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
+ return null();
+ kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
+ if (!kid)
+ return null();
+ if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return null();
+ break;
+ }
+ }
+
+ ParseNode* node = handler.newExportDefaultDeclaration(kid, nameNode,
+ TokenPos(begin, pos().end));
+ if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
+ return null();
+
+ return node;
+ }
+
+ case TOK_CONST:
+ kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
+ if (!kid)
+ return null();
+ if (!checkExportedNamesForDeclaration(kid))
+ return null();
+ break;
+
+ case TOK_NAME:
+ if (tokenStream.currentName() == context->names().let) {
+ if (!checkUnescapedName())
+ return null();
+
+ kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
+ if (!kid)
+ return null();
+ if (!checkExportedNamesForDeclaration(kid))
+ return null();
+ break;
+ }
+ MOZ_FALLTHROUGH;
+
+ default:
+ report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
+ return null();
+ }
+
+ ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
+ if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
+ return null();
+
+ return node;
+}
+
+template<>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::exportDeclaration()
+{
+ JS_ALWAYS_FALSE(abortIfSyntaxParser());
+ return SyntaxParseHandler::NodeFailure;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked)
+{
+ tokenStream.ungetToken();
+ Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
+ /* possibleError = */ nullptr, invoked);
+ if (!pnexpr)
+ return null();
+ if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return null();
+ return handler.newExprStatement(pnexpr, pos().end);
+}
+
+template <class ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling)
+{
+ TokenKind next;
+ if (!tokenStream.peekToken(&next, TokenStream::Operand))
+ return null();
+
+ if (next == TOK_FUNCTION) {
+ // Apply Annex B.3.4 in non-strict code to allow FunctionDeclaration as
+ // the consequent/alternative of an |if| or |else|. Parser::statement
+ // will report the strict mode error.
+ if (!pc->sc()->strict()) {
+ tokenStream.consumeKnownToken(next, TokenStream::Operand);
+ return functionStmt(yieldHandling, NameRequired);
+ }
+ }
+
+ return statement(yieldHandling);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
+{
+ Vector<Node, 4> condList(context), thenList(context);
+ Vector<uint32_t, 4> posList(context);
+ Node elseBranch;
+
+ ParseContext::Statement stmt(pc, StatementKind::If);
+
+ while (true) {
+ uint32_t begin = pos().begin;
+
+ /* An IF node has three kids: condition, then, and optional else. */
+ Node cond = condition(InAllowed, yieldHandling);
+ if (!cond)
+ return null();
+
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt == TOK_SEMI) {
+ if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
+ return null();
+ }
+
+ Node thenBranch = consequentOrAlternative(yieldHandling);
+ if (!thenBranch)
+ return null();
+
+ if (!condList.append(cond) || !thenList.append(thenBranch) || !posList.append(begin))
+ return null();
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_ELSE, TokenStream::Operand))
+ return null();
+ if (matched) {
+ if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
+ return null();
+ if (matched)
+ continue;
+ elseBranch = consequentOrAlternative(yieldHandling);
+ if (!elseBranch)
+ return null();
+ } else {
+ elseBranch = null();
+ }
+ break;
+ }
+
+ for (int i = condList.length() - 1; i >= 0; i--) {
+ elseBranch = handler.newIfStatement(posList[i], condList[i], thenList[i], elseBranch);
+ if (!elseBranch)
+ return null();
+ }
+
+ return elseBranch;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::doWhileStatement(YieldHandling yieldHandling)
+{
+ uint32_t begin = pos().begin;
+ ParseContext::Statement stmt(pc, StatementKind::DoLoop);
+ Node body = statement(yieldHandling);
+ if (!body)
+ return null();
+ MUST_MATCH_TOKEN_MOD(TOK_WHILE, TokenStream::Operand, JSMSG_WHILE_AFTER_DO);
+ Node cond = condition(InAllowed, yieldHandling);
+ if (!cond)
+ return null();
+
+ // The semicolon after do-while is even more optional than most
+ // semicolons in JS. Web compat required this by 2004:
+ // http://bugzilla.mozilla.org/show_bug.cgi?id=238945
+ // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
+ // https://bugs.ecmascript.org/show_bug.cgi?id=157
+ // To parse |do {} while (true) false| correctly, use Operand.
+ bool ignored;
+ if (!tokenStream.matchToken(&ignored, TOK_SEMI, TokenStream::Operand))
+ return null();
+ return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::whileStatement(YieldHandling yieldHandling)
+{
+ uint32_t begin = pos().begin;
+ ParseContext::Statement stmt(pc, StatementKind::WhileLoop);
+ Node cond = condition(InAllowed, yieldHandling);
+ if (!cond)
+ return null();
+ Node body = statement(yieldHandling);
+ if (!body)
+ return null();
+ return handler.newWhileStatement(begin, cond, body);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
+{
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return false;
+
+ *isForInp = tt == TOK_IN;
+ *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of;
+ if (!*isForInp && !*isForOfp) {
+ tokenStream.ungetToken();
+ } else {
+ if (tt == TOK_NAME && !checkUnescapedName())
+ return false;
+ }
+
+ MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
+ return true;
+}
+
+template <class ParseHandler>
+bool
+Parser<ParseHandler>::validateForInOrOfLHSExpression(Node target, PossibleError* possibleError)
+{
+ if (handler.isUnparenthesizedDestructuringPattern(target))
+ return checkDestructuringPattern(target, Nothing(), possibleError);
+
+ // All other permitted targets are simple.
+ if (!reportIfNotValidSimpleAssignmentTarget(target, ForInOrOfTarget))
+ return false;
+
+ if (handler.isPropertyAccess(target))
+ return true;
+
+ if (handler.isNameAnyParentheses(target)) {
+ // The arguments/eval identifiers are simple in non-strict mode code,
+ // but warn to discourage use nonetheless.
+ if (!reportIfArgumentsEvalTarget(target))
+ return false;
+
+ handler.adjustGetToSet(target);
+ return true;
+ }
+
+ if (handler.isFunctionCall(target))
+ return checkAssignmentToCall(target, JSMSG_BAD_FOR_LEFTSIDE);
+
+ report(ParseError, false, target, JSMSG_BAD_FOR_LEFTSIDE);
+ return false;
+}
+
+template <class ParseHandler>
+bool
+Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
+ ParseNodeKind* forHeadKind,
+ Node* forInitialPart,
+ Maybe<ParseContext::Scope>& forLoopLexicalScope,
+ Node* forInOrOfExpression)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+
+ // Super-duper easy case: |for (;| is a C-style for-loop with no init
+ // component.
+ if (tt == TOK_SEMI) {
+ *forInitialPart = null();
+ *forHeadKind = PNK_FORHEAD;
+ return true;
+ }
+
+ // Parsing after |for (var| is also relatively simple (from this method's
+ // point of view). No block-related work complicates matters, so delegate
+ // to Parser::declaration.
+ if (tt == TOK_VAR) {
+ tokenStream.consumeKnownToken(tt, TokenStream::Operand);
+
+ // Pass null for block object because |var| declarations don't use one.
+ *forInitialPart = declarationList(yieldHandling, PNK_VAR, forHeadKind,
+ forInOrOfExpression);
+ return *forInitialPart != null();
+ }
+
+ // Otherwise we have a lexical declaration or an expression.
+
+ // For-in loop backwards compatibility requires that |let| starting a
+ // for-loop that's not a (new to ES6) for-of loop, in non-strict mode code,
+ // parse as an identifier. (|let| in for-of is always a declaration.)
+ bool parsingLexicalDeclaration = false;
+ bool letIsIdentifier = false;
+ if (tt == TOK_CONST) {
+ parsingLexicalDeclaration = true;
+ tokenStream.consumeKnownToken(tt, TokenStream::Operand);
+ } else if (tt == TOK_NAME &&
+ tokenStream.nextName() == context->names().let &&
+ !tokenStream.nextNameContainsEscape())
+ {
+ // We could have a {For,Lexical}Declaration, or we could have a
+ // LeftHandSideExpression with lookahead restrictions so it's not
+ // ambiguous with the former. Check for a continuation of the former
+ // to decide which we have.
+ tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
+
+ TokenKind next;
+ if (!tokenStream.peekToken(&next))
+ return false;
+
+ parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next, yieldHandling);
+ if (!parsingLexicalDeclaration) {
+ tokenStream.ungetToken();
+ letIsIdentifier = true;
+ }
+ }
+
+ if (parsingLexicalDeclaration) {
+ forLoopLexicalScope.emplace(this);
+ if (!forLoopLexicalScope->init(pc))
+ return null();
+
+ // Push a temporary ForLoopLexicalHead Statement that allows for
+ // lexical declarations, as they are usually allowed only in braced
+ // statements.
+ ParseContext::Statement forHeadStmt(pc, StatementKind::ForLoopLexicalHead);
+
+ *forInitialPart = declarationList(yieldHandling, tt == TOK_CONST ? PNK_CONST : PNK_LET,
+ forHeadKind, forInOrOfExpression);
+ return *forInitialPart != null();
+ }
+
+ // Finally, handle for-loops that start with expressions. Pass
+ // |InProhibited| so that |in| isn't parsed in a RelationalExpression as a
+ // binary operator. |in| makes it a for-in loop, *not* an |in| expression.
+ PossibleError possibleError(*this);
+ *forInitialPart = expr(InProhibited, yieldHandling, TripledotProhibited, &possibleError);
+ if (!*forInitialPart)
+ return false;
+
+ bool isForIn, isForOf;
+ if (!matchInOrOf(&isForIn, &isForOf))
+ return false;
+
+ // If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled
+ // the init expression; the caller handles the rest. Allow the Operand
+ // modifier when regetting: Operand must be used to examine the ';' in
+ // |for (;|, and our caller handles this case and that.
+ if (!isForIn && !isForOf) {
+ if (!possibleError.checkForExpressionError())
+ return false;
+ *forHeadKind = PNK_FORHEAD;
+ tokenStream.addModifierException(TokenStream::OperandIsNone);
+ return true;
+ }
+
+ MOZ_ASSERT(isForIn != isForOf);
+
+ // In a for-of loop, 'let' that starts the loop head is a |let| keyword,
+ // per the [lookahead ≠ let] restriction on the LeftHandSideExpression
+ // variant of such loops. Expressions that start with |let| can't be used
+ // here.
+ //
+ // var let = {};
+ // for (let.prop of [1]) // BAD
+ // break;
+ //
+ // See ES6 13.7.
+ if (isForOf && letIsIdentifier) {
+ report(ParseError, false, *forInitialPart, JSMSG_LET_STARTING_FOROF_LHS);
+ return false;
+ }
+
+ *forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF;
+
+ if (!validateForInOrOfLHSExpression(*forInitialPart, &possibleError))
+ return false;
+ if (!possibleError.checkForExpressionError())
+ return false;
+
+ // Finally, parse the iterated expression, making the for-loop's closing
+ // ')' the next token.
+ *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
+ return *forInOrOfExpression != null();
+}
+
+template <class ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
+ uint32_t begin = pos().begin;
+
+ ParseContext::Statement stmt(pc, StatementKind::ForLoop);
+
+ bool isForEach = false;
+ unsigned iflags = 0;
+
+ if (allowsForEachIn()) {
+ bool matched;
+ if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
+ return null();
+ if (matched) {
+ iflags = JSITER_FOREACH;
+ isForEach = true;
+ addTelemetry(JSCompartment::DeprecatedForEach);
+ if (!warnOnceAboutForEach())
+ return null();
+ }
+ }
+
+ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
+
+ // PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type.
+ ParseNodeKind headKind;
+
+ // |x| in either |for (x; ...; ...)| or |for (x in/of ...)|.
+ Node startNode;
+
+ // The next two variables are used to implement `for (let/const ...)`.
+ //
+ // We generate an implicit block, wrapping the whole loop, to store loop
+ // variables declared this way. Note that if the loop uses `for (var...)`
+ // instead, those variables go on some existing enclosing scope, so no
+ // implicit block scope is created.
+ //
+ // Both variables remain null/none if the loop is any other form.
+
+ // The static block scope for the implicit block scope.
+ Maybe<ParseContext::Scope> forLoopLexicalScope;
+
+ // The expression being iterated over, for for-in/of loops only. Unused
+ // for for(;;) loops.
+ Node iteratedExpr;
+
+ // Parse the entirety of the loop-head for a for-in/of loop (so the next
+ // token is the closing ')'):
+ //
+ // for (... in/of ...) ...
+ // ^next token
+ //
+ // ...OR, parse up to the first ';' in a C-style for-loop:
+ //
+ // for (...; ...; ...) ...
+ // ^next token
+ //
+ // In either case the subsequent token can be consistently accessed using
+ // TokenStream::None semantics.
+ if (!forHeadStart(yieldHandling, &headKind, &startNode, forLoopLexicalScope,
+ &iteratedExpr))
+ {
+ return null();
+ }
+
+ MOZ_ASSERT(headKind == PNK_FORIN || headKind == PNK_FOROF || headKind == PNK_FORHEAD);
+
+ Node forHead;
+ if (headKind == PNK_FORHEAD) {
+ Node init = startNode;
+
+ if (isForEach) {
+ reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP);
+ return null();
+ }
+
+ // Look for an operand: |for (;| means we might have already examined
+ // this semicolon with that modifier.
+ MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
+
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+
+ Node test;
+ TokenStream::Modifier mod;
+ if (tt == TOK_SEMI) {
+ test = null();
+ mod = TokenStream::Operand;
+ } else {
+ test = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!test)
+ return null();
+ mod = TokenStream::None;
+ }
+
+ MUST_MATCH_TOKEN_MOD(TOK_SEMI, mod, JSMSG_SEMI_AFTER_FOR_COND);
+
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+
+ Node update;
+ if (tt == TOK_RP) {
+ update = null();
+ mod = TokenStream::Operand;
+ } else {
+ update = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!update)
+ return null();
+ mod = TokenStream::None;
+ }
+
+ MUST_MATCH_TOKEN_MOD(TOK_RP, mod, JSMSG_PAREN_AFTER_FOR_CTRL);
+
+ TokenPos headPos(begin, pos().end);
+ forHead = handler.newForHead(init, test, update, headPos);
+ if (!forHead)
+ return null();
+ } else {
+ MOZ_ASSERT(headKind == PNK_FORIN || headKind == PNK_FOROF);
+
+ // |target| is the LeftHandSideExpression or declaration to which the
+ // per-iteration value (an arbitrary value exposed by the iteration
+ // protocol, or a string naming a property) is assigned.
+ Node target = startNode;
+
+ // Parse the rest of the for-in/of head.
+ if (headKind == PNK_FORIN) {
+ stmt.refineForKind(StatementKind::ForInLoop);
+ iflags |= JSITER_ENUMERATE;
+ } else {
+ if (isForEach) {
+ report(ParseError, false, startNode, JSMSG_BAD_FOR_EACH_LOOP);
+ return null();
+ }
+
+ stmt.refineForKind(StatementKind::ForOfLoop);
+ }
+
+ if (!handler.isDeclarationList(target)) {
+ MOZ_ASSERT(!forLoopLexicalScope);
+ if (!checkAndMarkAsAssignmentLhs(target, PlainAssignment))
+ return null();
+ }
+
+ // Parser::declaration consumed everything up to the closing ')'. That
+ // token follows an {Assignment,}Expression, so the next token must be
+ // consumed as if an operator continued the expression, i.e. as None.
+ MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::None, JSMSG_PAREN_AFTER_FOR_CTRL);
+
+ TokenPos headPos(begin, pos().end);
+ forHead = handler.newForInOrOfHead(headKind, target, iteratedExpr, headPos);
+ if (!forHead)
+ return null();
+ }
+
+ Node body = statement(yieldHandling);
+ if (!body)
+ return null();
+
+ Node forLoop = handler.newForStatement(begin, forHead, body, iflags);
+ if (!forLoop)
+ return null();
+
+ if (forLoopLexicalScope)
+ return finishLexicalScope(*forLoopLexicalScope, forLoop);
+
+ return forLoop;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
+ uint32_t begin = pos().begin;
+
+ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
+
+ Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
+ if (!discriminant)
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
+ MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
+
+ ParseContext::Statement stmt(pc, StatementKind::Switch);
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ Node caseList = handler.newStatementList(pos());
+ if (!caseList)
+ return null();
+
+ bool seenDefault = false;
+ TokenKind tt;
+ while (true) {
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt == TOK_RC)
+ break;
+ uint32_t caseBegin = pos().begin;
+
+ Node caseExpr;
+ switch (tt) {
+ case TOK_DEFAULT:
+ if (seenDefault) {
+ report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
+ return null();
+ }
+ seenDefault = true;
+ caseExpr = null(); // The default case has pn_left == nullptr.
+ break;
+
+ case TOK_CASE:
+ caseExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!caseExpr)
+ return null();
+ break;
+
+ default:
+ report(ParseError, false, null(), JSMSG_BAD_SWITCH);
+ return null();
+ }
+
+ MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
+
+ Node body = handler.newStatementList(pos());
+ if (!body)
+ return null();
+
+ bool afterReturn = false;
+ bool warnedAboutStatementsAfterReturn = false;
+ uint32_t statementBegin = 0;
+ while (true) {
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
+ break;
+ if (afterReturn) {
+ TokenPos pos(0, 0);
+ if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
+ return null();
+ statementBegin = pos.begin;
+ }
+ Node stmt = statementListItem(yieldHandling);
+ if (!stmt)
+ return null();
+ if (!warnedAboutStatementsAfterReturn) {
+ if (afterReturn) {
+ if (!handler.isStatementPermittedAfterReturnStatement(stmt)) {
+ if (!reportWithOffset(ParseWarning, false, statementBegin,
+ JSMSG_STMT_AFTER_RETURN))
+ {
+ return null();
+ }
+ warnedAboutStatementsAfterReturn = true;
+ }
+ } else if (handler.isReturnStatement(stmt)) {
+ afterReturn = true;
+ }
+ }
+ handler.addStatementToList(body, stmt);
+ }
+
+ Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
+ if (!casepn)
+ return null();
+ handler.addCaseStatementToList(caseList, casepn);
+ }
+
+ caseList = finishLexicalScope(scope, caseList);
+ if (!caseList)
+ return null();
+
+ handler.setEndPosition(caseList, pos().end);
+
+ return handler.newSwitchStatement(begin, discriminant, caseList);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
+ uint32_t begin = pos().begin;
+
+ RootedPropertyName label(context);
+ if (!matchLabel(yieldHandling, &label))
+ return null();
+
+ // Labeled 'continue' statements target the nearest labeled loop
+ // statements with the same label. Unlabeled 'continue' statements target
+ // the innermost loop statement.
+ auto isLoop = [](ParseContext::Statement* stmt) {
+ return StatementKindIsLoop(stmt->kind());
+ };
+
+ if (label) {
+ ParseContext::Statement* stmt = pc->innermostStatement();
+ bool foundLoop = false;
+
+ for (;;) {
+ stmt = ParseContext::Statement::findNearest(stmt, isLoop);
+ if (!stmt) {
+ report(ParseError, false, null(),
+ foundLoop ? JSMSG_LABEL_NOT_FOUND : JSMSG_BAD_CONTINUE);
+ return null();
+ }
+
+ foundLoop = true;
+
+ // Is it labeled by our label?
+ bool foundTarget = false;
+ stmt = stmt->enclosing();
+ while (stmt && stmt->is<ParseContext::LabelStatement>()) {
+ if (stmt->as<ParseContext::LabelStatement>().label() == label) {
+ foundTarget = true;
+ break;
+ }
+ stmt = stmt->enclosing();
+ }
+ if (foundTarget)
+ break;
+ }
+ } else if (!pc->findInnermostStatement(isLoop)) {
+ report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
+ return null();
+ }
+
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+
+ return handler.newContinueStatement(label, TokenPos(begin, pos().end));
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
+ uint32_t begin = pos().begin;
+
+ RootedPropertyName label(context);
+ if (!matchLabel(yieldHandling, &label))
+ return null();
+
+ // Labeled 'break' statements target the nearest labeled statements (could
+ // be any kind) with the same label. Unlabeled 'break' statements target
+ // the innermost loop or switch statement.
+ if (label) {
+ auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
+ return stmt->label() == label;
+ };
+
+ if (!pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) {
+ report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
+ return null();
+ }
+ } else {
+ auto isBreakTarget = [](ParseContext::Statement* stmt) {
+ return StatementKindIsUnlabeledBreakTarget(stmt->kind());
+ };
+
+ if (!pc->findInnermostStatement(isBreakTarget)) {
+ report(ParseError, false, null(), JSMSG_TOUGH_BREAK);
+ return null();
+ }
+ }
+
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+
+ return handler.newBreakStatement(label, TokenPos(begin, pos().end));
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
+ uint32_t begin = pos().begin;
+
+ MOZ_ASSERT(pc->isFunctionBox());
+ pc->functionBox()->usesReturn = true;
+
+ // Parse an optional operand.
+ //
+ // This is ugly, but we don't want to require a semicolon.
+ Node exprNode;
+ TokenKind tt = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+ return null();
+ switch (tt) {
+ case TOK_EOL:
+ case TOK_EOF:
+ case TOK_SEMI:
+ case TOK_RC:
+ exprNode = null();
+ pc->funHasReturnVoid = true;
+ break;
+ default: {
+ exprNode = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!exprNode)
+ return null();
+ pc->funHasReturnExpr = true;
+ }
+ }
+
+ if (exprNode) {
+ if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return null();
+ } else {
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+ }
+
+ Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
+ if (!pn)
+ return null();
+
+ if (pc->isLegacyGenerator() && exprNode) {
+ /* Disallow "return v;" in legacy generators. */
+ reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
+ JSMSG_BAD_ANON_GENERATOR_RETURN);
+ return null();
+ }
+
+ return pn;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newYieldExpression(uint32_t begin, typename ParseHandler::Node expr,
+ bool isYieldStar)
+{
+ Node generator = newDotGeneratorName();
+ if (!generator)
+ return null();
+ if (isYieldStar)
+ return handler.newYieldStarExpression(begin, expr, generator);
+ return handler.newYieldExpression(begin, expr, generator);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newAwaitExpression(uint32_t begin, typename ParseHandler::Node expr)
+{
+ Node generator = newDotGeneratorName();
+ if (!generator)
+ return null();
+ return handler.newAwaitExpression(begin, expr, generator);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::yieldExpression(InHandling inHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
+ uint32_t begin = pos().begin;
+
+ switch (pc->generatorKind()) {
+ case StarGenerator:
+ {
+ MOZ_ASSERT(pc->isFunctionBox());
+
+ pc->lastYieldOffset = begin;
+
+ Node exprNode;
+ ParseNodeKind kind = PNK_YIELD;
+ TokenKind tt = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+ return null();
+ switch (tt) {
+ // TOK_EOL is special; it implements the [no LineTerminator here]
+ // quirk in the grammar.
+ case TOK_EOL:
+ // The rest of these make up the complete set of tokens that can
+ // appear after any of the places where AssignmentExpression is used
+ // throughout the grammar. Conveniently, none of them can also be the
+ // start an expression.
+ case TOK_EOF:
+ case TOK_SEMI:
+ case TOK_RC:
+ case TOK_RB:
+ case TOK_RP:
+ case TOK_COLON:
+ case TOK_COMMA:
+ case TOK_IN:
+ // No value.
+ exprNode = null();
+ tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ break;
+ case TOK_MUL:
+ kind = PNK_YIELD_STAR;
+ tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand);
+ MOZ_FALLTHROUGH;
+ default:
+ exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
+ if (!exprNode)
+ return null();
+ }
+ return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR);
+ }
+
+ case NotGenerator:
+ // We are in code that has not seen a yield, but we are in JS 1.7 or
+ // later. Try to transition to being a legacy generator.
+ MOZ_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
+ MOZ_ASSERT(pc->lastYieldOffset == ParseContext::NoYieldOffset);
+
+ if (!abortIfSyntaxParser())
+ return null();
+
+ if (!pc->isFunctionBox()) {
+ report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
+ return null();
+ }
+
+ if (pc->functionBox()->isArrow()) {
+ reportWithOffset(ParseError, false, begin,
+ JSMSG_YIELD_IN_ARROW, js_yield_str);
+ return null();
+ }
+
+ if (pc->functionBox()->function()->isMethod() ||
+ pc->functionBox()->function()->isGetter() ||
+ pc->functionBox()->function()->isSetter())
+ {
+ reportWithOffset(ParseError, false, begin,
+ JSMSG_YIELD_IN_METHOD, js_yield_str);
+ return null();
+ }
+
+ if (pc->funHasReturnExpr
+#if JS_HAS_EXPR_CLOSURES
+ || pc->functionBox()->function()->isExprBody()
+#endif
+ )
+ {
+ /* As in Python (see PEP-255), disallow return v; in generators. */
+ reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
+ JSMSG_BAD_ANON_GENERATOR_RETURN);
+ return null();
+ }
+
+ pc->functionBox()->setGeneratorKind(LegacyGenerator);
+ addTelemetry(JSCompartment::DeprecatedLegacyGenerator);
+
+ MOZ_FALLTHROUGH;
+
+ case LegacyGenerator:
+ {
+ // We are in a legacy generator: a function that has already seen a
+ // yield.
+ MOZ_ASSERT(pc->isFunctionBox());
+
+ pc->lastYieldOffset = begin;
+
+ // Legacy generators do not require a value.
+ Node exprNode;
+ TokenKind tt = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+ return null();
+ switch (tt) {
+ case TOK_EOF:
+ case TOK_EOL:
+ case TOK_SEMI:
+ case TOK_RC:
+ case TOK_RB:
+ case TOK_RP:
+ case TOK_COLON:
+ case TOK_COMMA:
+ // No value.
+ exprNode = null();
+ tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ break;
+ default:
+ exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
+ if (!exprNode)
+ return null();
+ }
+
+ return newYieldExpression(begin, exprNode);
+ }
+ }
+
+ MOZ_CRASH("yieldExpr");
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::withStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
+ uint32_t begin = pos().begin;
+
+ // In most cases, we want the constructs forbidden in strict mode code to be
+ // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should
+ // use reportStrictModeError. However, 'with' is the sole instance of a
+ // construct that is forbidden in strict mode code, but doesn't even merit a
+ // warning under JSOPTION_EXTRA_WARNINGS. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
+ if (pc->sc()->strict()) {
+ if (!report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
+ return null();
+ }
+
+ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
+ Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
+ if (!objectExpr)
+ return null();
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
+
+ Node innerBlock;
+ {
+ ParseContext::Statement stmt(pc, StatementKind::With);
+ innerBlock = statement(yieldHandling);
+ if (!innerBlock)
+ return null();
+ }
+
+ pc->sc()->setBindingsAccessedDynamically();
+
+ return handler.newWithStatement(begin, objectExpr, innerBlock);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling)
+{
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ if (tt == TOK_FUNCTION) {
+ TokenKind next;
+ if (!tokenStream.peekToken(&next))
+ return null();
+
+ // GeneratorDeclaration is only matched by HoistableDeclaration in
+ // StatementListItem, so generators can't be inside labels.
+ if (next == TOK_MUL) {
+ report(ParseError, false, null(), JSMSG_GENERATOR_LABEL);
+ return null();
+ }
+
+ // Per 13.13.1 it's a syntax error if LabelledItem: FunctionDeclaration
+ // is ever matched. Per Annex B.3.2 that modifies this text, this
+ // applies only to strict mode code.
+ if (pc->sc()->strict()) {
+ report(ParseError, false, null(), JSMSG_FUNCTION_LABEL);
+ return null();
+ }
+
+ return functionStmt(yieldHandling, NameRequired);
+ }
+
+ tokenStream.ungetToken();
+ return statement(yieldHandling);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
+{
+ RootedPropertyName label(context, labelIdentifier(yieldHandling));
+ if (!label)
+ return null();
+
+ auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
+ return stmt->label() == label;
+ };
+
+ if (pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) {
+ report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
+ return null();
+ }
+
+ uint32_t begin = pos().begin;
+
+ tokenStream.consumeKnownToken(TOK_COLON);
+
+ /* Push a label struct and parse the statement. */
+ ParseContext::LabelStatement stmt(pc, label);
+ Node pn = labeledItem(yieldHandling);
+ if (!pn)
+ return null();
+
+ return handler.newLabeledStatement(label, pn, begin);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
+ uint32_t begin = pos().begin;
+
+ /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
+ TokenKind tt = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+ return null();
+ if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
+ report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW);
+ return null();
+ }
+ if (tt == TOK_EOL) {
+ report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW);
+ return null();
+ }
+
+ Node throwExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!throwExpr)
+ return null();
+
+ if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return null();
+
+ return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
+ uint32_t begin = pos().begin;
+
+ /*
+ * try nodes are ternary.
+ * kid1 is the try statement
+ * kid2 is the catch node list or null
+ * kid3 is the finally statement
+ *
+ * catch nodes are ternary.
+ * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
+ * kid2 is the catch guard or null if no guard
+ * kid3 is the catch block
+ *
+ * catch lvalue nodes are either:
+ * TOK_NAME for a single identifier
+ * TOK_RB or TOK_RC for a destructuring left-hand side
+ *
+ * finally nodes are TOK_LC statement lists.
+ */
+
+ Node innerBlock;
+ {
+ MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
+
+ ParseContext::Statement stmt(pc, StatementKind::Try);
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ innerBlock = statementList(yieldHandling);
+ if (!innerBlock)
+ return null();
+
+ innerBlock = finishLexicalScope(scope, innerBlock);
+ if (!innerBlock)
+ return null();
+
+ MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_TRY);
+ }
+
+ bool hasUnconditionalCatch = false;
+ Node catchList = null();
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt == TOK_CATCH) {
+ catchList = handler.newCatchList();
+ if (!catchList)
+ return null();
+
+ do {
+ Node pnblock;
+
+ /* Check for another catch after unconditional catch. */
+ if (hasUnconditionalCatch) {
+ report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL);
+ return null();
+ }
+
+ /*
+ * Create a lexical scope node around the whole catch clause,
+ * including the head.
+ */
+ ParseContext::Statement stmt(pc, StatementKind::Catch);
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ /*
+ * Legal catch forms are:
+ * catch (lhs)
+ * catch (lhs if <boolean_expression>)
+ * where lhs is a name or a destructuring left-hand side.
+ * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
+ */
+ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+ Node catchName;
+ switch (tt) {
+ case TOK_LB:
+ case TOK_LC:
+ catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
+ yieldHandling, tt);
+ if (!catchName)
+ return null();
+ break;
+
+ case TOK_NAME:
+ case TOK_YIELD: {
+ RootedPropertyName param(context, bindingIdentifier(yieldHandling));
+ if (!param)
+ return null();
+ catchName = newName(param);
+ if (!catchName)
+ return null();
+ if (!noteDeclaredName(param, DeclarationKind::SimpleCatchParameter, pos()))
+ return null();
+ break;
+ }
+
+ default:
+ report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER);
+ return null();
+ }
+
+ Node catchGuard = null();
+#if JS_HAS_CATCH_GUARD
+ /*
+ * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
+ * to avoid conflicting with the JS2/ECMAv4 type annotation
+ * catchguard syntax.
+ */
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_IF))
+ return null();
+ if (matched) {
+ catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!catchGuard)
+ return null();
+ }
+#endif
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
+
+ MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
+
+ Node catchBody = catchBlockStatement(yieldHandling, scope);
+ if (!catchBody)
+ return null();
+
+ if (!catchGuard)
+ hasUnconditionalCatch = true;
+
+ pnblock = finishLexicalScope(scope, catchBody);
+ if (!pnblock)
+ return null();
+
+ if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
+ return null();
+ handler.setEndPosition(catchList, pos().end);
+ handler.setEndPosition(pnblock, pos().end);
+
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+ } while (tt == TOK_CATCH);
+ }
+
+ Node finallyBlock = null();
+
+ if (tt == TOK_FINALLY) {
+ MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
+
+ ParseContext::Statement stmt(pc, StatementKind::Finally);
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ finallyBlock = statementList(yieldHandling);
+ if (!finallyBlock)
+ return null();
+
+ finallyBlock = finishLexicalScope(scope, finallyBlock);
+ if (!finallyBlock)
+ return null();
+
+ MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_FINALLY);
+ } else {
+ tokenStream.ungetToken();
+ }
+ if (!catchList && !finallyBlock) {
+ report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY);
+ return null();
+ }
+
+ return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
+ ParseContext::Scope& catchParamScope)
+{
+ ParseContext::Statement stmt(pc, StatementKind::Block);
+
+ // ES 13.15.7 CatchClauseEvaluation
+ //
+ // Step 8 means that the body of a catch block always has an additional
+ // lexical scope.
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ // The catch parameter names cannot be redeclared inside the catch
+ // block, so declare the name in the inner scope.
+ if (!scope.addCatchParameters(pc, catchParamScope))
+ return null();
+
+ Node list = statementList(yieldHandling);
+ if (!list)
+ return null();
+
+ MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH);
+
+ // The catch parameter names are not bound in the body scope, so remove
+ // them before generating bindings.
+ scope.removeCatchParameters(pc, catchParamScope);
+ return finishLexicalScope(scope, list);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::debuggerStatement()
+{
+ TokenPos p;
+ p.begin = pos().begin;
+ if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ return null();
+ p.end = pos().end;
+
+ pc->sc()->setBindingsAccessedDynamically();
+ pc->sc()->setHasDebuggerStatement();
+
+ return handler.newDebuggerStatement(p);
+}
+
+static JSOp
+JSOpFromPropertyType(PropertyType propType)
+{
+ switch (propType) {
+ case PropertyType::Getter:
+ case PropertyType::GetterNoExpressionClosure:
+ return JSOP_INITPROP_GETTER;
+ case PropertyType::Setter:
+ case PropertyType::SetterNoExpressionClosure:
+ return JSOP_INITPROP_SETTER;
+ case PropertyType::Normal:
+ case PropertyType::Method:
+ case PropertyType::GeneratorMethod:
+ case PropertyType::AsyncMethod:
+ case PropertyType::Constructor:
+ case PropertyType::DerivedConstructor:
+ return JSOP_INITPROP;
+ default:
+ MOZ_CRASH("unexpected property type");
+ }
+}
+
+static FunctionSyntaxKind
+FunctionSyntaxKindFromPropertyType(PropertyType propType)
+{
+ switch (propType) {
+ case PropertyType::Getter:
+ return Getter;
+ case PropertyType::GetterNoExpressionClosure:
+ return GetterNoExpressionClosure;
+ case PropertyType::Setter:
+ return Setter;
+ case PropertyType::SetterNoExpressionClosure:
+ return SetterNoExpressionClosure;
+ case PropertyType::Method:
+ case PropertyType::GeneratorMethod:
+ case PropertyType::AsyncMethod:
+ return Method;
+ case PropertyType::Constructor:
+ return ClassConstructor;
+ case PropertyType::DerivedConstructor:
+ return DerivedClassConstructor;
+ default:
+ MOZ_CRASH("unexpected property type");
+ }
+}
+
+static GeneratorKind
+GeneratorKindFromPropertyType(PropertyType propType)
+{
+ if (propType == PropertyType::GeneratorMethod)
+ return StarGenerator;
+ if (propType == PropertyType::AsyncMethod)
+ return StarGenerator;
+ return NotGenerator;
+}
+
+static FunctionAsyncKind
+AsyncKindFromPropertyType(PropertyType propType)
+{
+ if (propType == PropertyType::AsyncMethod)
+ return AsyncFunction;
+ return SyncFunction;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
+ ClassContext classContext,
+ DefaultHandling defaultHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+
+ bool savedStrictness = setLocalStrictMode(true);
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ RootedPropertyName name(context);
+ if (tt == TOK_NAME || tt == TOK_YIELD) {
+ name = bindingIdentifier(yieldHandling);
+ if (!name)
+ return null();
+ } else if (classContext == ClassStatement) {
+ if (defaultHandling == AllowDefaultName) {
+ name = context->names().starDefaultStar;
+ tokenStream.ungetToken();
+ } else {
+ // Class statements must have a bound name
+ report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT);
+ return null();
+ }
+ } else {
+ // Make sure to put it back, whatever it was
+ tokenStream.ungetToken();
+ }
+
+ RootedAtom propAtom(context);
+
+ // A named class creates a new lexical scope with a const binding of the
+ // class name.
+ Maybe<ParseContext::Statement> classStmt;
+ Maybe<ParseContext::Scope> classScope;
+ if (name) {
+ classStmt.emplace(pc, StatementKind::Block);
+ classScope.emplace(this);
+ if (!classScope->init(pc))
+ return null();
+ }
+
+ // Because the binding definitions keep track of their blockId, we need to
+ // create at least the inner binding later. Keep track of the name's position
+ // in order to provide it for the nodes created later.
+ TokenPos namePos = pos();
+
+ Node classHeritage = null();
+ bool hasHeritage;
+ if (!tokenStream.matchToken(&hasHeritage, TOK_EXTENDS))
+ return null();
+ if (hasHeritage) {
+ if (!tokenStream.getToken(&tt))
+ return null();
+ classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt);
+ if (!classHeritage)
+ return null();
+ }
+
+ MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
+
+ Node classMethods = handler.newClassMethodList(pos().begin);
+ if (!classMethods)
+ return null();
+
+ bool seenConstructor = false;
+ for (;;) {
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ return null();
+ if (tt == TOK_RC)
+ break;
+
+ if (tt == TOK_SEMI)
+ continue;
+
+ bool isStatic = false;
+ if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) {
+ if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
+ return null();
+ if (tt == TOK_RC) {
+ tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "property name", TokenKindToDesc(tt));
+ return null();
+ }
+
+ if (tt != TOK_LP) {
+ if (!checkUnescapedName())
+ return null();
+
+ isStatic = true;
+ } else {
+ tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
+ tokenStream.ungetToken();
+ }
+ } else {
+ tokenStream.ungetToken();
+ }
+
+ PropertyType propType;
+ Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom);
+ if (!propName)
+ return null();
+
+ if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
+ propType != PropertyType::Method && propType != PropertyType::GeneratorMethod &&
+ propType != PropertyType::AsyncMethod &&
+ propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor)
+ {
+ report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
+ return null();
+ }
+
+ if (propType == PropertyType::Getter)
+ propType = PropertyType::GetterNoExpressionClosure;
+ if (propType == PropertyType::Setter)
+ propType = PropertyType::SetterNoExpressionClosure;
+ if (!isStatic && propAtom == context->names().constructor) {
+ if (propType != PropertyType::Method) {
+ report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF);
+ return null();
+ }
+ if (seenConstructor) {
+ report(ParseError, false, propName, JSMSG_DUPLICATE_PROPERTY, "constructor");
+ return null();
+ }
+ seenConstructor = true;
+ propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor;
+ } else if (isStatic && propAtom == context->names().prototype) {
+ report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF);
+ return null();
+ }
+
+ // FIXME: Implement ES6 function "name" property semantics
+ // (bug 883377).
+ RootedAtom funName(context);
+ switch (propType) {
+ case PropertyType::GetterNoExpressionClosure:
+ case PropertyType::SetterNoExpressionClosure:
+ if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+ funName = prefixAccessorName(propType, propAtom);
+ if (!funName)
+ return null();
+ }
+ break;
+ case PropertyType::Constructor:
+ case PropertyType::DerivedConstructor:
+ funName = name;
+ break;
+ default:
+ if (!tokenStream.isCurrentTokenType(TOK_RB))
+ funName = propAtom;
+ }
+ Node fn = methodDefinition(propType, funName);
+ if (!fn)
+ return null();
+
+ JSOp op = JSOpFromPropertyType(propType);
+ if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic))
+ return null();
+ }
+
+ Node nameNode = null();
+ Node methodsOrBlock = classMethods;
+ if (name) {
+ // The inner name is immutable.
+ if (!noteDeclaredName(name, DeclarationKind::Const, namePos))
+ return null();
+
+ Node innerName = newName(name, namePos);
+ if (!innerName)
+ return null();
+
+ Node classBlock = finishLexicalScope(*classScope, classMethods);
+ if (!classBlock)
+ return null();
+
+ methodsOrBlock = classBlock;
+
+ // Pop the inner scope.
+ classScope.reset();
+ classStmt.reset();
+
+ Node outerName = null();
+ if (classContext == ClassStatement) {
+ // The outer name is mutable.
+ if (!noteDeclaredName(name, DeclarationKind::Let, namePos))
+ return null();
+
+ outerName = newName(name, namePos);
+ if (!outerName)
+ return null();
+ }
+
+ nameNode = handler.newClassNames(outerName, innerName, namePos);
+ if (!nameNode)
+ return null();
+ }
+
+ MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
+
+ return handler.newClass(nameNode, classHeritage, methodsOrBlock);
+}
+
+template <class ParseHandler>
+bool
+Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
+ MOZ_ASSERT(tokenStream.currentName() == context->names().let);
+ MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
+
+#ifdef DEBUG
+ TokenKind verify;
+ MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
+ MOZ_ASSERT(next == verify);
+#endif
+
+ // Destructuring is (for once) the easy case.
+ if (next == TOK_LB || next == TOK_LC)
+ return true;
+
+ // Otherwise a let declaration must have a name.
+ if (next == TOK_NAME) {
+ if (tokenStream.nextName() == context->names().yield) {
+ MOZ_ASSERT(tokenStream.nextNameContainsEscape(),
+ "token stream should interpret unescaped 'yield' as TOK_YIELD");
+
+ // Same as |next == TOK_YIELD|.
+ return yieldHandling == YieldIsName;
+ }
+
+ // One non-"yield" TOK_NAME edge case deserves special comment.
+ // Consider this:
+ //
+ // let // not an ASI opportunity
+ // let;
+ //
+ // Static semantics in §13.3.1.1 turn a LexicalDeclaration that binds
+ // "let" into an early error. Does this retroactively permit ASI so
+ // that we should parse this as two ExpressionStatements? No. ASI
+ // resolves during parsing. Static semantics only apply to the full
+ // parse tree with ASI applied. No backsies!
+ return true;
+ }
+
+ // If we have the name "yield", the grammar parameter exactly states
+ // whether this is okay. (This wasn't true for SpiderMonkey's ancient
+ // legacy generator syntax, but that's dead now.) If YieldIsName,
+ // declaration-parsing code will (if necessary) enforce a strict mode
+ // restriction on defining "yield". If YieldIsKeyword, consider this the
+ // end of the declaration, in case ASI induces a semicolon that makes the
+ // "yield" valid.
+ if (next == TOK_YIELD)
+ return yieldHandling == YieldIsName;
+
+ // Otherwise not a let declaration.
+ return false;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
+{
+ Node vars = declarationList(yieldHandling, PNK_VAR);
+ if (!vars)
+ return null();
+ if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ return null();
+ return vars;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::statement(YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(checkOptionsCalled);
+
+ JS_CHECK_RECURSION(context, return null());
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ switch (tt) {
+ // BlockStatement[?Yield, ?Return]
+ case TOK_LC:
+ return blockStatement(yieldHandling);
+
+ // VariableStatement[?Yield]
+ case TOK_VAR:
+ return variableStatement(yieldHandling);
+
+ // EmptyStatement
+ case TOK_SEMI:
+ return handler.newEmptyStatement(pos());
+
+ // ExpressionStatement[?Yield].
+
+ case TOK_YIELD: {
+ // Don't use a ternary operator here due to obscure linker issues
+ // around using static consts in the arms of a ternary.
+ TokenStream::Modifier modifier;
+ if (yieldExpressionsSupported())
+ modifier = TokenStream::Operand;
+ else
+ modifier = TokenStream::None;
+
+ TokenKind next;
+ if (!tokenStream.peekToken(&next, modifier))
+ return null();
+
+ if (next == TOK_COLON)
+ return labeledStatement(yieldHandling);
+
+ return expressionStatement(yieldHandling);
+ }
+
+ case TOK_NAME: {
+ TokenKind next;
+ if (!tokenStream.peekToken(&next))
+ return null();
+
+ // |let| here can only be an Identifier, not a declaration. Give nicer
+ // errors for declaration-looking typos.
+ if (!tokenStream.currentToken().nameContainsEscape() &&
+ tokenStream.currentName() == context->names().let)
+ {
+ bool forbiddenLetDeclaration = false;
+
+ if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) {
+ // |let| can't be an Identifier in strict mode code. Ditto for
+ // non-standard JavaScript 1.7+.
+ forbiddenLetDeclaration = true;
+ } else if (next == TOK_LB) {
+ // Enforce ExpressionStatement's 'let [' lookahead restriction.
+ forbiddenLetDeclaration = true;
+ } else if (next == TOK_LC || next == TOK_NAME) {
+ // 'let {' and 'let foo' aren't completely forbidden, if ASI
+ // causes 'let' to be the entire Statement. But if they're
+ // same-line, we can aggressively give a better error message.
+ //
+ // Note that this ignores 'yield' as TOK_YIELD: we'll handle it
+ // correctly but with a worse error message.
+ TokenKind nextSameLine;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ MOZ_ASSERT(nextSameLine == TOK_NAME ||
+ nextSameLine == TOK_LC ||
+ nextSameLine == TOK_EOL);
+
+ forbiddenLetDeclaration = nextSameLine != TOK_EOL;
+ }
+
+ if (forbiddenLetDeclaration) {
+ report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT,
+ "lexical declarations");
+ return null();
+ }
+ }
+
+ // NOTE: It's unfortunately allowed to have a label named 'let' in
+ // non-strict code. 💯
+ if (next == TOK_COLON)
+ return labeledStatement(yieldHandling);
+
+ return expressionStatement(yieldHandling);
+ }
+
+ case TOK_NEW:
+ return expressionStatement(yieldHandling, PredictInvoked);
+
+ default:
+ return expressionStatement(yieldHandling);
+
+ // IfStatement[?Yield, ?Return]
+ case TOK_IF:
+ return ifStatement(yieldHandling);
+
+ // BreakableStatement[?Yield, ?Return]
+ //
+ // BreakableStatement[Yield, Return]:
+ // IterationStatement[?Yield, ?Return]
+ // SwitchStatement[?Yield, ?Return]
+ case TOK_DO:
+ return doWhileStatement(yieldHandling);
+
+ case TOK_WHILE:
+ return whileStatement(yieldHandling);
+
+ case TOK_FOR:
+ return forStatement(yieldHandling);
+
+ case TOK_SWITCH:
+ return switchStatement(yieldHandling);
+
+ // ContinueStatement[?Yield]
+ case TOK_CONTINUE:
+ return continueStatement(yieldHandling);
+
+ // BreakStatement[?Yield]
+ case TOK_BREAK:
+ return breakStatement(yieldHandling);
+
+ // [+Return] ReturnStatement[?Yield]
+ case TOK_RETURN:
+ // The Return parameter is only used here, and the effect is easily
+ // detected this way, so don't bother passing around an extra parameter
+ // everywhere.
+ if (!pc->isFunctionBox()) {
+ report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
+ return null();
+ }
+ return returnStatement(yieldHandling);
+
+ // WithStatement[?Yield, ?Return]
+ case TOK_WITH:
+ return withStatement(yieldHandling);
+
+ // LabelledStatement[?Yield, ?Return]
+ // This is really handled by TOK_NAME and TOK_YIELD cases above.
+
+ // ThrowStatement[?Yield]
+ case TOK_THROW:
+ return throwStatement(yieldHandling);
+
+ // TryStatement[?Yield, ?Return]
+ case TOK_TRY:
+ return tryStatement(yieldHandling);
+
+ // DebuggerStatement
+ case TOK_DEBUGGER:
+ return debuggerStatement();
+
+ // |function| is forbidden by lookahead restriction (unless as child
+ // statement of |if| or |else|, but Parser::consequentOrAlternative
+ // handles that).
+ case TOK_FUNCTION:
+ report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
+ return null();
+
+ // |class| is also forbidden by lookahead restriction.
+ case TOK_CLASS:
+ report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "classes");
+ return null();
+
+ // ImportDeclaration (only inside modules)
+ case TOK_IMPORT:
+ return importDeclaration();
+
+ // ExportDeclaration (only inside modules)
+ case TOK_EXPORT:
+ return exportDeclaration();
+
+ // Miscellaneous error cases arguably better caught here than elsewhere.
+
+ case TOK_CATCH:
+ report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
+ return null();
+
+ case TOK_FINALLY:
+ report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
+ return null();
+
+ // NOTE: default case handled in the ExpressionStatement section.
+ }
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
+ bool canHaveDirectives /* = false */)
+{
+ MOZ_ASSERT(checkOptionsCalled);
+
+ JS_CHECK_RECURSION(context, return null());
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ switch (tt) {
+ // BlockStatement[?Yield, ?Return]
+ case TOK_LC:
+ return blockStatement(yieldHandling);
+
+ // VariableStatement[?Yield]
+ case TOK_VAR:
+ return variableStatement(yieldHandling);
+
+ // EmptyStatement
+ case TOK_SEMI:
+ return handler.newEmptyStatement(pos());
+
+ // ExpressionStatement[?Yield].
+ //
+ // These should probably be handled by a single ExpressionStatement
+ // function in a default, not split up this way.
+ case TOK_STRING:
+ if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
+ if (!abortIfSyntaxParser())
+ return null();
+ if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
+ return null();
+ }
+ return expressionStatement(yieldHandling);
+
+ case TOK_YIELD: {
+ // Don't use a ternary operator here due to obscure linker issues
+ // around using static consts in the arms of a ternary.
+ TokenStream::Modifier modifier;
+ if (yieldExpressionsSupported())
+ modifier = TokenStream::Operand;
+ else
+ modifier = TokenStream::None;
+
+ TokenKind next;
+ if (!tokenStream.peekToken(&next, modifier))
+ return null();
+
+ if (next == TOK_COLON)
+ return labeledStatement(yieldHandling);
+
+ return expressionStatement(yieldHandling);
+ }
+
+ case TOK_NAME: {
+ TokenKind next;
+ if (!tokenStream.peekToken(&next))
+ return null();
+
+ if (!tokenStream.currentToken().nameContainsEscape() &&
+ tokenStream.currentName() == context->names().let &&
+ nextTokenContinuesLetDeclaration(next, yieldHandling))
+ {
+ return lexicalDeclaration(yieldHandling, /* isConst = */ false);
+ }
+
+ if (tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+ if (nextSameLine == TOK_FUNCTION) {
+ tokenStream.consumeKnownToken(TOK_FUNCTION);
+ return functionStmt(yieldHandling, NameRequired, AsyncFunction);
+ }
+ }
+
+ if (next == TOK_COLON)
+ return labeledStatement(yieldHandling);
+
+ return expressionStatement(yieldHandling);
+ }
+
+ case TOK_NEW:
+ return expressionStatement(yieldHandling, PredictInvoked);
+
+ default:
+ return expressionStatement(yieldHandling);
+
+ // IfStatement[?Yield, ?Return]
+ case TOK_IF:
+ return ifStatement(yieldHandling);
+
+ // BreakableStatement[?Yield, ?Return]
+ //
+ // BreakableStatement[Yield, Return]:
+ // IterationStatement[?Yield, ?Return]
+ // SwitchStatement[?Yield, ?Return]
+ case TOK_DO:
+ return doWhileStatement(yieldHandling);
+
+ case TOK_WHILE:
+ return whileStatement(yieldHandling);
+
+ case TOK_FOR:
+ return forStatement(yieldHandling);
+
+ case TOK_SWITCH:
+ return switchStatement(yieldHandling);
+
+ // ContinueStatement[?Yield]
+ case TOK_CONTINUE:
+ return continueStatement(yieldHandling);
+
+ // BreakStatement[?Yield]
+ case TOK_BREAK:
+ return breakStatement(yieldHandling);
+
+ // [+Return] ReturnStatement[?Yield]
+ case TOK_RETURN:
+ // The Return parameter is only used here, and the effect is easily
+ // detected this way, so don't bother passing around an extra parameter
+ // everywhere.
+ if (!pc->isFunctionBox()) {
+ report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
+ return null();
+ }
+ return returnStatement(yieldHandling);
+
+ // WithStatement[?Yield, ?Return]
+ case TOK_WITH:
+ return withStatement(yieldHandling);
+
+ // LabelledStatement[?Yield, ?Return]
+ // This is really handled by TOK_NAME and TOK_YIELD cases above.
+
+ // ThrowStatement[?Yield]
+ case TOK_THROW:
+ return throwStatement(yieldHandling);
+
+ // TryStatement[?Yield, ?Return]
+ case TOK_TRY:
+ return tryStatement(yieldHandling);
+
+ // DebuggerStatement
+ case TOK_DEBUGGER:
+ return debuggerStatement();
+
+ // Declaration[Yield]:
+
+ // HoistableDeclaration[?Yield, ~Default]
+ case TOK_FUNCTION:
+ return functionStmt(yieldHandling, NameRequired);
+
+ // ClassDeclaration[?Yield, ~Default]
+ case TOK_CLASS:
+ return classDefinition(yieldHandling, ClassStatement, NameRequired);
+
+ // LexicalDeclaration[In, ?Yield]
+ // LetOrConst BindingList[?In, ?Yield]
+ case TOK_CONST:
+ // [In] is the default behavior, because for-loops specially parse
+ // their heads to handle |in| in this situation.
+ return lexicalDeclaration(yieldHandling, /* isConst = */ true);
+
+ // ImportDeclaration (only inside modules)
+ case TOK_IMPORT:
+ return importDeclaration();
+
+ // ExportDeclaration (only inside modules)
+ case TOK_EXPORT:
+ return exportDeclaration();
+
+ // Miscellaneous error cases arguably better caught here than elsewhere.
+
+ case TOK_CATCH:
+ report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
+ return null();
+
+ case TOK_FINALLY:
+ report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
+ return null();
+
+ // NOTE: default case handled in the ExpressionStatement section.
+ }
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError /* = nullptr */,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
+ possibleError, invoked);
+ if (!pn)
+ return null();
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ if (!matched)
+ return pn;
+
+ Node seq = handler.newCommaExpressionList(pn);
+ if (!seq)
+ return null();
+ while (true) {
+ // Trailing comma before the closing parenthesis is valid in an arrow
+ // function parameters list: `(a, b, ) => body`. Check if we are
+ // directly under CoverParenthesizedExpressionAndArrowParameterList,
+ // and the next two tokens are closing parenthesis and arrow. If all
+ // are present allow the trailing comma.
+ if (tripledotHandling == TripledotAllowed) {
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+
+ if (tt == TOK_RP) {
+ tokenStream.consumeKnownToken(TOK_RP, TokenStream::Operand);
+
+ if (!tokenStream.peekToken(&tt))
+ return null();
+ if (tt != TOK_ARROW) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "expression", TokenKindToDesc(TOK_RP));
+ return null();
+ }
+
+ tokenStream.ungetToken(); // put back right paren
+ tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ break;
+ }
+ }
+
+ // Additional calls to assignExpr should not reuse the possibleError
+ // which had been passed into the function. Otherwise we would lose
+ // information needed to determine whether or not we're dealing with
+ // a non-recoverable situation.
+ PossibleError possibleErrorInner(*this);
+ pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
+ &possibleErrorInner);
+ if (!pn)
+ return null();
+
+ if (!possibleError) {
+ // Report any pending expression error.
+ if (!possibleErrorInner.checkForExpressionError())
+ return null();
+ } else {
+ possibleErrorInner.transferErrorsTo(possibleError);
+ }
+
+ handler.addList(seq, pn);
+
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ if (!matched)
+ break;
+ }
+ return seq;
+}
+
+static const JSOp ParseNodeKindToJSOp[] = {
+ JSOP_OR,
+ JSOP_AND,
+ JSOP_BITOR,
+ JSOP_BITXOR,
+ JSOP_BITAND,
+ JSOP_STRICTEQ,
+ JSOP_EQ,
+ JSOP_STRICTNE,
+ JSOP_NE,
+ JSOP_LT,
+ JSOP_LE,
+ JSOP_GT,
+ JSOP_GE,
+ JSOP_INSTANCEOF,
+ JSOP_IN,
+ JSOP_LSH,
+ JSOP_RSH,
+ JSOP_URSH,
+ JSOP_ADD,
+ JSOP_SUB,
+ JSOP_MUL,
+ JSOP_DIV,
+ JSOP_MOD,
+ JSOP_POW
+};
+
+static inline JSOp
+BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
+{
+ MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
+ MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
+ return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST];
+}
+
+static ParseNodeKind
+BinaryOpTokenKindToParseNodeKind(TokenKind tok)
+{
+ MOZ_ASSERT(TokenKindIsBinaryOp(tok));
+ return ParseNodeKind(PNK_BINOP_FIRST + (tok - TOK_BINOP_FIRST));
+}
+
+static const int PrecedenceTable[] = {
+ 1, /* PNK_OR */
+ 2, /* PNK_AND */
+ 3, /* PNK_BITOR */
+ 4, /* PNK_BITXOR */
+ 5, /* PNK_BITAND */
+ 6, /* PNK_STRICTEQ */
+ 6, /* PNK_EQ */
+ 6, /* PNK_STRICTNE */
+ 6, /* PNK_NE */
+ 7, /* PNK_LT */
+ 7, /* PNK_LE */
+ 7, /* PNK_GT */
+ 7, /* PNK_GE */
+ 7, /* PNK_INSTANCEOF */
+ 7, /* PNK_IN */
+ 8, /* PNK_LSH */
+ 8, /* PNK_RSH */
+ 8, /* PNK_URSH */
+ 9, /* PNK_ADD */
+ 9, /* PNK_SUB */
+ 10, /* PNK_STAR */
+ 10, /* PNK_DIV */
+ 10, /* PNK_MOD */
+ 11 /* PNK_POW */
+};
+
+static const int PRECEDENCE_CLASSES = 11;
+
+static int
+Precedence(ParseNodeKind pnk) {
+ // Everything binds tighter than PNK_LIMIT, because we want to reduce all
+ // nodes to a single node when we reach a token that is not another binary
+ // operator.
+ if (pnk == PNK_LIMIT)
+ return 0;
+
+ MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
+ MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
+ return PrecedenceTable[pnk - PNK_BINOP_FIRST];
+}
+
+template <typename ParseHandler>
+MOZ_ALWAYS_INLINE typename ParseHandler::Node
+Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ // Shift-reduce parser for the binary operator part of the JS expression
+ // syntax.
+
+ // Conceptually there's just one stack, a stack of pairs (lhs, op).
+ // It's implemented using two separate arrays, though.
+ Node nodeStack[PRECEDENCE_CLASSES];
+ ParseNodeKind kindStack[PRECEDENCE_CLASSES];
+ int depth = 0;
+ Node pn;
+ for (;;) {
+ pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
+ if (!pn)
+ return pn;
+
+ // If a binary operator follows, consume it and compute the
+ // corresponding operator.
+ TokenKind tok;
+ if (!tokenStream.getToken(&tok))
+ return null();
+
+ ParseNodeKind pnk;
+ if (tok == TOK_IN ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) {
+ // We're definitely not in a destructuring context, so report any
+ // pending expression error now.
+ if (possibleError && !possibleError->checkForExpressionError())
+ return null();
+ // Report an error for unary expressions on the LHS of **.
+ if (tok == TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) {
+ report(ParseError, false, null(), JSMSG_BAD_POW_LEFTSIDE);
+ return null();
+ }
+ pnk = BinaryOpTokenKindToParseNodeKind(tok);
+ } else {
+ tok = TOK_EOF;
+ pnk = PNK_LIMIT;
+ }
+
+ // From this point on, destructuring defaults are definitely an error.
+ possibleError = nullptr;
+
+ // If pnk has precedence less than or equal to another operator on the
+ // stack, reduce. This combines nodes on the stack until we form the
+ // actual lhs of pnk.
+ //
+ // The >= in this condition works because it is appendOrCreateList's
+ // job to decide if the operator in question is left- or
+ // right-associative, and build the corresponding tree.
+ while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
+ depth--;
+ ParseNodeKind combiningPnk = kindStack[depth];
+ JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
+ pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
+ if (!pn)
+ return pn;
+ }
+
+ if (pnk == PNK_LIMIT)
+ break;
+
+ nodeStack[depth] = pn;
+ kindStack[depth] = pnk;
+ depth++;
+ MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
+ }
+
+ MOZ_ASSERT(depth == 0);
+ return pn;
+}
+
+template <typename ParseHandler>
+MOZ_ALWAYS_INLINE typename ParseHandler::Node
+Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, possibleError, invoked);
+
+ if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
+ return condition;
+
+ Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!thenExpr)
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
+
+ Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+ if (!elseExpr)
+ return null();
+
+ // Advance to the next token; the caller is responsible for interpreting it.
+ TokenKind ignored;
+ if (!tokenStream.getToken(&ignored))
+ return null();
+ return handler.newConditional(condition, thenExpr, elseExpr);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor,
+ PossibleError* possibleError)
+{
+ MOZ_ASSERT(flavor != KeyedDestructuringAssignment,
+ "destructuring must use special checking/marking code, not "
+ "this method");
+
+ if (handler.isUnparenthesizedDestructuringPattern(target)) {
+ if (flavor == CompoundAssignment) {
+ report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
+ return false;
+ }
+
+ return checkDestructuringPattern(target, Nothing(), possibleError);
+ }
+
+ // All other permitted targets are simple.
+ if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
+ return false;
+
+ if (handler.isPropertyAccess(target))
+ return true;
+
+ if (handler.isNameAnyParentheses(target)) {
+ // The arguments/eval identifiers are simple in non-strict mode code,
+ // but warn to discourage use nonetheless.
+ if (!reportIfArgumentsEvalTarget(target))
+ return false;
+
+ handler.adjustGetToSet(target);
+ return true;
+ }
+
+ MOZ_ASSERT(handler.isFunctionCall(target));
+ return checkAssignmentToCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
+}
+
+class AutoClearInDestructuringDecl
+{
+ ParseContext* pc_;
+ Maybe<DeclarationKind> saved_;
+
+ public:
+ explicit AutoClearInDestructuringDecl(ParseContext* pc)
+ : pc_(pc),
+ saved_(pc->inDestructuringDecl)
+ {
+ pc->inDestructuringDecl = Nothing();
+ if (saved_ && *saved_ == DeclarationKind::FormalParameter)
+ pc->functionBox()->hasParameterExprs = true;
+ }
+
+ ~AutoClearInDestructuringDecl() {
+ pc_->inDestructuringDecl = saved_;
+ }
+};
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError /* = nullptr */,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ JS_CHECK_RECURSION(context, return null());
+
+ // It's very common at this point to have a "detectably simple" expression,
+ // i.e. a name/number/string token followed by one of the following tokens
+ // that obviously isn't part of an expression: , ; : ) ] }
+ //
+ // (In Parsemark this happens 81.4% of the time; in code with large
+ // numeric arrays, such as some Kraken benchmarks, it happens more often.)
+ //
+ // In such cases, we can avoid the full expression parsing route through
+ // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
+ // primaryExpr().
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ bool endsExpr;
+
+ if (tt == TOK_NAME) {
+ if (!tokenStream.nextTokenEndsExpr(&endsExpr))
+ return null();
+ if (endsExpr) {
+ Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+ if (!name)
+ return null();
+
+ return identifierReference(name);
+ }
+ }
+
+ if (tt == TOK_NUMBER) {
+ if (!tokenStream.nextTokenEndsExpr(&endsExpr))
+ return null();
+ if (endsExpr)
+ return newNumber(tokenStream.currentToken());
+ }
+
+ if (tt == TOK_STRING) {
+ if (!tokenStream.nextTokenEndsExpr(&endsExpr))
+ return null();
+ if (endsExpr)
+ return stringLiteral();
+ }
+
+ if (tt == TOK_YIELD && yieldExpressionsSupported())
+ return yieldExpression(inHandling);
+
+ bool maybeAsyncArrow = false;
+ if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD)
+ maybeAsyncArrow = true;
+ }
+
+ tokenStream.ungetToken();
+
+ // Save the tokenizer state in case we find an arrow function and have to
+ // rewind.
+ TokenStream::Position start(keepAtoms);
+ tokenStream.tell(&start);
+
+ PossibleError possibleErrorInner(*this);
+ Node lhs;
+ if (maybeAsyncArrow) {
+ tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
+ MOZ_ASSERT(tokenStream.currentName() == context->names().async);
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD);
+
+ // Check yield validity here.
+ RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+ if (!name)
+ return null();
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt != TOK_ARROW) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "'=>' after argument list", TokenKindToDesc(tt));
+
+ return null();
+ }
+ } else {
+ lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
+ if (!lhs) {
+ return null();
+ }
+ }
+
+ ParseNodeKind kind;
+ JSOp op;
+ switch (tokenStream.currentToken().type) {
+ case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break;
+ case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break;
+ case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break;
+ case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break;
+ case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
+ case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
+ case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; op = JSOP_LSH; break;
+ case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; op = JSOP_RSH; break;
+ case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; op = JSOP_URSH; break;
+ case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break;
+ case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break;
+ case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break;
+ case TOK_POWASSIGN: kind = PNK_POWASSIGN; op = JSOP_POW; break;
+
+ case TOK_ARROW: {
+
+ // A line terminator between ArrowParameters and the => should trigger a SyntaxError.
+ tokenStream.ungetToken();
+ TokenKind next;
+ if (!tokenStream.peekTokenSameLine(&next))
+ return null();
+ MOZ_ASSERT(next == TOK_ARROW || next == TOK_EOL);
+
+ if (next != TOK_ARROW) {
+ report(ParseError, false, null(), JSMSG_LINE_BREAK_BEFORE_ARROW);
+ return null();
+ }
+ tokenStream.consumeKnownToken(TOK_ARROW);
+
+ bool isBlock = false;
+ if (!tokenStream.peekToken(&next, TokenStream::Operand))
+ return null();
+ if (next == TOK_LC)
+ isBlock = true;
+
+ tokenStream.seek(start);
+
+ if (!tokenStream.peekToken(&next, TokenStream::Operand))
+ return null();
+
+ GeneratorKind generatorKind = NotGenerator;
+ FunctionAsyncKind asyncKind = SyncFunction;
+
+ if (next == TOK_NAME) {
+ tokenStream.consumeKnownToken(next, TokenStream::Operand);
+
+ if (tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ if (nextSameLine == TOK_ARROW) {
+ tokenStream.ungetToken();
+ } else {
+ generatorKind = StarGenerator;
+ asyncKind = AsyncFunction;
+ }
+ } else {
+ tokenStream.ungetToken();
+ }
+ }
+
+ Node arrowFunc = functionDefinition(inHandling, yieldHandling, nullptr,
+ Arrow, generatorKind, asyncKind);
+ if (!arrowFunc)
+ return null();
+
+ if (isBlock) {
+ // This arrow function could be a non-trailing member of a comma
+ // expression or a semicolon terminating a full expression. If so,
+ // the next token is that comma/semicolon, gotten with None:
+ //
+ // a => {}, b; // as if (a => {}), b;
+ // a => {};
+ //
+ // But if this arrow function ends a statement, ASI permits the
+ // next token to start an expression statement. In that case the
+ // next token must be gotten as Operand:
+ //
+ // a => {} // complete expression statement
+ // /x/g; // regular expression as a statement, *not* division
+ //
+ // Getting the second case right requires the first token-peek
+ // after the arrow function use Operand, and that peek must occur
+ // before Parser::expr() looks for a comma. Do so here, then
+ // immediately add the modifier exception needed for the first
+ // case.
+ //
+ // Note that the second case occurs *only* if the arrow function
+ // has block body. An arrow function not ending in such, ends in
+ // another AssignmentExpression that we can inductively assume was
+ // peeked consistently.
+ TokenKind ignored;
+ if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
+ return null();
+ tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ }
+ return arrowFunc;
+ }
+
+ default:
+ MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
+ if (!possibleError) {
+ if (!possibleErrorInner.checkForExpressionError())
+ return null();
+ } else {
+ possibleErrorInner.transferErrorsTo(possibleError);
+ }
+ tokenStream.ungetToken();
+ return lhs;
+ }
+
+ AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
+ if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner))
+ return null();
+ if (!possibleErrorInner.checkForExpressionError())
+ return null();
+
+ Node rhs;
+ {
+ AutoClearInDestructuringDecl autoClear(pc);
+ rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+ if (!rhs)
+ return null();
+ }
+
+ return handler.newAssignment(kind, lhs, rhs, op);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node,
+ FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
+{
+ // Note that this method implements *only* a boolean test. Reporting an
+ // error for the various syntaxes that fail this, and warning for the
+ // various syntaxes that "pass" this but should not, occurs elsewhere.
+
+ if (handler.isNameAnyParentheses(node)) {
+ if (!pc->sc()->strict())
+ return true;
+
+ return !handler.nameIsArgumentsEvalAnyParentheses(node, context);
+ }
+
+ if (handler.isPropertyAccess(node))
+ return true;
+
+ if (behavior == PermitAssignmentToFunctionCalls) {
+ if (handler.isFunctionCall(node))
+ return true;
+ }
+
+ return false;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportIfArgumentsEvalTarget(Node nameNode)
+{
+ const char* chars = handler.nameIsArgumentsEvalAnyParentheses(nameNode, context);
+ if (!chars)
+ return true;
+
+ if (!report(ParseStrictError, pc->sc()->strict(), nameNode, JSMSG_BAD_STRICT_ASSIGN, chars))
+ return false;
+
+ MOZ_ASSERT(!pc->sc()->strict(),
+ "an error should have been reported if this was strict mode "
+ "code");
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor)
+{
+ FunctionCallBehavior behavior = flavor == KeyedDestructuringAssignment
+ ? ForbidAssignmentToFunctionCalls
+ : PermitAssignmentToFunctionCalls;
+ if (isValidSimpleAssignmentTarget(target, behavior))
+ return true;
+
+ if (handler.isNameAnyParentheses(target)) {
+ // Use a special error if the target is arguments/eval. This ensures
+ // targeting these names is consistently a SyntaxError (which error numbers
+ // below don't guarantee) while giving us a nicer error message.
+ if (!reportIfArgumentsEvalTarget(target))
+ return false;
+ }
+
+ unsigned errnum = 0;
+ const char* extra = nullptr;
+
+ switch (flavor) {
+ case IncrementAssignment:
+ errnum = JSMSG_BAD_OPERAND;
+ extra = "increment";
+ break;
+
+ case DecrementAssignment:
+ errnum = JSMSG_BAD_OPERAND;
+ extra = "decrement";
+ break;
+
+ case KeyedDestructuringAssignment:
+ errnum = JSMSG_BAD_DESTRUCT_TARGET;
+ break;
+
+ case PlainAssignment:
+ case CompoundAssignment:
+ errnum = JSMSG_BAD_LEFTSIDE_OF_ASS;
+ break;
+
+ case ForInOrOfTarget:
+ errnum = JSMSG_BAD_FOR_LEFTSIDE;
+ break;
+ }
+
+ report(ParseError, pc->sc()->strict(), target, errnum, extra);
+ return false;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkAndMarkAsIncOperand(Node target, AssignmentFlavor flavor)
+{
+ MOZ_ASSERT(flavor == IncrementAssignment || flavor == DecrementAssignment);
+
+ // Check.
+ if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
+ return false;
+
+ // Mark.
+ if (handler.isNameAnyParentheses(target)) {
+ // Assignment to arguments/eval is allowed outside strict mode code,
+ // but it's dodgy. Report a strict warning (error, if werror was set).
+ if (!reportIfArgumentsEvalTarget(target))
+ return false;
+ } else if (handler.isFunctionCall(target)) {
+ if (!checkAssignmentToCall(target, JSMSG_BAD_INCOP_OPERAND))
+ return false;
+ }
+ return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
+ uint32_t begin)
+{
+ Node kid = unaryExpr(yieldHandling, TripledotProhibited);
+ if (!kid)
+ return null();
+ return handler.newUnary(kind, op, begin, kid);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+ PossibleError* possibleError /* = nullptr */,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ JS_CHECK_RECURSION(context, return null());
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+ uint32_t begin = pos().begin;
+ switch (tt) {
+ case TOK_VOID:
+ return unaryOpExpr(yieldHandling, PNK_VOID, JSOP_VOID, begin);
+ case TOK_NOT:
+ return unaryOpExpr(yieldHandling, PNK_NOT, JSOP_NOT, begin);
+ case TOK_BITNOT:
+ return unaryOpExpr(yieldHandling, PNK_BITNOT, JSOP_BITNOT, begin);
+ case TOK_ADD:
+ return unaryOpExpr(yieldHandling, PNK_POS, JSOP_POS, begin);
+ case TOK_SUB:
+ return unaryOpExpr(yieldHandling, PNK_NEG, JSOP_NEG, begin);
+
+ case TOK_TYPEOF: {
+ // The |typeof| operator is specially parsed to distinguish its
+ // application to a name, from its application to a non-name
+ // expression:
+ //
+ // // Looks up the name, doesn't find it and so evaluates to
+ // // "undefined".
+ // assertEq(typeof nonExistentName, "undefined");
+ //
+ // // Evaluates expression, triggering a runtime ReferenceError for
+ // // the undefined name.
+ // typeof (1, nonExistentName);
+ Node kid = unaryExpr(yieldHandling, TripledotProhibited);
+ if (!kid)
+ return null();
+
+ return handler.newTypeof(begin, kid);
+ }
+
+ case TOK_INC:
+ case TOK_DEC:
+ {
+ TokenKind tt2;
+ if (!tokenStream.getToken(&tt2, TokenStream::Operand))
+ return null();
+ Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2);
+ if (!pn2)
+ return null();
+ AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
+ if (!checkAndMarkAsIncOperand(pn2, flavor))
+ return null();
+ return handler.newUpdate((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
+ begin,
+ pn2);
+ }
+
+ case TOK_DELETE: {
+ Node expr = unaryExpr(yieldHandling, TripledotProhibited);
+ if (!expr)
+ return null();
+
+ // Per spec, deleting any unary expression is valid -- it simply
+ // returns true -- except for one case that is illegal in strict mode.
+ if (handler.isNameAnyParentheses(expr)) {
+ if (!report(ParseStrictError, pc->sc()->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
+ return null();
+ pc->sc()->setBindingsAccessedDynamically();
+ }
+
+ return handler.newDelete(begin, expr);
+ }
+
+ case TOK_AWAIT: {
+ if (!pc->isAsync()) {
+ // TOK_AWAIT can be returned in module, even if it's not inside
+ // async function.
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, "await");
+ return null();
+ }
+
+ Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
+ if (!kid)
+ return null();
+ pc->lastAwaitOffset = begin;
+ return newAwaitExpression(begin, kid);
+ }
+
+ default: {
+ Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
+ possibleError, invoked);
+ if (!pn)
+ return null();
+
+ /* Don't look across a newline boundary for a postfix incop. */
+ if (!tokenStream.peekTokenSameLine(&tt))
+ return null();
+ if (tt == TOK_INC || tt == TOK_DEC) {
+ tokenStream.consumeKnownToken(tt);
+ AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
+ if (!checkAndMarkAsIncOperand(pn, flavor))
+ return null();
+ return handler.newUpdate((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
+ begin,
+ pn);
+ }
+ return pn;
+ }
+ }
+}
+
+
+/*** Comprehensions *******************************************************************************
+ *
+ * We currently support two flavors of comprehensions, all deprecated:
+ *
+ * [for (V of OBJ) if (COND) EXPR] // ES6-era array comprehension
+ * (for (V of OBJ) if (COND) EXPR) // ES6-era generator expression
+ *
+ * (These flavors are called "ES6-era" because they were in ES6 draft
+ * specifications for a while. Shortly after this syntax was implemented in SM,
+ * TC39 decided to drop it.)
+ */
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
+{
+ Node genfn = handler.newFunctionDefinition();
+ if (!genfn)
+ return null();
+ handler.setOp(genfn, JSOP_LAMBDA);
+
+ ParseContext* outerpc = pc;
+
+ // If we are off the main thread, the generator meta-objects have
+ // already been created by js::StartOffThreadParseScript, so cx will not
+ // be necessary.
+ RootedObject proto(context);
+ JSContext* cx = context->maybeJSContext();
+ proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
+ if (!proto)
+ return null();
+
+ RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression,
+ StarGenerator, SyncFunction, proto));
+ if (!fun)
+ return null();
+
+ // Create box for fun->object early to root it.
+ Directives directives(/* strict = */ outerpc->sc()->strict());
+ FunctionBox* genFunbox = newFunctionBox(genfn, fun, directives, StarGenerator, SyncFunction,
+ /* tryAnnexB = */ false);
+ if (!genFunbox)
+ return null();
+ genFunbox->isGenexpLambda = true;
+ genFunbox->initWithEnclosingParseContext(outerpc, Expression);
+
+ ParseContext genpc(this, genFunbox, /* newDirectives = */ nullptr);
+ if (!genpc.init())
+ return null();
+ genpc.functionScope().useAsVarScope(&genpc);
+
+ /*
+ * We assume conservatively that any deoptimization flags in pc->sc()
+ * come from the kid. So we propagate these flags into genfn. For code
+ * simplicity we also do not detect if the flags were only set in the
+ * kid and could be removed from pc->sc().
+ */
+ genFunbox->anyCxFlags = outerpc->sc()->anyCxFlags;
+
+ if (!declareDotGeneratorName())
+ return null();
+
+ Node body = handler.newStatementList(TokenPos(begin, pos().end));
+ if (!body)
+ return null();
+
+ Node comp = comprehension(StarGenerator);
+ if (!comp)
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+
+ handler.setBeginPosition(comp, begin);
+ handler.setEndPosition(comp, pos().end);
+ handler.addStatementToList(body, comp);
+ handler.setEndPosition(body, pos().end);
+ handler.setBeginPosition(genfn, begin);
+ handler.setEndPosition(genfn, pos().end);
+
+ Node generator = newDotGeneratorName();
+ if (!generator)
+ return null();
+ if (!handler.prependInitialYield(body, generator))
+ return null();
+
+ if (!propagateFreeNamesAndMarkClosedOverBindings(pc->varScope()))
+ return null();
+ if (!finishFunction())
+ return null();
+ if (!leaveInnerFunction(outerpc))
+ return null();
+
+ // Note that if we ever start syntax-parsing generators, we will also
+ // need to propagate the closed-over variable set to the inner
+ // lazyscript.
+ if (!handler.setComprehensionLambdaBody(genfn, body))
+ return null();
+
+ return genfn;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
+
+ uint32_t begin = pos().begin;
+
+ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
+
+ // FIXME: Destructuring binding (bug 980828).
+
+ MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
+ RootedPropertyName name(context, tokenStream.currentName());
+ if (name == context->names().let) {
+ report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
+ return null();
+ }
+ TokenPos namePos = pos();
+ Node lhs = newName(name);
+ if (!lhs)
+ return null();
+ bool matched;
+ if (!tokenStream.matchContextualKeyword(&matched, context->names().of))
+ return null();
+ if (!matched) {
+ report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
+ return null();
+ }
+
+ Node rhs = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
+ if (!rhs)
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
+
+ TokenPos headPos(begin, pos().end);
+
+ ParseContext::Scope scope(this);
+ if (!scope.init(pc))
+ return null();
+
+ {
+ // Push a temporary ForLoopLexicalHead Statement that allows for
+ // lexical declarations, as they are usually allowed only in braced
+ // statements.
+ ParseContext::Statement forHeadStmt(pc, StatementKind::ForLoopLexicalHead);
+ if (!noteDeclaredName(name, DeclarationKind::Let, namePos))
+ return null();
+ }
+
+ Node decls = handler.newComprehensionBinding(lhs);
+ if (!decls)
+ return null();
+
+ Node tail = comprehensionTail(comprehensionKind);
+ if (!tail)
+ return null();
+
+ // Finish the lexical scope after parsing the tail.
+ Node lexicalScope = finishLexicalScope(scope, decls);
+ if (!lexicalScope)
+ return null();
+
+ Node head = handler.newForInOrOfHead(PNK_FOROF, lexicalScope, rhs, headPos);
+ if (!head)
+ return null();
+
+ return handler.newComprehensionFor(begin, head, tail);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
+
+ uint32_t begin = pos().begin;
+
+ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
+ Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
+ if (!cond)
+ return null();
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
+
+ /* Check for (a = b) and warn about possible (a == b) mistype. */
+ if (handler.isUnparenthesizedAssignment(cond)) {
+ if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+ return null();
+ }
+
+ Node then = comprehensionTail(comprehensionKind);
+ if (!then)
+ return null();
+
+ return handler.newIfStatement(begin, cond, then, null());
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
+{
+ JS_CHECK_RECURSION(context, return null());
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
+ return null();
+ if (matched)
+ return comprehensionFor(comprehensionKind);
+
+ if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
+ return null();
+ if (matched)
+ return comprehensionIf(comprehensionKind);
+
+ uint32_t begin = pos().begin;
+
+ Node bodyExpr = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
+ if (!bodyExpr)
+ return null();
+
+ if (comprehensionKind == NotGenerator)
+ return handler.newArrayPush(begin, bodyExpr);
+
+ MOZ_ASSERT(comprehensionKind == StarGenerator);
+ Node yieldExpr = newYieldExpression(begin, bodyExpr);
+ if (!yieldExpr)
+ return null();
+ yieldExpr = handler.parenthesize(yieldExpr);
+
+ return handler.newExprStatement(yieldExpr, pos().end);
+}
+
+// Parse an ES6-era generator or array comprehension, starting at the first
+// `for`. The caller is responsible for matching the ending TOK_RP or TOK_RB.
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
+
+ uint32_t startYieldOffset = pc->lastYieldOffset;
+
+ Node body = comprehensionFor(comprehensionKind);
+ if (!body)
+ return null();
+
+ if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
+ reportWithOffset(ParseError, false, pc->lastYieldOffset,
+ JSMSG_BAD_GENEXP_BODY, js_yield_str);
+ return null();
+ }
+
+ return body;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::arrayComprehension(uint32_t begin)
+{
+ Node inner = comprehension(NotGenerator);
+ if (!inner)
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
+
+ Node comp = handler.newList(PNK_ARRAYCOMP, inner);
+ if (!comp)
+ return null();
+
+ handler.setBeginPosition(comp, begin);
+ handler.setEndPosition(comp, pos().end);
+
+ return comp;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::generatorComprehension(uint32_t begin)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
+
+ // We have no problem parsing generator comprehensions inside lazy
+ // functions, but the bytecode emitter currently can't handle them that way,
+ // because when it goes to emit the code for the inner generator function,
+ // it expects outer functions to have non-lazy scripts.
+ if (!abortIfSyntaxParser())
+ return null();
+
+ Node genfn = generatorComprehensionLambda(begin);
+ if (!genfn)
+ return null();
+
+ Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL);
+ if (!result)
+ return null();
+ handler.setBeginPosition(result, begin);
+ handler.setEndPosition(result, pos().end);
+
+ return result;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
+{
+ uint32_t startYieldOffset = pc->lastYieldOffset;
+ uint32_t startAwaitOffset = pc->lastAwaitOffset;
+ Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (res) {
+ if (pc->lastYieldOffset != startYieldOffset) {
+ reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
+ return null();
+ }
+ if (pc->lastAwaitOffset != startAwaitOffset) {
+ reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
+ return null();
+ }
+ }
+ return res;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
+ PossibleError* possibleError /* = nullptr */)
+{
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
+ return false;
+ if (matched) {
+ handler.setEndPosition(listNode, pos().end);
+ return true;
+ }
+
+ while (true) {
+ bool spread = false;
+ uint32_t begin = 0;
+ if (!tokenStream.matchToken(&matched, TOK_TRIPLEDOT, TokenStream::Operand))
+ return false;
+ if (matched) {
+ spread = true;
+ begin = pos().begin;
+ *isSpread = true;
+ }
+
+ Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
+ if (!argNode)
+ return false;
+ if (spread) {
+ argNode = handler.newSpread(begin, argNode);
+ if (!argNode)
+ return false;
+ }
+
+ handler.addList(listNode, argNode);
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return false;
+ if (!matched)
+ break;
+
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt == TOK_RP) {
+ tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ break;
+ }
+ }
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return false;
+ if (tt != TOK_RP) {
+ report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
+ return false;
+ }
+ handler.setEndPosition(listNode, pos().end);
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkAndMarkSuperScope()
+{
+ if (!pc->sc()->allowSuperProperty())
+ return false;
+ pc->setSuperScopeNeedsHomeObject();
+ return true;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+ TokenKind tt, bool allowCallSyntax /* = true */,
+ PossibleError* possibleError /* = nullptr */,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+
+ Node lhs;
+
+ JS_CHECK_RECURSION(context, return null());
+
+ /* Check for new expression first. */
+ if (tt == TOK_NEW) {
+ uint32_t newBegin = pos().begin;
+ // Make sure this wasn't a |new.target| in disguise.
+ Node newTarget;
+ if (!tryNewTarget(newTarget))
+ return null();
+ if (newTarget) {
+ lhs = newTarget;
+ } else {
+ lhs = handler.newList(PNK_NEW, newBegin, JSOP_NEW);
+ if (!lhs)
+ return null();
+
+ // Gotten by tryNewTarget
+ tt = tokenStream.currentToken().type;
+ Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt,
+ /* allowCallSyntax = */ false,
+ /* possibleError = */ nullptr, PredictInvoked);
+ if (!ctorExpr)
+ return null();
+
+ handler.addList(lhs, ctorExpr);
+
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_LP))
+ return null();
+ if (matched) {
+ bool isSpread = false;
+ if (!argumentList(yieldHandling, lhs, &isSpread))
+ return null();
+ if (isSpread)
+ handler.setOp(lhs, JSOP_SPREADNEW);
+ }
+ }
+ } else if (tt == TOK_SUPER) {
+ Node thisName = newThisName();
+ if (!thisName)
+ return null();
+ lhs = handler.newSuperBase(thisName, pos());
+ if (!lhs)
+ return null();
+ } else {
+ lhs = primaryExpr(yieldHandling, tripledotHandling, tt, possibleError, invoked);
+ if (!lhs)
+ return null();
+ }
+
+ MOZ_ASSERT_IF(handler.isSuperBase(lhs), tokenStream.isCurrentTokenType(TOK_SUPER));
+
+ while (true) {
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt == TOK_EOF)
+ break;
+
+ Node nextMember;
+ if (tt == TOK_DOT) {
+ if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ return null();
+ if (tt == TOK_NAME) {
+ PropertyName* field = tokenStream.currentName();
+ if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
+ report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "property");
+ return null();
+ }
+ nextMember = handler.newPropertyAccess(lhs, field, pos().end);
+ if (!nextMember)
+ return null();
+ } else {
+ report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
+ return null();
+ }
+ } else if (tt == TOK_LB) {
+ Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!propExpr)
+ return null();
+
+ MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
+
+ if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
+ report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
+ return null();
+ }
+ nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
+ if (!nextMember)
+ return null();
+ } else if ((allowCallSyntax && tt == TOK_LP) ||
+ tt == TOK_TEMPLATE_HEAD ||
+ tt == TOK_NO_SUBS_TEMPLATE)
+ {
+ if (handler.isSuperBase(lhs)) {
+ if (!pc->sc()->allowSuperCall()) {
+ report(ParseError, false, null(), JSMSG_BAD_SUPERCALL);
+ return null();
+ }
+
+ if (tt != TOK_LP) {
+ report(ParseError, false, null(), JSMSG_BAD_SUPER);
+ return null();
+ }
+
+ nextMember = handler.newList(PNK_SUPERCALL, lhs, JSOP_SUPERCALL);
+ if (!nextMember)
+ return null();
+
+ // Despite the fact that it's impossible to have |super()| in a
+ // generator, we still inherit the yieldHandling of the
+ // memberExpression, per spec. Curious.
+ bool isSpread = false;
+ if (!argumentList(yieldHandling, nextMember, &isSpread))
+ return null();
+
+ if (isSpread)
+ handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
+
+ Node thisName = newThisName();
+ if (!thisName)
+ return null();
+
+ nextMember = handler.newSetThis(thisName, nextMember);
+ if (!nextMember)
+ return null();
+ } else {
+ if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
+ report(ParseError, false, null(), JSMSG_SELFHOSTED_METHOD_CALL);
+ return null();
+ }
+
+ nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate();
+ if (!nextMember)
+ return null();
+
+ JSOp op = JSOP_CALL;
+ bool maybeAsyncArrow = false;
+ if (tt == TOK_LP && handler.isNameAnyParentheses(lhs)) {
+ if (handler.nameIsEvalAnyParentheses(lhs, context)) {
+ // Select the right EVAL op and flag pc as having a
+ // direct eval.
+ op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
+ pc->sc()->setBindingsAccessedDynamically();
+ pc->sc()->setHasDirectEval();
+
+ // In non-strict mode code, direct calls to eval can
+ // add variables to the call object.
+ if (pc->isFunctionBox() && !pc->sc()->strict())
+ pc->functionBox()->setHasExtensibleScope();
+
+ // If we're in a method, mark the method as requiring
+ // support for 'super', since direct eval code can use
+ // it. (If we're not in a method, that's fine, so
+ // ignore the return value.)
+ checkAndMarkSuperScope();
+ } else if (handler.nameIsUnparenthesizedAsync(lhs, context)) {
+ // |async (| can be the start of an async arrow
+ // function, so we need to defer reporting possible
+ // errors from destructuring syntax. To give better
+ // error messages, we only allow the AsyncArrowHead
+ // part of the CoverCallExpressionAndAsyncArrowHead
+ // syntax when the initial name is "async".
+ maybeAsyncArrow = true;
+ }
+ } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
+ // Use the JSOP_FUN{APPLY,CALL} optimizations given the
+ // right syntax.
+ if (prop == context->names().apply) {
+ op = JSOP_FUNAPPLY;
+ if (pc->isFunctionBox())
+ pc->functionBox()->usesApply = true;
+ } else if (prop == context->names().call) {
+ op = JSOP_FUNCALL;
+ }
+ }
+
+ handler.setBeginPosition(nextMember, lhs);
+ handler.addList(nextMember, lhs);
+
+ if (tt == TOK_LP) {
+ bool isSpread = false;
+ PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
+ if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError))
+ return null();
+ if (isSpread) {
+ if (op == JSOP_EVAL)
+ op = JSOP_SPREADEVAL;
+ else if (op == JSOP_STRICTEVAL)
+ op = JSOP_STRICTSPREADEVAL;
+ else
+ op = JSOP_SPREADCALL;
+ }
+ } else {
+ if (!taggedTemplate(yieldHandling, nextMember, tt))
+ return null();
+ }
+ handler.setOp(nextMember, op);
+ }
+ } else {
+ tokenStream.ungetToken();
+ if (handler.isSuperBase(lhs))
+ break;
+ return lhs;
+ }
+
+ lhs = nextMember;
+ }
+
+ if (handler.isSuperBase(lhs)) {
+ report(ParseError, false, null(), JSMSG_BAD_SUPER);
+ return null();
+ }
+
+ return lhs;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newName(PropertyName* name)
+{
+ return newName(name, pos());
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
+{
+ return handler.newName(name, pos, context);
+}
+
+template <typename ParseHandler>
+PropertyName*
+Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling,
+ bool yieldTokenizedAsName)
+{
+ PropertyName* ident;
+ bool isYield;
+ const Token& tok = tokenStream.currentToken();
+ if (tok.type == TOK_NAME) {
+ MOZ_ASSERT(tok.name() != context->names().yield ||
+ tok.nameContainsEscape() ||
+ yieldTokenizedAsName,
+ "tokenizer should have treated unescaped 'yield' as TOK_YIELD");
+ MOZ_ASSERT_IF(yieldTokenizedAsName, tok.name() == context->names().yield);
+
+ ident = tok.name();
+ isYield = ident == context->names().yield;
+ } else {
+ MOZ_ASSERT(tok.type == TOK_YIELD && !yieldTokenizedAsName);
+
+ ident = context->names().yield;
+ isYield = true;
+ }
+
+ if (!isYield) {
+ if (pc->sc()->strict()) {
+ const char* badName = ident == context->names().let
+ ? "let"
+ : ident == context->names().static_
+ ? "static"
+ : nullptr;
+ if (badName) {
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
+ return nullptr;
+ }
+ }
+ } else {
+ if (yieldHandling == YieldIsKeyword ||
+ pc->sc()->strict() ||
+ versionNumber() >= JSVERSION_1_7)
+ {
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
+ return nullptr;
+ }
+ }
+
+ return ident;
+}
+
+template <typename ParseHandler>
+PropertyName*
+Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
+{
+ PropertyName* ident;
+ bool isYield;
+ const Token& tok = tokenStream.currentToken();
+ if (tok.type == TOK_NAME) {
+ MOZ_ASSERT(tok.name() != context->names().yield || tok.nameContainsEscape(),
+ "tokenizer should have treated unescaped 'yield' as TOK_YIELD");
+
+ ident = tok.name();
+ isYield = ident == context->names().yield;
+ } else {
+ MOZ_ASSERT(tok.type == TOK_YIELD);
+
+ ident = context->names().yield;
+ isYield = true;
+ }
+
+ if (!isYield) {
+ if (pc->sc()->strict()) {
+ const char* badName = ident == context->names().arguments
+ ? "arguments"
+ : ident == context->names().eval
+ ? "eval"
+ : nullptr;
+ if (badName) {
+ report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName);
+ return nullptr;
+ }
+
+ badName = ident == context->names().let
+ ? "let"
+ : ident == context->names().static_
+ ? "static"
+ : nullptr;
+ if (badName) {
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
+ return nullptr;
+ }
+ }
+ } else {
+ if (yieldHandling == YieldIsKeyword ||
+ pc->sc()->strict() ||
+ versionNumber() >= JSVERSION_1_7)
+ {
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
+ return nullptr;
+ }
+ }
+
+ return ident;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::identifierReference(Handle<PropertyName*> name)
+{
+ Node pn = newName(name);
+ if (!pn)
+ return null();
+
+ if (!pc->inDestructuringDecl && !noteUsedName(name))
+ return null();
+
+ return pn;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::stringLiteral()
+{
+ return handler.newStringLiteral(stopStringCompression(), pos());
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::noSubstitutionTemplate()
+{
+ return handler.newTemplateStringLiteral(stopStringCompression(), pos());
+}
+
+template <typename ParseHandler>
+JSAtom * Parser<ParseHandler>::stopStringCompression() {
+ JSAtom* atom = tokenStream.currentToken().atom();
+
+ // Large strings are fast to parse but slow to compress. Stop compression on
+ // them, so we don't wait for a long time for compression to finish at the
+ // end of compilation.
+ const size_t HUGE_STRING = 50000;
+ if (sct && sct->active() && atom->length() >= HUGE_STRING)
+ sct->abort();
+ return atom;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::newRegExp()
+{
+ MOZ_ASSERT(!options().selfHostingMode);
+ // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
+ const char16_t* chars = tokenStream.getTokenbuf().begin();
+ size_t length = tokenStream.getTokenbuf().length();
+ RegExpFlag flags = tokenStream.currentToken().regExpFlags();
+
+ Rooted<RegExpObject*> reobj(context);
+ reobj = RegExpObject::create(context, chars, length, flags, &tokenStream, alloc);
+ if (!reobj)
+ return null();
+
+ return handler.newRegExp(reobj, pos(), *this);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+
+ uint32_t begin = pos().begin;
+ Node literal = handler.newArrayLiteral(begin);
+ if (!literal)
+ return null();
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ return null();
+
+ // Handle an ES6-era array comprehension first.
+ if (tt == TOK_FOR)
+ return arrayComprehension(begin);
+
+ if (tt == TOK_RB) {
+ /*
+ * Mark empty arrays as non-constant, since we cannot easily
+ * determine their type.
+ */
+ handler.setListFlag(literal, PNX_NONCONST);
+ } else {
+ tokenStream.ungetToken();
+
+ uint32_t index = 0;
+ TokenStream::Modifier modifier = TokenStream::Operand;
+ for (; ; index++) {
+ if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
+ report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
+ return null();
+ }
+
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ return null();
+ if (tt == TOK_RB)
+ break;
+
+ if (tt == TOK_COMMA) {
+ tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
+ if (!handler.addElision(literal, pos()))
+ return null();
+ } else if (tt == TOK_TRIPLEDOT) {
+ tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
+ uint32_t begin = pos().begin;
+ Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+ possibleError);
+ if (!inner)
+ return null();
+ if (!handler.addSpreadElement(literal, begin, inner))
+ return null();
+ } else {
+ Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+ possibleError);
+ if (!element)
+ return null();
+ if (foldConstants && !FoldConstants(context, &element, this))
+ return null();
+ handler.addArrayElement(literal, element);
+ }
+
+ if (tt != TOK_COMMA) {
+ /* If we didn't already match TOK_COMMA in above case. */
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_COMMA))
+ return null();
+ if (!matched) {
+ modifier = TokenStream::None;
+ break;
+ }
+ if (tt == TOK_TRIPLEDOT && possibleError)
+ possibleError->setPendingDestructuringError(null(), JSMSG_REST_WITH_COMMA);
+ }
+ }
+
+ MUST_MATCH_TOKEN_MOD(TOK_RB, modifier, JSMSG_BRACKET_AFTER_LIST);
+ }
+ handler.setEndPosition(literal, pos().end);
+ return literal;
+}
+
+static JSAtom*
+DoubleToAtom(ExclusiveContext* cx, double value)
+{
+ // This is safe because doubles can not be moved.
+ Value tmp = DoubleValue(value);
+ return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp));
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
+ PropertyType* propType, MutableHandleAtom propAtom)
+{
+ TokenKind ltok;
+ if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+ return null();
+
+ MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
+
+ bool isGenerator = false;
+ bool isAsync = false;
+ if (ltok == TOK_MUL) {
+ isGenerator = true;
+ if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+ return null();
+ }
+
+ if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ // AsyncMethod[Yield, Await]:
+ // async [no LineTerminator here] PropertyName[?Yield, ?Await] ...
+ //
+ // PropertyName:
+ // LiteralPropertyName
+ // ComputedPropertyName[?Yield, ?Await]
+ //
+ // LiteralPropertyName:
+ // IdentifierName
+ // StringLiteral
+ // NumericLiteral
+ //
+ // ComputedPropertyName[Yield, Await]:
+ // [ ...
+ TokenKind tt = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&tt, TokenStream::KeywordIsName))
+ return null();
+ if (tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_LB ||
+ tt == TOK_NAME || tt == TOK_YIELD)
+ {
+ isAsync = true;
+ tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
+ ltok = tt;
+ } else {
+ tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
+ }
+ }
+
+ if (isAsync && isGenerator) {
+ report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
+ return null();
+ }
+
+ propAtom.set(nullptr);
+ Node propName;
+ switch (ltok) {
+ case TOK_NUMBER:
+ propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
+ if (!propAtom.get())
+ return null();
+ propName = newNumber(tokenStream.currentToken());
+ if (!propName)
+ return null();
+ break;
+
+ case TOK_LB:
+ propName = computedPropertyName(yieldHandling, propList);
+ if (!propName)
+ return null();
+ break;
+
+ case TOK_NAME: {
+ propAtom.set(tokenStream.currentName());
+ // Do not look for accessor syntax on generators
+ if (isGenerator || isAsync ||
+ !(propAtom.get() == context->names().get ||
+ propAtom.get() == context->names().set))
+ {
+ propName = handler.newObjectLiteralPropertyName(propAtom, pos());
+ if (!propName)
+ return null();
+ break;
+ }
+
+ *propType = propAtom.get() == context->names().get ? PropertyType::Getter
+ : PropertyType::Setter;
+
+ // We have parsed |get| or |set|. Look for an accessor property
+ // name next.
+ TokenKind tt;
+ if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
+ return null();
+ if (tt == TOK_NAME) {
+ if (!checkUnescapedName())
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_NAME, TokenStream::KeywordIsName);
+
+ propAtom.set(tokenStream.currentName());
+ return handler.newObjectLiteralPropertyName(propAtom, pos());
+ }
+ if (tt == TOK_STRING) {
+ if (!checkUnescapedName())
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_STRING, TokenStream::KeywordIsName);
+
+ propAtom.set(tokenStream.currentToken().atom());
+
+ uint32_t index;
+ if (propAtom->isIndex(&index)) {
+ propAtom.set(DoubleToAtom(context, index));
+ if (!propAtom.get())
+ return null();
+ return handler.newNumber(index, NoDecimal, pos());
+ }
+ return stringLiteral();
+ }
+ if (tt == TOK_NUMBER) {
+ if (!checkUnescapedName())
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_NUMBER, TokenStream::KeywordIsName);
+
+ propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
+ if (!propAtom.get())
+ return null();
+ return newNumber(tokenStream.currentToken());
+ }
+ if (tt == TOK_LB) {
+ if (!checkUnescapedName())
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_LB, TokenStream::KeywordIsName);
+
+ return computedPropertyName(yieldHandling, propList);
+ }
+
+ // Not an accessor property after all.
+ propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos());
+ if (!propName)
+ return null();
+ tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
+ break;
+ }
+
+ case TOK_STRING: {
+ propAtom.set(tokenStream.currentToken().atom());
+ uint32_t index;
+ if (propAtom->isIndex(&index)) {
+ propName = handler.newNumber(index, NoDecimal, pos());
+ if (!propName)
+ return null();
+ break;
+ }
+ propName = stringLiteral();
+ if (!propName)
+ return null();
+ break;
+ }
+
+ default:
+ report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
+ return null();
+ }
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+
+ if (tt == TOK_COLON) {
+ if (isGenerator) {
+ report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
+ return null();
+ }
+ *propType = PropertyType::Normal;
+ return propName;
+ }
+
+ if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) {
+ if (isGenerator) {
+ report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
+ return null();
+ }
+ tokenStream.ungetToken();
+ *propType = tt == TOK_ASSIGN ?
+ PropertyType::CoverInitializedName :
+ PropertyType::Shorthand;
+ return propName;
+ }
+
+ if (tt == TOK_LP) {
+ tokenStream.ungetToken();
+ if (isGenerator)
+ *propType = PropertyType::GeneratorMethod;
+ else if (isAsync)
+ *propType = PropertyType::AsyncMethod;
+ else
+ *propType = PropertyType::Method;
+ return propName;
+ }
+
+ report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
+ return null();
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling, Node literal)
+{
+ uint32_t begin = pos().begin;
+
+ Node assignNode;
+ {
+ // Turn off the inDestructuringDecl flag when parsing computed property
+ // names. In short, when parsing 'let {[x + y]: z} = obj;', noteUsedName()
+ // should be called on x and y, but not on z. See the comment on
+ // Parser<>::checkDestructuringPattern() for details.
+ AutoClearInDestructuringDecl autoClear(pc);
+ assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!assignNode)
+ return null();
+ }
+
+ MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
+ Node propname = handler.newComputedName(assignNode, begin, pos().end);
+ if (!propname)
+ return null();
+ handler.setListFlag(literal, PNX_NONCONST);
+ return propname;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+
+ Node literal = handler.newObjectLiteral(pos().begin);
+ if (!literal)
+ return null();
+
+ bool seenPrototypeMutation = false;
+ bool seenCoverInitializedName = false;
+ RootedAtom propAtom(context);
+ for (;;) {
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ return null();
+ if (tt == TOK_RC)
+ break;
+
+ tokenStream.ungetToken();
+
+ PropertyType propType;
+ Node propName = propertyName(yieldHandling, literal, &propType, &propAtom);
+ if (!propName)
+ return null();
+
+ if (propType == PropertyType::Normal) {
+ Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+ possibleError);
+ if (!propExpr)
+ return null();
+
+ if (foldConstants && !FoldConstants(context, &propExpr, this))
+ return null();
+
+ if (propAtom == context->names().proto) {
+ if (seenPrototypeMutation) {
+ // Directly report the error when we're not in a
+ // destructuring context.
+ if (!possibleError) {
+ report(ParseError, false, propName, JSMSG_DUPLICATE_PROTO_PROPERTY);
+ return null();
+ }
+
+ // Otherwise delay error reporting until we've determined
+ // whether or not we're destructuring.
+ possibleError->setPendingExpressionError(propName,
+ JSMSG_DUPLICATE_PROTO_PROPERTY);
+ }
+ seenPrototypeMutation = true;
+
+ // Note: this occurs *only* if we observe TOK_COLON! Only
+ // __proto__: v mutates [[Prototype]]. Getters, setters,
+ // method/generator definitions, computed property name
+ // versions of all of these, and shorthands do not.
+ uint32_t begin = handler.getPosition(propName).begin;
+ if (!handler.addPrototypeMutation(literal, begin, propExpr))
+ return null();
+ } else {
+ if (!handler.isConstant(propExpr))
+ handler.setListFlag(literal, PNX_NONCONST);
+
+ if (!handler.addPropertyDefinition(literal, propName, propExpr))
+ return null();
+ }
+ } else if (propType == PropertyType::Shorthand) {
+ /*
+ * Support, e.g., |var {x, y} = o| as destructuring shorthand
+ * for |var {x: x, y: y} = o|, and |var o = {x, y}| as initializer
+ * shorthand for |var o = {x: x, y: y}|.
+ */
+ TokenKind propToken = TOK_NAME;
+ if (!tokenStream.checkForKeyword(propAtom, &propToken))
+ return null();
+
+ if (propToken != TOK_NAME && propToken != TOK_YIELD) {
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, TokenKindToDesc(propToken));
+ return null();
+ }
+
+ Rooted<PropertyName*> name(context,
+ identifierReference(yieldHandling, propToken == TOK_YIELD));
+ if (!name)
+ return null();
+
+ Node nameExpr = identifierReference(name);
+ if (!nameExpr)
+ return null();
+
+ if (!handler.addShorthand(literal, propName, nameExpr))
+ return null();
+ } else if (propType == PropertyType::CoverInitializedName) {
+ /*
+ * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand
+ * with default values, as per ES6 12.14.5
+ */
+ TokenKind propToken = TOK_NAME;
+ if (!tokenStream.checkForKeyword(propAtom, &propToken))
+ return null();
+
+ if (propToken != TOK_NAME && propToken != TOK_YIELD) {
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, TokenKindToDesc(propToken));
+ return null();
+ }
+
+ Rooted<PropertyName*> name(context,
+ identifierReference(yieldHandling, propToken == TOK_YIELD));
+ if (!name)
+ return null();
+
+ Node lhs = identifierReference(name);
+ if (!lhs)
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_ASSIGN);
+
+ if (!seenCoverInitializedName) {
+ // "shorthand default" or "CoverInitializedName" syntax is only
+ // valid in the case of destructuring.
+ seenCoverInitializedName = true;
+
+ if (!possibleError) {
+ // Destructuring defaults are definitely not allowed in this object literal,
+ // because of something the caller knows about the preceding code.
+ // For example, maybe the preceding token is an operator: `x + {y=z}`.
+ report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
+ return null();
+ }
+
+ // Here we set a pending error so that later in the parse, once we've
+ // determined whether or not we're destructuring, the error can be
+ // reported or ignored appropriately.
+ possibleError->setPendingExpressionError(null(), JSMSG_COLON_AFTER_ID);
+ }
+
+ Node rhs;
+ {
+ // Clearing `inDestructuringDecl` allows name use to be noted
+ // in Parser::identifierReference. See bug 1255167.
+ AutoClearInDestructuringDecl autoClear(pc);
+ rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!rhs)
+ return null();
+ }
+
+ Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
+ if (!propExpr)
+ return null();
+
+ if (!handler.addPropertyDefinition(literal, propName, propExpr))
+ return null();
+
+ if (!abortIfSyntaxParser())
+ return null();
+ } else {
+ // FIXME: Implement ES6 function "name" property semantics
+ // (bug 883377).
+ RootedAtom funName(context);
+ if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+ funName = propAtom;
+
+ if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
+ funName = prefixAccessorName(propType, propAtom);
+ if (!funName)
+ return null();
+ }
+ }
+
+ Node fn = methodDefinition(propType, funName);
+ if (!fn)
+ return null();
+
+ JSOp op = JSOpFromPropertyType(propType);
+ if (!handler.addObjectMethodDefinition(literal, propName, fn, op))
+ return null();
+ }
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt == TOK_RC)
+ break;
+ if (tt != TOK_COMMA) {
+ report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
+ return null();
+ }
+ }
+
+ handler.setEndPosition(literal, pos().end);
+ return literal;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::methodDefinition(PropertyType propType, HandleAtom funName)
+{
+ FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType);
+ GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType);
+ FunctionAsyncKind asyncKind = AsyncKindFromPropertyType(propType);
+ YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+ return functionDefinition(InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::tryNewTarget(Node &newTarget)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW));
+
+ newTarget = null();
+
+ Node newHolder = handler.newPosHolder(pos());
+ if (!newHolder)
+ return false;
+
+ uint32_t begin = pos().begin;
+
+ // |new| expects to look for an operand, so we will honor that.
+ TokenKind next;
+ if (!tokenStream.getToken(&next, TokenStream::Operand))
+ return false;
+
+ // Don't unget the token, since lookahead cannot handle someone calling
+ // getToken() with a different modifier. Callers should inspect currentToken().
+ if (next != TOK_DOT)
+ return true;
+
+ if (!tokenStream.getToken(&next))
+ return false;
+ if (next != TOK_NAME || tokenStream.currentName() != context->names().target) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "target", TokenKindToDesc(next));
+ return false;
+ }
+
+ if (!checkUnescapedName())
+ return false;
+
+ if (!pc->sc()->allowNewTarget()) {
+ reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET);
+ return false;
+ }
+
+ Node targetHolder = handler.newPosHolder(pos());
+ if (!targetHolder)
+ return false;
+
+ newTarget = handler.newNewTarget(newHolder, targetHolder);
+ return !!newTarget;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+ TokenKind tt, PossibleError* possibleError,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+ JS_CHECK_RECURSION(context, return null());
+
+ switch (tt) {
+ case TOK_FUNCTION:
+ return functionExpr(invoked);
+
+ case TOK_CLASS:
+ return classDefinition(yieldHandling, ClassExpression, NameRequired);
+
+ case TOK_LB:
+ return arrayInitializer(yieldHandling, possibleError);
+
+ case TOK_LC:
+ return objectLiteral(yieldHandling, possibleError);
+
+ case TOK_LP: {
+ TokenKind next;
+ if (!tokenStream.peekToken(&next, TokenStream::Operand))
+ return null();
+
+ if (next == TOK_RP) {
+ // Not valid expression syntax, but this is valid in an arrow function
+ // with no params: `() => body`.
+ tokenStream.consumeKnownToken(next, TokenStream::Operand);
+
+ if (!tokenStream.peekToken(&next))
+ return null();
+ if (next != TOK_ARROW) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "expression", TokenKindToDesc(TOK_RP));
+ return null();
+ }
+
+ // Now just return something that will allow parsing to continue.
+ // It doesn't matter what; when we reach the =>, we will rewind and
+ // reparse the whole arrow function. See Parser::assignExpr.
+ return handler.newNullLiteral(pos());
+ }
+
+ if (next == TOK_FOR) {
+ uint32_t begin = pos().begin;
+ tokenStream.consumeKnownToken(next, TokenStream::Operand);
+ return generatorComprehension(begin);
+ }
+
+ // Pass |possibleError| to support destructuring in arrow parameters.
+ Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
+ if (!expr)
+ return null();
+ MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+ handler.setEndPosition(expr, pos().end);
+ return handler.parenthesize(expr);
+ }
+
+ case TOK_TEMPLATE_HEAD:
+ return templateLiteral(yieldHandling);
+
+ case TOK_NO_SUBS_TEMPLATE:
+ return noSubstitutionTemplate();
+
+ case TOK_STRING:
+ return stringLiteral();
+
+ case TOK_YIELD:
+ case TOK_NAME: {
+ if (tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ if (nextSameLine == TOK_FUNCTION) {
+ tokenStream.consumeKnownToken(TOK_FUNCTION);
+ return functionExpr(PredictUninvoked, AsyncFunction);
+ }
+ }
+
+ Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+ if (!name)
+ return null();
+
+ return identifierReference(name);
+ }
+
+ case TOK_REGEXP:
+ return newRegExp();
+
+ case TOK_NUMBER:
+ return newNumber(tokenStream.currentToken());
+
+ case TOK_TRUE:
+ return handler.newBooleanLiteral(true, pos());
+ case TOK_FALSE:
+ return handler.newBooleanLiteral(false, pos());
+ case TOK_THIS: {
+ if (pc->isFunctionBox())
+ pc->functionBox()->usesThis = true;
+ Node thisName = null();
+ if (pc->sc()->thisBinding() == ThisBinding::Function) {
+ thisName = newThisName();
+ if (!thisName)
+ return null();
+ }
+ return handler.newThisLiteral(pos(), thisName);
+ }
+ case TOK_NULL:
+ return handler.newNullLiteral(pos());
+
+ case TOK_TRIPLEDOT: {
+ // This isn't valid expression syntax, but it's valid in an arrow
+ // function as a trailing rest param: `(a, b, ...rest) => body`. Check
+ // if it's directly under
+ // CoverParenthesizedExpressionAndArrowParameterList, and check for a
+ // name, closing parenthesis, and arrow, and allow it only if all are
+ // present.
+ if (tripledotHandling != TripledotAllowed) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "expression", TokenKindToDesc(tt));
+ return null();
+ }
+
+ TokenKind next;
+ if (!tokenStream.getToken(&next))
+ return null();
+
+ if (next == TOK_LB || next == TOK_LC) {
+ // Validate, but don't store the pattern right now. The whole arrow
+ // function is reparsed in functionFormalParametersAndBody().
+ if (!destructuringDeclaration(DeclarationKind::CoverArrowParameter, yieldHandling,
+ next))
+ {
+ return null();
+ }
+ } else {
+ // This doesn't check that the provided name is allowed, e.g. if
+ // the enclosing code is strict mode code, any of "let", "yield",
+ // or "arguments" should be prohibited. Argument-parsing code
+ // handles that.
+ if (next != TOK_NAME && next != TOK_YIELD) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "rest argument name", TokenKindToDesc(next));
+ return null();
+ }
+ }
+
+ if (!tokenStream.getToken(&next))
+ return null();
+ if (next != TOK_RP) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "closing parenthesis", TokenKindToDesc(next));
+ return null();
+ }
+
+ if (!tokenStream.peekToken(&next))
+ return null();
+ if (next != TOK_ARROW) {
+ // Advance the scanner for proper error location reporting.
+ tokenStream.consumeKnownToken(next);
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "'=>' after argument list", TokenKindToDesc(next));
+ return null();
+ }
+
+ tokenStream.ungetToken(); // put back right paren
+
+ // Return an arbitrary expression node. See case TOK_RP above.
+ return handler.newNullLiteral(pos());
+ }
+
+ default:
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "expression", TokenKindToDesc(tt));
+ return null();
+ }
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError /* = nullptr */)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+ return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::addTelemetry(JSCompartment::DeprecatedLanguageExtension e)
+{
+ JSContext* cx = context->maybeJSContext();
+ if (!cx)
+ return;
+ cx->compartment()->addTelemetry(getFilename(), e);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::warnOnceAboutExprClosure()
+{
+#ifndef RELEASE_OR_BETA
+ JSContext* cx = context->maybeJSContext();
+ if (!cx)
+ return true;
+
+ if (!cx->compartment()->warnedAboutExprClosure) {
+ if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_EXPR_CLOSURE))
+ return false;
+ cx->compartment()->warnedAboutExprClosure = true;
+ }
+#endif
+ return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::warnOnceAboutForEach()
+{
+ JSContext* cx = context->maybeJSContext();
+ if (!cx)
+ return true;
+
+ if (!cx->compartment()->warnedAboutForEach) {
+ if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_FOR_EACH))
+ return false;
+ cx->compartment()->warnedAboutForEach = true;
+ }
+ return true;
+}
+
+template class Parser<FullParseHandler>;
+template class Parser<SyntaxParseHandler>;
+
+} /* namespace frontend */
+} /* namespace js */