Bumping manifests a=b2g-bump
[gecko.git] / js / src / frontend / Parser.cpp
blobf4f5d3909f030f7db51479c6ffcc8ed0aad4b979
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.
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.
17 * This parser attempts no error recovery.
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 "asmjs/AsmJSValidate.h"
32 #include "frontend/BytecodeCompiler.h"
33 #include "frontend/FoldConstants.h"
34 #include "frontend/ParseMaps.h"
35 #include "frontend/TokenStream.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;
46 using mozilla::Maybe;
48 using JS::AutoGCRooter;
50 namespace js {
51 namespace frontend {
53 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
54 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
55 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
56 typedef Handle<NestedScopeObject*> HandleNestedScopeObject;
59 /* Read a token. Report an error and return null() if that token isn't of type tt. */
60 #define MUST_MATCH_TOKEN(tt, errno) \
61 JS_BEGIN_MACRO \
62 TokenKind token; \
63 if (!tokenStream.getToken(&token)) \
64 return null(); \
65 if (token != tt) { \
66 report(ParseError, false, null(), errno); \
67 return null(); \
68 } \
69 JS_END_MACRO
71 static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits;
73 template <typename ParseHandler>
74 bool
75 GenerateBlockId(TokenStream& ts, ParseContext<ParseHandler>* pc, uint32_t& blockid)
77 if (pc->blockidGen == BlockIdLimit) {
78 ts.reportError(JSMSG_NEED_DIET, "program");
79 return false;
81 MOZ_ASSERT(pc->blockidGen < BlockIdLimit);
82 blockid = pc->blockidGen++;
83 return true;
86 template bool
87 GenerateBlockId(TokenStream& ts, ParseContext<SyntaxParseHandler>* pc, uint32_t& blockid);
89 template bool
90 GenerateBlockId(TokenStream& ts, ParseContext<FullParseHandler>* pc, uint32_t& blockid);
92 template <typename ParseHandler>
93 static void
94 PushStatementPC(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, StmtType type)
96 stmt->blockid = pc->blockid();
97 PushStatement(pc, stmt, type);
100 template <>
101 bool
102 ParseContext<FullParseHandler>::checkLocalsOverflow(TokenStream& ts)
104 if (vars_.length() + bodyLevelLexicals_.length() >= LOCALNO_LIMIT) {
105 ts.reportError(JSMSG_TOO_MANY_LOCALS);
106 return false;
108 return true;
111 static void
112 MarkUsesAsHoistedLexical(ParseNode* pn)
114 MOZ_ASSERT(pn->isDefn());
116 Definition* dn = (Definition*)pn;
117 ParseNode** pnup = &dn->dn_uses;
118 ParseNode* pnu;
119 unsigned start = pn->pn_blockid;
121 // In ES6, lexical bindings cannot be accessed until initialized.
122 // Distinguish hoisted uses as a different JSOp for easier compilation.
123 while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
124 MOZ_ASSERT(pnu->isUsed());
125 pnu->pn_dflags |= PND_LEXICAL;
126 pnup = &pnu->pn_link;
130 // See comment on member function declaration.
131 template <>
132 bool
133 ParseContext<FullParseHandler>::define(TokenStream& ts,
134 HandlePropertyName name, ParseNode* pn, Definition::Kind kind)
136 MOZ_ASSERT(!pn->isUsed());
137 MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
139 Definition* prevDef = nullptr;
140 if (kind == Definition::LET || kind == Definition::CONST)
141 prevDef = decls_.lookupFirst(name);
142 else
143 MOZ_ASSERT(!decls_.lookupFirst(name));
145 if (!prevDef)
146 prevDef = lexdeps.lookupDefn<FullParseHandler>(name);
148 if (prevDef) {
149 ParseNode** pnup = &prevDef->dn_uses;
150 ParseNode* pnu;
151 unsigned start = (kind == Definition::LET || kind == Definition::CONST) ? pn->pn_blockid
152 : bodyid;
154 while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
155 MOZ_ASSERT(pnu->pn_blockid >= bodyid);
156 MOZ_ASSERT(pnu->isUsed());
157 pnu->pn_lexdef = (Definition*) pn;
158 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
159 pnup = &pnu->pn_link;
162 if (!pnu || pnu != prevDef->dn_uses) {
163 *pnup = pn->dn_uses;
164 pn->dn_uses = prevDef->dn_uses;
165 prevDef->dn_uses = pnu;
167 if (!pnu && prevDef->isPlaceholder())
168 lexdeps->remove(name);
171 pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED;
174 MOZ_ASSERT_IF(kind != Definition::LET && kind != Definition::CONST, !lexdeps->lookup(name));
175 pn->setDefn(true);
176 pn->pn_dflags &= ~PND_PLACEHOLDER;
177 if (kind == Definition::CONST)
178 pn->pn_dflags |= PND_CONST;
180 Definition* dn = (Definition*)pn;
181 switch (kind) {
182 case Definition::ARG:
183 MOZ_ASSERT(sc->isFunctionBox());
184 dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETARG : JSOP_GETARG);
185 dn->pn_blockid = bodyid;
186 dn->pn_dflags |= PND_BOUND;
187 if (!dn->pn_cookie.set(ts, staticLevel, args_.length()))
188 return false;
189 if (!args_.append(dn))
190 return false;
191 if (args_.length() >= ARGNO_LIMIT) {
192 ts.reportError(JSMSG_TOO_MANY_FUN_ARGS);
193 return false;
195 if (name == ts.names().empty)
196 break;
197 if (!decls_.addUnique(name, dn))
198 return false;
199 break;
201 case Definition::GLOBALCONST:
202 case Definition::VAR:
203 if (sc->isFunctionBox()) {
204 dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETLOCAL : JSOP_GETLOCAL);
205 dn->pn_blockid = bodyid;
206 dn->pn_dflags |= PND_BOUND;
207 if (!dn->pn_cookie.set(ts, staticLevel, vars_.length()))
208 return false;
209 if (!vars_.append(dn))
210 return false;
211 if (!checkLocalsOverflow(ts))
212 return false;
214 if (!decls_.addUnique(name, dn))
215 return false;
216 break;
218 case Definition::LET:
219 case Definition::CONST:
220 dn->setOp(JSOP_INITLEXICAL);
221 dn->pn_dflags |= (PND_LEXICAL | PND_BOUND);
222 MOZ_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */
223 if (atBodyLevel()) {
224 if (!bodyLevelLexicals_.append(dn))
225 return false;
226 if (!checkLocalsOverflow(ts))
227 return false;
230 // In ES6, lexical bindings cannot be accessed until initialized. If
231 // the definition has existing uses, they need to be marked so that we
232 // emit dead zone checks.
233 MarkUsesAsHoistedLexical(pn);
235 if (!decls_.addShadow(name, dn))
236 return false;
237 break;
239 default:
240 MOZ_CRASH("unexpected kind");
243 return true;
246 template <>
247 bool
248 ParseContext<SyntaxParseHandler>::checkLocalsOverflow(TokenStream& ts)
250 return true;
253 template <>
254 bool
255 ParseContext<SyntaxParseHandler>::define(TokenStream& ts, HandlePropertyName name, Node pn,
256 Definition::Kind kind)
258 MOZ_ASSERT(!decls_.lookupFirst(name));
260 if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
261 lexdeps->remove(name);
263 // Keep track of the number of arguments in args_, for fun->nargs.
264 if (kind == Definition::ARG) {
265 if (!args_.append((Definition*) nullptr))
266 return false;
267 if (args_.length() >= ARGNO_LIMIT) {
268 ts.reportError(JSMSG_TOO_MANY_FUN_ARGS);
269 return false;
273 return decls_.addUnique(name, kind);
276 template <typename ParseHandler>
277 void
278 ParseContext<ParseHandler>::prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl)
280 MOZ_ASSERT(decls_.lookupFirst(name) == prevDecl);
281 decls_.remove(name);
284 template <typename ParseHandler>
285 void
286 ParseContext<ParseHandler>::updateDecl(JSAtom* atom, Node pn)
288 Definition* oldDecl = decls_.lookupFirst(atom);
290 pn->setDefn(true);
291 Definition* newDecl = (Definition*)pn;
292 decls_.updateFirst(atom, newDecl);
294 if (!sc->isFunctionBox()) {
295 MOZ_ASSERT(newDecl->isFreeVar());
296 return;
299 MOZ_ASSERT(oldDecl->isBound());
300 MOZ_ASSERT(!oldDecl->pn_cookie.isFree());
301 newDecl->pn_cookie = oldDecl->pn_cookie;
302 newDecl->pn_dflags |= PND_BOUND;
303 if (IsArgOp(oldDecl->getOp())) {
304 newDecl->setOp(JSOP_GETARG);
305 MOZ_ASSERT(args_[oldDecl->pn_cookie.slot()] == oldDecl);
306 args_[oldDecl->pn_cookie.slot()] = newDecl;
307 } else {
308 MOZ_ASSERT(IsLocalOp(oldDecl->getOp()));
309 newDecl->setOp(JSOP_GETLOCAL);
310 MOZ_ASSERT(vars_[oldDecl->pn_cookie.slot()] == oldDecl);
311 vars_[oldDecl->pn_cookie.slot()] = newDecl;
315 template <typename ParseHandler>
316 void
317 ParseContext<ParseHandler>::popLetDecl(JSAtom* atom)
319 MOZ_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET ||
320 ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::CONST);
321 decls_.remove(atom);
324 template <typename ParseHandler>
325 static void
326 AppendPackedBindings(const ParseContext<ParseHandler>* pc, const DeclVector& vec, Binding* dst,
327 uint32_t* numUnaliased = nullptr)
329 for (size_t i = 0; i < vec.length(); ++i, ++dst) {
330 Definition* dn = vec[i];
331 PropertyName* name = dn->name();
333 Binding::Kind kind;
334 switch (dn->kind()) {
335 case Definition::LET:
336 // Treat body-level let declarations as var bindings by falling
337 // through. The fact that the binding is in fact a let declaration
338 // is reflected in the slot. All body-level lets go after the
339 // vars.
340 case Definition::VAR:
341 kind = Binding::VARIABLE;
342 break;
343 case Definition::CONST:
344 case Definition::GLOBALCONST:
345 kind = Binding::CONSTANT;
346 break;
347 case Definition::ARG:
348 kind = Binding::ARGUMENT;
349 break;
350 default:
351 MOZ_CRASH("unexpected dn->kind");
355 * Bindings::init does not check for duplicates so we must ensure that
356 * only one binding with a given name is marked aliased. pc->decls
357 * maintains the canonical definition for each name, so use that.
359 MOZ_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn);
360 bool aliased = dn->isClosed() ||
361 (pc->sc->allLocalsAliased() &&
362 pc->decls().lookupFirst(name) == dn);
364 *dst = Binding(name, kind, aliased);
365 if (!aliased && numUnaliased)
366 ++*numUnaliased;
370 template <typename ParseHandler>
371 bool
372 ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
373 LifoAlloc& alloc,
374 InternalHandle<Bindings*> bindings) const
376 MOZ_ASSERT(sc->isFunctionBox());
377 MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
378 MOZ_ASSERT(vars_.length() + bodyLevelLexicals_.length() < LOCALNO_LIMIT);
381 * Avoid pathological edge cases by explicitly limiting the total number of
382 * bindings to what will fit in a uint32_t.
384 if (UINT32_MAX - args_.length() <= vars_.length() + bodyLevelLexicals_.length())
385 return ts.reportError(JSMSG_TOO_MANY_LOCALS);
387 // Fix up the slots of body-level lets to come after the vars now that we
388 // know how many vars there are.
389 for (size_t i = 0; i < bodyLevelLexicals_.length(); i++) {
390 Definition* dn = bodyLevelLexicals_[i];
391 if (!dn->pn_cookie.set(ts, dn->pn_cookie.level(), vars_.length() + i))
392 return false;
395 uint32_t count = args_.length() + vars_.length() + bodyLevelLexicals_.length();
396 Binding* packedBindings = alloc.newArrayUninitialized<Binding>(count);
397 if (!packedBindings) {
398 js_ReportOutOfMemory(cx);
399 return false;
402 uint32_t numUnaliasedVars = 0;
403 uint32_t numUnaliasedBodyLevelLexicals = 0;
405 AppendPackedBindings(this, args_, packedBindings);
406 AppendPackedBindings(this, vars_, packedBindings + args_.length(), &numUnaliasedVars);
407 AppendPackedBindings(this, bodyLevelLexicals_,
408 packedBindings + args_.length() + vars_.length(), &numUnaliasedBodyLevelLexicals);
410 return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
411 bodyLevelLexicals_.length(), blockScopeDepth,
412 numUnaliasedVars, numUnaliasedBodyLevelLexicals,
413 packedBindings);
416 template <typename ParseHandler>
417 bool
418 Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
419 unsigned errorNumber, va_list args)
421 bool result = false;
422 switch (kind) {
423 case ParseError:
424 result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
425 break;
426 case ParseWarning:
427 result =
428 tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args);
429 break;
430 case ParseExtraWarning:
431 result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args);
432 break;
433 case ParseStrictError:
434 result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args);
435 break;
437 return result;
440 template <typename ParseHandler>
441 bool
442 Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
444 uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin;
446 va_list args;
447 va_start(args, errorNumber);
448 bool result = reportHelper(kind, strict, offset, errorNumber, args);
449 va_end(args);
450 return result;
453 template <typename ParseHandler>
454 bool
455 Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
457 va_list args;
458 va_start(args, errorNumber);
459 bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args);
460 va_end(args);
461 return result;
464 template <typename ParseHandler>
465 bool
466 Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset,
467 unsigned errorNumber, ...)
469 va_list args;
470 va_start(args, errorNumber);
471 bool result = reportHelper(kind, strict, offset, errorNumber, args);
472 va_end(args);
473 return result;
476 template <>
477 bool
478 Parser<FullParseHandler>::abortIfSyntaxParser()
480 handler.disableSyntaxParser();
481 return true;
484 template <>
485 bool
486 Parser<SyntaxParseHandler>::abortIfSyntaxParser()
488 abortedSyntaxParse = true;
489 return false;
492 template <typename ParseHandler>
493 Parser<ParseHandler>::Parser(ExclusiveContext* cx, LifoAlloc* alloc,
494 const ReadOnlyCompileOptions& options,
495 const char16_t* chars, size_t length, bool foldConstants,
496 Parser<SyntaxParseHandler>* syntaxParser,
497 LazyScript* lazyOuterFunction)
498 : AutoGCRooter(cx, PARSER),
499 context(cx),
500 alloc(*alloc),
501 tokenStream(cx, options, chars, length, thisForCtor()),
502 traceListHead(nullptr),
503 pc(nullptr),
504 sct(nullptr),
505 ss(nullptr),
506 keepAtoms(cx->perThreadData),
507 foldConstants(foldConstants),
508 #ifdef DEBUG
509 checkOptionsCalled(false),
510 #endif
511 abortedSyntaxParse(false),
512 isUnexpectedEOF_(false),
513 sawDeprecatedForEach(false),
514 sawDeprecatedDestructuringForIn(false),
515 sawDeprecatedLegacyGenerator(false),
516 sawDeprecatedExpressionClosure(false),
517 sawDeprecatedLetBlock(false),
518 sawDeprecatedLetExpression(false),
519 handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
522 AutoLockForExclusiveAccess lock(cx);
523 cx->perThreadData->addActiveCompilation();
526 // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
527 // which are not generated if functions are parsed lazily. Note that the
528 // standard "use strict" does not inhibit lazy parsing.
529 if (options.extraWarningsOption)
530 handler.disableSyntaxParser();
532 tempPoolMark = alloc->mark();
535 template<typename ParseHandler>
536 bool
537 Parser<ParseHandler>::checkOptions()
539 #ifdef DEBUG
540 checkOptionsCalled = true;
541 #endif
543 if (!tokenStream.checkOptions())
544 return false;
546 return true;
549 template <typename ParseHandler>
550 Parser<ParseHandler>::~Parser()
552 MOZ_ASSERT(checkOptionsCalled);
554 accumulateTelemetry();
556 alloc.release(tempPoolMark);
559 * The parser can allocate enormous amounts of memory for large functions.
560 * Eagerly free the memory now (which otherwise won't be freed until the
561 * next GC) to avoid unnecessary OOMs.
563 alloc.freeAllIfHugeAndUnused();
566 AutoLockForExclusiveAccess lock(context);
567 context->perThreadData->removeActiveCompilation();
571 template <typename ParseHandler>
572 ObjectBox*
573 Parser<ParseHandler>::newObjectBox(NativeObject* obj)
575 MOZ_ASSERT(obj && !IsPoisonedPtr(obj));
578 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
579 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
580 * arenas containing the entries must be alive until we are done with
581 * scanning, parsing and code generation for the whole script or top-level
582 * function.
585 ObjectBox* objbox = alloc.new_<ObjectBox>(obj, traceListHead);
586 if (!objbox) {
587 js_ReportOutOfMemory(context);
588 return nullptr;
591 traceListHead = objbox;
593 return objbox;
596 template <typename ParseHandler>
597 FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
598 ParseContext<ParseHandler>* outerpc, Directives directives,
599 bool extraWarnings, GeneratorKind generatorKind)
600 : ObjectBox(fun, traceListHead),
601 SharedContext(cx, directives, extraWarnings),
602 bindings(),
603 bufStart(0),
604 bufEnd(0),
605 length(0),
606 generatorKindBits_(GeneratorKindAsBits(generatorKind)),
607 inWith(false), // initialized below
608 inGenexpLambda(false),
609 hasDestructuringArgs(false),
610 useAsm(false),
611 insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()),
612 usesArguments(false),
613 usesApply(false),
614 usesThis(false),
615 funCxFlags()
617 // Functions created at parse time may be set singleton after parsing and
618 // baked into JIT code, so they must be allocated tenured. They are held by
619 // the JSScript so cannot be collected during a minor GC anyway.
620 MOZ_ASSERT(fun->isTenured());
622 if (!outerpc) {
623 inWith = false;
625 } else if (outerpc->parsingWith) {
626 // This covers cases that don't involve eval(). For example:
628 // with (o) { (function() { g(); })(); }
630 // In this case, |outerpc| corresponds to global code, and
631 // outerpc->parsingWith is true.
632 inWith = true;
634 } else if (outerpc->sc->isGlobalSharedContext()) {
635 // This covers the case where a function is nested within an eval()
636 // within a |with| statement.
638 // with (o) { eval("(function() { g(); })();"); }
640 // In this case, |outerpc| corresponds to the eval(),
641 // outerpc->parsingWith is false because the eval() breaks the
642 // ParseContext chain, and |parent| is nullptr (again because of the
643 // eval(), so we have to look at |outerpc|'s scopeChain.
645 JSObject* scope = outerpc->sc->asGlobalSharedContext()->scopeChain();
646 while (scope) {
647 if (scope->is<DynamicWithObject>())
648 inWith = true;
649 scope = scope->enclosingScope();
651 } else if (outerpc->sc->isFunctionBox()) {
652 // This is like the above case, but for more deeply nested functions.
653 // For example:
655 // with (o) { eval("(function() { (function() { g(); })(); })();"); } }
657 // In this case, the inner anonymous function needs to inherit the
658 // setting of |inWith| from the outer one.
659 FunctionBox* parent = outerpc->sc->asFunctionBox();
660 if (parent && parent->inWith)
661 inWith = true;
665 template <typename ParseHandler>
666 FunctionBox*
667 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, ParseContext<ParseHandler>* outerpc,
668 Directives inheritedDirectives, GeneratorKind generatorKind)
670 MOZ_ASSERT(fun && !IsPoisonedPtr(fun));
673 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
674 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
675 * arenas containing the entries must be alive until we are done with
676 * scanning, parsing and code generation for the whole script or top-level
677 * function.
679 FunctionBox* funbox =
680 alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
681 inheritedDirectives, options().extraWarningsOption,
682 generatorKind);
683 if (!funbox) {
684 js_ReportOutOfMemory(context);
685 return nullptr;
688 traceListHead = funbox;
689 if (fn)
690 handler.setFunctionBox(fn, funbox);
692 return funbox;
695 template <typename ParseHandler>
696 void
697 Parser<ParseHandler>::trace(JSTracer* trc)
699 traceListHead->trace(trc);
702 void
703 MarkParser(JSTracer* trc, AutoGCRooter* parser)
705 static_cast<Parser<FullParseHandler>*>(parser)->trace(trc);
709 * Parse a top-level JS script.
711 template <typename ParseHandler>
712 typename ParseHandler::Node
713 Parser<ParseHandler>::parse(JSObject* chain)
715 MOZ_ASSERT(checkOptionsCalled);
718 * Protect atoms from being collected by a GC activation, which might
719 * - nest on this thread due to out of memory (the so-called "last ditch"
720 * GC attempted within js_NewGCThing), or
721 * - run for any reason on another thread if this thread is suspended on
722 * an object lock before it finishes generating bytecode into a script
723 * protected from the GC by a root or a stack frame reference.
725 Directives directives(options().strictOption);
726 GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption);
727 ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
728 &globalsc, /* newDirectives = */ nullptr,
729 /* staticLevel = */ 0, /* bodyid = */ 0,
730 /* blockScopeDepth = */ 0);
731 if (!globalpc.init(tokenStream))
732 return null();
734 Node pn = statements();
735 if (pn) {
736 TokenKind tt;
737 if (!tokenStream.getToken(&tt))
738 return null();
739 if (tt != TOK_EOF) {
740 report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
741 "script", TokenKindToDesc(tt));
742 return null();
744 if (foldConstants) {
745 if (!FoldConstants(context, &pn, this))
746 return null();
749 return pn;
752 template <typename ParseHandler>
753 bool
754 Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
755 unsigned errnum, unsigned anonerrnum)
757 JSAutoByteString name;
758 JSAtom* atom = pc->sc->asFunctionBox()->function()->atom();
759 if (atom) {
760 if (!AtomToPrintableString(context, atom, &name))
761 return false;
762 } else {
763 errnum = anonerrnum;
765 return report(kind, pc->sc->strict, pn, errnum, name.ptr());
769 * Check that assigning to lhs is permitted. Assigning to 'eval' or
770 * 'arguments' is banned in strict mode.
772 template <typename ParseHandler>
773 bool
774 Parser<ParseHandler>::checkStrictAssignment(Node lhs)
776 if (!pc->sc->needStrictChecks())
777 return true;
779 JSAtom* atom = handler.isName(lhs);
780 if (!atom)
781 return true;
783 if (atom == context->names().eval || atom == context->names().arguments) {
784 JSAutoByteString name;
785 if (!AtomToPrintableString(context, atom, &name))
786 return false;
788 if (!report(ParseStrictError, pc->sc->strict, lhs, JSMSG_BAD_STRICT_ASSIGN, name.ptr()))
789 return false;
791 return true;
795 * Check that it is permitted to introduce a binding for atom. Strict mode
796 * forbids introducing new definitions for 'eval', 'arguments', or for any
797 * strict mode reserved keyword. Use pn for reporting error locations, or use
798 * pc's token stream if pn is nullptr.
800 template <typename ParseHandler>
801 bool
802 Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
804 if (!pc->sc->needStrictChecks())
805 return true;
807 if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
808 JSAutoByteString bytes;
809 if (!AtomToPrintableString(context, name, &bytes))
810 return false;
811 return report(ParseStrictError, pc->sc->strict, pn,
812 JSMSG_BAD_BINDING, bytes.ptr());
815 return true;
818 template <>
819 ParseNode*
820 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector& formals,
821 GeneratorKind generatorKind,
822 Directives inheritedDirectives,
823 Directives* newDirectives)
825 MOZ_ASSERT(checkOptionsCalled);
827 Node fn = handler.newFunctionDefinition();
828 if (!fn)
829 return null();
831 ParseNode* argsbody = ListNode::create(PNK_ARGSBODY, &handler);
832 if (!argsbody)
833 return null();
834 argsbody->setOp(JSOP_NOP);
835 argsbody->makeEmpty();
836 fn->pn_body = argsbody;
838 FunctionBox* funbox = newFunctionBox(fn, fun, /* outerpc = */ nullptr, inheritedDirectives,
839 generatorKind);
840 if (!funbox)
841 return null();
842 funbox->length = fun->nargs() - fun->hasRest();
843 handler.setFunctionBox(fn, funbox);
845 ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives,
846 /* staticLevel = */ 0, /* bodyid = */ 0,
847 /* blockScopeDepth = */ 0);
848 if (!funpc.init(tokenStream))
849 return null();
851 for (unsigned i = 0; i < formals.length(); i++) {
852 if (!defineArg(fn, formals[i]))
853 return null();
856 ParseNode* pn = functionBody(Statement, StatementListBody);
857 if (!pn)
858 return null();
860 TokenKind tt;
861 if (!tokenStream.getToken(&tt))
862 return null();
863 if (tt != TOK_EOF) {
864 report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
865 "function body", TokenKindToDesc(tt));
866 return null();
869 if (!FoldConstants(context, &pn, this))
870 return null();
872 InternalHandle<Bindings*> funboxBindings =
873 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
874 if (!funpc.generateFunctionBindings(context, tokenStream, alloc, funboxBindings))
875 return null();
877 MOZ_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
878 fn->pn_body->append(pn);
879 fn->pn_body->pn_pos = pn->pn_pos;
880 return fn;
883 template <>
884 bool
885 Parser<FullParseHandler>::checkFunctionArguments()
888 * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope
889 * operation which means it aliases any bindings with the same name.
891 if (FuncStmtSet* set = pc->funcStmts) {
892 for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) {
893 PropertyName* name = r.front()->asPropertyName();
894 if (Definition* dn = pc->decls().lookupFirst(name))
895 dn->pn_dflags |= PND_CLOSED;
899 /* Time to implement the odd semantics of 'arguments'. */
900 HandlePropertyName arguments = context->names().arguments;
903 * As explained by the ContextFlags::funArgumentsHasLocalBinding comment,
904 * create a declaration for 'arguments' if there are any unbound uses in
905 * the function body.
907 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
908 if (r.front().key() == arguments) {
909 Definition* dn = r.front().value().get<FullParseHandler>();
910 pc->lexdeps->remove(arguments);
911 dn->pn_dflags |= PND_IMPLICITARGUMENTS;
912 if (!pc->define(tokenStream, arguments, dn, Definition::VAR))
913 return false;
914 pc->sc->asFunctionBox()->usesArguments = true;
915 break;
920 * Report error if both rest parameters and 'arguments' are used. Do this
921 * check before adding artificial 'arguments' below.
923 Definition* maybeArgDef = pc->decls().lookupFirst(arguments);
924 bool argumentsHasBinding = !!maybeArgDef;
925 // ES6 9.2.13.17 says that a lexical binding of 'arguments' shadows the
926 // arguments object.
927 bool argumentsHasLocalBinding = maybeArgDef && (maybeArgDef->kind() != Definition::ARG &&
928 maybeArgDef->kind() != Definition::LET &&
929 maybeArgDef->kind() != Definition::CONST);
930 bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
931 if (hasRest && argumentsHasLocalBinding) {
932 report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST);
933 return false;
937 * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
938 * forces an 'arguments' binding. The exception is that functions with rest
939 * parameters are free from 'arguments'.
941 if (!argumentsHasBinding && pc->sc->bindingsAccessedDynamically() && !hasRest) {
942 ParseNode* pn = newName(arguments);
943 if (!pn)
944 return false;
945 if (!pc->define(tokenStream, arguments, pn, Definition::VAR))
946 return false;
947 argumentsHasBinding = true;
948 argumentsHasLocalBinding = true;
952 * Now that all possible 'arguments' bindings have been added, note whether
953 * 'arguments' has a local binding and whether it unconditionally needs an
954 * arguments object. (Also see the flags' comments in ContextFlags.)
956 if (argumentsHasLocalBinding) {
957 FunctionBox* funbox = pc->sc->asFunctionBox();
958 funbox->setArgumentsHasLocalBinding();
961 * If a script has both explicit mentions of 'arguments' and dynamic
962 * name lookups which could access the arguments, an arguments object
963 * must be created eagerly. The SSA analysis used for lazy arguments
964 * cannot cope with dynamic name accesses, so any 'arguments' accessed
965 * via a NAME opcode must force construction of the arguments object.
967 if (pc->sc->bindingsAccessedDynamically() && maybeArgDef)
968 funbox->setDefinitelyNeedsArgsObj();
971 * If a script contains the debugger statement either directly or
972 * within an inner function, the arguments object must be created
973 * eagerly. The debugger can walk the scope chain and observe any
974 * values along it.
976 if (pc->sc->hasDebuggerStatement())
977 funbox->setDefinitelyNeedsArgsObj();
980 * Check whether any parameters have been assigned within this
981 * function. In strict mode parameters do not alias arguments[i], and
982 * to make the arguments object reflect initial parameter values prior
983 * to any mutation we create it eagerly whenever parameters are (or
984 * might, in the case of calls to eval) be assigned.
986 if (pc->sc->needStrictChecks()) {
987 for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) {
988 DefinitionList& dlist = r.front().value();
989 for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) {
990 Definition* dn = dr.front<FullParseHandler>();
991 if (dn->kind() == Definition::ARG && dn->isAssigned())
992 funbox->setDefinitelyNeedsArgsObj();
995 /* Watch for mutation of arguments through e.g. eval(). */
996 if (pc->sc->bindingsAccessedDynamically())
997 funbox->setDefinitelyNeedsArgsObj();
1001 return true;
1004 template <>
1005 bool
1006 Parser<SyntaxParseHandler>::checkFunctionArguments()
1008 bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
1010 if (pc->lexdeps->lookup(context->names().arguments)) {
1011 pc->sc->asFunctionBox()->usesArguments = true;
1012 if (hasRest) {
1013 report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
1014 return false;
1016 } else if (hasRest) {
1017 DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments);
1018 if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) {
1019 report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
1020 return false;
1024 return true;
1027 template <typename ParseHandler>
1028 typename ParseHandler::Node
1029 Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType type)
1031 MOZ_ASSERT(pc->sc->isFunctionBox());
1032 MOZ_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
1034 #ifdef DEBUG
1035 uint32_t startYieldOffset = pc->lastYieldOffset;
1036 #endif
1038 Node pn;
1039 if (type == StatementListBody) {
1040 pn = statements();
1041 if (!pn)
1042 return null();
1043 } else {
1044 MOZ_ASSERT(type == ExpressionBody);
1045 MOZ_ASSERT(JS_HAS_EXPR_CLOSURES);
1047 Node kid = assignExpr();
1048 if (!kid)
1049 return null();
1051 pn = handler.newReturnStatement(kid, null(), handler.getPosition(kid));
1052 if (!pn)
1053 return null();
1056 switch (pc->generatorKind()) {
1057 case NotGenerator:
1058 MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
1059 break;
1061 case LegacyGenerator:
1062 // FIXME: Catch these errors eagerly, in yieldExpression().
1063 MOZ_ASSERT(pc->lastYieldOffset != startYieldOffset);
1064 if (kind == Arrow) {
1065 reportWithOffset(ParseError, false, pc->lastYieldOffset,
1066 JSMSG_YIELD_IN_ARROW, js_yield_str);
1067 return null();
1069 if (type == ExpressionBody) {
1070 reportBadReturn(pn, ParseError,
1071 JSMSG_BAD_GENERATOR_RETURN,
1072 JSMSG_BAD_ANON_GENERATOR_RETURN);
1073 return null();
1075 break;
1077 case StarGenerator:
1078 MOZ_ASSERT(kind != Arrow);
1079 MOZ_ASSERT(type == StatementListBody);
1080 break;
1083 if (pc->isGenerator()) {
1084 MOZ_ASSERT(type == StatementListBody);
1085 Node generator = newName(context->names().dotGenerator);
1086 if (!generator)
1087 return null();
1088 if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR))
1089 return null();
1091 if (pc->isStarGenerator()) {
1092 Node genrval = newName(context->names().dotGenRVal);
1093 if (!genrval)
1094 return null();
1095 if (!pc->define(tokenStream, context->names().dotGenRVal, genrval, Definition::VAR))
1096 return null();
1099 generator = newName(context->names().dotGenerator);
1100 if (!generator)
1101 return null();
1102 if (!noteNameUse(context->names().dotGenerator, generator))
1103 return null();
1104 if (!handler.prependInitialYield(pn, generator))
1105 return null();
1108 /* Define the 'arguments' binding if necessary. */
1109 if (!checkFunctionArguments())
1110 return null();
1112 return pn;
1115 /* See comment for use in Parser::functionDef. */
1116 template <>
1117 bool
1118 Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, JSAtom* atom)
1120 /* Turn pn into a definition. */
1121 pc->updateDecl(atom, pn);
1123 /* Change all uses of dn to be uses of pn. */
1124 for (ParseNode* pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1125 MOZ_ASSERT(pnu->isUsed());
1126 MOZ_ASSERT(!pnu->isDefn());
1127 pnu->pn_lexdef = (Definition*) pn;
1128 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1130 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1131 pn->dn_uses = dn;
1134 * A PNK_FUNCTION node must be a definition, so convert shadowed function
1135 * statements into nops. This is valid since all body-level function
1136 * statement initialization happens at the beginning of the function
1137 * (thus, only the last statement's effect is visible). E.g., in
1139 * function outer() {
1140 * function g() { return 1 }
1141 * assertEq(g(), 2);
1142 * function g() { return 2 }
1143 * assertEq(g(), 2);
1146 * both asserts are valid.
1148 if (dn->getKind() == PNK_FUNCTION) {
1149 MOZ_ASSERT(dn->functionIsHoisted());
1150 pn->dn_uses = dn->pn_link;
1151 handler.prepareNodeForMutation(dn);
1152 dn->setKind(PNK_NOP);
1153 dn->setArity(PN_NULLARY);
1154 return true;
1158 * If dn is in [var, const, let] and has an initializer, then we
1159 * must rewrite it to be an assignment node, whose freshly allocated
1160 * left-hand side becomes a use of pn.
1162 if (dn->canHaveInitializer()) {
1163 if (ParseNode* rhs = dn->expr()) {
1164 ParseNode* lhs = handler.makeAssignment(dn, rhs);
1165 if (!lhs)
1166 return false;
1167 pn->dn_uses = lhs;
1168 dn->pn_link = nullptr;
1169 dn = (Definition*) lhs;
1173 /* Turn dn into a use of pn. */
1174 MOZ_ASSERT(dn->isKind(PNK_NAME));
1175 MOZ_ASSERT(dn->isArity(PN_NAME));
1176 MOZ_ASSERT(dn->pn_atom == atom);
1177 dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_GETNAME);
1178 dn->setDefn(false);
1179 dn->setUsed(true);
1180 dn->pn_lexdef = (Definition*) pn;
1181 dn->pn_cookie.makeFree();
1182 dn->pn_dflags &= ~PND_BOUND;
1183 return true;
1187 * Parameter block types for the several Binder functions. We use a common
1188 * helper function signature in order to share code among destructuring and
1189 * simple variable declaration parsers. In the destructuring case, the binder
1190 * function is called indirectly from the variable declaration parser by way
1191 * of checkDestructuring and its friends.
1194 template <typename ParseHandler>
1195 struct BindData
1197 explicit BindData(ExclusiveContext* cx) : let(cx) {}
1199 typedef bool
1200 (*Binder)(BindData* data, HandlePropertyName name, Parser<ParseHandler>* parser);
1202 /* name node for definition processing and error source coordinates */
1203 typename ParseHandler::Node pn;
1205 JSOp op; /* prolog bytecode or nop */
1206 Binder binder; /* binder, discriminates u */
1207 bool isConst; /* const binding? */
1209 struct LetData {
1210 explicit LetData(ExclusiveContext* cx) : blockObj(cx) {}
1211 VarContext varContext;
1212 RootedStaticBlockObject blockObj;
1213 unsigned overflow;
1214 } let;
1216 void initLexical(VarContext varContext, StaticBlockObject* blockObj, unsigned overflow,
1217 bool isConst = false) {
1218 this->pn = ParseHandler::null();
1219 this->op = JSOP_INITLEXICAL;
1220 this->isConst = isConst;
1221 this->binder = Parser<ParseHandler>::bindLexical;
1222 this->let.varContext = varContext;
1223 this->let.blockObj = blockObj;
1224 this->let.overflow = overflow;
1227 void initVarOrGlobalConst(JSOp op) {
1228 this->op = op;
1229 this->isConst = op == JSOP_DEFCONST;
1230 this->binder = Parser<ParseHandler>::bindVarOrGlobalConst;
1234 template <typename ParseHandler>
1235 JSFunction*
1236 Parser<ParseHandler>::newFunction(GenericParseContext* pc, HandleAtom atom,
1237 FunctionSyntaxKind kind, JSObject* proto)
1239 MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
1242 * Find the global compilation context in order to pre-set the newborn
1243 * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the
1244 * global context is a compile-and-go one, we leave the pre-set parent
1245 * intact; otherwise we clear parent and proto.
1247 while (pc->parent)
1248 pc = pc->parent;
1250 RootedFunction fun(context);
1251 JSFunction::Flags flags = (kind == Expression)
1252 ? JSFunction::INTERPRETED_LAMBDA
1253 : (kind == Arrow)
1254 ? JSFunction::INTERPRETED_LAMBDA_ARROW
1255 : JSFunction::INTERPRETED;
1256 gc::AllocKind allocKind = JSFunction::FinalizeKind;
1257 if (kind == Arrow)
1258 allocKind = JSFunction::ExtendedFinalizeKind;
1259 fun = NewFunctionWithProto(context, NullPtr(), nullptr, 0, flags, NullPtr(), atom, proto,
1260 allocKind, MaybeSingletonObject);
1261 if (!fun)
1262 return nullptr;
1263 if (options().selfHostingMode)
1264 fun->setIsSelfHostedBuiltin();
1265 return fun;
1268 static bool
1269 MatchOrInsertSemicolon(TokenStream& ts)
1271 TokenKind tt;
1272 if (!ts.peekTokenSameLine(&tt, TokenStream::Operand))
1273 return false;
1274 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1275 /* Advance the scanner for proper error location reporting. */
1276 ts.consumeKnownToken(tt);
1277 ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
1278 return false;
1280 bool ignored;
1281 return ts.matchToken(&ignored, TOK_SEMI);
1284 template <typename ParseHandler>
1285 typename ParseHandler::DefinitionNode
1286 Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler>* pc, JSAtom* atom)
1288 AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
1289 if (p)
1290 return p.value().get<ParseHandler>();
1292 DefinitionNode dn = handler.newPlaceholder(atom, pc->blockid(), pos());
1293 if (!dn)
1294 return ParseHandler::nullDefinition();
1295 DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
1296 if (!pc->lexdeps->add(p, atom, def))
1297 return ParseHandler::nullDefinition();
1298 return dn;
1301 static bool
1302 ConvertDefinitionToNamedLambdaUse(TokenStream& ts, ParseContext<FullParseHandler>* pc,
1303 FunctionBox* funbox, Definition* dn)
1305 dn->setOp(JSOP_CALLEE);
1306 if (!dn->pn_cookie.set(ts, pc->staticLevel, 0))
1307 return false;
1308 dn->pn_dflags |= PND_BOUND;
1309 MOZ_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
1312 * Since 'dn' is a placeholder, it has not been defined in the
1313 * ParseContext and hence we must manually flag a closed-over
1314 * callee name as needing a dynamic scope (this is done for all
1315 * definitions in the ParseContext by generateFunctionBindings).
1317 * If 'dn' has been assigned to, then we also flag the function
1318 * scope has needing a dynamic scope so that dynamic scope
1319 * setter can either ignore the set (in non-strict mode) or
1320 * produce an error (in strict mode).
1322 if (dn->isClosed() || dn->isAssigned())
1323 funbox->setNeedsDeclEnvObject();
1324 return true;
1327 static bool
1328 IsNonDominatingInScopedSwitch(ParseContext<FullParseHandler>* pc, HandleAtom name,
1329 Definition* dn)
1331 MOZ_ASSERT(dn->isLexical());
1332 StmtInfoPC* stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC*)nullptr);
1333 if (stmt && stmt->type == STMT_SWITCH)
1334 return dn->pn_cookie.slot() < stmt->firstDominatingLexicalInCase;
1335 return false;
1338 static void
1339 AssociateUsesWithOuterDefinition(ParseNode* pnu, Definition* dn, Definition* outer_dn,
1340 bool markUsesAsLexical)
1342 uint32_t dflags = markUsesAsLexical ? PND_LEXICAL : 0;
1343 while (true) {
1344 pnu->pn_lexdef = outer_dn;
1345 pnu->pn_dflags |= dflags;
1346 if (!pnu->pn_link)
1347 break;
1348 pnu = pnu->pn_link;
1350 pnu->pn_link = outer_dn->dn_uses;
1351 outer_dn->dn_uses = dn->dn_uses;
1352 dn->dn_uses = nullptr;
1356 * Beware: this function is called for functions nested in other functions or
1357 * global scripts but not for functions compiled through the Function
1358 * constructor or JSAPI. To always execute code when a function has finished
1359 * parsing, use Parser::functionBody.
1361 template <>
1362 bool
1363 Parser<FullParseHandler>::leaveFunction(ParseNode* fn, ParseContext<FullParseHandler>* outerpc,
1364 FunctionSyntaxKind kind)
1366 outerpc->blockidGen = pc->blockidGen;
1368 bool bodyLevel = outerpc->atBodyLevel();
1369 FunctionBox* funbox = fn->pn_funbox;
1370 MOZ_ASSERT(funbox == pc->sc->asFunctionBox());
1372 /* Propagate unresolved lexical names up to outerpc->lexdeps. */
1373 if (pc->lexdeps->count()) {
1374 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
1375 JSAtom* atom = r.front().key();
1376 Definition* dn = r.front().value().get<FullParseHandler>();
1377 MOZ_ASSERT(dn->isPlaceholder());
1379 if (atom == funbox->function()->name() && kind == Expression) {
1380 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
1381 return false;
1382 continue;
1385 Definition* outer_dn = outerpc->decls().lookupFirst(atom);
1388 * Make sure to deoptimize lexical dependencies that are polluted
1389 * by eval and function statements (which both flag the function as
1390 * having an extensible scope) or any enclosing 'with'.
1392 if (funbox->hasExtensibleScope() || outerpc->parsingWith)
1393 handler.deoptimizeUsesWithin(dn, fn->pn_pos);
1395 if (!outer_dn) {
1397 * Create a new placeholder for our outer lexdep. We could
1398 * simply re-use the inner placeholder, but that introduces
1399 * subtleties in the case where we find a later definition
1400 * that captures an existing lexdep. For example:
1402 * function f() { function g() { x; } let x; }
1404 * Here, g's TOK_UPVARS node lists the placeholder for x,
1405 * which must be captured by the 'let' declaration later,
1406 * since 'let's are hoisted. Taking g's placeholder as our
1407 * own would work fine. But consider:
1409 * function f() { x; { function g() { x; } let x; } }
1411 * Here, the 'let' must not capture all the uses of f's
1412 * lexdep entry for x, but it must capture the x node
1413 * referred to from g's TOK_UPVARS node. Always turning
1414 * inherited lexdeps into uses of a new outer definition
1415 * allows us to handle both these cases in a natural way.
1417 outer_dn = getOrCreateLexicalDependency(outerpc, atom);
1418 if (!outer_dn)
1419 return false;
1423 * Insert dn's uses list at the front of outer_dn's list.
1425 * Without loss of generality or correctness, we allow a dn to
1426 * be in inner and outer lexdeps, since the purpose of lexdeps
1427 * is one-pass coordination of name use and definition across
1428 * functions, and if different dn's are used we'll merge lists
1429 * when leaving the inner function.
1431 * The dn == outer_dn case arises with generator expressions
1432 * (see LegacyCompExprTransplanter::transplant, the PN_CODE/PN_NAME
1433 * case), and nowhere else, currently.
1435 if (dn != outer_dn) {
1436 if (ParseNode* pnu = dn->dn_uses) {
1437 // In ES6, lexical bindings cannot be accessed until
1438 // initialized. If we are parsing a body-level function,
1439 // it is hoisted to the top, so we conservatively mark all
1440 // uses linked to an outer lexical binding as needing TDZ
1441 // checks. e.g.,
1443 // function outer() {
1444 // inner2();
1445 // function inner() { use(x); }
1446 // function inner2() { inner(); }
1447 // let x;
1448 // }
1450 // The use of 'x' inside 'inner' needs to be marked.
1452 // Note that to not be fully conservative requires a call
1453 // graph analysis of all body-level functions to compute
1454 // the transitive closure of which hoisted body level use
1455 // of which function forces TDZ checks on which uses. This
1456 // is unreasonably difficult to do in a single pass parser
1457 // like ours.
1459 // Similarly, if we are closing over a lexical binding
1460 // from another case in a switch, those uses also need to
1461 // be marked as needing dead zone checks.
1462 RootedAtom name(context, atom);
1463 bool markUsesAsLexical = outer_dn->isLexical() &&
1464 (bodyLevel ||
1465 IsNonDominatingInScopedSwitch(outerpc, name, outer_dn));
1466 AssociateUsesWithOuterDefinition(pnu, dn, outer_dn, markUsesAsLexical);
1469 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
1472 /* Mark the outer dn as escaping. */
1473 outer_dn->pn_dflags |= PND_CLOSED;
1477 InternalHandle<Bindings*> bindings =
1478 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
1479 return pc->generateFunctionBindings(context, tokenStream, alloc, bindings);
1482 template <>
1483 bool
1484 Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler>* outerpc,
1485 FunctionSyntaxKind kind)
1487 outerpc->blockidGen = pc->blockidGen;
1489 FunctionBox* funbox = pc->sc->asFunctionBox();
1490 return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
1494 * defineArg is called for both the arguments of a regular function definition
1495 * and the arguments specified by the Function constructor.
1497 * The 'disallowDuplicateArgs' bool indicates whether the use of another
1498 * feature (destructuring or default arguments) disables duplicate arguments.
1499 * (ECMA-262 requires us to support duplicate parameter names, but, for newer
1500 * features, we consider the code to have "opted in" to higher standards and
1501 * forbid duplicates.)
1503 * If 'duplicatedArg' is non-null, then DefineArg assigns to it any previous
1504 * argument with the same name. The caller may use this to report an error when
1505 * one of the abovementioned features occurs after a duplicate.
1507 template <typename ParseHandler>
1508 bool
1509 Parser<ParseHandler>::defineArg(Node funcpn, HandlePropertyName name,
1510 bool disallowDuplicateArgs, Node* duplicatedArg)
1512 SharedContext* sc = pc->sc;
1514 /* Handle duplicate argument names. */
1515 if (DefinitionNode prevDecl = pc->decls().lookupFirst(name)) {
1516 Node pn = handler.getDefinitionNode(prevDecl);
1519 * Strict-mode disallows duplicate args. We may not know whether we are
1520 * in strict mode or not (since the function body hasn't been parsed).
1521 * In such cases, report will queue up the potential error and return
1522 * 'true'.
1524 if (sc->needStrictChecks()) {
1525 JSAutoByteString bytes;
1526 if (!AtomToPrintableString(context, name, &bytes))
1527 return false;
1528 if (!report(ParseStrictError, pc->sc->strict, pn,
1529 JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
1531 return false;
1535 if (disallowDuplicateArgs) {
1536 report(ParseError, false, pn, JSMSG_BAD_DUP_ARGS);
1537 return false;
1540 if (duplicatedArg)
1541 *duplicatedArg = pn;
1543 /* ParseContext::define assumes and asserts prevDecl is not in decls. */
1544 MOZ_ASSERT(handler.getDefinitionKind(prevDecl) == Definition::ARG);
1545 pc->prepareToAddDuplicateArg(name, prevDecl);
1548 Node argpn = newName(name);
1549 if (!argpn)
1550 return false;
1552 if (!checkStrictBinding(name, argpn))
1553 return false;
1555 handler.addFunctionArgument(funcpn, argpn);
1556 return pc->define(tokenStream, name, argpn, Definition::ARG);
1559 template <typename ParseHandler>
1560 /* static */ bool
1561 Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler>* data,
1562 HandlePropertyName name, Parser<ParseHandler>* parser)
1564 ParseContext<ParseHandler>* pc = parser->pc;
1565 MOZ_ASSERT(pc->sc->isFunctionBox());
1567 if (pc->decls().lookupFirst(name)) {
1568 parser->report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
1569 return false;
1572 if (!parser->checkStrictBinding(name, data->pn))
1573 return false;
1575 return pc->define(parser->tokenStream, name, data->pn, Definition::VAR);
1578 template <typename ParseHandler>
1579 bool
1580 Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node* listp, Node funcpn,
1581 bool* hasRest)
1583 FunctionBox* funbox = pc->sc->asFunctionBox();
1585 *hasRest = false;
1587 bool parenFreeArrow = false;
1588 if (kind == Arrow) {
1589 TokenKind tt;
1590 if (!tokenStream.peekToken(&tt))
1591 return false;
1592 if (tt == TOK_NAME)
1593 parenFreeArrow = true;
1595 if (!parenFreeArrow) {
1596 TokenKind tt;
1597 if (!tokenStream.getToken(&tt))
1598 return false;
1599 if (tt != TOK_LP) {
1600 report(ParseError, false, null(),
1601 kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
1602 return false;
1605 // Record the start of function source (for FunctionToString). If we
1606 // are parenFreeArrow, we will set this below, after consuming the NAME.
1607 funbox->setStart(tokenStream);
1610 Node argsbody = handler.newList(PNK_ARGSBODY);
1611 if (!argsbody)
1612 return false;
1613 handler.setFunctionBody(funcpn, argsbody);
1615 bool hasArguments = false;
1616 if (parenFreeArrow) {
1617 hasArguments = true;
1618 } else {
1619 bool matched;
1620 if (!tokenStream.matchToken(&matched, TOK_RP))
1621 return false;
1622 if (!matched)
1623 hasArguments = true;
1625 if (hasArguments) {
1626 bool hasDefaults = false;
1627 Node duplicatedArg = null();
1628 Node list = null();
1630 while (true) {
1631 if (*hasRest) {
1632 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
1633 return false;
1636 TokenKind tt;
1637 if (!tokenStream.getToken(&tt))
1638 return false;
1639 MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME);
1640 switch (tt) {
1641 case TOK_LB:
1642 case TOK_LC:
1644 /* See comment below in the TOK_NAME case. */
1645 if (duplicatedArg) {
1646 report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
1647 return false;
1650 if (hasDefaults) {
1651 report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
1652 return false;
1655 funbox->hasDestructuringArgs = true;
1658 * A destructuring formal parameter turns into one or more
1659 * local variables initialized from properties of a single
1660 * anonymous positional parameter, so here we must tweak our
1661 * binder and its data.
1663 BindData<ParseHandler> data(context);
1664 data.pn = ParseHandler::null();
1665 data.op = JSOP_DEFVAR;
1666 data.binder = bindDestructuringArg;
1667 Node lhs = destructuringExprWithoutYield(&data, tt, JSMSG_YIELD_IN_DEFAULT);
1668 if (!lhs)
1669 return false;
1672 * Synthesize a destructuring assignment from the single
1673 * anonymous positional parameter into the destructuring
1674 * left-hand-side expression and accumulate it in list.
1676 HandlePropertyName name = context->names().empty;
1677 Node rhs = newName(name);
1678 if (!rhs)
1679 return false;
1681 if (!pc->define(tokenStream, name, rhs, Definition::ARG))
1682 return false;
1684 Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs);
1685 if (!item)
1686 return false;
1687 if (list) {
1688 handler.addList(list, item);
1689 } else {
1690 list = handler.newList(PNK_VAR, item);
1691 if (!list)
1692 return false;
1693 *listp = list;
1695 break;
1698 case TOK_YIELD:
1699 if (!checkYieldNameValidity())
1700 return false;
1701 goto TOK_NAME;
1703 case TOK_TRIPLEDOT:
1705 *hasRest = true;
1706 if (!tokenStream.getToken(&tt))
1707 return false;
1708 if (tt != TOK_NAME) {
1709 report(ParseError, false, null(), JSMSG_NO_REST_NAME);
1710 return false;
1712 goto TOK_NAME;
1715 TOK_NAME:
1716 case TOK_NAME:
1718 if (parenFreeArrow)
1719 funbox->setStart(tokenStream);
1721 RootedPropertyName name(context, tokenStream.currentName());
1722 bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
1723 if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
1724 return false;
1726 bool matched;
1727 if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
1728 return false;
1729 if (matched) {
1730 // A default argument without parentheses would look like:
1731 // a = expr => body, but both operators are right-associative, so
1732 // that would have been parsed as a = (expr => body) instead.
1733 // Therefore it's impossible to get here with parenFreeArrow.
1734 MOZ_ASSERT(!parenFreeArrow);
1736 if (*hasRest) {
1737 report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
1738 return false;
1740 if (duplicatedArg) {
1741 report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
1742 return false;
1744 if (!hasDefaults) {
1745 hasDefaults = true;
1747 // The Function.length property is the number of formals
1748 // before the first default argument.
1749 funbox->length = pc->numArgs() - 1;
1751 Node def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT);
1752 if (!def_expr)
1753 return false;
1754 handler.setLastFunctionArgumentDefault(funcpn, def_expr);
1757 break;
1760 default:
1761 report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
1762 return false;
1765 if (parenFreeArrow)
1766 break;
1768 bool matched;
1769 if (!tokenStream.matchToken(&matched, TOK_COMMA))
1770 return false;
1771 if (!matched)
1772 break;
1775 if (!parenFreeArrow) {
1776 TokenKind tt;
1777 if (!tokenStream.getToken(&tt))
1778 return false;
1779 if (tt != TOK_RP) {
1780 report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
1781 return false;
1785 if (!hasDefaults)
1786 funbox->length = pc->numArgs() - *hasRest;
1789 return true;
1792 template <>
1793 bool
1794 Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
1795 ParseNode** pn_, FunctionSyntaxKind kind,
1796 bool* pbodyProcessed)
1798 ParseNode*& pn = *pn_;
1799 *pbodyProcessed = false;
1801 /* Function statements add a binding to the enclosing scope. */
1802 bool bodyLevel = pc->atBodyLevel();
1804 if (kind == Statement) {
1806 * Handle redeclaration and optimize cases where we can statically bind the
1807 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
1809 if (Definition* dn = pc->decls().lookupFirst(funName)) {
1810 MOZ_ASSERT(!dn->isUsed());
1811 MOZ_ASSERT(dn->isDefn());
1813 bool throwRedeclarationError = dn->kind() == Definition::GLOBALCONST ||
1814 dn->kind() == Definition::CONST ||
1815 dn->kind() == Definition::LET;
1816 if (options().extraWarningsOption || throwRedeclarationError) {
1817 JSAutoByteString name;
1818 ParseReportKind reporter = throwRedeclarationError
1819 ? ParseError
1820 : ParseExtraWarning;
1821 if (!AtomToPrintableString(context, funName, &name) ||
1822 !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR,
1823 Definition::kindString(dn->kind()), name.ptr()))
1825 return false;
1830 * Body-level function statements are effectively variable
1831 * declarations where the initialization is hoisted to the
1832 * beginning of the block. This means that any other variable
1833 * declaration with the same name is really just an assignment to
1834 * the function's binding (which is mutable), so turn any existing
1835 * declaration into a use.
1837 if (bodyLevel) {
1838 if (dn->kind() == Definition::ARG) {
1839 // The exception to the above comment is when the function
1840 // has the same name as an argument. Then the argument node
1841 // remains a definition. But change the function node pn so
1842 // that it knows where the argument is located.
1843 pn->setOp(JSOP_GETARG);
1844 pn->setDefn(true);
1845 pn->pn_cookie = dn->pn_cookie;
1846 pn->pn_dflags |= PND_BOUND;
1847 dn->markAsAssigned();
1848 } else {
1849 if (!makeDefIntoUse(dn, pn, funName))
1850 return false;
1853 } else if (bodyLevel) {
1855 * If this function was used before it was defined, claim the
1856 * pre-created definition node for this function that primaryExpr
1857 * put in pc->lexdeps on first forward reference, and recycle pn.
1859 if (Definition* fn = pc->lexdeps.lookupDefn<FullParseHandler>(funName)) {
1860 MOZ_ASSERT(fn->isDefn());
1861 fn->setKind(PNK_FUNCTION);
1862 fn->setArity(PN_CODE);
1863 fn->pn_pos.begin = pn->pn_pos.begin;
1864 fn->pn_pos.end = pn->pn_pos.end;
1866 fn->pn_body = nullptr;
1867 fn->pn_cookie.makeFree();
1869 pc->lexdeps->remove(funName);
1870 handler.freeTree(pn);
1871 pn = fn;
1874 if (!pc->define(tokenStream, funName, pn, Definition::VAR))
1875 return false;
1878 if (bodyLevel) {
1879 MOZ_ASSERT(pn->functionIsHoisted());
1880 MOZ_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree());
1881 MOZ_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree());
1882 } else {
1884 * As a SpiderMonkey-specific extension, non-body-level function
1885 * statements (e.g., functions in an "if" or "while" block) are
1886 * dynamically bound when control flow reaches the statement.
1888 MOZ_ASSERT(!pc->sc->strict);
1889 MOZ_ASSERT(pn->pn_cookie.isFree());
1890 if (pc->sc->isFunctionBox()) {
1891 FunctionBox* funbox = pc->sc->asFunctionBox();
1892 funbox->setMightAliasLocals();
1893 funbox->setHasExtensibleScope();
1895 pn->setOp(JSOP_DEFFUN);
1898 * Instead of setting bindingsAccessedDynamically, which would be
1899 * overly conservative, remember the names of all function
1900 * statements and mark any bindings with the same as aliased at the
1901 * end of functionBody.
1903 if (!pc->funcStmts) {
1904 pc->funcStmts = alloc.new_<FuncStmtSet>(alloc);
1905 if (!pc->funcStmts || !pc->funcStmts->init())
1906 return false;
1908 if (!pc->funcStmts->put(funName))
1909 return false;
1912 * Due to the implicit declaration mechanism, 'arguments' will not
1913 * have decls and, even if it did, they will not be noted as closed
1914 * in the emitter. Thus, in the corner case of function statements
1915 * overridding arguments, flag the whole scope as dynamic.
1917 if (funName == context->names().arguments)
1918 pc->sc->setBindingsAccessedDynamically();
1921 /* No further binding (in BindNameToSlot) is needed for functions. */
1922 pn->pn_dflags |= PND_BOUND;
1923 } else {
1924 /* A function expression does not introduce any binding. */
1925 pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
1928 // When a lazily-parsed function is called, we only fully parse (and emit)
1929 // that function, not any of its nested children. The initial syntax-only
1930 // parse recorded the free variables of nested functions and their extents,
1931 // so we can skip over them after accounting for their free variables.
1932 if (LazyScript* lazyOuter = handler.lazyOuterFunction()) {
1933 JSFunction* fun = handler.nextLazyInnerFunction();
1934 MOZ_ASSERT(!fun->isLegacyGenerator());
1935 FunctionBox* funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false),
1936 fun->generatorKind());
1937 if (!funbox)
1938 return false;
1940 if (!addFreeVariablesFromLazyFunction(fun, pc))
1941 return false;
1943 // The position passed to tokenStream.advance() is an offset of the sort
1944 // returned by userbuf.offset() and expected by userbuf.rawCharPtrAt(),
1945 // while LazyScript::{begin,end} offsets are relative to the outermost
1946 // script source.
1947 uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
1948 if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase))
1949 return false;
1951 *pbodyProcessed = true;
1952 return true;
1955 return true;
1958 template <class T, class U>
1959 static inline void
1960 PropagateTransitiveParseFlags(const T* inner, U* outer)
1962 if (inner->bindingsAccessedDynamically())
1963 outer->setBindingsAccessedDynamically();
1964 if (inner->hasDebuggerStatement())
1965 outer->setHasDebuggerStatement();
1968 template <typename ParseHandler>
1969 bool
1970 Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction* fun,
1971 ParseContext<ParseHandler>* pc)
1973 // Update any definition nodes in this context according to free variables
1974 // in a lazily parsed inner function.
1976 bool bodyLevel = pc->atBodyLevel();
1977 LazyScript* lazy = fun->lazyScript();
1978 LazyScript::FreeVariable* freeVariables = lazy->freeVariables();
1979 for (size_t i = 0; i < lazy->numFreeVariables(); i++) {
1980 JSAtom* atom = freeVariables[i].atom();
1982 // 'arguments' will be implicitly bound within the inner function.
1983 if (atom == context->names().arguments)
1984 continue;
1986 DefinitionNode dn = pc->decls().lookupFirst(atom);
1988 if (!dn) {
1989 dn = getOrCreateLexicalDependency(pc, atom);
1990 if (!dn)
1991 return false;
1994 // In ES6, lexical bindings are unaccessible before initialization. If
1995 // the inner function closes over a placeholder definition, we need to
1996 // mark the variable as maybe needing a dead zone check when we emit
1997 // bytecode.
1999 // Note that body-level function declaration statements are always
2000 // hoisted to the top, so all accesses to free let variables need the
2001 // dead zone check.
2003 // Subtlety: we don't need to check for closing over a non-dominating
2004 // lexical binding in a switch, as lexical declarations currently
2005 // disable syntax parsing. So a non-dominating but textually preceding
2006 // lexical declaration would have aborted syntax parsing, and a
2007 // textually following declaration would return true for
2008 // handler.isPlaceholderDefinition(dn) below.
2009 if (handler.isPlaceholderDefinition(dn) || bodyLevel)
2010 freeVariables[i].setIsHoistedUse();
2012 /* Mark the outer dn as escaping. */
2013 handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED);
2016 PropagateTransitiveParseFlags(lazy, pc->sc);
2017 return true;
2020 template <>
2021 bool
2022 Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
2023 Node* pn, FunctionSyntaxKind kind,
2024 bool* pbodyProcessed)
2026 *pbodyProcessed = false;
2028 /* Function statements add a binding to the enclosing scope. */
2029 bool bodyLevel = pc->atBodyLevel();
2031 if (kind == Statement) {
2033 * Handle redeclaration and optimize cases where we can statically bind the
2034 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
2036 if (DefinitionNode dn = pc->decls().lookupFirst(funName)) {
2037 if (dn == Definition::GLOBALCONST ||
2038 dn == Definition::CONST ||
2039 dn == Definition::LET)
2041 JSAutoByteString name;
2042 if (!AtomToPrintableString(context, funName, &name) ||
2043 !report(ParseError, false, null(), JSMSG_REDECLARED_VAR,
2044 Definition::kindString(dn), name.ptr()))
2046 return false;
2049 } else if (bodyLevel) {
2050 if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName))
2051 pc->lexdeps->remove(funName);
2053 if (!pc->define(tokenStream, funName, *pn, Definition::VAR))
2054 return false;
2057 if (!bodyLevel && funName == context->names().arguments)
2058 pc->sc->setBindingsAccessedDynamically();
2061 if (kind == Arrow) {
2062 /* Arrow functions cannot yet be parsed lazily. */
2063 return abortIfSyntaxParser();
2066 return true;
2069 template <typename ParseHandler>
2070 bool
2071 Parser<ParseHandler>::addExprAndGetNextTemplStrToken(Node nodeList, TokenKind* ttp)
2073 Node pn = expr();
2074 if (!pn)
2075 return false;
2076 handler.addList(nodeList, pn);
2078 TokenKind tt;
2079 if (!tokenStream.getToken(&tt))
2080 return false;
2081 if (tt != TOK_RC) {
2082 report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
2083 return false;
2086 return tokenStream.getToken(ttp, TokenStream::TemplateTail);
2089 template <typename ParseHandler>
2090 bool
2091 Parser<ParseHandler>::taggedTemplate(Node nodeList, TokenKind tt)
2093 Node callSiteObjNode = handler.newCallSiteObject(pos().begin, pc->blockidGen);
2094 if (!callSiteObjNode)
2095 return false;
2096 handler.addList(nodeList, callSiteObjNode);
2098 while (true) {
2099 if (!appendToCallSiteObj(callSiteObjNode))
2100 return false;
2101 if (tt != TOK_TEMPLATE_HEAD)
2102 break;
2104 if (!addExprAndGetNextTemplStrToken(nodeList, &tt))
2105 return false;
2107 handler.setEndPosition(nodeList, callSiteObjNode);
2108 return true;
2111 template <typename ParseHandler>
2112 typename ParseHandler::Node
2113 Parser<ParseHandler>::templateLiteral()
2115 Node pn = noSubstitutionTemplate();
2116 if (!pn)
2117 return null();
2118 Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
2120 TokenKind tt;
2121 do {
2122 if (!addExprAndGetNextTemplStrToken(nodeList, &tt))
2123 return null();
2125 pn = noSubstitutionTemplate();
2126 if (!pn)
2127 return null();
2129 handler.addList(nodeList, pn);
2130 } while (tt == TOK_TEMPLATE_HEAD);
2131 return nodeList;
2134 template <typename ParseHandler>
2135 typename ParseHandler::Node
2136 Parser<ParseHandler>::functionDef(HandlePropertyName funName,
2137 FunctionType type, FunctionSyntaxKind kind,
2138 GeneratorKind generatorKind)
2140 MOZ_ASSERT_IF(kind == Statement, funName);
2142 /* Make a TOK_FUNCTION node. */
2143 Node pn = handler.newFunctionDefinition();
2144 if (!pn)
2145 return null();
2147 bool bodyProcessed;
2148 if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
2149 return null();
2151 if (bodyProcessed)
2152 return pn;
2154 RootedObject proto(context);
2155 if (generatorKind == StarGenerator) {
2156 // If we are off the main thread, the generator meta-objects have
2157 // already been created by js::StartOffThreadParseScript, so cx will not
2158 // be necessary.
2159 JSContext* cx = context->maybeJSContext();
2160 proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
2161 if (!proto)
2162 return null();
2164 RootedFunction fun(context, newFunction(pc, funName, kind, proto));
2165 if (!fun)
2166 return null();
2168 // Speculatively parse using the directives of the parent parsing context.
2169 // If a directive is encountered (e.g., "use strict") that changes how the
2170 // function should have been parsed, we backup and reparse with the new set
2171 // of directives.
2172 Directives directives(pc);
2173 Directives newDirectives = directives;
2175 TokenStream::Position start(keepAtoms);
2176 tokenStream.tell(&start);
2178 while (true) {
2179 if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives))
2180 break;
2181 if (tokenStream.hadError() || directives == newDirectives)
2182 return null();
2184 // Assignment must be monotonic to prevent reparsing iloops
2185 MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
2186 MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
2187 directives = newDirectives;
2189 tokenStream.seek(start);
2191 // functionArgsAndBody may have already set pn->pn_body before failing.
2192 handler.setFunctionBody(pn, null());
2195 return pn;
2198 template <>
2199 bool
2200 Parser<FullParseHandler>::finishFunctionDefinition(ParseNode* pn, FunctionBox* funbox,
2201 ParseNode* prelude, ParseNode* body)
2203 pn->pn_pos.end = pos().end;
2206 * If there were destructuring formal parameters, prepend the initializing
2207 * comma expression that we synthesized to body. If the body is a return
2208 * node, we must make a special PNK_SEQ node, to prepend the destructuring
2209 * code without bracing the decompilation of the function body.
2211 if (prelude) {
2212 if (!body->isArity(PN_LIST)) {
2213 ParseNode* block;
2215 block = ListNode::create(PNK_SEQ, &handler);
2216 if (!block)
2217 return false;
2218 block->pn_pos = body->pn_pos;
2219 block->initList(body);
2221 body = block;
2224 ParseNode* item = UnaryNode::create(PNK_SEMI, &handler);
2225 if (!item)
2226 return false;
2228 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2229 item->pn_kid = prelude;
2230 item->pn_next = body->pn_head;
2231 body->pn_head = item;
2232 if (body->pn_tail == &body->pn_head)
2233 body->pn_tail = &item->pn_next;
2234 ++body->pn_count;
2235 body->pn_xflags |= PNX_DESTRUCT;
2238 MOZ_ASSERT(pn->pn_funbox == funbox);
2239 MOZ_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY));
2240 pn->pn_body->append(body);
2241 pn->pn_body->pn_pos = body->pn_pos;
2243 return true;
2246 template <>
2247 bool
2248 Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox* funbox,
2249 Node prelude, Node body)
2251 // The LazyScript for a lazily parsed function needs to be constructed
2252 // while its ParseContext and associated lexdeps and inner functions are
2253 // still available.
2255 if (funbox->inWith)
2256 return abortIfSyntaxParser();
2258 size_t numFreeVariables = pc->lexdeps->count();
2259 size_t numInnerFunctions = pc->innerFunctions.length();
2261 RootedFunction fun(context, funbox->function());
2262 LazyScript* lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions,
2263 versionNumber(), funbox->bufStart, funbox->bufEnd,
2264 funbox->startLine, funbox->startColumn);
2265 if (!lazy)
2266 return false;
2268 LazyScript::FreeVariable* freeVariables = lazy->freeVariables();
2269 size_t i = 0;
2270 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
2271 freeVariables[i++] = LazyScript::FreeVariable(r.front().key());
2272 MOZ_ASSERT(i == numFreeVariables);
2274 HeapPtrFunction* innerFunctions = lazy->innerFunctions();
2275 for (size_t i = 0; i < numInnerFunctions; i++)
2276 innerFunctions[i].init(pc->innerFunctions[i]);
2278 if (pc->sc->strict)
2279 lazy->setStrict();
2280 lazy->setGeneratorKind(funbox->generatorKind());
2281 if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
2282 lazy->setUsesArgumentsApplyAndThis();
2283 PropagateTransitiveParseFlags(funbox, lazy);
2285 fun->initLazyScript(lazy);
2286 return true;
2289 template <>
2290 bool
2291 Parser<FullParseHandler>::functionArgsAndBody(ParseNode* pn, HandleFunction fun,
2292 FunctionType type, FunctionSyntaxKind kind,
2293 GeneratorKind generatorKind,
2294 Directives inheritedDirectives,
2295 Directives* newDirectives)
2297 ParseContext<FullParseHandler>* outerpc = pc;
2299 // Create box for fun->object early to protect against last-ditch GC.
2300 FunctionBox* funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
2301 if (!funbox)
2302 return false;
2304 // Try a syntax parse for this inner function.
2305 do {
2306 Parser<SyntaxParseHandler>* parser = handler.syntaxParser;
2307 if (!parser)
2308 break;
2311 // Move the syntax parser to the current position in the stream.
2312 TokenStream::Position position(keepAtoms);
2313 tokenStream.tell(&position);
2314 if (!parser->tokenStream.seek(position, tokenStream))
2315 return false;
2317 ParseContext<SyntaxParseHandler> funpc(parser, outerpc, SyntaxParseHandler::null(), funbox,
2318 newDirectives, outerpc->staticLevel + 1,
2319 outerpc->blockidGen, /* blockScopeDepth = */ 0);
2320 if (!funpc.init(tokenStream))
2321 return false;
2323 if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
2324 fun, type, kind))
2326 if (parser->hadAbortedSyntaxParse()) {
2327 // Try again with a full parse.
2328 parser->clearAbortedSyntaxParse();
2329 MOZ_ASSERT_IF(parser->context->isJSContext(),
2330 !parser->context->asJSContext()->isExceptionPending());
2331 break;
2333 return false;
2336 outerpc->blockidGen = funpc.blockidGen;
2338 // Advance this parser over tokens processed by the syntax parser.
2339 parser->tokenStream.tell(&position);
2340 if (!tokenStream.seek(position, parser->tokenStream))
2341 return false;
2343 // Update the end position of the parse node.
2344 pn->pn_pos.end = tokenStream.currentToken().pos.end;
2347 if (!addFreeVariablesFromLazyFunction(fun, pc))
2348 return false;
2350 pn->pn_blockid = outerpc->blockid();
2351 PropagateTransitiveParseFlags(funbox, outerpc->sc);
2352 return true;
2353 } while (false);
2355 // Continue doing a full parse for this inner function.
2356 ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives,
2357 outerpc->staticLevel + 1, outerpc->blockidGen,
2358 /* blockScopeDepth = */ 0);
2359 if (!funpc.init(tokenStream))
2360 return false;
2362 if (!functionArgsAndBodyGeneric(pn, fun, type, kind))
2363 return false;
2365 if (!leaveFunction(pn, outerpc, kind))
2366 return false;
2368 pn->pn_blockid = outerpc->blockid();
2371 * Fruit of the poisonous tree: if a closure contains a dynamic name access
2372 * (eval, with, etc), we consider the parent to do the same. The reason is
2373 * that the deoptimizing effects of dynamic name access apply equally to
2374 * parents: any local can be read at runtime.
2376 PropagateTransitiveParseFlags(funbox, outerpc->sc);
2377 return true;
2380 template <>
2381 bool
2382 Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
2383 FunctionType type, FunctionSyntaxKind kind,
2384 GeneratorKind generatorKind,
2385 Directives inheritedDirectives,
2386 Directives* newDirectives)
2388 ParseContext<SyntaxParseHandler>* outerpc = pc;
2390 // Create box for fun->object early to protect against last-ditch GC.
2391 FunctionBox* funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
2392 if (!funbox)
2393 return false;
2395 // Initialize early for possible flags mutation via destructuringExpr.
2396 ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives,
2397 outerpc->staticLevel + 1, outerpc->blockidGen,
2398 /* blockScopeDepth = */ 0);
2399 if (!funpc.init(tokenStream))
2400 return false;
2402 if (!functionArgsAndBodyGeneric(pn, fun, type, kind))
2403 return false;
2405 if (!leaveFunction(pn, outerpc, kind))
2406 return false;
2408 // This is a lazy function inner to another lazy function. Remember the
2409 // inner function so that if the outer function is eventually parsed we do
2410 // not need any further parsing or processing of the inner function.
2411 MOZ_ASSERT(fun->lazyScript());
2412 return outerpc->innerFunctions.append(fun);
2415 template <typename ParseHandler>
2416 bool
2417 Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
2419 Node cookedNode = noSubstitutionTemplate();
2420 if (!cookedNode)
2421 return false;
2423 JSAtom* atom = tokenStream.getRawTemplateStringAtom();
2424 if (!atom)
2425 return false;
2426 Node rawNode = handler.newTemplateStringLiteral(atom, pos());
2427 if (!rawNode)
2428 return false;
2430 return handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
2433 template <>
2434 ParseNode*
2435 Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
2436 bool strict, GeneratorKind generatorKind)
2438 MOZ_ASSERT(checkOptionsCalled);
2440 Node pn = handler.newFunctionDefinition();
2441 if (!pn)
2442 return null();
2444 // Our tokenStream has no current token, so pn's position is garbage.
2445 // Substitute the position of the first token in our source.
2446 if (!tokenStream.peekTokenPos(&pn->pn_pos))
2447 return null();
2449 Directives directives(/* strict = */ strict);
2450 FunctionBox* funbox = newFunctionBox(pn, fun, /* outerpc = */ nullptr, directives,
2451 generatorKind);
2452 if (!funbox)
2453 return null();
2454 funbox->length = fun->nargs() - fun->hasRest();
2456 Directives newDirectives = directives;
2457 ParseContext<FullParseHandler> funpc(this, /* parent = */ nullptr, pn, funbox,
2458 &newDirectives, staticLevel, /* bodyid = */ 0,
2459 /* blockScopeDepth = */ 0);
2460 if (!funpc.init(tokenStream))
2461 return null();
2463 if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement)) {
2464 MOZ_ASSERT(directives == newDirectives);
2465 return null();
2468 if (fun->isNamedLambda()) {
2469 if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
2470 Definition* dn = p.value().get<FullParseHandler>();
2471 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
2472 return nullptr;
2476 InternalHandle<Bindings*> bindings =
2477 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
2478 if (!pc->generateFunctionBindings(context, tokenStream, alloc, bindings))
2479 return null();
2481 if (!FoldConstants(context, &pn, this))
2482 return null();
2484 return pn;
2487 template <typename ParseHandler>
2488 bool
2489 Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
2490 FunctionSyntaxKind kind)
2492 // Given a properly initialized parse context, try to parse an actual
2493 // function without concern for conversion to strict mode, use of lazy
2494 // parsing and such.
2496 Node prelude = null();
2497 bool hasRest;
2498 if (!functionArguments(kind, &prelude, pn, &hasRest))
2499 return false;
2501 FunctionBox* funbox = pc->sc->asFunctionBox();
2503 fun->setArgCount(pc->numArgs());
2504 if (hasRest)
2505 fun->setHasRest();
2507 if (type == Getter && fun->nargs() > 0) {
2508 report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
2509 return false;
2511 if (type == Setter && fun->nargs() != 1) {
2512 report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
2513 return false;
2516 if (kind == Arrow) {
2517 bool matched;
2518 if (!tokenStream.matchToken(&matched, TOK_ARROW))
2519 return false;
2520 if (!matched) {
2521 report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
2522 return false;
2526 // Parse the function body.
2527 FunctionBodyType bodyType = StatementListBody;
2528 TokenKind tt;
2529 if (!tokenStream.getToken(&tt, TokenStream::Operand))
2530 return false;
2531 if (tt != TOK_LC) {
2532 if (funbox->isStarGenerator()) {
2533 report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
2534 return false;
2537 if (kind != Arrow)
2538 sawDeprecatedExpressionClosure = true;
2540 tokenStream.ungetToken();
2541 bodyType = ExpressionBody;
2542 fun->setIsExprClosure();
2545 Node body = functionBody(kind, bodyType);
2546 if (!body)
2547 return false;
2549 if (fun->name() && !checkStrictBinding(fun->name(), pn))
2550 return false;
2552 #if JS_HAS_EXPR_CLOSURES
2553 if (bodyType == StatementListBody) {
2554 #endif
2555 bool matched;
2556 if (!tokenStream.matchToken(&matched, TOK_RC))
2557 return false;
2558 if (!matched) {
2559 report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY);
2560 return false;
2562 funbox->bufEnd = pos().begin + 1;
2563 #if JS_HAS_EXPR_CLOSURES
2564 } else {
2565 if (tokenStream.hadError())
2566 return false;
2567 funbox->bufEnd = pos().end;
2568 if (kind == Statement && !MatchOrInsertSemicolon(tokenStream))
2569 return false;
2571 #endif
2573 return finishFunctionDefinition(pn, funbox, prelude, body);
2576 template <typename ParseHandler>
2577 bool
2578 Parser<ParseHandler>::checkYieldNameValidity()
2580 // In star generators and in JS >= 1.7, yield is a keyword. Otherwise in
2581 // strict mode, yield is a future reserved word.
2582 if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict) {
2583 report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
2584 return false;
2586 return true;
2589 template <typename ParseHandler>
2590 typename ParseHandler::Node
2591 Parser<ParseHandler>::functionStmt()
2593 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
2595 RootedPropertyName name(context);
2596 GeneratorKind generatorKind = NotGenerator;
2597 TokenKind tt;
2598 if (!tokenStream.getToken(&tt))
2599 return null();
2601 if (tt == TOK_MUL) {
2602 generatorKind = StarGenerator;
2603 if (!tokenStream.getToken(&tt))
2604 return null();
2607 if (tt == TOK_NAME) {
2608 name = tokenStream.currentName();
2609 } else if (tt == TOK_YIELD) {
2610 if (!checkYieldNameValidity())
2611 return null();
2612 name = tokenStream.currentName();
2613 } else {
2614 /* Unnamed function expressions are forbidden in statement context. */
2615 report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
2616 return null();
2619 /* We forbid function statements in strict mode code. */
2620 if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
2621 !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
2622 return null();
2624 return functionDef(name, Normal, Statement, generatorKind);
2627 template <typename ParseHandler>
2628 typename ParseHandler::Node
2629 Parser<ParseHandler>::functionExpr()
2631 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
2633 GeneratorKind generatorKind = NotGenerator;
2634 TokenKind tt;
2635 if (!tokenStream.getToken(&tt))
2636 return null();
2638 if (tt == TOK_MUL) {
2639 generatorKind = StarGenerator;
2640 if (!tokenStream.getToken(&tt))
2641 return null();
2644 RootedPropertyName name(context);
2645 if (tt == TOK_NAME) {
2646 name = tokenStream.currentName();
2647 } else if (tt == TOK_YIELD) {
2648 if (!checkYieldNameValidity())
2649 return null();
2650 name = tokenStream.currentName();
2651 } else {
2652 tokenStream.ungetToken();
2655 return functionDef(name, Normal, Expression, generatorKind);
2659 * Return true if this node, known to be an unparenthesized string literal,
2660 * could be the string of a directive in a Directive Prologue. Directive
2661 * strings never contain escape sequences or line continuations.
2662 * isEscapeFreeStringLiteral, below, checks whether the node itself could be
2663 * a directive.
2665 static inline bool
2666 IsEscapeFreeStringLiteral(const TokenPos& pos, JSAtom* str)
2669 * If the string's length in the source code is its length as a value,
2670 * accounting for the quotes, then it must not contain any escape
2671 * sequences or line continuations.
2673 return pos.begin + str->length() + 2 == pos.end;
2676 template <>
2677 bool
2678 Parser<SyntaxParseHandler>::asmJS(Node list)
2680 // While asm.js could technically be validated and compiled during syntax
2681 // parsing, we have no guarantee that some later JS wouldn't abort the
2682 // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
2683 // For simplicity, unconditionally abort the syntax parse when "use asm" is
2684 // encountered so that asm.js is always validated/compiled exactly once
2685 // during a full parse.
2686 JS_ALWAYS_FALSE(abortIfSyntaxParser());
2687 return false;
2690 template <>
2691 bool
2692 Parser<FullParseHandler>::asmJS(Node list)
2694 // Disable syntax parsing in anything nested inside the asm.js module.
2695 handler.disableSyntaxParser();
2697 // We should be encountering the "use asm" directive for the first time; if
2698 // the directive is already, we must have failed asm.js validation and we're
2699 // reparsing. In that case, don't try to validate again. A non-null
2700 // newDirectives means we're not in a normal function.
2701 if (!pc->newDirectives || pc->newDirectives->asmJS())
2702 return true;
2704 // If there is no ScriptSource, then we are doing a non-compiling parse and
2705 // so we shouldn't (and can't, without a ScriptSource) compile.
2706 if (ss == nullptr)
2707 return true;
2709 pc->sc->asFunctionBox()->useAsm = true;
2711 // Attempt to validate and compile this asm.js module. On success, the
2712 // tokenStream has been advanced to the closing }. On failure, the
2713 // tokenStream is in an indeterminate state and we must reparse the
2714 // function from the beginning. Reparsing is triggered by marking that a
2715 // new directive has been encountered and returning 'false'.
2716 bool validated;
2717 if (!ValidateAsmJS(context, *this, list, &validated))
2718 return false;
2719 if (!validated) {
2720 pc->newDirectives->setAsmJS();
2721 return false;
2724 return true;
2728 * Recognize Directive Prologue members and directives. Assuming |pn| is a
2729 * candidate for membership in a directive prologue, recognize directives and
2730 * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
2731 * |pn_prologue| flag.
2733 * Note that the following is a strict mode function:
2735 * function foo() {
2736 * "blah" // inserted semi colon
2737 * "blurgh"
2738 * "use\x20loose"
2739 * "use strict"
2742 * That is, even though "use\x20loose" can never be a directive, now or in the
2743 * future (because of the hex escape), the Directive Prologue extends through it
2744 * to the "use strict" statement, which is indeed a directive.
2746 template <typename ParseHandler>
2747 bool
2748 Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
2750 TokenPos directivePos;
2751 JSAtom* directive = handler.isStringExprStatement(pn, &directivePos);
2753 *cont = !!directive;
2754 if (!*cont)
2755 return true;
2757 if (IsEscapeFreeStringLiteral(directivePos, directive)) {
2758 // Mark this statement as being a possibly legitimate part of a
2759 // directive prologue, so the bytecode emitter won't warn about it being
2760 // useless code. (We mustn't just omit the statement entirely yet, as it
2761 // could be producing the value of an eval or JSScript execution.)
2763 // Note that even if the string isn't one we recognize as a directive,
2764 // the emitter still shouldn't flag it as useless, as it could become a
2765 // directive in the future. We don't want to interfere with people
2766 // taking advantage of directive-prologue-enabled features that appear
2767 // in other browsers first.
2768 handler.setPrologue(pn);
2770 if (directive == context->names().useStrict) {
2771 // We're going to be in strict mode. Note that this scope explicitly
2772 // had "use strict";
2773 pc->sc->setExplicitUseStrict();
2774 if (!pc->sc->strict) {
2775 if (pc->sc->isFunctionBox()) {
2776 // Request that this function be reparsed as strict.
2777 pc->newDirectives->setStrict();
2778 return false;
2779 } else {
2780 // We don't reparse global scopes, so we keep track of the
2781 // one possible strict violation that could occur in the
2782 // directive prologue -- octal escapes -- and complain now.
2783 if (tokenStream.sawOctalEscape()) {
2784 report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
2785 return false;
2787 pc->sc->strict = true;
2790 } else if (directive == context->names().useAsm) {
2791 if (pc->sc->isFunctionBox())
2792 return asmJS(list);
2793 return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
2796 return true;
2800 * Parse the statements in a block, creating a StatementList node that lists
2801 * the statements. If called from block-parsing code, the caller must match
2802 * '{' before and '}' after.
2804 template <typename ParseHandler>
2805 typename ParseHandler::Node
2806 Parser<ParseHandler>::statements()
2808 JS_CHECK_RECURSION(context, return null());
2810 Node pn = handler.newStatementList(pc->blockid(), pos());
2811 if (!pn)
2812 return null();
2814 Node saveBlock = pc->blockNode;
2815 pc->blockNode = pn;
2817 bool canHaveDirectives = pc->atBodyLevel();
2818 for (;;) {
2819 TokenKind tt;
2820 if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
2821 if (tokenStream.isEOF())
2822 isUnexpectedEOF_ = true;
2823 return null();
2825 if (tt == TOK_EOF || tt == TOK_RC)
2826 break;
2827 Node next = statement(canHaveDirectives);
2828 if (!next) {
2829 if (tokenStream.isEOF())
2830 isUnexpectedEOF_ = true;
2831 return null();
2834 if (canHaveDirectives) {
2835 if (!maybeParseDirective(pn, next, &canHaveDirectives))
2836 return null();
2839 handler.addStatementToList(pn, next, pc);
2843 * Handle the case where there was a let declaration under this block. If
2844 * it replaced pc->blockNode with a new block node then we must refresh pn
2845 * and then restore pc->blockNode.
2847 if (pc->blockNode != pn)
2848 pn = pc->blockNode;
2849 pc->blockNode = saveBlock;
2850 return pn;
2853 template <typename ParseHandler>
2854 typename ParseHandler::Node
2855 Parser<ParseHandler>::condition()
2857 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
2858 Node pn = exprInParens();
2859 if (!pn)
2860 return null();
2861 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
2863 /* Check for (a = b) and warn about possible (a == b) mistype. */
2864 if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) &&
2865 !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
2867 return null();
2869 return pn;
2872 template <typename ParseHandler>
2873 bool
2874 Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
2876 TokenKind tt;
2877 if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
2878 return false;
2879 if (tt == TOK_NAME) {
2880 tokenStream.consumeKnownToken(TOK_NAME);
2881 label.set(tokenStream.currentName());
2882 } else if (tt == TOK_YIELD) {
2883 tokenStream.consumeKnownToken(TOK_YIELD);
2884 if (!checkYieldNameValidity())
2885 return false;
2886 label.set(tokenStream.currentName());
2887 } else {
2888 label.set(nullptr);
2890 return true;
2893 template <typename ParseHandler>
2894 bool
2895 Parser<ParseHandler>::reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name)
2897 JSAutoByteString printable;
2898 if (!AtomToPrintableString(context, name, &printable))
2899 return false;
2901 StmtInfoPC* stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC*)nullptr);
2902 if (stmt && stmt->type == STMT_CATCH) {
2903 report(ParseError, false, pn, JSMSG_REDECLARED_CATCH_IDENTIFIER, printable.ptr());
2904 } else {
2905 if (redeclKind == Definition::ARG) {
2906 report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, printable.ptr());
2907 } else {
2908 report(ParseError, false, pn, JSMSG_REDECLARED_VAR, Definition::kindString(redeclKind),
2909 printable.ptr());
2912 return false;
2916 * Define a lexical binding in a block, let-expression, or comprehension scope. pc
2917 * must already be in such a scope.
2919 * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
2920 * property for the new variable on the block object, pc->staticScope;
2921 * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
2922 * data->pn in a slot of the block object.
2924 template <>
2925 /* static */ bool
2926 Parser<FullParseHandler>::bindLexical(BindData<FullParseHandler>* data,
2927 HandlePropertyName name, Parser<FullParseHandler>* parser)
2929 ParseContext<FullParseHandler>* pc = parser->pc;
2930 ParseNode* pn = data->pn;
2931 if (!parser->checkStrictBinding(name, pn))
2932 return false;
2934 ExclusiveContext* cx = parser->context;
2935 Rooted<StaticBlockObject*> blockObj(cx, data->let.blockObj);
2937 unsigned index;
2938 if (blockObj) {
2939 index = blockObj->numVariables();
2940 if (index >= StaticBlockObject::LOCAL_INDEX_LIMIT) {
2941 parser->report(ParseError, false, pn, data->let.overflow);
2942 return false;
2944 } else {
2945 // If we don't have a block object, we are parsing a body-level let,
2946 // in which case we use a bogus index. See comment block below in
2947 // setting the pn_cookie for explanation on how it gets adjusted.
2948 index = 0;
2951 // For block-level lets, assign block-local index to pn->pn_cookie right
2952 // away, encoding it as an upvar cookie whose skip tells the current
2953 // static level. The emitter will adjust the node's slot based on its
2954 // stack depth model -- and, for global and eval code,
2955 // js::frontend::CompileScript will adjust the slot again to include
2956 // script->nfixed and body-level lets.
2958 // For body-level lets, the index is bogus at this point and is adjusted
2959 // when creating Bindings. See ParseContext::generateFunctionBindings and
2960 // AppendPackedBindings.
2961 if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, index))
2962 return false;
2964 Definition* dn = pc->decls().lookupFirst(name);
2965 Definition::Kind bindingKind = data->isConst ? Definition::CONST : Definition::LET;
2968 * For bindings that are hoisted to the beginning of the block/function,
2969 * define() right now. Otherwise, delay define until PushLetScope.
2971 if (data->let.varContext == HoistVars) {
2972 if (dn && dn->pn_blockid == pc->blockid())
2973 return parser->reportRedeclaration(pn, dn->kind(), name);
2974 if (!pc->define(parser->tokenStream, name, pn, bindingKind))
2975 return false;
2978 if (blockObj) {
2979 bool redeclared;
2980 RootedId id(cx, NameToId(name));
2981 RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id,
2982 data->isConst, index, &redeclared));
2983 if (!shape) {
2984 if (redeclared) {
2985 // The only way to be redeclared without a previous definition is if we're in a
2986 // comma separated list in a DontHoistVars block, so a let block of for header. In
2987 // that case, we must be redeclaring the same type of definition as we're trying to
2988 // make.
2989 Definition::Kind dnKind = dn ? dn->kind() : bindingKind;
2990 parser->reportRedeclaration(pn, dnKind, name);
2992 return false;
2995 /* Store pn in the static block object. */
2996 blockObj->setDefinitionParseNode(index, reinterpret_cast<Definition*>(pn));
2997 } else {
2998 // Body-level lets are hoisted and need to have been defined via
2999 // pc->define above.
3000 MOZ_ASSERT(data->let.varContext == HoistVars);
3001 MOZ_ASSERT(pc->decls().lookupFirst(name));
3004 return true;
3007 template <>
3008 /* static */ bool
3009 Parser<SyntaxParseHandler>::bindLexical(BindData<SyntaxParseHandler>* data,
3010 HandlePropertyName name, Parser<SyntaxParseHandler>* parser)
3012 if (!parser->checkStrictBinding(name, data->pn))
3013 return false;
3015 return true;
3018 template <typename ParseHandler, class Op>
3019 static inline bool
3020 ForEachLetDef(TokenStream& ts, ParseContext<ParseHandler>* pc,
3021 HandleStaticBlockObject blockObj, Op op)
3023 for (Shape::Range<CanGC> r(ts.context(), blockObj->lastProperty()); !r.empty(); r.popFront()) {
3024 Shape& shape = r.front();
3026 /* Beware the destructuring dummy slots. */
3027 if (JSID_IS_INT(shape.propid()))
3028 continue;
3030 if (!op(ts, pc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
3031 return false;
3033 return true;
3036 template <typename ParseHandler>
3037 struct PopLetDecl {
3038 bool operator()(TokenStream&, ParseContext<ParseHandler>* pc, HandleStaticBlockObject,
3039 const Shape&, JSAtom* atom)
3041 pc->popLetDecl(atom);
3042 return true;
3046 // We compute the maximum block scope depth, in slots, of a compilation unit at
3047 // parse-time. Each nested statement has a field indicating the maximum block
3048 // scope depth that is nested inside it. When we leave a nested statement, we
3049 // add the number of slots in the statement to the nested depth, and use that to
3050 // update the maximum block scope depth of the outer statement or parse
3051 // context. In the end, pc->blockScopeDepth will indicate the number of slots
3052 // to reserve in the fixed part of a stack frame.
3054 template <typename ParseHandler>
3055 static void
3056 AccumulateBlockScopeDepth(ParseContext<ParseHandler>* pc)
3058 uint32_t innerDepth = pc->topStmt->innerBlockScopeDepth;
3059 StmtInfoPC* outer = pc->topStmt->down;
3061 if (pc->topStmt->isBlockScope)
3062 innerDepth += pc->topStmt->staticScope->template as<StaticBlockObject>().numVariables();
3064 if (outer) {
3065 if (outer->innerBlockScopeDepth < innerDepth)
3066 outer->innerBlockScopeDepth = innerDepth;
3067 } else {
3068 if (pc->blockScopeDepth < innerDepth)
3069 pc->blockScopeDepth = innerDepth;
3073 template <typename ParseHandler>
3074 static void
3075 PopStatementPC(TokenStream& ts, ParseContext<ParseHandler>* pc)
3077 RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope);
3078 MOZ_ASSERT(!!scopeObj == pc->topStmt->isNestedScope);
3080 AccumulateBlockScopeDepth(pc);
3081 FinishPopStatement(pc);
3083 if (scopeObj) {
3084 if (scopeObj->is<StaticBlockObject>()) {
3085 RootedStaticBlockObject blockObj(ts.context(), &scopeObj->as<StaticBlockObject>());
3086 MOZ_ASSERT(!blockObj->inDictionaryMode());
3087 ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>());
3089 scopeObj->resetEnclosingNestedScopeFromParser();
3094 * The function LexicalLookup searches a static binding for the given name in
3095 * the stack of statements enclosing the statement currently being parsed. Each
3096 * statement that introduces a new scope has a corresponding scope object, on
3097 * which the bindings for that scope are stored. LexicalLookup either returns
3098 * the innermost statement which has a scope object containing a binding with
3099 * the given name, or nullptr.
3101 template <class ContextT>
3102 typename ContextT::StmtInfo*
3103 LexicalLookup(ContextT* ct, HandleAtom atom, int* slotp, typename ContextT::StmtInfo* stmt)
3105 RootedId id(ct->sc->context, AtomToId(atom));
3107 if (!stmt)
3108 stmt = ct->topScopeStmt;
3109 for (; stmt; stmt = stmt->downScope) {
3111 * With-statements introduce dynamic bindings. Since dynamic bindings
3112 * can potentially override any static bindings introduced by statements
3113 * further up the stack, we have to abort the search.
3115 if (stmt->type == STMT_WITH && !ct->sc->isDotVariable(atom))
3116 break;
3118 // Skip statements that do not introduce a new scope
3119 if (!stmt->isBlockScope)
3120 continue;
3122 StaticBlockObject& blockObj = stmt->staticBlock();
3123 Shape* shape = blockObj.lookup(ct->sc->context, id);
3124 if (shape) {
3125 if (slotp)
3126 *slotp = blockObj.shapeToIndex(*shape);
3127 return stmt;
3131 if (slotp)
3132 *slotp = -1;
3133 return stmt;
3136 template <typename ParseHandler>
3137 static inline bool
3138 OuterLet(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, HandleAtom atom)
3140 while (stmt->downScope) {
3141 stmt = LexicalLookup(pc, atom, nullptr, stmt->downScope);
3142 if (!stmt)
3143 return false;
3144 if (stmt->type == STMT_BLOCK)
3145 return true;
3147 return false;
3150 template <typename ParseHandler>
3151 /* static */ bool
3152 Parser<ParseHandler>::bindVarOrGlobalConst(BindData<ParseHandler>* data,
3153 HandlePropertyName name, Parser<ParseHandler>* parser)
3155 ExclusiveContext* cx = parser->context;
3156 ParseContext<ParseHandler>* pc = parser->pc;
3157 Node pn = data->pn;
3158 bool isConstDecl = data->op == JSOP_DEFCONST;
3160 /* Default best op for pn is JSOP_GETNAME; we'll try to improve below. */
3161 parser->handler.setOp(pn, JSOP_GETNAME);
3163 if (!parser->checkStrictBinding(name, pn))
3164 return false;
3166 StmtInfoPC* stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC*)nullptr);
3168 if (stmt && stmt->type == STMT_WITH) {
3169 parser->handler.setFlag(pn, PND_DEOPTIMIZED);
3170 if (pc->sc->isFunctionBox()) {
3171 FunctionBox* funbox = pc->sc->asFunctionBox();
3172 funbox->setMightAliasLocals();
3176 * This definition isn't being added to the parse context's
3177 * declarations, so make sure to indicate the need to deoptimize
3178 * the script's arguments object. Mark the function as if it
3179 * contained a debugger statement, which will deoptimize arguments
3180 * as much as possible.
3182 if (name == cx->names().arguments)
3183 pc->sc->setHasDebuggerStatement();
3185 return true;
3188 DefinitionList::Range defs = pc->decls().lookupMulti(name);
3189 MOZ_ASSERT_IF(stmt, !defs.empty());
3191 if (defs.empty()) {
3192 return pc->define(parser->tokenStream, name, pn,
3193 isConstDecl ? Definition::GLOBALCONST : Definition::VAR);
3197 * There was a previous declaration with the same name. The standard
3198 * disallows several forms of redeclaration. Critically,
3199 * let (x) { var x; } // error
3200 * is not allowed which allows us to turn any non-error redeclaration
3201 * into a use of the initial declaration.
3203 DefinitionNode dn = defs.front<ParseHandler>();
3204 Definition::Kind dn_kind = parser->handler.getDefinitionKind(dn);
3205 if (dn_kind == Definition::ARG) {
3206 JSAutoByteString bytes;
3207 if (!AtomToPrintableString(cx, name, &bytes))
3208 return false;
3210 if (isConstDecl) {
3211 parser->report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, bytes.ptr());
3212 return false;
3214 if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr()))
3215 return false;
3216 } else {
3217 bool inCatchBody = (stmt && stmt->type == STMT_CATCH);
3218 bool error = (isConstDecl ||
3219 dn_kind == Definition::CONST ||
3220 dn_kind == Definition::GLOBALCONST ||
3221 (dn_kind == Definition::LET &&
3222 (!inCatchBody || OuterLet(pc, stmt, name))));
3224 if (parser->options().extraWarningsOption
3225 ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR
3226 : error)
3228 JSAutoByteString bytes;
3229 if (!AtomToPrintableString(cx, name, &bytes))
3230 return false;
3232 ParseReportKind reporter = error ? ParseError : ParseExtraWarning;
3233 if (!(inCatchBody
3234 ? parser->report(reporter, false, pn,
3235 JSMSG_REDECLARED_CATCH_IDENTIFIER, bytes.ptr())
3236 : parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR,
3237 Definition::kindString(dn_kind), bytes.ptr())))
3239 return false;
3244 parser->handler.linkUseToDef(pn, dn);
3245 return true;
3248 template <>
3249 bool
3250 Parser<FullParseHandler>::makeSetCall(ParseNode* pn, unsigned msg)
3252 MOZ_ASSERT(pn->isKind(PNK_CALL));
3253 MOZ_ASSERT(pn->isArity(PN_LIST));
3254 MOZ_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) ||
3255 pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_STRICTEVAL) ||
3256 pn->isOp(JSOP_SPREADEVAL) || pn->isOp(JSOP_STRICTSPREADEVAL) ||
3257 pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
3259 if (!report(ParseStrictError, pc->sc->strict, pn, msg))
3260 return false;
3261 handler.markAsSetCall(pn);
3262 return true;
3265 template <typename ParseHandler>
3266 bool
3267 Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
3270 * The asm.js validator does all its own symbol-table management so, as an
3271 * optimization, avoid doing any work here. Use-def links are only necessary
3272 * for emitting bytecode and successfully-validated asm.js does not emit
3273 * bytecode. (On validation failure, the asm.js module is reparsed.)
3275 if (pc->useAsmOrInsideUseAsm())
3276 return true;
3278 StmtInfoPC* stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC*)nullptr);
3280 DefinitionList::Range defs = pc->decls().lookupMulti(name);
3282 DefinitionNode dn;
3283 if (!defs.empty()) {
3284 dn = defs.front<ParseHandler>();
3285 } else {
3287 * No definition before this use in any lexical scope.
3288 * Create a placeholder definition node to either:
3289 * - Be adopted when we parse the real defining
3290 * declaration, or
3291 * - Be left as a free variable definition if we never
3292 * see the real definition.
3294 dn = getOrCreateLexicalDependency(pc, name);
3295 if (!dn)
3296 return false;
3299 handler.linkUseToDef(pn, dn);
3301 if (stmt) {
3302 if (stmt->type == STMT_WITH) {
3303 handler.setFlag(pn, PND_DEOPTIMIZED);
3304 } else if (stmt->type == STMT_SWITCH && stmt->isBlockScope) {
3305 // See comments above StmtInfoPC and switchStatement for how
3306 // firstDominatingLexicalInCase is computed.
3307 MOZ_ASSERT(stmt->firstDominatingLexicalInCase <= stmt->staticBlock().numVariables());
3308 handler.markMaybeUninitializedLexicalUseInSwitch(pn, dn,
3309 stmt->firstDominatingLexicalInCase);
3313 return true;
3316 template <>
3317 bool
3318 Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler>* data, ParseNode* pn)
3320 MOZ_ASSERT(pn->isKind(PNK_NAME));
3322 RootedPropertyName name(context, pn->pn_atom->asPropertyName());
3324 data->pn = pn;
3325 if (!data->binder(data, name, this))
3326 return false;
3329 * Select the appropriate name-setting opcode, respecting eager selection
3330 * done by the data->binder function.
3332 if (data->op == JSOP_INITLEXICAL)
3333 pn->setOp(JSOP_INITLEXICAL);
3334 else if (pn->pn_dflags & PND_BOUND)
3335 pn->setOp(JSOP_SETLOCAL);
3336 else if (data->op == JSOP_DEFCONST)
3337 pn->setOp(JSOP_SETCONST);
3338 else
3339 pn->setOp(JSOP_SETNAME);
3341 if (data->op == JSOP_DEFCONST)
3342 pn->pn_dflags |= PND_CONST;
3344 pn->markAsAssigned();
3345 return true;
3348 template <>
3349 bool
3350 Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler>* data, ParseNode* left);
3352 template <>
3353 bool
3354 Parser<FullParseHandler>::checkDestructuringObject(BindData<FullParseHandler>* data,
3355 ParseNode* objectPattern)
3357 MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
3359 for (ParseNode* member = objectPattern->pn_head; member; member = member->pn_next) {
3360 ParseNode* expr;
3361 if (member->isKind(PNK_MUTATEPROTO)) {
3362 expr = member->pn_kid;
3363 } else {
3364 MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
3365 expr = member->pn_right;
3368 bool ok;
3369 if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) {
3370 ok = checkDestructuring(data, expr);
3371 } else if (data) {
3372 if (!expr->isKind(PNK_NAME)) {
3373 report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
3374 return false;
3376 ok = bindDestructuringVar(data, expr);
3377 } else {
3378 ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment);
3380 if (!ok)
3381 return false;
3384 return true;
3387 template <>
3388 bool
3389 Parser<FullParseHandler>::checkDestructuringArray(BindData<FullParseHandler>* data,
3390 ParseNode* arrayPattern)
3392 MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
3394 for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) {
3395 if (element->isKind(PNK_ELISION))
3396 continue;
3398 ParseNode* target = element;
3399 if (target->isKind(PNK_SPREAD)) {
3400 if (target->pn_next) {
3401 report(ParseError, false, target->pn_next, JSMSG_PARAMETER_AFTER_REST);
3402 return false;
3404 target = target->pn_kid;
3406 // The RestElement should not support nested patterns.
3407 if (target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT)) {
3408 report(ParseError, false, target, JSMSG_BAD_DESTRUCT_TARGET);
3409 return false;
3413 bool ok;
3414 if (target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT)) {
3415 ok = checkDestructuring(data, target);
3416 } else {
3417 if (data) {
3418 if (!target->isKind(PNK_NAME)) {
3419 report(ParseError, false, target, JSMSG_NO_VARIABLE_NAME);
3420 return false;
3422 ok = bindDestructuringVar(data, target);
3423 } else {
3424 ok = checkAndMarkAsAssignmentLhs(target, KeyedDestructuringAssignment);
3427 if (!ok)
3428 return false;
3431 return true;
3435 * Destructuring patterns can appear in two kinds of contexts:
3437 * - assignment-like: assignment expressions and |for| loop heads. In
3438 * these cases, the patterns' property value positions can be
3439 * arbitrary lvalue expressions; the destructuring is just a fancy
3440 * assignment.
3442 * - binding-like: |var| and |let| declarations, functions' formal
3443 * parameter lists, |catch| clauses, and comprehension tails. In
3444 * these cases, the patterns' property value positions must be
3445 * simple names; the destructuring defines them as new variables.
3447 * In both cases, other code parses the pattern as an arbitrary
3448 * primaryExpr, and then, here in checkDestructuring, verify that the
3449 * tree is a valid AssignmentPattern or BindingPattern.
3451 * In assignment-like contexts, we parse the pattern with
3452 * pc->inDeclDestructuring clear, so the lvalue expressions in the
3453 * pattern are parsed normally. primaryExpr links variable references
3454 * into the appropriate use chains; creates placeholder definitions;
3455 * and so on. checkDestructuring is called with |data| nullptr (since
3456 * we won't be binding any new names), and we specialize lvalues as
3457 * appropriate.
3459 * In declaration-like contexts, the normal variable reference
3460 * processing would just be an obstruction, because we're going to
3461 * define the names that appear in the property value positions as new
3462 * variables anyway. In this case, we parse the pattern with
3463 * pc->inDeclDestructuring set, which directs primaryExpr to leave
3464 * whatever name nodes it creates unconnected. Then, here in
3465 * checkDestructuring, we require the pattern's property value
3466 * positions to be simple names, and define them as appropriate to the
3467 * context. For these calls, |data| points to the right sort of
3468 * BindData.
3470 template <>
3471 bool
3472 Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler>* data, ParseNode* left)
3474 if (left->isKind(PNK_ARRAYCOMP)) {
3475 report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE);
3476 return false;
3479 if (left->isKind(PNK_ARRAY))
3480 return checkDestructuringArray(data, left);
3481 return checkDestructuringObject(data, left);
3484 template <>
3485 bool
3486 Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler>* data, Node left)
3488 return abortIfSyntaxParser();
3491 template <typename ParseHandler>
3492 typename ParseHandler::Node
3493 Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler>* data, TokenKind tt)
3495 MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
3497 pc->inDeclDestructuring = true;
3498 Node pn = primaryExpr(tt);
3499 pc->inDeclDestructuring = false;
3500 if (!pn)
3501 return null();
3502 if (!checkDestructuring(data, pn))
3503 return null();
3504 return pn;
3507 template <typename ParseHandler>
3508 typename ParseHandler::Node
3509 Parser<ParseHandler>::destructuringExprWithoutYield(BindData<ParseHandler>* data, TokenKind tt,
3510 unsigned msg)
3512 uint32_t startYieldOffset = pc->lastYieldOffset;
3513 Node res = destructuringExpr(data, tt);
3514 if (res && pc->lastYieldOffset != startYieldOffset) {
3515 reportWithOffset(ParseError, false, pc->lastYieldOffset,
3516 msg, js_yield_str);
3517 return null();
3519 return res;
3522 template <typename ParseHandler>
3523 typename ParseHandler::Node
3524 Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC* stmt)
3526 MOZ_ASSERT(blockObj);
3528 ObjectBox* blockbox = newObjectBox(blockObj);
3529 if (!blockbox)
3530 return null();
3532 PushStatementPC(pc, stmt, STMT_BLOCK);
3533 blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
3534 FinishPushNestedScope(pc, stmt, *blockObj.get());
3535 stmt->isBlockScope = true;
3537 Node pn = handler.newLexicalScope(blockbox);
3538 if (!pn)
3539 return null();
3541 if (!GenerateBlockId(tokenStream, pc, stmt->blockid))
3542 return null();
3543 handler.setBlockId(pn, stmt->blockid);
3544 return pn;
3547 template <typename ParseHandler>
3548 typename ParseHandler::Node
3549 Parser<ParseHandler>::pushLexicalScope(StmtInfoPC* stmt)
3551 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
3552 if (!blockObj)
3553 return null();
3555 return pushLexicalScope(blockObj, stmt);
3558 struct AddLetDecl
3560 uint32_t blockid;
3562 explicit AddLetDecl(uint32_t blockid) : blockid(blockid) {}
3564 bool operator()(TokenStream& ts, ParseContext<FullParseHandler>* pc,
3565 HandleStaticBlockObject blockObj, const Shape& shape, JSAtom*)
3567 ParseNode* def = (ParseNode*) blockObj->getSlot(shape.slot()).toPrivate();
3568 def->pn_blockid = blockid;
3569 RootedPropertyName name(ts.context(), def->name());
3570 return pc->define(ts, name, def, Definition::LET);
3574 template <>
3575 ParseNode*
3576 Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC* stmt)
3578 MOZ_ASSERT(blockObj);
3579 ParseNode* pn = pushLexicalScope(blockObj, stmt);
3580 if (!pn)
3581 return null();
3583 pn->pn_dflags |= PND_LEXICAL;
3585 /* Populate the new scope with decls found in the head with updated blockid. */
3586 if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid)))
3587 return null();
3589 return pn;
3592 template <>
3593 SyntaxParseHandler::Node
3594 Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC* stmt)
3596 JS_ALWAYS_FALSE(abortIfSyntaxParser());
3597 return SyntaxParseHandler::NodeFailure;
3601 * Parse a let block statement or let expression (determined by 'letContext').
3602 * In both cases, bindings are not hoisted to the top of the enclosing block
3603 * and thus must be carefully injected between variables() and the let body.
3605 template <typename ParseHandler>
3606 typename ParseHandler::Node
3607 Parser<ParseHandler>::letBlock(LetContext letContext)
3609 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
3611 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
3612 if (!blockObj)
3613 return null();
3615 uint32_t begin = pos().begin;
3617 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
3619 Node vars = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
3620 if (!vars)
3621 return null();
3623 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
3625 StmtInfoPC stmtInfo(context);
3626 Node block = pushLetScope(blockObj, &stmtInfo);
3627 if (!block)
3628 return null();
3630 Node pnlet = handler.newBinary(PNK_LET, vars, block);
3631 if (!pnlet)
3632 return null();
3633 handler.setBeginPosition(pnlet, begin);
3635 bool needExprStmt = false;
3636 if (letContext == LetStatement) {
3637 bool matched;
3638 if (!tokenStream.matchToken(&matched, TOK_LC, TokenStream::Operand))
3639 return null();
3640 if (!matched) {
3642 * Strict mode eliminates a grammar ambiguity with unparenthesized
3643 * LetExpressions in an ExpressionStatement. If followed immediately
3644 * by an arguments list, it's ambiguous whether the let expression
3645 * is the callee or the call is inside the let expression body.
3647 * function id(x) { return x; }
3648 * var x = "outer";
3649 * // Does this parse as
3650 * // (let (loc = "inner") id)(loc) // "outer"
3651 * // or as
3652 * // let (loc = "inner") (id(loc)) // "inner"
3653 * let (loc = "inner") id(loc);
3655 * See bug 569464.
3657 if (!report(ParseStrictError, pc->sc->strict, pnlet,
3658 JSMSG_STRICT_CODE_LET_EXPR_STMT))
3660 return null();
3664 * If this is really an expression in let statement guise, then we
3665 * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
3666 * the return value of the expression.
3668 needExprStmt = true;
3669 letContext = LetExpression;
3673 Node expr;
3674 if (letContext == LetStatement) {
3675 expr = statements();
3676 if (!expr)
3677 return null();
3678 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
3680 sawDeprecatedLetBlock = true;
3681 if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_BLOCK))
3682 return null();
3683 } else {
3684 MOZ_ASSERT(letContext == LetExpression);
3685 expr = assignExpr();
3686 if (!expr)
3687 return null();
3689 sawDeprecatedLetExpression = true;
3690 if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_EXPRESSION))
3691 return null();
3693 handler.setLexicalScopeBody(block, expr);
3694 PopStatementPC(tokenStream, pc);
3696 handler.setEndPosition(pnlet, pos().end);
3698 if (needExprStmt) {
3699 if (!MatchOrInsertSemicolon(tokenStream))
3700 return null();
3701 return handler.newExprStatement(pnlet, pos().end);
3703 return pnlet;
3706 template <typename ParseHandler>
3707 static bool
3708 PushBlocklikeStatement(TokenStream& ts, StmtInfoPC* stmt, StmtType type,
3709 ParseContext<ParseHandler>* pc)
3711 PushStatementPC(pc, stmt, type);
3712 return GenerateBlockId(ts, pc, stmt->blockid);
3715 template <typename ParseHandler>
3716 typename ParseHandler::Node
3717 Parser<ParseHandler>::blockStatement()
3719 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
3721 StmtInfoPC stmtInfo(context);
3722 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_BLOCK, pc))
3723 return null();
3725 Node list = statements();
3726 if (!list)
3727 return null();
3729 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
3730 PopStatementPC(tokenStream, pc);
3731 return list;
3734 template <typename ParseHandler>
3735 typename ParseHandler::Node
3736 Parser<ParseHandler>::newBindingNode(PropertyName* name, bool functionScope, VarContext varContext)
3739 * If this name is being injected into an existing block/function, see if
3740 * it has already been declared or if it resolves an outstanding lexdep.
3741 * Otherwise, this is a let block/expr that introduces a new scope and thus
3742 * shadows existing decls and doesn't resolve existing lexdeps. Duplicate
3743 * names are caught by bindLet.
3745 if (varContext == HoistVars) {
3746 if (AtomDefnPtr p = pc->lexdeps->lookup(name)) {
3747 DefinitionNode lexdep = p.value().get<ParseHandler>();
3748 MOZ_ASSERT(handler.getDefinitionKind(lexdep) == Definition::PLACEHOLDER);
3750 Node pn = handler.getDefinitionNode(lexdep);
3751 if (handler.dependencyCovered(pn, pc->blockid(), functionScope)) {
3752 handler.setBlockId(pn, pc->blockid());
3753 pc->lexdeps->remove(p);
3754 handler.setPosition(pn, pos());
3755 return pn;
3760 /* Make a new node for this declarator name (or destructuring pattern). */
3761 return newName(name);
3765 * The 'blockObj' parameter is non-null when parsing the 'vars' in a let
3766 * expression, block statement, non-top-level let declaration in statement
3767 * context, and the let-initializer of a for-statement.
3769 template <typename ParseHandler>
3770 typename ParseHandler::Node
3771 Parser<ParseHandler>::variables(ParseNodeKind kind, bool* psimple,
3772 StaticBlockObject* blockObj, VarContext varContext)
3775 * The four options here are:
3776 * - PNK_VAR: We're parsing var declarations.
3777 * - PNK_CONST: We're parsing const declarations.
3778 * - PNK_GLOBALCONST: We're parsing const declarations at toplevel (see bug 589119).
3779 * - PNK_LET: We are parsing a let declaration.
3781 MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_GLOBALCONST);
3784 * The simple flag is set if the declaration has the form 'var x', with
3785 * only one variable declared and no initializer expression.
3787 MOZ_ASSERT_IF(psimple, *psimple);
3789 JSOp op = JSOP_NOP;
3790 if (kind == PNK_VAR)
3791 op = JSOP_DEFVAR;
3792 else if (kind == PNK_GLOBALCONST)
3793 op = JSOP_DEFCONST;
3795 Node pn = handler.newList(kind, null(), op);
3796 if (!pn)
3797 return null();
3800 * SpiderMonkey const is really "write once per initialization evaluation"
3801 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
3802 * this code will change soon.
3804 BindData<ParseHandler> data(context);
3805 if (kind == PNK_VAR || kind == PNK_GLOBALCONST) {
3806 data.initVarOrGlobalConst(op);
3807 } else {
3808 data.initLexical(varContext, blockObj, JSMSG_TOO_MANY_LOCALS,
3809 /* isConst = */ kind == PNK_CONST);
3812 bool first = true;
3813 Node pn2;
3814 while (true) {
3815 do {
3816 if (psimple && !first)
3817 *psimple = false;
3818 first = false;
3820 TokenKind tt;
3821 if (!tokenStream.getToken(&tt))
3822 return null();
3823 if (tt == TOK_LB || tt == TOK_LC) {
3824 if (psimple)
3825 *psimple = false;
3827 pc->inDeclDestructuring = true;
3828 pn2 = primaryExpr(tt);
3829 pc->inDeclDestructuring = false;
3830 if (!pn2)
3831 return null();
3833 bool parsingForInOrOfInit = false;
3834 if (pc->parsingForInit) {
3835 bool isForIn, isForOf;
3836 if (!matchInOrOf(&isForIn, &isForOf))
3837 return null();
3838 parsingForInOrOfInit = isForIn || isForOf;
3841 // See comment below for bindBeforeInitializer in the code that
3842 // handles the non-destructuring case.
3843 bool bindBeforeInitializer = (kind != PNK_LET && kind != PNK_CONST) ||
3844 parsingForInOrOfInit;
3845 if (bindBeforeInitializer && !checkDestructuring(&data, pn2))
3846 return null();
3848 if (parsingForInOrOfInit) {
3849 tokenStream.ungetToken();
3850 handler.addList(pn, pn2);
3851 break;
3854 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
3856 Node init = assignExpr();
3857 if (!init)
3858 return null();
3860 if (!bindBeforeInitializer && !checkDestructuring(&data, pn2))
3861 return null();
3863 pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
3864 if (!pn2)
3865 return null();
3866 handler.addList(pn, pn2);
3867 break;
3870 if (tt != TOK_NAME) {
3871 if (tt == TOK_YIELD) {
3872 if (!checkYieldNameValidity())
3873 return null();
3874 } else {
3875 report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
3876 return null();
3880 RootedPropertyName name(context, tokenStream.currentName());
3881 pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_GLOBALCONST, varContext);
3882 if (!pn2)
3883 return null();
3884 if (data.isConst)
3885 handler.setFlag(pn2, PND_CONST);
3886 data.pn = pn2;
3888 handler.addList(pn, pn2);
3890 bool matched;
3891 if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
3892 return null();
3893 if (matched) {
3894 if (psimple)
3895 *psimple = false;
3897 // In ES6, lexical bindings may not be accessed until
3898 // initialized. So a declaration of the form |let x = x| results
3899 // in a ReferenceError, as the 'x' on the RHS is accessing the let
3900 // binding before it is initialized.
3902 // If we are not parsing a let declaration, bind the name
3903 // now. Otherwise we must wait until after parsing the initializing
3904 // assignment.
3905 bool bindBeforeInitializer = kind != PNK_LET && kind != PNK_CONST;
3906 if (bindBeforeInitializer && !data.binder(&data, name, this))
3907 return null();
3909 Node init = assignExpr();
3910 if (!init)
3911 return null();
3913 if (!bindBeforeInitializer && !data.binder(&data, name, this))
3914 return null();
3916 if (!handler.finishInitializerAssignment(pn2, init, data.op))
3917 return null();
3918 } else {
3919 if (data.isConst && !pc->parsingForInit) {
3920 report(ParseError, false, null(), JSMSG_BAD_CONST_DECL);
3921 return null();
3924 if (!data.binder(&data, name, this))
3925 return null();
3927 } while (false);
3929 bool matched;
3930 if (!tokenStream.matchToken(&matched, TOK_COMMA))
3931 return null();
3932 if (!matched)
3933 break;
3936 return pn;
3939 template <>
3940 ParseNode*
3941 Parser<FullParseHandler>::lexicalDeclaration(bool isConst)
3943 handler.disableSyntaxParser();
3945 ParseNode* pn;
3947 do {
3949 * This is a let declaration. We must be directly under a block per the
3950 * proposed ES4 specs, but not an implicit block created due to
3951 * 'for (let ...)'. If we pass this error test, make the enclosing
3952 * StmtInfoPC be our scope. Further let declarations in this block will
3953 * find this scope statement and use the same block object.
3955 * If we are the first let declaration in this block (i.e., when the
3956 * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
3957 * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
3959 StmtInfoPC* stmt = pc->topStmt;
3960 if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
3961 report(ParseError, false, null(), JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
3962 isConst ? "const" : "let");
3963 return null();
3966 if (stmt && stmt->isBlockScope) {
3967 MOZ_ASSERT(pc->staticScope == stmt->staticScope);
3968 } else {
3969 if (pc->atBodyLevel()) {
3971 * When bug 589199 is fixed, let variables will be stored in
3972 * the slots of a new scope chain object, encountered just
3973 * before the global object in the overall chain. This extra
3974 * object is present in the scope chain for all code in that
3975 * global, including self-hosted code. But self-hosted code
3976 * must be usable against *any* global object, including ones
3977 * with other let variables -- variables possibly placed in
3978 * conflicting slots. Forbid top-level let declarations to
3979 * prevent such conflicts from ever occurring.
3981 bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->topScopeStmt;
3982 if (options().selfHostingMode && isGlobal) {
3983 report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL,
3984 isConst ? "'const'" : "'let'");
3985 return null();
3989 * Parse body-level lets without a new block object. ES6 specs
3990 * that an execution environment's initial lexical environment
3991 * is the VariableEnvironment, i.e., body-level lets are in
3992 * the same environment record as vars.
3994 * However, they cannot be parsed exactly as vars, as ES6
3995 * requires that uninitialized lets throw ReferenceError on use.
3997 * See 8.1.1.1.6 and the note in 13.2.1.
3999 * FIXME global-level lets are still considered vars until
4000 * other bugs are fixed.
4002 ParseNodeKind kind = PNK_LET;
4003 if (isGlobal)
4004 kind = isConst ? PNK_GLOBALCONST : PNK_VAR;
4005 else if (isConst)
4006 kind = PNK_CONST;
4007 pn = variables(kind);
4008 if (!pn)
4009 return null();
4010 pn->pn_xflags |= PNX_POPVAR;
4011 break;
4015 * Some obvious assertions here, but they may help clarify the
4016 * situation. This stmt is not yet a scope, so it must not be a
4017 * catch block (catch is a lexical scope by definition).
4019 MOZ_ASSERT(!stmt->isBlockScope);
4020 MOZ_ASSERT(stmt != pc->topScopeStmt);
4021 MOZ_ASSERT(stmt->type == STMT_BLOCK ||
4022 stmt->type == STMT_SWITCH ||
4023 stmt->type == STMT_TRY ||
4024 stmt->type == STMT_FINALLY);
4025 MOZ_ASSERT(!stmt->downScope);
4027 /* Convert the block statement into a scope statement. */
4028 StaticBlockObject* blockObj = StaticBlockObject::create(context);
4029 if (!blockObj)
4030 return null();
4032 ObjectBox* blockbox = newObjectBox(blockObj);
4033 if (!blockbox)
4034 return null();
4037 * Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked
4038 * list stack, if it isn't already there. If it is there, but it
4039 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
4040 * block.
4042 stmt->isBlockScope = stmt->isNestedScope = true;
4043 stmt->downScope = pc->topScopeStmt;
4044 pc->topScopeStmt = stmt;
4046 blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
4047 pc->staticScope = blockObj;
4048 stmt->staticScope = blockObj;
4050 #ifdef DEBUG
4051 ParseNode* tmp = pc->blockNode;
4052 MOZ_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
4053 #endif
4055 /* Create a new lexical scope node for these statements. */
4056 ParseNode* pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler);
4057 if (!pn1)
4058 return null();
4060 pn1->pn_pos = pc->blockNode->pn_pos;
4061 pn1->pn_objbox = blockbox;
4062 pn1->pn_expr = pc->blockNode;
4063 pn1->pn_blockid = pc->blockNode->pn_blockid;
4064 pc->blockNode = pn1;
4067 pn = variables(isConst ? PNK_CONST : PNK_LET, nullptr,
4068 &pc->staticScope->as<StaticBlockObject>(), HoistVars);
4069 if (!pn)
4070 return null();
4071 pn->pn_xflags = PNX_POPVAR;
4072 } while (0);
4074 return MatchOrInsertSemicolon(tokenStream) ? pn : nullptr;
4077 template <>
4078 SyntaxParseHandler::Node
4079 Parser<SyntaxParseHandler>::lexicalDeclaration(bool)
4081 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4082 return SyntaxParseHandler::NodeFailure;
4085 template <>
4086 ParseNode*
4087 Parser<FullParseHandler>::letStatement()
4089 handler.disableSyntaxParser();
4091 /* Check for a let statement or let expression. */
4092 ParseNode* pn;
4093 TokenKind tt;
4094 if (!tokenStream.peekToken(&tt))
4095 return null();
4096 if (tt == TOK_LP) {
4097 pn = letBlock(LetStatement);
4098 MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
4099 } else {
4100 pn = lexicalDeclaration(/* isConst = */ false);
4102 return pn;
4105 template <>
4106 SyntaxParseHandler::Node
4107 Parser<SyntaxParseHandler>::letStatement()
4109 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4110 return SyntaxParseHandler::NodeFailure;
4113 template<typename ParseHandler>
4114 typename ParseHandler::Node
4115 Parser<ParseHandler>::importDeclaration()
4117 MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
4119 if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
4120 report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
4121 return null();
4124 uint32_t begin = pos().begin;
4125 TokenKind tt;
4126 if (!tokenStream.getToken(&tt))
4127 return null();
4129 Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
4130 if (!importSpecSet)
4131 return null();
4133 if (tt == TOK_NAME || tt == TOK_LC) {
4134 if (tt == TOK_NAME) {
4135 // Handle the form |import a from 'b'|, by adding a single import
4136 // specifier to the list, with 'default' as the import name and
4137 // 'a' as the binding name. This is equivalent to
4138 // |import { default as a } from 'b'|.
4139 Node importName = newName(context->names().default_);
4140 if (!importName)
4141 return null();
4143 Node bindingName = newName(tokenStream.currentName());
4144 if (!bindingName)
4145 return null();
4147 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
4148 if (!importSpec)
4149 return null();
4151 handler.addList(importSpecSet, importSpec);
4152 } else {
4153 while (true) {
4154 // Handle the forms |import {} from 'a'| and
4155 // |import { ..., } from 'a'| (where ... is non empty), by
4156 // escaping the loop early if the next token is }.
4157 if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
4158 return null();
4159 if (tt == TOK_RC)
4160 break;
4162 // If the next token is a keyword, the previous call to
4163 // peekToken matched it as a TOK_NAME, and put it in the
4164 // lookahead buffer, so this call will match keywords as well.
4165 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
4166 Node importName = newName(tokenStream.currentName());
4167 if (!importName)
4168 return null();
4170 if (!tokenStream.getToken(&tt))
4171 return null();
4172 if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
4173 if (!tokenStream.getToken(&tt))
4174 return null();
4175 if (tt != TOK_NAME) {
4176 report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
4177 return null();
4179 } else {
4180 // Keywords cannot be bound to themselves, so an import name
4181 // that is a keyword is a syntax error if it is not followed
4182 // by the keyword 'as'.
4183 if (IsKeyword(importName->name())) {
4184 JSAutoByteString bytes;
4185 if (!AtomToPrintableString(context, importName->name(), &bytes))
4186 return null();
4187 report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
4188 return null();
4190 tokenStream.ungetToken();
4192 Node bindingName = newName(tokenStream.currentName());
4193 if (!bindingName)
4194 return null();
4196 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
4197 if (!importSpec)
4198 return null();
4200 handler.addList(importSpecSet, importSpec);
4202 bool matched;
4203 if (!tokenStream.matchToken(&matched, TOK_COMMA))
4204 return null();
4205 if (!matched)
4206 break;
4209 MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
4212 if (!tokenStream.getToken(&tt))
4213 return null();
4214 if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
4215 report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET);
4216 return null();
4219 MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
4220 } else {
4221 if (tt != TOK_STRING) {
4222 report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
4223 return null();
4226 // Handle the form |import 'a'| by leaving the list empty. This is
4227 // equivalent to |import {} from 'a'|.
4228 importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
4231 Node moduleSpec = stringLiteral();
4232 if (!moduleSpec)
4233 return null();
4235 if (!MatchOrInsertSemicolon(tokenStream))
4236 return null();
4238 return handler.newImportDeclaration(importSpecSet, moduleSpec,
4239 TokenPos(begin, pos().end));
4242 template<>
4243 SyntaxParseHandler::Node
4244 Parser<SyntaxParseHandler>::importDeclaration()
4246 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4247 return SyntaxParseHandler::NodeFailure;
4250 template<typename ParseHandler>
4251 typename ParseHandler::Node
4252 Parser<ParseHandler>::exportDeclaration()
4254 MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
4256 if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
4257 report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
4258 return null();
4261 uint32_t begin = pos().begin;
4263 Node kid;
4264 TokenKind tt;
4265 if (!tokenStream.getToken(&tt))
4266 return null();
4267 switch (tt) {
4268 case TOK_LC:
4269 case TOK_MUL:
4270 kid = handler.newList(PNK_EXPORT_SPEC_LIST);
4271 if (!kid)
4272 return null();
4274 if (tt == TOK_LC) {
4275 while (true) {
4276 // Handle the forms |export {}| and |export { ..., }| (where ...
4277 // is non empty), by escaping the loop early if the next token
4278 // is }.
4279 if (!tokenStream.peekToken(&tt))
4280 return null();
4281 if (tt == TOK_RC)
4282 break;
4284 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
4285 Node bindingName = newName(tokenStream.currentName());
4286 if (!bindingName)
4287 return null();
4289 if (!tokenStream.getToken(&tt))
4290 return null();
4291 if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
4292 if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
4293 return null();
4294 if (tt != TOK_NAME) {
4295 report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME);
4296 return null();
4298 } else {
4299 tokenStream.ungetToken();
4301 Node exportName = newName(tokenStream.currentName());
4302 if (!exportName)
4303 return null();
4305 Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
4306 if (!exportSpec)
4307 return null();
4309 handler.addList(kid, exportSpec);
4311 bool matched;
4312 if (!tokenStream.matchToken(&matched, TOK_COMMA))
4313 return null();
4314 if (!matched)
4315 break;
4318 MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
4319 } else {
4320 // Handle the form |export *| by adding a special export batch
4321 // specifier to the list.
4322 Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
4323 if (!kid)
4324 return null();
4326 handler.addList(kid, exportSpec);
4328 if (!tokenStream.getToken(&tt))
4329 return null();
4330 if (tt == TOK_NAME && tokenStream.currentName() == context->names().from) {
4331 MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
4333 Node moduleSpec = stringLiteral();
4334 if (!moduleSpec)
4335 return null();
4337 if (!MatchOrInsertSemicolon(tokenStream))
4338 return null();
4340 return handler.newExportFromDeclaration(begin, kid, moduleSpec);
4341 } else {
4342 tokenStream.ungetToken();
4345 kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr;
4346 if (!kid)
4347 return null();
4348 break;
4350 case TOK_FUNCTION:
4351 kid = functionStmt();
4352 if (!kid)
4353 return null();
4354 break;
4356 case TOK_VAR:
4357 kid = variables(PNK_VAR);
4358 if (!kid)
4359 return null();
4360 kid->pn_xflags = PNX_POPVAR;
4362 kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr;
4363 if (!kid)
4364 return null();
4365 break;
4367 case TOK_NAME:
4368 // Handle the form |export a| in the same way as |export let a|, by
4369 // acting as if we've just seen the let keyword. Simply unget the token
4370 // and fall through.
4371 tokenStream.ungetToken();
4372 case TOK_LET:
4373 case TOK_CONST:
4374 kid = lexicalDeclaration(tt == TOK_CONST);
4375 if (!kid)
4376 return null();
4377 break;
4379 default:
4380 report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
4381 return null();
4384 return handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
4387 template<>
4388 SyntaxParseHandler::Node
4389 Parser<SyntaxParseHandler>::exportDeclaration()
4391 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4392 return SyntaxParseHandler::NodeFailure;
4395 template <typename ParseHandler>
4396 typename ParseHandler::Node
4397 Parser<ParseHandler>::expressionStatement()
4399 tokenStream.ungetToken();
4400 Node pnexpr = expr();
4401 if (!pnexpr)
4402 return null();
4403 if (!MatchOrInsertSemicolon(tokenStream))
4404 return null();
4405 return handler.newExprStatement(pnexpr, pos().end);
4408 template <typename ParseHandler>
4409 typename ParseHandler::Node
4410 Parser<ParseHandler>::ifStatement()
4412 uint32_t begin = pos().begin;
4414 /* An IF node has three kids: condition, then, and optional else. */
4415 Node cond = condition();
4416 if (!cond)
4417 return null();
4419 TokenKind tt;
4420 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
4421 return null();
4422 if (tt == TOK_SEMI) {
4423 if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
4424 return null();
4427 StmtInfoPC stmtInfo(context);
4428 PushStatementPC(pc, &stmtInfo, STMT_IF);
4429 Node thenBranch = statement();
4430 if (!thenBranch)
4431 return null();
4433 Node elseBranch;
4434 bool matched;
4435 if (!tokenStream.matchToken(&matched, TOK_ELSE, TokenStream::Operand))
4436 return null();
4437 if (matched) {
4438 stmtInfo.type = STMT_ELSE;
4439 elseBranch = statement();
4440 if (!elseBranch)
4441 return null();
4442 } else {
4443 elseBranch = null();
4446 PopStatementPC(tokenStream, pc);
4447 return handler.newIfStatement(begin, cond, thenBranch, elseBranch);
4450 template <typename ParseHandler>
4451 typename ParseHandler::Node
4452 Parser<ParseHandler>::doWhileStatement()
4454 uint32_t begin = pos().begin;
4455 StmtInfoPC stmtInfo(context);
4456 PushStatementPC(pc, &stmtInfo, STMT_DO_LOOP);
4457 Node body = statement();
4458 if (!body)
4459 return null();
4460 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4461 Node cond = condition();
4462 if (!cond)
4463 return null();
4464 PopStatementPC(tokenStream, pc);
4466 // The semicolon after do-while is even more optional than most
4467 // semicolons in JS. Web compat required this by 2004:
4468 // http://bugzilla.mozilla.org/show_bug.cgi?id=238945
4469 // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
4470 // https://bugs.ecmascript.org/show_bug.cgi?id=157
4471 bool ignored;
4472 if (!tokenStream.matchToken(&ignored, TOK_SEMI))
4473 return null();
4474 return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
4477 template <typename ParseHandler>
4478 typename ParseHandler::Node
4479 Parser<ParseHandler>::whileStatement()
4481 uint32_t begin = pos().begin;
4482 StmtInfoPC stmtInfo(context);
4483 PushStatementPC(pc, &stmtInfo, STMT_WHILE_LOOP);
4484 Node cond = condition();
4485 if (!cond)
4486 return null();
4487 Node body = statement();
4488 if (!body)
4489 return null();
4490 PopStatementPC(tokenStream, pc);
4491 return handler.newWhileStatement(begin, cond, body);
4494 template <typename ParseHandler>
4495 bool
4496 Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
4498 TokenKind tt;
4499 if (!tokenStream.getToken(&tt))
4500 return false;
4501 *isForInp = tt == TOK_IN;
4502 *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of;
4503 if (!*isForInp && !*isForOfp)
4504 tokenStream.ungetToken();
4505 return true;
4508 template <>
4509 bool
4510 Parser<FullParseHandler>::isValidForStatementLHS(ParseNode* pn1, JSVersion version,
4511 bool isForDecl, bool isForEach,
4512 ParseNodeKind headKind)
4514 if (isForDecl) {
4515 if (pn1->pn_count > 1)
4516 return false;
4517 if (pn1->isKind(PNK_CONST))
4518 return false;
4520 // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
4521 // Hence all other destructuring decls are banned there.
4522 if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) {
4523 ParseNode* lhs = pn1->pn_head;
4524 if (lhs->isKind(PNK_ASSIGN))
4525 lhs = lhs->pn_left;
4527 if (lhs->isKind(PNK_OBJECT))
4528 return false;
4529 if (lhs->isKind(PNK_ARRAY) && lhs->pn_count != 2)
4530 return false;
4532 return true;
4535 switch (pn1->getKind()) {
4536 case PNK_NAME:
4537 case PNK_DOT:
4538 case PNK_CALL:
4539 case PNK_ELEM:
4540 return true;
4542 case PNK_ARRAY:
4543 case PNK_OBJECT:
4544 // In JS 1.7 only, for ([K, V] in EXPR) has a special meaning.
4545 // Hence all other destructuring left-hand sides are banned there.
4546 if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN)
4547 return pn1->isKind(PNK_ARRAY) && pn1->pn_count == 2;
4548 return true;
4550 default:
4551 return false;
4555 template <>
4556 bool
4557 Parser<FullParseHandler>::checkForHeadConstInitializers(ParseNode* pn1)
4559 if (!pn1->isKind(PNK_CONST))
4560 return true;
4562 for (ParseNode* assign = pn1->pn_head; assign; assign = assign->pn_next) {
4563 MOZ_ASSERT(assign->isKind(PNK_ASSIGN) || assign->isKind(PNK_NAME));
4564 if (assign->isKind(PNK_NAME) && !assign->isAssigned())
4565 return false;
4566 // PNK_ASSIGN nodes (destructuring assignment) are always assignments.
4568 return true;
4571 template <>
4572 ParseNode*
4573 Parser<FullParseHandler>::forStatement()
4575 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
4576 uint32_t begin = pos().begin;
4578 StmtInfoPC forStmt(context);
4579 PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
4581 bool isForEach = false;
4582 unsigned iflags = 0;
4584 if (allowsForEachIn()) {
4585 bool matched;
4586 if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
4587 return null();
4588 if (matched) {
4589 iflags = JSITER_FOREACH;
4590 isForEach = true;
4591 sawDeprecatedForEach = true;
4592 if (versionNumber() < JSVERSION_LATEST) {
4593 if (!report(ParseWarning, pc->sc->strict, null(), JSMSG_DEPRECATED_FOR_EACH))
4594 return null();
4599 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4602 * True if we have 'for (var/let/const ...)', except in the oddball case
4603 * where 'let' begins a let-expression in 'for (let (...) ...)'.
4605 bool isForDecl = false;
4607 /* Non-null when isForDecl is true for a 'for (let ...)' statement. */
4608 RootedStaticBlockObject blockObj(context);
4610 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
4611 ParseNode* pn1;
4614 TokenKind tt;
4615 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
4616 return null();
4617 if (tt == TOK_SEMI) {
4618 pn1 = nullptr;
4619 } else {
4621 * Set pn1 to a var list or an initializing expression.
4623 * Set the parsingForInit flag during parsing of the first clause
4624 * of the for statement. This flag will be used by the RelExpr
4625 * production; if it is set, then the 'in' keyword will not be
4626 * recognized as an operator, leaving it available to be parsed as
4627 * part of a for/in loop.
4629 * A side effect of this restriction is that (unparenthesized)
4630 * expressions involving an 'in' operator are illegal in the init
4631 * clause of an ordinary for loop.
4633 pc->parsingForInit = true;
4634 if (tt == TOK_VAR) {
4635 isForDecl = true;
4636 tokenStream.consumeKnownToken(tt);
4637 pn1 = variables(PNK_VAR);
4638 } else if (tt == TOK_LET || tt == TOK_CONST) {
4639 handler.disableSyntaxParser();
4640 bool constDecl = tt == TOK_CONST;
4641 tokenStream.consumeKnownToken(tt);
4642 if (!tokenStream.peekToken(&tt))
4643 return null();
4644 if (tt == TOK_LP) {
4645 pn1 = letBlock(LetExpression);
4646 } else {
4647 isForDecl = true;
4648 blockObj = StaticBlockObject::create(context);
4649 if (!blockObj)
4650 return null();
4651 pn1 = variables(constDecl ? PNK_CONST : PNK_LET, nullptr, blockObj,
4652 DontHoistVars);
4654 } else {
4655 pn1 = expr();
4657 pc->parsingForInit = false;
4658 if (!pn1)
4659 return null();
4663 MOZ_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST));
4664 MOZ_ASSERT(!!blockObj == (isForDecl && pn1->isOp(JSOP_NOP)));
4666 // The form 'for (let <vars>; <expr2>; <expr3>) <stmt>' generates an
4667 // implicit block even if stmt is not a BlockStatement.
4668 // If the loop has that exact form, then:
4669 // - forLetImpliedBlock is the node for the implicit block scope.
4670 // - forLetDecl is the node for the decl 'let <vars>'.
4671 // Otherwise both are null.
4672 ParseNode* forLetImpliedBlock = nullptr;
4673 ParseNode* forLetDecl = nullptr;
4675 // If non-null, the node for the decl 'var v = expr1' in the weirdo form
4676 // 'for (var v = expr1 in expr2) stmt'.
4677 ParseNode* hoistedVar = nullptr;
4680 * We can be sure that it's a for/in loop if there's still an 'in'
4681 * keyword here, even if JavaScript recognizes 'in' as an operator,
4682 * as we've excluded 'in' from being parsed in RelExpr by setting
4683 * pc->parsingForInit.
4685 StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */
4686 ParseNode* pn2, *pn3; /* forHead->pn_kid2 and pn_kid3. */
4687 ParseNodeKind headKind = PNK_FORHEAD;
4688 if (pn1) {
4689 bool isForIn, isForOf;
4690 if (!matchInOrOf(&isForIn, &isForOf))
4691 return null();
4692 if (isForIn)
4693 headKind = PNK_FORIN;
4694 else if (isForOf)
4695 headKind = PNK_FOROF;
4698 if (headKind == PNK_FOROF || headKind == PNK_FORIN) {
4700 * Parse the rest of the for/in or for/of head.
4702 * Here pn1 is everything to the left of 'in' or 'of'. At the end of
4703 * this block, pn1 is a decl or nullptr, pn2 is the assignment target
4704 * that receives the enumeration value each iteration, and pn3 is the
4705 * rhs of 'in'.
4707 if (headKind == PNK_FOROF) {
4708 forStmt.type = STMT_FOR_OF_LOOP;
4709 forStmt.type = (headKind == PNK_FOROF) ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
4710 if (isForEach) {
4711 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
4712 return null();
4714 } else {
4715 forStmt.type = STMT_FOR_IN_LOOP;
4716 iflags |= JSITER_ENUMERATE;
4719 /* Check that the left side of the 'in' or 'of' is valid. */
4720 if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, headKind)) {
4721 report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE);
4722 return null();
4726 * After the following if-else, pn2 will point to the name or
4727 * destructuring pattern on in's left. pn1 will point to the decl, if
4728 * any, else nullptr. Note that the "declaration with initializer" case
4729 * rewrites the loop-head, moving the decl and setting pn1 to nullptr.
4731 if (isForDecl) {
4732 pn2 = pn1->pn_head;
4733 if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) {
4735 * Declaration with initializer.
4737 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
4738 * 'const' to hoist the initializer or the entire decl out of
4739 * the loop head.
4741 if (headKind == PNK_FOROF) {
4742 report(ParseError, false, pn2, JSMSG_INVALID_FOR_OF_INIT);
4743 return null();
4745 if (blockObj) {
4746 report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT);
4747 return null();
4750 hoistedVar = pn1;
4753 * All of 'var x = i' is hoisted above 'for (x in o)'.
4755 * Request JSOP_POP here since the var is for a simple
4756 * name (it is not a destructuring binding's left-hand
4757 * side) and it has an initializer.
4759 pn1->pn_xflags |= PNX_POPVAR;
4760 pn1 = nullptr;
4762 if (pn2->isKind(PNK_ASSIGN)) {
4763 pn2 = pn2->pn_left;
4764 MOZ_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) ||
4765 pn2->isKind(PNK_NAME));
4768 } else {
4769 /* Not a declaration. */
4770 MOZ_ASSERT(!blockObj);
4771 pn2 = pn1;
4772 pn1 = nullptr;
4774 if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
4775 return null();
4778 pn3 = (headKind == PNK_FOROF) ? assignExpr() : expr();
4779 if (!pn3)
4780 return null();
4782 if (blockObj) {
4784 * Now that the pn3 has been parsed, push the let scope. To hold
4785 * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node
4786 * created by PushLetScope around the for's initializer. This also
4787 * serves to indicate the let-decl to the emitter.
4789 ParseNode* block = pushLetScope(blockObj, &letStmt);
4790 if (!block)
4791 return null();
4792 letStmt.isForLetBlock = true;
4793 block->pn_expr = pn1;
4794 block->pn_pos = pn1->pn_pos;
4795 pn1 = block;
4798 if (isForDecl) {
4800 * pn2 is part of a declaration. Make a copy that can be passed to
4801 * EmitAssignment. Take care to do this after PushLetScope.
4803 pn2 = cloneLeftHandSide(pn2);
4804 if (!pn2)
4805 return null();
4808 switch (pn2->getKind()) {
4809 case PNK_NAME:
4810 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4811 pn2->markAsAssigned();
4812 break;
4814 case PNK_ASSIGN:
4815 MOZ_CRASH("forStatement TOK_ASSIGN");
4817 case PNK_ARRAY:
4818 case PNK_OBJECT:
4819 if (versionNumber() == JSVERSION_1_7) {
4821 * Destructuring for-in requires [key, value] enumeration
4822 * in JS1.7.
4824 if (!isForEach && headKind == PNK_FORIN) {
4825 iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
4826 sawDeprecatedDestructuringForIn = true;
4829 break;
4831 default:;
4833 } else {
4834 if (isForEach) {
4835 reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP);
4836 return null();
4839 headKind = PNK_FORHEAD;
4841 if (blockObj) {
4843 * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
4844 * to induce the correct scoping for A. Ensure here that the previously
4845 * unchecked assignment mandate for const declarations holds.
4847 if (!checkForHeadConstInitializers(pn1)) {
4848 report(ParseError, false, nullptr, JSMSG_BAD_CONST_DECL);
4849 return null();
4852 forLetImpliedBlock = pushLetScope(blockObj, &letStmt);
4853 if (!forLetImpliedBlock)
4854 return null();
4855 letStmt.isForLetBlock = true;
4857 forLetDecl = pn1;
4858 pn1 = nullptr;
4861 /* Parse the loop condition or null into pn2. */
4862 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
4863 TokenKind tt;
4864 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
4865 return null();
4866 if (tt == TOK_SEMI) {
4867 pn2 = nullptr;
4868 } else {
4869 pn2 = expr();
4870 if (!pn2)
4871 return null();
4874 /* Parse the update expression or null into pn3. */
4875 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
4876 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
4877 return null();
4878 if (tt == TOK_RP) {
4879 pn3 = nullptr;
4880 } else {
4881 pn3 = expr();
4882 if (!pn3)
4883 return null();
4887 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
4889 TokenPos headPos(begin, pos().end);
4890 ParseNode* forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos);
4891 if (!forHead)
4892 return null();
4894 /* Parse the loop body. */
4895 ParseNode* body = statement();
4896 if (!body)
4897 return null();
4899 if (blockObj)
4900 PopStatementPC(tokenStream, pc);
4901 PopStatementPC(tokenStream, pc);
4903 ParseNode* forLoop = handler.newForStatement(begin, forHead, body, iflags);
4904 if (!forLoop)
4905 return null();
4907 if (hoistedVar) {
4908 ParseNode* pnseq = handler.newList(PNK_SEQ, hoistedVar);
4909 if (!pnseq)
4910 return null();
4911 pnseq->pn_pos = forLoop->pn_pos;
4912 pnseq->append(forLoop);
4913 return pnseq;
4915 if (forLetImpliedBlock) {
4916 forLetImpliedBlock->pn_expr = forLoop;
4917 forLetImpliedBlock->pn_pos = forLoop->pn_pos;
4918 ParseNode* let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock);
4919 if (!let)
4920 return null();
4921 let->pn_pos = forLoop->pn_pos;
4922 return let;
4924 return forLoop;
4927 template <>
4928 SyntaxParseHandler::Node
4929 Parser<SyntaxParseHandler>::forStatement()
4932 * 'for' statement parsing is fantastically complicated and requires being
4933 * able to inspect the parse tree for previous parts of the 'for'. Syntax
4934 * parsing of 'for' statements is thus done separately, and only handles
4935 * the types of 'for' statements likely to be seen in web content.
4937 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
4939 StmtInfoPC forStmt(context);
4940 PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
4942 /* Don't parse 'for each' loops. */
4943 if (allowsForEachIn()) {
4944 TokenKind tt;
4945 if (!tokenStream.peekToken(&tt))
4946 return null();
4947 // Not all "yield" tokens are names, but the ones that aren't names are
4948 // invalid in this context anyway.
4949 if (tt == TOK_NAME || tt == TOK_YIELD) {
4950 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4951 return null();
4955 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4957 /* True if we have 'for (var ...)'. */
4958 bool isForDecl = false;
4959 bool simpleForDecl = true;
4961 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
4962 Node lhsNode;
4965 TokenKind tt;
4966 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
4967 return null();
4968 if (tt == TOK_SEMI) {
4969 lhsNode = null();
4970 } else {
4971 /* Set lhsNode to a var list or an initializing expression. */
4972 pc->parsingForInit = true;
4973 if (tt == TOK_VAR) {
4974 isForDecl = true;
4975 tokenStream.consumeKnownToken(tt);
4976 lhsNode = variables(PNK_VAR, &simpleForDecl);
4978 else if (tt == TOK_CONST || tt == TOK_LET) {
4979 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4980 return null();
4982 else {
4983 lhsNode = expr();
4985 if (!lhsNode)
4986 return null();
4987 pc->parsingForInit = false;
4992 * We can be sure that it's a for/in loop if there's still an 'in'
4993 * keyword here, even if JavaScript recognizes 'in' as an operator,
4994 * as we've excluded 'in' from being parsed in RelExpr by setting
4995 * pc->parsingForInit.
4997 bool isForIn = false, isForOf = false;
4998 if (lhsNode) {
4999 if (!matchInOrOf(&isForIn, &isForOf))
5000 return null();
5002 if (isForIn || isForOf) {
5003 /* Parse the rest of the for/in or for/of head. */
5004 forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
5006 /* Check that the left side of the 'in' or 'of' is valid. */
5007 if (!isForDecl &&
5008 lhsNode != SyntaxParseHandler::NodeName &&
5009 lhsNode != SyntaxParseHandler::NodeGetProp &&
5010 lhsNode != SyntaxParseHandler::NodeLValue)
5012 JS_ALWAYS_FALSE(abortIfSyntaxParser());
5013 return null();
5016 if (!simpleForDecl) {
5017 JS_ALWAYS_FALSE(abortIfSyntaxParser());
5018 return null();
5021 if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment))
5022 return null();
5024 if (!expr())
5025 return null();
5026 } else {
5027 /* Parse the loop condition or null. */
5028 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
5029 TokenKind tt;
5030 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
5031 return null();
5032 if (tt != TOK_SEMI) {
5033 if (!expr())
5034 return null();
5037 /* Parse the update expression or null. */
5038 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
5039 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
5040 return null();
5041 if (tt != TOK_RP) {
5042 if (!expr())
5043 return null();
5047 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5049 /* Parse the loop body. */
5050 if (!statement())
5051 return null();
5053 PopStatementPC(tokenStream, pc);
5054 return SyntaxParseHandler::NodeGeneric;
5057 template <typename ParseHandler>
5058 typename ParseHandler::Node
5059 Parser<ParseHandler>::switchStatement()
5061 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
5062 uint32_t begin = pos().begin;
5064 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
5066 Node discriminant = exprInParens();
5067 if (!discriminant)
5068 return null();
5070 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
5071 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
5073 StmtInfoPC stmtInfo(context);
5074 PushStatementPC(pc, &stmtInfo, STMT_SWITCH);
5076 if (!GenerateBlockId(tokenStream, pc, pc->topStmt->blockid))
5077 return null();
5079 Node caseList = handler.newStatementList(pc->blockid(), pos());
5080 if (!caseList)
5081 return null();
5083 Node saveBlock = pc->blockNode;
5084 pc->blockNode = caseList;
5086 bool seenDefault = false;
5087 TokenKind tt;
5088 while (true) {
5089 if (!tokenStream.getToken(&tt))
5090 return null();
5091 if (tt == TOK_RC)
5092 break;
5093 uint32_t caseBegin = pos().begin;
5095 Node caseExpr;
5096 switch (tt) {
5097 case TOK_DEFAULT:
5098 if (seenDefault) {
5099 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
5100 return null();
5102 seenDefault = true;
5103 caseExpr = null(); // The default case has pn_left == nullptr.
5104 break;
5106 case TOK_CASE:
5107 caseExpr = expr();
5108 if (!caseExpr)
5109 return null();
5110 break;
5112 default:
5113 report(ParseError, false, null(), JSMSG_BAD_SWITCH);
5114 return null();
5117 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
5119 Node body = handler.newStatementList(pc->blockid(), pos());
5120 if (!body)
5121 return null();
5123 while (true) {
5124 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
5125 return null();
5126 if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
5127 break;
5128 Node stmt = statement();
5129 if (!stmt)
5130 return null();
5131 handler.addList(body, stmt);
5134 // In ES6, lexical bindings canot be accessed until initialized. If
5135 // there was a 'let' declaration in the case we just parsed, remember
5136 // the slot starting at which new lexical bindings will be
5137 // assigned. Since lexical bindings from previous cases will not
5138 // dominate uses in the current case, any such uses will require a
5139 // dead zone check.
5141 // Currently this is overly conservative; we could do better, but
5142 // declaring lexical bindings within switch cases without introducing
5143 // a new block is poor form and should be avoided.
5144 if (stmtInfo.isBlockScope)
5145 stmtInfo.firstDominatingLexicalInCase = stmtInfo.staticBlock().numVariables();
5147 Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
5148 if (!casepn)
5149 return null();
5150 handler.addList(caseList, casepn);
5154 * Handle the case where there was a let declaration in any case in
5155 * the switch body, but not within an inner block. If it replaced
5156 * pc->blockNode with a new block node then we must refresh caseList and
5157 * then restore pc->blockNode.
5159 if (pc->blockNode != caseList)
5160 caseList = pc->blockNode;
5161 pc->blockNode = saveBlock;
5163 PopStatementPC(tokenStream, pc);
5165 handler.setEndPosition(caseList, pos().end);
5167 return handler.newSwitchStatement(begin, discriminant, caseList);
5170 template <typename ParseHandler>
5171 typename ParseHandler::Node
5172 Parser<ParseHandler>::continueStatement()
5174 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
5175 uint32_t begin = pos().begin;
5177 RootedPropertyName label(context);
5178 if (!matchLabel(&label))
5179 return null();
5181 StmtInfoPC* stmt = pc->topStmt;
5182 if (label) {
5183 for (StmtInfoPC* stmt2 = nullptr; ; stmt = stmt->down) {
5184 if (!stmt) {
5185 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
5186 return null();
5188 if (stmt->type == STMT_LABEL) {
5189 if (stmt->label == label) {
5190 if (!stmt2 || !stmt2->isLoop()) {
5191 report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
5192 return null();
5194 break;
5196 } else {
5197 stmt2 = stmt;
5200 } else {
5201 for (; ; stmt = stmt->down) {
5202 if (!stmt) {
5203 report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
5204 return null();
5206 if (stmt->isLoop())
5207 break;
5211 if (!MatchOrInsertSemicolon(tokenStream))
5212 return null();
5214 return handler.newContinueStatement(label, TokenPos(begin, pos().end));
5217 template <typename ParseHandler>
5218 typename ParseHandler::Node
5219 Parser<ParseHandler>::breakStatement()
5221 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
5222 uint32_t begin = pos().begin;
5224 RootedPropertyName label(context);
5225 if (!matchLabel(&label))
5226 return null();
5227 StmtInfoPC* stmt = pc->topStmt;
5228 if (label) {
5229 for (; ; stmt = stmt->down) {
5230 if (!stmt) {
5231 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
5232 return null();
5234 if (stmt->type == STMT_LABEL && stmt->label == label)
5235 break;
5237 } else {
5238 for (; ; stmt = stmt->down) {
5239 if (!stmt) {
5240 report(ParseError, false, null(), JSMSG_TOUGH_BREAK);
5241 return null();
5243 if (stmt->isLoop() || stmt->type == STMT_SWITCH)
5244 break;
5248 if (!MatchOrInsertSemicolon(tokenStream))
5249 return null();
5251 return handler.newBreakStatement(label, TokenPos(begin, pos().end));
5254 template <typename ParseHandler>
5255 typename ParseHandler::Node
5256 Parser<ParseHandler>::returnStatement()
5258 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
5259 uint32_t begin = pos().begin;
5261 if (!pc->sc->isFunctionBox()) {
5262 report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
5263 return null();
5266 // Parse an optional operand.
5268 // This is ugly, but we don't want to require a semicolon.
5269 Node exprNode;
5270 TokenKind tt;
5271 if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
5272 return null();
5273 switch (tt) {
5274 case TOK_EOF:
5275 case TOK_EOL:
5276 case TOK_SEMI:
5277 case TOK_RC:
5278 exprNode = null();
5279 pc->funHasReturnVoid = true;
5280 break;
5281 default: {
5282 exprNode = expr();
5283 if (!exprNode)
5284 return null();
5285 pc->funHasReturnExpr = true;
5289 if (!MatchOrInsertSemicolon(tokenStream))
5290 return null();
5292 Node genrval = null();
5293 if (pc->isStarGenerator()) {
5294 genrval = newName(context->names().dotGenRVal);
5295 if (!genrval)
5296 return null();
5297 if (!noteNameUse(context->names().dotGenRVal, genrval))
5298 return null();
5299 if (!checkAndMarkAsAssignmentLhs(genrval, PlainAssignment))
5300 return null();
5303 Node pn = handler.newReturnStatement(exprNode, genrval, TokenPos(begin, pos().end));
5304 if (!pn)
5305 return null();
5307 if (pc->isLegacyGenerator() && exprNode) {
5308 /* Disallow "return v;" in legacy generators. */
5309 reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
5310 JSMSG_BAD_ANON_GENERATOR_RETURN);
5311 return null();
5314 return pn;
5317 template <typename ParseHandler>
5318 typename ParseHandler::Node
5319 Parser<ParseHandler>::newYieldExpression(uint32_t begin, typename ParseHandler::Node expr,
5320 bool isYieldStar)
5322 Node generator = newName(context->names().dotGenerator);
5323 if (!generator)
5324 return null();
5325 if (!noteNameUse(context->names().dotGenerator, generator))
5326 return null();
5327 if (isYieldStar)
5328 return handler.newYieldStarExpression(begin, expr, generator);
5329 return handler.newYieldExpression(begin, expr, generator);
5332 template <typename ParseHandler>
5333 typename ParseHandler::Node
5334 Parser<ParseHandler>::yieldExpression()
5336 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
5337 uint32_t begin = pos().begin;
5339 switch (pc->generatorKind()) {
5340 case StarGenerator:
5342 MOZ_ASSERT(pc->sc->isFunctionBox());
5344 pc->lastYieldOffset = begin;
5346 Node exprNode;
5347 ParseNodeKind kind = PNK_YIELD;
5348 TokenKind tt;
5349 if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
5350 return null();
5351 switch (tt) {
5352 // TOK_EOL is special; it implements the [no LineTerminator here]
5353 // quirk in the grammar.
5354 case TOK_EOL:
5355 // The rest of these make up the complete set of tokens that can
5356 // appear after any of the places where AssignmentExpression is used
5357 // throughout the grammar. Conveniently, none of them can also be the
5358 // start an expression.
5359 case TOK_EOF:
5360 case TOK_SEMI:
5361 case TOK_RC:
5362 case TOK_RB:
5363 case TOK_RP:
5364 case TOK_COLON:
5365 case TOK_COMMA:
5366 // No value.
5367 exprNode = null();
5368 break;
5369 case TOK_MUL:
5370 kind = PNK_YIELD_STAR;
5371 tokenStream.consumeKnownToken(TOK_MUL);
5372 // Fall through.
5373 default:
5374 exprNode = assignExpr();
5375 if (!exprNode)
5376 return null();
5378 return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR);
5381 case NotGenerator:
5382 // We are in code that has not seen a yield, but we are in JS 1.7 or
5383 // later. Try to transition to being a legacy generator.
5384 MOZ_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
5385 MOZ_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
5387 if (!abortIfSyntaxParser())
5388 return null();
5390 if (!pc->sc->isFunctionBox()) {
5391 report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
5392 return null();
5395 pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
5396 sawDeprecatedLegacyGenerator = true;
5398 if (pc->funHasReturnExpr) {
5399 /* As in Python (see PEP-255), disallow return v; in generators. */
5400 reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
5401 JSMSG_BAD_ANON_GENERATOR_RETURN);
5402 return null();
5404 // Fall through.
5406 case LegacyGenerator:
5408 // We are in a legacy generator: a function that has already seen a
5409 // yield, or in a legacy generator comprehension.
5410 MOZ_ASSERT(pc->sc->isFunctionBox());
5412 pc->lastYieldOffset = begin;
5414 // Legacy generators do not require a value.
5415 Node exprNode;
5416 TokenKind tt;
5417 if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
5418 return null();
5419 switch (tt) {
5420 case TOK_EOF:
5421 case TOK_EOL:
5422 case TOK_SEMI:
5423 case TOK_RC:
5424 case TOK_RB:
5425 case TOK_RP:
5426 case TOK_COLON:
5427 case TOK_COMMA:
5428 // No value.
5429 exprNode = null();
5430 break;
5431 default:
5432 exprNode = assignExpr();
5433 if (!exprNode)
5434 return null();
5437 return newYieldExpression(begin, exprNode);
5441 MOZ_CRASH("yieldExpr");
5444 template <>
5445 ParseNode*
5446 Parser<FullParseHandler>::withStatement()
5448 // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements
5449 // in syntax-parse mode. See bug 892583.
5450 if (handler.syntaxParser) {
5451 handler.disableSyntaxParser();
5452 abortedSyntaxParse = true;
5453 return null();
5456 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
5457 uint32_t begin = pos().begin;
5459 // In most cases, we want the constructs forbidden in strict mode code to be
5460 // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should
5461 // use reportStrictModeError. However, 'with' is the sole instance of a
5462 // construct that is forbidden in strict mode code, but doesn't even merit a
5463 // warning under JSOPTION_EXTRA_WARNINGS. See
5464 // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5465 if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
5466 return null();
5468 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5469 Node objectExpr = exprInParens();
5470 if (!objectExpr)
5471 return null();
5472 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5474 bool oldParsingWith = pc->parsingWith;
5475 pc->parsingWith = true;
5477 StmtInfoPC stmtInfo(context);
5478 PushStatementPC(pc, &stmtInfo, STMT_WITH);
5479 Rooted<StaticWithObject*> staticWith(context, StaticWithObject::create(context));
5480 if (!staticWith)
5481 return null();
5482 staticWith->initEnclosingNestedScopeFromParser(pc->staticScope);
5483 FinishPushNestedScope(pc, &stmtInfo, *staticWith);
5485 Node innerBlock = statement();
5486 if (!innerBlock)
5487 return null();
5489 PopStatementPC(tokenStream, pc);
5491 pc->sc->setBindingsAccessedDynamically();
5492 pc->parsingWith = oldParsingWith;
5495 * Make sure to deoptimize lexical dependencies inside the |with|
5496 * to safely optimize binding globals (see bug 561923).
5498 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
5499 DefinitionNode defn = r.front().value().get<FullParseHandler>();
5500 DefinitionNode lexdep = handler.resolve(defn);
5501 if (!pc->sc->isDotVariable(lexdep->name()))
5502 handler.deoptimizeUsesWithin(lexdep, TokenPos(begin, pos().begin));
5505 ObjectBox* staticWithBox = newObjectBox(staticWith);
5506 if (!staticWithBox)
5507 return null();
5508 return handler.newWithStatement(begin, objectExpr, innerBlock, staticWithBox);
5511 template <>
5512 SyntaxParseHandler::Node
5513 Parser<SyntaxParseHandler>::withStatement()
5515 JS_ALWAYS_FALSE(abortIfSyntaxParser());
5516 return null();
5519 template <typename ParseHandler>
5520 typename ParseHandler::Node
5521 Parser<ParseHandler>::labeledStatement()
5523 uint32_t begin = pos().begin;
5524 RootedPropertyName label(context, tokenStream.currentName());
5525 for (StmtInfoPC* stmt = pc->topStmt; stmt; stmt = stmt->down) {
5526 if (stmt->type == STMT_LABEL && stmt->label == label) {
5527 report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
5528 return null();
5532 tokenStream.consumeKnownToken(TOK_COLON);
5534 /* Push a label struct and parse the statement. */
5535 StmtInfoPC stmtInfo(context);
5536 PushStatementPC(pc, &stmtInfo, STMT_LABEL);
5537 stmtInfo.label = label;
5538 Node pn = statement();
5539 if (!pn)
5540 return null();
5542 /* Pop the label, set pn_expr, and return early. */
5543 PopStatementPC(tokenStream, pc);
5545 return handler.newLabeledStatement(label, pn, begin);
5548 template <typename ParseHandler>
5549 typename ParseHandler::Node
5550 Parser<ParseHandler>::throwStatement()
5552 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
5553 uint32_t begin = pos().begin;
5555 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5556 TokenKind tt;
5557 if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
5558 return null();
5559 if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
5560 report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW);
5561 return null();
5563 if (tt == TOK_EOL) {
5564 report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW);
5565 return null();
5568 Node throwExpr = expr();
5569 if (!throwExpr)
5570 return null();
5572 if (!MatchOrInsertSemicolon(tokenStream))
5573 return null();
5575 return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
5578 template <typename ParseHandler>
5579 typename ParseHandler::Node
5580 Parser<ParseHandler>::tryStatement()
5582 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
5583 uint32_t begin = pos().begin;
5586 * try nodes are ternary.
5587 * kid1 is the try statement
5588 * kid2 is the catch node list or null
5589 * kid3 is the finally statement
5591 * catch nodes are ternary.
5592 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5593 * kid2 is the catch guard or null if no guard
5594 * kid3 is the catch block
5596 * catch lvalue nodes are either:
5597 * TOK_NAME for a single identifier
5598 * TOK_RB or TOK_RC for a destructuring left-hand side
5600 * finally nodes are TOK_LC statement lists.
5603 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
5604 StmtInfoPC stmtInfo(context);
5605 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_TRY, pc))
5606 return null();
5607 Node innerBlock = statements();
5608 if (!innerBlock)
5609 return null();
5610 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
5611 PopStatementPC(tokenStream, pc);
5613 bool hasUnconditionalCatch = false;
5614 Node catchList = null();
5615 TokenKind tt;
5616 if (!tokenStream.getToken(&tt))
5617 return null();
5618 if (tt == TOK_CATCH) {
5619 catchList = handler.newList(PNK_CATCH);
5620 if (!catchList)
5621 return null();
5623 do {
5624 Node pnblock;
5625 BindData<ParseHandler> data(context);
5627 /* Check for another catch after unconditional catch. */
5628 if (hasUnconditionalCatch) {
5629 report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL);
5630 return null();
5634 * Create a lexical scope node around the whole catch clause,
5635 * including the head.
5637 pnblock = pushLexicalScope(&stmtInfo);
5638 if (!pnblock)
5639 return null();
5640 stmtInfo.type = STMT_CATCH;
5643 * Legal catch forms are:
5644 * catch (lhs)
5645 * catch (lhs if <boolean_expression>)
5646 * where lhs is a name or a destructuring left-hand side.
5647 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5649 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5652 * Contrary to ECMA Ed. 3, the catch variable is lexically
5653 * scoped, not a property of a new Object instance. This is
5654 * an intentional change that anticipates ECMA Ed. 4.
5656 data.initLexical(HoistVars, &pc->staticScope->template as<StaticBlockObject>(),
5657 JSMSG_TOO_MANY_CATCH_VARS);
5658 MOZ_ASSERT(data.let.blockObj);
5660 if (!tokenStream.getToken(&tt))
5661 return null();
5662 Node catchName;
5663 switch (tt) {
5664 case TOK_LB:
5665 case TOK_LC:
5666 catchName = destructuringExpr(&data, tt);
5667 if (!catchName)
5668 return null();
5669 break;
5671 case TOK_YIELD:
5672 if (!checkYieldNameValidity())
5673 return null();
5674 // Fall through.
5675 case TOK_NAME:
5677 RootedPropertyName label(context, tokenStream.currentName());
5678 catchName = newBindingNode(label, false);
5679 if (!catchName)
5680 return null();
5681 data.pn = catchName;
5682 if (!data.binder(&data, label, this))
5683 return null();
5684 break;
5687 default:
5688 report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER);
5689 return null();
5692 Node catchGuard = null();
5693 #if JS_HAS_CATCH_GUARD
5695 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5696 * to avoid conflicting with the JS2/ECMAv4 type annotation
5697 * catchguard syntax.
5699 bool matched;
5700 if (!tokenStream.matchToken(&matched, TOK_IF))
5701 return null();
5702 if (matched) {
5703 catchGuard = expr();
5704 if (!catchGuard)
5705 return null();
5707 #endif
5708 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5710 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5711 Node catchBody = statements();
5712 if (!catchBody)
5713 return null();
5714 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5715 PopStatementPC(tokenStream, pc);
5717 if (!catchGuard)
5718 hasUnconditionalCatch = true;
5720 if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
5721 return null();
5722 handler.setEndPosition(catchList, pos().end);
5723 handler.setEndPosition(pnblock, pos().end);
5725 if (!tokenStream.getToken(&tt, TokenStream::Operand))
5726 return null();
5727 } while (tt == TOK_CATCH);
5730 Node finallyBlock = null();
5732 if (tt == TOK_FINALLY) {
5733 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5734 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc))
5735 return null();
5736 finallyBlock = statements();
5737 if (!finallyBlock)
5738 return null();
5739 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5740 PopStatementPC(tokenStream, pc);
5741 } else {
5742 tokenStream.ungetToken();
5744 if (!catchList && !finallyBlock) {
5745 report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY);
5746 return null();
5749 return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
5752 template <typename ParseHandler>
5753 typename ParseHandler::Node
5754 Parser<ParseHandler>::debuggerStatement()
5756 TokenPos p;
5757 p.begin = pos().begin;
5758 if (!MatchOrInsertSemicolon(tokenStream))
5759 return null();
5760 p.end = pos().end;
5762 pc->sc->setBindingsAccessedDynamically();
5763 pc->sc->setHasDebuggerStatement();
5765 return handler.newDebuggerStatement(p);
5768 template <typename ParseHandler>
5769 typename ParseHandler::Node
5770 Parser<ParseHandler>::statement(bool canHaveDirectives)
5772 MOZ_ASSERT(checkOptionsCalled);
5774 JS_CHECK_RECURSION(context, return null());
5776 TokenKind tt;
5777 if (!tokenStream.getToken(&tt, TokenStream::Operand))
5778 return null();
5779 switch (tt) {
5780 case TOK_LC:
5781 return blockStatement();
5783 case TOK_CONST:
5784 if (!abortIfSyntaxParser())
5785 return null();
5786 return lexicalDeclaration(/* isConst = */ true);
5788 case TOK_VAR: {
5789 Node pn = variables(PNK_VAR);
5790 if (!pn)
5791 return null();
5793 // Tell js_EmitTree to generate a final POP.
5794 handler.setListFlag(pn, PNX_POPVAR);
5796 if (!MatchOrInsertSemicolon(tokenStream))
5797 return null();
5798 return pn;
5801 case TOK_LET:
5802 return letStatement();
5803 case TOK_IMPORT:
5804 return importDeclaration();
5805 case TOK_EXPORT:
5806 return exportDeclaration();
5807 case TOK_SEMI:
5808 return handler.newEmptyStatement(pos());
5809 case TOK_IF:
5810 return ifStatement();
5811 case TOK_DO:
5812 return doWhileStatement();
5813 case TOK_WHILE:
5814 return whileStatement();
5815 case TOK_FOR:
5816 return forStatement();
5817 case TOK_SWITCH:
5818 return switchStatement();
5819 case TOK_CONTINUE:
5820 return continueStatement();
5821 case TOK_BREAK:
5822 return breakStatement();
5823 case TOK_RETURN:
5824 return returnStatement();
5825 case TOK_WITH:
5826 return withStatement();
5827 case TOK_THROW:
5828 return throwStatement();
5829 case TOK_TRY:
5830 return tryStatement();
5831 case TOK_FUNCTION:
5832 return functionStmt();
5833 case TOK_DEBUGGER:
5834 return debuggerStatement();
5836 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5837 case TOK_CATCH:
5838 report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
5839 return null();
5841 case TOK_FINALLY:
5842 report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
5843 return null();
5845 case TOK_STRING:
5846 if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
5847 if (!abortIfSyntaxParser())
5848 return null();
5849 if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
5850 return null();
5852 return expressionStatement();
5854 case TOK_YIELD: {
5855 TokenKind next;
5856 TokenStream::Modifier modifier = yieldExpressionsSupported()
5857 ? TokenStream::Operand
5858 : TokenStream::None;
5859 if (!tokenStream.peekToken(&next, modifier))
5860 return null();
5861 if (next == TOK_COLON) {
5862 if (!checkYieldNameValidity())
5863 return null();
5864 return labeledStatement();
5866 return expressionStatement();
5869 case TOK_NAME: {
5870 TokenKind next;
5871 if (!tokenStream.peekToken(&next))
5872 return null();
5873 if (next == TOK_COLON)
5874 return labeledStatement();
5875 return expressionStatement();
5878 default:
5879 return expressionStatement();
5883 template <typename ParseHandler>
5884 typename ParseHandler::Node
5885 Parser<ParseHandler>::expr()
5887 Node pn = assignExpr();
5888 if (!pn)
5889 return null();
5891 bool matched;
5892 if (!tokenStream.matchToken(&matched, TOK_COMMA))
5893 return null();
5894 if (matched) {
5895 Node seq = handler.newList(PNK_COMMA, pn);
5896 if (!seq)
5897 return null();
5898 while (true) {
5899 if (handler.isUnparenthesizedYield(pn)) {
5900 report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
5901 return null();
5904 pn = assignExpr();
5905 if (!pn)
5906 return null();
5907 handler.addList(seq, pn);
5909 if (!tokenStream.matchToken(&matched, TOK_COMMA))
5910 return null();
5911 if (!matched)
5912 break;
5914 return seq;
5916 return pn;
5919 static const JSOp ParseNodeKindToJSOp[] = {
5920 JSOP_OR,
5921 JSOP_AND,
5922 JSOP_BITOR,
5923 JSOP_BITXOR,
5924 JSOP_BITAND,
5925 JSOP_STRICTEQ,
5926 JSOP_EQ,
5927 JSOP_STRICTNE,
5928 JSOP_NE,
5929 JSOP_LT,
5930 JSOP_LE,
5931 JSOP_GT,
5932 JSOP_GE,
5933 JSOP_INSTANCEOF,
5934 JSOP_IN,
5935 JSOP_LSH,
5936 JSOP_RSH,
5937 JSOP_URSH,
5938 JSOP_ADD,
5939 JSOP_SUB,
5940 JSOP_MUL,
5941 JSOP_DIV,
5942 JSOP_MOD
5945 static inline JSOp
5946 BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
5948 MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
5949 MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
5950 return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST];
5953 static bool
5954 IsBinaryOpToken(TokenKind tok, bool parsingForInit)
5956 return tok == TOK_IN ? !parsingForInit : TokenKindIsBinaryOp(tok);
5959 static ParseNodeKind
5960 BinaryOpTokenKindToParseNodeKind(TokenKind tok)
5962 MOZ_ASSERT(TokenKindIsBinaryOp(tok));
5963 return ParseNodeKind(PNK_BINOP_FIRST + (tok - TOK_BINOP_FIRST));
5966 static const int PrecedenceTable[] = {
5967 1, /* PNK_OR */
5968 2, /* PNK_AND */
5969 3, /* PNK_BITOR */
5970 4, /* PNK_BITXOR */
5971 5, /* PNK_BITAND */
5972 6, /* PNK_STRICTEQ */
5973 6, /* PNK_EQ */
5974 6, /* PNK_STRICTNE */
5975 6, /* PNK_NE */
5976 7, /* PNK_LT */
5977 7, /* PNK_LE */
5978 7, /* PNK_GT */
5979 7, /* PNK_GE */
5980 7, /* PNK_INSTANCEOF */
5981 7, /* PNK_IN */
5982 8, /* PNK_LSH */
5983 8, /* PNK_RSH */
5984 8, /* PNK_URSH */
5985 9, /* PNK_ADD */
5986 9, /* PNK_SUB */
5987 10, /* PNK_STAR */
5988 10, /* PNK_DIV */
5989 10 /* PNK_MOD */
5992 static const int PRECEDENCE_CLASSES = 10;
5994 static int
5995 Precedence(ParseNodeKind pnk) {
5996 // Everything binds tighter than PNK_LIMIT, because we want to reduce all
5997 // nodes to a single node when we reach a token that is not another binary
5998 // operator.
5999 if (pnk == PNK_LIMIT)
6000 return 0;
6002 MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
6003 MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
6004 return PrecedenceTable[pnk - PNK_BINOP_FIRST];
6007 template <typename ParseHandler>
6008 MOZ_ALWAYS_INLINE typename ParseHandler::Node
6009 Parser<ParseHandler>::orExpr1()
6011 // Shift-reduce parser for the left-associative binary operator part of
6012 // the JS syntax.
6014 // Conceptually there's just one stack, a stack of pairs (lhs, op).
6015 // It's implemented using two separate arrays, though.
6016 Node nodeStack[PRECEDENCE_CLASSES];
6017 ParseNodeKind kindStack[PRECEDENCE_CLASSES];
6018 int depth = 0;
6020 bool oldParsingForInit = pc->parsingForInit;
6021 pc->parsingForInit = false;
6023 Node pn;
6024 for (;;) {
6025 pn = unaryExpr();
6026 if (!pn)
6027 return pn;
6029 // If a binary operator follows, consume it and compute the
6030 // corresponding operator.
6031 TokenKind tok;
6032 if (!tokenStream.getToken(&tok))
6033 return null();
6034 ParseNodeKind pnk;
6035 if (IsBinaryOpToken(tok, oldParsingForInit)) {
6036 pnk = BinaryOpTokenKindToParseNodeKind(tok);
6037 } else {
6038 tok = TOK_EOF;
6039 pnk = PNK_LIMIT;
6042 // If pnk has precedence less than or equal to another operator on the
6043 // stack, reduce. This combines nodes on the stack until we form the
6044 // actual lhs of pnk.
6046 // The >= in this condition works because all the operators in question
6047 // are left-associative; if any were not, the case where two operators
6048 // have equal precedence would need to be handled specially, and the
6049 // stack would need to be a Vector.
6050 while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
6051 depth--;
6052 ParseNodeKind combiningPnk = kindStack[depth];
6053 JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
6054 pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
6055 if (!pn)
6056 return pn;
6059 if (pnk == PNK_LIMIT)
6060 break;
6062 nodeStack[depth] = pn;
6063 kindStack[depth] = pnk;
6064 depth++;
6065 MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
6068 MOZ_ASSERT(depth == 0);
6069 pc->parsingForInit = oldParsingForInit;
6070 return pn;
6073 template <typename ParseHandler>
6074 MOZ_ALWAYS_INLINE typename ParseHandler::Node
6075 Parser<ParseHandler>::condExpr1()
6077 Node condition = orExpr1();
6078 if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
6079 return condition;
6082 * Always accept the 'in' operator in the middle clause of a ternary,
6083 * where it's unambiguous, even if we might be parsing the init of a
6084 * for statement.
6086 bool oldParsingForInit = pc->parsingForInit;
6087 pc->parsingForInit = false;
6088 Node thenExpr = assignExpr();
6089 pc->parsingForInit = oldParsingForInit;
6090 if (!thenExpr)
6091 return null();
6093 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
6095 Node elseExpr = assignExpr();
6096 if (!elseExpr)
6097 return null();
6099 // Advance to the next token; the caller is responsible for interpreting it.
6100 TokenKind ignored;
6101 if (!tokenStream.getToken(&ignored))
6102 return null();
6103 return handler.newConditional(condition, thenExpr, elseExpr);
6106 template <>
6107 bool
6108 Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentFlavor flavor)
6110 switch (pn->getKind()) {
6111 case PNK_NAME:
6112 if (!checkStrictAssignment(pn))
6113 return false;
6114 if (flavor == KeyedDestructuringAssignment) {
6116 * We may be called on a name node that has already been
6117 * specialized, in the very weird "for (var [x] = i in o) ..."
6118 * case. See bug 558633.
6120 if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
6121 pn->setOp(JSOP_SETNAME);
6122 } else {
6123 pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
6125 pn->markAsAssigned();
6126 break;
6128 case PNK_DOT:
6129 case PNK_ELEM:
6130 break;
6132 case PNK_ARRAY:
6133 case PNK_OBJECT:
6134 if (flavor == CompoundAssignment) {
6135 report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
6136 return false;
6138 if (!checkDestructuring(nullptr, pn))
6139 return false;
6140 break;
6142 case PNK_CALL:
6143 if (flavor == KeyedDestructuringAssignment) {
6144 report(ParseError, false, pn, JSMSG_BAD_DESTRUCT_TARGET);
6145 return false;
6147 if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
6148 return false;
6149 break;
6151 default:
6152 unsigned errnum = (flavor == KeyedDestructuringAssignment) ? JSMSG_BAD_DESTRUCT_TARGET :
6153 JSMSG_BAD_LEFTSIDE_OF_ASS;
6154 report(ParseError, false, pn, errnum);
6155 return false;
6157 return true;
6160 template <>
6161 bool
6162 Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
6164 /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
6165 if (pn != SyntaxParseHandler::NodeName &&
6166 pn != SyntaxParseHandler::NodeGetProp &&
6167 pn != SyntaxParseHandler::NodeLValue)
6169 return abortIfSyntaxParser();
6171 return checkStrictAssignment(pn);
6174 template <typename ParseHandler>
6175 typename ParseHandler::Node
6176 Parser<ParseHandler>::assignExpr()
6178 JS_CHECK_RECURSION(context, return null());
6180 // It's very common at this point to have a "detectably simple" expression,
6181 // i.e. a name/number/string token followed by one of the following tokens
6182 // that obviously isn't part of an expression: , ; : ) ] }
6184 // (In Parsemark this happens 81.4% of the time; in code with large
6185 // numeric arrays, such as some Kraken benchmarks, it happens more often.)
6187 // In such cases, we can avoid the full expression parsing route through
6188 // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
6189 // primaryExpr().
6191 TokenKind tt;
6192 if (!tokenStream.getToken(&tt, TokenStream::Operand))
6193 return null();
6195 bool endsExpr;
6197 if (tt == TOK_NAME) {
6198 if (!tokenStream.nextTokenEndsExpr(&endsExpr))
6199 return null();
6200 if (endsExpr)
6201 return identifierName();
6204 if (tt == TOK_NUMBER) {
6205 if (!tokenStream.nextTokenEndsExpr(&endsExpr))
6206 return null();
6207 if (endsExpr)
6208 return newNumber(tokenStream.currentToken());
6211 if (tt == TOK_STRING) {
6212 if (!tokenStream.nextTokenEndsExpr(&endsExpr))
6213 return null();
6214 if (endsExpr)
6215 return stringLiteral();
6218 if (tt == TOK_YIELD && yieldExpressionsSupported())
6219 return yieldExpression();
6221 tokenStream.ungetToken();
6223 // Save the tokenizer state in case we find an arrow function and have to
6224 // rewind.
6225 TokenStream::Position start(keepAtoms);
6226 tokenStream.tell(&start);
6228 Node lhs = condExpr1();
6229 if (!lhs)
6230 return null();
6232 ParseNodeKind kind;
6233 JSOp op;
6234 switch (tokenStream.currentToken().type) {
6235 case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break;
6236 case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break;
6237 case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break;
6238 case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break;
6239 case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
6240 case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
6241 case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; op = JSOP_LSH; break;
6242 case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; op = JSOP_RSH; break;
6243 case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; op = JSOP_URSH; break;
6244 case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break;
6245 case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break;
6246 case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break;
6248 case TOK_ARROW: {
6249 tokenStream.seek(start);
6250 if (!abortIfSyntaxParser())
6251 return null();
6253 TokenKind ignored;
6254 if (!tokenStream.peekToken(&ignored))
6255 return null();
6257 return functionDef(NullPtr(), Normal, Arrow, NotGenerator);
6260 default:
6261 MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
6262 tokenStream.ungetToken();
6263 return lhs;
6266 AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
6267 if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
6268 return null();
6270 Node rhs = assignExpr();
6271 if (!rhs)
6272 return null();
6274 return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op);
6277 static const char incop_name_str[][10] = {"increment", "decrement"};
6279 template <>
6280 bool
6281 Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode* kid, TokenKind tt, bool preorder)
6283 // Check.
6284 if (!kid->isKind(PNK_NAME) &&
6285 !kid->isKind(PNK_DOT) &&
6286 !kid->isKind(PNK_ELEM) &&
6287 !(kid->isKind(PNK_CALL) &&
6288 (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) ||
6289 kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_STRICTEVAL) ||
6290 kid->isOp(JSOP_SPREADEVAL) || kid->isOp(JSOP_STRICTSPREADEVAL) ||
6291 kid->isOp(JSOP_FUNCALL) ||
6292 kid->isOp(JSOP_FUNAPPLY))))
6294 report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
6295 return false;
6298 if (!checkStrictAssignment(kid))
6299 return false;
6301 // Mark.
6302 if (kid->isKind(PNK_NAME)) {
6303 kid->markAsAssigned();
6304 } else if (kid->isKind(PNK_CALL)) {
6305 if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
6306 return false;
6308 return true;
6311 template <>
6312 bool
6313 Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder)
6315 // To the extent of what we support in syntax-parse mode, the rules for
6316 // inc/dec operands are the same as for assignment. There are differences,
6317 // such as destructuring; but if we hit any of those cases, we'll abort and
6318 // reparse in full mode.
6319 return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment);
6322 template <typename ParseHandler>
6323 typename ParseHandler::Node
6324 Parser<ParseHandler>::unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin)
6326 Node kid = unaryExpr();
6327 if (!kid)
6328 return null();
6329 return handler.newUnary(kind, op, begin, kid);
6332 template <typename ParseHandler>
6333 typename ParseHandler::Node
6334 Parser<ParseHandler>::unaryExpr()
6336 Node pn, pn2;
6338 JS_CHECK_RECURSION(context, return null());
6340 TokenKind tt;
6341 if (!tokenStream.getToken(&tt, TokenStream::Operand))
6342 return null();
6343 uint32_t begin = pos().begin;
6344 switch (tt) {
6345 case TOK_TYPEOF:
6346 return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF, begin);
6347 case TOK_VOID:
6348 return unaryOpExpr(PNK_VOID, JSOP_VOID, begin);
6349 case TOK_NOT:
6350 return unaryOpExpr(PNK_NOT, JSOP_NOT, begin);
6351 case TOK_BITNOT:
6352 return unaryOpExpr(PNK_BITNOT, JSOP_BITNOT, begin);
6353 case TOK_ADD:
6354 return unaryOpExpr(PNK_POS, JSOP_POS, begin);
6355 case TOK_SUB:
6356 return unaryOpExpr(PNK_NEG, JSOP_NEG, begin);
6358 case TOK_INC:
6359 case TOK_DEC:
6361 TokenKind tt2;
6362 if (!tokenStream.getToken(&tt2, TokenStream::Operand))
6363 return null();
6364 pn2 = memberExpr(tt2, true);
6365 if (!pn2)
6366 return null();
6367 if (!checkAndMarkAsIncOperand(pn2, tt, true))
6368 return null();
6369 return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
6370 JSOP_NOP,
6371 begin,
6372 pn2);
6375 case TOK_DELETE: {
6376 Node expr = unaryExpr();
6377 if (!expr)
6378 return null();
6380 // Per spec, deleting any unary expression is valid -- it simply
6381 // returns true -- except for one case that is illegal in strict mode.
6382 if (handler.isName(expr)) {
6383 if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND))
6384 return null();
6385 pc->sc->setBindingsAccessedDynamically();
6388 return handler.newDelete(begin, expr);
6391 default:
6392 pn = memberExpr(tt, true);
6393 if (!pn)
6394 return null();
6396 /* Don't look across a newline boundary for a postfix incop. */
6397 if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
6398 return null();
6399 if (tt == TOK_INC || tt == TOK_DEC) {
6400 tokenStream.consumeKnownToken(tt);
6401 if (!checkAndMarkAsIncOperand(pn, tt, false))
6402 return null();
6403 return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
6404 JSOP_NOP,
6405 begin,
6406 pn);
6408 return pn;
6413 * A dedicated helper for transplanting the legacy comprehension expression E in
6415 * [E for (V in I)] // legacy array comprehension
6416 * (E for (V in I)) // legacy generator expression
6418 * from its initial location in the AST, on the left of the 'for', to its final
6419 * position on the right. To avoid a separate pass we do this by adjusting the
6420 * blockids and name binding links that were established when E was parsed.
6422 * A legacy generator expression desugars like so:
6424 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6426 * so the transplanter must adjust static level as well as blockid. E's source
6427 * coordinates in root->pn_pos are critical to deciding which binding links to
6428 * preserve and which to cut.
6430 * NB: This is not a general tree transplanter -- it knows in particular that
6431 * the one or more bindings induced by V have not yet been created.
6433 class LegacyCompExprTransplanter
6435 ParseNode* root;
6436 Parser<FullParseHandler>* parser;
6437 ParseContext<FullParseHandler>* outerpc;
6438 GeneratorKind comprehensionKind;
6439 unsigned adjust;
6440 HashSet<Definition*> visitedImplicitArguments;
6442 public:
6443 LegacyCompExprTransplanter(ParseNode* pn, Parser<FullParseHandler>* parser,
6444 ParseContext<FullParseHandler>* outerpc,
6445 GeneratorKind kind, unsigned adj)
6446 : root(pn), parser(parser), outerpc(outerpc), comprehensionKind(kind), adjust(adj),
6447 visitedImplicitArguments(parser->context)
6450 bool init() {
6451 return visitedImplicitArguments.init();
6454 bool transplant(ParseNode* pn);
6458 * Any definitions nested within the legacy comprehension expression of a
6459 * generator expression must move "down" one static level, which of course
6460 * increases the upvar-frame-skip count.
6462 template <typename ParseHandler>
6463 static bool
6464 BumpStaticLevel(TokenStream& ts, ParseNode* pn, ParseContext<ParseHandler>* pc)
6466 if (pn->pn_cookie.isFree())
6467 return true;
6469 unsigned level = unsigned(pn->pn_cookie.level()) + 1;
6470 MOZ_ASSERT(level >= pc->staticLevel);
6471 return pn->pn_cookie.set(ts, level, pn->pn_cookie.slot());
6474 template <typename ParseHandler>
6475 static bool
6476 AdjustBlockId(TokenStream& ts, ParseNode* pn, unsigned adjust, ParseContext<ParseHandler>* pc)
6478 MOZ_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_CODE) || pn->isArity(PN_NAME));
6479 if (BlockIdLimit - pn->pn_blockid <= adjust + 1) {
6480 ts.reportError(JSMSG_NEED_DIET, "program");
6481 return false;
6483 pn->pn_blockid += adjust;
6484 if (pn->pn_blockid >= pc->blockidGen)
6485 pc->blockidGen = pn->pn_blockid + 1;
6486 return true;
6489 bool
6490 LegacyCompExprTransplanter::transplant(ParseNode* pn)
6492 ParseContext<FullParseHandler>* pc = parser->pc;
6494 bool isGenexp = comprehensionKind != NotGenerator;
6496 if (!pn)
6497 return true;
6499 switch (pn->getArity()) {
6500 case PN_LIST:
6501 for (ParseNode* pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6502 if (!transplant(pn2))
6503 return false;
6505 if (pn->pn_pos >= root->pn_pos) {
6506 if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
6507 return false;
6509 break;
6511 case PN_TERNARY:
6512 if (!transplant(pn->pn_kid1) ||
6513 !transplant(pn->pn_kid2) ||
6514 !transplant(pn->pn_kid3))
6515 return false;
6516 break;
6518 case PN_BINARY:
6519 case PN_BINARY_OBJ:
6520 if (!transplant(pn->pn_left))
6521 return false;
6523 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6524 if (pn->pn_right != pn->pn_left) {
6525 if (!transplant(pn->pn_right))
6526 return false;
6528 break;
6530 case PN_UNARY:
6531 if (!transplant(pn->pn_kid))
6532 return false;
6533 break;
6535 case PN_CODE:
6536 case PN_NAME:
6537 if (!transplant(pn->maybeExpr()))
6538 return false;
6540 if (pn->isDefn()) {
6541 if (isGenexp && !BumpStaticLevel(parser->tokenStream, pn, pc))
6542 return false;
6543 } else if (pn->isUsed()) {
6544 MOZ_ASSERT(pn->pn_cookie.isFree());
6546 Definition* dn = pn->pn_lexdef;
6547 MOZ_ASSERT(dn->isDefn());
6550 * Adjust the definition's block id only if it is a placeholder not
6551 * to the left of the root node, and if pn is the last use visited
6552 * in the legacy comprehension expression (to avoid adjusting the
6553 * blockid multiple times).
6555 * Non-placeholder definitions within the legacy comprehension
6556 * expression will be visited further below.
6558 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6559 if (isGenexp && !BumpStaticLevel(parser->tokenStream, dn, pc))
6560 return false;
6561 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
6562 return false;
6565 RootedAtom atom(parser->context, pn->pn_atom);
6566 #ifdef DEBUG
6567 StmtInfoPC* stmt = LexicalLookup(pc, atom, nullptr, (StmtInfoPC*)nullptr);
6568 MOZ_ASSERT(!stmt || stmt != pc->topStmt);
6569 #endif
6570 if (isGenexp && !dn->isOp(JSOP_CALLEE)) {
6571 MOZ_ASSERT_IF(!pc->sc->isDotVariable(atom), !pc->decls().lookupFirst(atom));
6573 if (pc->sc->isDotVariable(atom)) {
6574 if (dn->dn_uses == pn) {
6575 if (!BumpStaticLevel(parser->tokenStream, dn, pc))
6576 return false;
6577 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
6578 return false;
6580 } else if (dn->pn_pos < root->pn_pos) {
6582 * The variable originally appeared to be a use of a
6583 * definition or placeholder outside the generator, but now
6584 * we know it is scoped within the legacy comprehension
6585 * tail's clauses. Make it (along with any other uses within
6586 * the generator) a use of a new placeholder in the
6587 * generator's lexdeps.
6589 Definition* dn2 = parser->handler.newPlaceholder(atom, parser->pc->blockid(),
6590 parser->pos());
6591 if (!dn2)
6592 return false;
6593 dn2->pn_pos = root->pn_pos;
6596 * Change all uses of |dn| that lie within the generator's
6597 * |yield| expression into uses of dn2.
6599 ParseNode** pnup = &dn->dn_uses;
6600 ParseNode* pnu;
6601 while ((pnu = *pnup) != nullptr && pnu->pn_pos >= root->pn_pos) {
6602 pnu->pn_lexdef = dn2;
6603 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
6604 pnup = &pnu->pn_link;
6606 dn2->dn_uses = dn->dn_uses;
6607 dn->dn_uses = *pnup;
6608 *pnup = nullptr;
6609 DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn2);
6610 if (!pc->lexdeps->put(atom, def))
6611 return false;
6612 if (dn->isClosed())
6613 dn2->pn_dflags |= PND_CLOSED;
6614 } else if (dn->isPlaceholder()) {
6616 * The variable first occurs free in the 'yield' expression;
6617 * move the existing placeholder node (and all its uses)
6618 * from the parent's lexdeps into the generator's lexdeps.
6620 outerpc->lexdeps->remove(atom);
6621 DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn);
6622 if (!pc->lexdeps->put(atom, def))
6623 return false;
6624 } else if (dn->isImplicitArguments()) {
6626 * Implicit 'arguments' Definition nodes (see
6627 * PND_IMPLICITARGUMENTS in Parser::functionBody) are only
6628 * reachable via the lexdefs of their uses. Unfortunately,
6629 * there may be multiple uses, so we need to maintain a set
6630 * to only bump the definition once.
6632 if (isGenexp && !visitedImplicitArguments.has(dn)) {
6633 if (!BumpStaticLevel(parser->tokenStream, dn, pc))
6634 return false;
6635 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
6636 return false;
6637 if (!visitedImplicitArguments.put(dn))
6638 return false;
6644 if (pn->pn_pos >= root->pn_pos) {
6645 if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
6646 return false;
6648 break;
6650 case PN_NULLARY:
6651 /* Nothing. */
6652 break;
6654 return true;
6657 // Parsing legacy (JS1.7-style) comprehensions is terrible: we parse the head
6658 // expression as if it's part of a comma expression, then when we see the "for"
6659 // we transplant the parsed expression into the inside of a constructed
6660 // for-of/for-in/for-each tail. Transplanting an already-parsed expression is
6661 // tricky, but the LegacyCompExprTransplanter handles most of that.
6663 // The one remaining thing to patch up is the block scope depth. We need to
6664 // compute the maximum block scope depth of a function, so we know how much
6665 // space to reserve in the fixed part of a stack frame. Normally this is done
6666 // whenever we leave a statement, via AccumulateBlockScopeDepth. However if the
6667 // head has a let expression, we need to re-assign that depth to the tail of the
6668 // comprehension.
6670 // Thing is, we don't actually know what that depth is, because the only
6671 // information we keep is the maximum nested depth within a statement, so we
6672 // just conservatively propagate the maximum nested depth from the top statement
6673 // to the comprehension tail.
6675 template <typename ParseHandler>
6676 static unsigned
6677 LegacyComprehensionHeadBlockScopeDepth(ParseContext<ParseHandler>* pc)
6679 return pc->topStmt ? pc->topStmt->innerBlockScopeDepth : pc->blockScopeDepth;
6683 * Starting from a |for| keyword after the first array initialiser element or
6684 * an expression in an open parenthesis, parse the tail of the comprehension
6685 * or generator expression signified by this |for| keyword in context.
6687 * Return null on failure, else return the top-most parse node for the array
6688 * comprehension or generator expression, with a unary node as the body of the
6689 * (possibly nested) for-loop, initialized by |kind, op, kid|.
6691 template <>
6692 ParseNode*
6693 Parser<FullParseHandler>::legacyComprehensionTail(ParseNode* bodyExpr, unsigned blockid,
6694 GeneratorKind comprehensionKind,
6695 ParseContext<FullParseHandler>* outerpc,
6696 unsigned innerBlockScopeDepth)
6699 * If we saw any inner functions while processing the generator expression
6700 * then they may have upvars referring to the let vars in this generator
6701 * which were not correctly processed. Bail out and start over without
6702 * allowing lazy parsing.
6704 if (handler.syntaxParser) {
6705 handler.disableSyntaxParser();
6706 abortedSyntaxParse = true;
6707 return nullptr;
6710 unsigned adjust;
6711 ParseNode* pn, *pn2, *pn3, **pnp;
6712 StmtInfoPC stmtInfo(context);
6713 BindData<FullParseHandler> data(context);
6714 TokenKind tt;
6716 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
6718 bool isGenexp = comprehensionKind != NotGenerator;
6720 if (isGenexp) {
6721 MOZ_ASSERT(comprehensionKind == LegacyGenerator);
6723 * Generator expression desugars to an immediately applied lambda that
6724 * yields the next value from a for-in loop (possibly nested, and with
6725 * optional if guard). Make pn be the TOK_LC body node.
6727 pn = pushLexicalScope(&stmtInfo);
6728 if (!pn)
6729 return null();
6730 adjust = pn->pn_blockid - blockid;
6731 } else {
6733 * Make a parse-node and literal object representing the block scope of
6734 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
6735 * aka the array initialiser case, has passed the blockid to claim for
6736 * the comprehension's block scope. We allocate that id or one above it
6737 * here, by calling PushLexicalScope.
6739 * In the case of a comprehension expression that has nested blocks
6740 * (e.g., let expressions), we will allocate a higher blockid but then
6741 * slide all blocks "to the right" to make room for the comprehension's
6742 * block scope.
6744 adjust = pc->blockid();
6745 pn = pushLexicalScope(&stmtInfo);
6746 if (!pn)
6747 return null();
6749 MOZ_ASSERT(blockid <= pn->pn_blockid);
6750 MOZ_ASSERT(blockid < pc->blockidGen);
6751 MOZ_ASSERT(pc->bodyid < blockid);
6752 pn->pn_blockid = stmtInfo.blockid = blockid;
6753 MOZ_ASSERT(adjust < blockid);
6754 adjust = blockid - adjust;
6757 handler.setBeginPosition(pn, bodyExpr);
6759 pnp = &pn->pn_expr;
6761 LegacyCompExprTransplanter transplanter(bodyExpr, this, outerpc, comprehensionKind, adjust);
6762 if (!transplanter.init())
6763 return null();
6765 if (!transplanter.transplant(bodyExpr))
6766 return null();
6768 MOZ_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object);
6769 data.initLexical(HoistVars, &pc->staticScope->as<StaticBlockObject>(),
6770 JSMSG_ARRAY_INIT_TOO_BIG);
6772 while (true) {
6774 * FOR node is binary, left is loop control and right is body. Use
6775 * index to count each block-local let-variable on the left-hand side
6776 * of the in/of.
6778 pn2 = BinaryNode::create(PNK_FOR, &handler);
6779 if (!pn2)
6780 return null();
6782 pn2->setOp(JSOP_ITER);
6783 pn2->pn_iflags = JSITER_ENUMERATE;
6784 if (allowsForEachIn()) {
6785 bool matched;
6786 if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
6787 return null();
6788 if (matched) {
6789 pn2->pn_iflags |= JSITER_FOREACH;
6790 sawDeprecatedForEach = true;
6791 if (versionNumber() < JSVERSION_LATEST) {
6792 if (!report(ParseWarning, pc->sc->strict, pn2, JSMSG_DEPRECATED_FOR_EACH))
6793 return null();
6797 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6799 uint32_t startYieldOffset = pc->lastYieldOffset;
6801 RootedPropertyName name(context);
6802 if (!tokenStream.getToken(&tt))
6803 return null();
6804 switch (tt) {
6805 case TOK_LB:
6806 case TOK_LC:
6807 pc->inDeclDestructuring = true;
6808 pn3 = primaryExpr(tt);
6809 pc->inDeclDestructuring = false;
6810 if (!pn3)
6811 return null();
6812 break;
6814 case TOK_NAME:
6815 name = tokenStream.currentName();
6818 * Create a name node with pn_op JSOP_GETNAME. We can't set pn_op to
6819 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6820 * in the operand stack frame. The code generator computes that,
6821 * and it tries to bind all names to slots, so we must let it do
6822 * the deed.
6824 pn3 = newBindingNode(name, false);
6825 if (!pn3)
6826 return null();
6827 break;
6829 default:
6830 report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
6831 return null();
6834 bool isForIn, isForOf;
6835 if (!matchInOrOf(&isForIn, &isForOf))
6836 return null();
6837 if (!isForIn && !isForOf) {
6838 report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME);
6839 return null();
6841 ParseNodeKind headKind = PNK_FORIN;
6842 if (isForOf) {
6843 if (pn2->pn_iflags != JSITER_ENUMERATE) {
6844 MOZ_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
6845 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
6846 return null();
6848 pn2->pn_iflags = 0;
6849 headKind = PNK_FOROF;
6852 ParseNode* pn4 = expr();
6853 if (!pn4)
6854 return null();
6855 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6857 if (isGenexp && pc->lastYieldOffset != startYieldOffset) {
6858 reportWithOffset(ParseError, false, pc->lastYieldOffset,
6859 JSMSG_BAD_GENEXP_BODY, js_yield_str);
6860 return null();
6863 switch (tt) {
6864 case TOK_LB:
6865 case TOK_LC:
6866 if (!checkDestructuring(&data, pn3))
6867 return null();
6869 if (versionNumber() == JSVERSION_1_7 &&
6870 !(pn2->pn_iflags & JSITER_FOREACH) &&
6871 !isForOf)
6873 /* Destructuring requires [key, value] enumeration in JS1.7. */
6874 if (!pn3->isKind(PNK_ARRAY) || pn3->pn_count != 2) {
6875 report(ParseError, false, null(), JSMSG_BAD_FOR_LEFTSIDE);
6876 return null();
6879 MOZ_ASSERT(pn2->isOp(JSOP_ITER));
6880 MOZ_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6881 MOZ_ASSERT(headKind == PNK_FORIN);
6882 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6884 break;
6886 case TOK_NAME:
6887 data.pn = pn3;
6888 if (!data.binder(&data, name, this))
6889 return null();
6890 break;
6892 default:;
6896 * Synthesize a declaration. Every definition must appear in the parse
6897 * tree in order for ComprehensionTranslator to work.
6899 * These are lets to tell the bytecode emitter to emit initialization
6900 * code for the temporal dead zone.
6902 ParseNode* lets = ListNode::create(PNK_LET, &handler);
6903 if (!lets)
6904 return null();
6905 lets->setOp(JSOP_NOP);
6906 lets->pn_pos = pn3->pn_pos;
6907 lets->makeEmpty();
6908 lets->append(pn3);
6909 lets->pn_xflags |= PNX_POPVAR;
6911 /* Definitions can't be passed directly to EmitAssignment as lhs. */
6912 pn3 = cloneLeftHandSide(pn3);
6913 if (!pn3)
6914 return null();
6916 pn2->pn_left = handler.newTernary(headKind, lets, pn3, pn4);
6917 if (!pn2->pn_left)
6918 return null();
6919 *pnp = pn2;
6920 pnp = &pn2->pn_right;
6922 bool matched;
6923 if (!tokenStream.matchToken(&matched, TOK_FOR))
6924 return null();
6925 if (!matched)
6926 break;
6929 bool matched;
6930 if (!tokenStream.matchToken(&matched, TOK_IF))
6931 return null();
6932 if (matched) {
6933 pn2 = TernaryNode::create(PNK_IF, &handler);
6934 if (!pn2)
6935 return null();
6936 pn2->pn_kid1 = condition();
6937 if (!pn2->pn_kid1)
6938 return null();
6939 *pnp = pn2;
6940 pnp = &pn2->pn_kid2;
6943 ParseNode* bodyStmt;
6944 if (isGenexp) {
6945 ParseNode* yieldExpr = newYieldExpression(bodyExpr->pn_pos.begin, bodyExpr);
6946 if (!yieldExpr)
6947 return null();
6948 yieldExpr->setInParens(true);
6950 bodyStmt = handler.newExprStatement(yieldExpr, bodyExpr->pn_pos.end);
6951 if (!bodyStmt)
6952 return null();
6953 } else {
6954 bodyStmt = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH,
6955 bodyExpr->pn_pos.begin, bodyExpr);
6956 if (!bodyStmt)
6957 return null();
6960 *pnp = bodyStmt;
6962 pc->topStmt->innerBlockScopeDepth += innerBlockScopeDepth;
6963 PopStatementPC(tokenStream, pc);
6965 handler.setEndPosition(pn, pos().end);
6967 return pn;
6970 template <>
6971 SyntaxParseHandler::Node
6972 Parser<SyntaxParseHandler>::legacyComprehensionTail(SyntaxParseHandler::Node bodyStmt,
6973 unsigned blockid,
6974 GeneratorKind comprehensionKind,
6975 ParseContext<SyntaxParseHandler>* outerpc,
6976 unsigned innerBlockScopeDepth)
6978 abortIfSyntaxParser();
6979 return null();
6982 template <>
6983 ParseNode*
6984 Parser<FullParseHandler>::legacyArrayComprehension(ParseNode* array)
6986 array->setKind(PNK_ARRAYCOMP);
6988 // Remove the single element from array's linked list, leaving us with an
6989 // empty array literal and a comprehension expression.
6990 MOZ_ASSERT(array->pn_count == 1);
6991 ParseNode* bodyExpr = array->last();
6992 array->pn_count = 0;
6993 array->pn_tail = &array->pn_head;
6994 *array->pn_tail = nullptr;
6996 ParseNode* comp = legacyComprehensionTail(bodyExpr, array->pn_blockid, NotGenerator,
6997 nullptr, LegacyComprehensionHeadBlockScopeDepth(pc));
6998 if (!comp)
6999 return null();
7001 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
7003 TokenPos p = handler.getPosition(array);
7004 p.end = pos().end;
7005 return handler.newArrayComprehension(comp, array->pn_blockid, p);
7008 template <>
7009 SyntaxParseHandler::Node
7010 Parser<SyntaxParseHandler>::legacyArrayComprehension(Node array)
7012 abortIfSyntaxParser();
7013 return null();
7016 template <typename ParseHandler>
7017 typename ParseHandler::Node
7018 Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKind,
7019 unsigned begin, Node innerExpr)
7021 MOZ_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator);
7022 MOZ_ASSERT(!!innerExpr == (comprehensionKind == LegacyGenerator));
7024 Node genfn = handler.newFunctionDefinition();
7025 if (!genfn)
7026 return null();
7027 handler.setOp(genfn, JSOP_LAMBDA);
7029 ParseContext<ParseHandler>* outerpc = pc;
7031 // If we are off the main thread, the generator meta-objects have
7032 // already been created by js::StartOffThreadParseScript, so cx will not
7033 // be necessary.
7034 RootedObject proto(context);
7035 if (comprehensionKind == StarGenerator) {
7036 JSContext* cx = context->maybeJSContext();
7037 proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
7038 if (!proto)
7039 return null();
7042 RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto));
7043 if (!fun)
7044 return null();
7046 // Create box for fun->object early to root it.
7047 Directives directives(/* strict = */ outerpc->sc->strict);
7048 FunctionBox* genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind);
7049 if (!genFunbox)
7050 return null();
7052 ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox,
7053 /* newDirectives = */ nullptr,
7054 outerpc->staticLevel + 1, outerpc->blockidGen,
7055 /* blockScopeDepth = */ 0);
7056 if (!genpc.init(tokenStream))
7057 return null();
7060 * We assume conservatively that any deoptimization flags in pc->sc
7061 * come from the kid. So we propagate these flags into genfn. For code
7062 * simplicity we also do not detect if the flags were only set in the
7063 * kid and could be removed from pc->sc.
7065 genFunbox->anyCxFlags = outerpc->sc->anyCxFlags;
7066 if (outerpc->sc->isFunctionBox())
7067 genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
7069 MOZ_ASSERT(genFunbox->generatorKind() == comprehensionKind);
7070 genFunbox->inGenexpLambda = true;
7071 handler.setBlockId(genfn, genpc.bodyid);
7073 Node generator = newName(context->names().dotGenerator);
7074 if (!generator)
7075 return null();
7076 if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR))
7077 return null();
7079 Node body = handler.newStatementList(pc->blockid(), TokenPos(begin, pos().end));
7080 if (!body)
7081 return null();
7083 Node comp;
7084 if (comprehensionKind == StarGenerator) {
7085 comp = comprehension(StarGenerator);
7086 if (!comp)
7087 return null();
7088 } else {
7089 MOZ_ASSERT(comprehensionKind == LegacyGenerator);
7090 comp = legacyComprehensionTail(innerExpr, outerpc->blockid(), LegacyGenerator,
7091 outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc));
7092 if (!comp)
7093 return null();
7096 if (comprehensionKind == StarGenerator)
7097 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
7099 handler.setBeginPosition(comp, begin);
7100 handler.setEndPosition(comp, pos().end);
7101 handler.addStatementToList(body, comp, pc);
7102 handler.setEndPosition(body, pos().end);
7103 handler.setBeginPosition(genfn, begin);
7104 handler.setEndPosition(genfn, pos().end);
7106 generator = newName(context->names().dotGenerator);
7107 if (!generator)
7108 return null();
7109 if (!noteNameUse(context->names().dotGenerator, generator))
7110 return null();
7111 if (!handler.prependInitialYield(body, generator))
7112 return null();
7114 // Note that if we ever start syntax-parsing generators, we will also
7115 // need to propagate the closed-over variable set to the inner
7116 // lazyscript, as in finishFunctionDefinition.
7117 handler.setFunctionBody(genfn, body);
7119 PropagateTransitiveParseFlags(genFunbox, outerpc->sc);
7121 if (!leaveFunction(genfn, outerpc))
7122 return null();
7124 return genfn;
7127 #if JS_HAS_GENERATOR_EXPRS
7130 * Starting from a |for| keyword after an expression, parse the comprehension
7131 * tail completing this generator expression. Wrap the expression at kid in a
7132 * generator function that is immediately called to evaluate to the generator
7133 * iterator that is the value of this legacy generator expression.
7135 * |kid| must be the expression before the |for| keyword; we return an
7136 * application of a generator function that includes the |for| loops and
7137 * |if| guards, with |kid| as the operand of a |yield| expression as the
7138 * innermost loop body.
7140 * Note how unlike Python, we do not evaluate the expression to the right of
7141 * the first |in| in the chain of |for| heads. Instead, a generator expression
7142 * is merely sugar for a generator function expression and its application.
7144 template <>
7145 ParseNode*
7146 Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode* expr)
7148 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
7150 /* Make a new node for the desugared generator function. */
7151 ParseNode* genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, expr);
7152 if (!genfn)
7153 return null();
7156 * Our result is a call expression that invokes the anonymous generator
7157 * function object.
7159 ParseNode* result = ListNode::create(PNK_GENEXP, &handler);
7160 if (!result)
7161 return null();
7162 result->setOp(JSOP_CALL);
7163 result->pn_pos.begin = genfn->pn_pos.begin;
7164 result->initList(genfn);
7165 return result;
7168 template <>
7169 SyntaxParseHandler::Node
7170 Parser<SyntaxParseHandler>::legacyGeneratorExpr(Node kid)
7172 JS_ALWAYS_FALSE(abortIfSyntaxParser());
7173 return SyntaxParseHandler::NodeFailure;
7176 static const char js_generator_str[] = "generator";
7178 #endif /* JS_HAS_GENERATOR_EXPRS */
7180 template <typename ParseHandler>
7181 typename ParseHandler::Node
7182 Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
7184 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
7186 uint32_t begin = pos().begin;
7188 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
7190 // FIXME: Destructuring binding (bug 980828).
7192 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
7193 RootedPropertyName name(context, tokenStream.currentName());
7194 if (name == context->names().let) {
7195 report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
7196 return null();
7198 bool matched;
7199 if (!tokenStream.matchContextualKeyword(&matched, context->names().of))
7200 return null();
7201 if (!matched) {
7202 report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
7203 return null();
7206 Node rhs = assignExpr();
7207 if (!rhs)
7208 return null();
7210 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
7212 TokenPos headPos(begin, pos().end);
7214 StmtInfoPC stmtInfo(context);
7215 BindData<ParseHandler> data(context);
7216 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
7217 if (!blockObj)
7218 return null();
7219 data.initLexical(DontHoistVars, blockObj, JSMSG_TOO_MANY_LOCALS);
7220 Node lhs = newName(name);
7221 if (!lhs)
7222 return null();
7223 Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP);
7224 if (!decls)
7225 return null();
7226 data.pn = lhs;
7227 if (!data.binder(&data, name, this))
7228 return null();
7229 Node letScope = pushLetScope(blockObj, &stmtInfo);
7230 if (!letScope)
7231 return null();
7232 handler.setLexicalScopeBody(letScope, decls);
7234 Node assignLhs = newName(name);
7235 if (!assignLhs)
7236 return null();
7237 if (!noteNameUse(name, assignLhs))
7238 return null();
7239 handler.setOp(assignLhs, JSOP_SETNAME);
7241 Node head = handler.newForHead(PNK_FOROF, letScope, assignLhs, rhs, headPos);
7242 if (!head)
7243 return null();
7245 Node tail = comprehensionTail(comprehensionKind);
7246 if (!tail)
7247 return null();
7249 PopStatementPC(tokenStream, pc);
7251 return handler.newForStatement(begin, head, tail, JSOP_ITER);
7254 template <typename ParseHandler>
7255 typename ParseHandler::Node
7256 Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
7258 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
7260 uint32_t begin = pos().begin;
7262 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
7263 Node cond = assignExpr();
7264 if (!cond)
7265 return null();
7266 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
7268 /* Check for (a = b) and warn about possible (a == b) mistype. */
7269 if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) &&
7270 !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
7272 return null();
7275 Node then = comprehensionTail(comprehensionKind);
7276 if (!then)
7277 return null();
7279 return handler.newIfStatement(begin, cond, then, null());
7282 template <typename ParseHandler>
7283 typename ParseHandler::Node
7284 Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
7286 JS_CHECK_RECURSION(context, return null());
7288 bool matched;
7289 if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
7290 return null();
7291 if (matched)
7292 return comprehensionFor(comprehensionKind);
7294 if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
7295 return null();
7296 if (matched)
7297 return comprehensionIf(comprehensionKind);
7299 uint32_t begin = pos().begin;
7301 Node bodyExpr = assignExpr();
7302 if (!bodyExpr)
7303 return null();
7305 if (comprehensionKind == NotGenerator)
7306 return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
7308 MOZ_ASSERT(comprehensionKind == StarGenerator);
7309 Node yieldExpr = newYieldExpression(begin, bodyExpr);
7310 if (!yieldExpr)
7311 return null();
7312 handler.setInParens(yieldExpr);
7314 return handler.newExprStatement(yieldExpr, pos().end);
7317 // Parse an ES6 generator or array comprehension, starting at the first 'for'.
7318 // The caller is responsible for matching the ending TOK_RP or TOK_RB.
7319 template <typename ParseHandler>
7320 typename ParseHandler::Node
7321 Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
7323 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
7325 uint32_t startYieldOffset = pc->lastYieldOffset;
7327 Node body = comprehensionFor(comprehensionKind);
7328 if (!body)
7329 return null();
7331 if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
7332 reportWithOffset(ParseError, false, pc->lastYieldOffset,
7333 JSMSG_BAD_GENEXP_BODY, js_yield_str);
7334 return null();
7337 return body;
7340 template <typename ParseHandler>
7341 typename ParseHandler::Node
7342 Parser<ParseHandler>::arrayComprehension(uint32_t begin)
7344 Node inner = comprehension(NotGenerator);
7345 if (!inner)
7346 return null();
7348 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
7350 Node comp = handler.newList(PNK_ARRAYCOMP, inner);
7351 if (!comp)
7352 return null();
7354 handler.setBeginPosition(comp, begin);
7355 handler.setEndPosition(comp, pos().end);
7357 return comp;
7360 template <typename ParseHandler>
7361 typename ParseHandler::Node
7362 Parser<ParseHandler>::generatorComprehension(uint32_t begin)
7364 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
7366 // We have no problem parsing generator comprehensions inside lazy
7367 // functions, but the bytecode emitter currently can't handle them that way,
7368 // because when it goes to emit the code for the inner generator function,
7369 // it expects outer functions to have non-lazy scripts.
7370 if (!abortIfSyntaxParser())
7371 return null();
7373 Node genfn = generatorComprehensionLambda(StarGenerator, begin, null());
7374 if (!genfn)
7375 return null();
7377 Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL);
7378 if (!result)
7379 return null();
7380 handler.setBeginPosition(result, begin);
7381 handler.setEndPosition(result, pos().end);
7383 return result;
7386 template <typename ParseHandler>
7387 typename ParseHandler::Node
7388 Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
7390 uint32_t startYieldOffset = pc->lastYieldOffset;
7391 Node res = assignExpr();
7392 if (res && pc->lastYieldOffset != startYieldOffset) {
7393 reportWithOffset(ParseError, false, pc->lastYieldOffset,
7394 msg, js_yield_str);
7395 return null();
7397 return res;
7400 template <typename ParseHandler>
7401 bool
7402 Parser<ParseHandler>::argumentList(Node listNode, bool* isSpread)
7404 bool matched;
7405 if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
7406 return false;
7407 if (matched) {
7408 handler.setEndPosition(listNode, pos().end);
7409 return true;
7412 uint32_t startYieldOffset = pc->lastYieldOffset;
7413 bool arg0 = true;
7415 while (true) {
7416 bool spread = false;
7417 uint32_t begin = 0;
7418 if (!tokenStream.matchToken(&matched, TOK_TRIPLEDOT, TokenStream::Operand))
7419 return false;
7420 if (matched) {
7421 spread = true;
7422 begin = pos().begin;
7423 *isSpread = true;
7426 Node argNode = assignExpr();
7427 if (!argNode)
7428 return false;
7429 if (spread) {
7430 argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
7431 if (!argNode)
7432 return false;
7435 if (handler.isOperationWithoutParens(argNode, PNK_YIELD)) {
7436 TokenKind tt;
7437 if (!tokenStream.peekToken(&tt))
7438 return false;
7439 if (tt == TOK_COMMA) {
7440 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
7441 return false;
7444 #if JS_HAS_GENERATOR_EXPRS
7445 if (!spread) {
7446 if (!tokenStream.matchToken(&matched, TOK_FOR))
7447 return false;
7448 if (matched) {
7449 if (pc->lastYieldOffset != startYieldOffset) {
7450 reportWithOffset(ParseError, false, pc->lastYieldOffset,
7451 JSMSG_BAD_GENEXP_BODY, js_yield_str);
7452 return false;
7454 argNode = legacyGeneratorExpr(argNode);
7455 if (!argNode)
7456 return false;
7457 if (!arg0) {
7458 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
7459 return false;
7461 TokenKind tt;
7462 if (!tokenStream.peekToken(&tt))
7463 return false;
7464 if (tt == TOK_COMMA) {
7465 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
7466 return false;
7470 #endif
7471 arg0 = false;
7473 handler.addList(listNode, argNode);
7475 bool matched;
7476 if (!tokenStream.matchToken(&matched, TOK_COMMA))
7477 return false;
7478 if (!matched)
7479 break;
7482 TokenKind tt;
7483 if (!tokenStream.getToken(&tt))
7484 return false;
7485 if (tt != TOK_RP) {
7486 report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
7487 return false;
7489 handler.setEndPosition(listNode, pos().end);
7490 return true;
7493 template <typename ParseHandler>
7494 typename ParseHandler::Node
7495 Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
7497 MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
7499 Node lhs;
7501 JS_CHECK_RECURSION(context, return null());
7503 /* Check for new expression first. */
7504 if (tt == TOK_NEW) {
7505 lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
7506 if (!lhs)
7507 return null();
7509 if (!tokenStream.getToken(&tt, TokenStream::Operand))
7510 return null();
7511 Node ctorExpr = memberExpr(tt, false);
7512 if (!ctorExpr)
7513 return null();
7515 handler.addList(lhs, ctorExpr);
7517 bool matched;
7518 if (!tokenStream.matchToken(&matched, TOK_LP))
7519 return null();
7520 if (matched) {
7521 bool isSpread = false;
7522 if (!argumentList(lhs, &isSpread))
7523 return null();
7524 if (isSpread)
7525 handler.setOp(lhs, JSOP_SPREADNEW);
7527 } else {
7528 lhs = primaryExpr(tt);
7529 if (!lhs)
7530 return null();
7533 while (true) {
7534 if (!tokenStream.getToken(&tt))
7535 return null();
7536 if (tt == TOK_EOF)
7537 break;
7539 Node nextMember;
7540 if (tt == TOK_DOT) {
7541 if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
7542 return null();
7543 if (tt == TOK_NAME) {
7544 PropertyName* field = tokenStream.currentName();
7545 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
7546 if (!nextMember)
7547 return null();
7548 } else {
7549 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
7550 return null();
7552 } else if (tt == TOK_LB) {
7553 Node propExpr = expr();
7554 if (!propExpr)
7555 return null();
7557 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
7559 nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
7560 if (!nextMember)
7561 return null();
7562 } else if ((allowCallSyntax && tt == TOK_LP) ||
7563 tt == TOK_TEMPLATE_HEAD ||
7564 tt == TOK_NO_SUBS_TEMPLATE)
7566 JSOp op = JSOP_CALL;
7567 if (tt == TOK_LP)
7568 nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL);
7569 else
7570 nextMember = handler.newList(PNK_TAGGED_TEMPLATE, null(), JSOP_CALL);
7572 if (!nextMember)
7573 return null();
7575 if (JSAtom* atom = handler.isName(lhs)) {
7576 if (tt == TOK_LP && atom == context->names().eval) {
7577 /* Select JSOP_EVAL and flag pc as heavyweight. */
7578 op = pc->sc->strict ? JSOP_STRICTEVAL : JSOP_EVAL;
7579 pc->sc->setBindingsAccessedDynamically();
7582 * In non-strict mode code, direct calls to eval can add
7583 * variables to the call object.
7585 if (pc->sc->isFunctionBox() && !pc->sc->strict)
7586 pc->sc->asFunctionBox()->setHasExtensibleScope();
7588 } else if (JSAtom* atom = handler.isGetProp(lhs)) {
7589 /* Select JSOP_FUNAPPLY given foo.apply(...). */
7590 if (atom == context->names().apply) {
7591 op = JSOP_FUNAPPLY;
7592 if (pc->sc->isFunctionBox())
7593 pc->sc->asFunctionBox()->usesApply = true;
7594 } else if (atom == context->names().call) {
7595 op = JSOP_FUNCALL;
7599 handler.setBeginPosition(nextMember, lhs);
7600 handler.addList(nextMember, lhs);
7602 if (tt == TOK_LP) {
7603 bool isSpread = false;
7604 if (!argumentList(nextMember, &isSpread))
7605 return null();
7606 if (isSpread) {
7607 if (op == JSOP_EVAL)
7608 op = JSOP_SPREADEVAL;
7609 else if (op == JSOP_STRICTEVAL)
7610 op = JSOP_STRICTSPREADEVAL;
7611 else
7612 op = JSOP_SPREADCALL;
7614 } else {
7615 if (!taggedTemplate(nextMember, tt))
7616 return null();
7618 handler.setOp(nextMember, op);
7619 } else {
7620 tokenStream.ungetToken();
7621 return lhs;
7624 lhs = nextMember;
7626 return lhs;
7629 template <typename ParseHandler>
7630 typename ParseHandler::Node
7631 Parser<ParseHandler>::newName(PropertyName* name)
7633 return handler.newName(name, pc->blockid(), pos());
7636 template <typename ParseHandler>
7637 typename ParseHandler::Node
7638 Parser<ParseHandler>::identifierName()
7640 RootedPropertyName name(context, tokenStream.currentName());
7641 Node pn = newName(name);
7642 if (!pn)
7643 return null();
7645 if (!pc->inDeclDestructuring && !noteNameUse(name, pn))
7646 return null();
7648 return pn;
7651 template <typename ParseHandler>
7652 typename ParseHandler::Node
7653 Parser<ParseHandler>::stringLiteral()
7655 return handler.newStringLiteral(stopStringCompression(), pos());
7658 template <typename ParseHandler>
7659 typename ParseHandler::Node
7660 Parser<ParseHandler>::noSubstitutionTemplate()
7662 return handler.newTemplateStringLiteral(stopStringCompression(), pos());
7665 template <typename ParseHandler>
7666 JSAtom * Parser<ParseHandler>::stopStringCompression() {
7667 JSAtom* atom = tokenStream.currentToken().atom();
7669 // Large strings are fast to parse but slow to compress. Stop compression on
7670 // them, so we don't wait for a long time for compression to finish at the
7671 // end of compilation.
7672 const size_t HUGE_STRING = 50000;
7673 if (sct && sct->active() && atom->length() >= HUGE_STRING)
7674 sct->abort();
7675 return atom;
7678 template <typename ParseHandler>
7679 typename ParseHandler::Node
7680 Parser<ParseHandler>::newRegExp()
7682 // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
7683 const char16_t* chars = tokenStream.getTokenbuf().begin();
7684 size_t length = tokenStream.getTokenbuf().length();
7685 RegExpFlag flags = tokenStream.currentToken().regExpFlags();
7687 Rooted<RegExpObject*> reobj(context);
7688 RegExpStatics* res = context->global()->getRegExpStatics(context);
7689 if (!res)
7690 return null();
7692 reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream, alloc);
7693 if (!reobj)
7694 return null();
7696 return handler.newRegExp(reobj, pos(), *this);
7699 template <typename ParseHandler>
7700 typename ParseHandler::Node
7701 Parser<ParseHandler>::arrayInitializer()
7703 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
7705 uint32_t begin = pos().begin;
7706 Node literal = handler.newArrayLiteral(begin, pc->blockidGen);
7707 if (!literal)
7708 return null();
7710 TokenKind tt;
7711 if (!tokenStream.getToken(&tt, TokenStream::Operand))
7712 return null();
7713 if (tt == TOK_RB) {
7715 * Mark empty arrays as non-constant, since we cannot easily
7716 * determine their type.
7718 handler.setListFlag(literal, PNX_NONCONST);
7719 } else if (tt == TOK_FOR) {
7720 // ES6 array comprehension.
7721 return arrayComprehension(begin);
7722 } else {
7723 tokenStream.ungetToken();
7725 bool spread = false, missingTrailingComma = false;
7726 uint32_t index = 0;
7727 for (; ; index++) {
7728 if (index == NativeObject::NELEMENTS_LIMIT) {
7729 report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
7730 return null();
7733 TokenKind tt;
7734 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
7735 return null();
7736 if (tt == TOK_RB)
7737 break;
7739 if (tt == TOK_COMMA) {
7740 tokenStream.consumeKnownToken(TOK_COMMA);
7741 if (!handler.addElision(literal, pos()))
7742 return null();
7743 } else if (tt == TOK_TRIPLEDOT) {
7744 spread = true;
7745 tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
7746 uint32_t begin = pos().begin;
7747 Node inner = assignExpr();
7748 if (!inner)
7749 return null();
7750 if (!handler.addSpreadElement(literal, begin, inner))
7751 return null();
7752 } else {
7753 Node element = assignExpr();
7754 if (!element)
7755 return null();
7756 if (foldConstants && !FoldConstants(context, &element, this))
7757 return null();
7758 if (!handler.addArrayElement(literal, element))
7759 return null();
7762 if (tt != TOK_COMMA) {
7763 /* If we didn't already match TOK_COMMA in above case. */
7764 bool matched;
7765 if (!tokenStream.matchToken(&matched, TOK_COMMA))
7766 return null();
7767 if (!matched) {
7768 missingTrailingComma = true;
7769 break;
7775 * At this point, (index == 0 && missingTrailingComma) implies one
7776 * element initialiser was parsed.
7778 * A legacy array comprehension of the form:
7780 * [i * j for (i in o) for (j in p) if (i != j)]
7782 * translates to roughly the following let expression:
7784 * let (array = new Array, i, j) {
7785 * for (i in o) let {
7786 * for (j in p)
7787 * if (i != j)
7788 * array.push(i * j)
7790 * array
7793 * where array is a nameless block-local variable. The "roughly" means
7794 * that an implementation may optimize away the array.push. A legacy
7795 * array comprehension opens exactly one block scope, no matter how many
7796 * for heads it contains.
7798 * Each let () {...} or for (let ...) ... compiles to:
7800 * JSOP_PUSHN <N> // Push space for block-scoped locals.
7801 * (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope
7802 * // chain.
7803 * ...
7804 * JSOP_DEBUGLEAVEBLOCK // Invalidate any DebugScope proxies.
7805 * JSOP_POPBLOCKSCOPE? // Pop off scope chain, if needed.
7806 * JSOP_POPN <N> // Pop space for block-scoped locals.
7808 * where <o> is a literal object representing the block scope,
7809 * with <n> properties, naming each var declared in the block.
7811 * Each var declaration in a let-block binds a name in <o> at compile
7812 * time. A block-local var is accessed by the JSOP_GETLOCAL and
7813 * JSOP_SETLOCAL ops. These ops have an immediate operand, the local
7814 * slot's stack index from fp->spbase.
7816 * The legacy array comprehension iteration step, array.push(i * j) in
7817 * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, where
7818 * <array> is the index of array's stack slot.
7820 if (index == 0 && !spread) {
7821 bool matched;
7822 if (!tokenStream.matchToken(&matched, TOK_FOR))
7823 return null();
7824 if (matched && missingTrailingComma)
7825 return legacyArrayComprehension(literal);
7828 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
7830 handler.setEndPosition(literal, pos().end);
7831 return literal;
7834 static JSAtom*
7835 DoubleToAtom(ExclusiveContext* cx, double value)
7837 // This is safe because doubles can not be moved.
7838 Value tmp = DoubleValue(value);
7839 return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp));
7842 template <typename ParseHandler>
7843 typename ParseHandler::Node
7844 Parser<ParseHandler>::computedPropertyName(Node literal)
7846 uint32_t begin = pos().begin;
7848 // Turn off the inDeclDestructuring flag when parsing computed property
7849 // names. In short, when parsing 'let {[x + y]: z} = obj;', noteNameUse()
7850 // should be called on x and y, but not on z. See the comment on
7851 // Parser<>::checkDestructuring() for details.
7852 bool saved = pc->inDeclDestructuring;
7853 pc->inDeclDestructuring = false;
7854 Node assignNode = assignExpr();
7855 pc->inDeclDestructuring = saved;
7856 if (!assignNode)
7857 return null();
7859 MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
7860 Node propname = handler.newComputedName(assignNode, begin, pos().end);
7861 if (!propname)
7862 return null();
7863 handler.setListFlag(literal, PNX_NONCONST);
7864 return propname;
7867 template <typename ParseHandler>
7868 typename ParseHandler::Node
7869 Parser<ParseHandler>::objectLiteral()
7871 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
7873 Node literal = handler.newObjectLiteral(pos().begin);
7874 if (!literal)
7875 return null();
7877 bool seenPrototypeMutation = false;
7878 RootedAtom atom(context);
7879 for (;;) {
7880 TokenKind ltok;
7881 if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
7882 return null();
7883 if (ltok == TOK_RC)
7884 break;
7886 bool isGenerator = false;
7887 if (ltok == TOK_MUL) {
7888 isGenerator = true;
7889 if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
7890 return null();
7893 atom = nullptr;
7895 JSOp op = JSOP_INITPROP;
7896 Node propname;
7897 switch (ltok) {
7898 case TOK_NUMBER:
7899 atom = DoubleToAtom(context, tokenStream.currentToken().number());
7900 if (!atom)
7901 return null();
7902 propname = newNumber(tokenStream.currentToken());
7903 break;
7905 case TOK_LB: {
7906 propname = computedPropertyName(literal);
7907 if (!propname)
7908 return null();
7909 break;
7912 case TOK_NAME: {
7913 atom = tokenStream.currentName();
7914 if (atom == context->names().get) {
7915 if (isGenerator) {
7916 report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
7917 return null();
7919 op = JSOP_INITPROP_GETTER;
7920 } else if (atom == context->names().set) {
7921 if (isGenerator) {
7922 report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
7923 return null();
7925 op = JSOP_INITPROP_SETTER;
7926 } else {
7927 propname = handler.newIdentifier(atom, pos());
7928 if (!propname)
7929 return null();
7930 break;
7933 // We have parsed |get| or |set|. Look for an accessor property
7934 // name next.
7935 TokenKind tt;
7936 if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
7937 return null();
7938 if (tt == TOK_NAME) {
7939 atom = tokenStream.currentName();
7940 propname = newName(atom->asPropertyName());
7941 if (!propname)
7942 return null();
7943 } else if (tt == TOK_STRING) {
7944 atom = tokenStream.currentToken().atom();
7946 uint32_t index;
7947 if (atom->isIndex(&index)) {
7948 propname = handler.newNumber(index, NoDecimal, pos());
7949 if (!propname)
7950 return null();
7951 atom = DoubleToAtom(context, index);
7952 if (!atom)
7953 return null();
7954 } else {
7955 propname = stringLiteral();
7956 if (!propname)
7957 return null();
7959 } else if (tt == TOK_NUMBER) {
7960 atom = DoubleToAtom(context, tokenStream.currentToken().number());
7961 if (!atom)
7962 return null();
7963 propname = newNumber(tokenStream.currentToken());
7964 if (!propname)
7965 return null();
7966 } else if (tt == TOK_LB) {
7967 propname = computedPropertyName(literal);
7968 if (!propname)
7969 return null();
7970 } else {
7971 // Not an accessor property after all.
7972 tokenStream.ungetToken();
7973 propname = handler.newIdentifier(atom, pos());
7974 if (!propname)
7975 return null();
7976 op = JSOP_INITPROP;
7977 break;
7980 MOZ_ASSERT(op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER);
7981 break;
7984 case TOK_STRING: {
7985 atom = tokenStream.currentToken().atom();
7986 uint32_t index;
7987 if (atom->isIndex(&index)) {
7988 propname = handler.newNumber(index, NoDecimal, pos());
7989 if (!propname)
7990 return null();
7991 } else {
7992 propname = stringLiteral();
7993 if (!propname)
7994 return null();
7996 break;
7999 default:
8000 report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
8001 return null();
8004 if (op == JSOP_INITPROP) {
8005 TokenKind tt;
8006 if (!tokenStream.getToken(&tt))
8007 return null();
8009 if (tt == TOK_COLON) {
8010 if (isGenerator) {
8011 report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
8012 return null();
8015 Node propexpr = assignExpr();
8016 if (!propexpr)
8017 return null();
8019 if (foldConstants && !FoldConstants(context, &propexpr, this))
8020 return null();
8022 if (atom == context->names().proto) {
8023 if (seenPrototypeMutation) {
8024 report(ParseError, false, propname, JSMSG_DUPLICATE_PROPERTY, "__proto__");
8025 return null();
8027 seenPrototypeMutation = true;
8029 // Note: this occurs *only* if we observe TOK_COLON! Only
8030 // __proto__: v mutates [[Prototype]]. Getters, setters,
8031 // method/generator definitions, computed property name
8032 // versions of all of these, and shorthands do not.
8033 uint32_t begin = handler.getPosition(propname).begin;
8034 if (!handler.addPrototypeMutation(literal, begin, propexpr))
8035 return null();
8036 } else {
8037 if (!handler.isConstant(propexpr))
8038 handler.setListFlag(literal, PNX_NONCONST);
8040 if (!handler.addPropertyDefinition(literal, propname, propexpr))
8041 return null();
8043 } else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
8045 * Support, e.g., |var {x, y} = o| as destructuring shorthand
8046 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8048 if (isGenerator) {
8049 report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
8050 return null();
8052 if (!abortIfSyntaxParser())
8053 return null();
8054 tokenStream.ungetToken();
8055 if (!tokenStream.checkForKeyword(atom, nullptr))
8056 return null();
8057 PropertyName* name = handler.isName(propname);
8058 MOZ_ASSERT(atom);
8059 propname = newName(name);
8060 if (!propname)
8061 return null();
8062 Node ident = identifierName();
8063 if (!handler.addPropertyDefinition(literal, propname, ident, true))
8064 return null();
8065 } else if (tt == TOK_LP) {
8066 tokenStream.ungetToken();
8067 if (!methodDefinition(literal, propname, Normal, Method,
8068 isGenerator ? StarGenerator : NotGenerator, op)) {
8069 return null();
8071 } else {
8072 report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
8073 return null();
8075 } else {
8076 /* NB: Getter function in { get x(){} } is unnamed. */
8077 if (!methodDefinition(literal, propname, op == JSOP_INITPROP_GETTER ? Getter : Setter,
8078 Expression, NotGenerator, op)) {
8079 return null();
8083 TokenKind tt;
8084 if (!tokenStream.getToken(&tt))
8085 return null();
8086 if (tt == TOK_RC)
8087 break;
8088 if (tt != TOK_COMMA) {
8089 report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
8090 return null();
8094 handler.setEndPosition(literal, pos().end);
8095 return literal;
8098 template <typename ParseHandler>
8099 bool
8100 Parser<ParseHandler>::methodDefinition(Node literal, Node propname, FunctionType type,
8101 FunctionSyntaxKind kind, GeneratorKind generatorKind,
8102 JSOp op)
8104 RootedPropertyName funName(context);
8105 if (kind == Method && tokenStream.isCurrentTokenType(TOK_NAME))
8106 funName = tokenStream.currentName();
8107 else
8108 funName = nullptr;
8110 Node fn = functionDef(funName, type, kind, generatorKind);
8111 if (!fn)
8112 return false;
8113 if (!handler.addMethodDefinition(literal, propname, fn, op))
8114 return false;
8115 return true;
8118 template <typename ParseHandler>
8119 typename ParseHandler::Node
8120 Parser<ParseHandler>::primaryExpr(TokenKind tt)
8122 MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
8123 JS_CHECK_RECURSION(context, return null());
8125 switch (tt) {
8126 case TOK_FUNCTION:
8127 return functionExpr();
8129 case TOK_LB:
8130 return arrayInitializer();
8132 case TOK_LC:
8133 return objectLiteral();
8135 case TOK_LET:
8136 return letBlock(LetExpression);
8138 case TOK_LP: {
8139 TokenKind next;
8140 if (!tokenStream.peekToken(&next, TokenStream::Operand))
8141 return null();
8142 if (next != TOK_RP)
8143 return parenExprOrGeneratorComprehension();
8145 // Not valid expression syntax, but this is valid in an arrow function
8146 // with no params: `() => body`.
8147 tokenStream.consumeKnownToken(next);
8149 if (!tokenStream.peekToken(&next))
8150 return null();
8151 if (next != TOK_ARROW) {
8152 report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
8153 "expression", TokenKindToDesc(TOK_RP));
8154 return null();
8157 // Now just return something that will allow parsing to continue.
8158 // It doesn't matter what; when we reach the =>, we will rewind and
8159 // reparse the whole arrow function. See Parser::assignExpr.
8160 return handler.newNullLiteral(pos());
8163 case TOK_TEMPLATE_HEAD:
8164 return templateLiteral();
8166 case TOK_NO_SUBS_TEMPLATE:
8167 return noSubstitutionTemplate();
8169 case TOK_STRING:
8170 return stringLiteral();
8172 case TOK_YIELD:
8173 if (!checkYieldNameValidity())
8174 return null();
8175 // Fall through.
8176 case TOK_NAME:
8177 return identifierName();
8179 case TOK_REGEXP:
8180 return newRegExp();
8182 case TOK_NUMBER:
8183 return newNumber(tokenStream.currentToken());
8185 case TOK_TRUE:
8186 return handler.newBooleanLiteral(true, pos());
8187 case TOK_FALSE:
8188 return handler.newBooleanLiteral(false, pos());
8189 case TOK_THIS:
8190 if (pc->sc->isFunctionBox())
8191 pc->sc->asFunctionBox()->usesThis = true;
8192 return handler.newThisLiteral(pos());
8193 case TOK_NULL:
8194 return handler.newNullLiteral(pos());
8196 case TOK_TRIPLEDOT: {
8197 TokenKind next;
8199 // This isn't valid expression syntax, but it's valid in an arrow
8200 // function as a trailing rest param: `(a, b, ...rest) => body`. Check
8201 // for a name, closing parenthesis, and arrow, and allow it only if all
8202 // are present.
8203 if (!tokenStream.getToken(&next))
8204 return null();
8205 if (next != TOK_NAME) {
8206 report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
8207 "rest argument name", TokenKindToDesc(next));
8208 return null();
8211 if (!tokenStream.getToken(&next))
8212 return null();
8213 if (next != TOK_RP) {
8214 report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
8215 "closing parenthesis", TokenKindToDesc(next));
8216 return null();
8219 if (!tokenStream.peekToken(&next))
8220 return null();
8221 if (next != TOK_ARROW) {
8222 report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
8223 "'=>' after argument list", TokenKindToDesc(next));
8224 return null();
8227 tokenStream.ungetToken(); // put back right paren
8229 // Return an arbitrary expression node. See case TOK_RP above.
8230 return handler.newNullLiteral(pos());
8233 default:
8234 report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
8235 "expression", TokenKindToDesc(tt));
8236 return null();
8240 template <typename ParseHandler>
8241 typename ParseHandler::Node
8242 Parser<ParseHandler>::parenExprOrGeneratorComprehension()
8244 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
8245 uint32_t begin = pos().begin;
8246 uint32_t startYieldOffset = pc->lastYieldOffset;
8248 bool matched;
8249 if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
8250 return null();
8251 if (matched)
8252 return generatorComprehension(begin);
8255 * Always accept the 'in' operator in a parenthesized expression,
8256 * where it's unambiguous, even if we might be parsing the init of a
8257 * for statement.
8259 bool oldParsingForInit = pc->parsingForInit;
8260 pc->parsingForInit = false;
8261 Node pn = expr();
8262 pc->parsingForInit = oldParsingForInit;
8264 if (!pn)
8265 return null();
8267 #if JS_HAS_GENERATOR_EXPRS
8268 if (!tokenStream.matchToken(&matched, TOK_FOR))
8269 return null();
8270 if (matched) {
8271 if (pc->lastYieldOffset != startYieldOffset) {
8272 reportWithOffset(ParseError, false, pc->lastYieldOffset,
8273 JSMSG_BAD_GENEXP_BODY, js_yield_str);
8274 return null();
8276 if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
8277 report(ParseError, false, null(),
8278 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
8279 return null();
8281 pn = legacyGeneratorExpr(pn);
8282 if (!pn)
8283 return null();
8284 handler.setBeginPosition(pn, begin);
8285 TokenKind tt;
8286 if (!tokenStream.getToken(&tt))
8287 return null();
8288 if (tt != TOK_RP) {
8289 report(ParseError, false, null(),
8290 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
8291 return null();
8293 handler.setEndPosition(pn, pos().end);
8294 handler.setInParens(pn);
8295 return pn;
8297 #endif /* JS_HAS_GENERATOR_EXPRS */
8299 pn = handler.setInParens(pn);
8301 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8303 return pn;
8306 // Legacy generator comprehensions can sometimes appear without parentheses.
8307 // For example:
8309 // foo(x for (x in bar))
8311 // In this case the parens are part of the call, and not part of the generator
8312 // comprehension. This can happen in these contexts:
8314 // if (_)
8315 // while (_) {}
8316 // do {} while (_)
8317 // switch (_) {}
8318 // with (_) {}
8319 // foo(_) // must be first and only argument
8321 // This is not the case for ES6 generator comprehensions; they must always be in
8322 // parentheses.
8324 template <typename ParseHandler>
8325 typename ParseHandler::Node
8326 Parser<ParseHandler>::exprInParens()
8328 MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
8329 uint32_t begin = pos().begin;
8330 uint32_t startYieldOffset = pc->lastYieldOffset;
8333 * Always accept the 'in' operator in a parenthesized expression,
8334 * where it's unambiguous, even if we might be parsing the init of a
8335 * for statement.
8337 bool oldParsingForInit = pc->parsingForInit;
8338 pc->parsingForInit = false;
8339 Node pn = expr();
8340 pc->parsingForInit = oldParsingForInit;
8342 if (!pn)
8343 return null();
8345 #if JS_HAS_GENERATOR_EXPRS
8346 bool matched;
8347 if (!tokenStream.matchToken(&matched, TOK_FOR))
8348 return null();
8349 if (matched) {
8350 if (pc->lastYieldOffset != startYieldOffset) {
8351 reportWithOffset(ParseError, false, pc->lastYieldOffset,
8352 JSMSG_BAD_GENEXP_BODY, js_yield_str);
8353 return null();
8355 if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
8356 report(ParseError, false, null(),
8357 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
8358 return null();
8360 pn = legacyGeneratorExpr(pn);
8361 if (!pn)
8362 return null();
8363 handler.setBeginPosition(pn, begin);
8365 #endif /* JS_HAS_GENERATOR_EXPRS */
8367 return pn;
8370 template <typename ParseHandler>
8371 void
8372 Parser<ParseHandler>::accumulateTelemetry()
8374 JSContext* cx = context->maybeJSContext();
8375 if (!cx)
8376 return;
8377 const char* filename = getFilename();
8378 if (!filename)
8379 return;
8381 bool isAddon = !!cx->compartment()->addonId;
8382 bool isHTTP = strncmp(filename, "http://", 7) == 0 || strncmp(filename, "https://", 8) == 0;
8384 // Only report telemetry for web content, not add-ons or chrome JS.
8385 if (isAddon || !isHTTP)
8386 return;
8388 enum DeprecatedLanguageExtensions {
8389 DeprecatedForEach = 0, // JS 1.6+
8390 DeprecatedDestructuringForIn = 1, // JS 1.7 only
8391 DeprecatedLegacyGenerator = 2, // JS 1.7+
8392 DeprecatedExpressionClosure = 3, // Added in JS 1.8, but not version-gated
8393 DeprecatedLetBlock = 4, // Added in JS 1.7, but not version-gated
8394 DeprecatedLetExpression = 5, // Added in JS 1.7, but not version-gated
8397 // Hazard analysis can't tell that the telemetry callbacks don't GC.
8398 JS::AutoSuppressGCAnalysis nogc;
8400 // Call back into Firefox's Telemetry reporter.
8401 int id = JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
8402 if (sawDeprecatedForEach)
8403 cx->runtime()->addTelemetry(id, DeprecatedForEach);
8404 if (sawDeprecatedDestructuringForIn)
8405 cx->runtime()->addTelemetry(id, DeprecatedDestructuringForIn);
8406 if (sawDeprecatedLegacyGenerator)
8407 cx->runtime()->addTelemetry(id, DeprecatedLegacyGenerator);
8408 if (sawDeprecatedExpressionClosure)
8409 cx->runtime()->addTelemetry(id, DeprecatedExpressionClosure);
8410 if (sawDeprecatedLetBlock)
8411 cx->runtime()->addTelemetry(id, DeprecatedLetBlock);
8412 if (sawDeprecatedLetExpression)
8413 cx->runtime()->addTelemetry(id, DeprecatedLetExpression);
8416 template class Parser<FullParseHandler>;
8417 template class Parser<SyntaxParseHandler>;
8419 } /* namespace frontend */
8420 } /* namespace js */