js/src/frontend/Parser.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * JS parser.
     9  *
    10  * This is a recursive-descent parser for the JavaScript language specified by
    11  * "The JavaScript 1.5 Language Specification".  It uses lexical and semantic
    12  * feedback to disambiguate non-LL(1) structures.  It generates trees of nodes
    13  * induced by the recursive parsing (not precise syntax trees, see Parser.h).
    14  * After tree construction, it rewrites trees to fold constants and evaluate
    15  * compile-time expressions.
    16  *
    17  * This parser attempts no error recovery.
    18  */
    20 #include "frontend/Parser-inl.h"
    22 #include "jsapi.h"
    23 #include "jsatom.h"
    24 #include "jscntxt.h"
    25 #include "jsfun.h"
    26 #include "jsobj.h"
    27 #include "jsopcode.h"
    28 #include "jsscript.h"
    29 #include "jstypes.h"
    31 #include "frontend/BytecodeCompiler.h"
    32 #include "frontend/FoldConstants.h"
    33 #include "frontend/ParseMaps.h"
    34 #include "frontend/TokenStream.h"
    35 #include "jit/AsmJS.h"
    36 #include "vm/Shape.h"
    38 #include "jsatominlines.h"
    39 #include "jsscriptinlines.h"
    41 #include "frontend/ParseNode-inl.h"
    43 using namespace js;
    44 using namespace js::gc;
    45 using mozilla::Maybe;
    47 namespace js {
    48 namespace frontend {
    50 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
    51 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
    52 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
    53 typedef Handle<NestedScopeObject*> HandleNestedScopeObject;
    56 /*
    57  * Insist that the next token be of type tt, or report errno and return null.
    58  * NB: this macro uses cx and ts from its lexical environment.
    59  */
    60 #define MUST_MATCH_TOKEN(tt, errno)                                                         \
    61     JS_BEGIN_MACRO                                                                          \
    62         if (tokenStream.getToken() != tt) {                                                 \
    63             report(ParseError, false, null(), errno);                                       \
    64             return null();                                                                  \
    65         }                                                                                   \
    66     JS_END_MACRO
    68 static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits;
    70 template <typename ParseHandler>
    71 bool
    72 GenerateBlockId(TokenStream &ts, ParseContext<ParseHandler> *pc, uint32_t &blockid)
    73 {
    74     if (pc->blockidGen == BlockIdLimit) {
    75         ts.reportError(JSMSG_NEED_DIET, "program");
    76         return false;
    77     }
    78     JS_ASSERT(pc->blockidGen < BlockIdLimit);
    79     blockid = pc->blockidGen++;
    80     return true;
    81 }
    83 template bool
    84 GenerateBlockId(TokenStream &ts, ParseContext<SyntaxParseHandler> *pc, uint32_t &blockid);
    86 template bool
    87 GenerateBlockId(TokenStream &ts, ParseContext<FullParseHandler> *pc, uint32_t &blockid);
    89 template <typename ParseHandler>
    90 static void
    91 PushStatementPC(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, StmtType type)
    92 {
    93     stmt->blockid = pc->blockid();
    94     PushStatement(pc, stmt, type);
    95 }
    97 // See comment on member function declaration.
    98 template <>
    99 bool
   100 ParseContext<FullParseHandler>::define(TokenStream &ts,
   101                                        HandlePropertyName name, ParseNode *pn, Definition::Kind kind)
   102 {
   103     JS_ASSERT(!pn->isUsed());
   104     JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
   106     Definition *prevDef = nullptr;
   107     if (kind == Definition::LET)
   108         prevDef = decls_.lookupFirst(name);
   109     else
   110         JS_ASSERT(!decls_.lookupFirst(name));
   112     if (!prevDef)
   113         prevDef = lexdeps.lookupDefn<FullParseHandler>(name);
   115     if (prevDef) {
   116         ParseNode **pnup = &prevDef->dn_uses;
   117         ParseNode *pnu;
   118         unsigned start = (kind == Definition::LET) ? pn->pn_blockid : bodyid;
   120         while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
   121             JS_ASSERT(pnu->pn_blockid >= bodyid);
   122             JS_ASSERT(pnu->isUsed());
   123             pnu->pn_lexdef = (Definition *) pn;
   124             pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
   125             pnup = &pnu->pn_link;
   126         }
   128         if (!pnu || pnu != prevDef->dn_uses) {
   129             *pnup = pn->dn_uses;
   130             pn->dn_uses = prevDef->dn_uses;
   131             prevDef->dn_uses = pnu;
   133             if (!pnu && prevDef->isPlaceholder())
   134                 lexdeps->remove(name);
   135         }
   137         pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED;
   138     }
   140     JS_ASSERT_IF(kind != Definition::LET, !lexdeps->lookup(name));
   141     pn->setDefn(true);
   142     pn->pn_dflags &= ~PND_PLACEHOLDER;
   143     if (kind == Definition::CONST)
   144         pn->pn_dflags |= PND_CONST;
   146     Definition *dn = (Definition *)pn;
   147     switch (kind) {
   148       case Definition::ARG:
   149         JS_ASSERT(sc->isFunctionBox());
   150         dn->setOp(JSOP_GETARG);
   151         dn->pn_dflags |= PND_BOUND;
   152         if (!dn->pn_cookie.set(ts, staticLevel, args_.length()))
   153             return false;
   154         if (!args_.append(dn))
   155             return false;
   156         if (args_.length() >= ARGNO_LIMIT) {
   157             ts.reportError(JSMSG_TOO_MANY_FUN_ARGS);
   158             return false;
   159         }
   160         if (name == ts.names().empty)
   161             break;
   162         if (!decls_.addUnique(name, dn))
   163             return false;
   164         break;
   166       case Definition::CONST:
   167       case Definition::VAR:
   168         if (sc->isFunctionBox()) {
   169             dn->setOp(JSOP_GETLOCAL);
   170             dn->pn_dflags |= PND_BOUND;
   171             if (!dn->pn_cookie.set(ts, staticLevel, vars_.length()))
   172                 return false;
   173             if (!vars_.append(dn))
   174                 return false;
   175             if (vars_.length() >= LOCALNO_LIMIT) {
   176                 ts.reportError(JSMSG_TOO_MANY_LOCALS);
   177                 return false;
   178             }
   179         }
   180         if (!decls_.addUnique(name, dn))
   181             return false;
   182         break;
   184       case Definition::LET:
   185         dn->setOp(JSOP_GETLOCAL);
   186         dn->pn_dflags |= (PND_LET | PND_BOUND);
   187         JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */
   188         if (!decls_.addShadow(name, dn))
   189             return false;
   190         break;
   192       default:
   193         MOZ_ASSUME_UNREACHABLE("unexpected kind");
   194     }
   196     return true;
   197 }
   199 template <>
   200 bool
   201 ParseContext<SyntaxParseHandler>::define(TokenStream &ts, HandlePropertyName name, Node pn,
   202                                          Definition::Kind kind)
   203 {
   204     JS_ASSERT(!decls_.lookupFirst(name));
   206     if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
   207         lexdeps->remove(name);
   209     // Keep track of the number of arguments in args_, for fun->nargs.
   210     if (kind == Definition::ARG) {
   211         if (!args_.append((Definition *) nullptr))
   212             return false;
   213         if (args_.length() >= ARGNO_LIMIT) {
   214             ts.reportError(JSMSG_TOO_MANY_FUN_ARGS);
   215             return false;
   216         }
   217     }
   219     return decls_.addUnique(name, kind);
   220 }
   222 template <typename ParseHandler>
   223 void
   224 ParseContext<ParseHandler>::prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl)
   225 {
   226     JS_ASSERT(decls_.lookupFirst(name) == prevDecl);
   227     decls_.remove(name);
   228 }
   230 template <typename ParseHandler>
   231 void
   232 ParseContext<ParseHandler>::updateDecl(JSAtom *atom, Node pn)
   233 {
   234     Definition *oldDecl = decls_.lookupFirst(atom);
   236     pn->setDefn(true);
   237     Definition *newDecl = (Definition *)pn;
   238     decls_.updateFirst(atom, newDecl);
   240     if (!sc->isFunctionBox()) {
   241         JS_ASSERT(newDecl->isFreeVar());
   242         return;
   243     }
   245     JS_ASSERT(oldDecl->isBound());
   246     JS_ASSERT(!oldDecl->pn_cookie.isFree());
   247     newDecl->pn_cookie = oldDecl->pn_cookie;
   248     newDecl->pn_dflags |= PND_BOUND;
   249     if (IsArgOp(oldDecl->getOp())) {
   250         newDecl->setOp(JSOP_GETARG);
   251         JS_ASSERT(args_[oldDecl->pn_cookie.slot()] == oldDecl);
   252         args_[oldDecl->pn_cookie.slot()] = newDecl;
   253     } else {
   254         JS_ASSERT(IsLocalOp(oldDecl->getOp()));
   255         newDecl->setOp(JSOP_GETLOCAL);
   256         JS_ASSERT(vars_[oldDecl->pn_cookie.slot()] == oldDecl);
   257         vars_[oldDecl->pn_cookie.slot()] = newDecl;
   258     }
   259 }
   261 template <typename ParseHandler>
   262 void
   263 ParseContext<ParseHandler>::popLetDecl(JSAtom *atom)
   264 {
   265     JS_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET);
   266     decls_.remove(atom);
   267 }
   269 template <typename ParseHandler>
   270 static void
   271 AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst)
   272 {
   273     for (size_t i = 0; i < vec.length(); ++i, ++dst) {
   274         Definition *dn = vec[i];
   275         PropertyName *name = dn->name();
   277         Binding::Kind kind;
   278         switch (dn->kind()) {
   279           case Definition::VAR:
   280             kind = Binding::VARIABLE;
   281             break;
   282           case Definition::CONST:
   283             kind = Binding::CONSTANT;
   284             break;
   285           case Definition::ARG:
   286             kind = Binding::ARGUMENT;
   287             break;
   288           default:
   289             MOZ_ASSUME_UNREACHABLE("unexpected dn->kind");
   290         }
   292         /*
   293          * Bindings::init does not check for duplicates so we must ensure that
   294          * only one binding with a given name is marked aliased. pc->decls
   295          * maintains the canonical definition for each name, so use that.
   296          */
   297         JS_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn);
   298         bool aliased = dn->isClosed() ||
   299                        (pc->sc->allLocalsAliased() &&
   300                         pc->decls().lookupFirst(name) == dn);
   302         *dst = Binding(name, kind, aliased);
   303     }
   304 }
   306 template <typename ParseHandler>
   307 bool
   308 ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext *cx, TokenStream &ts,
   309                                                      LifoAlloc &alloc,
   310                                                      InternalHandle<Bindings*> bindings) const
   311 {
   312     JS_ASSERT(sc->isFunctionBox());
   313     JS_ASSERT(args_.length() < ARGNO_LIMIT);
   314     JS_ASSERT(vars_.length() < LOCALNO_LIMIT);
   316     /*
   317      * Avoid pathological edge cases by explicitly limiting the total number of
   318      * bindings to what will fit in a uint32_t.
   319      */
   320     if (UINT32_MAX - args_.length() <= vars_.length())
   321         return ts.reportError(JSMSG_TOO_MANY_LOCALS);
   323     uint32_t count = args_.length() + vars_.length();
   324     Binding *packedBindings = alloc.newArrayUninitialized<Binding>(count);
   325     if (!packedBindings) {
   326         js_ReportOutOfMemory(cx);
   327         return false;
   328     }
   330     AppendPackedBindings(this, args_, packedBindings);
   331     AppendPackedBindings(this, vars_, packedBindings + args_.length());
   333     return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
   334                                               packedBindings, blockScopeDepth);
   335 }
   337 template <typename ParseHandler>
   338 bool
   339 Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
   340                                    unsigned errorNumber, va_list args)
   341 {
   342     bool result = false;
   343     switch (kind) {
   344       case ParseError:
   345         result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
   346         break;
   347       case ParseWarning:
   348         result =
   349             tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args);
   350         break;
   351       case ParseExtraWarning:
   352         result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args);
   353         break;
   354       case ParseStrictError:
   355         result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args);
   356         break;
   357     }
   358     return result;
   359 }
   361 template <typename ParseHandler>
   362 bool
   363 Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
   364 {
   365     uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin;
   367     va_list args;
   368     va_start(args, errorNumber);
   369     bool result = reportHelper(kind, strict, offset, errorNumber, args);
   370     va_end(args);
   371     return result;
   372 }
   374 template <typename ParseHandler>
   375 bool
   376 Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
   377 {
   378     va_list args;
   379     va_start(args, errorNumber);
   380     bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args);
   381     va_end(args);
   382     return result;
   383 }
   385 template <typename ParseHandler>
   386 bool
   387 Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset,
   388                                        unsigned errorNumber, ...)
   389 {
   390     va_list args;
   391     va_start(args, errorNumber);
   392     bool result = reportHelper(kind, strict, offset, errorNumber, args);
   393     va_end(args);
   394     return result;
   395 }
   397 template <>
   398 bool
   399 Parser<FullParseHandler>::abortIfSyntaxParser()
   400 {
   401     handler.disableSyntaxParser();
   402     return true;
   403 }
   405 template <>
   406 bool
   407 Parser<SyntaxParseHandler>::abortIfSyntaxParser()
   408 {
   409     abortedSyntaxParse = true;
   410     return false;
   411 }
   413 template <typename ParseHandler>
   414 Parser<ParseHandler>::Parser(ExclusiveContext *cx, LifoAlloc *alloc,
   415                              const ReadOnlyCompileOptions &options,
   416                              const jschar *chars, size_t length, bool foldConstants,
   417                              Parser<SyntaxParseHandler> *syntaxParser,
   418                              LazyScript *lazyOuterFunction)
   419   : AutoGCRooter(cx, PARSER),
   420     context(cx),
   421     alloc(*alloc),
   422     tokenStream(cx, options, chars, length, thisForCtor()),
   423     traceListHead(nullptr),
   424     pc(nullptr),
   425     sct(nullptr),
   426     ss(nullptr),
   427     keepAtoms(cx->perThreadData),
   428     foldConstants(foldConstants),
   429     abortedSyntaxParse(false),
   430     isUnexpectedEOF_(false),
   431     handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
   432 {
   433     {
   434         AutoLockForExclusiveAccess lock(cx);
   435         cx->perThreadData->addActiveCompilation();
   436     }
   438     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
   439     // which are not generated if functions are parsed lazily. Note that the
   440     // standard "use strict" does not inhibit lazy parsing.
   441     if (options.extraWarningsOption)
   442         handler.disableSyntaxParser();
   444     tempPoolMark = alloc->mark();
   445 }
   447 template <typename ParseHandler>
   448 Parser<ParseHandler>::~Parser()
   449 {
   450     alloc.release(tempPoolMark);
   452     /*
   453      * The parser can allocate enormous amounts of memory for large functions.
   454      * Eagerly free the memory now (which otherwise won't be freed until the
   455      * next GC) to avoid unnecessary OOMs.
   456      */
   457     alloc.freeAllIfHugeAndUnused();
   459     {
   460         AutoLockForExclusiveAccess lock(context);
   461         context->perThreadData->removeActiveCompilation();
   462     }
   463 }
   465 template <typename ParseHandler>
   466 ObjectBox *
   467 Parser<ParseHandler>::newObjectBox(JSObject *obj)
   468 {
   469     JS_ASSERT(obj && !IsPoisonedPtr(obj));
   471     /*
   472      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
   473      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
   474      * arenas containing the entries must be alive until we are done with
   475      * scanning, parsing and code generation for the whole script or top-level
   476      * function.
   477      */
   479     ObjectBox *objbox = alloc.new_<ObjectBox>(obj, traceListHead);
   480     if (!objbox) {
   481         js_ReportOutOfMemory(context);
   482         return nullptr;
   483     }
   485     traceListHead = objbox;
   487     return objbox;
   488 }
   490 template <typename ParseHandler>
   491 FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
   492                          ParseContext<ParseHandler> *outerpc, Directives directives,
   493                          bool extraWarnings, GeneratorKind generatorKind)
   494   : ObjectBox(fun, traceListHead),
   495     SharedContext(cx, directives, extraWarnings),
   496     bindings(),
   497     bufStart(0),
   498     bufEnd(0),
   499     length(0),
   500     generatorKindBits_(GeneratorKindAsBits(generatorKind)),
   501     inWith(false),                  // initialized below
   502     inGenexpLambda(false),
   503     hasDestructuringArgs(false),
   504     useAsm(directives.asmJS()),
   505     insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()),
   506     usesArguments(false),
   507     usesApply(false),
   508     funCxFlags()
   509 {
   510     // Functions created at parse time may be set singleton after parsing and
   511     // baked into JIT code, so they must be allocated tenured. They are held by
   512     // the JSScript so cannot be collected during a minor GC anyway.
   513     JS_ASSERT(fun->isTenured());
   515     if (!outerpc) {
   516         inWith = false;
   518     } else if (outerpc->parsingWith) {
   519         // This covers cases that don't involve eval().  For example:
   520         //
   521         //   with (o) { (function() { g(); })(); }
   522         //
   523         // In this case, |outerpc| corresponds to global code, and
   524         // outerpc->parsingWith is true.
   525         inWith = true;
   527     } else if (outerpc->sc->isGlobalSharedContext()) {
   528         // This covers the case where a function is nested within an eval()
   529         // within a |with| statement.
   530         //
   531         //   with (o) { eval("(function() { g(); })();"); }
   532         //
   533         // In this case, |outerpc| corresponds to the eval(),
   534         // outerpc->parsingWith is false because the eval() breaks the
   535         // ParseContext chain, and |parent| is nullptr (again because of the
   536         // eval(), so we have to look at |outerpc|'s scopeChain.
   537         //
   538         JSObject *scope = outerpc->sc->asGlobalSharedContext()->scopeChain();
   539         while (scope) {
   540             if (scope->is<DynamicWithObject>())
   541                 inWith = true;
   542             scope = scope->enclosingScope();
   543         }
   544     } else if (outerpc->sc->isFunctionBox()) {
   545         // This is like the above case, but for more deeply nested functions.
   546         // For example:
   547         //
   548         //   with (o) { eval("(function() { (function() { g(); })(); })();"); } }
   549         //
   550         // In this case, the inner anonymous function needs to inherit the
   551         // setting of |inWith| from the outer one.
   552         FunctionBox *parent = outerpc->sc->asFunctionBox();
   553         if (parent && parent->inWith)
   554             inWith = true;
   555     }
   556 }
   558 template <typename ParseHandler>
   559 FunctionBox *
   560 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc,
   561                                      Directives inheritedDirectives, GeneratorKind generatorKind)
   562 {
   563     JS_ASSERT(fun && !IsPoisonedPtr(fun));
   565     /*
   566      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
   567      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
   568      * arenas containing the entries must be alive until we are done with
   569      * scanning, parsing and code generation for the whole script or top-level
   570      * function.
   571      */
   572     FunctionBox *funbox =
   573         alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
   574                                 inheritedDirectives, options().extraWarningsOption,
   575                                 generatorKind);
   576     if (!funbox) {
   577         js_ReportOutOfMemory(context);
   578         return nullptr;
   579     }
   581     traceListHead = funbox;
   582     if (fn)
   583         handler.setFunctionBox(fn, funbox);
   585     return funbox;
   586 }
   588 template <typename ParseHandler>
   589 void
   590 Parser<ParseHandler>::trace(JSTracer *trc)
   591 {
   592     traceListHead->trace(trc);
   593 }
   595 void
   596 MarkParser(JSTracer *trc, AutoGCRooter *parser)
   597 {
   598     static_cast<Parser<FullParseHandler> *>(parser)->trace(trc);
   599 }
   601 /*
   602  * Parse a top-level JS script.
   603  */
   604 template <typename ParseHandler>
   605 typename ParseHandler::Node
   606 Parser<ParseHandler>::parse(JSObject *chain)
   607 {
   608     /*
   609      * Protect atoms from being collected by a GC activation, which might
   610      * - nest on this thread due to out of memory (the so-called "last ditch"
   611      *   GC attempted within js_NewGCThing), or
   612      * - run for any reason on another thread if this thread is suspended on
   613      *   an object lock before it finishes generating bytecode into a script
   614      *   protected from the GC by a root or a stack frame reference.
   615      */
   616     Directives directives(options().strictOption);
   617     GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption);
   618     ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
   619                                         &globalsc, /* newDirectives = */ nullptr,
   620                                         /* staticLevel = */ 0, /* bodyid = */ 0,
   621                                         /* blockScopeDepth = */ 0);
   622     if (!globalpc.init(tokenStream))
   623         return null();
   625     Node pn = statements();
   626     if (pn) {
   627         if (!tokenStream.matchToken(TOK_EOF)) {
   628             report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
   629             return null();
   630         }
   631         if (foldConstants) {
   632             if (!FoldConstants(context, &pn, this))
   633                 return null();
   634         }
   635     }
   636     return pn;
   637 }
   639 /*
   640  * Insist on a final return before control flows out of pn.  Try to be a bit
   641  * smart about loops: do {...; return e2;} while(0) at the end of a function
   642  * that contains an early return e1 will get a strict warning.  Similarly for
   643  * iloops: while (true){...} is treated as though ... returns.
   644  */
   645 enum {
   646     ENDS_IN_OTHER = 0,
   647     ENDS_IN_RETURN = 1,
   648     ENDS_IN_BREAK = 2
   649 };
   651 static int
   652 HasFinalReturn(ParseNode *pn)
   653 {
   654     ParseNode *pn2, *pn3;
   655     unsigned rv, rv2, hasDefault;
   657     switch (pn->getKind()) {
   658       case PNK_STATEMENTLIST:
   659         if (!pn->pn_head)
   660             return ENDS_IN_OTHER;
   661         return HasFinalReturn(pn->last());
   663       case PNK_IF:
   664         if (!pn->pn_kid3)
   665             return ENDS_IN_OTHER;
   666         return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
   668       case PNK_WHILE:
   669         pn2 = pn->pn_left;
   670         if (pn2->isKind(PNK_TRUE))
   671             return ENDS_IN_RETURN;
   672         if (pn2->isKind(PNK_NUMBER) && pn2->pn_dval)
   673             return ENDS_IN_RETURN;
   674         return ENDS_IN_OTHER;
   676       case PNK_DOWHILE:
   677         pn2 = pn->pn_right;
   678         if (pn2->isKind(PNK_FALSE))
   679             return HasFinalReturn(pn->pn_left);
   680         if (pn2->isKind(PNK_TRUE))
   681             return ENDS_IN_RETURN;
   682         if (pn2->isKind(PNK_NUMBER)) {
   683             if (pn2->pn_dval == 0)
   684                 return HasFinalReturn(pn->pn_left);
   685             return ENDS_IN_RETURN;
   686         }
   687         return ENDS_IN_OTHER;
   689       case PNK_FOR:
   690         pn2 = pn->pn_left;
   691         if (pn2->isArity(PN_TERNARY) && !pn2->pn_kid2)
   692             return ENDS_IN_RETURN;
   693         return ENDS_IN_OTHER;
   695       case PNK_SWITCH:
   696         rv = ENDS_IN_RETURN;
   697         hasDefault = ENDS_IN_OTHER;
   698         pn2 = pn->pn_right;
   699         if (pn2->isKind(PNK_LEXICALSCOPE))
   700             pn2 = pn2->expr();
   701         for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
   702             if (pn2->isKind(PNK_DEFAULT))
   703                 hasDefault = ENDS_IN_RETURN;
   704             pn3 = pn2->pn_right;
   705             JS_ASSERT(pn3->isKind(PNK_STATEMENTLIST));
   706             if (pn3->pn_head) {
   707                 rv2 = HasFinalReturn(pn3->last());
   708                 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
   709                     /* Falling through to next case or default. */;
   710                 else
   711                     rv &= rv2;
   712             }
   713         }
   714         /* If a final switch has no default case, we judge it harshly. */
   715         rv &= hasDefault;
   716         return rv;
   718       case PNK_BREAK:
   719         return ENDS_IN_BREAK;
   721       case PNK_WITH:
   722         return HasFinalReturn(pn->pn_right);
   724       case PNK_RETURN:
   725         return ENDS_IN_RETURN;
   727       case PNK_COLON:
   728       case PNK_LEXICALSCOPE:
   729         return HasFinalReturn(pn->expr());
   731       case PNK_THROW:
   732         return ENDS_IN_RETURN;
   734       case PNK_TRY:
   735         /* If we have a finally block that returns, we are done. */
   736         if (pn->pn_kid3) {
   737             rv = HasFinalReturn(pn->pn_kid3);
   738             if (rv == ENDS_IN_RETURN)
   739                 return rv;
   740         }
   742         /* Else check the try block and any and all catch statements. */
   743         rv = HasFinalReturn(pn->pn_kid1);
   744         if (pn->pn_kid2) {
   745             JS_ASSERT(pn->pn_kid2->isArity(PN_LIST));
   746             for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
   747                 rv &= HasFinalReturn(pn2);
   748         }
   749         return rv;
   751       case PNK_CATCH:
   752         /* Check this catch block's body. */
   753         return HasFinalReturn(pn->pn_kid3);
   755       case PNK_LET:
   756         /* Non-binary let statements are let declarations. */
   757         if (!pn->isArity(PN_BINARY))
   758             return ENDS_IN_OTHER;
   759         return HasFinalReturn(pn->pn_right);
   761       default:
   762         return ENDS_IN_OTHER;
   763     }
   764 }
   766 static int
   767 HasFinalReturn(SyntaxParseHandler::Node pn)
   768 {
   769     return ENDS_IN_RETURN;
   770 }
   772 template <typename ParseHandler>
   773 bool
   774 Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
   775                                       unsigned errnum, unsigned anonerrnum)
   776 {
   777     JSAutoByteString name;
   778     JSAtom *atom = pc->sc->asFunctionBox()->function()->atom();
   779     if (atom) {
   780         if (!AtomToPrintableString(context, atom, &name))
   781             return false;
   782     } else {
   783         errnum = anonerrnum;
   784     }
   785     return report(kind, pc->sc->strict, pn, errnum, name.ptr());
   786 }
   788 template <typename ParseHandler>
   789 bool
   790 Parser<ParseHandler>::checkFinalReturn(Node pn)
   791 {
   792     JS_ASSERT(pc->sc->isFunctionBox());
   793     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
   794            reportBadReturn(pn, ParseExtraWarning,
   795                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
   796 }
   798 /*
   799  * Check that assigning to lhs is permitted.  Assigning to 'eval' or
   800  * 'arguments' is banned in strict mode and in destructuring assignment.
   801  */
   802 template <typename ParseHandler>
   803 bool
   804 Parser<ParseHandler>::checkStrictAssignment(Node lhs, AssignmentFlavor flavor)
   805 {
   806     if (!pc->sc->needStrictChecks() && flavor != KeyedDestructuringAssignment)
   807         return true;
   809     JSAtom *atom = handler.isName(lhs);
   810     if (!atom)
   811         return true;
   813     if (atom == context->names().eval || atom == context->names().arguments) {
   814         JSAutoByteString name;
   815         if (!AtomToPrintableString(context, atom, &name))
   816             return false;
   818         ParseReportKind kind;
   819         unsigned errnum;
   820         if (pc->sc->strict || flavor != KeyedDestructuringAssignment) {
   821             kind = ParseStrictError;
   822             errnum = JSMSG_BAD_STRICT_ASSIGN;
   823         } else {
   824             kind = ParseError;
   825             errnum = JSMSG_BAD_DESTRUCT_ASSIGN;
   826         }
   827         if (!report(kind, pc->sc->strict, lhs, errnum, name.ptr()))
   828             return false;
   829     }
   830     return true;
   831 }
   833 /*
   834  * Check that it is permitted to introduce a binding for atom.  Strict mode
   835  * forbids introducing new definitions for 'eval', 'arguments', or for any
   836  * strict mode reserved keyword.  Use pn for reporting error locations, or use
   837  * pc's token stream if pn is nullptr.
   838  */
   839 template <typename ParseHandler>
   840 bool
   841 Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
   842 {
   843     if (!pc->sc->needStrictChecks())
   844         return true;
   846     if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
   847         JSAutoByteString bytes;
   848         if (!AtomToPrintableString(context, name, &bytes))
   849             return false;
   850         return report(ParseStrictError, pc->sc->strict, pn,
   851                       JSMSG_BAD_BINDING, bytes.ptr());
   852     }
   854     return true;
   855 }
   857 template <>
   858 ParseNode *
   859 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
   860                                                  GeneratorKind generatorKind,
   861                                                  Directives inheritedDirectives,
   862                                                  Directives *newDirectives)
   863 {
   864     Node fn = handler.newFunctionDefinition();
   865     if (!fn)
   866         return null();
   868     ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &handler);
   869     if (!argsbody)
   870         return null();
   871     argsbody->setOp(JSOP_NOP);
   872     argsbody->makeEmpty();
   873     fn->pn_body = argsbody;
   875     FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ nullptr, inheritedDirectives,
   876                                          generatorKind);
   877     if (!funbox)
   878         return null();
   879     funbox->length = fun->nargs() - fun->hasRest();
   880     handler.setFunctionBox(fn, funbox);
   882     ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives,
   883                                          /* staticLevel = */ 0, /* bodyid = */ 0,
   884                                          /* blockScopeDepth = */ 0);
   885     if (!funpc.init(tokenStream))
   886         return null();
   888     for (unsigned i = 0; i < formals.length(); i++) {
   889         if (!defineArg(fn, formals[i]))
   890             return null();
   891     }
   893     ParseNode *pn = functionBody(Statement, StatementListBody);
   894     if (!pn)
   895         return null();
   897     if (!tokenStream.matchToken(TOK_EOF)) {
   898         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
   899         return null();
   900     }
   902     if (!FoldConstants(context, &pn, this))
   903         return null();
   905     InternalHandle<Bindings*> funboxBindings =
   906         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
   907     if (!funpc.generateFunctionBindings(context, tokenStream, alloc, funboxBindings))
   908         return null();
   910     JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
   911     fn->pn_body->append(pn);
   912     fn->pn_body->pn_pos = pn->pn_pos;
   913     return fn;
   914 }
   916 template <>
   917 bool
   918 Parser<FullParseHandler>::checkFunctionArguments()
   919 {
   920     /*
   921      * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope
   922      * operation which means it aliases any bindings with the same name.
   923      */
   924     if (FuncStmtSet *set = pc->funcStmts) {
   925         for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) {
   926             PropertyName *name = r.front()->asPropertyName();
   927             if (Definition *dn = pc->decls().lookupFirst(name))
   928                 dn->pn_dflags |= PND_CLOSED;
   929         }
   930     }
   932     /* Time to implement the odd semantics of 'arguments'. */
   933     HandlePropertyName arguments = context->names().arguments;
   935     /*
   936      * As explained by the ContextFlags::funArgumentsHasLocalBinding comment,
   937      * create a declaration for 'arguments' if there are any unbound uses in
   938      * the function body.
   939      */
   940     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
   941         if (r.front().key() == arguments) {
   942             Definition *dn = r.front().value().get<FullParseHandler>();
   943             pc->lexdeps->remove(arguments);
   944             dn->pn_dflags |= PND_IMPLICITARGUMENTS;
   945             if (!pc->define(tokenStream, arguments, dn, Definition::VAR))
   946                 return false;
   947             pc->sc->asFunctionBox()->usesArguments = true;
   948             break;
   949         }
   950     }
   952     /*
   953      * Report error if both rest parameters and 'arguments' are used. Do this
   954      * check before adding artificial 'arguments' below.
   955      */
   956     Definition *maybeArgDef = pc->decls().lookupFirst(arguments);
   957     bool argumentsHasBinding = !!maybeArgDef;
   958     bool argumentsHasLocalBinding = maybeArgDef && maybeArgDef->kind() != Definition::ARG;
   959     bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
   960     if (hasRest && argumentsHasLocalBinding) {
   961         report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST);
   962         return false;
   963     }
   965     /*
   966      * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
   967      * forces an 'arguments' binding. The exception is that functions with rest
   968      * parameters are free from 'arguments'.
   969      */
   970     if (!argumentsHasBinding && pc->sc->bindingsAccessedDynamically() && !hasRest) {
   971         ParseNode *pn = newName(arguments);
   972         if (!pn)
   973             return false;
   974         if (!pc->define(tokenStream, arguments, pn, Definition::VAR))
   975             return false;
   976         argumentsHasBinding = true;
   977         argumentsHasLocalBinding = true;
   978     }
   980     /*
   981      * Now that all possible 'arguments' bindings have been added, note whether
   982      * 'arguments' has a local binding and whether it unconditionally needs an
   983      * arguments object. (Also see the flags' comments in ContextFlags.)
   984      */
   985     if (argumentsHasLocalBinding) {
   986         FunctionBox *funbox = pc->sc->asFunctionBox();
   987         funbox->setArgumentsHasLocalBinding();
   989         /*
   990          * If a script has both explicit mentions of 'arguments' and dynamic
   991          * name lookups which could access the arguments, an arguments object
   992          * must be created eagerly. The SSA analysis used for lazy arguments
   993          * cannot cope with dynamic name accesses, so any 'arguments' accessed
   994          * via a NAME opcode must force construction of the arguments object.
   995          */
   996         if (pc->sc->bindingsAccessedDynamically() && maybeArgDef)
   997             funbox->setDefinitelyNeedsArgsObj();
   999         /*
  1000          * If a script contains the debugger statement either directly or
  1001          * within an inner function, the arguments object must be created
  1002          * eagerly. The debugger can walk the scope chain and observe any
  1003          * values along it.
  1004          */
  1005         if (pc->sc->hasDebuggerStatement())
  1006             funbox->setDefinitelyNeedsArgsObj();
  1008         /*
  1009          * Check whether any parameters have been assigned within this
  1010          * function. In strict mode parameters do not alias arguments[i], and
  1011          * to make the arguments object reflect initial parameter values prior
  1012          * to any mutation we create it eagerly whenever parameters are (or
  1013          * might, in the case of calls to eval) be assigned.
  1014          */
  1015         if (pc->sc->needStrictChecks()) {
  1016             for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) {
  1017                 DefinitionList &dlist = r.front().value();
  1018                 for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) {
  1019                     Definition *dn = dr.front<FullParseHandler>();
  1020                     if (dn->kind() == Definition::ARG && dn->isAssigned())
  1021                         funbox->setDefinitelyNeedsArgsObj();
  1024             /* Watch for mutation of arguments through e.g. eval(). */
  1025             if (pc->sc->bindingsAccessedDynamically())
  1026                 funbox->setDefinitelyNeedsArgsObj();
  1030     return true;
  1033 template <>
  1034 bool
  1035 Parser<SyntaxParseHandler>::checkFunctionArguments()
  1037     bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
  1039     if (pc->lexdeps->lookup(context->names().arguments)) {
  1040         pc->sc->asFunctionBox()->usesArguments = true;
  1041         if (hasRest) {
  1042             report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
  1043             return false;
  1045     } else if (hasRest) {
  1046         DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments);
  1047         if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) {
  1048             report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
  1049             return false;
  1053     return true;
  1056 template <typename ParseHandler>
  1057 typename ParseHandler::Node
  1058 Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType type)
  1060     JS_ASSERT(pc->sc->isFunctionBox());
  1061     JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
  1063 #ifdef DEBUG
  1064     uint32_t startYieldOffset = pc->lastYieldOffset;
  1065 #endif
  1067     Node pn;
  1068     if (type == StatementListBody) {
  1069         pn = statements();
  1070         if (!pn)
  1071             return null();
  1072     } else {
  1073         JS_ASSERT(type == ExpressionBody);
  1074         JS_ASSERT(JS_HAS_EXPR_CLOSURES);
  1076         Node kid = assignExpr();
  1077         if (!kid)
  1078             return null();
  1080         pn = handler.newReturnStatement(kid, handler.getPosition(kid));
  1081         if (!pn)
  1082             return null();
  1085     switch (pc->generatorKind()) {
  1086       case NotGenerator:
  1087         JS_ASSERT(pc->lastYieldOffset == startYieldOffset);
  1088         break;
  1090       case LegacyGenerator:
  1091         // FIXME: Catch these errors eagerly, in yieldExpression().
  1092         JS_ASSERT(pc->lastYieldOffset != startYieldOffset);
  1093         if (kind == Arrow) {
  1094             reportWithOffset(ParseError, false, pc->lastYieldOffset,
  1095                              JSMSG_YIELD_IN_ARROW, js_yield_str);
  1096             return null();
  1098         if (type == ExpressionBody) {
  1099             reportBadReturn(pn, ParseError,
  1100                             JSMSG_BAD_GENERATOR_RETURN,
  1101                             JSMSG_BAD_ANON_GENERATOR_RETURN);
  1102             return null();
  1104         break;
  1106       case StarGenerator:
  1107         JS_ASSERT(kind != Arrow);
  1108         JS_ASSERT(type == StatementListBody);
  1109         break;
  1112     /* Check for falling off the end of a function that returns a value. */
  1113     if (options().extraWarningsOption && pc->funHasReturnExpr && !checkFinalReturn(pn))
  1114         return null();
  1116     /* Define the 'arguments' binding if necessary. */
  1117     if (!checkFunctionArguments())
  1118         return null();
  1120     return pn;
  1123 /* See comment for use in Parser::functionDef. */
  1124 template <>
  1125 bool
  1126 Parser<FullParseHandler>::makeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom)
  1128     /* Turn pn into a definition. */
  1129     pc->updateDecl(atom, pn);
  1131     /* Change all uses of dn to be uses of pn. */
  1132     for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
  1133         JS_ASSERT(pnu->isUsed());
  1134         JS_ASSERT(!pnu->isDefn());
  1135         pnu->pn_lexdef = (Definition *) pn;
  1136         pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
  1138     pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
  1139     pn->dn_uses = dn;
  1141     /*
  1142      * A PNK_FUNCTION node must be a definition, so convert shadowed function
  1143      * statements into nops. This is valid since all body-level function
  1144      * statement initialization happens at the beginning of the function
  1145      * (thus, only the last statement's effect is visible). E.g., in
  1147      *   function outer() {
  1148      *     function g() { return 1 }
  1149      *     assertEq(g(), 2);
  1150      *     function g() { return 2 }
  1151      *     assertEq(g(), 2);
  1152      *   }
  1154      * both asserts are valid.
  1155      */
  1156     if (dn->getKind() == PNK_FUNCTION) {
  1157         JS_ASSERT(dn->functionIsHoisted());
  1158         pn->dn_uses = dn->pn_link;
  1159         handler.prepareNodeForMutation(dn);
  1160         dn->setKind(PNK_NOP);
  1161         dn->setArity(PN_NULLARY);
  1162         return true;
  1165     /*
  1166      * If dn is arg, or in [var, const, let] and has an initializer, then we
  1167      * must rewrite it to be an assignment node, whose freshly allocated
  1168      * left-hand side becomes a use of pn.
  1169      */
  1170     if (dn->canHaveInitializer()) {
  1171         if (ParseNode *rhs = dn->expr()) {
  1172             ParseNode *lhs = handler.makeAssignment(dn, rhs);
  1173             if (!lhs)
  1174                 return false;
  1175             pn->dn_uses = lhs;
  1176             dn->pn_link = nullptr;
  1177             dn = (Definition *) lhs;
  1181     /* Turn dn into a use of pn. */
  1182     JS_ASSERT(dn->isKind(PNK_NAME));
  1183     JS_ASSERT(dn->isArity(PN_NAME));
  1184     JS_ASSERT(dn->pn_atom == atom);
  1185     dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME);
  1186     dn->setDefn(false);
  1187     dn->setUsed(true);
  1188     dn->pn_lexdef = (Definition *) pn;
  1189     dn->pn_cookie.makeFree();
  1190     dn->pn_dflags &= ~PND_BOUND;
  1191     return true;
  1194 /*
  1195  * Parameter block types for the several Binder functions.  We use a common
  1196  * helper function signature in order to share code among destructuring and
  1197  * simple variable declaration parsers.  In the destructuring case, the binder
  1198  * function is called indirectly from the variable declaration parser by way
  1199  * of CheckDestructuring and its friends.
  1200  */
  1202 template <typename ParseHandler>
  1203 struct BindData
  1205     BindData(ExclusiveContext *cx) : let(cx) {}
  1207     typedef bool
  1208     (*Binder)(BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser);
  1210     /* name node for definition processing and error source coordinates */
  1211     typename ParseHandler::Node pn;
  1213     JSOp            op;         /* prolog bytecode or nop */
  1214     Binder          binder;     /* binder, discriminates u */
  1216     struct LetData {
  1217         LetData(ExclusiveContext *cx) : blockObj(cx) {}
  1218         VarContext varContext;
  1219         RootedStaticBlockObject blockObj;
  1220         unsigned   overflow;
  1221     } let;
  1223     void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) {
  1224         this->pn = ParseHandler::null();
  1225         this->op = JSOP_NOP;
  1226         this->binder = Parser<ParseHandler>::bindLet;
  1227         this->let.varContext = varContext;
  1228         this->let.blockObj = &blockObj;
  1229         this->let.overflow = overflow;
  1232     void initVarOrConst(JSOp op) {
  1233         this->op = op;
  1234         this->binder = Parser<ParseHandler>::bindVarOrConst;
  1236 };
  1238 template <typename ParseHandler>
  1239 JSFunction *
  1240 Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
  1241                                   FunctionSyntaxKind kind, JSObject *proto)
  1243     JS_ASSERT_IF(kind == Statement, atom != nullptr);
  1245     /*
  1246      * Find the global compilation context in order to pre-set the newborn
  1247      * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the
  1248      * global context is a compile-and-go one, we leave the pre-set parent
  1249      * intact; otherwise we clear parent and proto.
  1250      */
  1251     while (pc->parent)
  1252         pc = pc->parent;
  1254     RootedFunction fun(context);
  1255     JSFunction::Flags flags = (kind == Expression)
  1256                               ? JSFunction::INTERPRETED_LAMBDA
  1257                               : (kind == Arrow)
  1258                                 ? JSFunction::INTERPRETED_LAMBDA_ARROW
  1259                                 : JSFunction::INTERPRETED;
  1260     gc::AllocKind allocKind = JSFunction::FinalizeKind;
  1261     if (kind == Arrow)
  1262         allocKind = JSFunction::ExtendedFinalizeKind;
  1263     fun = NewFunctionWithProto(context, NullPtr(), nullptr, 0, flags, NullPtr(), atom, proto,
  1264                                allocKind, MaybeSingletonObject);
  1265     if (!fun)
  1266         return nullptr;
  1267     if (options().selfHostingMode)
  1268         fun->setIsSelfHostedBuiltin();
  1269     return fun;
  1272 static bool
  1273 MatchOrInsertSemicolon(TokenStream &ts)
  1275     TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
  1276     if (tt == TOK_ERROR)
  1277         return false;
  1278     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
  1279         /* Advance the scanner for proper error location reporting. */
  1280         ts.getToken(TokenStream::Operand);
  1281         ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
  1282         return false;
  1284     (void) ts.matchToken(TOK_SEMI);
  1285     return true;
  1288 template <typename ParseHandler>
  1289 typename ParseHandler::DefinitionNode
  1290 Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
  1292     AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
  1293     if (p)
  1294         return p.value().get<ParseHandler>();
  1296     DefinitionNode dn = handler.newPlaceholder(atom, pc->blockid(), pos());
  1297     if (!dn)
  1298         return ParseHandler::nullDefinition();
  1299     DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
  1300     if (!pc->lexdeps->add(p, atom, def))
  1301         return ParseHandler::nullDefinition();
  1302     return dn;
  1305 static bool
  1306 ConvertDefinitionToNamedLambdaUse(TokenStream &ts, ParseContext<FullParseHandler> *pc,
  1307                                   FunctionBox *funbox, Definition *dn)
  1309     dn->setOp(JSOP_CALLEE);
  1310     if (!dn->pn_cookie.set(ts, pc->staticLevel, 0))
  1311         return false;
  1312     dn->pn_dflags |= PND_BOUND;
  1313     JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
  1315     /*
  1316      * Since 'dn' is a placeholder, it has not been defined in the
  1317      * ParseContext and hence we must manually flag a closed-over
  1318      * callee name as needing a dynamic scope (this is done for all
  1319      * definitions in the ParseContext by generateFunctionBindings).
  1321      * If 'dn' has been assigned to, then we also flag the function
  1322      * scope has needing a dynamic scope so that dynamic scope
  1323      * setter can either ignore the set (in non-strict mode) or
  1324      * produce an error (in strict mode).
  1325      */
  1326     if (dn->isClosed() || dn->isAssigned())
  1327         funbox->setNeedsDeclEnvObject();
  1328     return true;
  1331 /*
  1332  * Beware: this function is called for functions nested in other functions or
  1333  * global scripts but not for functions compiled through the Function
  1334  * constructor or JSAPI. To always execute code when a function has finished
  1335  * parsing, use Parser::functionBody.
  1336  */
  1337 template <>
  1338 bool
  1339 Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHandler> *outerpc,
  1340                                         FunctionSyntaxKind kind)
  1342     outerpc->blockidGen = pc->blockidGen;
  1344     FunctionBox *funbox = fn->pn_funbox;
  1345     JS_ASSERT(funbox == pc->sc->asFunctionBox());
  1347     /* Propagate unresolved lexical names up to outerpc->lexdeps. */
  1348     if (pc->lexdeps->count()) {
  1349         for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
  1350             JSAtom *atom = r.front().key();
  1351             Definition *dn = r.front().value().get<FullParseHandler>();
  1352             JS_ASSERT(dn->isPlaceholder());
  1354             if (atom == funbox->function()->name() && kind == Expression) {
  1355                 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
  1356                     return false;
  1357                 continue;
  1360             Definition *outer_dn = outerpc->decls().lookupFirst(atom);
  1362             /*
  1363              * Make sure to deoptimize lexical dependencies that are polluted
  1364              * by eval and function statements (which both flag the function as
  1365              * having an extensible scope) or any enclosing 'with'.
  1366              */
  1367             if (funbox->hasExtensibleScope() || outerpc->parsingWith)
  1368                 handler.deoptimizeUsesWithin(dn, fn->pn_pos);
  1370             if (!outer_dn) {
  1371                 /*
  1372                  * Create a new placeholder for our outer lexdep. We could
  1373                  * simply re-use the inner placeholder, but that introduces
  1374                  * subtleties in the case where we find a later definition
  1375                  * that captures an existing lexdep. For example:
  1377                  *   function f() { function g() { x; } let x; }
  1379                  * Here, g's TOK_UPVARS node lists the placeholder for x,
  1380                  * which must be captured by the 'let' declaration later,
  1381                  * since 'let's are hoisted.  Taking g's placeholder as our
  1382                  * own would work fine. But consider:
  1384                  *   function f() { x; { function g() { x; } let x; } }
  1386                  * Here, the 'let' must not capture all the uses of f's
  1387                  * lexdep entry for x, but it must capture the x node
  1388                  * referred to from g's TOK_UPVARS node.  Always turning
  1389                  * inherited lexdeps into uses of a new outer definition
  1390                  * allows us to handle both these cases in a natural way.
  1391                  */
  1392                 outer_dn = getOrCreateLexicalDependency(outerpc, atom);
  1393                 if (!outer_dn)
  1394                     return false;
  1397             /*
  1398              * Insert dn's uses list at the front of outer_dn's list.
  1400              * Without loss of generality or correctness, we allow a dn to
  1401              * be in inner and outer lexdeps, since the purpose of lexdeps
  1402              * is one-pass coordination of name use and definition across
  1403              * functions, and if different dn's are used we'll merge lists
  1404              * when leaving the inner function.
  1406              * The dn == outer_dn case arises with generator expressions
  1407              * (see LegacyCompExprTransplanter::transplant, the PN_CODE/PN_NAME
  1408              * case), and nowhere else, currently.
  1409              */
  1410             if (dn != outer_dn) {
  1411                 if (ParseNode *pnu = dn->dn_uses) {
  1412                     while (true) {
  1413                         pnu->pn_lexdef = outer_dn;
  1414                         if (!pnu->pn_link)
  1415                             break;
  1416                         pnu = pnu->pn_link;
  1418                     pnu->pn_link = outer_dn->dn_uses;
  1419                     outer_dn->dn_uses = dn->dn_uses;
  1420                     dn->dn_uses = nullptr;
  1423                 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
  1426             /* Mark the outer dn as escaping. */
  1427             outer_dn->pn_dflags |= PND_CLOSED;
  1431     InternalHandle<Bindings*> bindings =
  1432         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
  1433     return pc->generateFunctionBindings(context, tokenStream, alloc, bindings);
  1436 template <>
  1437 bool
  1438 Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler> *outerpc,
  1439                                           FunctionSyntaxKind kind)
  1441     outerpc->blockidGen = pc->blockidGen;
  1443     FunctionBox *funbox = pc->sc->asFunctionBox();
  1444     return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
  1447 /*
  1448  * defineArg is called for both the arguments of a regular function definition
  1449  * and the arguments specified by the Function constructor.
  1451  * The 'disallowDuplicateArgs' bool indicates whether the use of another
  1452  * feature (destructuring or default arguments) disables duplicate arguments.
  1453  * (ECMA-262 requires us to support duplicate parameter names, but, for newer
  1454  * features, we consider the code to have "opted in" to higher standards and
  1455  * forbid duplicates.)
  1457  * If 'duplicatedArg' is non-null, then DefineArg assigns to it any previous
  1458  * argument with the same name. The caller may use this to report an error when
  1459  * one of the abovementioned features occurs after a duplicate.
  1460  */
  1461 template <typename ParseHandler>
  1462 bool
  1463 Parser<ParseHandler>::defineArg(Node funcpn, HandlePropertyName name,
  1464                                 bool disallowDuplicateArgs, Node *duplicatedArg)
  1466     SharedContext *sc = pc->sc;
  1468     /* Handle duplicate argument names. */
  1469     if (DefinitionNode prevDecl = pc->decls().lookupFirst(name)) {
  1470         Node pn = handler.getDefinitionNode(prevDecl);
  1472         /*
  1473          * Strict-mode disallows duplicate args. We may not know whether we are
  1474          * in strict mode or not (since the function body hasn't been parsed).
  1475          * In such cases, report will queue up the potential error and return
  1476          * 'true'.
  1477          */
  1478         if (sc->needStrictChecks()) {
  1479             JSAutoByteString bytes;
  1480             if (!AtomToPrintableString(context, name, &bytes))
  1481                 return false;
  1482             if (!report(ParseStrictError, pc->sc->strict, pn,
  1483                         JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
  1485                 return false;
  1489         if (disallowDuplicateArgs) {
  1490             report(ParseError, false, pn, JSMSG_BAD_DUP_ARGS);
  1491             return false;
  1494         if (duplicatedArg)
  1495             *duplicatedArg = pn;
  1497         /* ParseContext::define assumes and asserts prevDecl is not in decls. */
  1498         JS_ASSERT(handler.getDefinitionKind(prevDecl) == Definition::ARG);
  1499         pc->prepareToAddDuplicateArg(name, prevDecl);
  1502     Node argpn = newName(name);
  1503     if (!argpn)
  1504         return false;
  1506     if (!checkStrictBinding(name, argpn))
  1507         return false;
  1509     handler.addFunctionArgument(funcpn, argpn);
  1510     return pc->define(tokenStream, name, argpn, Definition::ARG);
  1513 template <typename ParseHandler>
  1514 /* static */ bool
  1515 Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler> *data,
  1516                                            HandlePropertyName name, Parser<ParseHandler> *parser)
  1518     ParseContext<ParseHandler> *pc = parser->pc;
  1519     JS_ASSERT(pc->sc->isFunctionBox());
  1521     if (pc->decls().lookupFirst(name)) {
  1522         parser->report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
  1523         return false;
  1526     if (!parser->checkStrictBinding(name, data->pn))
  1527         return false;
  1529     return pc->define(parser->tokenStream, name, data->pn, Definition::VAR);
  1532 template <typename ParseHandler>
  1533 bool
  1534 Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, Node funcpn,
  1535                                         bool *hasRest)
  1537     FunctionBox *funbox = pc->sc->asFunctionBox();
  1539     *hasRest = false;
  1541     bool parenFreeArrow = false;
  1542     if (kind == Arrow && tokenStream.peekToken() == TOK_NAME) {
  1543         parenFreeArrow = true;
  1544     } else {
  1545         if (tokenStream.getToken() != TOK_LP) {
  1546             report(ParseError, false, null(),
  1547                    kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
  1548             return false;
  1551         // Record the start of function source (for FunctionToString). If we
  1552         // are parenFreeArrow, we will set this below, after consuming the NAME.
  1553         funbox->setStart(tokenStream);
  1556     Node argsbody = handler.newList(PNK_ARGSBODY);
  1557     if (!argsbody)
  1558         return false;
  1559     handler.setFunctionBody(funcpn, argsbody);
  1561     if (parenFreeArrow || !tokenStream.matchToken(TOK_RP)) {
  1562         bool hasDefaults = false;
  1563         Node duplicatedArg = null();
  1564         Node list = null();
  1566         do {
  1567             if (*hasRest) {
  1568                 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
  1569                 return false;
  1572             TokenKind tt = tokenStream.getToken();
  1573             JS_ASSERT_IF(parenFreeArrow, tt == TOK_NAME);
  1574             switch (tt) {
  1575               case TOK_LB:
  1576               case TOK_LC:
  1578                 /* See comment below in the TOK_NAME case. */
  1579                 if (duplicatedArg) {
  1580                     report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
  1581                     return false;
  1584                 if (hasDefaults) {
  1585                     report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
  1586                     return false;
  1589                 funbox->hasDestructuringArgs = true;
  1591                 /*
  1592                  * A destructuring formal parameter turns into one or more
  1593                  * local variables initialized from properties of a single
  1594                  * anonymous positional parameter, so here we must tweak our
  1595                  * binder and its data.
  1596                  */
  1597                 BindData<ParseHandler> data(context);
  1598                 data.pn = ParseHandler::null();
  1599                 data.op = JSOP_DEFVAR;
  1600                 data.binder = bindDestructuringArg;
  1601                 Node lhs = destructuringExpr(&data, tt);
  1602                 if (!lhs)
  1603                     return false;
  1605                 /*
  1606                  * Synthesize a destructuring assignment from the single
  1607                  * anonymous positional parameter into the destructuring
  1608                  * left-hand-side expression and accumulate it in list.
  1609                  */
  1610                 HandlePropertyName name = context->names().empty;
  1611                 Node rhs = newName(name);
  1612                 if (!rhs)
  1613                     return false;
  1615                 if (!pc->define(tokenStream, name, rhs, Definition::ARG))
  1616                     return false;
  1618                 Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs);
  1619                 if (!item)
  1620                     return false;
  1621                 if (list) {
  1622                     handler.addList(list, item);
  1623                 } else {
  1624                     list = handler.newList(PNK_VAR, item);
  1625                     if (!list)
  1626                         return false;
  1627                     *listp = list;
  1629                 break;
  1632               case TOK_YIELD:
  1633                 if (!checkYieldNameValidity())
  1634                     return false;
  1635                 goto TOK_NAME;
  1637               case TOK_TRIPLEDOT:
  1639                 *hasRest = true;
  1640                 tt = tokenStream.getToken();
  1641                 if (tt != TOK_NAME) {
  1642                     if (tt != TOK_ERROR)
  1643                         report(ParseError, false, null(), JSMSG_NO_REST_NAME);
  1644                     return false;
  1646                 goto TOK_NAME;
  1649               TOK_NAME:
  1650               case TOK_NAME:
  1652                 if (parenFreeArrow)
  1653                     funbox->setStart(tokenStream);
  1655                 RootedPropertyName name(context, tokenStream.currentName());
  1656                 bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
  1657                 if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
  1658                     return false;
  1660                 if (tokenStream.matchToken(TOK_ASSIGN)) {
  1661                     // A default argument without parentheses would look like:
  1662                     // a = expr => body, but both operators are right-associative, so
  1663                     // that would have been parsed as a = (expr => body) instead.
  1664                     // Therefore it's impossible to get here with parenFreeArrow.
  1665                     JS_ASSERT(!parenFreeArrow);
  1667                     if (*hasRest) {
  1668                         report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
  1669                         return false;
  1671                     if (duplicatedArg) {
  1672                         report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
  1673                         return false;
  1675                     if (!hasDefaults) {
  1676                         hasDefaults = true;
  1678                         // The Function.length property is the number of formals
  1679                         // before the first default argument.
  1680                         funbox->length = pc->numArgs() - 1;
  1682                     Node def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT);
  1683                     if (!def_expr)
  1684                         return false;
  1685                     handler.setLastFunctionArgumentDefault(funcpn, def_expr);
  1688                 break;
  1691               default:
  1692                 report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
  1693                 /* FALL THROUGH */
  1694               case TOK_ERROR:
  1695                 return false;
  1697         } while (!parenFreeArrow && tokenStream.matchToken(TOK_COMMA));
  1699         if (!parenFreeArrow && tokenStream.getToken() != TOK_RP) {
  1700             report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
  1701             return false;
  1704         if (!hasDefaults)
  1705             funbox->length = pc->numArgs() - *hasRest;
  1708     return true;
  1711 template <>
  1712 bool
  1713 Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
  1714                                                   ParseNode **pn_, FunctionSyntaxKind kind,
  1715                                                   bool *pbodyProcessed)
  1717     ParseNode *&pn = *pn_;
  1718     *pbodyProcessed = false;
  1720     /* Function statements add a binding to the enclosing scope. */
  1721     bool bodyLevel = pc->atBodyLevel();
  1723     if (kind == Statement) {
  1724         /*
  1725          * Handle redeclaration and optimize cases where we can statically bind the
  1726          * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
  1727          */
  1728         if (Definition *dn = pc->decls().lookupFirst(funName)) {
  1729             JS_ASSERT(!dn->isUsed());
  1730             JS_ASSERT(dn->isDefn());
  1732             if (options().extraWarningsOption || dn->kind() == Definition::CONST) {
  1733                 JSAutoByteString name;
  1734                 ParseReportKind reporter = (dn->kind() != Definition::CONST)
  1735                                            ? ParseExtraWarning
  1736                                            : ParseError;
  1737                 if (!AtomToPrintableString(context, funName, &name) ||
  1738                     !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR,
  1739                             Definition::kindString(dn->kind()), name.ptr()))
  1741                     return false;
  1745             /*
  1746              * Body-level function statements are effectively variable
  1747              * declarations where the initialization is hoisted to the
  1748              * beginning of the block. This means that any other variable
  1749              * declaration with the same name is really just an assignment to
  1750              * the function's binding (which is mutable), so turn any existing
  1751              * declaration into a use.
  1752              */
  1753             if (bodyLevel && !makeDefIntoUse(dn, pn, funName))
  1754                 return false;
  1755         } else if (bodyLevel) {
  1756             /*
  1757              * If this function was used before it was defined, claim the
  1758              * pre-created definition node for this function that primaryExpr
  1759              * put in pc->lexdeps on first forward reference, and recycle pn.
  1760              */
  1761             if (Definition *fn = pc->lexdeps.lookupDefn<FullParseHandler>(funName)) {
  1762                 JS_ASSERT(fn->isDefn());
  1763                 fn->setKind(PNK_FUNCTION);
  1764                 fn->setArity(PN_CODE);
  1765                 fn->pn_pos.begin = pn->pn_pos.begin;
  1766                 fn->pn_pos.end = pn->pn_pos.end;
  1768                 fn->pn_body = nullptr;
  1769                 fn->pn_cookie.makeFree();
  1771                 pc->lexdeps->remove(funName);
  1772                 handler.freeTree(pn);
  1773                 pn = fn;
  1776             if (!pc->define(tokenStream, funName, pn, Definition::VAR))
  1777                 return false;
  1780         if (bodyLevel) {
  1781             JS_ASSERT(pn->functionIsHoisted());
  1782             JS_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree());
  1783             JS_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree());
  1784         } else {
  1785             /*
  1786              * As a SpiderMonkey-specific extension, non-body-level function
  1787              * statements (e.g., functions in an "if" or "while" block) are
  1788              * dynamically bound when control flow reaches the statement.
  1789              */
  1790             JS_ASSERT(!pc->sc->strict);
  1791             JS_ASSERT(pn->pn_cookie.isFree());
  1792             if (pc->sc->isFunctionBox()) {
  1793                 FunctionBox *funbox = pc->sc->asFunctionBox();
  1794                 funbox->setMightAliasLocals();
  1795                 funbox->setHasExtensibleScope();
  1797             pn->setOp(JSOP_DEFFUN);
  1799             /*
  1800              * Instead of setting bindingsAccessedDynamically, which would be
  1801              * overly conservative, remember the names of all function
  1802              * statements and mark any bindings with the same as aliased at the
  1803              * end of functionBody.
  1804              */
  1805             if (!pc->funcStmts) {
  1806                 pc->funcStmts = context->new_<FuncStmtSet>(context);
  1807                 if (!pc->funcStmts || !pc->funcStmts->init())
  1808                     return false;
  1810             if (!pc->funcStmts->put(funName))
  1811                 return false;
  1813             /*
  1814              * Due to the implicit declaration mechanism, 'arguments' will not
  1815              * have decls and, even if it did, they will not be noted as closed
  1816              * in the emitter. Thus, in the corner case of function statements
  1817              * overridding arguments, flag the whole scope as dynamic.
  1818              */
  1819             if (funName == context->names().arguments)
  1820                 pc->sc->setBindingsAccessedDynamically();
  1823         /* No further binding (in BindNameToSlot) is needed for functions. */
  1824         pn->pn_dflags |= PND_BOUND;
  1825     } else {
  1826         /* A function expression does not introduce any binding. */
  1827         pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
  1830     // When a lazily-parsed function is called, we only fully parse (and emit)
  1831     // that function, not any of its nested children. The initial syntax-only
  1832     // parse recorded the free variables of nested functions and their extents,
  1833     // so we can skip over them after accounting for their free variables.
  1834     if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
  1835         JSFunction *fun = handler.nextLazyInnerFunction();
  1836         JS_ASSERT(!fun->isLegacyGenerator());
  1837         FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false),
  1838                                              fun->generatorKind());
  1839         if (!funbox)
  1840             return false;
  1842         if (!addFreeVariablesFromLazyFunction(fun, pc))
  1843             return false;
  1845         // The position passed to tokenStream.advance() is relative to
  1846         // userbuf.base() while LazyScript::{begin,end} offsets are relative to
  1847         // the outermost script source. N.B: userbuf.base() is initialized
  1848         // (in TokenStream()) to begin() - column() so that column numbers in
  1849         // the lazily parsed script are correct.
  1850         uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
  1851         tokenStream.advance(fun->lazyScript()->end() - userbufBase);
  1853         *pbodyProcessed = true;
  1854         return true;
  1857     return true;
  1860 template <class T, class U>
  1861 static inline void
  1862 PropagateTransitiveParseFlags(const T *inner, U *outer)
  1864    if (inner->bindingsAccessedDynamically())
  1865      outer->setBindingsAccessedDynamically();
  1866    if (inner->hasDebuggerStatement())
  1867      outer->setHasDebuggerStatement();
  1870 template <typename ParseHandler>
  1871 bool
  1872 Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun,
  1873                                                        ParseContext<ParseHandler> *pc)
  1875     // Update any definition nodes in this context according to free variables
  1876     // in a lazily parsed inner function.
  1878     LazyScript *lazy = fun->lazyScript();
  1879     HeapPtrAtom *freeVariables = lazy->freeVariables();
  1880     for (size_t i = 0; i < lazy->numFreeVariables(); i++) {
  1881         JSAtom *atom = freeVariables[i];
  1883         // 'arguments' will be implicitly bound within the inner function.
  1884         if (atom == context->names().arguments)
  1885             continue;
  1887         DefinitionNode dn = pc->decls().lookupFirst(atom);
  1889         if (!dn) {
  1890             dn = getOrCreateLexicalDependency(pc, atom);
  1891             if (!dn)
  1892                 return false;
  1895         /* Mark the outer dn as escaping. */
  1896         handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED);
  1899     PropagateTransitiveParseFlags(lazy, pc->sc);
  1900     return true;
  1903 template <>
  1904 bool
  1905 Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
  1906                                                     Node *pn, FunctionSyntaxKind kind,
  1907                                                     bool *pbodyProcessed)
  1909     *pbodyProcessed = false;
  1911     /* Function statements add a binding to the enclosing scope. */
  1912     bool bodyLevel = pc->atBodyLevel();
  1914     if (kind == Statement) {
  1915         /*
  1916          * Handle redeclaration and optimize cases where we can statically bind the
  1917          * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
  1918          */
  1919         if (DefinitionNode dn = pc->decls().lookupFirst(funName)) {
  1920             if (dn == Definition::CONST) {
  1921                 JSAutoByteString name;
  1922                 if (!AtomToPrintableString(context, funName, &name) ||
  1923                     !report(ParseError, false, null(), JSMSG_REDECLARED_VAR,
  1924                             Definition::kindString(dn), name.ptr()))
  1926                     return false;
  1929         } else if (bodyLevel) {
  1930             if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName))
  1931                 pc->lexdeps->remove(funName);
  1933             if (!pc->define(tokenStream, funName, *pn, Definition::VAR))
  1934                 return false;
  1937         if (!bodyLevel && funName == context->names().arguments)
  1938             pc->sc->setBindingsAccessedDynamically();
  1941     if (kind == Arrow) {
  1942         /* Arrow functions cannot yet be parsed lazily. */
  1943         return abortIfSyntaxParser();
  1946     return true;
  1949 template <typename ParseHandler>
  1950 typename ParseHandler::Node
  1951 Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
  1952                                   FunctionType type, FunctionSyntaxKind kind,
  1953                                   GeneratorKind generatorKind)
  1955     JS_ASSERT_IF(kind == Statement, funName);
  1957     /* Make a TOK_FUNCTION node. */
  1958     Node pn = handler.newFunctionDefinition();
  1959     if (!pn)
  1960         return null();
  1962     bool bodyProcessed;
  1963     if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
  1964         return null();
  1966     if (bodyProcessed)
  1967         return pn;
  1969     RootedObject proto(context);
  1970     if (generatorKind == StarGenerator) {
  1971         // If we are off the main thread, the generator meta-objects have
  1972         // already been created by js::StartOffThreadParseScript, so cx will not
  1973         // be necessary.
  1974         JSContext *cx = context->maybeJSContext();
  1975         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
  1976         if (!proto)
  1977             return null();
  1979     RootedFunction fun(context, newFunction(pc, funName, kind, proto));
  1980     if (!fun)
  1981         return null();
  1983     // Speculatively parse using the directives of the parent parsing context.
  1984     // If a directive is encountered (e.g., "use strict") that changes how the
  1985     // function should have been parsed, we backup and reparse with the new set
  1986     // of directives.
  1987     Directives directives(pc);
  1988     Directives newDirectives = directives;
  1990     while (true) {
  1991         if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives))
  1992             break;
  1993         if (tokenStream.hadError() || directives == newDirectives)
  1994             return null();
  1996         // Assignment must be monotonic to prevent reparsing iloops
  1997         JS_ASSERT_IF(directives.strict(), newDirectives.strict());
  1998         JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
  1999         directives = newDirectives;
  2001         tokenStream.seek(start);
  2002         if (funName && tokenStream.getToken() == TOK_ERROR)
  2003             return null();
  2005         // functionArgsAndBody may have already set pn->pn_body before failing.
  2006         handler.setFunctionBody(pn, null());
  2009     return pn;
  2012 template <>
  2013 bool
  2014 Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
  2015                                                    ParseNode *prelude, ParseNode *body)
  2017     pn->pn_pos.end = pos().end;
  2019     /*
  2020      * If there were destructuring formal parameters, prepend the initializing
  2021      * comma expression that we synthesized to body. If the body is a return
  2022      * node, we must make a special PNK_SEQ node, to prepend the destructuring
  2023      * code without bracing the decompilation of the function body.
  2024      */
  2025     if (prelude) {
  2026         if (!body->isArity(PN_LIST)) {
  2027             ParseNode *block;
  2029             block = ListNode::create(PNK_SEQ, &handler);
  2030             if (!block)
  2031                 return false;
  2032             block->pn_pos = body->pn_pos;
  2033             block->initList(body);
  2035             body = block;
  2038         ParseNode *item = UnaryNode::create(PNK_SEMI, &handler);
  2039         if (!item)
  2040             return false;
  2042         item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
  2043         item->pn_kid = prelude;
  2044         item->pn_next = body->pn_head;
  2045         body->pn_head = item;
  2046         if (body->pn_tail == &body->pn_head)
  2047             body->pn_tail = &item->pn_next;
  2048         ++body->pn_count;
  2049         body->pn_xflags |= PNX_DESTRUCT;
  2052     JS_ASSERT(pn->pn_funbox == funbox);
  2053     JS_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY));
  2054     pn->pn_body->append(body);
  2055     pn->pn_body->pn_pos = body->pn_pos;
  2057     return true;
  2060 template <>
  2061 bool
  2062 Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox,
  2063                                                      Node prelude, Node body)
  2065     // The LazyScript for a lazily parsed function needs to be constructed
  2066     // while its ParseContext and associated lexdeps and inner functions are
  2067     // still available.
  2069     if (funbox->inWith)
  2070         return abortIfSyntaxParser();
  2072     size_t numFreeVariables = pc->lexdeps->count();
  2073     size_t numInnerFunctions = pc->innerFunctions.length();
  2075     RootedFunction fun(context, funbox->function());
  2076     LazyScript *lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions,
  2077                                              versionNumber(), funbox->bufStart, funbox->bufEnd,
  2078                                              funbox->startLine, funbox->startColumn);
  2079     if (!lazy)
  2080         return false;
  2082     HeapPtrAtom *freeVariables = lazy->freeVariables();
  2083     size_t i = 0;
  2084     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
  2085         freeVariables[i++].init(r.front().key());
  2086     JS_ASSERT(i == numFreeVariables);
  2088     HeapPtrFunction *innerFunctions = lazy->innerFunctions();
  2089     for (size_t i = 0; i < numInnerFunctions; i++)
  2090         innerFunctions[i].init(pc->innerFunctions[i]);
  2092     if (pc->sc->strict)
  2093         lazy->setStrict();
  2094     lazy->setGeneratorKind(funbox->generatorKind());
  2095     if (funbox->usesArguments && funbox->usesApply)
  2096         lazy->setUsesArgumentsAndApply();
  2097     PropagateTransitiveParseFlags(funbox, lazy);
  2099     fun->initLazyScript(lazy);
  2100     return true;
  2103 template <>
  2104 bool
  2105 Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
  2106                                               FunctionType type, FunctionSyntaxKind kind,
  2107                                               GeneratorKind generatorKind,
  2108                                               Directives inheritedDirectives,
  2109                                               Directives *newDirectives)
  2111     ParseContext<FullParseHandler> *outerpc = pc;
  2113     // Create box for fun->object early to protect against last-ditch GC.
  2114     FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
  2115     if (!funbox)
  2116         return false;
  2118     // Try a syntax parse for this inner function.
  2119     do {
  2120         Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
  2121         if (!parser)
  2122             break;
  2125             // Move the syntax parser to the current position in the stream.
  2126             TokenStream::Position position(keepAtoms);
  2127             tokenStream.tell(&position);
  2128             if (!parser->tokenStream.seek(position, tokenStream))
  2129                 return false;
  2131             ParseContext<SyntaxParseHandler> funpc(parser, outerpc, SyntaxParseHandler::null(), funbox,
  2132                                                    newDirectives, outerpc->staticLevel + 1,
  2133                                                    outerpc->blockidGen,
  2134                                                    /* blockScopeDepth = */ 0);
  2135             if (!funpc.init(tokenStream))
  2136                 return false;
  2138             if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
  2139                                                     fun, type, kind, newDirectives))
  2141                 if (parser->hadAbortedSyntaxParse()) {
  2142                     // Try again with a full parse.
  2143                     parser->clearAbortedSyntaxParse();
  2144                     break;
  2146                 return false;
  2149             outerpc->blockidGen = funpc.blockidGen;
  2151             // Advance this parser over tokens processed by the syntax parser.
  2152             parser->tokenStream.tell(&position);
  2153             if (!tokenStream.seek(position, parser->tokenStream))
  2154                 return false;
  2156             // Update the end position of the parse node.
  2157             pn->pn_pos.end = tokenStream.currentToken().pos.end;
  2160         if (!addFreeVariablesFromLazyFunction(fun, pc))
  2161             return false;
  2163         pn->pn_blockid = outerpc->blockid();
  2164         PropagateTransitiveParseFlags(funbox, outerpc->sc);
  2165         return true;
  2166     } while (false);
  2168     // Continue doing a full parse for this inner function.
  2169     ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives,
  2170                                          outerpc->staticLevel + 1, outerpc->blockidGen,
  2171                                          /* blockScopeDepth = */ 0);
  2172     if (!funpc.init(tokenStream))
  2173         return false;
  2175     if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
  2176         return false;
  2178     if (!leaveFunction(pn, outerpc, kind))
  2179         return false;
  2181     pn->pn_blockid = outerpc->blockid();
  2183     /*
  2184      * Fruit of the poisonous tree: if a closure contains a dynamic name access
  2185      * (eval, with, etc), we consider the parent to do the same. The reason is
  2186      * that the deoptimizing effects of dynamic name access apply equally to
  2187      * parents: any local can be read at runtime.
  2188      */
  2189     PropagateTransitiveParseFlags(funbox, outerpc->sc);
  2190     return true;
  2193 template <>
  2194 bool
  2195 Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
  2196                                                 FunctionType type, FunctionSyntaxKind kind,
  2197                                                 GeneratorKind generatorKind,
  2198                                                 Directives inheritedDirectives,
  2199                                                 Directives *newDirectives)
  2201     ParseContext<SyntaxParseHandler> *outerpc = pc;
  2203     // Create box for fun->object early to protect against last-ditch GC.
  2204     FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
  2205     if (!funbox)
  2206         return false;
  2208     // Initialize early for possible flags mutation via destructuringExpr.
  2209     ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives,
  2210                                            outerpc->staticLevel + 1, outerpc->blockidGen,
  2211                                            /* blockScopeDepth = */ 0);
  2212     if (!funpc.init(tokenStream))
  2213         return false;
  2215     if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
  2216         return false;
  2218     if (!leaveFunction(pn, outerpc, kind))
  2219         return false;
  2221     // This is a lazy function inner to another lazy function. Remember the
  2222     // inner function so that if the outer function is eventually parsed we do
  2223     // not need any further parsing or processing of the inner function.
  2224     JS_ASSERT(fun->lazyScript());
  2225     return outerpc->innerFunctions.append(fun);
  2228 template <>
  2229 ParseNode *
  2230 Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
  2231                                                  bool strict, GeneratorKind generatorKind)
  2233     Node pn = handler.newFunctionDefinition();
  2234     if (!pn)
  2235         return null();
  2237     Directives directives(/* strict = */ strict);
  2238     FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ nullptr, directives,
  2239                                          generatorKind);
  2240     if (!funbox)
  2241         return null();
  2242     funbox->length = fun->nargs() - fun->hasRest();
  2244     Directives newDirectives = directives;
  2245     ParseContext<FullParseHandler> funpc(this, /* parent = */ nullptr, pn, funbox,
  2246                                          &newDirectives, staticLevel, /* bodyid = */ 0,
  2247                                          /* blockScopeDepth = */ 0);
  2248     if (!funpc.init(tokenStream))
  2249         return null();
  2251     if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, &newDirectives)) {
  2252         JS_ASSERT(directives == newDirectives);
  2253         return null();
  2256     if (fun->isNamedLambda()) {
  2257         if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
  2258             Definition *dn = p.value().get<FullParseHandler>();
  2259             if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
  2260                 return nullptr;
  2264     InternalHandle<Bindings*> bindings =
  2265         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
  2266     if (!pc->generateFunctionBindings(context, tokenStream, alloc, bindings))
  2267         return null();
  2269     if (!FoldConstants(context, &pn, this))
  2270         return null();
  2272     return pn;
  2275 template <typename ParseHandler>
  2276 bool
  2277 Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
  2278                                                  FunctionSyntaxKind kind,
  2279                                                  Directives *newDirectives)
  2281     // Given a properly initialized parse context, try to parse an actual
  2282     // function without concern for conversion to strict mode, use of lazy
  2283     // parsing and such.
  2285     Node prelude = null();
  2286     bool hasRest;
  2287     if (!functionArguments(kind, &prelude, pn, &hasRest))
  2288         return false;
  2290     FunctionBox *funbox = pc->sc->asFunctionBox();
  2292     fun->setArgCount(pc->numArgs());
  2293     if (hasRest)
  2294         fun->setHasRest();
  2296     if (type == Getter && fun->nargs() > 0) {
  2297         report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
  2298         return false;
  2300     if (type == Setter && fun->nargs() != 1) {
  2301         report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
  2302         return false;
  2305     if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) {
  2306         report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
  2307         return false;
  2310     // Parse the function body.
  2311     FunctionBodyType bodyType = StatementListBody;
  2312     if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
  2313         if (funbox->isStarGenerator()) {
  2314             report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
  2315             return false;
  2317         tokenStream.ungetToken();
  2318         bodyType = ExpressionBody;
  2319         fun->setIsExprClosure();
  2322     Node body = functionBody(kind, bodyType);
  2323     if (!body)
  2324         return false;
  2326     if (fun->name() && !checkStrictBinding(fun->name(), pn))
  2327         return false;
  2329 #if JS_HAS_EXPR_CLOSURES
  2330     if (bodyType == StatementListBody) {
  2331 #endif
  2332         if (!tokenStream.matchToken(TOK_RC)) {
  2333             report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY);
  2334             return false;
  2336         funbox->bufEnd = pos().begin + 1;
  2337 #if JS_HAS_EXPR_CLOSURES
  2338     } else {
  2339         if (tokenStream.hadError())
  2340             return false;
  2341         funbox->bufEnd = pos().end;
  2342         if (kind == Statement && !MatchOrInsertSemicolon(tokenStream))
  2343             return false;
  2345 #endif
  2347     return finishFunctionDefinition(pn, funbox, prelude, body);
  2350 template <typename ParseHandler>
  2351 bool
  2352 Parser<ParseHandler>::checkYieldNameValidity()
  2354     // In star generators and in JS >= 1.7, yield is a keyword.  Otherwise in
  2355     // strict mode, yield is a future reserved word.
  2356     if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict) {
  2357         report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
  2358         return false;
  2360     return true;
  2363 template <typename ParseHandler>
  2364 typename ParseHandler::Node
  2365 Parser<ParseHandler>::functionStmt()
  2367     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
  2369     TokenStream::Position start(keepAtoms);
  2370     tokenStream.tell(&start);
  2372     RootedPropertyName name(context);
  2373     GeneratorKind generatorKind = NotGenerator;
  2374     TokenKind tt = tokenStream.getToken();
  2376     if (tt == TOK_MUL) {
  2377         tokenStream.tell(&start);
  2378         tt = tokenStream.getToken();
  2379         generatorKind = StarGenerator;
  2382     if (tt == TOK_NAME) {
  2383         name = tokenStream.currentName();
  2384     } else if (tt == TOK_YIELD) {
  2385         if (!checkYieldNameValidity())
  2386             return null();
  2387         name = tokenStream.currentName();
  2388     } else {
  2389         /* Unnamed function expressions are forbidden in statement context. */
  2390         report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
  2391         return null();
  2394     /* We forbid function statements in strict mode code. */
  2395     if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
  2396         !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
  2397         return null();
  2399     return functionDef(name, start, Normal, Statement, generatorKind);
  2402 template <typename ParseHandler>
  2403 typename ParseHandler::Node
  2404 Parser<ParseHandler>::functionExpr()
  2406     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
  2408     TokenStream::Position start(keepAtoms);
  2409     tokenStream.tell(&start);
  2411     GeneratorKind generatorKind = NotGenerator;
  2412     TokenKind tt = tokenStream.getToken();
  2414     if (tt == TOK_MUL) {
  2415         tokenStream.tell(&start);
  2416         tt = tokenStream.getToken();
  2417         generatorKind = StarGenerator;
  2420     RootedPropertyName name(context);
  2421     if (tt == TOK_NAME) {
  2422         name = tokenStream.currentName();
  2423     } else if (tt == TOK_YIELD) {
  2424         if (!checkYieldNameValidity())
  2425             return null();
  2426         name = tokenStream.currentName();
  2427     } else {
  2428         tokenStream.ungetToken();
  2431     return functionDef(name, start, Normal, Expression, generatorKind);
  2434 /*
  2435  * Return true if this node, known to be an unparenthesized string literal,
  2436  * could be the string of a directive in a Directive Prologue. Directive
  2437  * strings never contain escape sequences or line continuations.
  2438  * isEscapeFreeStringLiteral, below, checks whether the node itself could be
  2439  * a directive.
  2440  */
  2441 static inline bool
  2442 IsEscapeFreeStringLiteral(const TokenPos &pos, JSAtom *str)
  2444     /*
  2445      * If the string's length in the source code is its length as a value,
  2446      * accounting for the quotes, then it must not contain any escape
  2447      * sequences or line continuations.
  2448      */
  2449     return pos.begin + str->length() + 2 == pos.end;
  2452 template <>
  2453 bool
  2454 Parser<SyntaxParseHandler>::asmJS(Node list)
  2456     // While asm.js could technically be validated and compiled during syntax
  2457     // parsing, we have no guarantee that some later JS wouldn't abort the
  2458     // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
  2459     // For simplicity, unconditionally abort the syntax parse when "use asm" is
  2460     // encountered so that asm.js is always validated/compiled exactly once
  2461     // during a full parse.
  2462     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  2463     return false;
  2466 template <>
  2467 bool
  2468 Parser<FullParseHandler>::asmJS(Node list)
  2470     // If we are already inside "use asm" that means we are either actively
  2471     // compiling or we are reparsing after asm.js validation failure. In either
  2472     // case, nothing to do here.
  2473     if (pc->useAsmOrInsideUseAsm())
  2474         return true;
  2476     // If there is no ScriptSource, then we are doing a non-compiling parse and
  2477     // so we shouldn't (and can't, without a ScriptSource) compile.
  2478     if (ss == nullptr)
  2479         return true;
  2481     pc->sc->asFunctionBox()->useAsm = true;
  2483 #ifdef JS_ION
  2484     // Attempt to validate and compile this asm.js module. On success, the
  2485     // tokenStream has been advanced to the closing }. On failure, the
  2486     // tokenStream is in an indeterminate state and we must reparse the
  2487     // function from the beginning. Reparsing is triggered by marking that a
  2488     // new directive has been encountered and returning 'false'.
  2489     bool validated;
  2490     if (!CompileAsmJS(context, *this, list, &validated))
  2491         return false;
  2492     if (!validated) {
  2493         pc->newDirectives->setAsmJS();
  2494         return false;
  2496 #endif
  2498     return true;
  2501 /*
  2502  * Recognize Directive Prologue members and directives. Assuming |pn| is a
  2503  * candidate for membership in a directive prologue, recognize directives and
  2504  * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
  2505  * |pn_prologue| flag.
  2507  * Note that the following is a strict mode function:
  2509  * function foo() {
  2510  *   "blah" // inserted semi colon
  2511  *        "blurgh"
  2512  *   "use\x20loose"
  2513  *   "use strict"
  2514  * }
  2516  * That is, even though "use\x20loose" can never be a directive, now or in the
  2517  * future (because of the hex escape), the Directive Prologue extends through it
  2518  * to the "use strict" statement, which is indeed a directive.
  2519  */
  2520 template <typename ParseHandler>
  2521 bool
  2522 Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
  2524     TokenPos directivePos;
  2525     JSAtom *directive = handler.isStringExprStatement(pn, &directivePos);
  2527     *cont = !!directive;
  2528     if (!*cont)
  2529         return true;
  2531     if (IsEscapeFreeStringLiteral(directivePos, directive)) {
  2532         // Mark this statement as being a possibly legitimate part of a
  2533         // directive prologue, so the bytecode emitter won't warn about it being
  2534         // useless code. (We mustn't just omit the statement entirely yet, as it
  2535         // could be producing the value of an eval or JSScript execution.)
  2536         //
  2537         // Note that even if the string isn't one we recognize as a directive,
  2538         // the emitter still shouldn't flag it as useless, as it could become a
  2539         // directive in the future. We don't want to interfere with people
  2540         // taking advantage of directive-prologue-enabled features that appear
  2541         // in other browsers first.
  2542         handler.setPrologue(pn);
  2544         if (directive == context->names().useStrict) {
  2545             // We're going to be in strict mode. Note that this scope explicitly
  2546             // had "use strict";
  2547             pc->sc->setExplicitUseStrict();
  2548             if (!pc->sc->strict) {
  2549                 if (pc->sc->isFunctionBox()) {
  2550                     // Request that this function be reparsed as strict.
  2551                     pc->newDirectives->setStrict();
  2552                     return false;
  2553                 } else {
  2554                     // We don't reparse global scopes, so we keep track of the
  2555                     // one possible strict violation that could occur in the
  2556                     // directive prologue -- octal escapes -- and complain now.
  2557                     if (tokenStream.sawOctalEscape()) {
  2558                         report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
  2559                         return false;
  2561                     pc->sc->strict = true;
  2564         } else if (directive == context->names().useAsm) {
  2565             if (pc->sc->isFunctionBox())
  2566                 return asmJS(list);
  2567             return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
  2570     return true;
  2573 /*
  2574  * Parse the statements in a block, creating a StatementList node that lists
  2575  * the statements.  If called from block-parsing code, the caller must match
  2576  * '{' before and '}' after.
  2577  */
  2578 template <typename ParseHandler>
  2579 typename ParseHandler::Node
  2580 Parser<ParseHandler>::statements()
  2582     JS_CHECK_RECURSION(context, return null());
  2584     Node pn = handler.newStatementList(pc->blockid(), pos());
  2585     if (!pn)
  2586         return null();
  2588     Node saveBlock = pc->blockNode;
  2589     pc->blockNode = pn;
  2591     bool canHaveDirectives = pc->atBodyLevel();
  2592     for (;;) {
  2593         TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
  2594         if (tt <= TOK_EOF || tt == TOK_RC) {
  2595             if (tt == TOK_ERROR) {
  2596                 if (tokenStream.isEOF())
  2597                     isUnexpectedEOF_ = true;
  2598                 return null();
  2600             break;
  2602         Node next = statement(canHaveDirectives);
  2603         if (!next) {
  2604             if (tokenStream.isEOF())
  2605                 isUnexpectedEOF_ = true;
  2606             return null();
  2609         if (canHaveDirectives) {
  2610             if (!maybeParseDirective(pn, next, &canHaveDirectives))
  2611                 return null();
  2614         handler.addStatementToList(pn, next, pc);
  2617     /*
  2618      * Handle the case where there was a let declaration under this block.  If
  2619      * it replaced pc->blockNode with a new block node then we must refresh pn
  2620      * and then restore pc->blockNode.
  2621      */
  2622     if (pc->blockNode != pn)
  2623         pn = pc->blockNode;
  2624     pc->blockNode = saveBlock;
  2625     return pn;
  2628 template <typename ParseHandler>
  2629 typename ParseHandler::Node
  2630 Parser<ParseHandler>::condition()
  2632     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
  2633     Node pn = exprInParens();
  2634     if (!pn)
  2635         return null();
  2636     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
  2638     /* Check for (a = b) and warn about possible (a == b) mistype. */
  2639     if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) &&
  2640         !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
  2642         return null();
  2644     return pn;
  2647 template <typename ParseHandler>
  2648 bool
  2649 Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
  2651     TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
  2652     if (tt == TOK_ERROR)
  2653         return false;
  2654     if (tt == TOK_NAME) {
  2655         tokenStream.consumeKnownToken(TOK_NAME);
  2656         label.set(tokenStream.currentName());
  2657     } else if (tt == TOK_YIELD) {
  2658         tokenStream.consumeKnownToken(TOK_YIELD);
  2659         if (!checkYieldNameValidity())
  2660             return false;
  2661         label.set(tokenStream.currentName());
  2662     } else {
  2663         label.set(nullptr);
  2665     return true;
  2668 template <typename ParseHandler>
  2669 bool
  2670 Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, JSAtom *atom)
  2672     JSAutoByteString name;
  2673     if (AtomToPrintableString(context, atom, &name))
  2674         report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", name.ptr());
  2675     return false;
  2678 /*
  2679  * Define a let-variable in a block, let-expression, or comprehension scope. pc
  2680  * must already be in such a scope.
  2682  * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
  2683  * property for the new variable on the block object, pc->staticScope;
  2684  * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
  2685  * data->pn in a slot of the block object.
  2686  */
  2687 template <>
  2688 /* static */ bool
  2689 Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data,
  2690                                   HandlePropertyName name, Parser<FullParseHandler> *parser)
  2692     ParseContext<FullParseHandler> *pc = parser->pc;
  2693     ParseNode *pn = data->pn;
  2694     if (!parser->checkStrictBinding(name, pn))
  2695         return false;
  2697     ExclusiveContext *cx = parser->context;
  2699     Rooted<StaticBlockObject *> blockObj(cx, data->let.blockObj);
  2700     unsigned index = blockObj->numVariables();
  2701     if (index >= StaticBlockObject::LOCAL_INDEX_LIMIT) {
  2702         parser->report(ParseError, false, pn, data->let.overflow);
  2703         return false;
  2706     /*
  2707      * Assign block-local index to pn->pn_cookie right away, encoding it as an
  2708      * upvar cookie whose skip tells the current static level. The emitter will
  2709      * adjust the node's slot based on its stack depth model -- and, for global
  2710      * and eval code, js::frontend::CompileScript will adjust the slot
  2711      * again to include script->nfixed.
  2712      */
  2713     if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, index))
  2714         return false;
  2716     /*
  2717      * For bindings that are hoisted to the beginning of the block/function,
  2718      * define() right now. Otherwise, delay define until PushLetScope.
  2719      */
  2720     if (data->let.varContext == HoistVars) {
  2721         JS_ASSERT(!pc->atBodyLevel());
  2722         Definition *dn = pc->decls().lookupFirst(name);
  2723         if (dn && dn->pn_blockid == pc->blockid())
  2724             return parser->reportRedeclaration(pn, dn->isConst(), name);
  2725         if (!pc->define(parser->tokenStream, name, pn, Definition::LET))
  2726             return false;
  2729     bool redeclared;
  2730     RootedId id(cx, NameToId(name));
  2731     RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, index, &redeclared));
  2732     if (!shape) {
  2733         if (redeclared)
  2734             parser->reportRedeclaration(pn, false, name);
  2735         return false;
  2738     /* Store pn in the static block object. */
  2739     blockObj->setDefinitionParseNode(index, reinterpret_cast<Definition *>(pn));
  2740     return true;
  2743 template <>
  2744 /* static */ bool
  2745 Parser<SyntaxParseHandler>::bindLet(BindData<SyntaxParseHandler> *data,
  2746                                     HandlePropertyName name, Parser<SyntaxParseHandler> *parser)
  2748     if (!parser->checkStrictBinding(name, data->pn))
  2749         return false;
  2751     return true;
  2754 template <typename ParseHandler, class Op>
  2755 static inline bool
  2756 ForEachLetDef(TokenStream &ts, ParseContext<ParseHandler> *pc,
  2757               HandleStaticBlockObject blockObj, Op op)
  2759     for (Shape::Range<CanGC> r(ts.context(), blockObj->lastProperty()); !r.empty(); r.popFront()) {
  2760         Shape &shape = r.front();
  2762         /* Beware the destructuring dummy slots. */
  2763         if (JSID_IS_INT(shape.propid()))
  2764             continue;
  2766         if (!op(ts, pc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
  2767             return false;
  2769     return true;
  2772 template <typename ParseHandler>
  2773 struct PopLetDecl {
  2774     bool operator()(TokenStream &, ParseContext<ParseHandler> *pc, HandleStaticBlockObject,
  2775                     const Shape &, JSAtom *atom)
  2777         pc->popLetDecl(atom);
  2778         return true;
  2780 };
  2782 // We compute the maximum block scope depth, in slots, of a compilation unit at
  2783 // parse-time.  Each nested statement has a field indicating the maximum block
  2784 // scope depth that is nested inside it.  When we leave a nested statement, we
  2785 // add the number of slots in the statement to the nested depth, and use that to
  2786 // update the maximum block scope depth of the outer statement or parse
  2787 // context.  In the end, pc->blockScopeDepth will indicate the number of slots
  2788 // to reserve in the fixed part of a stack frame.
  2789 //
  2790 template <typename ParseHandler>
  2791 static void
  2792 AccumulateBlockScopeDepth(ParseContext<ParseHandler> *pc)
  2794     uint32_t innerDepth = pc->topStmt->innerBlockScopeDepth;
  2795     StmtInfoPC *outer = pc->topStmt->down;
  2797     if (pc->topStmt->isBlockScope)
  2798         innerDepth += pc->topStmt->staticScope->template as<StaticBlockObject>().numVariables();
  2800     if (outer) {
  2801         if (outer->innerBlockScopeDepth < innerDepth)
  2802             outer->innerBlockScopeDepth = innerDepth;
  2803     } else {
  2804         if (pc->blockScopeDepth < innerDepth)
  2805             pc->blockScopeDepth = innerDepth;
  2809 template <typename ParseHandler>
  2810 static void
  2811 PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc)
  2813     RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope);
  2814     JS_ASSERT(!!scopeObj == pc->topStmt->isNestedScope);
  2816     AccumulateBlockScopeDepth(pc);
  2817     FinishPopStatement(pc);
  2819     if (scopeObj) {
  2820         if (scopeObj->is<StaticBlockObject>()) {
  2821             RootedStaticBlockObject blockObj(ts.context(), &scopeObj->as<StaticBlockObject>());
  2822             JS_ASSERT(!blockObj->inDictionaryMode());
  2823             ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>());
  2825         scopeObj->resetEnclosingNestedScopeFromParser();
  2829 /*
  2830  * The function LexicalLookup searches a static binding for the given name in
  2831  * the stack of statements enclosing the statement currently being parsed. Each
  2832  * statement that introduces a new scope has a corresponding scope object, on
  2833  * which the bindings for that scope are stored. LexicalLookup either returns
  2834  * the innermost statement which has a scope object containing a binding with
  2835  * the given name, or nullptr.
  2836  */
  2837 template <class ContextT>
  2838 typename ContextT::StmtInfo *
  2839 LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt)
  2841     RootedId id(ct->sc->context, AtomToId(atom));
  2843     if (!stmt)
  2844         stmt = ct->topScopeStmt;
  2845     for (; stmt; stmt = stmt->downScope) {
  2846         /*
  2847          * With-statements introduce dynamic bindings. Since dynamic bindings
  2848          * can potentially override any static bindings introduced by statements
  2849          * further up the stack, we have to abort the search.
  2850          */
  2851         if (stmt->type == STMT_WITH)
  2852             break;
  2854         // Skip statements that do not introduce a new scope
  2855         if (!stmt->isBlockScope)
  2856             continue;
  2858         StaticBlockObject &blockObj = stmt->staticBlock();
  2859         Shape *shape = blockObj.nativeLookup(ct->sc->context, id);
  2860         if (shape) {
  2861             if (slotp)
  2862                 *slotp = blockObj.shapeToIndex(*shape);
  2863             return stmt;
  2867     if (slotp)
  2868         *slotp = -1;
  2869     return stmt;
  2872 template <typename ParseHandler>
  2873 static inline bool
  2874 OuterLet(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, HandleAtom atom)
  2876     while (stmt->downScope) {
  2877         stmt = LexicalLookup(pc, atom, nullptr, stmt->downScope);
  2878         if (!stmt)
  2879             return false;
  2880         if (stmt->type == STMT_BLOCK)
  2881             return true;
  2883     return false;
  2886 template <typename ParseHandler>
  2887 /* static */ bool
  2888 Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data,
  2889                                      HandlePropertyName name, Parser<ParseHandler> *parser)
  2891     ExclusiveContext *cx = parser->context;
  2892     ParseContext<ParseHandler> *pc = parser->pc;
  2893     Node pn = data->pn;
  2894     bool isConstDecl = data->op == JSOP_DEFCONST;
  2896     /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
  2897     parser->handler.setOp(pn, JSOP_NAME);
  2899     if (!parser->checkStrictBinding(name, pn))
  2900         return false;
  2902     StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr);
  2904     if (stmt && stmt->type == STMT_WITH) {
  2905         parser->handler.setFlag(pn, PND_DEOPTIMIZED);
  2906         if (pc->sc->isFunctionBox()) {
  2907             FunctionBox *funbox = pc->sc->asFunctionBox();
  2908             funbox->setMightAliasLocals();
  2911         /*
  2912          * This definition isn't being added to the parse context's
  2913          * declarations, so make sure to indicate the need to deoptimize
  2914          * the script's arguments object. Mark the function as if it
  2915          * contained a debugger statement, which will deoptimize arguments
  2916          * as much as possible.
  2917          */
  2918         if (name == cx->names().arguments)
  2919             pc->sc->setHasDebuggerStatement();
  2921         return true;
  2924     DefinitionList::Range defs = pc->decls().lookupMulti(name);
  2925     JS_ASSERT_IF(stmt, !defs.empty());
  2927     if (defs.empty()) {
  2928         return pc->define(parser->tokenStream, name, pn,
  2929                           isConstDecl ? Definition::CONST : Definition::VAR);
  2932     /*
  2933      * There was a previous declaration with the same name. The standard
  2934      * disallows several forms of redeclaration. Critically,
  2935      *   let (x) { var x; } // error
  2936      * is not allowed which allows us to turn any non-error redeclaration
  2937      * into a use of the initial declaration.
  2938      */
  2939     DefinitionNode dn = defs.front<ParseHandler>();
  2940     Definition::Kind dn_kind = parser->handler.getDefinitionKind(dn);
  2941     if (dn_kind == Definition::ARG) {
  2942         JSAutoByteString bytes;
  2943         if (!AtomToPrintableString(cx, name, &bytes))
  2944             return false;
  2946         if (isConstDecl) {
  2947             parser->report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, bytes.ptr());
  2948             return false;
  2950         if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr()))
  2951             return false;
  2952     } else {
  2953         bool error = (isConstDecl ||
  2954                       dn_kind == Definition::CONST ||
  2955                       (dn_kind == Definition::LET &&
  2956                        (stmt->type != STMT_CATCH || OuterLet(pc, stmt, name))));
  2958         if (parser->options().extraWarningsOption
  2959             ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR
  2960             : error)
  2962             JSAutoByteString bytes;
  2963             ParseReportKind reporter = error ? ParseError : ParseExtraWarning;
  2964             if (!AtomToPrintableString(cx, name, &bytes) ||
  2965                 !parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR,
  2966                                 Definition::kindString(dn_kind), bytes.ptr()))
  2968                 return false;
  2973     parser->handler.linkUseToDef(pn, dn);
  2974     return true;
  2977 template <>
  2978 bool
  2979 Parser<FullParseHandler>::makeSetCall(ParseNode *pn, unsigned msg)
  2981     JS_ASSERT(pn->isKind(PNK_CALL));
  2982     JS_ASSERT(pn->isArity(PN_LIST));
  2983     JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) ||
  2984               pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_SPREADEVAL) ||
  2985               pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
  2987     if (!report(ParseStrictError, pc->sc->strict, pn, msg))
  2988         return false;
  2989     handler.markAsSetCall(pn);
  2990     return true;
  2993 template <typename ParseHandler>
  2994 bool
  2995 Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
  2997     StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr);
  2999     DefinitionList::Range defs = pc->decls().lookupMulti(name);
  3001     DefinitionNode dn;
  3002     if (!defs.empty()) {
  3003         dn = defs.front<ParseHandler>();
  3004     } else {
  3005         /*
  3006          * No definition before this use in any lexical scope.
  3007          * Create a placeholder definition node to either:
  3008          * - Be adopted when we parse the real defining
  3009          *   declaration, or
  3010          * - Be left as a free variable definition if we never
  3011          *   see the real definition.
  3012          */
  3013         dn = getOrCreateLexicalDependency(pc, name);
  3014         if (!dn)
  3015             return false;
  3018     handler.linkUseToDef(pn, dn);
  3020     if (stmt && stmt->type == STMT_WITH)
  3021         handler.setFlag(pn, PND_DEOPTIMIZED);
  3023     return true;
  3026 template <>
  3027 bool
  3028 Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn)
  3030     JS_ASSERT(pn->isKind(PNK_NAME));
  3032     RootedPropertyName name(context, pn->pn_atom->asPropertyName());
  3034     data->pn = pn;
  3035     if (!data->binder(data, name, this))
  3036         return false;
  3038     /*
  3039      * Select the appropriate name-setting opcode, respecting eager selection
  3040      * done by the data->binder function.
  3041      */
  3042     if (pn->pn_dflags & PND_BOUND)
  3043         pn->setOp(JSOP_SETLOCAL);
  3044     else if (data->op == JSOP_DEFCONST)
  3045         pn->setOp(JSOP_SETCONST);
  3046     else
  3047         pn->setOp(JSOP_SETNAME);
  3049     if (data->op == JSOP_DEFCONST)
  3050         pn->pn_dflags |= PND_CONST;
  3052     pn->markAsAssigned();
  3053     return true;
  3056 /*
  3057  * Destructuring patterns can appear in two kinds of contexts:
  3059  * - assignment-like: assignment expressions and |for| loop heads.  In
  3060  *   these cases, the patterns' property value positions can be
  3061  *   arbitrary lvalue expressions; the destructuring is just a fancy
  3062  *   assignment.
  3064  * - declaration-like: |var| and |let| declarations, functions' formal
  3065  *   parameter lists, |catch| clauses, and comprehension tails.  In
  3066  *   these cases, the patterns' property value positions must be
  3067  *   simple names; the destructuring defines them as new variables.
  3069  * In both cases, other code parses the pattern as an arbitrary
  3070  * primaryExpr, and then, here in CheckDestructuring, verify that the
  3071  * tree is a valid destructuring expression.
  3073  * In assignment-like contexts, we parse the pattern with
  3074  * pc->inDeclDestructuring clear, so the lvalue expressions in the
  3075  * pattern are parsed normally.  primaryExpr links variable references
  3076  * into the appropriate use chains; creates placeholder definitions;
  3077  * and so on.  CheckDestructuring is called with |data| nullptr (since
  3078  * we won't be binding any new names), and we specialize lvalues as
  3079  * appropriate.
  3081  * In declaration-like contexts, the normal variable reference
  3082  * processing would just be an obstruction, because we're going to
  3083  * define the names that appear in the property value positions as new
  3084  * variables anyway.  In this case, we parse the pattern with
  3085  * pc->inDeclDestructuring set, which directs primaryExpr to leave
  3086  * whatever name nodes it creates unconnected.  Then, here in
  3087  * CheckDestructuring, we require the pattern's property value
  3088  * positions to be simple names, and define them as appropriate to the
  3089  * context.  For these calls, |data| points to the right sort of
  3090  * BindData.
  3092  * The 'toplevel' is a private detail of the recursive strategy used by
  3093  * CheckDestructuring and callers should use the default value.
  3094  */
  3095 template <>
  3096 bool
  3097 Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
  3098                                              ParseNode *left, bool toplevel)
  3100     bool ok;
  3102     if (left->isKind(PNK_ARRAYCOMP)) {
  3103         report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE);
  3104         return false;
  3107     Rooted<StaticBlockObject *> blockObj(context);
  3108     blockObj = data && data->binder == bindLet ? data->let.blockObj.get() : nullptr;
  3110     if (left->isKind(PNK_ARRAY)) {
  3111         for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
  3112             if (!pn->isKind(PNK_ELISION)) {
  3113                 if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
  3114                     ok = checkDestructuring(data, pn, false);
  3115                 } else {
  3116                     if (data) {
  3117                         if (!pn->isKind(PNK_NAME)) {
  3118                             report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME);
  3119                             return false;
  3121                         ok = bindDestructuringVar(data, pn);
  3122                     } else {
  3123                         ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
  3126                 if (!ok)
  3127                     return false;
  3130     } else {
  3131         JS_ASSERT(left->isKind(PNK_OBJECT));
  3132         for (ParseNode *member = left->pn_head; member; member = member->pn_next) {
  3133             MOZ_ASSERT(member->isKind(PNK_COLON));
  3134             ParseNode *expr = member->pn_right;
  3136             if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) {
  3137                 ok = checkDestructuring(data, expr, false);
  3138             } else if (data) {
  3139                 if (!expr->isKind(PNK_NAME)) {
  3140                     report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
  3141                     return false;
  3143                 ok = bindDestructuringVar(data, expr);
  3144             } else {
  3145                 /*
  3146                  * If this is a destructuring shorthand ({x} = ...), then
  3147                  * identifierName wasn't used to parse |x|.  As a result, |x|
  3148                  * hasn't been officially linked to its def or registered in
  3149                  * lexdeps.  Do that now.
  3150                  */
  3151                 if (member->pn_right == member->pn_left) {
  3152                     RootedPropertyName name(context, expr->pn_atom->asPropertyName());
  3153                     if (!noteNameUse(name, expr))
  3154                         return false;
  3156                 ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment);
  3158             if (!ok)
  3159                 return false;
  3163     return true;
  3166 template <>
  3167 bool
  3168 Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
  3169                                                Node left, bool toplevel)
  3171     return abortIfSyntaxParser();
  3174 template <typename ParseHandler>
  3175 typename ParseHandler::Node
  3176 Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind tt)
  3178     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
  3180     pc->inDeclDestructuring = true;
  3181     Node pn = primaryExpr(tt);
  3182     pc->inDeclDestructuring = false;
  3183     if (!pn)
  3184         return null();
  3185     if (!checkDestructuring(data, pn))
  3186         return null();
  3187     return pn;
  3190 template <typename ParseHandler>
  3191 typename ParseHandler::Node
  3192 Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
  3194     JS_ASSERT(blockObj);
  3196     ObjectBox *blockbox = newObjectBox(blockObj);
  3197     if (!blockbox)
  3198         return null();
  3200     PushStatementPC(pc, stmt, STMT_BLOCK);
  3201     blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
  3202     FinishPushNestedScope(pc, stmt, *blockObj.get());
  3203     stmt->isBlockScope = true;
  3205     Node pn = handler.newLexicalScope(blockbox);
  3206     if (!pn)
  3207         return null();
  3209     if (!GenerateBlockId(tokenStream, pc, stmt->blockid))
  3210         return null();
  3211     handler.setBlockId(pn, stmt->blockid);
  3212     return pn;
  3215 template <typename ParseHandler>
  3216 typename ParseHandler::Node
  3217 Parser<ParseHandler>::pushLexicalScope(StmtInfoPC *stmt)
  3219     RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
  3220     if (!blockObj)
  3221         return null();
  3223     return pushLexicalScope(blockObj, stmt);
  3226 struct AddLetDecl
  3228     uint32_t blockid;
  3230     AddLetDecl(uint32_t blockid) : blockid(blockid) {}
  3232     bool operator()(TokenStream &ts, ParseContext<FullParseHandler> *pc,
  3233                     HandleStaticBlockObject blockObj, const Shape &shape, JSAtom *)
  3235         ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate();
  3236         def->pn_blockid = blockid;
  3237         RootedPropertyName name(ts.context(), def->name());
  3238         return pc->define(ts, name, def, Definition::LET);
  3240 };
  3242 template <>
  3243 ParseNode *
  3244 Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
  3246     JS_ASSERT(blockObj);
  3247     ParseNode *pn = pushLexicalScope(blockObj, stmt);
  3248     if (!pn)
  3249         return null();
  3251     pn->pn_dflags |= PND_LET;
  3253     /* Populate the new scope with decls found in the head with updated blockid. */
  3254     if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid)))
  3255         return null();
  3257     return pn;
  3260 template <>
  3261 SyntaxParseHandler::Node
  3262 Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
  3264     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  3265     return SyntaxParseHandler::NodeFailure;
  3268 /*
  3269  * Parse a let block statement or let expression (determined by 'letContext').
  3270  * In both cases, bindings are not hoisted to the top of the enclosing block
  3271  * and thus must be carefully injected between variables() and the let body.
  3272  */
  3273 template <typename ParseHandler>
  3274 typename ParseHandler::Node
  3275 Parser<ParseHandler>::letBlock(LetContext letContext)
  3277     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
  3279     RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
  3280     if (!blockObj)
  3281         return null();
  3283     uint32_t begin = pos().begin;
  3285     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
  3287     Node vars = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
  3288     if (!vars)
  3289         return null();
  3291     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
  3293     StmtInfoPC stmtInfo(context);
  3294     Node block = pushLetScope(blockObj, &stmtInfo);
  3295     if (!block)
  3296         return null();
  3298     Node pnlet = handler.newBinary(PNK_LET, vars, block);
  3299     if (!pnlet)
  3300         return null();
  3301     handler.setBeginPosition(pnlet, begin);
  3303     bool needExprStmt = false;
  3304     if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TokenStream::Operand)) {
  3305         /*
  3306          * Strict mode eliminates a grammar ambiguity with unparenthesized
  3307          * LetExpressions in an ExpressionStatement. If followed immediately
  3308          * by an arguments list, it's ambiguous whether the let expression
  3309          * is the callee or the call is inside the let expression body.
  3311          * See bug 569464.
  3312          */
  3313         if (!report(ParseStrictError, pc->sc->strict, pnlet,
  3314                     JSMSG_STRICT_CODE_LET_EXPR_STMT))
  3316             return null();
  3319         /*
  3320          * If this is really an expression in let statement guise, then we
  3321          * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
  3322          * the return value of the expression.
  3323          */
  3324         needExprStmt = true;
  3325         letContext = LetExpresion;
  3328     Node expr;
  3329     if (letContext == LetStatement) {
  3330         expr = statements();
  3331         if (!expr)
  3332             return null();
  3333         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
  3334     } else {
  3335         JS_ASSERT(letContext == LetExpresion);
  3336         expr = assignExpr();
  3337         if (!expr)
  3338             return null();
  3340     handler.setLexicalScopeBody(block, expr);
  3341     PopStatementPC(tokenStream, pc);
  3343     handler.setEndPosition(pnlet, pos().end);
  3345     if (needExprStmt) {
  3346         if (!MatchOrInsertSemicolon(tokenStream))
  3347             return null();
  3348         return handler.newExprStatement(pnlet, pos().end);
  3350     return pnlet;
  3353 template <typename ParseHandler>
  3354 static bool
  3355 PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type,
  3356                        ParseContext<ParseHandler> *pc)
  3358     PushStatementPC(pc, stmt, type);
  3359     return GenerateBlockId(ts, pc, stmt->blockid);
  3362 template <typename ParseHandler>
  3363 typename ParseHandler::Node
  3364 Parser<ParseHandler>::blockStatement()
  3366     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
  3368     StmtInfoPC stmtInfo(context);
  3369     if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_BLOCK, pc))
  3370         return null();
  3372     Node list = statements();
  3373     if (!list)
  3374         return null();
  3376     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
  3377     PopStatementPC(tokenStream, pc);
  3378     return list;
  3381 template <typename ParseHandler>
  3382 typename ParseHandler::Node
  3383 Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, VarContext varContext)
  3385     /*
  3386      * If this name is being injected into an existing block/function, see if
  3387      * it has already been declared or if it resolves an outstanding lexdep.
  3388      * Otherwise, this is a let block/expr that introduces a new scope and thus
  3389      * shadows existing decls and doesn't resolve existing lexdeps. Duplicate
  3390      * names are caught by bindLet.
  3391      */
  3392     if (varContext == HoistVars) {
  3393         if (AtomDefnPtr p = pc->lexdeps->lookup(name)) {
  3394             DefinitionNode lexdep = p.value().get<ParseHandler>();
  3395             JS_ASSERT(handler.getDefinitionKind(lexdep) == Definition::PLACEHOLDER);
  3397             Node pn = handler.getDefinitionNode(lexdep);
  3398             if (handler.dependencyCovered(pn, pc->blockid(), functionScope)) {
  3399                 handler.setBlockId(pn, pc->blockid());
  3400                 pc->lexdeps->remove(p);
  3401                 handler.setPosition(pn, pos());
  3402                 return pn;
  3407     /* Make a new node for this declarator name (or destructuring pattern). */
  3408     return newName(name);
  3411 /*
  3412  * The 'blockObj' parameter is non-null when parsing the 'vars' in a let
  3413  * expression, block statement, non-top-level let declaration in statement
  3414  * context, and the let-initializer of a for-statement.
  3415  */
  3416 template <typename ParseHandler>
  3417 typename ParseHandler::Node
  3418 Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
  3419                                 StaticBlockObject *blockObj, VarContext varContext)
  3421     /*
  3422      * The four options here are:
  3423      * - PNK_VAR:   We're parsing var declarations.
  3424      * - PNK_CONST: We're parsing const declarations.
  3425      * - PNK_LET:   We are parsing a let declaration.
  3426      * - PNK_CALL:  We are parsing the head of a let block.
  3427      */
  3428     JS_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_CALL);
  3430     /*
  3431      * The simple flag is set if the declaration has the form 'var x', with
  3432      * only one variable declared and no initializer expression.
  3433      */
  3434     JS_ASSERT_IF(psimple, *psimple);
  3436     JSOp op = blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST;
  3438     Node pn = handler.newList(kind, null(), op);
  3439     if (!pn)
  3440         return null();
  3442     /*
  3443      * SpiderMonkey const is really "write once per initialization evaluation"
  3444      * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
  3445      * this code will change soon.
  3446      */
  3447     BindData<ParseHandler> data(context);
  3448     if (blockObj)
  3449         data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS);
  3450     else
  3451         data.initVarOrConst(op);
  3453     bool first = true;
  3454     Node pn2;
  3455     do {
  3456         if (psimple && !first)
  3457             *psimple = false;
  3458         first = false;
  3460         TokenKind tt = tokenStream.getToken();
  3461         if (tt == TOK_LB || tt == TOK_LC) {
  3462             if (psimple)
  3463                 *psimple = false;
  3465             pc->inDeclDestructuring = true;
  3466             pn2 = primaryExpr(tt);
  3467             pc->inDeclDestructuring = false;
  3468             if (!pn2)
  3469                 return null();
  3471             if (!checkDestructuring(&data, pn2))
  3472                 return null();
  3473             bool ignored;
  3474             if (pc->parsingForInit && matchInOrOf(&ignored)) {
  3475                 tokenStream.ungetToken();
  3476                 handler.addList(pn, pn2);
  3477                 continue;
  3480             MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
  3482             Node init = assignExpr();
  3483             if (!init)
  3484                 return null();
  3486             pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
  3487             if (!pn2)
  3488                 return null();
  3489             handler.addList(pn, pn2);
  3490             continue;
  3493         if (tt != TOK_NAME) {
  3494             if (tt == TOK_YIELD) {
  3495                 if (!checkYieldNameValidity())
  3496                     return null();
  3497             } else {
  3498                 if (tt != TOK_ERROR)
  3499                     report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
  3500                 return null();
  3504         RootedPropertyName name(context, tokenStream.currentName());
  3505         pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
  3506         if (!pn2)
  3507             return null();
  3508         if (data.op == JSOP_DEFCONST)
  3509             handler.setFlag(pn2, PND_CONST);
  3510         data.pn = pn2;
  3511         if (!data.binder(&data, name, this))
  3512             return null();
  3513         handler.addList(pn, pn2);
  3515         if (tokenStream.matchToken(TOK_ASSIGN)) {
  3516             if (psimple)
  3517                 *psimple = false;
  3519             Node init = assignExpr();
  3520             if (!init)
  3521                 return null();
  3523             if (!handler.finishInitializerAssignment(pn2, init, data.op))
  3524                 return null();
  3526     } while (tokenStream.matchToken(TOK_COMMA));
  3528     return pn;
  3531 template <>
  3532 ParseNode *
  3533 Parser<FullParseHandler>::letDeclaration()
  3535     handler.disableSyntaxParser();
  3537     ParseNode *pn;
  3539     do {
  3540         /*
  3541          * This is a let declaration. We must be directly under a block per the
  3542          * proposed ES4 specs, but not an implicit block created due to
  3543          * 'for (let ...)'. If we pass this error test, make the enclosing
  3544          * StmtInfoPC be our scope. Further let declarations in this block will
  3545          * find this scope statement and use the same block object.
  3547          * If we are the first let declaration in this block (i.e., when the
  3548          * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
  3549          * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
  3550          */
  3551         StmtInfoPC *stmt = pc->topStmt;
  3552         if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
  3553             report(ParseError, false, null(), JSMSG_LET_DECL_NOT_IN_BLOCK);
  3554             return null();
  3557         if (stmt && stmt->isBlockScope) {
  3558             JS_ASSERT(pc->staticScope == stmt->staticScope);
  3559         } else {
  3560             if (pc->atBodyLevel()) {
  3561                 /*
  3562                  * ES4 specifies that let at top level and at body-block scope
  3563                  * does not shadow var, so convert back to var.
  3564                  */
  3565                 pn = variables(PNK_VAR);
  3566                 if (!pn)
  3567                     return null();
  3568                 pn->pn_xflags |= PNX_POPVAR;
  3569                 break;
  3572             /*
  3573              * Some obvious assertions here, but they may help clarify the
  3574              * situation. This stmt is not yet a scope, so it must not be a
  3575              * catch block (catch is a lexical scope by definition).
  3576              */
  3577             JS_ASSERT(!stmt->isBlockScope);
  3578             JS_ASSERT(stmt != pc->topScopeStmt);
  3579             JS_ASSERT(stmt->type == STMT_BLOCK ||
  3580                       stmt->type == STMT_SWITCH ||
  3581                       stmt->type == STMT_TRY ||
  3582                       stmt->type == STMT_FINALLY);
  3583             JS_ASSERT(!stmt->downScope);
  3585             /* Convert the block statement into a scope statement. */
  3586             StaticBlockObject *blockObj = StaticBlockObject::create(context);
  3587             if (!blockObj)
  3588                 return null();
  3590             ObjectBox *blockbox = newObjectBox(blockObj);
  3591             if (!blockbox)
  3592                 return null();
  3594             /*
  3595              * Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked
  3596              * list stack, if it isn't already there.  If it is there, but it
  3597              * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
  3598              * block.
  3599              */
  3600             stmt->isBlockScope = stmt->isNestedScope = true;
  3601             stmt->downScope = pc->topScopeStmt;
  3602             pc->topScopeStmt = stmt;
  3604             blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
  3605             pc->staticScope = blockObj;
  3606             stmt->staticScope = blockObj;
  3608 #ifdef DEBUG
  3609             ParseNode *tmp = pc->blockNode;
  3610             JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
  3611 #endif
  3613             /* Create a new lexical scope node for these statements. */
  3614             ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler);
  3615             if (!pn1)
  3616                 return null();
  3618             pn1->pn_pos = pc->blockNode->pn_pos;
  3619             pn1->pn_objbox = blockbox;
  3620             pn1->pn_expr = pc->blockNode;
  3621             pn1->pn_blockid = pc->blockNode->pn_blockid;
  3622             pc->blockNode = pn1;
  3625         pn = variables(PNK_LET, nullptr, &pc->staticScope->as<StaticBlockObject>(), HoistVars);
  3626         if (!pn)
  3627             return null();
  3628         pn->pn_xflags = PNX_POPVAR;
  3629     } while (0);
  3631     return MatchOrInsertSemicolon(tokenStream) ? pn : nullptr;
  3634 template <>
  3635 SyntaxParseHandler::Node
  3636 Parser<SyntaxParseHandler>::letDeclaration()
  3638     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  3639     return SyntaxParseHandler::NodeFailure;
  3642 template <>
  3643 ParseNode *
  3644 Parser<FullParseHandler>::letStatement()
  3646     handler.disableSyntaxParser();
  3648     /* Check for a let statement or let expression. */
  3649     ParseNode *pn;
  3650     if (tokenStream.peekToken() == TOK_LP) {
  3651         pn = letBlock(LetStatement);
  3652         JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
  3653     } else {
  3654         pn = letDeclaration();
  3656     return pn;
  3659 template <>
  3660 SyntaxParseHandler::Node
  3661 Parser<SyntaxParseHandler>::letStatement()
  3663     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  3664     return SyntaxParseHandler::NodeFailure;
  3667 template<typename ParseHandler>
  3668 typename ParseHandler::Node
  3669 Parser<ParseHandler>::importDeclaration()
  3671     JS_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
  3673     if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
  3674         report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
  3675         return null();
  3678     uint32_t begin = pos().begin;
  3679     TokenKind tt = tokenStream.getToken();
  3681     Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
  3682     if (!importSpecSet)
  3683         return null();
  3685     if (tt == TOK_NAME || tt == TOK_LC) {
  3686         if (tt == TOK_NAME) {
  3687             // Handle the form |import a from 'b'|, by adding a single import
  3688             // specifier to the list, with 'default' as the import name and
  3689             // 'a' as the binding name. This is equivalent to
  3690             // |import { default as a } from 'b'|.
  3691             Node importName = newName(context->names().default_);
  3692             if (!importName)
  3693                 return null();
  3695             Node bindingName = newName(tokenStream.currentName());
  3696             if (!bindingName)
  3697                 return null();
  3699             Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
  3700             if (!importSpec)
  3701                 return null();
  3703             handler.addList(importSpecSet, importSpec);
  3704         } else {
  3705             do {
  3706                 // Handle the forms |import {} from 'a'| and
  3707                 // |import { ..., } from 'a'| (where ... is non empty), by
  3708                 // escaping the loop early if the next token is }.
  3709                 tt = tokenStream.peekToken(TokenStream::KeywordIsName);
  3710                 if (tt == TOK_ERROR)
  3711                     return null();
  3712                 if (tt == TOK_RC)
  3713                     break;
  3715                 // If the next token is a keyword, the previous call to
  3716                 // peekToken matched it as a TOK_NAME, and put it in the
  3717                 // lookahead buffer, so this call will match keywords as well.
  3718                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
  3719                 Node importName = newName(tokenStream.currentName());
  3720                 if (!importName)
  3721                     return null();
  3723                 if (tokenStream.getToken() == TOK_NAME &&
  3724                     tokenStream.currentName() == context->names().as)
  3726                     if (tokenStream.getToken() != TOK_NAME) {
  3727                         report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
  3728                         return null();
  3730                 } else {
  3731                     // Keywords cannot be bound to themselves, so an import name
  3732                     // that is a keyword is a syntax error if it is not followed
  3733                     // by the keyword 'as'.
  3734                     if (IsKeyword(importName->name())) {
  3735                         JSAutoByteString bytes;
  3736                         if (!AtomToPrintableString(context, importName->name(), &bytes))
  3737                             return null();
  3738                         report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
  3739                         return null();
  3741                     tokenStream.ungetToken();
  3743                 Node bindingName = newName(tokenStream.currentName());
  3744                 if (!bindingName)
  3745                     return null();
  3747                 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
  3748                 if (!importSpec)
  3749                     return null();
  3751                 handler.addList(importSpecSet, importSpec);
  3752             } while (tokenStream.matchToken(TOK_COMMA));
  3754             MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
  3757         if (tokenStream.getToken() != TOK_NAME ||
  3758             tokenStream.currentName() != context->names().from)
  3760             report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET);
  3761             return null();
  3764         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
  3765     } else {
  3766         if (tt != TOK_STRING) {
  3767             report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
  3768             return null();
  3771         // Handle the form |import 'a'| by leaving the list empty. This is
  3772         // equivalent to |import {} from 'a'|.
  3773         importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
  3776     Node moduleSpec = stringLiteral();
  3777     if (!moduleSpec)
  3778         return null();
  3780     if (!MatchOrInsertSemicolon(tokenStream))
  3781         return null();
  3783     return handler.newImportDeclaration(importSpecSet, moduleSpec,
  3784                                         TokenPos(begin, pos().end));
  3787 template<>
  3788 SyntaxParseHandler::Node
  3789 Parser<SyntaxParseHandler>::importDeclaration()
  3791     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  3792     return SyntaxParseHandler::NodeFailure;
  3795 template<typename ParseHandler>
  3796 typename ParseHandler::Node
  3797 Parser<ParseHandler>::exportDeclaration()
  3799     JS_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
  3801     if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
  3802         report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
  3803         return null();
  3806     uint32_t begin = pos().begin;
  3808     Node kid;
  3809     switch (TokenKind tt = tokenStream.getToken()) {
  3810       case TOK_LC:
  3811       case TOK_MUL:
  3812         kid = handler.newList(PNK_EXPORT_SPEC_LIST);
  3813         if (!kid)
  3814             return null();
  3816         if (tt == TOK_LC) {
  3817             do {
  3818                 // Handle the forms |export {}| and |export { ..., }| (where ...
  3819                 // is non empty), by escaping the loop early if the next token
  3820                 // is }.
  3821                 tt = tokenStream.peekToken();
  3822                 if (tt == TOK_ERROR)
  3823                     return null();
  3824                 if (tt == TOK_RC)
  3825                     break;
  3827                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
  3828                 Node bindingName = newName(tokenStream.currentName());
  3829                 if (!bindingName)
  3830                     return null();
  3832                 if (tokenStream.getToken() == TOK_NAME &&
  3833                     tokenStream.currentName() == context->names().as)
  3835                     if (tokenStream.getToken(TokenStream::KeywordIsName) != TOK_NAME) {
  3836                         report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME);
  3837                         return null();
  3839                 } else {
  3840                     tokenStream.ungetToken();
  3842                 Node exportName = newName(tokenStream.currentName());
  3843                 if (!exportName)
  3844                     return null();
  3846                 Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
  3847                 if (!exportSpec)
  3848                     return null();
  3850                 handler.addList(kid, exportSpec);
  3851             } while (tokenStream.matchToken(TOK_COMMA));
  3853             MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
  3854         } else {
  3855             // Handle the form |export *| by adding a special export batch
  3856             // specifier to the list.
  3857             Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
  3858             if (!kid)
  3859                 return null();
  3861             handler.addList(kid, exportSpec);
  3863         if (tokenStream.getToken() == TOK_NAME &&
  3864             tokenStream.currentName() == context->names().from)
  3866             MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
  3868             Node moduleSpec = stringLiteral();
  3869             if (!moduleSpec)
  3870                 return null();
  3872             if (!MatchOrInsertSemicolon(tokenStream))
  3873                 return null();
  3875             return handler.newExportFromDeclaration(begin, kid, moduleSpec);
  3876         } else {
  3877             tokenStream.ungetToken();
  3880         kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr;
  3881         if (!kid)
  3882             return null();
  3883         break;
  3885       case TOK_FUNCTION:
  3886         kid = functionStmt();
  3887         if (!kid)
  3888             return null();
  3889         break;
  3891       case TOK_VAR:
  3892       case TOK_CONST:
  3893         kid = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
  3894         if (!kid)
  3895             return null();
  3896         kid->pn_xflags = PNX_POPVAR;
  3898         kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr;
  3899         if (!kid)
  3900             return null();
  3901         break;
  3903       case TOK_NAME:
  3904         // Handle the form |export a} in the same way as |export let a|, by
  3905         // acting as if we've just seen the let keyword. Simply unget the token
  3906         // and fall through.
  3907         tokenStream.ungetToken();
  3908       case TOK_LET:
  3909         kid = letDeclaration();
  3910         if (!kid)
  3911             return null();
  3912         break;
  3914       default:
  3915         report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
  3916         return null();
  3919     return handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
  3922 template<>
  3923 SyntaxParseHandler::Node
  3924 Parser<SyntaxParseHandler>::exportDeclaration()
  3926     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  3927     return SyntaxParseHandler::NodeFailure;
  3931 template <typename ParseHandler>
  3932 typename ParseHandler::Node
  3933 Parser<ParseHandler>::expressionStatement()
  3935     tokenStream.ungetToken();
  3936     Node pnexpr = expr();
  3937     if (!pnexpr)
  3938         return null();
  3939     if (!MatchOrInsertSemicolon(tokenStream))
  3940         return null();
  3941     return handler.newExprStatement(pnexpr, pos().end);
  3944 template <typename ParseHandler>
  3945 typename ParseHandler::Node
  3946 Parser<ParseHandler>::ifStatement()
  3948     uint32_t begin = pos().begin;
  3950     /* An IF node has three kids: condition, then, and optional else. */
  3951     Node cond = condition();
  3952     if (!cond)
  3953         return null();
  3955     if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI &&
  3956         !report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
  3958         return null();
  3961     StmtInfoPC stmtInfo(context);
  3962     PushStatementPC(pc, &stmtInfo, STMT_IF);
  3963     Node thenBranch = statement();
  3964     if (!thenBranch)
  3965         return null();
  3967     Node elseBranch;
  3968     if (tokenStream.matchToken(TOK_ELSE, TokenStream::Operand)) {
  3969         stmtInfo.type = STMT_ELSE;
  3970         elseBranch = statement();
  3971         if (!elseBranch)
  3972             return null();
  3973     } else {
  3974         elseBranch = null();
  3977     PopStatementPC(tokenStream, pc);
  3978     return handler.newIfStatement(begin, cond, thenBranch, elseBranch);
  3981 template <typename ParseHandler>
  3982 typename ParseHandler::Node
  3983 Parser<ParseHandler>::doWhileStatement()
  3985     uint32_t begin = pos().begin;
  3986     StmtInfoPC stmtInfo(context);
  3987     PushStatementPC(pc, &stmtInfo, STMT_DO_LOOP);
  3988     Node body = statement();
  3989     if (!body)
  3990         return null();
  3991     MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
  3992     Node cond = condition();
  3993     if (!cond)
  3994         return null();
  3995     PopStatementPC(tokenStream, pc);
  3997     if (versionNumber() == JSVERSION_ECMA_3) {
  3998         // Pedantically require a semicolon or line break, following ES3.
  3999         // Bug 880329 proposes removing this case.
  4000         if (!MatchOrInsertSemicolon(tokenStream))
  4001             return null();
  4002     } else {
  4003         // The semicolon after do-while is even more optional than most
  4004         // semicolons in JS.  Web compat required this by 2004:
  4005         //   http://bugzilla.mozilla.org/show_bug.cgi?id=238945
  4006         // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
  4007         //   https://bugs.ecmascript.org/show_bug.cgi?id=157
  4008         (void) tokenStream.matchToken(TOK_SEMI);
  4011     return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
  4014 template <typename ParseHandler>
  4015 typename ParseHandler::Node
  4016 Parser<ParseHandler>::whileStatement()
  4018     uint32_t begin = pos().begin;
  4019     StmtInfoPC stmtInfo(context);
  4020     PushStatementPC(pc, &stmtInfo, STMT_WHILE_LOOP);
  4021     Node cond = condition();
  4022     if (!cond)
  4023         return null();
  4024     Node body = statement();
  4025     if (!body)
  4026         return null();
  4027     PopStatementPC(tokenStream, pc);
  4028     return handler.newWhileStatement(begin, cond, body);
  4031 template <typename ParseHandler>
  4032 bool
  4033 Parser<ParseHandler>::matchInOrOf(bool *isForOfp)
  4035     if (tokenStream.matchToken(TOK_IN)) {
  4036         *isForOfp = false;
  4037         return true;
  4039     if (tokenStream.matchContextualKeyword(context->names().of)) {
  4040         *isForOfp = true;
  4041         return true;
  4043     return false;
  4046 template <>
  4047 bool
  4048 Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version,
  4049                                                  bool isForDecl, bool isForEach,
  4050                                                  ParseNodeKind headKind)
  4052     if (isForDecl) {
  4053         if (pn1->pn_count > 1)
  4054             return false;
  4055         if (pn1->isOp(JSOP_DEFCONST))
  4056             return false;
  4058         // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
  4059         // Hence all other destructuring decls are banned there.
  4060         if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) {
  4061             ParseNode *lhs = pn1->pn_head;
  4062             if (lhs->isKind(PNK_ASSIGN))
  4063                 lhs = lhs->pn_left;
  4065             if (lhs->isKind(PNK_OBJECT))
  4066                 return false;
  4067             if (lhs->isKind(PNK_ARRAY) && lhs->pn_count != 2)
  4068                 return false;
  4070         return true;
  4073     switch (pn1->getKind()) {
  4074       case PNK_NAME:
  4075       case PNK_DOT:
  4076       case PNK_CALL:
  4077       case PNK_ELEM:
  4078         return true;
  4080       case PNK_ARRAY:
  4081       case PNK_OBJECT:
  4082         // In JS 1.7 only, for ([K, V] in EXPR) has a special meaning.
  4083         // Hence all other destructuring left-hand sides are banned there.
  4084         if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN)
  4085             return pn1->isKind(PNK_ARRAY) && pn1->pn_count == 2;
  4086         return true;
  4088       default:
  4089         return false;
  4093 template <>
  4094 ParseNode *
  4095 Parser<FullParseHandler>::forStatement()
  4097     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
  4098     uint32_t begin = pos().begin;
  4100     StmtInfoPC forStmt(context);
  4101     PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
  4103     bool isForEach = false;
  4104     unsigned iflags = 0;
  4106     if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each)) {
  4107         iflags = JSITER_FOREACH;
  4108         isForEach = true;
  4111     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
  4113     /*
  4114      * True if we have 'for (var/let/const ...)', except in the oddball case
  4115      * where 'let' begins a let-expression in 'for (let (...) ...)'.
  4116      */
  4117     bool isForDecl = false;
  4119     /* Non-null when isForDecl is true for a 'for (let ...)' statement. */
  4120     RootedStaticBlockObject blockObj(context);
  4122     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
  4123     ParseNode *pn1;
  4126         TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
  4127         if (tt == TOK_SEMI) {
  4128             pn1 = nullptr;
  4129         } else {
  4130             /*
  4131              * Set pn1 to a var list or an initializing expression.
  4133              * Set the parsingForInit flag during parsing of the first clause
  4134              * of the for statement.  This flag will be used by the RelExpr
  4135              * production; if it is set, then the 'in' keyword will not be
  4136              * recognized as an operator, leaving it available to be parsed as
  4137              * part of a for/in loop.
  4139              * A side effect of this restriction is that (unparenthesized)
  4140              * expressions involving an 'in' operator are illegal in the init
  4141              * clause of an ordinary for loop.
  4142              */
  4143             pc->parsingForInit = true;
  4144             if (tt == TOK_VAR || tt == TOK_CONST) {
  4145                 isForDecl = true;
  4146                 tokenStream.consumeKnownToken(tt);
  4147                 pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
  4149             else if (tt == TOK_LET) {
  4150                 handler.disableSyntaxParser();
  4151                 (void) tokenStream.getToken();
  4152                 if (tokenStream.peekToken() == TOK_LP) {
  4153                     pn1 = letBlock(LetExpresion);
  4154                 } else {
  4155                     isForDecl = true;
  4156                     blockObj = StaticBlockObject::create(context);
  4157                     if (!blockObj)
  4158                         return null();
  4159                     pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
  4162             else {
  4163                 pn1 = expr();
  4165             pc->parsingForInit = false;
  4166             if (!pn1)
  4167                 return null();
  4171     JS_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST));
  4172     JS_ASSERT(!!blockObj == (isForDecl && pn1->isOp(JSOP_NOP)));
  4174     // The form 'for (let <vars>; <expr2>; <expr3>) <stmt>' generates an
  4175     // implicit block even if stmt is not a BlockStatement.
  4176     // If the loop has that exact form, then:
  4177     // - forLetImpliedBlock is the node for the implicit block scope.
  4178     // - forLetDecl is the node for the decl 'let <vars>'.
  4179     // Otherwise both are null.
  4180     ParseNode *forLetImpliedBlock = nullptr;
  4181     ParseNode *forLetDecl = nullptr;
  4183     // If non-null, the node for the decl 'var v = expr1' in the weirdo form
  4184     // 'for (var v = expr1 in expr2) stmt'.
  4185     ParseNode *hoistedVar = nullptr;
  4187     /*
  4188      * We can be sure that it's a for/in loop if there's still an 'in'
  4189      * keyword here, even if JavaScript recognizes 'in' as an operator,
  4190      * as we've excluded 'in' from being parsed in RelExpr by setting
  4191      * pc->parsingForInit.
  4192      */
  4193     StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */
  4194     ParseNode *pn2, *pn3;      /* forHead->pn_kid2 and pn_kid3. */
  4195     ParseNodeKind headKind = PNK_FORHEAD;
  4196     if (pn1) {
  4197         bool isForOf;
  4198         if (matchInOrOf(&isForOf))
  4199             headKind = isForOf ? PNK_FOROF : PNK_FORIN;
  4202     if (headKind == PNK_FOROF || headKind == PNK_FORIN) {
  4203         /*
  4204          * Parse the rest of the for/in or for/of head.
  4206          * Here pn1 is everything to the left of 'in' or 'of'. At the end of
  4207          * this block, pn1 is a decl or nullptr, pn2 is the assignment target
  4208          * that receives the enumeration value each iteration, and pn3 is the
  4209          * rhs of 'in'.
  4210          */
  4211         if (headKind == PNK_FOROF) {
  4212             forStmt.type = STMT_FOR_OF_LOOP;
  4213             forStmt.type = (headKind == PNK_FOROF) ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
  4214             if (isForEach) {
  4215                 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
  4216                 return null();
  4218         } else {
  4219             forStmt.type = STMT_FOR_IN_LOOP;
  4220             iflags |= JSITER_ENUMERATE;
  4223         /* Check that the left side of the 'in' or 'of' is valid. */
  4224         if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, headKind)) {
  4225             report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE);
  4226             return null();
  4229         /*
  4230          * After the following if-else, pn2 will point to the name or
  4231          * destructuring pattern on in's left. pn1 will point to the decl, if
  4232          * any, else nullptr. Note that the "declaration with initializer" case
  4233          * rewrites the loop-head, moving the decl and setting pn1 to nullptr.
  4234          */
  4235         if (isForDecl) {
  4236             pn2 = pn1->pn_head;
  4237             if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) {
  4238                 /*
  4239                  * Declaration with initializer.
  4241                  * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
  4242                  * 'const' to hoist the initializer or the entire decl out of
  4243                  * the loop head.
  4244                  */
  4245                 if (headKind == PNK_FOROF) {
  4246                     report(ParseError, false, pn2, JSMSG_INVALID_FOR_OF_INIT);
  4247                     return null();
  4249                 if (blockObj) {
  4250                     report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT);
  4251                     return null();
  4254                 hoistedVar = pn1;
  4256                 /*
  4257                  * All of 'var x = i' is hoisted above 'for (x in o)'.
  4259                  * Request JSOP_POP here since the var is for a simple
  4260                  * name (it is not a destructuring binding's left-hand
  4261                  * side) and it has an initializer.
  4262                  */
  4263                 pn1->pn_xflags |= PNX_POPVAR;
  4264                 pn1 = nullptr;
  4266                 if (pn2->isKind(PNK_ASSIGN)) {
  4267                     pn2 = pn2->pn_left;
  4268                     JS_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) ||
  4269                               pn2->isKind(PNK_NAME));
  4272         } else {
  4273             /* Not a declaration. */
  4274             JS_ASSERT(!blockObj);
  4275             pn2 = pn1;
  4276             pn1 = nullptr;
  4278             if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
  4279                 return null();
  4282         pn3 = (headKind == PNK_FOROF) ? assignExpr() : expr();
  4283         if (!pn3)
  4284             return null();
  4286         if (blockObj) {
  4287             /*
  4288              * Now that the pn3 has been parsed, push the let scope. To hold
  4289              * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node
  4290              * created by PushLetScope around the for's initializer. This also
  4291              * serves to indicate the let-decl to the emitter.
  4292              */
  4293             ParseNode *block = pushLetScope(blockObj, &letStmt);
  4294             if (!block)
  4295                 return null();
  4296             letStmt.isForLetBlock = true;
  4297             block->pn_expr = pn1;
  4298             block->pn_pos = pn1->pn_pos;
  4299             pn1 = block;
  4302         if (isForDecl) {
  4303             /*
  4304              * pn2 is part of a declaration. Make a copy that can be passed to
  4305              * EmitAssignment. Take care to do this after PushLetScope.
  4306              */
  4307             pn2 = cloneLeftHandSide(pn2);
  4308             if (!pn2)
  4309                 return null();
  4312         switch (pn2->getKind()) {
  4313           case PNK_NAME:
  4314             /* Beware 'for (arguments in ...)' with or without a 'var'. */
  4315             pn2->markAsAssigned();
  4316             break;
  4318           case PNK_ASSIGN:
  4319             MOZ_ASSUME_UNREACHABLE("forStatement TOK_ASSIGN");
  4321           case PNK_ARRAY:
  4322           case PNK_OBJECT:
  4323             if (versionNumber() == JSVERSION_1_7) {
  4324                 /*
  4325                  * Destructuring for-in requires [key, value] enumeration
  4326                  * in JS1.7.
  4327                  */
  4328                 if (!isForEach && headKind == PNK_FORIN)
  4329                     iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
  4331             break;
  4333           default:;
  4335     } else {
  4336         if (isForEach) {
  4337             reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP);
  4338             return null();
  4341         headKind = PNK_FORHEAD;
  4343         if (blockObj) {
  4344             /*
  4345              * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
  4346              * to induce the correct scoping for A.
  4347              */
  4348             forLetImpliedBlock = pushLetScope(blockObj, &letStmt);
  4349             if (!forLetImpliedBlock)
  4350                 return null();
  4351             letStmt.isForLetBlock = true;
  4353             forLetDecl = pn1;
  4354             pn1 = nullptr;
  4357         /* Parse the loop condition or null into pn2. */
  4358         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
  4359         if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI) {
  4360             pn2 = nullptr;
  4361         } else {
  4362             pn2 = expr();
  4363             if (!pn2)
  4364                 return null();
  4367         /* Parse the update expression or null into pn3. */
  4368         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
  4369         if (tokenStream.peekToken(TokenStream::Operand) == TOK_RP) {
  4370             pn3 = nullptr;
  4371         } else {
  4372             pn3 = expr();
  4373             if (!pn3)
  4374                 return null();
  4378     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
  4380     TokenPos headPos(begin, pos().end);
  4381     ParseNode *forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos);
  4382     if (!forHead)
  4383         return null();
  4385     /* Parse the loop body. */
  4386     ParseNode *body = statement();
  4387     if (!body)
  4388         return null();
  4390     if (blockObj)
  4391         PopStatementPC(tokenStream, pc);
  4392     PopStatementPC(tokenStream, pc);
  4394     ParseNode *forLoop = handler.newForStatement(begin, forHead, body, iflags);
  4395     if (!forLoop)
  4396         return null();
  4398     if (hoistedVar) {
  4399         ParseNode *pnseq = handler.newList(PNK_SEQ, hoistedVar);
  4400         if (!pnseq)
  4401             return null();
  4402         pnseq->pn_pos = forLoop->pn_pos;
  4403         pnseq->append(forLoop);
  4404         return pnseq;
  4406     if (forLetImpliedBlock) {
  4407         forLetImpliedBlock->pn_expr = forLoop;
  4408         forLetImpliedBlock->pn_pos = forLoop->pn_pos;
  4409         ParseNode *let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock);
  4410         if (!let)
  4411             return null();
  4412         let->pn_pos = forLoop->pn_pos;
  4413         return let;
  4415     return forLoop;
  4418 template <>
  4419 SyntaxParseHandler::Node
  4420 Parser<SyntaxParseHandler>::forStatement()
  4422     /*
  4423      * 'for' statement parsing is fantastically complicated and requires being
  4424      * able to inspect the parse tree for previous parts of the 'for'. Syntax
  4425      * parsing of 'for' statements is thus done separately, and only handles
  4426      * the types of 'for' statements likely to be seen in web content.
  4427      */
  4428     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
  4430     StmtInfoPC forStmt(context);
  4431     PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
  4433     /* Don't parse 'for each' loops. */
  4434     if (allowsForEachIn()) {
  4435         TokenKind tt = tokenStream.peekToken();
  4436         // Not all "yield" tokens are names, but the ones that aren't names are
  4437         // invalid in this context anyway.
  4438         if (tt == TOK_NAME || tt == TOK_YIELD) {
  4439             JS_ALWAYS_FALSE(abortIfSyntaxParser());
  4440             return null();
  4444     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
  4446     /* True if we have 'for (var ...)'. */
  4447     bool isForDecl = false;
  4448     bool simpleForDecl = true;
  4450     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
  4451     Node lhsNode;
  4454         TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
  4455         if (tt == TOK_SEMI) {
  4456             lhsNode = null();
  4457         } else {
  4458             /* Set lhsNode to a var list or an initializing expression. */
  4459             pc->parsingForInit = true;
  4460             if (tt == TOK_VAR) {
  4461                 isForDecl = true;
  4462                 tokenStream.consumeKnownToken(tt);
  4463                 lhsNode = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, &simpleForDecl);
  4465             else if (tt == TOK_CONST || tt == TOK_LET) {
  4466                 JS_ALWAYS_FALSE(abortIfSyntaxParser());
  4467                 return null();
  4469             else {
  4470                 lhsNode = expr();
  4472             if (!lhsNode)
  4473                 return null();
  4474             pc->parsingForInit = false;
  4478     /*
  4479      * We can be sure that it's a for/in loop if there's still an 'in'
  4480      * keyword here, even if JavaScript recognizes 'in' as an operator,
  4481      * as we've excluded 'in' from being parsed in RelExpr by setting
  4482      * pc->parsingForInit.
  4483      */
  4484     bool isForOf;
  4485     if (lhsNode && matchInOrOf(&isForOf)) {
  4486         /* Parse the rest of the for/in or for/of head. */
  4487         forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
  4489         /* Check that the left side of the 'in' or 'of' is valid. */
  4490         if (!isForDecl &&
  4491             lhsNode != SyntaxParseHandler::NodeName &&
  4492             lhsNode != SyntaxParseHandler::NodeGetProp &&
  4493             lhsNode != SyntaxParseHandler::NodeLValue)
  4495             JS_ALWAYS_FALSE(abortIfSyntaxParser());
  4496             return null();
  4499         if (!simpleForDecl) {
  4500             JS_ALWAYS_FALSE(abortIfSyntaxParser());
  4501             return null();
  4504         if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment))
  4505             return null();
  4507         if (!expr())
  4508             return null();
  4509     } else {
  4510         /* Parse the loop condition or null. */
  4511         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
  4512         if (tokenStream.peekToken(TokenStream::Operand) != TOK_SEMI) {
  4513             if (!expr())
  4514                 return null();
  4517         /* Parse the update expression or null. */
  4518         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
  4519         if (tokenStream.peekToken(TokenStream::Operand) != TOK_RP) {
  4520             if (!expr())
  4521                 return null();
  4525     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
  4527     /* Parse the loop body. */
  4528     if (!statement())
  4529         return null();
  4531     PopStatementPC(tokenStream, pc);
  4532     return SyntaxParseHandler::NodeGeneric;
  4535 template <typename ParseHandler>
  4536 typename ParseHandler::Node
  4537 Parser<ParseHandler>::switchStatement()
  4539     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
  4540     uint32_t begin = pos().begin;
  4542     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
  4544     Node discriminant = exprInParens();
  4545     if (!discriminant)
  4546         return null();
  4548     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
  4549     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
  4551     StmtInfoPC stmtInfo(context);
  4552     PushStatementPC(pc, &stmtInfo, STMT_SWITCH);
  4554     if (!GenerateBlockId(tokenStream, pc, pc->topStmt->blockid))
  4555         return null();
  4557     Node caseList = handler.newStatementList(pc->blockid(), pos());
  4558     if (!caseList)
  4559         return null();
  4561     Node saveBlock = pc->blockNode;
  4562     pc->blockNode = caseList;
  4564     bool seenDefault = false;
  4565     TokenKind tt;
  4566     while ((tt = tokenStream.getToken()) != TOK_RC) {
  4567         uint32_t caseBegin = pos().begin;
  4569         Node caseExpr;
  4570         switch (tt) {
  4571           case TOK_DEFAULT:
  4572             if (seenDefault) {
  4573                 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
  4574                 return null();
  4576             seenDefault = true;
  4577             caseExpr = null();  // The default case has pn_left == nullptr.
  4578             break;
  4580           case TOK_CASE:
  4581             caseExpr = expr();
  4582             if (!caseExpr)
  4583                 return null();
  4584             break;
  4586           case TOK_ERROR:
  4587             return null();
  4589           default:
  4590             report(ParseError, false, null(), JSMSG_BAD_SWITCH);
  4591             return null();
  4594         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
  4596         Node body = handler.newStatementList(pc->blockid(), pos());
  4597         if (!body)
  4598             return null();
  4600         while ((tt = tokenStream.peekToken(TokenStream::Operand)) != TOK_RC &&
  4601                tt != TOK_CASE && tt != TOK_DEFAULT) {
  4602             if (tt == TOK_ERROR)
  4603                 return null();
  4604             Node stmt = statement();
  4605             if (!stmt)
  4606                 return null();
  4607             handler.addList(body, stmt);
  4610         Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
  4611         if (!casepn)
  4612             return null();
  4613         handler.addList(caseList, casepn);
  4616     /*
  4617      * Handle the case where there was a let declaration in any case in
  4618      * the switch body, but not within an inner block.  If it replaced
  4619      * pc->blockNode with a new block node then we must refresh caseList and
  4620      * then restore pc->blockNode.
  4621      */
  4622     if (pc->blockNode != caseList)
  4623         caseList = pc->blockNode;
  4624     pc->blockNode = saveBlock;
  4626     PopStatementPC(tokenStream, pc);
  4628     handler.setEndPosition(caseList, pos().end);
  4630     return handler.newSwitchStatement(begin, discriminant, caseList);
  4633 template <typename ParseHandler>
  4634 typename ParseHandler::Node
  4635 Parser<ParseHandler>::continueStatement()
  4637     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
  4638     uint32_t begin = pos().begin;
  4640     RootedPropertyName label(context);
  4641     if (!matchLabel(&label))
  4642         return null();
  4644     StmtInfoPC *stmt = pc->topStmt;
  4645     if (label) {
  4646         for (StmtInfoPC *stmt2 = nullptr; ; stmt = stmt->down) {
  4647             if (!stmt) {
  4648                 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
  4649                 return null();
  4651             if (stmt->type == STMT_LABEL) {
  4652                 if (stmt->label == label) {
  4653                     if (!stmt2 || !stmt2->isLoop()) {
  4654                         report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
  4655                         return null();
  4657                     break;
  4659             } else {
  4660                 stmt2 = stmt;
  4663     } else {
  4664         for (; ; stmt = stmt->down) {
  4665             if (!stmt) {
  4666                 report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
  4667                 return null();
  4669             if (stmt->isLoop())
  4670                 break;
  4674     if (!MatchOrInsertSemicolon(tokenStream))
  4675         return null();
  4677     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
  4680 template <typename ParseHandler>
  4681 typename ParseHandler::Node
  4682 Parser<ParseHandler>::breakStatement()
  4684     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
  4685     uint32_t begin = pos().begin;
  4687     RootedPropertyName label(context);
  4688     if (!matchLabel(&label))
  4689         return null();
  4690     StmtInfoPC *stmt = pc->topStmt;
  4691     if (label) {
  4692         for (; ; stmt = stmt->down) {
  4693             if (!stmt) {
  4694                 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
  4695                 return null();
  4697             if (stmt->type == STMT_LABEL && stmt->label == label)
  4698                 break;
  4700     } else {
  4701         for (; ; stmt = stmt->down) {
  4702             if (!stmt) {
  4703                 report(ParseError, false, null(), JSMSG_TOUGH_BREAK);
  4704                 return null();
  4706             if (stmt->isLoop() || stmt->type == STMT_SWITCH)
  4707                 break;
  4711     if (!MatchOrInsertSemicolon(tokenStream))
  4712         return null();
  4714     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
  4717 template <typename ParseHandler>
  4718 typename ParseHandler::Node
  4719 Parser<ParseHandler>::returnStatement()
  4721     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
  4722     uint32_t begin = pos().begin;
  4724     if (!pc->sc->isFunctionBox()) {
  4725         report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
  4726         return null();
  4729     // Parse an optional operand.
  4730     //
  4731     // This is ugly, but we don't want to require a semicolon.
  4732     Node exprNode;
  4733     switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
  4734       case TOK_ERROR:
  4735         return null();
  4736       case TOK_EOF:
  4737       case TOK_EOL:
  4738       case TOK_SEMI:
  4739       case TOK_RC:
  4740         exprNode = null();
  4741         pc->funHasReturnVoid = true;
  4742         break;
  4743       default: {
  4744         exprNode = expr();
  4745         if (!exprNode)
  4746             return null();
  4747         pc->funHasReturnExpr = true;
  4751     if (!MatchOrInsertSemicolon(tokenStream))
  4752         return null();
  4754     Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
  4755     if (!pn)
  4756         return null();
  4758     if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid &&
  4759         !reportBadReturn(pn, ParseExtraWarning,
  4760                          JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
  4762         return null();
  4765     if (pc->isLegacyGenerator() && exprNode) {
  4766         /* Disallow "return v;" in legacy generators. */
  4767         reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
  4768                         JSMSG_BAD_ANON_GENERATOR_RETURN);
  4769         return null();
  4772     return pn;
  4775 template <typename ParseHandler>
  4776 typename ParseHandler::Node
  4777 Parser<ParseHandler>::yieldExpression()
  4779     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
  4780     uint32_t begin = pos().begin;
  4782     switch (pc->generatorKind()) {
  4783       case StarGenerator:
  4785         JS_ASSERT(pc->sc->isFunctionBox());
  4787         pc->lastYieldOffset = begin;
  4789         ParseNodeKind kind = tokenStream.matchToken(TOK_MUL) ? PNK_YIELD_STAR : PNK_YIELD;
  4791         // ES6 generators require a value.
  4792         Node exprNode = assignExpr();
  4793         if (!exprNode)
  4794             return null();
  4796         return handler.newUnary(kind, JSOP_NOP, begin, exprNode);
  4799       case NotGenerator:
  4800         // We are in code that has not seen a yield, but we are in JS 1.7 or
  4801         // later.  Try to transition to being a legacy generator.
  4802         JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
  4803         JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
  4805         if (!abortIfSyntaxParser())
  4806             return null();
  4808         if (!pc->sc->isFunctionBox()) {
  4809             report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
  4810             return null();
  4813         pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
  4815         if (pc->funHasReturnExpr) {
  4816             /* As in Python (see PEP-255), disallow return v; in generators. */
  4817             reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
  4818                             JSMSG_BAD_ANON_GENERATOR_RETURN);
  4819             return null();
  4821         // Fall through.
  4823       case LegacyGenerator:
  4825         // We are in a legacy generator: a function that has already seen a
  4826         // yield, or in a legacy generator comprehension.
  4827         JS_ASSERT(pc->sc->isFunctionBox());
  4829         pc->lastYieldOffset = begin;
  4831         // Legacy generators do not require a value.
  4832         Node exprNode;
  4833         switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
  4834           case TOK_ERROR:
  4835             return null();
  4836           case TOK_EOF:
  4837           case TOK_EOL:
  4838           case TOK_SEMI:
  4839           case TOK_RC:
  4840           case TOK_RB:
  4841           case TOK_RP:
  4842           case TOK_COLON:
  4843           case TOK_COMMA:
  4844             // No value.
  4845             exprNode = null();
  4846             // ES6 does not permit yield without an operand.  We should
  4847             // encourage users of yield expressions of this kind to pass an
  4848             // operand, to bring users closer to standard syntax.
  4849             if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
  4850                 return null();
  4851             break;
  4852           default:
  4853             exprNode = assignExpr();
  4854             if (!exprNode)
  4855                 return null();
  4858         return handler.newUnary(PNK_YIELD, JSOP_NOP, begin, exprNode);
  4862     MOZ_ASSUME_UNREACHABLE("yieldExpr");
  4865 template <>
  4866 ParseNode *
  4867 Parser<FullParseHandler>::withStatement()
  4869     // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements
  4870     // in syntax-parse mode. See bug 892583.
  4871     if (handler.syntaxParser) {
  4872         handler.disableSyntaxParser();
  4873         abortedSyntaxParse = true;
  4874         return null();
  4877     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
  4878     uint32_t begin = pos().begin;
  4880     // In most cases, we want the constructs forbidden in strict mode code to be
  4881     // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should
  4882     // use reportStrictModeError.  However, 'with' is the sole instance of a
  4883     // construct that is forbidden in strict mode code, but doesn't even merit a
  4884     // warning under JSOPTION_EXTRA_WARNINGS.  See
  4885     // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
  4886     if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
  4887         return null();
  4889     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
  4890     Node objectExpr = exprInParens();
  4891     if (!objectExpr)
  4892         return null();
  4893     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
  4895     bool oldParsingWith = pc->parsingWith;
  4896     pc->parsingWith = true;
  4898     StmtInfoPC stmtInfo(context);
  4899     PushStatementPC(pc, &stmtInfo, STMT_WITH);
  4900     Rooted<StaticWithObject *> staticWith(context, StaticWithObject::create(context));
  4901     if (!staticWith)
  4902         return null();
  4903     staticWith->initEnclosingNestedScopeFromParser(pc->staticScope);
  4904     FinishPushNestedScope(pc, &stmtInfo, *staticWith);
  4906     Node innerBlock = statement();
  4907     if (!innerBlock)
  4908         return null();
  4910     PopStatementPC(tokenStream, pc);
  4912     pc->sc->setBindingsAccessedDynamically();
  4913     pc->parsingWith = oldParsingWith;
  4915     /*
  4916      * Make sure to deoptimize lexical dependencies inside the |with|
  4917      * to safely optimize binding globals (see bug 561923).
  4918      */
  4919     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
  4920         DefinitionNode defn = r.front().value().get<FullParseHandler>();
  4921         DefinitionNode lexdep = handler.resolve(defn);
  4922         handler.deoptimizeUsesWithin(lexdep, TokenPos(begin, pos().begin));
  4925     ObjectBox *staticWithBox = newObjectBox(staticWith);
  4926     if (!staticWithBox)
  4927         return null();
  4928     return handler.newWithStatement(begin, objectExpr, innerBlock, staticWithBox);
  4931 template <>
  4932 SyntaxParseHandler::Node
  4933 Parser<SyntaxParseHandler>::withStatement()
  4935     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  4936     return null();
  4939 template <typename ParseHandler>
  4940 typename ParseHandler::Node
  4941 Parser<ParseHandler>::labeledStatement()
  4943     uint32_t begin = pos().begin;
  4944     RootedPropertyName label(context, tokenStream.currentName());
  4945     for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) {
  4946         if (stmt->type == STMT_LABEL && stmt->label == label) {
  4947             report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
  4948             return null();
  4952     tokenStream.consumeKnownToken(TOK_COLON);
  4954     /* Push a label struct and parse the statement. */
  4955     StmtInfoPC stmtInfo(context);
  4956     PushStatementPC(pc, &stmtInfo, STMT_LABEL);
  4957     stmtInfo.label = label;
  4958     Node pn = statement();
  4959     if (!pn)
  4960         return null();
  4962     /* Pop the label, set pn_expr, and return early. */
  4963     PopStatementPC(tokenStream, pc);
  4965     return handler.newLabeledStatement(label, pn, begin);
  4968 template <typename ParseHandler>
  4969 typename ParseHandler::Node
  4970 Parser<ParseHandler>::throwStatement()
  4972     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
  4973     uint32_t begin = pos().begin;
  4975     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
  4976     TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
  4977     if (tt == TOK_ERROR)
  4978         return null();
  4979     if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
  4980         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
  4981         return null();
  4984     Node throwExpr = expr();
  4985     if (!throwExpr)
  4986         return null();
  4988     if (!MatchOrInsertSemicolon(tokenStream))
  4989         return null();
  4991     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
  4994 template <typename ParseHandler>
  4995 typename ParseHandler::Node
  4996 Parser<ParseHandler>::tryStatement()
  4998     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
  4999     uint32_t begin = pos().begin;
  5001     /*
  5002      * try nodes are ternary.
  5003      * kid1 is the try statement
  5004      * kid2 is the catch node list or null
  5005      * kid3 is the finally statement
  5007      * catch nodes are ternary.
  5008      * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
  5009      * kid2 is the catch guard or null if no guard
  5010      * kid3 is the catch block
  5012      * catch lvalue nodes are either:
  5013      *   TOK_NAME for a single identifier
  5014      *   TOK_RB or TOK_RC for a destructuring left-hand side
  5016      * finally nodes are TOK_LC statement lists.
  5017      */
  5019     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
  5020     StmtInfoPC stmtInfo(context);
  5021     if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_TRY, pc))
  5022         return null();
  5023     Node innerBlock = statements();
  5024     if (!innerBlock)
  5025         return null();
  5026     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
  5027     PopStatementPC(tokenStream, pc);
  5029     bool hasUnconditionalCatch = false;
  5030     Node catchList = null();
  5031     TokenKind tt = tokenStream.getToken();
  5032     if (tt == TOK_CATCH) {
  5033         catchList = handler.newList(PNK_CATCH);
  5034         if (!catchList)
  5035             return null();
  5037         do {
  5038             Node pnblock;
  5039             BindData<ParseHandler> data(context);
  5041             /* Check for another catch after unconditional catch. */
  5042             if (hasUnconditionalCatch) {
  5043                 report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL);
  5044                 return null();
  5047             /*
  5048              * Create a lexical scope node around the whole catch clause,
  5049              * including the head.
  5050              */
  5051             pnblock = pushLexicalScope(&stmtInfo);
  5052             if (!pnblock)
  5053                 return null();
  5054             stmtInfo.type = STMT_CATCH;
  5056             /*
  5057              * Legal catch forms are:
  5058              *   catch (lhs)
  5059              *   catch (lhs if <boolean_expression>)
  5060              * where lhs is a name or a destructuring left-hand side.
  5061              * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
  5062              */
  5063             MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
  5065             /*
  5066              * Contrary to ECMA Ed. 3, the catch variable is lexically
  5067              * scoped, not a property of a new Object instance.  This is
  5068              * an intentional change that anticipates ECMA Ed. 4.
  5069              */
  5070             data.initLet(HoistVars, pc->staticScope->template as<StaticBlockObject>(),
  5071                          JSMSG_TOO_MANY_CATCH_VARS);
  5072             JS_ASSERT(data.let.blockObj);
  5074             tt = tokenStream.getToken();
  5075             Node catchName;
  5076             switch (tt) {
  5077               case TOK_LB:
  5078               case TOK_LC:
  5079                 catchName = destructuringExpr(&data, tt);
  5080                 if (!catchName)
  5081                     return null();
  5082                 break;
  5084               case TOK_YIELD:
  5085                 if (!checkYieldNameValidity())
  5086                     return null();
  5087                 // Fall through.
  5088               case TOK_NAME:
  5090                 RootedPropertyName label(context, tokenStream.currentName());
  5091                 catchName = newBindingNode(label, false);
  5092                 if (!catchName)
  5093                     return null();
  5094                 data.pn = catchName;
  5095                 if (!data.binder(&data, label, this))
  5096                     return null();
  5097                 break;
  5100               default:
  5101                 report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER);
  5102                 return null();
  5105             Node catchGuard = null();
  5106 #if JS_HAS_CATCH_GUARD
  5107             /*
  5108              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
  5109              * to avoid conflicting with the JS2/ECMAv4 type annotation
  5110              * catchguard syntax.
  5111              */
  5112             if (tokenStream.matchToken(TOK_IF)) {
  5113                 catchGuard = expr();
  5114                 if (!catchGuard)
  5115                     return null();
  5117 #endif
  5118             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
  5120             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
  5121             Node catchBody = statements();
  5122             if (!catchBody)
  5123                 return null();
  5124             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
  5125             PopStatementPC(tokenStream, pc);
  5127             if (!catchGuard)
  5128                 hasUnconditionalCatch = true;
  5130             if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
  5131                 return null();
  5132             handler.setEndPosition(catchList, pos().end);
  5133             handler.setEndPosition(pnblock, pos().end);
  5135             tt = tokenStream.getToken(TokenStream::Operand);
  5136         } while (tt == TOK_CATCH);
  5139     Node finallyBlock = null();
  5141     if (tt == TOK_FINALLY) {
  5142         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
  5143         if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc))
  5144             return null();
  5145         finallyBlock = statements();
  5146         if (!finallyBlock)
  5147             return null();
  5148         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
  5149         PopStatementPC(tokenStream, pc);
  5150     } else {
  5151         tokenStream.ungetToken();
  5153     if (!catchList && !finallyBlock) {
  5154         report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY);
  5155         return null();
  5158     return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
  5161 template <typename ParseHandler>
  5162 typename ParseHandler::Node
  5163 Parser<ParseHandler>::debuggerStatement()
  5165     TokenPos p;
  5166     p.begin = pos().begin;
  5167     if (!MatchOrInsertSemicolon(tokenStream))
  5168         return null();
  5169     p.end = pos().end;
  5171     pc->sc->setBindingsAccessedDynamically();
  5172     pc->sc->setHasDebuggerStatement();
  5174     return handler.newDebuggerStatement(p);
  5177 template <typename ParseHandler>
  5178 typename ParseHandler::Node
  5179 Parser<ParseHandler>::statement(bool canHaveDirectives)
  5181     JS_CHECK_RECURSION(context, return null());
  5183     switch (TokenKind tt = tokenStream.getToken(TokenStream::Operand)) {
  5184       case TOK_LC:
  5185         return blockStatement();
  5187       case TOK_CONST:
  5188         if (!abortIfSyntaxParser())
  5189             return null();
  5190         // FALL THROUGH
  5191       case TOK_VAR: {
  5192         Node pn = variables(tt == TOK_CONST ? PNK_CONST : PNK_VAR);
  5193         if (!pn)
  5194             return null();
  5196         // Tell js_EmitTree to generate a final POP.
  5197         handler.setListFlag(pn, PNX_POPVAR);
  5199         if (!MatchOrInsertSemicolon(tokenStream))
  5200             return null();
  5201         return pn;
  5204       case TOK_LET:
  5205         return letStatement();
  5206       case TOK_IMPORT:
  5207         return importDeclaration();
  5208       case TOK_EXPORT:
  5209         return exportDeclaration();
  5210       case TOK_SEMI:
  5211         return handler.newEmptyStatement(pos());
  5212       case TOK_IF:
  5213         return ifStatement();
  5214       case TOK_DO:
  5215         return doWhileStatement();
  5216       case TOK_WHILE:
  5217         return whileStatement();
  5218       case TOK_FOR:
  5219         return forStatement();
  5220       case TOK_SWITCH:
  5221         return switchStatement();
  5222       case TOK_CONTINUE:
  5223         return continueStatement();
  5224       case TOK_BREAK:
  5225         return breakStatement();
  5226       case TOK_RETURN:
  5227         return returnStatement();
  5228       case TOK_WITH:
  5229         return withStatement();
  5230       case TOK_THROW:
  5231         return throwStatement();
  5232       case TOK_TRY:
  5233         return tryStatement();
  5234       case TOK_FUNCTION:
  5235         return functionStmt();
  5236       case TOK_DEBUGGER:
  5237         return debuggerStatement();
  5239       /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
  5240       case TOK_CATCH:
  5241         report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
  5242         return null();
  5244       case TOK_FINALLY:
  5245         report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
  5246         return null();
  5248       case TOK_ERROR:
  5249         return null();
  5251       case TOK_STRING:
  5252         if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
  5253             if (!abortIfSyntaxParser())
  5254                 return null();
  5255             if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
  5256                 return null();
  5258         return expressionStatement();
  5260       case TOK_YIELD:
  5261         if (tokenStream.peekToken() == TOK_COLON) {
  5262             if (!checkYieldNameValidity())
  5263                 return null();
  5264             return labeledStatement();
  5266         return expressionStatement();
  5268       case TOK_NAME:
  5269         if (tokenStream.peekToken() == TOK_COLON)
  5270             return labeledStatement();
  5271         return expressionStatement();
  5273       default:
  5274         return expressionStatement();
  5278 template <typename ParseHandler>
  5279 typename ParseHandler::Node
  5280 Parser<ParseHandler>::expr()
  5282     Node pn = assignExpr();
  5283     if (pn && tokenStream.matchToken(TOK_COMMA)) {
  5284         Node seq = handler.newList(PNK_COMMA, pn);
  5285         if (!seq)
  5286             return null();
  5287         do {
  5288             if (handler.isUnparenthesizedYield(pn)) {
  5289                 report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
  5290                 return null();
  5293             pn = assignExpr();
  5294             if (!pn)
  5295                 return null();
  5296             handler.addList(seq, pn);
  5297         } while (tokenStream.matchToken(TOK_COMMA));
  5298         return seq;
  5300     return pn;
  5303 static const JSOp ParseNodeKindToJSOp[] = {
  5304     JSOP_OR,
  5305     JSOP_AND,
  5306     JSOP_BITOR,
  5307     JSOP_BITXOR,
  5308     JSOP_BITAND,
  5309     JSOP_STRICTEQ,
  5310     JSOP_EQ,
  5311     JSOP_STRICTNE,
  5312     JSOP_NE,
  5313     JSOP_LT,
  5314     JSOP_LE,
  5315     JSOP_GT,
  5316     JSOP_GE,
  5317     JSOP_INSTANCEOF,
  5318     JSOP_IN,
  5319     JSOP_LSH,
  5320     JSOP_RSH,
  5321     JSOP_URSH,
  5322     JSOP_ADD,
  5323     JSOP_SUB,
  5324     JSOP_MUL,
  5325     JSOP_DIV,
  5326     JSOP_MOD
  5327 };
  5329 static inline JSOp
  5330 BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
  5332     JS_ASSERT(pnk >= PNK_BINOP_FIRST);
  5333     JS_ASSERT(pnk <= PNK_BINOP_LAST);
  5334     return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST];
  5337 static bool
  5338 IsBinaryOpToken(TokenKind tok, bool parsingForInit)
  5340     return tok == TOK_IN ? !parsingForInit : TokenKindIsBinaryOp(tok);
  5343 static ParseNodeKind
  5344 BinaryOpTokenKindToParseNodeKind(TokenKind tok)
  5346     JS_ASSERT(TokenKindIsBinaryOp(tok));
  5347     return ParseNodeKind(PNK_BINOP_FIRST + (tok - TOK_BINOP_FIRST));
  5350 static const int PrecedenceTable[] = {
  5351     1, /* PNK_OR */
  5352     2, /* PNK_AND */
  5353     3, /* PNK_BITOR */
  5354     4, /* PNK_BITXOR */
  5355     5, /* PNK_BITAND */
  5356     6, /* PNK_STRICTEQ */
  5357     6, /* PNK_EQ */
  5358     6, /* PNK_STRICTNE */
  5359     6, /* PNK_NE */
  5360     7, /* PNK_LT */
  5361     7, /* PNK_LE */
  5362     7, /* PNK_GT */
  5363     7, /* PNK_GE */
  5364     7, /* PNK_INSTANCEOF */
  5365     7, /* PNK_IN */
  5366     8, /* PNK_LSH */
  5367     8, /* PNK_RSH */
  5368     8, /* PNK_URSH */
  5369     9, /* PNK_ADD */
  5370     9, /* PNK_SUB */
  5371     10, /* PNK_STAR */
  5372     10, /* PNK_DIV */
  5373     10  /* PNK_MOD */
  5374 };
  5376 static const int PRECEDENCE_CLASSES = 10;
  5378 static int
  5379 Precedence(ParseNodeKind pnk) {
  5380     // Everything binds tighter than PNK_LIMIT, because we want to reduce all
  5381     // nodes to a single node when we reach a token that is not another binary
  5382     // operator.
  5383     if (pnk == PNK_LIMIT)
  5384         return 0;
  5386     JS_ASSERT(pnk >= PNK_BINOP_FIRST);
  5387     JS_ASSERT(pnk <= PNK_BINOP_LAST);
  5388     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
  5391 template <typename ParseHandler>
  5392 MOZ_ALWAYS_INLINE typename ParseHandler::Node
  5393 Parser<ParseHandler>::orExpr1()
  5395     // Shift-reduce parser for the left-associative binary operator part of
  5396     // the JS syntax.
  5398     // Conceptually there's just one stack, a stack of pairs (lhs, op).
  5399     // It's implemented using two separate arrays, though.
  5400     Node nodeStack[PRECEDENCE_CLASSES];
  5401     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
  5402     int depth = 0;
  5404     bool oldParsingForInit = pc->parsingForInit;
  5405     pc->parsingForInit = false;
  5407     Node pn;
  5408     for (;;) {
  5409         pn = unaryExpr();
  5410         if (!pn)
  5411             return pn;
  5413         // If a binary operator follows, consume it and compute the
  5414         // corresponding operator.
  5415         TokenKind tok = tokenStream.getToken();
  5416         if (tok == TOK_ERROR)
  5417             return null();
  5418         ParseNodeKind pnk;
  5419         if (IsBinaryOpToken(tok, oldParsingForInit)) {
  5420             pnk = BinaryOpTokenKindToParseNodeKind(tok);
  5421         } else {
  5422             tok = TOK_EOF;
  5423             pnk = PNK_LIMIT;
  5426         // If pnk has precedence less than or equal to another operator on the
  5427         // stack, reduce. This combines nodes on the stack until we form the
  5428         // actual lhs of pnk.
  5429         //
  5430         // The >= in this condition works because all the operators in question
  5431         // are left-associative; if any were not, the case where two operators
  5432         // have equal precedence would need to be handled specially, and the
  5433         // stack would need to be a Vector.
  5434         while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
  5435             depth--;
  5436             ParseNodeKind combiningPnk = kindStack[depth];
  5437             JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
  5438             pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
  5439             if (!pn)
  5440                 return pn;
  5443         if (pnk == PNK_LIMIT)
  5444             break;
  5446         nodeStack[depth] = pn;
  5447         kindStack[depth] = pnk;
  5448         depth++;
  5449         JS_ASSERT(depth <= PRECEDENCE_CLASSES);
  5452     JS_ASSERT(depth == 0);
  5453     pc->parsingForInit = oldParsingForInit;
  5454     return pn;
  5457 template <typename ParseHandler>
  5458 MOZ_ALWAYS_INLINE typename ParseHandler::Node
  5459 Parser<ParseHandler>::condExpr1()
  5461     Node condition = orExpr1();
  5462     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
  5463         return condition;
  5465     /*
  5466      * Always accept the 'in' operator in the middle clause of a ternary,
  5467      * where it's unambiguous, even if we might be parsing the init of a
  5468      * for statement.
  5469      */
  5470     bool oldParsingForInit = pc->parsingForInit;
  5471     pc->parsingForInit = false;
  5472     Node thenExpr = assignExpr();
  5473     pc->parsingForInit = oldParsingForInit;
  5474     if (!thenExpr)
  5475         return null();
  5477     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
  5479     Node elseExpr = assignExpr();
  5480     if (!elseExpr)
  5481         return null();
  5483     tokenStream.getToken(); /* read one token past the end */
  5484     return handler.newConditional(condition, thenExpr, elseExpr);
  5487 template <>
  5488 bool
  5489 Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor)
  5491     switch (pn->getKind()) {
  5492       case PNK_NAME:
  5493         if (!checkStrictAssignment(pn, flavor))
  5494             return false;
  5495         if (flavor == KeyedDestructuringAssignment) {
  5496             /*
  5497              * We may be called on a name node that has already been
  5498              * specialized, in the very weird "for (var [x] = i in o) ..."
  5499              * case. See bug 558633.
  5500              */
  5501             if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
  5502                 pn->setOp(JSOP_SETNAME);
  5503         } else {
  5504             pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
  5506         pn->markAsAssigned();
  5507         break;
  5509       case PNK_DOT:
  5510       case PNK_ELEM:
  5511         break;
  5513       case PNK_ARRAY:
  5514       case PNK_OBJECT:
  5515         if (flavor == CompoundAssignment) {
  5516             report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
  5517             return false;
  5519         if (!checkDestructuring(nullptr, pn))
  5520             return false;
  5521         break;
  5523       case PNK_CALL:
  5524         if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
  5525             return false;
  5526         break;
  5528       default:
  5529         report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
  5530         return false;
  5532     return true;
  5535 template <>
  5536 bool
  5537 Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
  5539     /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
  5540     if (pn != SyntaxParseHandler::NodeName &&
  5541         pn != SyntaxParseHandler::NodeGetProp &&
  5542         pn != SyntaxParseHandler::NodeLValue)
  5544         return abortIfSyntaxParser();
  5546     return checkStrictAssignment(pn, flavor);
  5549 template <typename ParseHandler>
  5550 typename ParseHandler::Node
  5551 Parser<ParseHandler>::assignExpr()
  5553     JS_CHECK_RECURSION(context, return null());
  5555     // It's very common at this point to have a "detectably simple" expression,
  5556     // i.e. a name/number/string token followed by one of the following tokens
  5557     // that obviously isn't part of an expression: , ; : ) ] }
  5558     //
  5559     // (In Parsemark this happens 81.4% of the time;  in code with large
  5560     // numeric arrays, such as some Kraken benchmarks, it happens more often.)
  5561     //
  5562     // In such cases, we can avoid the full expression parsing route through
  5563     // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
  5564     // primaryExpr().
  5566     TokenKind tt = tokenStream.getToken(TokenStream::Operand);
  5568     if (tt == TOK_NAME && tokenStream.nextTokenEndsExpr())
  5569         return identifierName();
  5571     if (tt == TOK_NUMBER && tokenStream.nextTokenEndsExpr())
  5572         return newNumber(tokenStream.currentToken());
  5574     if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
  5575         return stringLiteral();
  5577     if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
  5578         return yieldExpression();
  5580     tokenStream.ungetToken();
  5582     // Save the tokenizer state in case we find an arrow function and have to
  5583     // rewind.
  5584     TokenStream::Position start(keepAtoms);
  5585     tokenStream.tell(&start);
  5587     Node lhs = condExpr1();
  5588     if (!lhs)
  5589         return null();
  5591     ParseNodeKind kind;
  5592     JSOp op;
  5593     switch (tokenStream.currentToken().type) {
  5594       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
  5595       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
  5596       case TOK_SUBASSIGN:    kind = PNK_SUBASSIGN;    op = JSOP_SUB;    break;
  5597       case TOK_BITORASSIGN:  kind = PNK_BITORASSIGN;  op = JSOP_BITOR;  break;
  5598       case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
  5599       case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
  5600       case TOK_LSHASSIGN:    kind = PNK_LSHASSIGN;    op = JSOP_LSH;    break;
  5601       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    op = JSOP_RSH;    break;
  5602       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   op = JSOP_URSH;   break;
  5603       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    op = JSOP_MUL;    break;
  5604       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
  5605       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
  5607       case TOK_ARROW: {
  5608         tokenStream.seek(start);
  5609         if (!abortIfSyntaxParser())
  5610             return null();
  5612         if (tokenStream.getToken() == TOK_ERROR)
  5613             return null();
  5614         tokenStream.ungetToken();
  5616         return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator);
  5619       default:
  5620         JS_ASSERT(!tokenStream.isCurrentTokenAssignment());
  5621         tokenStream.ungetToken();
  5622         return lhs;
  5625     AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
  5626     if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
  5627         return null();
  5629     Node rhs = assignExpr();
  5630     if (!rhs)
  5631         return null();
  5633     return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op);
  5636 static const char incop_name_str[][10] = {"increment", "decrement"};
  5638 template <>
  5639 bool
  5640 Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder)
  5642     // Check.
  5643     if (!kid->isKind(PNK_NAME) &&
  5644         !kid->isKind(PNK_DOT) &&
  5645         !kid->isKind(PNK_ELEM) &&
  5646         !(kid->isKind(PNK_CALL) &&
  5647           (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) ||
  5648            kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_SPREADEVAL) ||
  5649            kid->isOp(JSOP_FUNCALL) ||
  5650            kid->isOp(JSOP_FUNAPPLY))))
  5652         report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
  5653         return false;
  5656     if (!checkStrictAssignment(kid, IncDecAssignment))
  5657         return false;
  5659     // Mark.
  5660     if (kid->isKind(PNK_NAME)) {
  5661         kid->markAsAssigned();
  5662     } else if (kid->isKind(PNK_CALL)) {
  5663         if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
  5664             return false;
  5666     return true;
  5669 template <>
  5670 bool
  5671 Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder)
  5673     // To the extent of what we support in syntax-parse mode, the rules for
  5674     // inc/dec operands are the same as for assignment. There are differences,
  5675     // such as destructuring; but if we hit any of those cases, we'll abort and
  5676     // reparse in full mode.
  5677     return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment);
  5680 template <typename ParseHandler>
  5681 typename ParseHandler::Node
  5682 Parser<ParseHandler>::unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin)
  5684     Node kid = unaryExpr();
  5685     if (!kid)
  5686         return null();
  5687     return handler.newUnary(kind, op, begin, kid);
  5690 template <typename ParseHandler>
  5691 typename ParseHandler::Node
  5692 Parser<ParseHandler>::unaryExpr()
  5694     Node pn, pn2;
  5696     JS_CHECK_RECURSION(context, return null());
  5698     TokenKind tt = tokenStream.getToken(TokenStream::Operand);
  5699     uint32_t begin = pos().begin;
  5700     switch (tt) {
  5701       case TOK_TYPEOF:
  5702         return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF, begin);
  5703       case TOK_VOID:
  5704         return unaryOpExpr(PNK_VOID, JSOP_VOID, begin);
  5705       case TOK_NOT:
  5706         return unaryOpExpr(PNK_NOT, JSOP_NOT, begin);
  5707       case TOK_BITNOT:
  5708         return unaryOpExpr(PNK_BITNOT, JSOP_BITNOT, begin);
  5709       case TOK_ADD:
  5710         return unaryOpExpr(PNK_POS, JSOP_POS, begin);
  5711       case TOK_SUB:
  5712         return unaryOpExpr(PNK_NEG, JSOP_NEG, begin);
  5714       case TOK_INC:
  5715       case TOK_DEC:
  5717         TokenKind tt2 = tokenStream.getToken(TokenStream::Operand);
  5718         pn2 = memberExpr(tt2, true);
  5719         if (!pn2)
  5720             return null();
  5721         if (!checkAndMarkAsIncOperand(pn2, tt, true))
  5722             return null();
  5723         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
  5724                                 JSOP_NOP,
  5725                                 begin,
  5726                                 pn2);
  5729       case TOK_DELETE: {
  5730         Node expr = unaryExpr();
  5731         if (!expr)
  5732             return null();
  5734         // Per spec, deleting any unary expression is valid -- it simply
  5735         // returns true -- except for one case that is illegal in strict mode.
  5736         if (handler.isName(expr)) {
  5737             if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND))
  5738                 return null();
  5739             pc->sc->setBindingsAccessedDynamically();
  5742         return handler.newDelete(begin, expr);
  5745       case TOK_ERROR:
  5746         return null();
  5748       default:
  5749         pn = memberExpr(tt, true);
  5750         if (!pn)
  5751             return null();
  5753         /* Don't look across a newline boundary for a postfix incop. */
  5754         tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
  5755         if (tt == TOK_INC || tt == TOK_DEC) {
  5756             tokenStream.consumeKnownToken(tt);
  5757             if (!checkAndMarkAsIncOperand(pn, tt, false))
  5758                 return null();
  5759             return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
  5760                                     JSOP_NOP,
  5761                                     begin,
  5762                                     pn);
  5764         return pn;
  5766     MOZ_ASSUME_UNREACHABLE("unaryExpr");
  5769 /*
  5770  * A dedicated helper for transplanting the legacy comprehension expression E in
  5772  *   [E for (V in I)]   // legacy array comprehension
  5773  *   (E for (V in I))   // legacy generator expression
  5775  * from its initial location in the AST, on the left of the 'for', to its final
  5776  * position on the right. To avoid a separate pass we do this by adjusting the
  5777  * blockids and name binding links that were established when E was parsed.
  5779  * A legacy generator expression desugars like so:
  5781  *   (E for (V in I)) => (function () { for (var V in I) yield E; })()
  5783  * so the transplanter must adjust static level as well as blockid. E's source
  5784  * coordinates in root->pn_pos are critical to deciding which binding links to
  5785  * preserve and which to cut.
  5787  * NB: This is not a general tree transplanter -- it knows in particular that
  5788  * the one or more bindings induced by V have not yet been created.
  5789  */
  5790 class LegacyCompExprTransplanter
  5792     ParseNode       *root;
  5793     Parser<FullParseHandler> *parser;
  5794     ParseContext<FullParseHandler> *outerpc;
  5795     GeneratorKind   comprehensionKind;
  5796     unsigned        adjust;
  5797     HashSet<Definition *> visitedImplicitArguments;
  5799   public:
  5800     LegacyCompExprTransplanter(ParseNode *pn, Parser<FullParseHandler> *parser,
  5801                                ParseContext<FullParseHandler> *outerpc,
  5802                                GeneratorKind kind, unsigned adj)
  5803       : root(pn), parser(parser), outerpc(outerpc), comprehensionKind(kind), adjust(adj),
  5804         visitedImplicitArguments(parser->context)
  5805     {}
  5807     bool init() {
  5808         return visitedImplicitArguments.init();
  5811     bool transplant(ParseNode *pn);
  5812 };
  5814 /*
  5815  * Any definitions nested within the legacy comprehension expression of a
  5816  * generator expression must move "down" one static level, which of course
  5817  * increases the upvar-frame-skip count.
  5818  */
  5819 template <typename ParseHandler>
  5820 static bool
  5821 BumpStaticLevel(TokenStream &ts, ParseNode *pn, ParseContext<ParseHandler> *pc)
  5823     if (pn->pn_cookie.isFree())
  5824         return true;
  5826     unsigned level = unsigned(pn->pn_cookie.level()) + 1;
  5827     JS_ASSERT(level >= pc->staticLevel);
  5828     return pn->pn_cookie.set(ts, level, pn->pn_cookie.slot());
  5831 template <typename ParseHandler>
  5832 static bool
  5833 AdjustBlockId(TokenStream &ts, ParseNode *pn, unsigned adjust, ParseContext<ParseHandler> *pc)
  5835     JS_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_CODE) || pn->isArity(PN_NAME));
  5836     if (BlockIdLimit - pn->pn_blockid <= adjust + 1) {
  5837         ts.reportError(JSMSG_NEED_DIET, "program");
  5838         return false;
  5840     pn->pn_blockid += adjust;
  5841     if (pn->pn_blockid >= pc->blockidGen)
  5842         pc->blockidGen = pn->pn_blockid + 1;
  5843     return true;
  5846 bool
  5847 LegacyCompExprTransplanter::transplant(ParseNode *pn)
  5849     ParseContext<FullParseHandler> *pc = parser->pc;
  5851     bool isGenexp = comprehensionKind != NotGenerator;
  5853     if (!pn)
  5854         return true;
  5856     switch (pn->getArity()) {
  5857       case PN_LIST:
  5858         for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
  5859             if (!transplant(pn2))
  5860                 return false;
  5862         if (pn->pn_pos >= root->pn_pos) {
  5863             if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
  5864                 return false;
  5866         break;
  5868       case PN_TERNARY:
  5869         if (!transplant(pn->pn_kid1) ||
  5870             !transplant(pn->pn_kid2) ||
  5871             !transplant(pn->pn_kid3))
  5872             return false;
  5873         break;
  5875       case PN_BINARY:
  5876       case PN_BINARY_OBJ:
  5877         if (!transplant(pn->pn_left))
  5878             return false;
  5880         /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
  5881         if (pn->pn_right != pn->pn_left) {
  5882             if (!transplant(pn->pn_right))
  5883                 return false;
  5885         break;
  5887       case PN_UNARY:
  5888         if (!transplant(pn->pn_kid))
  5889             return false;
  5890         break;
  5892       case PN_CODE:
  5893       case PN_NAME:
  5894         if (!transplant(pn->maybeExpr()))
  5895             return false;
  5897         if (pn->isDefn()) {
  5898             if (isGenexp && !BumpStaticLevel(parser->tokenStream, pn, pc))
  5899                 return false;
  5900         } else if (pn->isUsed()) {
  5901             JS_ASSERT(pn->pn_cookie.isFree());
  5903             Definition *dn = pn->pn_lexdef;
  5904             JS_ASSERT(dn->isDefn());
  5906             /*
  5907              * Adjust the definition's block id only if it is a placeholder not
  5908              * to the left of the root node, and if pn is the last use visited
  5909              * in the legacy comprehension expression (to avoid adjusting the
  5910              * blockid multiple times).
  5912              * Non-placeholder definitions within the legacy comprehension
  5913              * expression will be visited further below.
  5914              */
  5915             if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
  5916                 if (isGenexp && !BumpStaticLevel(parser->tokenStream, dn, pc))
  5917                     return false;
  5918                 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
  5919                     return false;
  5922             RootedAtom atom(parser->context, pn->pn_atom);
  5923 #ifdef DEBUG
  5924             StmtInfoPC *stmt = LexicalLookup(pc, atom, nullptr, (StmtInfoPC *)nullptr);
  5925             JS_ASSERT(!stmt || stmt != pc->topStmt);
  5926 #endif
  5927             if (isGenexp && !dn->isOp(JSOP_CALLEE)) {
  5928                 JS_ASSERT(!pc->decls().lookupFirst(atom));
  5930                 if (dn->pn_pos < root->pn_pos) {
  5931                     /*
  5932                      * The variable originally appeared to be a use of a
  5933                      * definition or placeholder outside the generator, but now
  5934                      * we know it is scoped within the legacy comprehension
  5935                      * tail's clauses. Make it (along with any other uses within
  5936                      * the generator) a use of a new placeholder in the
  5937                      * generator's lexdeps.
  5938                      */
  5939                     Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc->blockid(),
  5940                                                                      parser->pos());
  5941                     if (!dn2)
  5942                         return false;
  5943                     dn2->pn_pos = root->pn_pos;
  5945                     /*
  5946                      * Change all uses of |dn| that lie within the generator's
  5947                      * |yield| expression into uses of dn2.
  5948                      */
  5949                     ParseNode **pnup = &dn->dn_uses;
  5950                     ParseNode *pnu;
  5951                     while ((pnu = *pnup) != nullptr && pnu->pn_pos >= root->pn_pos) {
  5952                         pnu->pn_lexdef = dn2;
  5953                         dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
  5954                         pnup = &pnu->pn_link;
  5956                     dn2->dn_uses = dn->dn_uses;
  5957                     dn->dn_uses = *pnup;
  5958                     *pnup = nullptr;
  5959                     DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn2);
  5960                     if (!pc->lexdeps->put(atom, def))
  5961                         return false;
  5962                     if (dn->isClosed())
  5963                         dn2->pn_dflags |= PND_CLOSED;
  5964                 } else if (dn->isPlaceholder()) {
  5965                     /*
  5966                      * The variable first occurs free in the 'yield' expression;
  5967                      * move the existing placeholder node (and all its uses)
  5968                      * from the parent's lexdeps into the generator's lexdeps.
  5969                      */
  5970                     outerpc->lexdeps->remove(atom);
  5971                     DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn);
  5972                     if (!pc->lexdeps->put(atom, def))
  5973                         return false;
  5974                 } else if (dn->isImplicitArguments()) {
  5975                     /*
  5976                      * Implicit 'arguments' Definition nodes (see
  5977                      * PND_IMPLICITARGUMENTS in Parser::functionBody) are only
  5978                      * reachable via the lexdefs of their uses. Unfortunately,
  5979                      * there may be multiple uses, so we need to maintain a set
  5980                      * to only bump the definition once.
  5981                      */
  5982                     if (isGenexp && !visitedImplicitArguments.has(dn)) {
  5983                         if (!BumpStaticLevel(parser->tokenStream, dn, pc))
  5984                             return false;
  5985                         if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
  5986                             return false;
  5987                         if (!visitedImplicitArguments.put(dn))
  5988                             return false;
  5994         if (pn->pn_pos >= root->pn_pos) {
  5995             if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
  5996                 return false;
  5998         break;
  6000       case PN_NULLARY:
  6001         /* Nothing. */
  6002         break;
  6004     return true;
  6007 // Parsing legacy (JS1.7-style) comprehensions is terrible: we parse the head
  6008 // expression as if it's part of a comma expression, then when we see the "for"
  6009 // we transplant the parsed expression into the inside of a constructed
  6010 // for-of/for-in/for-each tail.  Transplanting an already-parsed expression is
  6011 // tricky, but the LegacyCompExprTransplanter handles most of that.
  6012 //
  6013 // The one remaining thing to patch up is the block scope depth.  We need to
  6014 // compute the maximum block scope depth of a function, so we know how much
  6015 // space to reserve in the fixed part of a stack frame.  Normally this is done
  6016 // whenever we leave a statement, via AccumulateBlockScopeDepth.  However if the
  6017 // head has a let expression, we need to re-assign that depth to the tail of the
  6018 // comprehension.
  6019 //
  6020 // Thing is, we don't actually know what that depth is, because the only
  6021 // information we keep is the maximum nested depth within a statement, so we
  6022 // just conservatively propagate the maximum nested depth from the top statement
  6023 // to the comprehension tail.
  6024 //
  6025 template <typename ParseHandler>
  6026 static unsigned
  6027 LegacyComprehensionHeadBlockScopeDepth(ParseContext<ParseHandler> *pc)
  6029     return pc->topStmt ? pc->topStmt->innerBlockScopeDepth : pc->blockScopeDepth;
  6032 /*
  6033  * Starting from a |for| keyword after the first array initialiser element or
  6034  * an expression in an open parenthesis, parse the tail of the comprehension
  6035  * or generator expression signified by this |for| keyword in context.
  6037  * Return null on failure, else return the top-most parse node for the array
  6038  * comprehension or generator expression, with a unary node as the body of the
  6039  * (possibly nested) for-loop, initialized by |kind, op, kid|.
  6040  */
  6041 template <>
  6042 ParseNode *
  6043 Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyStmt, unsigned blockid,
  6044                                                   GeneratorKind comprehensionKind,
  6045                                                   ParseContext<FullParseHandler> *outerpc,
  6046                                                   unsigned innerBlockScopeDepth)
  6048     /*
  6049      * If we saw any inner functions while processing the generator expression
  6050      * then they may have upvars referring to the let vars in this generator
  6051      * which were not correctly processed. Bail out and start over without
  6052      * allowing lazy parsing.
  6053      */
  6054     if (handler.syntaxParser) {
  6055         handler.disableSyntaxParser();
  6056         abortedSyntaxParse = true;
  6057         return nullptr;
  6060     unsigned adjust;
  6061     ParseNode *pn, *pn2, *pn3, **pnp;
  6062     StmtInfoPC stmtInfo(context);
  6063     BindData<FullParseHandler> data(context);
  6064     TokenKind tt;
  6066     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
  6068     bool isGenexp = comprehensionKind != NotGenerator;
  6070     if (isGenexp) {
  6071         JS_ASSERT(comprehensionKind == LegacyGenerator);
  6072         /*
  6073          * Generator expression desugars to an immediately applied lambda that
  6074          * yields the next value from a for-in loop (possibly nested, and with
  6075          * optional if guard). Make pn be the TOK_LC body node.
  6076          */
  6077         pn = pushLexicalScope(&stmtInfo);
  6078         if (!pn)
  6079             return null();
  6080         adjust = pn->pn_blockid - blockid;
  6081     } else {
  6082         /*
  6083          * Make a parse-node and literal object representing the block scope of
  6084          * this array comprehension. Our caller in primaryExpr, the TOK_LB case
  6085          * aka the array initialiser case, has passed the blockid to claim for
  6086          * the comprehension's block scope. We allocate that id or one above it
  6087          * here, by calling PushLexicalScope.
  6089          * In the case of a comprehension expression that has nested blocks
  6090          * (e.g., let expressions), we will allocate a higher blockid but then
  6091          * slide all blocks "to the right" to make room for the comprehension's
  6092          * block scope.
  6093          */
  6094         adjust = pc->blockid();
  6095         pn = pushLexicalScope(&stmtInfo);
  6096         if (!pn)
  6097             return null();
  6099         JS_ASSERT(blockid <= pn->pn_blockid);
  6100         JS_ASSERT(blockid < pc->blockidGen);
  6101         JS_ASSERT(pc->bodyid < blockid);
  6102         pn->pn_blockid = stmtInfo.blockid = blockid;
  6103         JS_ASSERT(adjust < blockid);
  6104         adjust = blockid - adjust;
  6107     handler.setBeginPosition(pn, bodyStmt);
  6109     pnp = &pn->pn_expr;
  6111     LegacyCompExprTransplanter transplanter(bodyStmt, this, outerpc, comprehensionKind, adjust);
  6112     if (!transplanter.init())
  6113         return null();
  6115     if (!transplanter.transplant(bodyStmt))
  6116         return null();
  6118     JS_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object);
  6119     data.initLet(HoistVars, pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG);
  6121     do {
  6122         /*
  6123          * FOR node is binary, left is loop control and right is body.  Use
  6124          * index to count each block-local let-variable on the left-hand side
  6125          * of the in/of.
  6126          */
  6127         pn2 = BinaryNode::create(PNK_FOR, &handler);
  6128         if (!pn2)
  6129             return null();
  6131         pn2->setOp(JSOP_ITER);
  6132         pn2->pn_iflags = JSITER_ENUMERATE;
  6133         if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each))
  6134             pn2->pn_iflags |= JSITER_FOREACH;
  6135         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
  6137         uint32_t startYieldOffset = pc->lastYieldOffset;
  6139         RootedPropertyName name(context);
  6140         tt = tokenStream.getToken();
  6141         switch (tt) {
  6142           case TOK_LB:
  6143           case TOK_LC:
  6144             pc->inDeclDestructuring = true;
  6145             pn3 = primaryExpr(tt);
  6146             pc->inDeclDestructuring = false;
  6147             if (!pn3)
  6148                 return null();
  6149             break;
  6151           case TOK_NAME:
  6152             name = tokenStream.currentName();
  6154             /*
  6155              * Create a name node with pn_op JSOP_NAME.  We can't set pn_op to
  6156              * JSOP_GETLOCAL here, because we don't yet know the block's depth
  6157              * in the operand stack frame.  The code generator computes that,
  6158              * and it tries to bind all names to slots, so we must let it do
  6159              * the deed.
  6160              */
  6161             pn3 = newBindingNode(name, false);
  6162             if (!pn3)
  6163                 return null();
  6164             break;
  6166           default:
  6167             report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
  6169           case TOK_ERROR:
  6170             return null();
  6173         bool isForOf;
  6174         if (!matchInOrOf(&isForOf)) {
  6175             report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME);
  6176             return null();
  6178         ParseNodeKind headKind = PNK_FORIN;
  6179         if (isForOf) {
  6180             if (pn2->pn_iflags != JSITER_ENUMERATE) {
  6181                 JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
  6182                 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
  6183                 return null();
  6185             pn2->pn_iflags = 0;
  6186             headKind = PNK_FOROF;
  6189         ParseNode *pn4 = expr();
  6190         if (!pn4)
  6191             return null();
  6192         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
  6194         if (isGenexp && pc->lastYieldOffset != startYieldOffset) {
  6195             reportWithOffset(ParseError, false, pc->lastYieldOffset,
  6196                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
  6197             return null();
  6200         switch (tt) {
  6201           case TOK_LB:
  6202           case TOK_LC:
  6203             if (!checkDestructuring(&data, pn3))
  6204                 return null();
  6206             if (versionNumber() == JSVERSION_1_7 &&
  6207                 !(pn2->pn_iflags & JSITER_FOREACH) &&
  6208                 !isForOf)
  6210                 /* Destructuring requires [key, value] enumeration in JS1.7. */
  6211                 if (!pn3->isKind(PNK_ARRAY) || pn3->pn_count != 2) {
  6212                     report(ParseError, false, null(), JSMSG_BAD_FOR_LEFTSIDE);
  6213                     return null();
  6216                 JS_ASSERT(pn2->isOp(JSOP_ITER));
  6217                 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
  6218                 JS_ASSERT(headKind == PNK_FORIN);
  6219                 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
  6221             break;
  6223           case TOK_NAME:
  6224             data.pn = pn3;
  6225             if (!data.binder(&data, name, this))
  6226                 return null();
  6227             break;
  6229           default:;
  6232         /*
  6233          * Synthesize a declaration. Every definition must appear in the parse
  6234          * tree in order for ComprehensionTranslator to work.
  6235          */
  6236         ParseNode *vars = ListNode::create(PNK_VAR, &handler);
  6237         if (!vars)
  6238             return null();
  6239         vars->setOp(JSOP_NOP);
  6240         vars->pn_pos = pn3->pn_pos;
  6241         vars->makeEmpty();
  6242         vars->append(pn3);
  6244         /* Definitions can't be passed directly to EmitAssignment as lhs. */
  6245         pn3 = cloneLeftHandSide(pn3);
  6246         if (!pn3)
  6247             return null();
  6249         pn2->pn_left = handler.newTernary(headKind, vars, pn3, pn4);
  6250         if (!pn2->pn_left)
  6251             return null();
  6252         *pnp = pn2;
  6253         pnp = &pn2->pn_right;
  6254     } while (tokenStream.matchToken(TOK_FOR));
  6256     if (tokenStream.matchToken(TOK_IF)) {
  6257         pn2 = TernaryNode::create(PNK_IF, &handler);
  6258         if (!pn2)
  6259             return null();
  6260         pn2->pn_kid1 = condition();
  6261         if (!pn2->pn_kid1)
  6262             return null();
  6263         *pnp = pn2;
  6264         pnp = &pn2->pn_kid2;
  6267     *pnp = bodyStmt;
  6269     pc->topStmt->innerBlockScopeDepth += innerBlockScopeDepth;
  6270     PopStatementPC(tokenStream, pc);
  6272     handler.setEndPosition(pn, pos().end);
  6274     return pn;
  6277 template <>
  6278 SyntaxParseHandler::Node
  6279 Parser<SyntaxParseHandler>::legacyComprehensionTail(SyntaxParseHandler::Node bodyStmt,
  6280                                                     unsigned blockid,
  6281                                                     GeneratorKind comprehensionKind,
  6282                                                     ParseContext<SyntaxParseHandler> *outerpc,
  6283                                                     unsigned innerBlockScopeDepth)
  6285     abortIfSyntaxParser();
  6286     return null();
  6289 template <>
  6290 ParseNode*
  6291 Parser<FullParseHandler>::legacyArrayComprehension(ParseNode *array)
  6293     array->setKind(PNK_ARRAYCOMP);
  6295     // Remove the single element from array's linked list, leaving us with an
  6296     // empty array literal and a comprehension expression.
  6297     JS_ASSERT(array->pn_count == 1);
  6298     ParseNode *bodyExpr = array->last();
  6299     array->pn_count = 0;
  6300     array->pn_tail = &array->pn_head;
  6301     *array->pn_tail = nullptr;
  6303     ParseNode *arrayPush = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH,
  6304                                             bodyExpr->pn_pos.begin, bodyExpr);
  6305     if (!arrayPush)
  6306         return null();
  6308     ParseNode *comp = legacyComprehensionTail(arrayPush, array->pn_blockid, NotGenerator,
  6309                                               nullptr, LegacyComprehensionHeadBlockScopeDepth(pc));
  6310     if (!comp)
  6311         return null();
  6313     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
  6315     TokenPos p = handler.getPosition(array);
  6316     p.end = pos().end;
  6317     return handler.newArrayComprehension(comp, array->pn_blockid, p);
  6320 template <>
  6321 SyntaxParseHandler::Node
  6322 Parser<SyntaxParseHandler>::legacyArrayComprehension(Node array)
  6324     abortIfSyntaxParser();
  6325     return null();
  6328 template <typename ParseHandler>
  6329 typename ParseHandler::Node
  6330 Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKind,
  6331                                                    unsigned begin, Node innerStmt)
  6333     JS_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator);
  6334     JS_ASSERT(!!innerStmt == (comprehensionKind == LegacyGenerator));
  6336     Node genfn = handler.newFunctionDefinition();
  6337     if (!genfn)
  6338         return null();
  6339     handler.setOp(genfn, JSOP_LAMBDA);
  6341     ParseContext<ParseHandler> *outerpc = pc;
  6343     // If we are off the main thread, the generator meta-objects have
  6344     // already been created by js::StartOffThreadParseScript, so cx will not
  6345     // be necessary.
  6346     RootedObject proto(context);
  6347     if (comprehensionKind == StarGenerator) {
  6348         JSContext *cx = context->maybeJSContext();
  6349         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
  6350         if (!proto)
  6351             return null();
  6354     RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto));
  6355     if (!fun)
  6356         return null();
  6358     // Create box for fun->object early to root it.
  6359     Directives directives(/* strict = */ outerpc->sc->strict);
  6360     FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind);
  6361     if (!genFunbox)
  6362         return null();
  6364     ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox,
  6365                                      /* newDirectives = */ nullptr,
  6366                                      outerpc->staticLevel + 1, outerpc->blockidGen,
  6367                                      /* blockScopeDepth = */ 0);
  6368     if (!genpc.init(tokenStream))
  6369         return null();
  6371     /*
  6372      * We assume conservatively that any deoptimization flags in pc->sc
  6373      * come from the kid. So we propagate these flags into genfn. For code
  6374      * simplicity we also do not detect if the flags were only set in the
  6375      * kid and could be removed from pc->sc.
  6376      */
  6377     genFunbox->anyCxFlags = outerpc->sc->anyCxFlags;
  6378     if (outerpc->sc->isFunctionBox())
  6379         genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
  6381     JS_ASSERT(genFunbox->generatorKind() == comprehensionKind);
  6382     genFunbox->inGenexpLambda = true;
  6383     handler.setBlockId(genfn, genpc.bodyid);
  6385     Node body;
  6387     if (comprehensionKind == StarGenerator) {
  6388         body = comprehension(StarGenerator);
  6389         if (!body)
  6390             return null();
  6391     } else {
  6392         JS_ASSERT(comprehensionKind == LegacyGenerator);
  6393         body = legacyComprehensionTail(innerStmt, outerpc->blockid(), LegacyGenerator,
  6394                                        outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc));
  6395         if (!body)
  6396             return null();
  6399     if (comprehensionKind == StarGenerator)
  6400         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
  6402     handler.setBeginPosition(body, begin);
  6403     handler.setEndPosition(body, pos().end);
  6405     handler.setBeginPosition(genfn, begin);
  6406     handler.setEndPosition(genfn, pos().end);
  6408     // Note that if we ever start syntax-parsing generators, we will also
  6409     // need to propagate the closed-over variable set to the inner
  6410     // lazyscript, as in finishFunctionDefinition.
  6411     handler.setFunctionBody(genfn, body);
  6413     PropagateTransitiveParseFlags(genFunbox, outerpc->sc);
  6415     if (!leaveFunction(genfn, outerpc))
  6416         return null();
  6418     return genfn;
  6421 #if JS_HAS_GENERATOR_EXPRS
  6423 /*
  6424  * Starting from a |for| keyword after an expression, parse the comprehension
  6425  * tail completing this generator expression. Wrap the expression at kid in a
  6426  * generator function that is immediately called to evaluate to the generator
  6427  * iterator that is the value of this legacy generator expression.
  6429  * |kid| must be the expression before the |for| keyword; we return an
  6430  * application of a generator function that includes the |for| loops and
  6431  * |if| guards, with |kid| as the operand of a |yield| expression as the
  6432  * innermost loop body.
  6434  * Note how unlike Python, we do not evaluate the expression to the right of
  6435  * the first |in| in the chain of |for| heads. Instead, a generator expression
  6436  * is merely sugar for a generator function expression and its application.
  6437  */
  6438 template <>
  6439 ParseNode *
  6440 Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode *expr)
  6442     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
  6444     /* Create a |yield| node for |kid|. */
  6445     ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, expr->pn_pos.begin, expr);
  6446     if (!yieldExpr)
  6447         return null();
  6448     yieldExpr->setInParens(true);
  6450     // A statement to wrap the yield expression.
  6451     ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, expr->pn_pos.end);
  6452     if (!yieldStmt)
  6453         return null();
  6455     /* Make a new node for the desugared generator function. */
  6456     ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, yieldStmt);
  6457     if (!genfn)
  6458         return null();
  6460     /*
  6461      * Our result is a call expression that invokes the anonymous generator
  6462      * function object.
  6463      */
  6464     ParseNode *result = ListNode::create(PNK_GENEXP, &handler);
  6465     if (!result)
  6466         return null();
  6467     result->setOp(JSOP_CALL);
  6468     result->pn_pos.begin = genfn->pn_pos.begin;
  6469     result->initList(genfn);
  6470     return result;
  6473 template <>
  6474 SyntaxParseHandler::Node
  6475 Parser<SyntaxParseHandler>::legacyGeneratorExpr(Node kid)
  6477     JS_ALWAYS_FALSE(abortIfSyntaxParser());
  6478     return SyntaxParseHandler::NodeFailure;
  6481 static const char js_generator_str[] = "generator";
  6483 #endif /* JS_HAS_GENERATOR_EXPRS */
  6485 template <typename ParseHandler>
  6486 typename ParseHandler::Node
  6487 Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
  6489     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
  6491     uint32_t begin = pos().begin;
  6493     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
  6495     // FIXME: Destructuring binding (bug 980828).
  6497     MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
  6498     RootedPropertyName name(context, tokenStream.currentName());
  6499     if (name == context->names().let) {
  6500         report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
  6501         return null();
  6503     if (!tokenStream.matchContextualKeyword(context->names().of)) {
  6504         report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
  6505         return null();
  6508     Node rhs = assignExpr();
  6509     if (!rhs)
  6510         return null();
  6512     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
  6514     TokenPos headPos(begin, pos().end);
  6516     StmtInfoPC stmtInfo(context);
  6517     BindData<ParseHandler> data(context);
  6518     RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
  6519     if (!blockObj)
  6520         return null();
  6521     data.initLet(DontHoistVars, *blockObj, JSMSG_TOO_MANY_LOCALS);
  6522     Node lhs = newName(name);
  6523     if (!lhs)
  6524         return null();
  6525     Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP);
  6526     if (!decls)
  6527         return null();
  6528     data.pn = lhs;
  6529     if (!data.binder(&data, name, this))
  6530         return null();
  6531     Node letScope = pushLetScope(blockObj, &stmtInfo);
  6532     if (!letScope)
  6533         return null();
  6534     handler.setLexicalScopeBody(letScope, decls);
  6536     Node assignLhs = newName(name);
  6537     if (!assignLhs)
  6538         return null();
  6539     if (!noteNameUse(name, assignLhs))
  6540         return null();
  6541     handler.setOp(assignLhs, JSOP_SETNAME);
  6543     Node head = handler.newForHead(PNK_FOROF, letScope, assignLhs, rhs, headPos);
  6544     if (!head)
  6545         return null();
  6547     Node tail = comprehensionTail(comprehensionKind);
  6548     if (!tail)
  6549         return null();
  6551     PopStatementPC(tokenStream, pc);
  6553     return handler.newForStatement(begin, head, tail, JSOP_ITER);
  6556 template <typename ParseHandler>
  6557 typename ParseHandler::Node
  6558 Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
  6560     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
  6562     uint32_t begin = pos().begin;
  6564     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
  6565     Node cond = assignExpr();
  6566     if (!cond)
  6567         return null();
  6568     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
  6570     /* Check for (a = b) and warn about possible (a == b) mistype. */
  6571     if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) &&
  6572         !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
  6574         return null();
  6577     Node then = comprehensionTail(comprehensionKind);
  6578     if (!then)
  6579         return null();
  6581     return handler.newIfStatement(begin, cond, then, null());
  6584 template <typename ParseHandler>
  6585 typename ParseHandler::Node
  6586 Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
  6588     JS_CHECK_RECURSION(context, return null());
  6590     if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
  6591         return comprehensionFor(comprehensionKind);
  6593     if (tokenStream.matchToken(TOK_IF, TokenStream::Operand))
  6594         return comprehensionIf(comprehensionKind);
  6596     uint32_t begin = pos().begin;
  6598     Node bodyExpr = assignExpr();
  6599     if (!bodyExpr)
  6600         return null();
  6602     if (comprehensionKind == NotGenerator)
  6603         return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
  6605     JS_ASSERT(comprehensionKind == StarGenerator);
  6606     Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr);
  6607     if (!yieldExpr)
  6608         return null();
  6609     handler.setInParens(yieldExpr);
  6611     return handler.newExprStatement(yieldExpr, pos().end);
  6614 // Parse an ES6 generator or array comprehension, starting at the first 'for'.
  6615 // The caller is responsible for matching the ending TOK_RP or TOK_RB.
  6616 template <typename ParseHandler>
  6617 typename ParseHandler::Node
  6618 Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
  6620     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
  6622     uint32_t startYieldOffset = pc->lastYieldOffset;
  6624     Node body = comprehensionFor(comprehensionKind);
  6625     if (!body)
  6626         return null();
  6628     if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
  6629         reportWithOffset(ParseError, false, pc->lastYieldOffset,
  6630                          JSMSG_BAD_GENEXP_BODY, js_yield_str);
  6631         return null();
  6634     return body;
  6637 template <typename ParseHandler>
  6638 typename ParseHandler::Node
  6639 Parser<ParseHandler>::arrayComprehension(uint32_t begin)
  6641     Node inner = comprehension(NotGenerator);
  6642     if (!inner)
  6643         return null();
  6645     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
  6647     Node comp = handler.newList(PNK_ARRAYCOMP, inner);
  6648     if (!comp)
  6649         return null();
  6651     handler.setBeginPosition(comp, begin);
  6652     handler.setEndPosition(comp, pos().end);
  6654     return comp;
  6657 template <typename ParseHandler>
  6658 typename ParseHandler::Node
  6659 Parser<ParseHandler>::generatorComprehension(uint32_t begin)
  6661     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
  6663     // We have no problem parsing generator comprehensions inside lazy
  6664     // functions, but the bytecode emitter currently can't handle them that way,
  6665     // because when it goes to emit the code for the inner generator function,
  6666     // it expects outer functions to have non-lazy scripts.
  6667     if (!abortIfSyntaxParser())
  6668         return null();
  6670     Node genfn = generatorComprehensionLambda(StarGenerator, begin, null());
  6671     if (!genfn)
  6672         return null();
  6674     Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL);
  6675     if (!result)
  6676         return null();
  6677     handler.setBeginPosition(result, begin);
  6678     handler.setEndPosition(result, pos().end);
  6680     return result;
  6683 template <typename ParseHandler>
  6684 typename ParseHandler::Node
  6685 Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
  6687     uint32_t startYieldOffset = pc->lastYieldOffset;
  6688     Node res = assignExpr();
  6689     if (res && pc->lastYieldOffset != startYieldOffset) {
  6690         reportWithOffset(ParseError, false, pc->lastYieldOffset,
  6691                          msg, js_yield_str);
  6692         return null();
  6694     return res;
  6697 template <typename ParseHandler>
  6698 bool
  6699 Parser<ParseHandler>::argumentList(Node listNode, bool *isSpread)
  6701     if (tokenStream.matchToken(TOK_RP, TokenStream::Operand)) {
  6702         handler.setEndPosition(listNode, pos().end);
  6703         return true;
  6706     uint32_t startYieldOffset = pc->lastYieldOffset;
  6707     bool arg0 = true;
  6709     do {
  6710         bool spread = false;
  6711         uint32_t begin = 0;
  6712         if (tokenStream.matchToken(TOK_TRIPLEDOT, TokenStream::Operand)) {
  6713             spread = true;
  6714             begin = pos().begin;
  6715             *isSpread = true;
  6718         Node argNode = assignExpr();
  6719         if (!argNode)
  6720             return false;
  6721         if (spread) {
  6722             argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
  6723             if (!argNode)
  6724                 return null();
  6727         if (handler.isOperationWithoutParens(argNode, PNK_YIELD) &&
  6728             tokenStream.peekToken() == TOK_COMMA) {
  6729             report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
  6730             return false;
  6732 #if JS_HAS_GENERATOR_EXPRS
  6733         if (!spread && tokenStream.matchToken(TOK_FOR)) {
  6734             if (pc->lastYieldOffset != startYieldOffset) {
  6735                 reportWithOffset(ParseError, false, pc->lastYieldOffset,
  6736                                  JSMSG_BAD_GENEXP_BODY, js_yield_str);
  6737                 return false;
  6739             argNode = legacyGeneratorExpr(argNode);
  6740             if (!argNode)
  6741                 return false;
  6742             if (!arg0 || tokenStream.peekToken() == TOK_COMMA) {
  6743                 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
  6744                 return false;
  6747 #endif
  6748         arg0 = false;
  6750         handler.addList(listNode, argNode);
  6751     } while (tokenStream.matchToken(TOK_COMMA));
  6753     if (tokenStream.getToken() != TOK_RP) {
  6754         report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
  6755         return false;
  6757     handler.setEndPosition(listNode, pos().end);
  6758     return true;
  6761 template <typename ParseHandler>
  6762 typename ParseHandler::Node
  6763 Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
  6765     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
  6767     Node lhs;
  6769     JS_CHECK_RECURSION(context, return null());
  6771     /* Check for new expression first. */
  6772     if (tt == TOK_NEW) {
  6773         lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
  6774         if (!lhs)
  6775             return null();
  6777         tt = tokenStream.getToken(TokenStream::Operand);
  6778         Node ctorExpr = memberExpr(tt, false);
  6779         if (!ctorExpr)
  6780             return null();
  6782         handler.addList(lhs, ctorExpr);
  6784         if (tokenStream.matchToken(TOK_LP)) {
  6785             bool isSpread = false;
  6786             if (!argumentList(lhs, &isSpread))
  6787                 return null();
  6788             if (isSpread)
  6789                 handler.setOp(lhs, JSOP_SPREADNEW);
  6791     } else {
  6792         lhs = primaryExpr(tt);
  6793         if (!lhs)
  6794             return null();
  6797     while ((tt = tokenStream.getToken()) > TOK_EOF) {
  6798         Node nextMember;
  6799         if (tt == TOK_DOT) {
  6800             tt = tokenStream.getToken(TokenStream::KeywordIsName);
  6801             if (tt == TOK_ERROR)
  6802                 return null();
  6803             if (tt == TOK_NAME) {
  6804                 PropertyName *field = tokenStream.currentName();
  6805                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
  6806                 if (!nextMember)
  6807                     return null();
  6808             } else {
  6809                 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
  6810                 return null();
  6812         } else if (tt == TOK_LB) {
  6813             Node propExpr = expr();
  6814             if (!propExpr)
  6815                 return null();
  6817             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
  6819             nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
  6820             if (!nextMember)
  6821                 return null();
  6822         } else if (allowCallSyntax && tt == TOK_LP) {
  6823             JSOp op = JSOP_CALL;
  6824             nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL);
  6825             if (!nextMember)
  6826                 return null();
  6828             if (JSAtom *atom = handler.isName(lhs)) {
  6829                 if (atom == context->names().eval) {
  6830                     /* Select JSOP_EVAL and flag pc as heavyweight. */
  6831                     op = JSOP_EVAL;
  6832                     pc->sc->setBindingsAccessedDynamically();
  6834                     /*
  6835                      * In non-strict mode code, direct calls to eval can add
  6836                      * variables to the call object.
  6837                      */
  6838                     if (pc->sc->isFunctionBox() && !pc->sc->strict)
  6839                         pc->sc->asFunctionBox()->setHasExtensibleScope();
  6841             } else if (JSAtom *atom = handler.isGetProp(lhs)) {
  6842                 /* Select JSOP_FUNAPPLY given foo.apply(...). */
  6843                 if (atom == context->names().apply) {
  6844                     op = JSOP_FUNAPPLY;
  6845                     if (pc->sc->isFunctionBox())
  6846                         pc->sc->asFunctionBox()->usesApply = true;
  6847                 } else if (atom == context->names().call) {
  6848                     op = JSOP_FUNCALL;
  6852             handler.setBeginPosition(nextMember, lhs);
  6853             handler.addList(nextMember, lhs);
  6855             bool isSpread = false;
  6856             if (!argumentList(nextMember, &isSpread))
  6857                 return null();
  6858             if (isSpread)
  6859                 op = (op == JSOP_EVAL ? JSOP_SPREADEVAL : JSOP_SPREADCALL);
  6860             handler.setOp(nextMember, op);
  6861         } else {
  6862             tokenStream.ungetToken();
  6863             return lhs;
  6866         lhs = nextMember;
  6868     if (tt == TOK_ERROR)
  6869         return null();
  6870     return lhs;
  6873 template <typename ParseHandler>
  6874 typename ParseHandler::Node
  6875 Parser<ParseHandler>::newName(PropertyName *name)
  6877     return handler.newName(name, pc->blockid(), pos());
  6880 template <typename ParseHandler>
  6881 typename ParseHandler::Node
  6882 Parser<ParseHandler>::identifierName()
  6884     RootedPropertyName name(context, tokenStream.currentName());
  6885     Node pn = newName(name);
  6886     if (!pn)
  6887         return null();
  6889     if (!pc->inDeclDestructuring && !noteNameUse(name, pn))
  6890         return null();
  6892     return pn;
  6895 template <typename ParseHandler>
  6896 typename ParseHandler::Node
  6897 Parser<ParseHandler>::stringLiteral()
  6899     JSAtom *atom = tokenStream.currentToken().atom();
  6901     // Large strings are fast to parse but slow to compress. Stop compression on
  6902     // them, so we don't wait for a long time for compression to finish at the
  6903     // end of compilation.
  6904     const size_t HUGE_STRING = 50000;
  6905     if (sct && sct->active() && atom->length() >= HUGE_STRING)
  6906         sct->abort();
  6908     return handler.newStringLiteral(atom, pos());
  6911 template <typename ParseHandler>
  6912 typename ParseHandler::Node
  6913 Parser<ParseHandler>::newRegExp()
  6915     // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
  6916     const jschar *chars = tokenStream.getTokenbuf().begin();
  6917     size_t length = tokenStream.getTokenbuf().length();
  6918     RegExpFlag flags = tokenStream.currentToken().regExpFlags();
  6920     Rooted<RegExpObject*> reobj(context);
  6921     if (RegExpStatics *res = context->global()->getRegExpStatics())
  6922         reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
  6923     else
  6924         reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream);
  6926     if (!reobj)
  6927         return null();
  6929     return handler.newRegExp(reobj, pos(), *this);
  6932 template <typename ParseHandler>
  6933 typename ParseHandler::Node
  6934 Parser<ParseHandler>::arrayInitializer()
  6936     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
  6938     uint32_t begin = pos().begin;
  6939     Node literal = handler.newArrayLiteral(begin, pc->blockidGen);
  6940     if (!literal)
  6941         return null();
  6943     if (tokenStream.matchToken(TOK_RB, TokenStream::Operand)) {
  6944         /*
  6945          * Mark empty arrays as non-constant, since we cannot easily
  6946          * determine their type.
  6947          */
  6948         handler.setListFlag(literal, PNX_NONCONST);
  6949     } else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) {
  6950         // ES6 array comprehension.
  6951         return arrayComprehension(begin);
  6952     } else {
  6953         bool spread = false, missingTrailingComma = false;
  6954         uint32_t index = 0;
  6955         for (; ; index++) {
  6956             if (index == JSObject::NELEMENTS_LIMIT) {
  6957                 report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
  6958                 return null();
  6961             TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
  6962             if (tt == TOK_RB)
  6963                 break;
  6965             if (tt == TOK_COMMA) {
  6966                 tokenStream.consumeKnownToken(TOK_COMMA);
  6967                 if (!handler.addElision(literal, pos()))
  6968                     return null();
  6969             } else if (tt == TOK_TRIPLEDOT) {
  6970                 spread = true;
  6971                 tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
  6972                 uint32_t begin = pos().begin;
  6973                 Node inner = assignExpr();
  6974                 if (!inner)
  6975                     return null();
  6976                 if (!handler.addSpreadElement(literal, begin, inner))
  6977                     return null();
  6978             } else {
  6979                 Node element = assignExpr();
  6980                 if (!element)
  6981                     return null();
  6982                 if (foldConstants && !FoldConstants(context, &element, this))
  6983                     return null();
  6984                 if (!handler.addArrayElement(literal, element))
  6985                     return null();
  6988             if (tt != TOK_COMMA) {
  6989                 /* If we didn't already match TOK_COMMA in above case. */
  6990                 if (!tokenStream.matchToken(TOK_COMMA)) {
  6991                     missingTrailingComma = true;
  6992                     break;
  6997         /*
  6998          * At this point, (index == 0 && missingTrailingComma) implies one
  6999          * element initialiser was parsed.
  7001          * A legacy array comprehension of the form:
  7003          *   [i * j for (i in o) for (j in p) if (i != j)]
  7005          * translates to roughly the following let expression:
  7007          *   let (array = new Array, i, j) {
  7008          *     for (i in o) let {
  7009          *       for (j in p)
  7010          *         if (i != j)
  7011          *           array.push(i * j)
  7012          *     }
  7013          *     array
  7014          *   }
  7016          * where array is a nameless block-local variable. The "roughly" means
  7017          * that an implementation may optimize away the array.push.  A legacy
  7018          * array comprehension opens exactly one block scope, no matter how many
  7019          * for heads it contains.
  7021          * Each let () {...} or for (let ...) ... compiles to:
  7023          *   JSOP_PUSHN <N>            // Push space for block-scoped locals.
  7024          *   (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope
  7025          *                             // chain.
  7026          *   ...
  7027          *   JSOP_DEBUGLEAVEBLOCK      // Invalidate any DebugScope proxies.
  7028          *   JSOP_POPBLOCKSCOPE?       // Pop off scope chain, if needed.
  7029          *   JSOP_POPN <N>             // Pop space for block-scoped locals.
  7031          * where <o> is a literal object representing the block scope,
  7032          * with <n> properties, naming each var declared in the block.
  7034          * Each var declaration in a let-block binds a name in <o> at compile
  7035          * time. A block-local var is accessed by the JSOP_GETLOCAL and
  7036          * JSOP_SETLOCAL ops. These ops have an immediate operand, the local
  7037          * slot's stack index from fp->spbase.
  7039          * The legacy array comprehension iteration step, array.push(i * j) in
  7040          * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, where
  7041          * <array> is the index of array's stack slot.
  7042          */
  7043         if (index == 0 && !spread && tokenStream.matchToken(TOK_FOR) && missingTrailingComma)
  7044             return legacyArrayComprehension(literal);
  7046         MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
  7048     handler.setEndPosition(literal, pos().end);
  7049     return literal;
  7052 static JSAtom*
  7053 DoubleToAtom(ExclusiveContext *cx, double value)
  7055     // This is safe because doubles can not be moved.
  7056     Value tmp = DoubleValue(value);
  7057     return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp));
  7060 template <typename ParseHandler>
  7061 typename ParseHandler::Node
  7062 Parser<ParseHandler>::objectLiteral()
  7064     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
  7066     /*
  7067      * A map from property names we've seen thus far to a mask of property
  7068      * assignment types.
  7069      */
  7070     AtomIndexMap seen;
  7072     enum AssignmentType {
  7073         GET     = 0x1,
  7074         SET     = 0x2,
  7075         VALUE   = 0x4 | GET | SET
  7076     };
  7078     Node literal = handler.newObjectLiteral(pos().begin);
  7079     if (!literal)
  7080         return null();
  7082     RootedAtom atom(context);
  7083     for (;;) {
  7084         TokenKind ltok = tokenStream.getToken(TokenStream::KeywordIsName);
  7085         if (ltok == TOK_RC)
  7086             break;
  7088         JSOp op = JSOP_INITPROP;
  7089         Node propname;
  7090         switch (ltok) {
  7091           case TOK_NUMBER:
  7092             atom = DoubleToAtom(context, tokenStream.currentToken().number());
  7093             if (!atom)
  7094                 return null();
  7095             propname = newNumber(tokenStream.currentToken());
  7096             break;
  7098           case TOK_NAME: {
  7099             atom = tokenStream.currentName();
  7100             if (atom == context->names().get) {
  7101                 op = JSOP_INITPROP_GETTER;
  7102             } else if (atom == context->names().set) {
  7103                 op = JSOP_INITPROP_SETTER;
  7104             } else {
  7105                 propname = handler.newIdentifier(atom, pos());
  7106                 if (!propname)
  7107                     return null();
  7108                 break;
  7111             // We have parsed |get| or |set|. Look for an accessor property
  7112             // name next.
  7113             TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
  7114             if (tt == TOK_NAME) {
  7115                 atom = tokenStream.currentName();
  7116                 propname = newName(atom->asPropertyName());
  7117                 if (!propname)
  7118                     return null();
  7119             } else if (tt == TOK_STRING) {
  7120                 atom = tokenStream.currentToken().atom();
  7122                 uint32_t index;
  7123                 if (atom->isIndex(&index)) {
  7124                     propname = handler.newNumber(index, NoDecimal, pos());
  7125                     if (!propname)
  7126                         return null();
  7127                     atom = DoubleToAtom(context, index);
  7128                     if (!atom)
  7129                         return null();
  7130                 } else {
  7131                     propname = stringLiteral();
  7132                     if (!propname)
  7133                         return null();
  7135             } else if (tt == TOK_NUMBER) {
  7136                 atom = DoubleToAtom(context, tokenStream.currentToken().number());
  7137                 if (!atom)
  7138                     return null();
  7139                 propname = newNumber(tokenStream.currentToken());
  7140                 if (!propname)
  7141                     return null();
  7142             } else {
  7143                 // Not an accessor property after all.
  7144                 tokenStream.ungetToken();
  7145                 propname = handler.newIdentifier(atom, pos());
  7146                 if (!propname)
  7147                     return null();
  7148                 op = JSOP_INITPROP;
  7149                 break;
  7152             JS_ASSERT(op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER);
  7153             break;
  7156           case TOK_STRING: {
  7157             atom = tokenStream.currentToken().atom();
  7158             uint32_t index;
  7159             if (atom->isIndex(&index)) {
  7160                 propname = handler.newNumber(index, NoDecimal, pos());
  7161                 if (!propname)
  7162                     return null();
  7163             } else {
  7164                 propname = stringLiteral();
  7165                 if (!propname)
  7166                     return null();
  7168             break;
  7171           default:
  7172             report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
  7173             return null();
  7176         if (op == JSOP_INITPROP) {
  7177             TokenKind tt = tokenStream.getToken();
  7178             Node propexpr;
  7179             if (tt == TOK_COLON) {
  7180                 propexpr = assignExpr();
  7181                 if (!propexpr)
  7182                     return null();
  7184                 if (foldConstants && !FoldConstants(context, &propexpr, this))
  7185                     return null();
  7187                 /*
  7188                  * Treat initializers which mutate __proto__ as non-constant,
  7189                  * so that we can later assume singleton objects delegate to
  7190                  * the default Object.prototype.
  7191                  */
  7192                 if (!handler.isConstant(propexpr) || atom == context->names().proto)
  7193                     handler.setListFlag(literal, PNX_NONCONST);
  7195                 if (!handler.addPropertyDefinition(literal, propname, propexpr))
  7196                     return null();
  7198             else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
  7199                 /*
  7200                  * Support, e.g., |var {x, y} = o| as destructuring shorthand
  7201                  * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
  7202                  */
  7203                 if (!abortIfSyntaxParser())
  7204                     return null();
  7205                 tokenStream.ungetToken();
  7206                 if (!tokenStream.checkForKeyword(atom->charsZ(), atom->length(), nullptr))
  7207                     return null();
  7208                 PropertyName *name = handler.isName(propname);
  7209                 JS_ASSERT(atom);
  7210                 propname = newName(name);
  7211                 if (!propname)
  7212                     return null();
  7213                 if (!handler.addShorthandPropertyDefinition(literal, propname))
  7214                     return null();
  7216             else {
  7217                 report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
  7218                 return null();
  7220         } else {
  7221             /* NB: Getter function in { get x(){} } is unnamed. */
  7222             Rooted<PropertyName*> funName(context, nullptr);
  7223             TokenStream::Position start(keepAtoms);
  7224             tokenStream.tell(&start);
  7225             Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
  7226                                         Expression, NotGenerator);
  7227             if (!accessor)
  7228                 return null();
  7229             if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op))
  7230                 return null();
  7233         /*
  7234          * Check for duplicate property names.  Duplicate data properties
  7235          * only conflict in strict mode.  Duplicate getter or duplicate
  7236          * setter halves always conflict.  A data property conflicts with
  7237          * any part of an accessor property.
  7238          */
  7239         AssignmentType assignType;
  7240         if (op == JSOP_INITPROP)
  7241             assignType = VALUE;
  7242         else if (op == JSOP_INITPROP_GETTER)
  7243             assignType = GET;
  7244         else if (op == JSOP_INITPROP_SETTER)
  7245             assignType = SET;
  7246         else
  7247             MOZ_ASSUME_UNREACHABLE("bad opcode in object initializer");
  7249         AtomIndexAddPtr p = seen.lookupForAdd(atom);
  7250         if (p) {
  7251             jsatomid index = p.value();
  7252             AssignmentType oldAssignType = AssignmentType(index);
  7253             if ((oldAssignType & assignType) &&
  7254                 (oldAssignType != VALUE || assignType != VALUE || pc->sc->needStrictChecks()))
  7256                 JSAutoByteString name;
  7257                 if (!AtomToPrintableString(context, atom, &name))
  7258                     return null();
  7260                 ParseReportKind reportKind =
  7261                     (oldAssignType == VALUE && assignType == VALUE && !pc->sc->needStrictChecks())
  7262                     ? ParseWarning
  7263                     : (pc->sc->needStrictChecks() ? ParseStrictError : ParseError);
  7264                 if (!report(reportKind, pc->sc->strict, null(),
  7265                             JSMSG_DUPLICATE_PROPERTY, name.ptr()))
  7267                     return null();
  7270             p.value() = assignType | oldAssignType;
  7271         } else {
  7272             if (!seen.add(p, atom, assignType))
  7273                 return null();
  7276         TokenKind tt = tokenStream.getToken();
  7277         if (tt == TOK_RC)
  7278             break;
  7279         if (tt != TOK_COMMA) {
  7280             report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
  7281             return null();
  7285     handler.setEndPosition(literal, pos().end);
  7286     return literal;
  7289 template <typename ParseHandler>
  7290 typename ParseHandler::Node
  7291 Parser<ParseHandler>::primaryExpr(TokenKind tt)
  7293     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
  7294     JS_CHECK_RECURSION(context, return null());
  7296     switch (tt) {
  7297       case TOK_FUNCTION:
  7298         return functionExpr();
  7300       case TOK_LB:
  7301         return arrayInitializer();
  7303       case TOK_LC:
  7304         return objectLiteral();
  7306       case TOK_LET:
  7307         return letBlock(LetExpresion);
  7309       case TOK_LP:
  7310         return parenExprOrGeneratorComprehension();
  7312       case TOK_STRING:
  7313         return stringLiteral();
  7315       case TOK_YIELD:
  7316         if (!checkYieldNameValidity())
  7317             return null();
  7318         // Fall through.
  7319       case TOK_NAME:
  7320         return identifierName();
  7322       case TOK_REGEXP:
  7323         return newRegExp();
  7325       case TOK_NUMBER:
  7326         return newNumber(tokenStream.currentToken());
  7328       case TOK_TRUE:
  7329         return handler.newBooleanLiteral(true, pos());
  7330       case TOK_FALSE:
  7331         return handler.newBooleanLiteral(false, pos());
  7332       case TOK_THIS:
  7333         return handler.newThisLiteral(pos());
  7334       case TOK_NULL:
  7335         return handler.newNullLiteral(pos());
  7337       case TOK_RP:
  7338         // Not valid expression syntax, but this is valid in an arrow function
  7339         // with no params: `() => body`.
  7340         if (tokenStream.peekToken() == TOK_ARROW) {
  7341             tokenStream.ungetToken();  // put back right paren
  7343             // Now just return something that will allow parsing to continue.
  7344             // It doesn't matter what; when we reach the =>, we will rewind and
  7345             // reparse the whole arrow function. See Parser::assignExpr.
  7346             return handler.newNullLiteral(pos());
  7348         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
  7349         return null();
  7351       case TOK_TRIPLEDOT:
  7352         // Not valid expression syntax, but this is valid in an arrow function
  7353         // with a rest param: `(a, b, ...rest) => body`.
  7354         if (tokenStream.matchToken(TOK_NAME) &&
  7355             tokenStream.matchToken(TOK_RP) &&
  7356             tokenStream.peekToken() == TOK_ARROW)
  7358             tokenStream.ungetToken();  // put back right paren
  7360             // Return an arbitrary expression node. See case TOK_RP above.
  7361             return handler.newNullLiteral(pos());
  7363         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
  7364         return null();
  7366       case TOK_ERROR:
  7367         /* The scanner or one of its subroutines reported the error. */
  7368         return null();
  7370       default:
  7371         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
  7372         return null();
  7376 template <typename ParseHandler>
  7377 typename ParseHandler::Node
  7378 Parser<ParseHandler>::parenExprOrGeneratorComprehension()
  7380     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
  7381     uint32_t begin = pos().begin;
  7382     uint32_t startYieldOffset = pc->lastYieldOffset;
  7384     if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
  7385         return generatorComprehension(begin);
  7387     /*
  7388      * Always accept the 'in' operator in a parenthesized expression,
  7389      * where it's unambiguous, even if we might be parsing the init of a
  7390      * for statement.
  7391      */
  7392     bool oldParsingForInit = pc->parsingForInit;
  7393     pc->parsingForInit = false;
  7394     Node pn = expr();
  7395     pc->parsingForInit = oldParsingForInit;
  7397     if (!pn)
  7398         return null();
  7400 #if JS_HAS_GENERATOR_EXPRS
  7401     if (tokenStream.matchToken(TOK_FOR)) {
  7402         if (pc->lastYieldOffset != startYieldOffset) {
  7403             reportWithOffset(ParseError, false, pc->lastYieldOffset,
  7404                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
  7405             return null();
  7407         if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
  7408             report(ParseError, false, null(),
  7409                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
  7410             return null();
  7412         pn = legacyGeneratorExpr(pn);
  7413         if (!pn)
  7414             return null();
  7415         handler.setBeginPosition(pn, begin);
  7416         if (tokenStream.getToken() != TOK_RP) {
  7417             report(ParseError, false, null(),
  7418                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
  7419             return null();
  7421         handler.setEndPosition(pn, pos().end);
  7422         handler.setInParens(pn);
  7423         return pn;
  7425 #endif /* JS_HAS_GENERATOR_EXPRS */
  7427     pn = handler.setInParens(pn);
  7429     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
  7431     return pn;
  7434 // Legacy generator comprehensions can sometimes appear without parentheses.
  7435 // For example:
  7436 //
  7437 //   foo(x for (x in bar))
  7438 //
  7439 // In this case the parens are part of the call, and not part of the generator
  7440 // comprehension.  This can happen in these contexts:
  7441 //
  7442 //   if (_)
  7443 //   while (_) {}
  7444 //   do {} while (_)
  7445 //   switch (_) {}
  7446 //   with (_) {}
  7447 //   foo(_) // must be first and only argument
  7448 //
  7449 // This is not the case for ES6 generator comprehensions; they must always be in
  7450 // parentheses.
  7452 template <typename ParseHandler>
  7453 typename ParseHandler::Node
  7454 Parser<ParseHandler>::exprInParens()
  7456     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
  7457     uint32_t begin = pos().begin;
  7458     uint32_t startYieldOffset = pc->lastYieldOffset;
  7460     /*
  7461      * Always accept the 'in' operator in a parenthesized expression,
  7462      * where it's unambiguous, even if we might be parsing the init of a
  7463      * for statement.
  7464      */
  7465     bool oldParsingForInit = pc->parsingForInit;
  7466     pc->parsingForInit = false;
  7467     Node pn = expr();
  7468     pc->parsingForInit = oldParsingForInit;
  7470     if (!pn)
  7471         return null();
  7473 #if JS_HAS_GENERATOR_EXPRS
  7474     if (tokenStream.matchToken(TOK_FOR)) {
  7475         if (pc->lastYieldOffset != startYieldOffset) {
  7476             reportWithOffset(ParseError, false, pc->lastYieldOffset,
  7477                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
  7478             return null();
  7480         if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
  7481             report(ParseError, false, null(),
  7482                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
  7483             return null();
  7485         pn = legacyGeneratorExpr(pn);
  7486         if (!pn)
  7487             return null();
  7488         handler.setBeginPosition(pn, begin);
  7490 #endif /* JS_HAS_GENERATOR_EXPRS */
  7492     return pn;
  7495 template class Parser<FullParseHandler>;
  7496 template class Parser<SyntaxParseHandler>;
  7498 } /* namespace frontend */
  7499 } /* namespace js */

mercurial