js/src/frontend/Parser.cpp

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

mercurial