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/. */
10 * This is a recursive-descent parser for the JavaScript language specified by
11 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
12 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
13 * induced by the recursive parsing (not precise syntax trees, see Parser.h).
14 * After tree construction, it rewrites trees to fold constants and evaluate
15 * compile-time expressions.
17 * This parser attempts no error recovery.
20 #include "frontend/Parser-inl.h"
31 #include "asmjs/AsmJSValidate.h"
32 #include "frontend/BytecodeCompiler.h"
33 #include "frontend/FoldConstants.h"
34 #include "frontend/ParseMaps.h"
35 #include "frontend/TokenStream.h"
38 #include "jsatominlines.h"
39 #include "jsscriptinlines.h"
41 #include "frontend/ParseNode-inl.h"
44 using namespace js::gc
;
48 using JS::AutoGCRooter
;
53 typedef Rooted
<StaticBlockObject
*> RootedStaticBlockObject
;
54 typedef Handle
<StaticBlockObject
*> HandleStaticBlockObject
;
55 typedef Rooted
<NestedScopeObject
*> RootedNestedScopeObject
;
56 typedef Handle
<NestedScopeObject
*> HandleNestedScopeObject
;
59 /* Read a token. Report an error and return null() if that token isn't of type tt. */
60 #define MUST_MATCH_TOKEN(tt, errno) \
63 if (!tokenStream.getToken(&token)) \
66 report(ParseError, false, null(), errno); \
71 static const unsigned BlockIdLimit
= 1 << ParseNode::NumBlockIdBits
;
73 template <typename ParseHandler
>
75 GenerateBlockId(TokenStream
& ts
, ParseContext
<ParseHandler
>* pc
, uint32_t& blockid
)
77 if (pc
->blockidGen
== BlockIdLimit
) {
78 ts
.reportError(JSMSG_NEED_DIET
, "program");
81 MOZ_ASSERT(pc
->blockidGen
< BlockIdLimit
);
82 blockid
= pc
->blockidGen
++;
87 GenerateBlockId(TokenStream
& ts
, ParseContext
<SyntaxParseHandler
>* pc
, uint32_t& blockid
);
90 GenerateBlockId(TokenStream
& ts
, ParseContext
<FullParseHandler
>* pc
, uint32_t& blockid
);
92 template <typename ParseHandler
>
94 PushStatementPC(ParseContext
<ParseHandler
>* pc
, StmtInfoPC
* stmt
, StmtType type
)
96 stmt
->blockid
= pc
->blockid();
97 PushStatement(pc
, stmt
, type
);
102 ParseContext
<FullParseHandler
>::checkLocalsOverflow(TokenStream
& ts
)
104 if (vars_
.length() + bodyLevelLexicals_
.length() >= LOCALNO_LIMIT
) {
105 ts
.reportError(JSMSG_TOO_MANY_LOCALS
);
112 MarkUsesAsHoistedLexical(ParseNode
* pn
)
114 MOZ_ASSERT(pn
->isDefn());
116 Definition
* dn
= (Definition
*)pn
;
117 ParseNode
** pnup
= &dn
->dn_uses
;
119 unsigned start
= pn
->pn_blockid
;
121 // In ES6, lexical bindings cannot be accessed until initialized.
122 // Distinguish hoisted uses as a different JSOp for easier compilation.
123 while ((pnu
= *pnup
) != nullptr && pnu
->pn_blockid
>= start
) {
124 MOZ_ASSERT(pnu
->isUsed());
125 pnu
->pn_dflags
|= PND_LEXICAL
;
126 pnup
= &pnu
->pn_link
;
130 // See comment on member function declaration.
133 ParseContext
<FullParseHandler
>::define(TokenStream
& ts
,
134 HandlePropertyName name
, ParseNode
* pn
, Definition::Kind kind
)
136 MOZ_ASSERT(!pn
->isUsed());
137 MOZ_ASSERT_IF(pn
->isDefn(), pn
->isPlaceholder());
139 Definition
* prevDef
= nullptr;
140 if (kind
== Definition::LET
|| kind
== Definition::CONST
)
141 prevDef
= decls_
.lookupFirst(name
);
143 MOZ_ASSERT(!decls_
.lookupFirst(name
));
146 prevDef
= lexdeps
.lookupDefn
<FullParseHandler
>(name
);
149 ParseNode
** pnup
= &prevDef
->dn_uses
;
151 unsigned start
= (kind
== Definition::LET
|| kind
== Definition::CONST
) ? pn
->pn_blockid
154 while ((pnu
= *pnup
) != nullptr && pnu
->pn_blockid
>= start
) {
155 MOZ_ASSERT(pnu
->pn_blockid
>= bodyid
);
156 MOZ_ASSERT(pnu
->isUsed());
157 pnu
->pn_lexdef
= (Definition
*) pn
;
158 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
159 pnup
= &pnu
->pn_link
;
162 if (!pnu
|| pnu
!= prevDef
->dn_uses
) {
164 pn
->dn_uses
= prevDef
->dn_uses
;
165 prevDef
->dn_uses
= pnu
;
167 if (!pnu
&& prevDef
->isPlaceholder())
168 lexdeps
->remove(name
);
171 pn
->pn_dflags
|= prevDef
->pn_dflags
& PND_CLOSED
;
174 MOZ_ASSERT_IF(kind
!= Definition::LET
&& kind
!= Definition::CONST
, !lexdeps
->lookup(name
));
176 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
177 if (kind
== Definition::CONST
)
178 pn
->pn_dflags
|= PND_CONST
;
180 Definition
* dn
= (Definition
*)pn
;
182 case Definition::ARG
:
183 MOZ_ASSERT(sc
->isFunctionBox());
184 dn
->setOp((js_CodeSpec
[dn
->getOp()].format
& JOF_SET
) ? JSOP_SETARG
: JSOP_GETARG
);
185 dn
->pn_blockid
= bodyid
;
186 dn
->pn_dflags
|= PND_BOUND
;
187 if (!dn
->pn_cookie
.set(ts
, staticLevel
, args_
.length()))
189 if (!args_
.append(dn
))
191 if (args_
.length() >= ARGNO_LIMIT
) {
192 ts
.reportError(JSMSG_TOO_MANY_FUN_ARGS
);
195 if (name
== ts
.names().empty
)
197 if (!decls_
.addUnique(name
, dn
))
201 case Definition::GLOBALCONST
:
202 case Definition::VAR
:
203 if (sc
->isFunctionBox()) {
204 dn
->setOp((js_CodeSpec
[dn
->getOp()].format
& JOF_SET
) ? JSOP_SETLOCAL
: JSOP_GETLOCAL
);
205 dn
->pn_blockid
= bodyid
;
206 dn
->pn_dflags
|= PND_BOUND
;
207 if (!dn
->pn_cookie
.set(ts
, staticLevel
, vars_
.length()))
209 if (!vars_
.append(dn
))
211 if (!checkLocalsOverflow(ts
))
214 if (!decls_
.addUnique(name
, dn
))
218 case Definition::LET
:
219 case Definition::CONST
:
220 dn
->setOp(JSOP_INITLEXICAL
);
221 dn
->pn_dflags
|= (PND_LEXICAL
| PND_BOUND
);
222 MOZ_ASSERT(dn
->pn_cookie
.level() == staticLevel
); /* see bindLet */
224 if (!bodyLevelLexicals_
.append(dn
))
226 if (!checkLocalsOverflow(ts
))
230 // In ES6, lexical bindings cannot be accessed until initialized. If
231 // the definition has existing uses, they need to be marked so that we
232 // emit dead zone checks.
233 MarkUsesAsHoistedLexical(pn
);
235 if (!decls_
.addShadow(name
, dn
))
240 MOZ_CRASH("unexpected kind");
248 ParseContext
<SyntaxParseHandler
>::checkLocalsOverflow(TokenStream
& ts
)
255 ParseContext
<SyntaxParseHandler
>::define(TokenStream
& ts
, HandlePropertyName name
, Node pn
,
256 Definition::Kind kind
)
258 MOZ_ASSERT(!decls_
.lookupFirst(name
));
260 if (lexdeps
.lookupDefn
<SyntaxParseHandler
>(name
))
261 lexdeps
->remove(name
);
263 // Keep track of the number of arguments in args_, for fun->nargs.
264 if (kind
== Definition::ARG
) {
265 if (!args_
.append((Definition
*) nullptr))
267 if (args_
.length() >= ARGNO_LIMIT
) {
268 ts
.reportError(JSMSG_TOO_MANY_FUN_ARGS
);
273 return decls_
.addUnique(name
, kind
);
276 template <typename ParseHandler
>
278 ParseContext
<ParseHandler
>::prepareToAddDuplicateArg(HandlePropertyName name
, DefinitionNode prevDecl
)
280 MOZ_ASSERT(decls_
.lookupFirst(name
) == prevDecl
);
284 template <typename ParseHandler
>
286 ParseContext
<ParseHandler
>::updateDecl(JSAtom
* atom
, Node pn
)
288 Definition
* oldDecl
= decls_
.lookupFirst(atom
);
291 Definition
* newDecl
= (Definition
*)pn
;
292 decls_
.updateFirst(atom
, newDecl
);
294 if (!sc
->isFunctionBox()) {
295 MOZ_ASSERT(newDecl
->isFreeVar());
299 MOZ_ASSERT(oldDecl
->isBound());
300 MOZ_ASSERT(!oldDecl
->pn_cookie
.isFree());
301 newDecl
->pn_cookie
= oldDecl
->pn_cookie
;
302 newDecl
->pn_dflags
|= PND_BOUND
;
303 if (IsArgOp(oldDecl
->getOp())) {
304 newDecl
->setOp(JSOP_GETARG
);
305 MOZ_ASSERT(args_
[oldDecl
->pn_cookie
.slot()] == oldDecl
);
306 args_
[oldDecl
->pn_cookie
.slot()] = newDecl
;
308 MOZ_ASSERT(IsLocalOp(oldDecl
->getOp()));
309 newDecl
->setOp(JSOP_GETLOCAL
);
310 MOZ_ASSERT(vars_
[oldDecl
->pn_cookie
.slot()] == oldDecl
);
311 vars_
[oldDecl
->pn_cookie
.slot()] = newDecl
;
315 template <typename ParseHandler
>
317 ParseContext
<ParseHandler
>::popLetDecl(JSAtom
* atom
)
319 MOZ_ASSERT(ParseHandler::getDefinitionKind(decls_
.lookupFirst(atom
)) == Definition::LET
||
320 ParseHandler::getDefinitionKind(decls_
.lookupFirst(atom
)) == Definition::CONST
);
324 template <typename ParseHandler
>
326 AppendPackedBindings(const ParseContext
<ParseHandler
>* pc
, const DeclVector
& vec
, Binding
* dst
,
327 uint32_t* numUnaliased
= nullptr)
329 for (size_t i
= 0; i
< vec
.length(); ++i
, ++dst
) {
330 Definition
* dn
= vec
[i
];
331 PropertyName
* name
= dn
->name();
334 switch (dn
->kind()) {
335 case Definition::LET
:
336 // Treat body-level let declarations as var bindings by falling
337 // through. The fact that the binding is in fact a let declaration
338 // is reflected in the slot. All body-level lets go after the
340 case Definition::VAR
:
341 kind
= Binding::VARIABLE
;
343 case Definition::CONST
:
344 case Definition::GLOBALCONST
:
345 kind
= Binding::CONSTANT
;
347 case Definition::ARG
:
348 kind
= Binding::ARGUMENT
;
351 MOZ_CRASH("unexpected dn->kind");
355 * Bindings::init does not check for duplicates so we must ensure that
356 * only one binding with a given name is marked aliased. pc->decls
357 * maintains the canonical definition for each name, so use that.
359 MOZ_ASSERT_IF(dn
->isClosed(), pc
->decls().lookupFirst(name
) == dn
);
360 bool aliased
= dn
->isClosed() ||
361 (pc
->sc
->allLocalsAliased() &&
362 pc
->decls().lookupFirst(name
) == dn
);
364 *dst
= Binding(name
, kind
, aliased
);
365 if (!aliased
&& numUnaliased
)
370 template <typename ParseHandler
>
372 ParseContext
<ParseHandler
>::generateFunctionBindings(ExclusiveContext
* cx
, TokenStream
& ts
,
374 InternalHandle
<Bindings
*> bindings
) const
376 MOZ_ASSERT(sc
->isFunctionBox());
377 MOZ_ASSERT(args_
.length() < ARGNO_LIMIT
);
378 MOZ_ASSERT(vars_
.length() + bodyLevelLexicals_
.length() < LOCALNO_LIMIT
);
381 * Avoid pathological edge cases by explicitly limiting the total number of
382 * bindings to what will fit in a uint32_t.
384 if (UINT32_MAX
- args_
.length() <= vars_
.length() + bodyLevelLexicals_
.length())
385 return ts
.reportError(JSMSG_TOO_MANY_LOCALS
);
387 // Fix up the slots of body-level lets to come after the vars now that we
388 // know how many vars there are.
389 for (size_t i
= 0; i
< bodyLevelLexicals_
.length(); i
++) {
390 Definition
* dn
= bodyLevelLexicals_
[i
];
391 if (!dn
->pn_cookie
.set(ts
, dn
->pn_cookie
.level(), vars_
.length() + i
))
395 uint32_t count
= args_
.length() + vars_
.length() + bodyLevelLexicals_
.length();
396 Binding
* packedBindings
= alloc
.newArrayUninitialized
<Binding
>(count
);
397 if (!packedBindings
) {
398 js_ReportOutOfMemory(cx
);
402 uint32_t numUnaliasedVars
= 0;
403 uint32_t numUnaliasedBodyLevelLexicals
= 0;
405 AppendPackedBindings(this, args_
, packedBindings
);
406 AppendPackedBindings(this, vars_
, packedBindings
+ args_
.length(), &numUnaliasedVars
);
407 AppendPackedBindings(this, bodyLevelLexicals_
,
408 packedBindings
+ args_
.length() + vars_
.length(), &numUnaliasedBodyLevelLexicals
);
410 return Bindings::initWithTemporaryStorage(cx
, bindings
, args_
.length(), vars_
.length(),
411 bodyLevelLexicals_
.length(), blockScopeDepth
,
412 numUnaliasedVars
, numUnaliasedBodyLevelLexicals
,
416 template <typename ParseHandler
>
418 Parser
<ParseHandler
>::reportHelper(ParseReportKind kind
, bool strict
, uint32_t offset
,
419 unsigned errorNumber
, va_list args
)
424 result
= tokenStream
.reportCompileErrorNumberVA(offset
, JSREPORT_ERROR
, errorNumber
, args
);
428 tokenStream
.reportCompileErrorNumberVA(offset
, JSREPORT_WARNING
, errorNumber
, args
);
430 case ParseExtraWarning
:
431 result
= tokenStream
.reportStrictWarningErrorNumberVA(offset
, errorNumber
, args
);
433 case ParseStrictError
:
434 result
= tokenStream
.reportStrictModeErrorNumberVA(offset
, strict
, errorNumber
, args
);
440 template <typename ParseHandler
>
442 Parser
<ParseHandler
>::report(ParseReportKind kind
, bool strict
, Node pn
, unsigned errorNumber
, ...)
444 uint32_t offset
= (pn
? handler
.getPosition(pn
) : pos()).begin
;
447 va_start(args
, errorNumber
);
448 bool result
= reportHelper(kind
, strict
, offset
, errorNumber
, args
);
453 template <typename ParseHandler
>
455 Parser
<ParseHandler
>::reportNoOffset(ParseReportKind kind
, bool strict
, unsigned errorNumber
, ...)
458 va_start(args
, errorNumber
);
459 bool result
= reportHelper(kind
, strict
, TokenStream::NoOffset
, errorNumber
, args
);
464 template <typename ParseHandler
>
466 Parser
<ParseHandler
>::reportWithOffset(ParseReportKind kind
, bool strict
, uint32_t offset
,
467 unsigned errorNumber
, ...)
470 va_start(args
, errorNumber
);
471 bool result
= reportHelper(kind
, strict
, offset
, errorNumber
, args
);
478 Parser
<FullParseHandler
>::abortIfSyntaxParser()
480 handler
.disableSyntaxParser();
486 Parser
<SyntaxParseHandler
>::abortIfSyntaxParser()
488 abortedSyntaxParse
= true;
492 template <typename ParseHandler
>
493 Parser
<ParseHandler
>::Parser(ExclusiveContext
* cx
, LifoAlloc
* alloc
,
494 const ReadOnlyCompileOptions
& options
,
495 const char16_t
* chars
, size_t length
, bool foldConstants
,
496 Parser
<SyntaxParseHandler
>* syntaxParser
,
497 LazyScript
* lazyOuterFunction
)
498 : AutoGCRooter(cx
, PARSER
),
501 tokenStream(cx
, options
, chars
, length
, thisForCtor()),
502 traceListHead(nullptr),
506 keepAtoms(cx
->perThreadData
),
507 foldConstants(foldConstants
),
509 checkOptionsCalled(false),
511 abortedSyntaxParse(false),
512 isUnexpectedEOF_(false),
513 sawDeprecatedForEach(false),
514 sawDeprecatedDestructuringForIn(false),
515 sawDeprecatedLegacyGenerator(false),
516 sawDeprecatedExpressionClosure(false),
517 sawDeprecatedLetBlock(false),
518 sawDeprecatedLetExpression(false),
519 handler(cx
, *alloc
, tokenStream
, foldConstants
, syntaxParser
, lazyOuterFunction
)
522 AutoLockForExclusiveAccess
lock(cx
);
523 cx
->perThreadData
->addActiveCompilation();
526 // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
527 // which are not generated if functions are parsed lazily. Note that the
528 // standard "use strict" does not inhibit lazy parsing.
529 if (options
.extraWarningsOption
)
530 handler
.disableSyntaxParser();
532 tempPoolMark
= alloc
->mark();
535 template<typename ParseHandler
>
537 Parser
<ParseHandler
>::checkOptions()
540 checkOptionsCalled
= true;
543 if (!tokenStream
.checkOptions())
549 template <typename ParseHandler
>
550 Parser
<ParseHandler
>::~Parser()
552 MOZ_ASSERT(checkOptionsCalled
);
554 accumulateTelemetry();
556 alloc
.release(tempPoolMark
);
559 * The parser can allocate enormous amounts of memory for large functions.
560 * Eagerly free the memory now (which otherwise won't be freed until the
561 * next GC) to avoid unnecessary OOMs.
563 alloc
.freeAllIfHugeAndUnused();
566 AutoLockForExclusiveAccess
lock(context
);
567 context
->perThreadData
->removeActiveCompilation();
571 template <typename ParseHandler
>
573 Parser
<ParseHandler
>::newObjectBox(NativeObject
* obj
)
575 MOZ_ASSERT(obj
&& !IsPoisonedPtr(obj
));
578 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
579 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
580 * arenas containing the entries must be alive until we are done with
581 * scanning, parsing and code generation for the whole script or top-level
585 ObjectBox
* objbox
= alloc
.new_
<ObjectBox
>(obj
, traceListHead
);
587 js_ReportOutOfMemory(context
);
591 traceListHead
= objbox
;
596 template <typename ParseHandler
>
597 FunctionBox::FunctionBox(ExclusiveContext
* cx
, ObjectBox
* traceListHead
, JSFunction
* fun
,
598 ParseContext
<ParseHandler
>* outerpc
, Directives directives
,
599 bool extraWarnings
, GeneratorKind generatorKind
)
600 : ObjectBox(fun
, traceListHead
),
601 SharedContext(cx
, directives
, extraWarnings
),
606 generatorKindBits_(GeneratorKindAsBits(generatorKind
)),
607 inWith(false), // initialized below
608 inGenexpLambda(false),
609 hasDestructuringArgs(false),
611 insideUseAsm(outerpc
&& outerpc
->useAsmOrInsideUseAsm()),
612 usesArguments(false),
617 // Functions created at parse time may be set singleton after parsing and
618 // baked into JIT code, so they must be allocated tenured. They are held by
619 // the JSScript so cannot be collected during a minor GC anyway.
620 MOZ_ASSERT(fun
->isTenured());
625 } else if (outerpc
->parsingWith
) {
626 // This covers cases that don't involve eval(). For example:
628 // with (o) { (function() { g(); })(); }
630 // In this case, |outerpc| corresponds to global code, and
631 // outerpc->parsingWith is true.
634 } else if (outerpc
->sc
->isGlobalSharedContext()) {
635 // This covers the case where a function is nested within an eval()
636 // within a |with| statement.
638 // with (o) { eval("(function() { g(); })();"); }
640 // In this case, |outerpc| corresponds to the eval(),
641 // outerpc->parsingWith is false because the eval() breaks the
642 // ParseContext chain, and |parent| is nullptr (again because of the
643 // eval(), so we have to look at |outerpc|'s scopeChain.
645 JSObject
* scope
= outerpc
->sc
->asGlobalSharedContext()->scopeChain();
647 if (scope
->is
<DynamicWithObject
>())
649 scope
= scope
->enclosingScope();
651 } else if (outerpc
->sc
->isFunctionBox()) {
652 // This is like the above case, but for more deeply nested functions.
655 // with (o) { eval("(function() { (function() { g(); })(); })();"); } }
657 // In this case, the inner anonymous function needs to inherit the
658 // setting of |inWith| from the outer one.
659 FunctionBox
* parent
= outerpc
->sc
->asFunctionBox();
660 if (parent
&& parent
->inWith
)
665 template <typename ParseHandler
>
667 Parser
<ParseHandler
>::newFunctionBox(Node fn
, JSFunction
* fun
, ParseContext
<ParseHandler
>* outerpc
,
668 Directives inheritedDirectives
, GeneratorKind generatorKind
)
670 MOZ_ASSERT(fun
&& !IsPoisonedPtr(fun
));
673 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
674 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
675 * arenas containing the entries must be alive until we are done with
676 * scanning, parsing and code generation for the whole script or top-level
679 FunctionBox
* funbox
=
680 alloc
.new_
<FunctionBox
>(context
, traceListHead
, fun
, outerpc
,
681 inheritedDirectives
, options().extraWarningsOption
,
684 js_ReportOutOfMemory(context
);
688 traceListHead
= funbox
;
690 handler
.setFunctionBox(fn
, funbox
);
695 template <typename ParseHandler
>
697 Parser
<ParseHandler
>::trace(JSTracer
* trc
)
699 traceListHead
->trace(trc
);
703 MarkParser(JSTracer
* trc
, AutoGCRooter
* parser
)
705 static_cast<Parser
<FullParseHandler
>*>(parser
)->trace(trc
);
709 * Parse a top-level JS script.
711 template <typename ParseHandler
>
712 typename
ParseHandler::Node
713 Parser
<ParseHandler
>::parse(JSObject
* chain
)
715 MOZ_ASSERT(checkOptionsCalled
);
718 * Protect atoms from being collected by a GC activation, which might
719 * - nest on this thread due to out of memory (the so-called "last ditch"
720 * GC attempted within js_NewGCThing), or
721 * - run for any reason on another thread if this thread is suspended on
722 * an object lock before it finishes generating bytecode into a script
723 * protected from the GC by a root or a stack frame reference.
725 Directives
directives(options().strictOption
);
726 GlobalSharedContext
globalsc(context
, chain
, directives
, options().extraWarningsOption
);
727 ParseContext
<ParseHandler
> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
728 &globalsc
, /* newDirectives = */ nullptr,
729 /* staticLevel = */ 0, /* bodyid = */ 0,
730 /* blockScopeDepth = */ 0);
731 if (!globalpc
.init(tokenStream
))
734 Node pn
= statements();
737 if (!tokenStream
.getToken(&tt
))
740 report(ParseError
, false, null(), JSMSG_GARBAGE_AFTER_INPUT
,
741 "script", TokenKindToDesc(tt
));
745 if (!FoldConstants(context
, &pn
, this))
752 template <typename ParseHandler
>
754 Parser
<ParseHandler
>::reportBadReturn(Node pn
, ParseReportKind kind
,
755 unsigned errnum
, unsigned anonerrnum
)
757 JSAutoByteString name
;
758 JSAtom
* atom
= pc
->sc
->asFunctionBox()->function()->atom();
760 if (!AtomToPrintableString(context
, atom
, &name
))
765 return report(kind
, pc
->sc
->strict
, pn
, errnum
, name
.ptr());
769 * Check that assigning to lhs is permitted. Assigning to 'eval' or
770 * 'arguments' is banned in strict mode.
772 template <typename ParseHandler
>
774 Parser
<ParseHandler
>::checkStrictAssignment(Node lhs
)
776 if (!pc
->sc
->needStrictChecks())
779 JSAtom
* atom
= handler
.isName(lhs
);
783 if (atom
== context
->names().eval
|| atom
== context
->names().arguments
) {
784 JSAutoByteString name
;
785 if (!AtomToPrintableString(context
, atom
, &name
))
788 if (!report(ParseStrictError
, pc
->sc
->strict
, lhs
, JSMSG_BAD_STRICT_ASSIGN
, name
.ptr()))
795 * Check that it is permitted to introduce a binding for atom. Strict mode
796 * forbids introducing new definitions for 'eval', 'arguments', or for any
797 * strict mode reserved keyword. Use pn for reporting error locations, or use
798 * pc's token stream if pn is nullptr.
800 template <typename ParseHandler
>
802 Parser
<ParseHandler
>::checkStrictBinding(PropertyName
* name
, Node pn
)
804 if (!pc
->sc
->needStrictChecks())
807 if (name
== context
->names().eval
|| name
== context
->names().arguments
|| IsKeyword(name
)) {
808 JSAutoByteString bytes
;
809 if (!AtomToPrintableString(context
, name
, &bytes
))
811 return report(ParseStrictError
, pc
->sc
->strict
, pn
,
812 JSMSG_BAD_BINDING
, bytes
.ptr());
820 Parser
<FullParseHandler
>::standaloneFunctionBody(HandleFunction fun
, const AutoNameVector
& formals
,
821 GeneratorKind generatorKind
,
822 Directives inheritedDirectives
,
823 Directives
* newDirectives
)
825 MOZ_ASSERT(checkOptionsCalled
);
827 Node fn
= handler
.newFunctionDefinition();
831 ParseNode
* argsbody
= ListNode::create(PNK_ARGSBODY
, &handler
);
834 argsbody
->setOp(JSOP_NOP
);
835 argsbody
->makeEmpty();
836 fn
->pn_body
= argsbody
;
838 FunctionBox
* funbox
= newFunctionBox(fn
, fun
, /* outerpc = */ nullptr, inheritedDirectives
,
842 funbox
->length
= fun
->nargs() - fun
->hasRest();
843 handler
.setFunctionBox(fn
, funbox
);
845 ParseContext
<FullParseHandler
> funpc(this, pc
, fn
, funbox
, newDirectives
,
846 /* staticLevel = */ 0, /* bodyid = */ 0,
847 /* blockScopeDepth = */ 0);
848 if (!funpc
.init(tokenStream
))
851 for (unsigned i
= 0; i
< formals
.length(); i
++) {
852 if (!defineArg(fn
, formals
[i
]))
856 ParseNode
* pn
= functionBody(Statement
, StatementListBody
);
861 if (!tokenStream
.getToken(&tt
))
864 report(ParseError
, false, null(), JSMSG_GARBAGE_AFTER_INPUT
,
865 "function body", TokenKindToDesc(tt
));
869 if (!FoldConstants(context
, &pn
, this))
872 InternalHandle
<Bindings
*> funboxBindings
=
873 InternalHandle
<Bindings
*>::fromMarkedLocation(&funbox
->bindings
);
874 if (!funpc
.generateFunctionBindings(context
, tokenStream
, alloc
, funboxBindings
))
877 MOZ_ASSERT(fn
->pn_body
->isKind(PNK_ARGSBODY
));
878 fn
->pn_body
->append(pn
);
879 fn
->pn_body
->pn_pos
= pn
->pn_pos
;
885 Parser
<FullParseHandler
>::checkFunctionArguments()
888 * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope
889 * operation which means it aliases any bindings with the same name.
891 if (FuncStmtSet
* set
= pc
->funcStmts
) {
892 for (FuncStmtSet::Range r
= set
->all(); !r
.empty(); r
.popFront()) {
893 PropertyName
* name
= r
.front()->asPropertyName();
894 if (Definition
* dn
= pc
->decls().lookupFirst(name
))
895 dn
->pn_dflags
|= PND_CLOSED
;
899 /* Time to implement the odd semantics of 'arguments'. */
900 HandlePropertyName arguments
= context
->names().arguments
;
903 * As explained by the ContextFlags::funArgumentsHasLocalBinding comment,
904 * create a declaration for 'arguments' if there are any unbound uses in
907 for (AtomDefnRange r
= pc
->lexdeps
->all(); !r
.empty(); r
.popFront()) {
908 if (r
.front().key() == arguments
) {
909 Definition
* dn
= r
.front().value().get
<FullParseHandler
>();
910 pc
->lexdeps
->remove(arguments
);
911 dn
->pn_dflags
|= PND_IMPLICITARGUMENTS
;
912 if (!pc
->define(tokenStream
, arguments
, dn
, Definition::VAR
))
914 pc
->sc
->asFunctionBox()->usesArguments
= true;
920 * Report error if both rest parameters and 'arguments' are used. Do this
921 * check before adding artificial 'arguments' below.
923 Definition
* maybeArgDef
= pc
->decls().lookupFirst(arguments
);
924 bool argumentsHasBinding
= !!maybeArgDef
;
925 // ES6 9.2.13.17 says that a lexical binding of 'arguments' shadows the
927 bool argumentsHasLocalBinding
= maybeArgDef
&& (maybeArgDef
->kind() != Definition::ARG
&&
928 maybeArgDef
->kind() != Definition::LET
&&
929 maybeArgDef
->kind() != Definition::CONST
);
930 bool hasRest
= pc
->sc
->asFunctionBox()->function()->hasRest();
931 if (hasRest
&& argumentsHasLocalBinding
) {
932 report(ParseError
, false, nullptr, JSMSG_ARGUMENTS_AND_REST
);
937 * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
938 * forces an 'arguments' binding. The exception is that functions with rest
939 * parameters are free from 'arguments'.
941 if (!argumentsHasBinding
&& pc
->sc
->bindingsAccessedDynamically() && !hasRest
) {
942 ParseNode
* pn
= newName(arguments
);
945 if (!pc
->define(tokenStream
, arguments
, pn
, Definition::VAR
))
947 argumentsHasBinding
= true;
948 argumentsHasLocalBinding
= true;
952 * Now that all possible 'arguments' bindings have been added, note whether
953 * 'arguments' has a local binding and whether it unconditionally needs an
954 * arguments object. (Also see the flags' comments in ContextFlags.)
956 if (argumentsHasLocalBinding
) {
957 FunctionBox
* funbox
= pc
->sc
->asFunctionBox();
958 funbox
->setArgumentsHasLocalBinding();
961 * If a script has both explicit mentions of 'arguments' and dynamic
962 * name lookups which could access the arguments, an arguments object
963 * must be created eagerly. The SSA analysis used for lazy arguments
964 * cannot cope with dynamic name accesses, so any 'arguments' accessed
965 * via a NAME opcode must force construction of the arguments object.
967 if (pc
->sc
->bindingsAccessedDynamically() && maybeArgDef
)
968 funbox
->setDefinitelyNeedsArgsObj();
971 * If a script contains the debugger statement either directly or
972 * within an inner function, the arguments object must be created
973 * eagerly. The debugger can walk the scope chain and observe any
976 if (pc
->sc
->hasDebuggerStatement())
977 funbox
->setDefinitelyNeedsArgsObj();
980 * Check whether any parameters have been assigned within this
981 * function. In strict mode parameters do not alias arguments[i], and
982 * to make the arguments object reflect initial parameter values prior
983 * to any mutation we create it eagerly whenever parameters are (or
984 * might, in the case of calls to eval) be assigned.
986 if (pc
->sc
->needStrictChecks()) {
987 for (AtomDefnListMap::Range r
= pc
->decls().all(); !r
.empty(); r
.popFront()) {
988 DefinitionList
& dlist
= r
.front().value();
989 for (DefinitionList::Range dr
= dlist
.all(); !dr
.empty(); dr
.popFront()) {
990 Definition
* dn
= dr
.front
<FullParseHandler
>();
991 if (dn
->kind() == Definition::ARG
&& dn
->isAssigned())
992 funbox
->setDefinitelyNeedsArgsObj();
995 /* Watch for mutation of arguments through e.g. eval(). */
996 if (pc
->sc
->bindingsAccessedDynamically())
997 funbox
->setDefinitelyNeedsArgsObj();
1006 Parser
<SyntaxParseHandler
>::checkFunctionArguments()
1008 bool hasRest
= pc
->sc
->asFunctionBox()->function()->hasRest();
1010 if (pc
->lexdeps
->lookup(context
->names().arguments
)) {
1011 pc
->sc
->asFunctionBox()->usesArguments
= true;
1013 report(ParseError
, false, null(), JSMSG_ARGUMENTS_AND_REST
);
1016 } else if (hasRest
) {
1017 DefinitionNode maybeArgDef
= pc
->decls().lookupFirst(context
->names().arguments
);
1018 if (maybeArgDef
&& handler
.getDefinitionKind(maybeArgDef
) != Definition::ARG
) {
1019 report(ParseError
, false, null(), JSMSG_ARGUMENTS_AND_REST
);
1027 template <typename ParseHandler
>
1028 typename
ParseHandler::Node
1029 Parser
<ParseHandler
>::functionBody(FunctionSyntaxKind kind
, FunctionBodyType type
)
1031 MOZ_ASSERT(pc
->sc
->isFunctionBox());
1032 MOZ_ASSERT(!pc
->funHasReturnExpr
&& !pc
->funHasReturnVoid
);
1035 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
1039 if (type
== StatementListBody
) {
1044 MOZ_ASSERT(type
== ExpressionBody
);
1045 MOZ_ASSERT(JS_HAS_EXPR_CLOSURES
);
1047 Node kid
= assignExpr();
1051 pn
= handler
.newReturnStatement(kid
, null(), handler
.getPosition(kid
));
1056 switch (pc
->generatorKind()) {
1058 MOZ_ASSERT(pc
->lastYieldOffset
== startYieldOffset
);
1061 case LegacyGenerator
:
1062 // FIXME: Catch these errors eagerly, in yieldExpression().
1063 MOZ_ASSERT(pc
->lastYieldOffset
!= startYieldOffset
);
1064 if (kind
== Arrow
) {
1065 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
1066 JSMSG_YIELD_IN_ARROW
, js_yield_str
);
1069 if (type
== ExpressionBody
) {
1070 reportBadReturn(pn
, ParseError
,
1071 JSMSG_BAD_GENERATOR_RETURN
,
1072 JSMSG_BAD_ANON_GENERATOR_RETURN
);
1078 MOZ_ASSERT(kind
!= Arrow
);
1079 MOZ_ASSERT(type
== StatementListBody
);
1083 if (pc
->isGenerator()) {
1084 MOZ_ASSERT(type
== StatementListBody
);
1085 Node generator
= newName(context
->names().dotGenerator
);
1088 if (!pc
->define(tokenStream
, context
->names().dotGenerator
, generator
, Definition::VAR
))
1091 if (pc
->isStarGenerator()) {
1092 Node genrval
= newName(context
->names().dotGenRVal
);
1095 if (!pc
->define(tokenStream
, context
->names().dotGenRVal
, genrval
, Definition::VAR
))
1099 generator
= newName(context
->names().dotGenerator
);
1102 if (!noteNameUse(context
->names().dotGenerator
, generator
))
1104 if (!handler
.prependInitialYield(pn
, generator
))
1108 /* Define the 'arguments' binding if necessary. */
1109 if (!checkFunctionArguments())
1115 /* See comment for use in Parser::functionDef. */
1118 Parser
<FullParseHandler
>::makeDefIntoUse(Definition
* dn
, ParseNode
* pn
, JSAtom
* atom
)
1120 /* Turn pn into a definition. */
1121 pc
->updateDecl(atom
, pn
);
1123 /* Change all uses of dn to be uses of pn. */
1124 for (ParseNode
* pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
1125 MOZ_ASSERT(pnu
->isUsed());
1126 MOZ_ASSERT(!pnu
->isDefn());
1127 pnu
->pn_lexdef
= (Definition
*) pn
;
1128 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1130 pn
->pn_dflags
|= dn
->pn_dflags
& PND_USE2DEF_FLAGS
;
1134 * A PNK_FUNCTION node must be a definition, so convert shadowed function
1135 * statements into nops. This is valid since all body-level function
1136 * statement initialization happens at the beginning of the function
1137 * (thus, only the last statement's effect is visible). E.g., in
1139 * function outer() {
1140 * function g() { return 1 }
1142 * function g() { return 2 }
1146 * both asserts are valid.
1148 if (dn
->getKind() == PNK_FUNCTION
) {
1149 MOZ_ASSERT(dn
->functionIsHoisted());
1150 pn
->dn_uses
= dn
->pn_link
;
1151 handler
.prepareNodeForMutation(dn
);
1152 dn
->setKind(PNK_NOP
);
1153 dn
->setArity(PN_NULLARY
);
1158 * If dn is in [var, const, let] and has an initializer, then we
1159 * must rewrite it to be an assignment node, whose freshly allocated
1160 * left-hand side becomes a use of pn.
1162 if (dn
->canHaveInitializer()) {
1163 if (ParseNode
* rhs
= dn
->expr()) {
1164 ParseNode
* lhs
= handler
.makeAssignment(dn
, rhs
);
1168 dn
->pn_link
= nullptr;
1169 dn
= (Definition
*) lhs
;
1173 /* Turn dn into a use of pn. */
1174 MOZ_ASSERT(dn
->isKind(PNK_NAME
));
1175 MOZ_ASSERT(dn
->isArity(PN_NAME
));
1176 MOZ_ASSERT(dn
->pn_atom
== atom
);
1177 dn
->setOp((js_CodeSpec
[dn
->getOp()].format
& JOF_SET
) ? JSOP_SETNAME
: JSOP_GETNAME
);
1180 dn
->pn_lexdef
= (Definition
*) pn
;
1181 dn
->pn_cookie
.makeFree();
1182 dn
->pn_dflags
&= ~PND_BOUND
;
1187 * Parameter block types for the several Binder functions. We use a common
1188 * helper function signature in order to share code among destructuring and
1189 * simple variable declaration parsers. In the destructuring case, the binder
1190 * function is called indirectly from the variable declaration parser by way
1191 * of checkDestructuring and its friends.
1194 template <typename ParseHandler
>
1197 explicit BindData(ExclusiveContext
* cx
) : let(cx
) {}
1200 (*Binder
)(BindData
* data
, HandlePropertyName name
, Parser
<ParseHandler
>* parser
);
1202 /* name node for definition processing and error source coordinates */
1203 typename
ParseHandler::Node pn
;
1205 JSOp op
; /* prolog bytecode or nop */
1206 Binder binder
; /* binder, discriminates u */
1207 bool isConst
; /* const binding? */
1210 explicit LetData(ExclusiveContext
* cx
) : blockObj(cx
) {}
1211 VarContext varContext
;
1212 RootedStaticBlockObject blockObj
;
1216 void initLexical(VarContext varContext
, StaticBlockObject
* blockObj
, unsigned overflow
,
1217 bool isConst
= false) {
1218 this->pn
= ParseHandler::null();
1219 this->op
= JSOP_INITLEXICAL
;
1220 this->isConst
= isConst
;
1221 this->binder
= Parser
<ParseHandler
>::bindLexical
;
1222 this->let
.varContext
= varContext
;
1223 this->let
.blockObj
= blockObj
;
1224 this->let
.overflow
= overflow
;
1227 void initVarOrGlobalConst(JSOp op
) {
1229 this->isConst
= op
== JSOP_DEFCONST
;
1230 this->binder
= Parser
<ParseHandler
>::bindVarOrGlobalConst
;
1234 template <typename ParseHandler
>
1236 Parser
<ParseHandler
>::newFunction(GenericParseContext
* pc
, HandleAtom atom
,
1237 FunctionSyntaxKind kind
, JSObject
* proto
)
1239 MOZ_ASSERT_IF(kind
== Statement
, atom
!= nullptr);
1242 * Find the global compilation context in order to pre-set the newborn
1243 * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the
1244 * global context is a compile-and-go one, we leave the pre-set parent
1245 * intact; otherwise we clear parent and proto.
1250 RootedFunction
fun(context
);
1251 JSFunction::Flags flags
= (kind
== Expression
)
1252 ? JSFunction::INTERPRETED_LAMBDA
1254 ? JSFunction::INTERPRETED_LAMBDA_ARROW
1255 : JSFunction::INTERPRETED
;
1256 gc::AllocKind allocKind
= JSFunction::FinalizeKind
;
1258 allocKind
= JSFunction::ExtendedFinalizeKind
;
1259 fun
= NewFunctionWithProto(context
, NullPtr(), nullptr, 0, flags
, NullPtr(), atom
, proto
,
1260 allocKind
, MaybeSingletonObject
);
1263 if (options().selfHostingMode
)
1264 fun
->setIsSelfHostedBuiltin();
1269 MatchOrInsertSemicolon(TokenStream
& ts
)
1272 if (!ts
.peekTokenSameLine(&tt
, TokenStream::Operand
))
1274 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1275 /* Advance the scanner for proper error location reporting. */
1276 ts
.consumeKnownToken(tt
);
1277 ts
.reportError(JSMSG_SEMI_BEFORE_STMNT
);
1281 return ts
.matchToken(&ignored
, TOK_SEMI
);
1284 template <typename ParseHandler
>
1285 typename
ParseHandler::DefinitionNode
1286 Parser
<ParseHandler
>::getOrCreateLexicalDependency(ParseContext
<ParseHandler
>* pc
, JSAtom
* atom
)
1288 AtomDefnAddPtr p
= pc
->lexdeps
->lookupForAdd(atom
);
1290 return p
.value().get
<ParseHandler
>();
1292 DefinitionNode dn
= handler
.newPlaceholder(atom
, pc
->blockid(), pos());
1294 return ParseHandler::nullDefinition();
1295 DefinitionSingle def
= DefinitionSingle::new_
<ParseHandler
>(dn
);
1296 if (!pc
->lexdeps
->add(p
, atom
, def
))
1297 return ParseHandler::nullDefinition();
1302 ConvertDefinitionToNamedLambdaUse(TokenStream
& ts
, ParseContext
<FullParseHandler
>* pc
,
1303 FunctionBox
* funbox
, Definition
* dn
)
1305 dn
->setOp(JSOP_CALLEE
);
1306 if (!dn
->pn_cookie
.set(ts
, pc
->staticLevel
, 0))
1308 dn
->pn_dflags
|= PND_BOUND
;
1309 MOZ_ASSERT(dn
->kind() == Definition::NAMED_LAMBDA
);
1312 * Since 'dn' is a placeholder, it has not been defined in the
1313 * ParseContext and hence we must manually flag a closed-over
1314 * callee name as needing a dynamic scope (this is done for all
1315 * definitions in the ParseContext by generateFunctionBindings).
1317 * If 'dn' has been assigned to, then we also flag the function
1318 * scope has needing a dynamic scope so that dynamic scope
1319 * setter can either ignore the set (in non-strict mode) or
1320 * produce an error (in strict mode).
1322 if (dn
->isClosed() || dn
->isAssigned())
1323 funbox
->setNeedsDeclEnvObject();
1328 IsNonDominatingInScopedSwitch(ParseContext
<FullParseHandler
>* pc
, HandleAtom name
,
1331 MOZ_ASSERT(dn
->isLexical());
1332 StmtInfoPC
* stmt
= LexicalLookup(pc
, name
, nullptr, (StmtInfoPC
*)nullptr);
1333 if (stmt
&& stmt
->type
== STMT_SWITCH
)
1334 return dn
->pn_cookie
.slot() < stmt
->firstDominatingLexicalInCase
;
1339 AssociateUsesWithOuterDefinition(ParseNode
* pnu
, Definition
* dn
, Definition
* outer_dn
,
1340 bool markUsesAsLexical
)
1342 uint32_t dflags
= markUsesAsLexical
? PND_LEXICAL
: 0;
1344 pnu
->pn_lexdef
= outer_dn
;
1345 pnu
->pn_dflags
|= dflags
;
1350 pnu
->pn_link
= outer_dn
->dn_uses
;
1351 outer_dn
->dn_uses
= dn
->dn_uses
;
1352 dn
->dn_uses
= nullptr;
1356 * Beware: this function is called for functions nested in other functions or
1357 * global scripts but not for functions compiled through the Function
1358 * constructor or JSAPI. To always execute code when a function has finished
1359 * parsing, use Parser::functionBody.
1363 Parser
<FullParseHandler
>::leaveFunction(ParseNode
* fn
, ParseContext
<FullParseHandler
>* outerpc
,
1364 FunctionSyntaxKind kind
)
1366 outerpc
->blockidGen
= pc
->blockidGen
;
1368 bool bodyLevel
= outerpc
->atBodyLevel();
1369 FunctionBox
* funbox
= fn
->pn_funbox
;
1370 MOZ_ASSERT(funbox
== pc
->sc
->asFunctionBox());
1372 /* Propagate unresolved lexical names up to outerpc->lexdeps. */
1373 if (pc
->lexdeps
->count()) {
1374 for (AtomDefnRange r
= pc
->lexdeps
->all(); !r
.empty(); r
.popFront()) {
1375 JSAtom
* atom
= r
.front().key();
1376 Definition
* dn
= r
.front().value().get
<FullParseHandler
>();
1377 MOZ_ASSERT(dn
->isPlaceholder());
1379 if (atom
== funbox
->function()->name() && kind
== Expression
) {
1380 if (!ConvertDefinitionToNamedLambdaUse(tokenStream
, pc
, funbox
, dn
))
1385 Definition
* outer_dn
= outerpc
->decls().lookupFirst(atom
);
1388 * Make sure to deoptimize lexical dependencies that are polluted
1389 * by eval and function statements (which both flag the function as
1390 * having an extensible scope) or any enclosing 'with'.
1392 if (funbox
->hasExtensibleScope() || outerpc
->parsingWith
)
1393 handler
.deoptimizeUsesWithin(dn
, fn
->pn_pos
);
1397 * Create a new placeholder for our outer lexdep. We could
1398 * simply re-use the inner placeholder, but that introduces
1399 * subtleties in the case where we find a later definition
1400 * that captures an existing lexdep. For example:
1402 * function f() { function g() { x; } let x; }
1404 * Here, g's TOK_UPVARS node lists the placeholder for x,
1405 * which must be captured by the 'let' declaration later,
1406 * since 'let's are hoisted. Taking g's placeholder as our
1407 * own would work fine. But consider:
1409 * function f() { x; { function g() { x; } let x; } }
1411 * Here, the 'let' must not capture all the uses of f's
1412 * lexdep entry for x, but it must capture the x node
1413 * referred to from g's TOK_UPVARS node. Always turning
1414 * inherited lexdeps into uses of a new outer definition
1415 * allows us to handle both these cases in a natural way.
1417 outer_dn
= getOrCreateLexicalDependency(outerpc
, atom
);
1423 * Insert dn's uses list at the front of outer_dn's list.
1425 * Without loss of generality or correctness, we allow a dn to
1426 * be in inner and outer lexdeps, since the purpose of lexdeps
1427 * is one-pass coordination of name use and definition across
1428 * functions, and if different dn's are used we'll merge lists
1429 * when leaving the inner function.
1431 * The dn == outer_dn case arises with generator expressions
1432 * (see LegacyCompExprTransplanter::transplant, the PN_CODE/PN_NAME
1433 * case), and nowhere else, currently.
1435 if (dn
!= outer_dn
) {
1436 if (ParseNode
* pnu
= dn
->dn_uses
) {
1437 // In ES6, lexical bindings cannot be accessed until
1438 // initialized. If we are parsing a body-level function,
1439 // it is hoisted to the top, so we conservatively mark all
1440 // uses linked to an outer lexical binding as needing TDZ
1443 // function outer() {
1445 // function inner() { use(x); }
1446 // function inner2() { inner(); }
1450 // The use of 'x' inside 'inner' needs to be marked.
1452 // Note that to not be fully conservative requires a call
1453 // graph analysis of all body-level functions to compute
1454 // the transitive closure of which hoisted body level use
1455 // of which function forces TDZ checks on which uses. This
1456 // is unreasonably difficult to do in a single pass parser
1459 // Similarly, if we are closing over a lexical binding
1460 // from another case in a switch, those uses also need to
1461 // be marked as needing dead zone checks.
1462 RootedAtom
name(context
, atom
);
1463 bool markUsesAsLexical
= outer_dn
->isLexical() &&
1465 IsNonDominatingInScopedSwitch(outerpc
, name
, outer_dn
));
1466 AssociateUsesWithOuterDefinition(pnu
, dn
, outer_dn
, markUsesAsLexical
);
1469 outer_dn
->pn_dflags
|= dn
->pn_dflags
& ~PND_PLACEHOLDER
;
1472 /* Mark the outer dn as escaping. */
1473 outer_dn
->pn_dflags
|= PND_CLOSED
;
1477 InternalHandle
<Bindings
*> bindings
=
1478 InternalHandle
<Bindings
*>::fromMarkedLocation(&funbox
->bindings
);
1479 return pc
->generateFunctionBindings(context
, tokenStream
, alloc
, bindings
);
1484 Parser
<SyntaxParseHandler
>::leaveFunction(Node fn
, ParseContext
<SyntaxParseHandler
>* outerpc
,
1485 FunctionSyntaxKind kind
)
1487 outerpc
->blockidGen
= pc
->blockidGen
;
1489 FunctionBox
* funbox
= pc
->sc
->asFunctionBox();
1490 return addFreeVariablesFromLazyFunction(funbox
->function(), outerpc
);
1494 * defineArg is called for both the arguments of a regular function definition
1495 * and the arguments specified by the Function constructor.
1497 * The 'disallowDuplicateArgs' bool indicates whether the use of another
1498 * feature (destructuring or default arguments) disables duplicate arguments.
1499 * (ECMA-262 requires us to support duplicate parameter names, but, for newer
1500 * features, we consider the code to have "opted in" to higher standards and
1501 * forbid duplicates.)
1503 * If 'duplicatedArg' is non-null, then DefineArg assigns to it any previous
1504 * argument with the same name. The caller may use this to report an error when
1505 * one of the abovementioned features occurs after a duplicate.
1507 template <typename ParseHandler
>
1509 Parser
<ParseHandler
>::defineArg(Node funcpn
, HandlePropertyName name
,
1510 bool disallowDuplicateArgs
, Node
* duplicatedArg
)
1512 SharedContext
* sc
= pc
->sc
;
1514 /* Handle duplicate argument names. */
1515 if (DefinitionNode prevDecl
= pc
->decls().lookupFirst(name
)) {
1516 Node pn
= handler
.getDefinitionNode(prevDecl
);
1519 * Strict-mode disallows duplicate args. We may not know whether we are
1520 * in strict mode or not (since the function body hasn't been parsed).
1521 * In such cases, report will queue up the potential error and return
1524 if (sc
->needStrictChecks()) {
1525 JSAutoByteString bytes
;
1526 if (!AtomToPrintableString(context
, name
, &bytes
))
1528 if (!report(ParseStrictError
, pc
->sc
->strict
, pn
,
1529 JSMSG_DUPLICATE_FORMAL
, bytes
.ptr()))
1535 if (disallowDuplicateArgs
) {
1536 report(ParseError
, false, pn
, JSMSG_BAD_DUP_ARGS
);
1541 *duplicatedArg
= pn
;
1543 /* ParseContext::define assumes and asserts prevDecl is not in decls. */
1544 MOZ_ASSERT(handler
.getDefinitionKind(prevDecl
) == Definition::ARG
);
1545 pc
->prepareToAddDuplicateArg(name
, prevDecl
);
1548 Node argpn
= newName(name
);
1552 if (!checkStrictBinding(name
, argpn
))
1555 handler
.addFunctionArgument(funcpn
, argpn
);
1556 return pc
->define(tokenStream
, name
, argpn
, Definition::ARG
);
1559 template <typename ParseHandler
>
1561 Parser
<ParseHandler
>::bindDestructuringArg(BindData
<ParseHandler
>* data
,
1562 HandlePropertyName name
, Parser
<ParseHandler
>* parser
)
1564 ParseContext
<ParseHandler
>* pc
= parser
->pc
;
1565 MOZ_ASSERT(pc
->sc
->isFunctionBox());
1567 if (pc
->decls().lookupFirst(name
)) {
1568 parser
->report(ParseError
, false, null(), JSMSG_BAD_DUP_ARGS
);
1572 if (!parser
->checkStrictBinding(name
, data
->pn
))
1575 return pc
->define(parser
->tokenStream
, name
, data
->pn
, Definition::VAR
);
1578 template <typename ParseHandler
>
1580 Parser
<ParseHandler
>::functionArguments(FunctionSyntaxKind kind
, Node
* listp
, Node funcpn
,
1583 FunctionBox
* funbox
= pc
->sc
->asFunctionBox();
1587 bool parenFreeArrow
= false;
1588 if (kind
== Arrow
) {
1590 if (!tokenStream
.peekToken(&tt
))
1593 parenFreeArrow
= true;
1595 if (!parenFreeArrow
) {
1597 if (!tokenStream
.getToken(&tt
))
1600 report(ParseError
, false, null(),
1601 kind
== Arrow
? JSMSG_BAD_ARROW_ARGS
: JSMSG_PAREN_BEFORE_FORMAL
);
1605 // Record the start of function source (for FunctionToString). If we
1606 // are parenFreeArrow, we will set this below, after consuming the NAME.
1607 funbox
->setStart(tokenStream
);
1610 Node argsbody
= handler
.newList(PNK_ARGSBODY
);
1613 handler
.setFunctionBody(funcpn
, argsbody
);
1615 bool hasArguments
= false;
1616 if (parenFreeArrow
) {
1617 hasArguments
= true;
1620 if (!tokenStream
.matchToken(&matched
, TOK_RP
))
1623 hasArguments
= true;
1626 bool hasDefaults
= false;
1627 Node duplicatedArg
= null();
1632 report(ParseError
, false, null(), JSMSG_PARAMETER_AFTER_REST
);
1637 if (!tokenStream
.getToken(&tt
))
1639 MOZ_ASSERT_IF(parenFreeArrow
, tt
== TOK_NAME
);
1644 /* See comment below in the TOK_NAME case. */
1645 if (duplicatedArg
) {
1646 report(ParseError
, false, duplicatedArg
, JSMSG_BAD_DUP_ARGS
);
1651 report(ParseError
, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT
);
1655 funbox
->hasDestructuringArgs
= true;
1658 * A destructuring formal parameter turns into one or more
1659 * local variables initialized from properties of a single
1660 * anonymous positional parameter, so here we must tweak our
1661 * binder and its data.
1663 BindData
<ParseHandler
> data(context
);
1664 data
.pn
= ParseHandler::null();
1665 data
.op
= JSOP_DEFVAR
;
1666 data
.binder
= bindDestructuringArg
;
1667 Node lhs
= destructuringExprWithoutYield(&data
, tt
, JSMSG_YIELD_IN_DEFAULT
);
1672 * Synthesize a destructuring assignment from the single
1673 * anonymous positional parameter into the destructuring
1674 * left-hand-side expression and accumulate it in list.
1676 HandlePropertyName name
= context
->names().empty
;
1677 Node rhs
= newName(name
);
1681 if (!pc
->define(tokenStream
, name
, rhs
, Definition::ARG
))
1684 Node item
= handler
.newBinary(PNK_ASSIGN
, lhs
, rhs
);
1688 handler
.addList(list
, item
);
1690 list
= handler
.newList(PNK_VAR
, item
);
1699 if (!checkYieldNameValidity())
1706 if (!tokenStream
.getToken(&tt
))
1708 if (tt
!= TOK_NAME
) {
1709 report(ParseError
, false, null(), JSMSG_NO_REST_NAME
);
1719 funbox
->setStart(tokenStream
);
1721 RootedPropertyName
name(context
, tokenStream
.currentName());
1722 bool disallowDuplicateArgs
= funbox
->hasDestructuringArgs
|| hasDefaults
;
1723 if (!defineArg(funcpn
, name
, disallowDuplicateArgs
, &duplicatedArg
))
1727 if (!tokenStream
.matchToken(&matched
, TOK_ASSIGN
))
1730 // A default argument without parentheses would look like:
1731 // a = expr => body, but both operators are right-associative, so
1732 // that would have been parsed as a = (expr => body) instead.
1733 // Therefore it's impossible to get here with parenFreeArrow.
1734 MOZ_ASSERT(!parenFreeArrow
);
1737 report(ParseError
, false, null(), JSMSG_REST_WITH_DEFAULT
);
1740 if (duplicatedArg
) {
1741 report(ParseError
, false, duplicatedArg
, JSMSG_BAD_DUP_ARGS
);
1747 // The Function.length property is the number of formals
1748 // before the first default argument.
1749 funbox
->length
= pc
->numArgs() - 1;
1751 Node def_expr
= assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT
);
1754 handler
.setLastFunctionArgumentDefault(funcpn
, def_expr
);
1761 report(ParseError
, false, null(), JSMSG_MISSING_FORMAL
);
1769 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
1775 if (!parenFreeArrow
) {
1777 if (!tokenStream
.getToken(&tt
))
1780 report(ParseError
, false, null(), JSMSG_PAREN_AFTER_FORMAL
);
1786 funbox
->length
= pc
->numArgs() - *hasRest
;
1794 Parser
<FullParseHandler
>::checkFunctionDefinition(HandlePropertyName funName
,
1795 ParseNode
** pn_
, FunctionSyntaxKind kind
,
1796 bool* pbodyProcessed
)
1798 ParseNode
*& pn
= *pn_
;
1799 *pbodyProcessed
= false;
1801 /* Function statements add a binding to the enclosing scope. */
1802 bool bodyLevel
= pc
->atBodyLevel();
1804 if (kind
== Statement
) {
1806 * Handle redeclaration and optimize cases where we can statically bind the
1807 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
1809 if (Definition
* dn
= pc
->decls().lookupFirst(funName
)) {
1810 MOZ_ASSERT(!dn
->isUsed());
1811 MOZ_ASSERT(dn
->isDefn());
1813 bool throwRedeclarationError
= dn
->kind() == Definition::GLOBALCONST
||
1814 dn
->kind() == Definition::CONST
||
1815 dn
->kind() == Definition::LET
;
1816 if (options().extraWarningsOption
|| throwRedeclarationError
) {
1817 JSAutoByteString name
;
1818 ParseReportKind reporter
= throwRedeclarationError
1820 : ParseExtraWarning
;
1821 if (!AtomToPrintableString(context
, funName
, &name
) ||
1822 !report(reporter
, false, nullptr, JSMSG_REDECLARED_VAR
,
1823 Definition::kindString(dn
->kind()), name
.ptr()))
1830 * Body-level function statements are effectively variable
1831 * declarations where the initialization is hoisted to the
1832 * beginning of the block. This means that any other variable
1833 * declaration with the same name is really just an assignment to
1834 * the function's binding (which is mutable), so turn any existing
1835 * declaration into a use.
1838 if (dn
->kind() == Definition::ARG
) {
1839 // The exception to the above comment is when the function
1840 // has the same name as an argument. Then the argument node
1841 // remains a definition. But change the function node pn so
1842 // that it knows where the argument is located.
1843 pn
->setOp(JSOP_GETARG
);
1845 pn
->pn_cookie
= dn
->pn_cookie
;
1846 pn
->pn_dflags
|= PND_BOUND
;
1847 dn
->markAsAssigned();
1849 if (!makeDefIntoUse(dn
, pn
, funName
))
1853 } else if (bodyLevel
) {
1855 * If this function was used before it was defined, claim the
1856 * pre-created definition node for this function that primaryExpr
1857 * put in pc->lexdeps on first forward reference, and recycle pn.
1859 if (Definition
* fn
= pc
->lexdeps
.lookupDefn
<FullParseHandler
>(funName
)) {
1860 MOZ_ASSERT(fn
->isDefn());
1861 fn
->setKind(PNK_FUNCTION
);
1862 fn
->setArity(PN_CODE
);
1863 fn
->pn_pos
.begin
= pn
->pn_pos
.begin
;
1864 fn
->pn_pos
.end
= pn
->pn_pos
.end
;
1866 fn
->pn_body
= nullptr;
1867 fn
->pn_cookie
.makeFree();
1869 pc
->lexdeps
->remove(funName
);
1870 handler
.freeTree(pn
);
1874 if (!pc
->define(tokenStream
, funName
, pn
, Definition::VAR
))
1879 MOZ_ASSERT(pn
->functionIsHoisted());
1880 MOZ_ASSERT_IF(pc
->sc
->isFunctionBox(), !pn
->pn_cookie
.isFree());
1881 MOZ_ASSERT_IF(!pc
->sc
->isFunctionBox(), pn
->pn_cookie
.isFree());
1884 * As a SpiderMonkey-specific extension, non-body-level function
1885 * statements (e.g., functions in an "if" or "while" block) are
1886 * dynamically bound when control flow reaches the statement.
1888 MOZ_ASSERT(!pc
->sc
->strict
);
1889 MOZ_ASSERT(pn
->pn_cookie
.isFree());
1890 if (pc
->sc
->isFunctionBox()) {
1891 FunctionBox
* funbox
= pc
->sc
->asFunctionBox();
1892 funbox
->setMightAliasLocals();
1893 funbox
->setHasExtensibleScope();
1895 pn
->setOp(JSOP_DEFFUN
);
1898 * Instead of setting bindingsAccessedDynamically, which would be
1899 * overly conservative, remember the names of all function
1900 * statements and mark any bindings with the same as aliased at the
1901 * end of functionBody.
1903 if (!pc
->funcStmts
) {
1904 pc
->funcStmts
= alloc
.new_
<FuncStmtSet
>(alloc
);
1905 if (!pc
->funcStmts
|| !pc
->funcStmts
->init())
1908 if (!pc
->funcStmts
->put(funName
))
1912 * Due to the implicit declaration mechanism, 'arguments' will not
1913 * have decls and, even if it did, they will not be noted as closed
1914 * in the emitter. Thus, in the corner case of function statements
1915 * overridding arguments, flag the whole scope as dynamic.
1917 if (funName
== context
->names().arguments
)
1918 pc
->sc
->setBindingsAccessedDynamically();
1921 /* No further binding (in BindNameToSlot) is needed for functions. */
1922 pn
->pn_dflags
|= PND_BOUND
;
1924 /* A function expression does not introduce any binding. */
1925 pn
->setOp(kind
== Arrow
? JSOP_LAMBDA_ARROW
: JSOP_LAMBDA
);
1928 // When a lazily-parsed function is called, we only fully parse (and emit)
1929 // that function, not any of its nested children. The initial syntax-only
1930 // parse recorded the free variables of nested functions and their extents,
1931 // so we can skip over them after accounting for their free variables.
1932 if (LazyScript
* lazyOuter
= handler
.lazyOuterFunction()) {
1933 JSFunction
* fun
= handler
.nextLazyInnerFunction();
1934 MOZ_ASSERT(!fun
->isLegacyGenerator());
1935 FunctionBox
* funbox
= newFunctionBox(pn
, fun
, pc
, Directives(/* strict = */ false),
1936 fun
->generatorKind());
1940 if (!addFreeVariablesFromLazyFunction(fun
, pc
))
1943 // The position passed to tokenStream.advance() is an offset of the sort
1944 // returned by userbuf.offset() and expected by userbuf.rawCharPtrAt(),
1945 // while LazyScript::{begin,end} offsets are relative to the outermost
1947 uint32_t userbufBase
= lazyOuter
->begin() - lazyOuter
->column();
1948 if (!tokenStream
.advance(fun
->lazyScript()->end() - userbufBase
))
1951 *pbodyProcessed
= true;
1958 template <class T
, class U
>
1960 PropagateTransitiveParseFlags(const T
* inner
, U
* outer
)
1962 if (inner
->bindingsAccessedDynamically())
1963 outer
->setBindingsAccessedDynamically();
1964 if (inner
->hasDebuggerStatement())
1965 outer
->setHasDebuggerStatement();
1968 template <typename ParseHandler
>
1970 Parser
<ParseHandler
>::addFreeVariablesFromLazyFunction(JSFunction
* fun
,
1971 ParseContext
<ParseHandler
>* pc
)
1973 // Update any definition nodes in this context according to free variables
1974 // in a lazily parsed inner function.
1976 bool bodyLevel
= pc
->atBodyLevel();
1977 LazyScript
* lazy
= fun
->lazyScript();
1978 LazyScript::FreeVariable
* freeVariables
= lazy
->freeVariables();
1979 for (size_t i
= 0; i
< lazy
->numFreeVariables(); i
++) {
1980 JSAtom
* atom
= freeVariables
[i
].atom();
1982 // 'arguments' will be implicitly bound within the inner function.
1983 if (atom
== context
->names().arguments
)
1986 DefinitionNode dn
= pc
->decls().lookupFirst(atom
);
1989 dn
= getOrCreateLexicalDependency(pc
, atom
);
1994 // In ES6, lexical bindings are unaccessible before initialization. If
1995 // the inner function closes over a placeholder definition, we need to
1996 // mark the variable as maybe needing a dead zone check when we emit
1999 // Note that body-level function declaration statements are always
2000 // hoisted to the top, so all accesses to free let variables need the
2003 // Subtlety: we don't need to check for closing over a non-dominating
2004 // lexical binding in a switch, as lexical declarations currently
2005 // disable syntax parsing. So a non-dominating but textually preceding
2006 // lexical declaration would have aborted syntax parsing, and a
2007 // textually following declaration would return true for
2008 // handler.isPlaceholderDefinition(dn) below.
2009 if (handler
.isPlaceholderDefinition(dn
) || bodyLevel
)
2010 freeVariables
[i
].setIsHoistedUse();
2012 /* Mark the outer dn as escaping. */
2013 handler
.setFlag(handler
.getDefinitionNode(dn
), PND_CLOSED
);
2016 PropagateTransitiveParseFlags(lazy
, pc
->sc
);
2022 Parser
<SyntaxParseHandler
>::checkFunctionDefinition(HandlePropertyName funName
,
2023 Node
* pn
, FunctionSyntaxKind kind
,
2024 bool* pbodyProcessed
)
2026 *pbodyProcessed
= false;
2028 /* Function statements add a binding to the enclosing scope. */
2029 bool bodyLevel
= pc
->atBodyLevel();
2031 if (kind
== Statement
) {
2033 * Handle redeclaration and optimize cases where we can statically bind the
2034 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
2036 if (DefinitionNode dn
= pc
->decls().lookupFirst(funName
)) {
2037 if (dn
== Definition::GLOBALCONST
||
2038 dn
== Definition::CONST
||
2039 dn
== Definition::LET
)
2041 JSAutoByteString name
;
2042 if (!AtomToPrintableString(context
, funName
, &name
) ||
2043 !report(ParseError
, false, null(), JSMSG_REDECLARED_VAR
,
2044 Definition::kindString(dn
), name
.ptr()))
2049 } else if (bodyLevel
) {
2050 if (pc
->lexdeps
.lookupDefn
<SyntaxParseHandler
>(funName
))
2051 pc
->lexdeps
->remove(funName
);
2053 if (!pc
->define(tokenStream
, funName
, *pn
, Definition::VAR
))
2057 if (!bodyLevel
&& funName
== context
->names().arguments
)
2058 pc
->sc
->setBindingsAccessedDynamically();
2061 if (kind
== Arrow
) {
2062 /* Arrow functions cannot yet be parsed lazily. */
2063 return abortIfSyntaxParser();
2069 template <typename ParseHandler
>
2071 Parser
<ParseHandler
>::addExprAndGetNextTemplStrToken(Node nodeList
, TokenKind
* ttp
)
2076 handler
.addList(nodeList
, pn
);
2079 if (!tokenStream
.getToken(&tt
))
2082 report(ParseError
, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR
);
2086 return tokenStream
.getToken(ttp
, TokenStream::TemplateTail
);
2089 template <typename ParseHandler
>
2091 Parser
<ParseHandler
>::taggedTemplate(Node nodeList
, TokenKind tt
)
2093 Node callSiteObjNode
= handler
.newCallSiteObject(pos().begin
, pc
->blockidGen
);
2094 if (!callSiteObjNode
)
2096 handler
.addList(nodeList
, callSiteObjNode
);
2099 if (!appendToCallSiteObj(callSiteObjNode
))
2101 if (tt
!= TOK_TEMPLATE_HEAD
)
2104 if (!addExprAndGetNextTemplStrToken(nodeList
, &tt
))
2107 handler
.setEndPosition(nodeList
, callSiteObjNode
);
2111 template <typename ParseHandler
>
2112 typename
ParseHandler::Node
2113 Parser
<ParseHandler
>::templateLiteral()
2115 Node pn
= noSubstitutionTemplate();
2118 Node nodeList
= handler
.newList(PNK_TEMPLATE_STRING_LIST
, pn
);
2122 if (!addExprAndGetNextTemplStrToken(nodeList
, &tt
))
2125 pn
= noSubstitutionTemplate();
2129 handler
.addList(nodeList
, pn
);
2130 } while (tt
== TOK_TEMPLATE_HEAD
);
2134 template <typename ParseHandler
>
2135 typename
ParseHandler::Node
2136 Parser
<ParseHandler
>::functionDef(HandlePropertyName funName
,
2137 FunctionType type
, FunctionSyntaxKind kind
,
2138 GeneratorKind generatorKind
)
2140 MOZ_ASSERT_IF(kind
== Statement
, funName
);
2142 /* Make a TOK_FUNCTION node. */
2143 Node pn
= handler
.newFunctionDefinition();
2148 if (!checkFunctionDefinition(funName
, &pn
, kind
, &bodyProcessed
))
2154 RootedObject
proto(context
);
2155 if (generatorKind
== StarGenerator
) {
2156 // If we are off the main thread, the generator meta-objects have
2157 // already been created by js::StartOffThreadParseScript, so cx will not
2159 JSContext
* cx
= context
->maybeJSContext();
2160 proto
= GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx
, context
->global());
2164 RootedFunction
fun(context
, newFunction(pc
, funName
, kind
, proto
));
2168 // Speculatively parse using the directives of the parent parsing context.
2169 // If a directive is encountered (e.g., "use strict") that changes how the
2170 // function should have been parsed, we backup and reparse with the new set
2172 Directives
directives(pc
);
2173 Directives newDirectives
= directives
;
2175 TokenStream::Position
start(keepAtoms
);
2176 tokenStream
.tell(&start
);
2179 if (functionArgsAndBody(pn
, fun
, type
, kind
, generatorKind
, directives
, &newDirectives
))
2181 if (tokenStream
.hadError() || directives
== newDirectives
)
2184 // Assignment must be monotonic to prevent reparsing iloops
2185 MOZ_ASSERT_IF(directives
.strict(), newDirectives
.strict());
2186 MOZ_ASSERT_IF(directives
.asmJS(), newDirectives
.asmJS());
2187 directives
= newDirectives
;
2189 tokenStream
.seek(start
);
2191 // functionArgsAndBody may have already set pn->pn_body before failing.
2192 handler
.setFunctionBody(pn
, null());
2200 Parser
<FullParseHandler
>::finishFunctionDefinition(ParseNode
* pn
, FunctionBox
* funbox
,
2201 ParseNode
* prelude
, ParseNode
* body
)
2203 pn
->pn_pos
.end
= pos().end
;
2206 * If there were destructuring formal parameters, prepend the initializing
2207 * comma expression that we synthesized to body. If the body is a return
2208 * node, we must make a special PNK_SEQ node, to prepend the destructuring
2209 * code without bracing the decompilation of the function body.
2212 if (!body
->isArity(PN_LIST
)) {
2215 block
= ListNode::create(PNK_SEQ
, &handler
);
2218 block
->pn_pos
= body
->pn_pos
;
2219 block
->initList(body
);
2224 ParseNode
* item
= UnaryNode::create(PNK_SEMI
, &handler
);
2228 item
->pn_pos
.begin
= item
->pn_pos
.end
= body
->pn_pos
.begin
;
2229 item
->pn_kid
= prelude
;
2230 item
->pn_next
= body
->pn_head
;
2231 body
->pn_head
= item
;
2232 if (body
->pn_tail
== &body
->pn_head
)
2233 body
->pn_tail
= &item
->pn_next
;
2235 body
->pn_xflags
|= PNX_DESTRUCT
;
2238 MOZ_ASSERT(pn
->pn_funbox
== funbox
);
2239 MOZ_ASSERT(pn
->pn_body
->isKind(PNK_ARGSBODY
));
2240 pn
->pn_body
->append(body
);
2241 pn
->pn_body
->pn_pos
= body
->pn_pos
;
2248 Parser
<SyntaxParseHandler
>::finishFunctionDefinition(Node pn
, FunctionBox
* funbox
,
2249 Node prelude
, Node body
)
2251 // The LazyScript for a lazily parsed function needs to be constructed
2252 // while its ParseContext and associated lexdeps and inner functions are
2256 return abortIfSyntaxParser();
2258 size_t numFreeVariables
= pc
->lexdeps
->count();
2259 size_t numInnerFunctions
= pc
->innerFunctions
.length();
2261 RootedFunction
fun(context
, funbox
->function());
2262 LazyScript
* lazy
= LazyScript::CreateRaw(context
, fun
, numFreeVariables
, numInnerFunctions
,
2263 versionNumber(), funbox
->bufStart
, funbox
->bufEnd
,
2264 funbox
->startLine
, funbox
->startColumn
);
2268 LazyScript::FreeVariable
* freeVariables
= lazy
->freeVariables();
2270 for (AtomDefnRange r
= pc
->lexdeps
->all(); !r
.empty(); r
.popFront())
2271 freeVariables
[i
++] = LazyScript::FreeVariable(r
.front().key());
2272 MOZ_ASSERT(i
== numFreeVariables
);
2274 HeapPtrFunction
* innerFunctions
= lazy
->innerFunctions();
2275 for (size_t i
= 0; i
< numInnerFunctions
; i
++)
2276 innerFunctions
[i
].init(pc
->innerFunctions
[i
]);
2280 lazy
->setGeneratorKind(funbox
->generatorKind());
2281 if (funbox
->usesArguments
&& funbox
->usesApply
&& funbox
->usesThis
)
2282 lazy
->setUsesArgumentsApplyAndThis();
2283 PropagateTransitiveParseFlags(funbox
, lazy
);
2285 fun
->initLazyScript(lazy
);
2291 Parser
<FullParseHandler
>::functionArgsAndBody(ParseNode
* pn
, HandleFunction fun
,
2292 FunctionType type
, FunctionSyntaxKind kind
,
2293 GeneratorKind generatorKind
,
2294 Directives inheritedDirectives
,
2295 Directives
* newDirectives
)
2297 ParseContext
<FullParseHandler
>* outerpc
= pc
;
2299 // Create box for fun->object early to protect against last-ditch GC.
2300 FunctionBox
* funbox
= newFunctionBox(pn
, fun
, pc
, inheritedDirectives
, generatorKind
);
2304 // Try a syntax parse for this inner function.
2306 Parser
<SyntaxParseHandler
>* parser
= handler
.syntaxParser
;
2311 // Move the syntax parser to the current position in the stream.
2312 TokenStream::Position
position(keepAtoms
);
2313 tokenStream
.tell(&position
);
2314 if (!parser
->tokenStream
.seek(position
, tokenStream
))
2317 ParseContext
<SyntaxParseHandler
> funpc(parser
, outerpc
, SyntaxParseHandler::null(), funbox
,
2318 newDirectives
, outerpc
->staticLevel
+ 1,
2319 outerpc
->blockidGen
, /* blockScopeDepth = */ 0);
2320 if (!funpc
.init(tokenStream
))
2323 if (!parser
->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric
,
2326 if (parser
->hadAbortedSyntaxParse()) {
2327 // Try again with a full parse.
2328 parser
->clearAbortedSyntaxParse();
2329 MOZ_ASSERT_IF(parser
->context
->isJSContext(),
2330 !parser
->context
->asJSContext()->isExceptionPending());
2336 outerpc
->blockidGen
= funpc
.blockidGen
;
2338 // Advance this parser over tokens processed by the syntax parser.
2339 parser
->tokenStream
.tell(&position
);
2340 if (!tokenStream
.seek(position
, parser
->tokenStream
))
2343 // Update the end position of the parse node.
2344 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
2347 if (!addFreeVariablesFromLazyFunction(fun
, pc
))
2350 pn
->pn_blockid
= outerpc
->blockid();
2351 PropagateTransitiveParseFlags(funbox
, outerpc
->sc
);
2355 // Continue doing a full parse for this inner function.
2356 ParseContext
<FullParseHandler
> funpc(this, pc
, pn
, funbox
, newDirectives
,
2357 outerpc
->staticLevel
+ 1, outerpc
->blockidGen
,
2358 /* blockScopeDepth = */ 0);
2359 if (!funpc
.init(tokenStream
))
2362 if (!functionArgsAndBodyGeneric(pn
, fun
, type
, kind
))
2365 if (!leaveFunction(pn
, outerpc
, kind
))
2368 pn
->pn_blockid
= outerpc
->blockid();
2371 * Fruit of the poisonous tree: if a closure contains a dynamic name access
2372 * (eval, with, etc), we consider the parent to do the same. The reason is
2373 * that the deoptimizing effects of dynamic name access apply equally to
2374 * parents: any local can be read at runtime.
2376 PropagateTransitiveParseFlags(funbox
, outerpc
->sc
);
2382 Parser
<SyntaxParseHandler
>::functionArgsAndBody(Node pn
, HandleFunction fun
,
2383 FunctionType type
, FunctionSyntaxKind kind
,
2384 GeneratorKind generatorKind
,
2385 Directives inheritedDirectives
,
2386 Directives
* newDirectives
)
2388 ParseContext
<SyntaxParseHandler
>* outerpc
= pc
;
2390 // Create box for fun->object early to protect against last-ditch GC.
2391 FunctionBox
* funbox
= newFunctionBox(pn
, fun
, pc
, inheritedDirectives
, generatorKind
);
2395 // Initialize early for possible flags mutation via destructuringExpr.
2396 ParseContext
<SyntaxParseHandler
> funpc(this, pc
, handler
.null(), funbox
, newDirectives
,
2397 outerpc
->staticLevel
+ 1, outerpc
->blockidGen
,
2398 /* blockScopeDepth = */ 0);
2399 if (!funpc
.init(tokenStream
))
2402 if (!functionArgsAndBodyGeneric(pn
, fun
, type
, kind
))
2405 if (!leaveFunction(pn
, outerpc
, kind
))
2408 // This is a lazy function inner to another lazy function. Remember the
2409 // inner function so that if the outer function is eventually parsed we do
2410 // not need any further parsing or processing of the inner function.
2411 MOZ_ASSERT(fun
->lazyScript());
2412 return outerpc
->innerFunctions
.append(fun
);
2415 template <typename ParseHandler
>
2417 Parser
<ParseHandler
>::appendToCallSiteObj(Node callSiteObj
)
2419 Node cookedNode
= noSubstitutionTemplate();
2423 JSAtom
* atom
= tokenStream
.getRawTemplateStringAtom();
2426 Node rawNode
= handler
.newTemplateStringLiteral(atom
, pos());
2430 return handler
.addToCallSiteObject(callSiteObj
, rawNode
, cookedNode
);
2435 Parser
<FullParseHandler
>::standaloneLazyFunction(HandleFunction fun
, unsigned staticLevel
,
2436 bool strict
, GeneratorKind generatorKind
)
2438 MOZ_ASSERT(checkOptionsCalled
);
2440 Node pn
= handler
.newFunctionDefinition();
2444 // Our tokenStream has no current token, so pn's position is garbage.
2445 // Substitute the position of the first token in our source.
2446 if (!tokenStream
.peekTokenPos(&pn
->pn_pos
))
2449 Directives
directives(/* strict = */ strict
);
2450 FunctionBox
* funbox
= newFunctionBox(pn
, fun
, /* outerpc = */ nullptr, directives
,
2454 funbox
->length
= fun
->nargs() - fun
->hasRest();
2456 Directives newDirectives
= directives
;
2457 ParseContext
<FullParseHandler
> funpc(this, /* parent = */ nullptr, pn
, funbox
,
2458 &newDirectives
, staticLevel
, /* bodyid = */ 0,
2459 /* blockScopeDepth = */ 0);
2460 if (!funpc
.init(tokenStream
))
2463 if (!functionArgsAndBodyGeneric(pn
, fun
, Normal
, Statement
)) {
2464 MOZ_ASSERT(directives
== newDirectives
);
2468 if (fun
->isNamedLambda()) {
2469 if (AtomDefnPtr p
= pc
->lexdeps
->lookup(fun
->name())) {
2470 Definition
* dn
= p
.value().get
<FullParseHandler
>();
2471 if (!ConvertDefinitionToNamedLambdaUse(tokenStream
, pc
, funbox
, dn
))
2476 InternalHandle
<Bindings
*> bindings
=
2477 InternalHandle
<Bindings
*>::fromMarkedLocation(&funbox
->bindings
);
2478 if (!pc
->generateFunctionBindings(context
, tokenStream
, alloc
, bindings
))
2481 if (!FoldConstants(context
, &pn
, this))
2487 template <typename ParseHandler
>
2489 Parser
<ParseHandler
>::functionArgsAndBodyGeneric(Node pn
, HandleFunction fun
, FunctionType type
,
2490 FunctionSyntaxKind kind
)
2492 // Given a properly initialized parse context, try to parse an actual
2493 // function without concern for conversion to strict mode, use of lazy
2494 // parsing and such.
2496 Node prelude
= null();
2498 if (!functionArguments(kind
, &prelude
, pn
, &hasRest
))
2501 FunctionBox
* funbox
= pc
->sc
->asFunctionBox();
2503 fun
->setArgCount(pc
->numArgs());
2507 if (type
== Getter
&& fun
->nargs() > 0) {
2508 report(ParseError
, false, null(), JSMSG_ACCESSOR_WRONG_ARGS
, "getter", "no", "s");
2511 if (type
== Setter
&& fun
->nargs() != 1) {
2512 report(ParseError
, false, null(), JSMSG_ACCESSOR_WRONG_ARGS
, "setter", "one", "");
2516 if (kind
== Arrow
) {
2518 if (!tokenStream
.matchToken(&matched
, TOK_ARROW
))
2521 report(ParseError
, false, null(), JSMSG_BAD_ARROW_ARGS
);
2526 // Parse the function body.
2527 FunctionBodyType bodyType
= StatementListBody
;
2529 if (!tokenStream
.getToken(&tt
, TokenStream::Operand
))
2532 if (funbox
->isStarGenerator()) {
2533 report(ParseError
, false, null(), JSMSG_CURLY_BEFORE_BODY
);
2538 sawDeprecatedExpressionClosure
= true;
2540 tokenStream
.ungetToken();
2541 bodyType
= ExpressionBody
;
2542 fun
->setIsExprClosure();
2545 Node body
= functionBody(kind
, bodyType
);
2549 if (fun
->name() && !checkStrictBinding(fun
->name(), pn
))
2552 #if JS_HAS_EXPR_CLOSURES
2553 if (bodyType
== StatementListBody
) {
2556 if (!tokenStream
.matchToken(&matched
, TOK_RC
))
2559 report(ParseError
, false, null(), JSMSG_CURLY_AFTER_BODY
);
2562 funbox
->bufEnd
= pos().begin
+ 1;
2563 #if JS_HAS_EXPR_CLOSURES
2565 if (tokenStream
.hadError())
2567 funbox
->bufEnd
= pos().end
;
2568 if (kind
== Statement
&& !MatchOrInsertSemicolon(tokenStream
))
2573 return finishFunctionDefinition(pn
, funbox
, prelude
, body
);
2576 template <typename ParseHandler
>
2578 Parser
<ParseHandler
>::checkYieldNameValidity()
2580 // In star generators and in JS >= 1.7, yield is a keyword. Otherwise in
2581 // strict mode, yield is a future reserved word.
2582 if (pc
->isStarGenerator() || versionNumber() >= JSVERSION_1_7
|| pc
->sc
->strict
) {
2583 report(ParseError
, false, null(), JSMSG_RESERVED_ID
, "yield");
2589 template <typename ParseHandler
>
2590 typename
ParseHandler::Node
2591 Parser
<ParseHandler
>::functionStmt()
2593 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FUNCTION
));
2595 RootedPropertyName
name(context
);
2596 GeneratorKind generatorKind
= NotGenerator
;
2598 if (!tokenStream
.getToken(&tt
))
2601 if (tt
== TOK_MUL
) {
2602 generatorKind
= StarGenerator
;
2603 if (!tokenStream
.getToken(&tt
))
2607 if (tt
== TOK_NAME
) {
2608 name
= tokenStream
.currentName();
2609 } else if (tt
== TOK_YIELD
) {
2610 if (!checkYieldNameValidity())
2612 name
= tokenStream
.currentName();
2614 /* Unnamed function expressions are forbidden in statement context. */
2615 report(ParseError
, false, null(), JSMSG_UNNAMED_FUNCTION_STMT
);
2619 /* We forbid function statements in strict mode code. */
2620 if (!pc
->atBodyLevel() && pc
->sc
->needStrictChecks() &&
2621 !report(ParseStrictError
, pc
->sc
->strict
, null(), JSMSG_STRICT_FUNCTION_STATEMENT
))
2624 return functionDef(name
, Normal
, Statement
, generatorKind
);
2627 template <typename ParseHandler
>
2628 typename
ParseHandler::Node
2629 Parser
<ParseHandler
>::functionExpr()
2631 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FUNCTION
));
2633 GeneratorKind generatorKind
= NotGenerator
;
2635 if (!tokenStream
.getToken(&tt
))
2638 if (tt
== TOK_MUL
) {
2639 generatorKind
= StarGenerator
;
2640 if (!tokenStream
.getToken(&tt
))
2644 RootedPropertyName
name(context
);
2645 if (tt
== TOK_NAME
) {
2646 name
= tokenStream
.currentName();
2647 } else if (tt
== TOK_YIELD
) {
2648 if (!checkYieldNameValidity())
2650 name
= tokenStream
.currentName();
2652 tokenStream
.ungetToken();
2655 return functionDef(name
, Normal
, Expression
, generatorKind
);
2659 * Return true if this node, known to be an unparenthesized string literal,
2660 * could be the string of a directive in a Directive Prologue. Directive
2661 * strings never contain escape sequences or line continuations.
2662 * isEscapeFreeStringLiteral, below, checks whether the node itself could be
2666 IsEscapeFreeStringLiteral(const TokenPos
& pos
, JSAtom
* str
)
2669 * If the string's length in the source code is its length as a value,
2670 * accounting for the quotes, then it must not contain any escape
2671 * sequences or line continuations.
2673 return pos
.begin
+ str
->length() + 2 == pos
.end
;
2678 Parser
<SyntaxParseHandler
>::asmJS(Node list
)
2680 // While asm.js could technically be validated and compiled during syntax
2681 // parsing, we have no guarantee that some later JS wouldn't abort the
2682 // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
2683 // For simplicity, unconditionally abort the syntax parse when "use asm" is
2684 // encountered so that asm.js is always validated/compiled exactly once
2685 // during a full parse.
2686 JS_ALWAYS_FALSE(abortIfSyntaxParser());
2692 Parser
<FullParseHandler
>::asmJS(Node list
)
2694 // Disable syntax parsing in anything nested inside the asm.js module.
2695 handler
.disableSyntaxParser();
2697 // We should be encountering the "use asm" directive for the first time; if
2698 // the directive is already, we must have failed asm.js validation and we're
2699 // reparsing. In that case, don't try to validate again. A non-null
2700 // newDirectives means we're not in a normal function.
2701 if (!pc
->newDirectives
|| pc
->newDirectives
->asmJS())
2704 // If there is no ScriptSource, then we are doing a non-compiling parse and
2705 // so we shouldn't (and can't, without a ScriptSource) compile.
2709 pc
->sc
->asFunctionBox()->useAsm
= true;
2711 // Attempt to validate and compile this asm.js module. On success, the
2712 // tokenStream has been advanced to the closing }. On failure, the
2713 // tokenStream is in an indeterminate state and we must reparse the
2714 // function from the beginning. Reparsing is triggered by marking that a
2715 // new directive has been encountered and returning 'false'.
2717 if (!ValidateAsmJS(context
, *this, list
, &validated
))
2720 pc
->newDirectives
->setAsmJS();
2728 * Recognize Directive Prologue members and directives. Assuming |pn| is a
2729 * candidate for membership in a directive prologue, recognize directives and
2730 * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
2731 * |pn_prologue| flag.
2733 * Note that the following is a strict mode function:
2736 * "blah" // inserted semi colon
2742 * That is, even though "use\x20loose" can never be a directive, now or in the
2743 * future (because of the hex escape), the Directive Prologue extends through it
2744 * to the "use strict" statement, which is indeed a directive.
2746 template <typename ParseHandler
>
2748 Parser
<ParseHandler
>::maybeParseDirective(Node list
, Node pn
, bool* cont
)
2750 TokenPos directivePos
;
2751 JSAtom
* directive
= handler
.isStringExprStatement(pn
, &directivePos
);
2753 *cont
= !!directive
;
2757 if (IsEscapeFreeStringLiteral(directivePos
, directive
)) {
2758 // Mark this statement as being a possibly legitimate part of a
2759 // directive prologue, so the bytecode emitter won't warn about it being
2760 // useless code. (We mustn't just omit the statement entirely yet, as it
2761 // could be producing the value of an eval or JSScript execution.)
2763 // Note that even if the string isn't one we recognize as a directive,
2764 // the emitter still shouldn't flag it as useless, as it could become a
2765 // directive in the future. We don't want to interfere with people
2766 // taking advantage of directive-prologue-enabled features that appear
2767 // in other browsers first.
2768 handler
.setPrologue(pn
);
2770 if (directive
== context
->names().useStrict
) {
2771 // We're going to be in strict mode. Note that this scope explicitly
2772 // had "use strict";
2773 pc
->sc
->setExplicitUseStrict();
2774 if (!pc
->sc
->strict
) {
2775 if (pc
->sc
->isFunctionBox()) {
2776 // Request that this function be reparsed as strict.
2777 pc
->newDirectives
->setStrict();
2780 // We don't reparse global scopes, so we keep track of the
2781 // one possible strict violation that could occur in the
2782 // directive prologue -- octal escapes -- and complain now.
2783 if (tokenStream
.sawOctalEscape()) {
2784 report(ParseError
, false, null(), JSMSG_DEPRECATED_OCTAL
);
2787 pc
->sc
->strict
= true;
2790 } else if (directive
== context
->names().useAsm
) {
2791 if (pc
->sc
->isFunctionBox())
2793 return report(ParseWarning
, false, pn
, JSMSG_USE_ASM_DIRECTIVE_FAIL
);
2800 * Parse the statements in a block, creating a StatementList node that lists
2801 * the statements. If called from block-parsing code, the caller must match
2802 * '{' before and '}' after.
2804 template <typename ParseHandler
>
2805 typename
ParseHandler::Node
2806 Parser
<ParseHandler
>::statements()
2808 JS_CHECK_RECURSION(context
, return null());
2810 Node pn
= handler
.newStatementList(pc
->blockid(), pos());
2814 Node saveBlock
= pc
->blockNode
;
2817 bool canHaveDirectives
= pc
->atBodyLevel();
2820 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
)) {
2821 if (tokenStream
.isEOF())
2822 isUnexpectedEOF_
= true;
2825 if (tt
== TOK_EOF
|| tt
== TOK_RC
)
2827 Node next
= statement(canHaveDirectives
);
2829 if (tokenStream
.isEOF())
2830 isUnexpectedEOF_
= true;
2834 if (canHaveDirectives
) {
2835 if (!maybeParseDirective(pn
, next
, &canHaveDirectives
))
2839 handler
.addStatementToList(pn
, next
, pc
);
2843 * Handle the case where there was a let declaration under this block. If
2844 * it replaced pc->blockNode with a new block node then we must refresh pn
2845 * and then restore pc->blockNode.
2847 if (pc
->blockNode
!= pn
)
2849 pc
->blockNode
= saveBlock
;
2853 template <typename ParseHandler
>
2854 typename
ParseHandler::Node
2855 Parser
<ParseHandler
>::condition()
2857 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
2858 Node pn
= exprInParens();
2861 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
2863 /* Check for (a = b) and warn about possible (a == b) mistype. */
2864 if (handler
.isOperationWithoutParens(pn
, PNK_ASSIGN
) &&
2865 !report(ParseExtraWarning
, false, null(), JSMSG_EQUAL_AS_ASSIGN
))
2872 template <typename ParseHandler
>
2874 Parser
<ParseHandler
>::matchLabel(MutableHandle
<PropertyName
*> label
)
2877 if (!tokenStream
.peekTokenSameLine(&tt
, TokenStream::Operand
))
2879 if (tt
== TOK_NAME
) {
2880 tokenStream
.consumeKnownToken(TOK_NAME
);
2881 label
.set(tokenStream
.currentName());
2882 } else if (tt
== TOK_YIELD
) {
2883 tokenStream
.consumeKnownToken(TOK_YIELD
);
2884 if (!checkYieldNameValidity())
2886 label
.set(tokenStream
.currentName());
2893 template <typename ParseHandler
>
2895 Parser
<ParseHandler
>::reportRedeclaration(Node pn
, Definition::Kind redeclKind
, HandlePropertyName name
)
2897 JSAutoByteString printable
;
2898 if (!AtomToPrintableString(context
, name
, &printable
))
2901 StmtInfoPC
* stmt
= LexicalLookup(pc
, name
, nullptr, (StmtInfoPC
*)nullptr);
2902 if (stmt
&& stmt
->type
== STMT_CATCH
) {
2903 report(ParseError
, false, pn
, JSMSG_REDECLARED_CATCH_IDENTIFIER
, printable
.ptr());
2905 if (redeclKind
== Definition::ARG
) {
2906 report(ParseError
, false, pn
, JSMSG_REDECLARED_PARAM
, printable
.ptr());
2908 report(ParseError
, false, pn
, JSMSG_REDECLARED_VAR
, Definition::kindString(redeclKind
),
2916 * Define a lexical binding in a block, let-expression, or comprehension scope. pc
2917 * must already be in such a scope.
2919 * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
2920 * property for the new variable on the block object, pc->staticScope;
2921 * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
2922 * data->pn in a slot of the block object.
2926 Parser
<FullParseHandler
>::bindLexical(BindData
<FullParseHandler
>* data
,
2927 HandlePropertyName name
, Parser
<FullParseHandler
>* parser
)
2929 ParseContext
<FullParseHandler
>* pc
= parser
->pc
;
2930 ParseNode
* pn
= data
->pn
;
2931 if (!parser
->checkStrictBinding(name
, pn
))
2934 ExclusiveContext
* cx
= parser
->context
;
2935 Rooted
<StaticBlockObject
*> blockObj(cx
, data
->let
.blockObj
);
2939 index
= blockObj
->numVariables();
2940 if (index
>= StaticBlockObject::LOCAL_INDEX_LIMIT
) {
2941 parser
->report(ParseError
, false, pn
, data
->let
.overflow
);
2945 // If we don't have a block object, we are parsing a body-level let,
2946 // in which case we use a bogus index. See comment block below in
2947 // setting the pn_cookie for explanation on how it gets adjusted.
2951 // For block-level lets, assign block-local index to pn->pn_cookie right
2952 // away, encoding it as an upvar cookie whose skip tells the current
2953 // static level. The emitter will adjust the node's slot based on its
2954 // stack depth model -- and, for global and eval code,
2955 // js::frontend::CompileScript will adjust the slot again to include
2956 // script->nfixed and body-level lets.
2958 // For body-level lets, the index is bogus at this point and is adjusted
2959 // when creating Bindings. See ParseContext::generateFunctionBindings and
2960 // AppendPackedBindings.
2961 if (!pn
->pn_cookie
.set(parser
->tokenStream
, pc
->staticLevel
, index
))
2964 Definition
* dn
= pc
->decls().lookupFirst(name
);
2965 Definition::Kind bindingKind
= data
->isConst
? Definition::CONST
: Definition::LET
;
2968 * For bindings that are hoisted to the beginning of the block/function,
2969 * define() right now. Otherwise, delay define until PushLetScope.
2971 if (data
->let
.varContext
== HoistVars
) {
2972 if (dn
&& dn
->pn_blockid
== pc
->blockid())
2973 return parser
->reportRedeclaration(pn
, dn
->kind(), name
);
2974 if (!pc
->define(parser
->tokenStream
, name
, pn
, bindingKind
))
2980 RootedId
id(cx
, NameToId(name
));
2981 RootedShape
shape(cx
, StaticBlockObject::addVar(cx
, blockObj
, id
,
2982 data
->isConst
, index
, &redeclared
));
2985 // The only way to be redeclared without a previous definition is if we're in a
2986 // comma separated list in a DontHoistVars block, so a let block of for header. In
2987 // that case, we must be redeclaring the same type of definition as we're trying to
2989 Definition::Kind dnKind
= dn
? dn
->kind() : bindingKind
;
2990 parser
->reportRedeclaration(pn
, dnKind
, name
);
2995 /* Store pn in the static block object. */
2996 blockObj
->setDefinitionParseNode(index
, reinterpret_cast<Definition
*>(pn
));
2998 // Body-level lets are hoisted and need to have been defined via
2999 // pc->define above.
3000 MOZ_ASSERT(data
->let
.varContext
== HoistVars
);
3001 MOZ_ASSERT(pc
->decls().lookupFirst(name
));
3009 Parser
<SyntaxParseHandler
>::bindLexical(BindData
<SyntaxParseHandler
>* data
,
3010 HandlePropertyName name
, Parser
<SyntaxParseHandler
>* parser
)
3012 if (!parser
->checkStrictBinding(name
, data
->pn
))
3018 template <typename ParseHandler
, class Op
>
3020 ForEachLetDef(TokenStream
& ts
, ParseContext
<ParseHandler
>* pc
,
3021 HandleStaticBlockObject blockObj
, Op op
)
3023 for (Shape::Range
<CanGC
> r(ts
.context(), blockObj
->lastProperty()); !r
.empty(); r
.popFront()) {
3024 Shape
& shape
= r
.front();
3026 /* Beware the destructuring dummy slots. */
3027 if (JSID_IS_INT(shape
.propid()))
3030 if (!op(ts
, pc
, blockObj
, shape
, JSID_TO_ATOM(shape
.propid())))
3036 template <typename ParseHandler
>
3038 bool operator()(TokenStream
&, ParseContext
<ParseHandler
>* pc
, HandleStaticBlockObject
,
3039 const Shape
&, JSAtom
* atom
)
3041 pc
->popLetDecl(atom
);
3046 // We compute the maximum block scope depth, in slots, of a compilation unit at
3047 // parse-time. Each nested statement has a field indicating the maximum block
3048 // scope depth that is nested inside it. When we leave a nested statement, we
3049 // add the number of slots in the statement to the nested depth, and use that to
3050 // update the maximum block scope depth of the outer statement or parse
3051 // context. In the end, pc->blockScopeDepth will indicate the number of slots
3052 // to reserve in the fixed part of a stack frame.
3054 template <typename ParseHandler
>
3056 AccumulateBlockScopeDepth(ParseContext
<ParseHandler
>* pc
)
3058 uint32_t innerDepth
= pc
->topStmt
->innerBlockScopeDepth
;
3059 StmtInfoPC
* outer
= pc
->topStmt
->down
;
3061 if (pc
->topStmt
->isBlockScope
)
3062 innerDepth
+= pc
->topStmt
->staticScope
->template as
<StaticBlockObject
>().numVariables();
3065 if (outer
->innerBlockScopeDepth
< innerDepth
)
3066 outer
->innerBlockScopeDepth
= innerDepth
;
3068 if (pc
->blockScopeDepth
< innerDepth
)
3069 pc
->blockScopeDepth
= innerDepth
;
3073 template <typename ParseHandler
>
3075 PopStatementPC(TokenStream
& ts
, ParseContext
<ParseHandler
>* pc
)
3077 RootedNestedScopeObject
scopeObj(ts
.context(), pc
->topStmt
->staticScope
);
3078 MOZ_ASSERT(!!scopeObj
== pc
->topStmt
->isNestedScope
);
3080 AccumulateBlockScopeDepth(pc
);
3081 FinishPopStatement(pc
);
3084 if (scopeObj
->is
<StaticBlockObject
>()) {
3085 RootedStaticBlockObject
blockObj(ts
.context(), &scopeObj
->as
<StaticBlockObject
>());
3086 MOZ_ASSERT(!blockObj
->inDictionaryMode());
3087 ForEachLetDef(ts
, pc
, blockObj
, PopLetDecl
<ParseHandler
>());
3089 scopeObj
->resetEnclosingNestedScopeFromParser();
3094 * The function LexicalLookup searches a static binding for the given name in
3095 * the stack of statements enclosing the statement currently being parsed. Each
3096 * statement that introduces a new scope has a corresponding scope object, on
3097 * which the bindings for that scope are stored. LexicalLookup either returns
3098 * the innermost statement which has a scope object containing a binding with
3099 * the given name, or nullptr.
3101 template <class ContextT
>
3102 typename
ContextT::StmtInfo
*
3103 LexicalLookup(ContextT
* ct
, HandleAtom atom
, int* slotp
, typename
ContextT::StmtInfo
* stmt
)
3105 RootedId
id(ct
->sc
->context
, AtomToId(atom
));
3108 stmt
= ct
->topScopeStmt
;
3109 for (; stmt
; stmt
= stmt
->downScope
) {
3111 * With-statements introduce dynamic bindings. Since dynamic bindings
3112 * can potentially override any static bindings introduced by statements
3113 * further up the stack, we have to abort the search.
3115 if (stmt
->type
== STMT_WITH
&& !ct
->sc
->isDotVariable(atom
))
3118 // Skip statements that do not introduce a new scope
3119 if (!stmt
->isBlockScope
)
3122 StaticBlockObject
& blockObj
= stmt
->staticBlock();
3123 Shape
* shape
= blockObj
.lookup(ct
->sc
->context
, id
);
3126 *slotp
= blockObj
.shapeToIndex(*shape
);
3136 template <typename ParseHandler
>
3138 OuterLet(ParseContext
<ParseHandler
>* pc
, StmtInfoPC
* stmt
, HandleAtom atom
)
3140 while (stmt
->downScope
) {
3141 stmt
= LexicalLookup(pc
, atom
, nullptr, stmt
->downScope
);
3144 if (stmt
->type
== STMT_BLOCK
)
3150 template <typename ParseHandler
>
3152 Parser
<ParseHandler
>::bindVarOrGlobalConst(BindData
<ParseHandler
>* data
,
3153 HandlePropertyName name
, Parser
<ParseHandler
>* parser
)
3155 ExclusiveContext
* cx
= parser
->context
;
3156 ParseContext
<ParseHandler
>* pc
= parser
->pc
;
3158 bool isConstDecl
= data
->op
== JSOP_DEFCONST
;
3160 /* Default best op for pn is JSOP_GETNAME; we'll try to improve below. */
3161 parser
->handler
.setOp(pn
, JSOP_GETNAME
);
3163 if (!parser
->checkStrictBinding(name
, pn
))
3166 StmtInfoPC
* stmt
= LexicalLookup(pc
, name
, nullptr, (StmtInfoPC
*)nullptr);
3168 if (stmt
&& stmt
->type
== STMT_WITH
) {
3169 parser
->handler
.setFlag(pn
, PND_DEOPTIMIZED
);
3170 if (pc
->sc
->isFunctionBox()) {
3171 FunctionBox
* funbox
= pc
->sc
->asFunctionBox();
3172 funbox
->setMightAliasLocals();
3176 * This definition isn't being added to the parse context's
3177 * declarations, so make sure to indicate the need to deoptimize
3178 * the script's arguments object. Mark the function as if it
3179 * contained a debugger statement, which will deoptimize arguments
3180 * as much as possible.
3182 if (name
== cx
->names().arguments
)
3183 pc
->sc
->setHasDebuggerStatement();
3188 DefinitionList::Range defs
= pc
->decls().lookupMulti(name
);
3189 MOZ_ASSERT_IF(stmt
, !defs
.empty());
3192 return pc
->define(parser
->tokenStream
, name
, pn
,
3193 isConstDecl
? Definition::GLOBALCONST
: Definition::VAR
);
3197 * There was a previous declaration with the same name. The standard
3198 * disallows several forms of redeclaration. Critically,
3199 * let (x) { var x; } // error
3200 * is not allowed which allows us to turn any non-error redeclaration
3201 * into a use of the initial declaration.
3203 DefinitionNode dn
= defs
.front
<ParseHandler
>();
3204 Definition::Kind dn_kind
= parser
->handler
.getDefinitionKind(dn
);
3205 if (dn_kind
== Definition::ARG
) {
3206 JSAutoByteString bytes
;
3207 if (!AtomToPrintableString(cx
, name
, &bytes
))
3211 parser
->report(ParseError
, false, pn
, JSMSG_REDECLARED_PARAM
, bytes
.ptr());
3214 if (!parser
->report(ParseExtraWarning
, false, pn
, JSMSG_VAR_HIDES_ARG
, bytes
.ptr()))
3217 bool inCatchBody
= (stmt
&& stmt
->type
== STMT_CATCH
);
3218 bool error
= (isConstDecl
||
3219 dn_kind
== Definition::CONST
||
3220 dn_kind
== Definition::GLOBALCONST
||
3221 (dn_kind
== Definition::LET
&&
3222 (!inCatchBody
|| OuterLet(pc
, stmt
, name
))));
3224 if (parser
->options().extraWarningsOption
3225 ? data
->op
!= JSOP_DEFVAR
|| dn_kind
!= Definition::VAR
3228 JSAutoByteString bytes
;
3229 if (!AtomToPrintableString(cx
, name
, &bytes
))
3232 ParseReportKind reporter
= error
? ParseError
: ParseExtraWarning
;
3234 ? parser
->report(reporter
, false, pn
,
3235 JSMSG_REDECLARED_CATCH_IDENTIFIER
, bytes
.ptr())
3236 : parser
->report(reporter
, false, pn
, JSMSG_REDECLARED_VAR
,
3237 Definition::kindString(dn_kind
), bytes
.ptr())))
3244 parser
->handler
.linkUseToDef(pn
, dn
);
3250 Parser
<FullParseHandler
>::makeSetCall(ParseNode
* pn
, unsigned msg
)
3252 MOZ_ASSERT(pn
->isKind(PNK_CALL
));
3253 MOZ_ASSERT(pn
->isArity(PN_LIST
));
3254 MOZ_ASSERT(pn
->isOp(JSOP_CALL
) || pn
->isOp(JSOP_SPREADCALL
) ||
3255 pn
->isOp(JSOP_EVAL
) || pn
->isOp(JSOP_STRICTEVAL
) ||
3256 pn
->isOp(JSOP_SPREADEVAL
) || pn
->isOp(JSOP_STRICTSPREADEVAL
) ||
3257 pn
->isOp(JSOP_FUNCALL
) || pn
->isOp(JSOP_FUNAPPLY
));
3259 if (!report(ParseStrictError
, pc
->sc
->strict
, pn
, msg
))
3261 handler
.markAsSetCall(pn
);
3265 template <typename ParseHandler
>
3267 Parser
<ParseHandler
>::noteNameUse(HandlePropertyName name
, Node pn
)
3270 * The asm.js validator does all its own symbol-table management so, as an
3271 * optimization, avoid doing any work here. Use-def links are only necessary
3272 * for emitting bytecode and successfully-validated asm.js does not emit
3273 * bytecode. (On validation failure, the asm.js module is reparsed.)
3275 if (pc
->useAsmOrInsideUseAsm())
3278 StmtInfoPC
* stmt
= LexicalLookup(pc
, name
, nullptr, (StmtInfoPC
*)nullptr);
3280 DefinitionList::Range defs
= pc
->decls().lookupMulti(name
);
3283 if (!defs
.empty()) {
3284 dn
= defs
.front
<ParseHandler
>();
3287 * No definition before this use in any lexical scope.
3288 * Create a placeholder definition node to either:
3289 * - Be adopted when we parse the real defining
3291 * - Be left as a free variable definition if we never
3292 * see the real definition.
3294 dn
= getOrCreateLexicalDependency(pc
, name
);
3299 handler
.linkUseToDef(pn
, dn
);
3302 if (stmt
->type
== STMT_WITH
) {
3303 handler
.setFlag(pn
, PND_DEOPTIMIZED
);
3304 } else if (stmt
->type
== STMT_SWITCH
&& stmt
->isBlockScope
) {
3305 // See comments above StmtInfoPC and switchStatement for how
3306 // firstDominatingLexicalInCase is computed.
3307 MOZ_ASSERT(stmt
->firstDominatingLexicalInCase
<= stmt
->staticBlock().numVariables());
3308 handler
.markMaybeUninitializedLexicalUseInSwitch(pn
, dn
,
3309 stmt
->firstDominatingLexicalInCase
);
3318 Parser
<FullParseHandler
>::bindDestructuringVar(BindData
<FullParseHandler
>* data
, ParseNode
* pn
)
3320 MOZ_ASSERT(pn
->isKind(PNK_NAME
));
3322 RootedPropertyName
name(context
, pn
->pn_atom
->asPropertyName());
3325 if (!data
->binder(data
, name
, this))
3329 * Select the appropriate name-setting opcode, respecting eager selection
3330 * done by the data->binder function.
3332 if (data
->op
== JSOP_INITLEXICAL
)
3333 pn
->setOp(JSOP_INITLEXICAL
);
3334 else if (pn
->pn_dflags
& PND_BOUND
)
3335 pn
->setOp(JSOP_SETLOCAL
);
3336 else if (data
->op
== JSOP_DEFCONST
)
3337 pn
->setOp(JSOP_SETCONST
);
3339 pn
->setOp(JSOP_SETNAME
);
3341 if (data
->op
== JSOP_DEFCONST
)
3342 pn
->pn_dflags
|= PND_CONST
;
3344 pn
->markAsAssigned();
3350 Parser
<FullParseHandler
>::checkDestructuring(BindData
<FullParseHandler
>* data
, ParseNode
* left
);
3354 Parser
<FullParseHandler
>::checkDestructuringObject(BindData
<FullParseHandler
>* data
,
3355 ParseNode
* objectPattern
)
3357 MOZ_ASSERT(objectPattern
->isKind(PNK_OBJECT
));
3359 for (ParseNode
* member
= objectPattern
->pn_head
; member
; member
= member
->pn_next
) {
3361 if (member
->isKind(PNK_MUTATEPROTO
)) {
3362 expr
= member
->pn_kid
;
3364 MOZ_ASSERT(member
->isKind(PNK_COLON
) || member
->isKind(PNK_SHORTHAND
));
3365 expr
= member
->pn_right
;
3369 if (expr
->isKind(PNK_ARRAY
) || expr
->isKind(PNK_OBJECT
)) {
3370 ok
= checkDestructuring(data
, expr
);
3372 if (!expr
->isKind(PNK_NAME
)) {
3373 report(ParseError
, false, expr
, JSMSG_NO_VARIABLE_NAME
);
3376 ok
= bindDestructuringVar(data
, expr
);
3378 ok
= checkAndMarkAsAssignmentLhs(expr
, KeyedDestructuringAssignment
);
3389 Parser
<FullParseHandler
>::checkDestructuringArray(BindData
<FullParseHandler
>* data
,
3390 ParseNode
* arrayPattern
)
3392 MOZ_ASSERT(arrayPattern
->isKind(PNK_ARRAY
));
3394 for (ParseNode
* element
= arrayPattern
->pn_head
; element
; element
= element
->pn_next
) {
3395 if (element
->isKind(PNK_ELISION
))
3398 ParseNode
* target
= element
;
3399 if (target
->isKind(PNK_SPREAD
)) {
3400 if (target
->pn_next
) {
3401 report(ParseError
, false, target
->pn_next
, JSMSG_PARAMETER_AFTER_REST
);
3404 target
= target
->pn_kid
;
3406 // The RestElement should not support nested patterns.
3407 if (target
->isKind(PNK_ARRAY
) || target
->isKind(PNK_OBJECT
)) {
3408 report(ParseError
, false, target
, JSMSG_BAD_DESTRUCT_TARGET
);
3414 if (target
->isKind(PNK_ARRAY
) || target
->isKind(PNK_OBJECT
)) {
3415 ok
= checkDestructuring(data
, target
);
3418 if (!target
->isKind(PNK_NAME
)) {
3419 report(ParseError
, false, target
, JSMSG_NO_VARIABLE_NAME
);
3422 ok
= bindDestructuringVar(data
, target
);
3424 ok
= checkAndMarkAsAssignmentLhs(target
, KeyedDestructuringAssignment
);
3435 * Destructuring patterns can appear in two kinds of contexts:
3437 * - assignment-like: assignment expressions and |for| loop heads. In
3438 * these cases, the patterns' property value positions can be
3439 * arbitrary lvalue expressions; the destructuring is just a fancy
3442 * - binding-like: |var| and |let| declarations, functions' formal
3443 * parameter lists, |catch| clauses, and comprehension tails. In
3444 * these cases, the patterns' property value positions must be
3445 * simple names; the destructuring defines them as new variables.
3447 * In both cases, other code parses the pattern as an arbitrary
3448 * primaryExpr, and then, here in checkDestructuring, verify that the
3449 * tree is a valid AssignmentPattern or BindingPattern.
3451 * In assignment-like contexts, we parse the pattern with
3452 * pc->inDeclDestructuring clear, so the lvalue expressions in the
3453 * pattern are parsed normally. primaryExpr links variable references
3454 * into the appropriate use chains; creates placeholder definitions;
3455 * and so on. checkDestructuring is called with |data| nullptr (since
3456 * we won't be binding any new names), and we specialize lvalues as
3459 * In declaration-like contexts, the normal variable reference
3460 * processing would just be an obstruction, because we're going to
3461 * define the names that appear in the property value positions as new
3462 * variables anyway. In this case, we parse the pattern with
3463 * pc->inDeclDestructuring set, which directs primaryExpr to leave
3464 * whatever name nodes it creates unconnected. Then, here in
3465 * checkDestructuring, we require the pattern's property value
3466 * positions to be simple names, and define them as appropriate to the
3467 * context. For these calls, |data| points to the right sort of
3472 Parser
<FullParseHandler
>::checkDestructuring(BindData
<FullParseHandler
>* data
, ParseNode
* left
)
3474 if (left
->isKind(PNK_ARRAYCOMP
)) {
3475 report(ParseError
, false, left
, JSMSG_ARRAY_COMP_LEFTSIDE
);
3479 if (left
->isKind(PNK_ARRAY
))
3480 return checkDestructuringArray(data
, left
);
3481 return checkDestructuringObject(data
, left
);
3486 Parser
<SyntaxParseHandler
>::checkDestructuring(BindData
<SyntaxParseHandler
>* data
, Node left
)
3488 return abortIfSyntaxParser();
3491 template <typename ParseHandler
>
3492 typename
ParseHandler::Node
3493 Parser
<ParseHandler
>::destructuringExpr(BindData
<ParseHandler
>* data
, TokenKind tt
)
3495 MOZ_ASSERT(tokenStream
.isCurrentTokenType(tt
));
3497 pc
->inDeclDestructuring
= true;
3498 Node pn
= primaryExpr(tt
);
3499 pc
->inDeclDestructuring
= false;
3502 if (!checkDestructuring(data
, pn
))
3507 template <typename ParseHandler
>
3508 typename
ParseHandler::Node
3509 Parser
<ParseHandler
>::destructuringExprWithoutYield(BindData
<ParseHandler
>* data
, TokenKind tt
,
3512 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
3513 Node res
= destructuringExpr(data
, tt
);
3514 if (res
&& pc
->lastYieldOffset
!= startYieldOffset
) {
3515 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
3522 template <typename ParseHandler
>
3523 typename
ParseHandler::Node
3524 Parser
<ParseHandler
>::pushLexicalScope(HandleStaticBlockObject blockObj
, StmtInfoPC
* stmt
)
3526 MOZ_ASSERT(blockObj
);
3528 ObjectBox
* blockbox
= newObjectBox(blockObj
);
3532 PushStatementPC(pc
, stmt
, STMT_BLOCK
);
3533 blockObj
->initEnclosingNestedScopeFromParser(pc
->staticScope
);
3534 FinishPushNestedScope(pc
, stmt
, *blockObj
.get());
3535 stmt
->isBlockScope
= true;
3537 Node pn
= handler
.newLexicalScope(blockbox
);
3541 if (!GenerateBlockId(tokenStream
, pc
, stmt
->blockid
))
3543 handler
.setBlockId(pn
, stmt
->blockid
);
3547 template <typename ParseHandler
>
3548 typename
ParseHandler::Node
3549 Parser
<ParseHandler
>::pushLexicalScope(StmtInfoPC
* stmt
)
3551 RootedStaticBlockObject
blockObj(context
, StaticBlockObject::create(context
));
3555 return pushLexicalScope(blockObj
, stmt
);
3562 explicit AddLetDecl(uint32_t blockid
) : blockid(blockid
) {}
3564 bool operator()(TokenStream
& ts
, ParseContext
<FullParseHandler
>* pc
,
3565 HandleStaticBlockObject blockObj
, const Shape
& shape
, JSAtom
*)
3567 ParseNode
* def
= (ParseNode
*) blockObj
->getSlot(shape
.slot()).toPrivate();
3568 def
->pn_blockid
= blockid
;
3569 RootedPropertyName
name(ts
.context(), def
->name());
3570 return pc
->define(ts
, name
, def
, Definition::LET
);
3576 Parser
<FullParseHandler
>::pushLetScope(HandleStaticBlockObject blockObj
, StmtInfoPC
* stmt
)
3578 MOZ_ASSERT(blockObj
);
3579 ParseNode
* pn
= pushLexicalScope(blockObj
, stmt
);
3583 pn
->pn_dflags
|= PND_LEXICAL
;
3585 /* Populate the new scope with decls found in the head with updated blockid. */
3586 if (!ForEachLetDef(tokenStream
, pc
, blockObj
, AddLetDecl(stmt
->blockid
)))
3593 SyntaxParseHandler::Node
3594 Parser
<SyntaxParseHandler
>::pushLetScope(HandleStaticBlockObject blockObj
, StmtInfoPC
* stmt
)
3596 JS_ALWAYS_FALSE(abortIfSyntaxParser());
3597 return SyntaxParseHandler::NodeFailure
;
3601 * Parse a let block statement or let expression (determined by 'letContext').
3602 * In both cases, bindings are not hoisted to the top of the enclosing block
3603 * and thus must be carefully injected between variables() and the let body.
3605 template <typename ParseHandler
>
3606 typename
ParseHandler::Node
3607 Parser
<ParseHandler
>::letBlock(LetContext letContext
)
3609 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_LET
));
3611 RootedStaticBlockObject
blockObj(context
, StaticBlockObject::create(context
));
3615 uint32_t begin
= pos().begin
;
3617 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_LET
);
3619 Node vars
= variables(PNK_LET
, nullptr, blockObj
, DontHoistVars
);
3623 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_LET
);
3625 StmtInfoPC
stmtInfo(context
);
3626 Node block
= pushLetScope(blockObj
, &stmtInfo
);
3630 Node pnlet
= handler
.newBinary(PNK_LET
, vars
, block
);
3633 handler
.setBeginPosition(pnlet
, begin
);
3635 bool needExprStmt
= false;
3636 if (letContext
== LetStatement
) {
3638 if (!tokenStream
.matchToken(&matched
, TOK_LC
, TokenStream::Operand
))
3642 * Strict mode eliminates a grammar ambiguity with unparenthesized
3643 * LetExpressions in an ExpressionStatement. If followed immediately
3644 * by an arguments list, it's ambiguous whether the let expression
3645 * is the callee or the call is inside the let expression body.
3647 * function id(x) { return x; }
3649 * // Does this parse as
3650 * // (let (loc = "inner") id)(loc) // "outer"
3652 * // let (loc = "inner") (id(loc)) // "inner"
3653 * let (loc = "inner") id(loc);
3657 if (!report(ParseStrictError
, pc
->sc
->strict
, pnlet
,
3658 JSMSG_STRICT_CODE_LET_EXPR_STMT
))
3664 * If this is really an expression in let statement guise, then we
3665 * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
3666 * the return value of the expression.
3668 needExprStmt
= true;
3669 letContext
= LetExpression
;
3674 if (letContext
== LetStatement
) {
3675 expr
= statements();
3678 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LET
);
3680 sawDeprecatedLetBlock
= true;
3681 if (!report(ParseWarning
, pc
->sc
->strict
, expr
, JSMSG_DEPRECATED_LET_BLOCK
))
3684 MOZ_ASSERT(letContext
== LetExpression
);
3685 expr
= assignExpr();
3689 sawDeprecatedLetExpression
= true;
3690 if (!report(ParseWarning
, pc
->sc
->strict
, expr
, JSMSG_DEPRECATED_LET_EXPRESSION
))
3693 handler
.setLexicalScopeBody(block
, expr
);
3694 PopStatementPC(tokenStream
, pc
);
3696 handler
.setEndPosition(pnlet
, pos().end
);
3699 if (!MatchOrInsertSemicolon(tokenStream
))
3701 return handler
.newExprStatement(pnlet
, pos().end
);
3706 template <typename ParseHandler
>
3708 PushBlocklikeStatement(TokenStream
& ts
, StmtInfoPC
* stmt
, StmtType type
,
3709 ParseContext
<ParseHandler
>* pc
)
3711 PushStatementPC(pc
, stmt
, type
);
3712 return GenerateBlockId(ts
, pc
, stmt
->blockid
);
3715 template <typename ParseHandler
>
3716 typename
ParseHandler::Node
3717 Parser
<ParseHandler
>::blockStatement()
3719 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_LC
));
3721 StmtInfoPC
stmtInfo(context
);
3722 if (!PushBlocklikeStatement(tokenStream
, &stmtInfo
, STMT_BLOCK
, pc
))
3725 Node list
= statements();
3729 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
3730 PopStatementPC(tokenStream
, pc
);
3734 template <typename ParseHandler
>
3735 typename
ParseHandler::Node
3736 Parser
<ParseHandler
>::newBindingNode(PropertyName
* name
, bool functionScope
, VarContext varContext
)
3739 * If this name is being injected into an existing block/function, see if
3740 * it has already been declared or if it resolves an outstanding lexdep.
3741 * Otherwise, this is a let block/expr that introduces a new scope and thus
3742 * shadows existing decls and doesn't resolve existing lexdeps. Duplicate
3743 * names are caught by bindLet.
3745 if (varContext
== HoistVars
) {
3746 if (AtomDefnPtr p
= pc
->lexdeps
->lookup(name
)) {
3747 DefinitionNode lexdep
= p
.value().get
<ParseHandler
>();
3748 MOZ_ASSERT(handler
.getDefinitionKind(lexdep
) == Definition::PLACEHOLDER
);
3750 Node pn
= handler
.getDefinitionNode(lexdep
);
3751 if (handler
.dependencyCovered(pn
, pc
->blockid(), functionScope
)) {
3752 handler
.setBlockId(pn
, pc
->blockid());
3753 pc
->lexdeps
->remove(p
);
3754 handler
.setPosition(pn
, pos());
3760 /* Make a new node for this declarator name (or destructuring pattern). */
3761 return newName(name
);
3765 * The 'blockObj' parameter is non-null when parsing the 'vars' in a let
3766 * expression, block statement, non-top-level let declaration in statement
3767 * context, and the let-initializer of a for-statement.
3769 template <typename ParseHandler
>
3770 typename
ParseHandler::Node
3771 Parser
<ParseHandler
>::variables(ParseNodeKind kind
, bool* psimple
,
3772 StaticBlockObject
* blockObj
, VarContext varContext
)
3775 * The four options here are:
3776 * - PNK_VAR: We're parsing var declarations.
3777 * - PNK_CONST: We're parsing const declarations.
3778 * - PNK_GLOBALCONST: We're parsing const declarations at toplevel (see bug 589119).
3779 * - PNK_LET: We are parsing a let declaration.
3781 MOZ_ASSERT(kind
== PNK_VAR
|| kind
== PNK_CONST
|| kind
== PNK_LET
|| kind
== PNK_GLOBALCONST
);
3784 * The simple flag is set if the declaration has the form 'var x', with
3785 * only one variable declared and no initializer expression.
3787 MOZ_ASSERT_IF(psimple
, *psimple
);
3790 if (kind
== PNK_VAR
)
3792 else if (kind
== PNK_GLOBALCONST
)
3795 Node pn
= handler
.newList(kind
, null(), op
);
3800 * SpiderMonkey const is really "write once per initialization evaluation"
3801 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
3802 * this code will change soon.
3804 BindData
<ParseHandler
> data(context
);
3805 if (kind
== PNK_VAR
|| kind
== PNK_GLOBALCONST
) {
3806 data
.initVarOrGlobalConst(op
);
3808 data
.initLexical(varContext
, blockObj
, JSMSG_TOO_MANY_LOCALS
,
3809 /* isConst = */ kind
== PNK_CONST
);
3816 if (psimple
&& !first
)
3821 if (!tokenStream
.getToken(&tt
))
3823 if (tt
== TOK_LB
|| tt
== TOK_LC
) {
3827 pc
->inDeclDestructuring
= true;
3828 pn2
= primaryExpr(tt
);
3829 pc
->inDeclDestructuring
= false;
3833 bool parsingForInOrOfInit
= false;
3834 if (pc
->parsingForInit
) {
3835 bool isForIn
, isForOf
;
3836 if (!matchInOrOf(&isForIn
, &isForOf
))
3838 parsingForInOrOfInit
= isForIn
|| isForOf
;
3841 // See comment below for bindBeforeInitializer in the code that
3842 // handles the non-destructuring case.
3843 bool bindBeforeInitializer
= (kind
!= PNK_LET
&& kind
!= PNK_CONST
) ||
3844 parsingForInOrOfInit
;
3845 if (bindBeforeInitializer
&& !checkDestructuring(&data
, pn2
))
3848 if (parsingForInOrOfInit
) {
3849 tokenStream
.ungetToken();
3850 handler
.addList(pn
, pn2
);
3854 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_BAD_DESTRUCT_DECL
);
3856 Node init
= assignExpr();
3860 if (!bindBeforeInitializer
&& !checkDestructuring(&data
, pn2
))
3863 pn2
= handler
.newBinaryOrAppend(PNK_ASSIGN
, pn2
, init
, pc
);
3866 handler
.addList(pn
, pn2
);
3870 if (tt
!= TOK_NAME
) {
3871 if (tt
== TOK_YIELD
) {
3872 if (!checkYieldNameValidity())
3875 report(ParseError
, false, null(), JSMSG_NO_VARIABLE_NAME
);
3880 RootedPropertyName
name(context
, tokenStream
.currentName());
3881 pn2
= newBindingNode(name
, kind
== PNK_VAR
|| kind
== PNK_GLOBALCONST
, varContext
);
3885 handler
.setFlag(pn2
, PND_CONST
);
3888 handler
.addList(pn
, pn2
);
3891 if (!tokenStream
.matchToken(&matched
, TOK_ASSIGN
))
3897 // In ES6, lexical bindings may not be accessed until
3898 // initialized. So a declaration of the form |let x = x| results
3899 // in a ReferenceError, as the 'x' on the RHS is accessing the let
3900 // binding before it is initialized.
3902 // If we are not parsing a let declaration, bind the name
3903 // now. Otherwise we must wait until after parsing the initializing
3905 bool bindBeforeInitializer
= kind
!= PNK_LET
&& kind
!= PNK_CONST
;
3906 if (bindBeforeInitializer
&& !data
.binder(&data
, name
, this))
3909 Node init
= assignExpr();
3913 if (!bindBeforeInitializer
&& !data
.binder(&data
, name
, this))
3916 if (!handler
.finishInitializerAssignment(pn2
, init
, data
.op
))
3919 if (data
.isConst
&& !pc
->parsingForInit
) {
3920 report(ParseError
, false, null(), JSMSG_BAD_CONST_DECL
);
3924 if (!data
.binder(&data
, name
, this))
3930 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
3941 Parser
<FullParseHandler
>::lexicalDeclaration(bool isConst
)
3943 handler
.disableSyntaxParser();
3949 * This is a let declaration. We must be directly under a block per the
3950 * proposed ES4 specs, but not an implicit block created due to
3951 * 'for (let ...)'. If we pass this error test, make the enclosing
3952 * StmtInfoPC be our scope. Further let declarations in this block will
3953 * find this scope statement and use the same block object.
3955 * If we are the first let declaration in this block (i.e., when the
3956 * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
3957 * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
3959 StmtInfoPC
* stmt
= pc
->topStmt
;
3960 if (stmt
&& (!stmt
->maybeScope() || stmt
->isForLetBlock
)) {
3961 report(ParseError
, false, null(), JSMSG_LEXICAL_DECL_NOT_IN_BLOCK
,
3962 isConst
? "const" : "let");
3966 if (stmt
&& stmt
->isBlockScope
) {
3967 MOZ_ASSERT(pc
->staticScope
== stmt
->staticScope
);
3969 if (pc
->atBodyLevel()) {
3971 * When bug 589199 is fixed, let variables will be stored in
3972 * the slots of a new scope chain object, encountered just
3973 * before the global object in the overall chain. This extra
3974 * object is present in the scope chain for all code in that
3975 * global, including self-hosted code. But self-hosted code
3976 * must be usable against *any* global object, including ones
3977 * with other let variables -- variables possibly placed in
3978 * conflicting slots. Forbid top-level let declarations to
3979 * prevent such conflicts from ever occurring.
3981 bool isGlobal
= !pc
->sc
->isFunctionBox() && stmt
== pc
->topScopeStmt
;
3982 if (options().selfHostingMode
&& isGlobal
) {
3983 report(ParseError
, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL
,
3984 isConst
? "'const'" : "'let'");
3989 * Parse body-level lets without a new block object. ES6 specs
3990 * that an execution environment's initial lexical environment
3991 * is the VariableEnvironment, i.e., body-level lets are in
3992 * the same environment record as vars.
3994 * However, they cannot be parsed exactly as vars, as ES6
3995 * requires that uninitialized lets throw ReferenceError on use.
3997 * See 8.1.1.1.6 and the note in 13.2.1.
3999 * FIXME global-level lets are still considered vars until
4000 * other bugs are fixed.
4002 ParseNodeKind kind
= PNK_LET
;
4004 kind
= isConst
? PNK_GLOBALCONST
: PNK_VAR
;
4007 pn
= variables(kind
);
4010 pn
->pn_xflags
|= PNX_POPVAR
;
4015 * Some obvious assertions here, but they may help clarify the
4016 * situation. This stmt is not yet a scope, so it must not be a
4017 * catch block (catch is a lexical scope by definition).
4019 MOZ_ASSERT(!stmt
->isBlockScope
);
4020 MOZ_ASSERT(stmt
!= pc
->topScopeStmt
);
4021 MOZ_ASSERT(stmt
->type
== STMT_BLOCK
||
4022 stmt
->type
== STMT_SWITCH
||
4023 stmt
->type
== STMT_TRY
||
4024 stmt
->type
== STMT_FINALLY
);
4025 MOZ_ASSERT(!stmt
->downScope
);
4027 /* Convert the block statement into a scope statement. */
4028 StaticBlockObject
* blockObj
= StaticBlockObject::create(context
);
4032 ObjectBox
* blockbox
= newObjectBox(blockObj
);
4037 * Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked
4038 * list stack, if it isn't already there. If it is there, but it
4039 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
4042 stmt
->isBlockScope
= stmt
->isNestedScope
= true;
4043 stmt
->downScope
= pc
->topScopeStmt
;
4044 pc
->topScopeStmt
= stmt
;
4046 blockObj
->initEnclosingNestedScopeFromParser(pc
->staticScope
);
4047 pc
->staticScope
= blockObj
;
4048 stmt
->staticScope
= blockObj
;
4051 ParseNode
* tmp
= pc
->blockNode
;
4052 MOZ_ASSERT(!tmp
|| !tmp
->isKind(PNK_LEXICALSCOPE
));
4055 /* Create a new lexical scope node for these statements. */
4056 ParseNode
* pn1
= LexicalScopeNode::create(PNK_LEXICALSCOPE
, &handler
);
4060 pn1
->pn_pos
= pc
->blockNode
->pn_pos
;
4061 pn1
->pn_objbox
= blockbox
;
4062 pn1
->pn_expr
= pc
->blockNode
;
4063 pn1
->pn_blockid
= pc
->blockNode
->pn_blockid
;
4064 pc
->blockNode
= pn1
;
4067 pn
= variables(isConst
? PNK_CONST
: PNK_LET
, nullptr,
4068 &pc
->staticScope
->as
<StaticBlockObject
>(), HoistVars
);
4071 pn
->pn_xflags
= PNX_POPVAR
;
4074 return MatchOrInsertSemicolon(tokenStream
) ? pn
: nullptr;
4078 SyntaxParseHandler::Node
4079 Parser
<SyntaxParseHandler
>::lexicalDeclaration(bool)
4081 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4082 return SyntaxParseHandler::NodeFailure
;
4087 Parser
<FullParseHandler
>::letStatement()
4089 handler
.disableSyntaxParser();
4091 /* Check for a let statement or let expression. */
4094 if (!tokenStream
.peekToken(&tt
))
4097 pn
= letBlock(LetStatement
);
4098 MOZ_ASSERT_IF(pn
, pn
->isKind(PNK_LET
) || pn
->isKind(PNK_SEMI
));
4100 pn
= lexicalDeclaration(/* isConst = */ false);
4106 SyntaxParseHandler::Node
4107 Parser
<SyntaxParseHandler
>::letStatement()
4109 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4110 return SyntaxParseHandler::NodeFailure
;
4113 template<typename ParseHandler
>
4114 typename
ParseHandler::Node
4115 Parser
<ParseHandler
>::importDeclaration()
4117 MOZ_ASSERT(tokenStream
.currentToken().type
== TOK_IMPORT
);
4119 if (pc
->sc
->isFunctionBox() || !pc
->atBodyLevel()) {
4120 report(ParseError
, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL
);
4124 uint32_t begin
= pos().begin
;
4126 if (!tokenStream
.getToken(&tt
))
4129 Node importSpecSet
= handler
.newList(PNK_IMPORT_SPEC_LIST
);
4133 if (tt
== TOK_NAME
|| tt
== TOK_LC
) {
4134 if (tt
== TOK_NAME
) {
4135 // Handle the form |import a from 'b'|, by adding a single import
4136 // specifier to the list, with 'default' as the import name and
4137 // 'a' as the binding name. This is equivalent to
4138 // |import { default as a } from 'b'|.
4139 Node importName
= newName(context
->names().default_
);
4143 Node bindingName
= newName(tokenStream
.currentName());
4147 Node importSpec
= handler
.newBinary(PNK_IMPORT_SPEC
, importName
, bindingName
);
4151 handler
.addList(importSpecSet
, importSpec
);
4154 // Handle the forms |import {} from 'a'| and
4155 // |import { ..., } from 'a'| (where ... is non empty), by
4156 // escaping the loop early if the next token is }.
4157 if (!tokenStream
.peekToken(&tt
, TokenStream::KeywordIsName
))
4162 // If the next token is a keyword, the previous call to
4163 // peekToken matched it as a TOK_NAME, and put it in the
4164 // lookahead buffer, so this call will match keywords as well.
4165 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NO_IMPORT_NAME
);
4166 Node importName
= newName(tokenStream
.currentName());
4170 if (!tokenStream
.getToken(&tt
))
4172 if (tt
== TOK_NAME
&& tokenStream
.currentName() == context
->names().as
) {
4173 if (!tokenStream
.getToken(&tt
))
4175 if (tt
!= TOK_NAME
) {
4176 report(ParseError
, false, null(), JSMSG_NO_BINDING_NAME
);
4180 // Keywords cannot be bound to themselves, so an import name
4181 // that is a keyword is a syntax error if it is not followed
4182 // by the keyword 'as'.
4183 if (IsKeyword(importName
->name())) {
4184 JSAutoByteString bytes
;
4185 if (!AtomToPrintableString(context
, importName
->name(), &bytes
))
4187 report(ParseError
, false, null(), JSMSG_AS_AFTER_RESERVED_WORD
, bytes
.ptr());
4190 tokenStream
.ungetToken();
4192 Node bindingName
= newName(tokenStream
.currentName());
4196 Node importSpec
= handler
.newBinary(PNK_IMPORT_SPEC
, importName
, bindingName
);
4200 handler
.addList(importSpecSet
, importSpec
);
4203 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
4209 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_RC_AFTER_IMPORT_SPEC_LIST
);
4212 if (!tokenStream
.getToken(&tt
))
4214 if (tt
!= TOK_NAME
|| tokenStream
.currentName() != context
->names().from
) {
4215 report(ParseError
, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET
);
4219 MUST_MATCH_TOKEN(TOK_STRING
, JSMSG_MODULE_SPEC_AFTER_FROM
);
4221 if (tt
!= TOK_STRING
) {
4222 report(ParseError
, false, null(), JSMSG_DECLARATION_AFTER_IMPORT
);
4226 // Handle the form |import 'a'| by leaving the list empty. This is
4227 // equivalent to |import {} from 'a'|.
4228 importSpecSet
->pn_pos
.end
= importSpecSet
->pn_pos
.begin
;
4231 Node moduleSpec
= stringLiteral();
4235 if (!MatchOrInsertSemicolon(tokenStream
))
4238 return handler
.newImportDeclaration(importSpecSet
, moduleSpec
,
4239 TokenPos(begin
, pos().end
));
4243 SyntaxParseHandler::Node
4244 Parser
<SyntaxParseHandler
>::importDeclaration()
4246 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4247 return SyntaxParseHandler::NodeFailure
;
4250 template<typename ParseHandler
>
4251 typename
ParseHandler::Node
4252 Parser
<ParseHandler
>::exportDeclaration()
4254 MOZ_ASSERT(tokenStream
.currentToken().type
== TOK_EXPORT
);
4256 if (pc
->sc
->isFunctionBox() || !pc
->atBodyLevel()) {
4257 report(ParseError
, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL
);
4261 uint32_t begin
= pos().begin
;
4265 if (!tokenStream
.getToken(&tt
))
4270 kid
= handler
.newList(PNK_EXPORT_SPEC_LIST
);
4276 // Handle the forms |export {}| and |export { ..., }| (where ...
4277 // is non empty), by escaping the loop early if the next token
4279 if (!tokenStream
.peekToken(&tt
))
4284 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NO_BINDING_NAME
);
4285 Node bindingName
= newName(tokenStream
.currentName());
4289 if (!tokenStream
.getToken(&tt
))
4291 if (tt
== TOK_NAME
&& tokenStream
.currentName() == context
->names().as
) {
4292 if (!tokenStream
.getToken(&tt
, TokenStream::KeywordIsName
))
4294 if (tt
!= TOK_NAME
) {
4295 report(ParseError
, false, null(), JSMSG_NO_EXPORT_NAME
);
4299 tokenStream
.ungetToken();
4301 Node exportName
= newName(tokenStream
.currentName());
4305 Node exportSpec
= handler
.newBinary(PNK_EXPORT_SPEC
, bindingName
, exportName
);
4309 handler
.addList(kid
, exportSpec
);
4312 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
4318 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_RC_AFTER_EXPORT_SPEC_LIST
);
4320 // Handle the form |export *| by adding a special export batch
4321 // specifier to the list.
4322 Node exportSpec
= handler
.newNullary(PNK_EXPORT_BATCH_SPEC
, JSOP_NOP
, pos());
4326 handler
.addList(kid
, exportSpec
);
4328 if (!tokenStream
.getToken(&tt
))
4330 if (tt
== TOK_NAME
&& tokenStream
.currentName() == context
->names().from
) {
4331 MUST_MATCH_TOKEN(TOK_STRING
, JSMSG_MODULE_SPEC_AFTER_FROM
);
4333 Node moduleSpec
= stringLiteral();
4337 if (!MatchOrInsertSemicolon(tokenStream
))
4340 return handler
.newExportFromDeclaration(begin
, kid
, moduleSpec
);
4342 tokenStream
.ungetToken();
4345 kid
= MatchOrInsertSemicolon(tokenStream
) ? kid
: nullptr;
4351 kid
= functionStmt();
4357 kid
= variables(PNK_VAR
);
4360 kid
->pn_xflags
= PNX_POPVAR
;
4362 kid
= MatchOrInsertSemicolon(tokenStream
) ? kid
: nullptr;
4368 // Handle the form |export a| in the same way as |export let a|, by
4369 // acting as if we've just seen the let keyword. Simply unget the token
4370 // and fall through.
4371 tokenStream
.ungetToken();
4374 kid
= lexicalDeclaration(tt
== TOK_CONST
);
4380 report(ParseError
, false, null(), JSMSG_DECLARATION_AFTER_EXPORT
);
4384 return handler
.newExportDeclaration(kid
, TokenPos(begin
, pos().end
));
4388 SyntaxParseHandler::Node
4389 Parser
<SyntaxParseHandler
>::exportDeclaration()
4391 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4392 return SyntaxParseHandler::NodeFailure
;
4395 template <typename ParseHandler
>
4396 typename
ParseHandler::Node
4397 Parser
<ParseHandler
>::expressionStatement()
4399 tokenStream
.ungetToken();
4400 Node pnexpr
= expr();
4403 if (!MatchOrInsertSemicolon(tokenStream
))
4405 return handler
.newExprStatement(pnexpr
, pos().end
);
4408 template <typename ParseHandler
>
4409 typename
ParseHandler::Node
4410 Parser
<ParseHandler
>::ifStatement()
4412 uint32_t begin
= pos().begin
;
4414 /* An IF node has three kids: condition, then, and optional else. */
4415 Node cond
= condition();
4420 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
4422 if (tt
== TOK_SEMI
) {
4423 if (!report(ParseExtraWarning
, false, null(), JSMSG_EMPTY_CONSEQUENT
))
4427 StmtInfoPC
stmtInfo(context
);
4428 PushStatementPC(pc
, &stmtInfo
, STMT_IF
);
4429 Node thenBranch
= statement();
4435 if (!tokenStream
.matchToken(&matched
, TOK_ELSE
, TokenStream::Operand
))
4438 stmtInfo
.type
= STMT_ELSE
;
4439 elseBranch
= statement();
4443 elseBranch
= null();
4446 PopStatementPC(tokenStream
, pc
);
4447 return handler
.newIfStatement(begin
, cond
, thenBranch
, elseBranch
);
4450 template <typename ParseHandler
>
4451 typename
ParseHandler::Node
4452 Parser
<ParseHandler
>::doWhileStatement()
4454 uint32_t begin
= pos().begin
;
4455 StmtInfoPC
stmtInfo(context
);
4456 PushStatementPC(pc
, &stmtInfo
, STMT_DO_LOOP
);
4457 Node body
= statement();
4460 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
4461 Node cond
= condition();
4464 PopStatementPC(tokenStream
, pc
);
4466 // The semicolon after do-while is even more optional than most
4467 // semicolons in JS. Web compat required this by 2004:
4468 // http://bugzilla.mozilla.org/show_bug.cgi?id=238945
4469 // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
4470 // https://bugs.ecmascript.org/show_bug.cgi?id=157
4472 if (!tokenStream
.matchToken(&ignored
, TOK_SEMI
))
4474 return handler
.newDoWhileStatement(body
, cond
, TokenPos(begin
, pos().end
));
4477 template <typename ParseHandler
>
4478 typename
ParseHandler::Node
4479 Parser
<ParseHandler
>::whileStatement()
4481 uint32_t begin
= pos().begin
;
4482 StmtInfoPC
stmtInfo(context
);
4483 PushStatementPC(pc
, &stmtInfo
, STMT_WHILE_LOOP
);
4484 Node cond
= condition();
4487 Node body
= statement();
4490 PopStatementPC(tokenStream
, pc
);
4491 return handler
.newWhileStatement(begin
, cond
, body
);
4494 template <typename ParseHandler
>
4496 Parser
<ParseHandler
>::matchInOrOf(bool* isForInp
, bool* isForOfp
)
4499 if (!tokenStream
.getToken(&tt
))
4501 *isForInp
= tt
== TOK_IN
;
4502 *isForOfp
= tt
== TOK_NAME
&& tokenStream
.currentToken().name() == context
->names().of
;
4503 if (!*isForInp
&& !*isForOfp
)
4504 tokenStream
.ungetToken();
4510 Parser
<FullParseHandler
>::isValidForStatementLHS(ParseNode
* pn1
, JSVersion version
,
4511 bool isForDecl
, bool isForEach
,
4512 ParseNodeKind headKind
)
4515 if (pn1
->pn_count
> 1)
4517 if (pn1
->isKind(PNK_CONST
))
4520 // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
4521 // Hence all other destructuring decls are banned there.
4522 if (version
== JSVERSION_1_7
&& !isForEach
&& headKind
== PNK_FORIN
) {
4523 ParseNode
* lhs
= pn1
->pn_head
;
4524 if (lhs
->isKind(PNK_ASSIGN
))
4527 if (lhs
->isKind(PNK_OBJECT
))
4529 if (lhs
->isKind(PNK_ARRAY
) && lhs
->pn_count
!= 2)
4535 switch (pn1
->getKind()) {
4544 // In JS 1.7 only, for ([K, V] in EXPR) has a special meaning.
4545 // Hence all other destructuring left-hand sides are banned there.
4546 if (version
== JSVERSION_1_7
&& !isForEach
&& headKind
== PNK_FORIN
)
4547 return pn1
->isKind(PNK_ARRAY
) && pn1
->pn_count
== 2;
4557 Parser
<FullParseHandler
>::checkForHeadConstInitializers(ParseNode
* pn1
)
4559 if (!pn1
->isKind(PNK_CONST
))
4562 for (ParseNode
* assign
= pn1
->pn_head
; assign
; assign
= assign
->pn_next
) {
4563 MOZ_ASSERT(assign
->isKind(PNK_ASSIGN
) || assign
->isKind(PNK_NAME
));
4564 if (assign
->isKind(PNK_NAME
) && !assign
->isAssigned())
4566 // PNK_ASSIGN nodes (destructuring assignment) are always assignments.
4573 Parser
<FullParseHandler
>::forStatement()
4575 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FOR
));
4576 uint32_t begin
= pos().begin
;
4578 StmtInfoPC
forStmt(context
);
4579 PushStatementPC(pc
, &forStmt
, STMT_FOR_LOOP
);
4581 bool isForEach
= false;
4582 unsigned iflags
= 0;
4584 if (allowsForEachIn()) {
4586 if (!tokenStream
.matchContextualKeyword(&matched
, context
->names().each
))
4589 iflags
= JSITER_FOREACH
;
4591 sawDeprecatedForEach
= true;
4592 if (versionNumber() < JSVERSION_LATEST
) {
4593 if (!report(ParseWarning
, pc
->sc
->strict
, null(), JSMSG_DEPRECATED_FOR_EACH
))
4599 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4602 * True if we have 'for (var/let/const ...)', except in the oddball case
4603 * where 'let' begins a let-expression in 'for (let (...) ...)'.
4605 bool isForDecl
= false;
4607 /* Non-null when isForDecl is true for a 'for (let ...)' statement. */
4608 RootedStaticBlockObject
blockObj(context
);
4610 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
4615 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
4617 if (tt
== TOK_SEMI
) {
4621 * Set pn1 to a var list or an initializing expression.
4623 * Set the parsingForInit flag during parsing of the first clause
4624 * of the for statement. This flag will be used by the RelExpr
4625 * production; if it is set, then the 'in' keyword will not be
4626 * recognized as an operator, leaving it available to be parsed as
4627 * part of a for/in loop.
4629 * A side effect of this restriction is that (unparenthesized)
4630 * expressions involving an 'in' operator are illegal in the init
4631 * clause of an ordinary for loop.
4633 pc
->parsingForInit
= true;
4634 if (tt
== TOK_VAR
) {
4636 tokenStream
.consumeKnownToken(tt
);
4637 pn1
= variables(PNK_VAR
);
4638 } else if (tt
== TOK_LET
|| tt
== TOK_CONST
) {
4639 handler
.disableSyntaxParser();
4640 bool constDecl
= tt
== TOK_CONST
;
4641 tokenStream
.consumeKnownToken(tt
);
4642 if (!tokenStream
.peekToken(&tt
))
4645 pn1
= letBlock(LetExpression
);
4648 blockObj
= StaticBlockObject::create(context
);
4651 pn1
= variables(constDecl
? PNK_CONST
: PNK_LET
, nullptr, blockObj
,
4657 pc
->parsingForInit
= false;
4663 MOZ_ASSERT_IF(isForDecl
, pn1
->isArity(PN_LIST
));
4664 MOZ_ASSERT(!!blockObj
== (isForDecl
&& pn1
->isOp(JSOP_NOP
)));
4666 // The form 'for (let <vars>; <expr2>; <expr3>) <stmt>' generates an
4667 // implicit block even if stmt is not a BlockStatement.
4668 // If the loop has that exact form, then:
4669 // - forLetImpliedBlock is the node for the implicit block scope.
4670 // - forLetDecl is the node for the decl 'let <vars>'.
4671 // Otherwise both are null.
4672 ParseNode
* forLetImpliedBlock
= nullptr;
4673 ParseNode
* forLetDecl
= nullptr;
4675 // If non-null, the node for the decl 'var v = expr1' in the weirdo form
4676 // 'for (var v = expr1 in expr2) stmt'.
4677 ParseNode
* hoistedVar
= nullptr;
4680 * We can be sure that it's a for/in loop if there's still an 'in'
4681 * keyword here, even if JavaScript recognizes 'in' as an operator,
4682 * as we've excluded 'in' from being parsed in RelExpr by setting
4683 * pc->parsingForInit.
4685 StmtInfoPC
letStmt(context
); /* used if blockObj != nullptr. */
4686 ParseNode
* pn2
, *pn3
; /* forHead->pn_kid2 and pn_kid3. */
4687 ParseNodeKind headKind
= PNK_FORHEAD
;
4689 bool isForIn
, isForOf
;
4690 if (!matchInOrOf(&isForIn
, &isForOf
))
4693 headKind
= PNK_FORIN
;
4695 headKind
= PNK_FOROF
;
4698 if (headKind
== PNK_FOROF
|| headKind
== PNK_FORIN
) {
4700 * Parse the rest of the for/in or for/of head.
4702 * Here pn1 is everything to the left of 'in' or 'of'. At the end of
4703 * this block, pn1 is a decl or nullptr, pn2 is the assignment target
4704 * that receives the enumeration value each iteration, and pn3 is the
4707 if (headKind
== PNK_FOROF
) {
4708 forStmt
.type
= STMT_FOR_OF_LOOP
;
4709 forStmt
.type
= (headKind
== PNK_FOROF
) ? STMT_FOR_OF_LOOP
: STMT_FOR_IN_LOOP
;
4711 report(ParseError
, false, null(), JSMSG_BAD_FOR_EACH_LOOP
);
4715 forStmt
.type
= STMT_FOR_IN_LOOP
;
4716 iflags
|= JSITER_ENUMERATE
;
4719 /* Check that the left side of the 'in' or 'of' is valid. */
4720 if (!isValidForStatementLHS(pn1
, versionNumber(), isForDecl
, isForEach
, headKind
)) {
4721 report(ParseError
, false, pn1
, JSMSG_BAD_FOR_LEFTSIDE
);
4726 * After the following if-else, pn2 will point to the name or
4727 * destructuring pattern on in's left. pn1 will point to the decl, if
4728 * any, else nullptr. Note that the "declaration with initializer" case
4729 * rewrites the loop-head, moving the decl and setting pn1 to nullptr.
4733 if ((pn2
->isKind(PNK_NAME
) && pn2
->maybeExpr()) || pn2
->isKind(PNK_ASSIGN
)) {
4735 * Declaration with initializer.
4737 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
4738 * 'const' to hoist the initializer or the entire decl out of
4741 if (headKind
== PNK_FOROF
) {
4742 report(ParseError
, false, pn2
, JSMSG_INVALID_FOR_OF_INIT
);
4746 report(ParseError
, false, pn2
, JSMSG_INVALID_FOR_IN_INIT
);
4753 * All of 'var x = i' is hoisted above 'for (x in o)'.
4755 * Request JSOP_POP here since the var is for a simple
4756 * name (it is not a destructuring binding's left-hand
4757 * side) and it has an initializer.
4759 pn1
->pn_xflags
|= PNX_POPVAR
;
4762 if (pn2
->isKind(PNK_ASSIGN
)) {
4764 MOZ_ASSERT(pn2
->isKind(PNK_ARRAY
) || pn2
->isKind(PNK_OBJECT
) ||
4765 pn2
->isKind(PNK_NAME
));
4769 /* Not a declaration. */
4770 MOZ_ASSERT(!blockObj
);
4774 if (!checkAndMarkAsAssignmentLhs(pn2
, PlainAssignment
))
4778 pn3
= (headKind
== PNK_FOROF
) ? assignExpr() : expr();
4784 * Now that the pn3 has been parsed, push the let scope. To hold
4785 * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node
4786 * created by PushLetScope around the for's initializer. This also
4787 * serves to indicate the let-decl to the emitter.
4789 ParseNode
* block
= pushLetScope(blockObj
, &letStmt
);
4792 letStmt
.isForLetBlock
= true;
4793 block
->pn_expr
= pn1
;
4794 block
->pn_pos
= pn1
->pn_pos
;
4800 * pn2 is part of a declaration. Make a copy that can be passed to
4801 * EmitAssignment. Take care to do this after PushLetScope.
4803 pn2
= cloneLeftHandSide(pn2
);
4808 switch (pn2
->getKind()) {
4810 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4811 pn2
->markAsAssigned();
4815 MOZ_CRASH("forStatement TOK_ASSIGN");
4819 if (versionNumber() == JSVERSION_1_7
) {
4821 * Destructuring for-in requires [key, value] enumeration
4824 if (!isForEach
&& headKind
== PNK_FORIN
) {
4825 iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
4826 sawDeprecatedDestructuringForIn
= true;
4835 reportWithOffset(ParseError
, false, begin
, JSMSG_BAD_FOR_EACH_LOOP
);
4839 headKind
= PNK_FORHEAD
;
4843 * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
4844 * to induce the correct scoping for A. Ensure here that the previously
4845 * unchecked assignment mandate for const declarations holds.
4847 if (!checkForHeadConstInitializers(pn1
)) {
4848 report(ParseError
, false, nullptr, JSMSG_BAD_CONST_DECL
);
4852 forLetImpliedBlock
= pushLetScope(blockObj
, &letStmt
);
4853 if (!forLetImpliedBlock
)
4855 letStmt
.isForLetBlock
= true;
4861 /* Parse the loop condition or null into pn2. */
4862 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
4864 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
4866 if (tt
== TOK_SEMI
) {
4874 /* Parse the update expression or null into pn3. */
4875 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
4876 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
4887 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
4889 TokenPos
headPos(begin
, pos().end
);
4890 ParseNode
* forHead
= handler
.newForHead(headKind
, pn1
, pn2
, pn3
, headPos
);
4894 /* Parse the loop body. */
4895 ParseNode
* body
= statement();
4900 PopStatementPC(tokenStream
, pc
);
4901 PopStatementPC(tokenStream
, pc
);
4903 ParseNode
* forLoop
= handler
.newForStatement(begin
, forHead
, body
, iflags
);
4908 ParseNode
* pnseq
= handler
.newList(PNK_SEQ
, hoistedVar
);
4911 pnseq
->pn_pos
= forLoop
->pn_pos
;
4912 pnseq
->append(forLoop
);
4915 if (forLetImpliedBlock
) {
4916 forLetImpliedBlock
->pn_expr
= forLoop
;
4917 forLetImpliedBlock
->pn_pos
= forLoop
->pn_pos
;
4918 ParseNode
* let
= handler
.newBinary(PNK_LET
, forLetDecl
, forLetImpliedBlock
);
4921 let
->pn_pos
= forLoop
->pn_pos
;
4928 SyntaxParseHandler::Node
4929 Parser
<SyntaxParseHandler
>::forStatement()
4932 * 'for' statement parsing is fantastically complicated and requires being
4933 * able to inspect the parse tree for previous parts of the 'for'. Syntax
4934 * parsing of 'for' statements is thus done separately, and only handles
4935 * the types of 'for' statements likely to be seen in web content.
4937 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FOR
));
4939 StmtInfoPC
forStmt(context
);
4940 PushStatementPC(pc
, &forStmt
, STMT_FOR_LOOP
);
4942 /* Don't parse 'for each' loops. */
4943 if (allowsForEachIn()) {
4945 if (!tokenStream
.peekToken(&tt
))
4947 // Not all "yield" tokens are names, but the ones that aren't names are
4948 // invalid in this context anyway.
4949 if (tt
== TOK_NAME
|| tt
== TOK_YIELD
) {
4950 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4955 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4957 /* True if we have 'for (var ...)'. */
4958 bool isForDecl
= false;
4959 bool simpleForDecl
= true;
4961 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
4966 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
4968 if (tt
== TOK_SEMI
) {
4971 /* Set lhsNode to a var list or an initializing expression. */
4972 pc
->parsingForInit
= true;
4973 if (tt
== TOK_VAR
) {
4975 tokenStream
.consumeKnownToken(tt
);
4976 lhsNode
= variables(PNK_VAR
, &simpleForDecl
);
4978 else if (tt
== TOK_CONST
|| tt
== TOK_LET
) {
4979 JS_ALWAYS_FALSE(abortIfSyntaxParser());
4987 pc
->parsingForInit
= false;
4992 * We can be sure that it's a for/in loop if there's still an 'in'
4993 * keyword here, even if JavaScript recognizes 'in' as an operator,
4994 * as we've excluded 'in' from being parsed in RelExpr by setting
4995 * pc->parsingForInit.
4997 bool isForIn
= false, isForOf
= false;
4999 if (!matchInOrOf(&isForIn
, &isForOf
))
5002 if (isForIn
|| isForOf
) {
5003 /* Parse the rest of the for/in or for/of head. */
5004 forStmt
.type
= isForOf
? STMT_FOR_OF_LOOP
: STMT_FOR_IN_LOOP
;
5006 /* Check that the left side of the 'in' or 'of' is valid. */
5008 lhsNode
!= SyntaxParseHandler::NodeName
&&
5009 lhsNode
!= SyntaxParseHandler::NodeGetProp
&&
5010 lhsNode
!= SyntaxParseHandler::NodeLValue
)
5012 JS_ALWAYS_FALSE(abortIfSyntaxParser());
5016 if (!simpleForDecl
) {
5017 JS_ALWAYS_FALSE(abortIfSyntaxParser());
5021 if (!isForDecl
&& !checkAndMarkAsAssignmentLhs(lhsNode
, PlainAssignment
))
5027 /* Parse the loop condition or null. */
5028 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
5030 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
5032 if (tt
!= TOK_SEMI
) {
5037 /* Parse the update expression or null. */
5038 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
5039 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
5047 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
5049 /* Parse the loop body. */
5053 PopStatementPC(tokenStream
, pc
);
5054 return SyntaxParseHandler::NodeGeneric
;
5057 template <typename ParseHandler
>
5058 typename
ParseHandler::Node
5059 Parser
<ParseHandler
>::switchStatement()
5061 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_SWITCH
));
5062 uint32_t begin
= pos().begin
;
5064 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
5066 Node discriminant
= exprInParens();
5070 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
5071 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
5073 StmtInfoPC
stmtInfo(context
);
5074 PushStatementPC(pc
, &stmtInfo
, STMT_SWITCH
);
5076 if (!GenerateBlockId(tokenStream
, pc
, pc
->topStmt
->blockid
))
5079 Node caseList
= handler
.newStatementList(pc
->blockid(), pos());
5083 Node saveBlock
= pc
->blockNode
;
5084 pc
->blockNode
= caseList
;
5086 bool seenDefault
= false;
5089 if (!tokenStream
.getToken(&tt
))
5093 uint32_t caseBegin
= pos().begin
;
5099 report(ParseError
, false, null(), JSMSG_TOO_MANY_DEFAULTS
);
5103 caseExpr
= null(); // The default case has pn_left == nullptr.
5113 report(ParseError
, false, null(), JSMSG_BAD_SWITCH
);
5117 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
5119 Node body
= handler
.newStatementList(pc
->blockid(), pos());
5124 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
5126 if (tt
== TOK_RC
|| tt
== TOK_CASE
|| tt
== TOK_DEFAULT
)
5128 Node stmt
= statement();
5131 handler
.addList(body
, stmt
);
5134 // In ES6, lexical bindings canot be accessed until initialized. If
5135 // there was a 'let' declaration in the case we just parsed, remember
5136 // the slot starting at which new lexical bindings will be
5137 // assigned. Since lexical bindings from previous cases will not
5138 // dominate uses in the current case, any such uses will require a
5141 // Currently this is overly conservative; we could do better, but
5142 // declaring lexical bindings within switch cases without introducing
5143 // a new block is poor form and should be avoided.
5144 if (stmtInfo
.isBlockScope
)
5145 stmtInfo
.firstDominatingLexicalInCase
= stmtInfo
.staticBlock().numVariables();
5147 Node casepn
= handler
.newCaseOrDefault(caseBegin
, caseExpr
, body
);
5150 handler
.addList(caseList
, casepn
);
5154 * Handle the case where there was a let declaration in any case in
5155 * the switch body, but not within an inner block. If it replaced
5156 * pc->blockNode with a new block node then we must refresh caseList and
5157 * then restore pc->blockNode.
5159 if (pc
->blockNode
!= caseList
)
5160 caseList
= pc
->blockNode
;
5161 pc
->blockNode
= saveBlock
;
5163 PopStatementPC(tokenStream
, pc
);
5165 handler
.setEndPosition(caseList
, pos().end
);
5167 return handler
.newSwitchStatement(begin
, discriminant
, caseList
);
5170 template <typename ParseHandler
>
5171 typename
ParseHandler::Node
5172 Parser
<ParseHandler
>::continueStatement()
5174 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_CONTINUE
));
5175 uint32_t begin
= pos().begin
;
5177 RootedPropertyName
label(context
);
5178 if (!matchLabel(&label
))
5181 StmtInfoPC
* stmt
= pc
->topStmt
;
5183 for (StmtInfoPC
* stmt2
= nullptr; ; stmt
= stmt
->down
) {
5185 report(ParseError
, false, null(), JSMSG_LABEL_NOT_FOUND
);
5188 if (stmt
->type
== STMT_LABEL
) {
5189 if (stmt
->label
== label
) {
5190 if (!stmt2
|| !stmt2
->isLoop()) {
5191 report(ParseError
, false, null(), JSMSG_BAD_CONTINUE
);
5201 for (; ; stmt
= stmt
->down
) {
5203 report(ParseError
, false, null(), JSMSG_BAD_CONTINUE
);
5211 if (!MatchOrInsertSemicolon(tokenStream
))
5214 return handler
.newContinueStatement(label
, TokenPos(begin
, pos().end
));
5217 template <typename ParseHandler
>
5218 typename
ParseHandler::Node
5219 Parser
<ParseHandler
>::breakStatement()
5221 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_BREAK
));
5222 uint32_t begin
= pos().begin
;
5224 RootedPropertyName
label(context
);
5225 if (!matchLabel(&label
))
5227 StmtInfoPC
* stmt
= pc
->topStmt
;
5229 for (; ; stmt
= stmt
->down
) {
5231 report(ParseError
, false, null(), JSMSG_LABEL_NOT_FOUND
);
5234 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
)
5238 for (; ; stmt
= stmt
->down
) {
5240 report(ParseError
, false, null(), JSMSG_TOUGH_BREAK
);
5243 if (stmt
->isLoop() || stmt
->type
== STMT_SWITCH
)
5248 if (!MatchOrInsertSemicolon(tokenStream
))
5251 return handler
.newBreakStatement(label
, TokenPos(begin
, pos().end
));
5254 template <typename ParseHandler
>
5255 typename
ParseHandler::Node
5256 Parser
<ParseHandler
>::returnStatement()
5258 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_RETURN
));
5259 uint32_t begin
= pos().begin
;
5261 if (!pc
->sc
->isFunctionBox()) {
5262 report(ParseError
, false, null(), JSMSG_BAD_RETURN_OR_YIELD
, js_return_str
);
5266 // Parse an optional operand.
5268 // This is ugly, but we don't want to require a semicolon.
5271 if (!tokenStream
.peekTokenSameLine(&tt
, TokenStream::Operand
))
5279 pc
->funHasReturnVoid
= true;
5285 pc
->funHasReturnExpr
= true;
5289 if (!MatchOrInsertSemicolon(tokenStream
))
5292 Node genrval
= null();
5293 if (pc
->isStarGenerator()) {
5294 genrval
= newName(context
->names().dotGenRVal
);
5297 if (!noteNameUse(context
->names().dotGenRVal
, genrval
))
5299 if (!checkAndMarkAsAssignmentLhs(genrval
, PlainAssignment
))
5303 Node pn
= handler
.newReturnStatement(exprNode
, genrval
, TokenPos(begin
, pos().end
));
5307 if (pc
->isLegacyGenerator() && exprNode
) {
5308 /* Disallow "return v;" in legacy generators. */
5309 reportBadReturn(pn
, ParseError
, JSMSG_BAD_GENERATOR_RETURN
,
5310 JSMSG_BAD_ANON_GENERATOR_RETURN
);
5317 template <typename ParseHandler
>
5318 typename
ParseHandler::Node
5319 Parser
<ParseHandler
>::newYieldExpression(uint32_t begin
, typename
ParseHandler::Node expr
,
5322 Node generator
= newName(context
->names().dotGenerator
);
5325 if (!noteNameUse(context
->names().dotGenerator
, generator
))
5328 return handler
.newYieldStarExpression(begin
, expr
, generator
);
5329 return handler
.newYieldExpression(begin
, expr
, generator
);
5332 template <typename ParseHandler
>
5333 typename
ParseHandler::Node
5334 Parser
<ParseHandler
>::yieldExpression()
5336 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_YIELD
));
5337 uint32_t begin
= pos().begin
;
5339 switch (pc
->generatorKind()) {
5342 MOZ_ASSERT(pc
->sc
->isFunctionBox());
5344 pc
->lastYieldOffset
= begin
;
5347 ParseNodeKind kind
= PNK_YIELD
;
5349 if (!tokenStream
.peekTokenSameLine(&tt
, TokenStream::Operand
))
5352 // TOK_EOL is special; it implements the [no LineTerminator here]
5353 // quirk in the grammar.
5355 // The rest of these make up the complete set of tokens that can
5356 // appear after any of the places where AssignmentExpression is used
5357 // throughout the grammar. Conveniently, none of them can also be the
5358 // start an expression.
5370 kind
= PNK_YIELD_STAR
;
5371 tokenStream
.consumeKnownToken(TOK_MUL
);
5374 exprNode
= assignExpr();
5378 return newYieldExpression(begin
, exprNode
, kind
== PNK_YIELD_STAR
);
5382 // We are in code that has not seen a yield, but we are in JS 1.7 or
5383 // later. Try to transition to being a legacy generator.
5384 MOZ_ASSERT(tokenStream
.versionNumber() >= JSVERSION_1_7
);
5385 MOZ_ASSERT(pc
->lastYieldOffset
== ParseContext
<ParseHandler
>::NoYieldOffset
);
5387 if (!abortIfSyntaxParser())
5390 if (!pc
->sc
->isFunctionBox()) {
5391 report(ParseError
, false, null(), JSMSG_BAD_RETURN_OR_YIELD
, js_yield_str
);
5395 pc
->sc
->asFunctionBox()->setGeneratorKind(LegacyGenerator
);
5396 sawDeprecatedLegacyGenerator
= true;
5398 if (pc
->funHasReturnExpr
) {
5399 /* As in Python (see PEP-255), disallow return v; in generators. */
5400 reportBadReturn(null(), ParseError
, JSMSG_BAD_GENERATOR_RETURN
,
5401 JSMSG_BAD_ANON_GENERATOR_RETURN
);
5406 case LegacyGenerator
:
5408 // We are in a legacy generator: a function that has already seen a
5409 // yield, or in a legacy generator comprehension.
5410 MOZ_ASSERT(pc
->sc
->isFunctionBox());
5412 pc
->lastYieldOffset
= begin
;
5414 // Legacy generators do not require a value.
5417 if (!tokenStream
.peekTokenSameLine(&tt
, TokenStream::Operand
))
5432 exprNode
= assignExpr();
5437 return newYieldExpression(begin
, exprNode
);
5441 MOZ_CRASH("yieldExpr");
5446 Parser
<FullParseHandler
>::withStatement()
5448 // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements
5449 // in syntax-parse mode. See bug 892583.
5450 if (handler
.syntaxParser
) {
5451 handler
.disableSyntaxParser();
5452 abortedSyntaxParse
= true;
5456 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_WITH
));
5457 uint32_t begin
= pos().begin
;
5459 // In most cases, we want the constructs forbidden in strict mode code to be
5460 // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should
5461 // use reportStrictModeError. However, 'with' is the sole instance of a
5462 // construct that is forbidden in strict mode code, but doesn't even merit a
5463 // warning under JSOPTION_EXTRA_WARNINGS. See
5464 // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5465 if (pc
->sc
->strict
&& !report(ParseStrictError
, true, null(), JSMSG_STRICT_CODE_WITH
))
5468 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
5469 Node objectExpr
= exprInParens();
5472 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
5474 bool oldParsingWith
= pc
->parsingWith
;
5475 pc
->parsingWith
= true;
5477 StmtInfoPC
stmtInfo(context
);
5478 PushStatementPC(pc
, &stmtInfo
, STMT_WITH
);
5479 Rooted
<StaticWithObject
*> staticWith(context
, StaticWithObject::create(context
));
5482 staticWith
->initEnclosingNestedScopeFromParser(pc
->staticScope
);
5483 FinishPushNestedScope(pc
, &stmtInfo
, *staticWith
);
5485 Node innerBlock
= statement();
5489 PopStatementPC(tokenStream
, pc
);
5491 pc
->sc
->setBindingsAccessedDynamically();
5492 pc
->parsingWith
= oldParsingWith
;
5495 * Make sure to deoptimize lexical dependencies inside the |with|
5496 * to safely optimize binding globals (see bug 561923).
5498 for (AtomDefnRange r
= pc
->lexdeps
->all(); !r
.empty(); r
.popFront()) {
5499 DefinitionNode defn
= r
.front().value().get
<FullParseHandler
>();
5500 DefinitionNode lexdep
= handler
.resolve(defn
);
5501 if (!pc
->sc
->isDotVariable(lexdep
->name()))
5502 handler
.deoptimizeUsesWithin(lexdep
, TokenPos(begin
, pos().begin
));
5505 ObjectBox
* staticWithBox
= newObjectBox(staticWith
);
5508 return handler
.newWithStatement(begin
, objectExpr
, innerBlock
, staticWithBox
);
5512 SyntaxParseHandler::Node
5513 Parser
<SyntaxParseHandler
>::withStatement()
5515 JS_ALWAYS_FALSE(abortIfSyntaxParser());
5519 template <typename ParseHandler
>
5520 typename
ParseHandler::Node
5521 Parser
<ParseHandler
>::labeledStatement()
5523 uint32_t begin
= pos().begin
;
5524 RootedPropertyName
label(context
, tokenStream
.currentName());
5525 for (StmtInfoPC
* stmt
= pc
->topStmt
; stmt
; stmt
= stmt
->down
) {
5526 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
) {
5527 report(ParseError
, false, null(), JSMSG_DUPLICATE_LABEL
);
5532 tokenStream
.consumeKnownToken(TOK_COLON
);
5534 /* Push a label struct and parse the statement. */
5535 StmtInfoPC
stmtInfo(context
);
5536 PushStatementPC(pc
, &stmtInfo
, STMT_LABEL
);
5537 stmtInfo
.label
= label
;
5538 Node pn
= statement();
5542 /* Pop the label, set pn_expr, and return early. */
5543 PopStatementPC(tokenStream
, pc
);
5545 return handler
.newLabeledStatement(label
, pn
, begin
);
5548 template <typename ParseHandler
>
5549 typename
ParseHandler::Node
5550 Parser
<ParseHandler
>::throwStatement()
5552 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_THROW
));
5553 uint32_t begin
= pos().begin
;
5555 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5557 if (!tokenStream
.peekTokenSameLine(&tt
, TokenStream::Operand
))
5559 if (tt
== TOK_EOF
|| tt
== TOK_SEMI
|| tt
== TOK_RC
) {
5560 report(ParseError
, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW
);
5563 if (tt
== TOK_EOL
) {
5564 report(ParseError
, false, null(), JSMSG_LINE_BREAK_AFTER_THROW
);
5568 Node throwExpr
= expr();
5572 if (!MatchOrInsertSemicolon(tokenStream
))
5575 return handler
.newThrowStatement(throwExpr
, TokenPos(begin
, pos().end
));
5578 template <typename ParseHandler
>
5579 typename
ParseHandler::Node
5580 Parser
<ParseHandler
>::tryStatement()
5582 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_TRY
));
5583 uint32_t begin
= pos().begin
;
5586 * try nodes are ternary.
5587 * kid1 is the try statement
5588 * kid2 is the catch node list or null
5589 * kid3 is the finally statement
5591 * catch nodes are ternary.
5592 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5593 * kid2 is the catch guard or null if no guard
5594 * kid3 is the catch block
5596 * catch lvalue nodes are either:
5597 * TOK_NAME for a single identifier
5598 * TOK_RB or TOK_RC for a destructuring left-hand side
5600 * finally nodes are TOK_LC statement lists.
5603 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
5604 StmtInfoPC
stmtInfo(context
);
5605 if (!PushBlocklikeStatement(tokenStream
, &stmtInfo
, STMT_TRY
, pc
))
5607 Node innerBlock
= statements();
5610 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
5611 PopStatementPC(tokenStream
, pc
);
5613 bool hasUnconditionalCatch
= false;
5614 Node catchList
= null();
5616 if (!tokenStream
.getToken(&tt
))
5618 if (tt
== TOK_CATCH
) {
5619 catchList
= handler
.newList(PNK_CATCH
);
5625 BindData
<ParseHandler
> data(context
);
5627 /* Check for another catch after unconditional catch. */
5628 if (hasUnconditionalCatch
) {
5629 report(ParseError
, false, null(), JSMSG_CATCH_AFTER_GENERAL
);
5634 * Create a lexical scope node around the whole catch clause,
5635 * including the head.
5637 pnblock
= pushLexicalScope(&stmtInfo
);
5640 stmtInfo
.type
= STMT_CATCH
;
5643 * Legal catch forms are:
5645 * catch (lhs if <boolean_expression>)
5646 * where lhs is a name or a destructuring left-hand side.
5647 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5649 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
5652 * Contrary to ECMA Ed. 3, the catch variable is lexically
5653 * scoped, not a property of a new Object instance. This is
5654 * an intentional change that anticipates ECMA Ed. 4.
5656 data
.initLexical(HoistVars
, &pc
->staticScope
->template as
<StaticBlockObject
>(),
5657 JSMSG_TOO_MANY_CATCH_VARS
);
5658 MOZ_ASSERT(data
.let
.blockObj
);
5660 if (!tokenStream
.getToken(&tt
))
5666 catchName
= destructuringExpr(&data
, tt
);
5672 if (!checkYieldNameValidity())
5677 RootedPropertyName
label(context
, tokenStream
.currentName());
5678 catchName
= newBindingNode(label
, false);
5681 data
.pn
= catchName
;
5682 if (!data
.binder(&data
, label
, this))
5688 report(ParseError
, false, null(), JSMSG_CATCH_IDENTIFIER
);
5692 Node catchGuard
= null();
5693 #if JS_HAS_CATCH_GUARD
5695 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5696 * to avoid conflicting with the JS2/ECMAv4 type annotation
5697 * catchguard syntax.
5700 if (!tokenStream
.matchToken(&matched
, TOK_IF
))
5703 catchGuard
= expr();
5708 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
5710 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
5711 Node catchBody
= statements();
5714 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
5715 PopStatementPC(tokenStream
, pc
);
5718 hasUnconditionalCatch
= true;
5720 if (!handler
.addCatchBlock(catchList
, pnblock
, catchName
, catchGuard
, catchBody
))
5722 handler
.setEndPosition(catchList
, pos().end
);
5723 handler
.setEndPosition(pnblock
, pos().end
);
5725 if (!tokenStream
.getToken(&tt
, TokenStream::Operand
))
5727 } while (tt
== TOK_CATCH
);
5730 Node finallyBlock
= null();
5732 if (tt
== TOK_FINALLY
) {
5733 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
5734 if (!PushBlocklikeStatement(tokenStream
, &stmtInfo
, STMT_FINALLY
, pc
))
5736 finallyBlock
= statements();
5739 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
5740 PopStatementPC(tokenStream
, pc
);
5742 tokenStream
.ungetToken();
5744 if (!catchList
&& !finallyBlock
) {
5745 report(ParseError
, false, null(), JSMSG_CATCH_OR_FINALLY
);
5749 return handler
.newTryStatement(begin
, innerBlock
, catchList
, finallyBlock
);
5752 template <typename ParseHandler
>
5753 typename
ParseHandler::Node
5754 Parser
<ParseHandler
>::debuggerStatement()
5757 p
.begin
= pos().begin
;
5758 if (!MatchOrInsertSemicolon(tokenStream
))
5762 pc
->sc
->setBindingsAccessedDynamically();
5763 pc
->sc
->setHasDebuggerStatement();
5765 return handler
.newDebuggerStatement(p
);
5768 template <typename ParseHandler
>
5769 typename
ParseHandler::Node
5770 Parser
<ParseHandler
>::statement(bool canHaveDirectives
)
5772 MOZ_ASSERT(checkOptionsCalled
);
5774 JS_CHECK_RECURSION(context
, return null());
5777 if (!tokenStream
.getToken(&tt
, TokenStream::Operand
))
5781 return blockStatement();
5784 if (!abortIfSyntaxParser())
5786 return lexicalDeclaration(/* isConst = */ true);
5789 Node pn
= variables(PNK_VAR
);
5793 // Tell js_EmitTree to generate a final POP.
5794 handler
.setListFlag(pn
, PNX_POPVAR
);
5796 if (!MatchOrInsertSemicolon(tokenStream
))
5802 return letStatement();
5804 return importDeclaration();
5806 return exportDeclaration();
5808 return handler
.newEmptyStatement(pos());
5810 return ifStatement();
5812 return doWhileStatement();
5814 return whileStatement();
5816 return forStatement();
5818 return switchStatement();
5820 return continueStatement();
5822 return breakStatement();
5824 return returnStatement();
5826 return withStatement();
5828 return throwStatement();
5830 return tryStatement();
5832 return functionStmt();
5834 return debuggerStatement();
5836 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5838 report(ParseError
, false, null(), JSMSG_CATCH_WITHOUT_TRY
);
5842 report(ParseError
, false, null(), JSMSG_FINALLY_WITHOUT_TRY
);
5846 if (!canHaveDirectives
&& tokenStream
.currentToken().atom() == context
->names().useAsm
) {
5847 if (!abortIfSyntaxParser())
5849 if (!report(ParseWarning
, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL
))
5852 return expressionStatement();
5856 TokenStream::Modifier modifier
= yieldExpressionsSupported()
5857 ? TokenStream::Operand
5858 : TokenStream::None
;
5859 if (!tokenStream
.peekToken(&next
, modifier
))
5861 if (next
== TOK_COLON
) {
5862 if (!checkYieldNameValidity())
5864 return labeledStatement();
5866 return expressionStatement();
5871 if (!tokenStream
.peekToken(&next
))
5873 if (next
== TOK_COLON
)
5874 return labeledStatement();
5875 return expressionStatement();
5879 return expressionStatement();
5883 template <typename ParseHandler
>
5884 typename
ParseHandler::Node
5885 Parser
<ParseHandler
>::expr()
5887 Node pn
= assignExpr();
5892 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
5895 Node seq
= handler
.newList(PNK_COMMA
, pn
);
5899 if (handler
.isUnparenthesizedYield(pn
)) {
5900 report(ParseError
, false, pn
, JSMSG_BAD_GENERATOR_SYNTAX
, js_yield_str
);
5907 handler
.addList(seq
, pn
);
5909 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
5919 static const JSOp ParseNodeKindToJSOp
[] = {
5946 BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk
)
5948 MOZ_ASSERT(pnk
>= PNK_BINOP_FIRST
);
5949 MOZ_ASSERT(pnk
<= PNK_BINOP_LAST
);
5950 return ParseNodeKindToJSOp
[pnk
- PNK_BINOP_FIRST
];
5954 IsBinaryOpToken(TokenKind tok
, bool parsingForInit
)
5956 return tok
== TOK_IN
? !parsingForInit
: TokenKindIsBinaryOp(tok
);
5959 static ParseNodeKind
5960 BinaryOpTokenKindToParseNodeKind(TokenKind tok
)
5962 MOZ_ASSERT(TokenKindIsBinaryOp(tok
));
5963 return ParseNodeKind(PNK_BINOP_FIRST
+ (tok
- TOK_BINOP_FIRST
));
5966 static const int PrecedenceTable
[] = {
5972 6, /* PNK_STRICTEQ */
5974 6, /* PNK_STRICTNE */
5980 7, /* PNK_INSTANCEOF */
5992 static const int PRECEDENCE_CLASSES
= 10;
5995 Precedence(ParseNodeKind pnk
) {
5996 // Everything binds tighter than PNK_LIMIT, because we want to reduce all
5997 // nodes to a single node when we reach a token that is not another binary
5999 if (pnk
== PNK_LIMIT
)
6002 MOZ_ASSERT(pnk
>= PNK_BINOP_FIRST
);
6003 MOZ_ASSERT(pnk
<= PNK_BINOP_LAST
);
6004 return PrecedenceTable
[pnk
- PNK_BINOP_FIRST
];
6007 template <typename ParseHandler
>
6008 MOZ_ALWAYS_INLINE typename
ParseHandler::Node
6009 Parser
<ParseHandler
>::orExpr1()
6011 // Shift-reduce parser for the left-associative binary operator part of
6014 // Conceptually there's just one stack, a stack of pairs (lhs, op).
6015 // It's implemented using two separate arrays, though.
6016 Node nodeStack
[PRECEDENCE_CLASSES
];
6017 ParseNodeKind kindStack
[PRECEDENCE_CLASSES
];
6020 bool oldParsingForInit
= pc
->parsingForInit
;
6021 pc
->parsingForInit
= false;
6029 // If a binary operator follows, consume it and compute the
6030 // corresponding operator.
6032 if (!tokenStream
.getToken(&tok
))
6035 if (IsBinaryOpToken(tok
, oldParsingForInit
)) {
6036 pnk
= BinaryOpTokenKindToParseNodeKind(tok
);
6042 // If pnk has precedence less than or equal to another operator on the
6043 // stack, reduce. This combines nodes on the stack until we form the
6044 // actual lhs of pnk.
6046 // The >= in this condition works because all the operators in question
6047 // are left-associative; if any were not, the case where two operators
6048 // have equal precedence would need to be handled specially, and the
6049 // stack would need to be a Vector.
6050 while (depth
> 0 && Precedence(kindStack
[depth
- 1]) >= Precedence(pnk
)) {
6052 ParseNodeKind combiningPnk
= kindStack
[depth
];
6053 JSOp combiningOp
= BinaryOpParseNodeKindToJSOp(combiningPnk
);
6054 pn
= handler
.newBinaryOrAppend(combiningPnk
, nodeStack
[depth
], pn
, pc
, combiningOp
);
6059 if (pnk
== PNK_LIMIT
)
6062 nodeStack
[depth
] = pn
;
6063 kindStack
[depth
] = pnk
;
6065 MOZ_ASSERT(depth
<= PRECEDENCE_CLASSES
);
6068 MOZ_ASSERT(depth
== 0);
6069 pc
->parsingForInit
= oldParsingForInit
;
6073 template <typename ParseHandler
>
6074 MOZ_ALWAYS_INLINE typename
ParseHandler::Node
6075 Parser
<ParseHandler
>::condExpr1()
6077 Node condition
= orExpr1();
6078 if (!condition
|| !tokenStream
.isCurrentTokenType(TOK_HOOK
))
6082 * Always accept the 'in' operator in the middle clause of a ternary,
6083 * where it's unambiguous, even if we might be parsing the init of a
6086 bool oldParsingForInit
= pc
->parsingForInit
;
6087 pc
->parsingForInit
= false;
6088 Node thenExpr
= assignExpr();
6089 pc
->parsingForInit
= oldParsingForInit
;
6093 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
6095 Node elseExpr
= assignExpr();
6099 // Advance to the next token; the caller is responsible for interpreting it.
6101 if (!tokenStream
.getToken(&ignored
))
6103 return handler
.newConditional(condition
, thenExpr
, elseExpr
);
6108 Parser
<FullParseHandler
>::checkAndMarkAsAssignmentLhs(ParseNode
* pn
, AssignmentFlavor flavor
)
6110 switch (pn
->getKind()) {
6112 if (!checkStrictAssignment(pn
))
6114 if (flavor
== KeyedDestructuringAssignment
) {
6116 * We may be called on a name node that has already been
6117 * specialized, in the very weird "for (var [x] = i in o) ..."
6118 * case. See bug 558633.
6120 if (!(js_CodeSpec
[pn
->getOp()].format
& JOF_SET
))
6121 pn
->setOp(JSOP_SETNAME
);
6123 pn
->setOp(pn
->isOp(JSOP_GETLOCAL
) ? JSOP_SETLOCAL
: JSOP_SETNAME
);
6125 pn
->markAsAssigned();
6134 if (flavor
== CompoundAssignment
) {
6135 report(ParseError
, false, null(), JSMSG_BAD_DESTRUCT_ASS
);
6138 if (!checkDestructuring(nullptr, pn
))
6143 if (flavor
== KeyedDestructuringAssignment
) {
6144 report(ParseError
, false, pn
, JSMSG_BAD_DESTRUCT_TARGET
);
6147 if (!makeSetCall(pn
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
6152 unsigned errnum
= (flavor
== KeyedDestructuringAssignment
) ? JSMSG_BAD_DESTRUCT_TARGET
:
6153 JSMSG_BAD_LEFTSIDE_OF_ASS
;
6154 report(ParseError
, false, pn
, errnum
);
6162 Parser
<SyntaxParseHandler
>::checkAndMarkAsAssignmentLhs(Node pn
, AssignmentFlavor flavor
)
6164 /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
6165 if (pn
!= SyntaxParseHandler::NodeName
&&
6166 pn
!= SyntaxParseHandler::NodeGetProp
&&
6167 pn
!= SyntaxParseHandler::NodeLValue
)
6169 return abortIfSyntaxParser();
6171 return checkStrictAssignment(pn
);
6174 template <typename ParseHandler
>
6175 typename
ParseHandler::Node
6176 Parser
<ParseHandler
>::assignExpr()
6178 JS_CHECK_RECURSION(context
, return null());
6180 // It's very common at this point to have a "detectably simple" expression,
6181 // i.e. a name/number/string token followed by one of the following tokens
6182 // that obviously isn't part of an expression: , ; : ) ] }
6184 // (In Parsemark this happens 81.4% of the time; in code with large
6185 // numeric arrays, such as some Kraken benchmarks, it happens more often.)
6187 // In such cases, we can avoid the full expression parsing route through
6188 // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
6192 if (!tokenStream
.getToken(&tt
, TokenStream::Operand
))
6197 if (tt
== TOK_NAME
) {
6198 if (!tokenStream
.nextTokenEndsExpr(&endsExpr
))
6201 return identifierName();
6204 if (tt
== TOK_NUMBER
) {
6205 if (!tokenStream
.nextTokenEndsExpr(&endsExpr
))
6208 return newNumber(tokenStream
.currentToken());
6211 if (tt
== TOK_STRING
) {
6212 if (!tokenStream
.nextTokenEndsExpr(&endsExpr
))
6215 return stringLiteral();
6218 if (tt
== TOK_YIELD
&& yieldExpressionsSupported())
6219 return yieldExpression();
6221 tokenStream
.ungetToken();
6223 // Save the tokenizer state in case we find an arrow function and have to
6225 TokenStream::Position
start(keepAtoms
);
6226 tokenStream
.tell(&start
);
6228 Node lhs
= condExpr1();
6234 switch (tokenStream
.currentToken().type
) {
6235 case TOK_ASSIGN
: kind
= PNK_ASSIGN
; op
= JSOP_NOP
; break;
6236 case TOK_ADDASSIGN
: kind
= PNK_ADDASSIGN
; op
= JSOP_ADD
; break;
6237 case TOK_SUBASSIGN
: kind
= PNK_SUBASSIGN
; op
= JSOP_SUB
; break;
6238 case TOK_BITORASSIGN
: kind
= PNK_BITORASSIGN
; op
= JSOP_BITOR
; break;
6239 case TOK_BITXORASSIGN
: kind
= PNK_BITXORASSIGN
; op
= JSOP_BITXOR
; break;
6240 case TOK_BITANDASSIGN
: kind
= PNK_BITANDASSIGN
; op
= JSOP_BITAND
; break;
6241 case TOK_LSHASSIGN
: kind
= PNK_LSHASSIGN
; op
= JSOP_LSH
; break;
6242 case TOK_RSHASSIGN
: kind
= PNK_RSHASSIGN
; op
= JSOP_RSH
; break;
6243 case TOK_URSHASSIGN
: kind
= PNK_URSHASSIGN
; op
= JSOP_URSH
; break;
6244 case TOK_MULASSIGN
: kind
= PNK_MULASSIGN
; op
= JSOP_MUL
; break;
6245 case TOK_DIVASSIGN
: kind
= PNK_DIVASSIGN
; op
= JSOP_DIV
; break;
6246 case TOK_MODASSIGN
: kind
= PNK_MODASSIGN
; op
= JSOP_MOD
; break;
6249 tokenStream
.seek(start
);
6250 if (!abortIfSyntaxParser())
6254 if (!tokenStream
.peekToken(&ignored
))
6257 return functionDef(NullPtr(), Normal
, Arrow
, NotGenerator
);
6261 MOZ_ASSERT(!tokenStream
.isCurrentTokenAssignment());
6262 tokenStream
.ungetToken();
6266 AssignmentFlavor flavor
= kind
== PNK_ASSIGN
? PlainAssignment
: CompoundAssignment
;
6267 if (!checkAndMarkAsAssignmentLhs(lhs
, flavor
))
6270 Node rhs
= assignExpr();
6274 return handler
.newBinaryOrAppend(kind
, lhs
, rhs
, pc
, op
);
6277 static const char incop_name_str
[][10] = {"increment", "decrement"};
6281 Parser
<FullParseHandler
>::checkAndMarkAsIncOperand(ParseNode
* kid
, TokenKind tt
, bool preorder
)
6284 if (!kid
->isKind(PNK_NAME
) &&
6285 !kid
->isKind(PNK_DOT
) &&
6286 !kid
->isKind(PNK_ELEM
) &&
6287 !(kid
->isKind(PNK_CALL
) &&
6288 (kid
->isOp(JSOP_CALL
) || kid
->isOp(JSOP_SPREADCALL
) ||
6289 kid
->isOp(JSOP_EVAL
) || kid
->isOp(JSOP_STRICTEVAL
) ||
6290 kid
->isOp(JSOP_SPREADEVAL
) || kid
->isOp(JSOP_STRICTSPREADEVAL
) ||
6291 kid
->isOp(JSOP_FUNCALL
) ||
6292 kid
->isOp(JSOP_FUNAPPLY
))))
6294 report(ParseError
, false, null(), JSMSG_BAD_OPERAND
, incop_name_str
[tt
== TOK_DEC
]);
6298 if (!checkStrictAssignment(kid
))
6302 if (kid
->isKind(PNK_NAME
)) {
6303 kid
->markAsAssigned();
6304 } else if (kid
->isKind(PNK_CALL
)) {
6305 if (!makeSetCall(kid
, JSMSG_BAD_INCOP_OPERAND
))
6313 Parser
<SyntaxParseHandler
>::checkAndMarkAsIncOperand(Node kid
, TokenKind tt
, bool preorder
)
6315 // To the extent of what we support in syntax-parse mode, the rules for
6316 // inc/dec operands are the same as for assignment. There are differences,
6317 // such as destructuring; but if we hit any of those cases, we'll abort and
6318 // reparse in full mode.
6319 return checkAndMarkAsAssignmentLhs(kid
, IncDecAssignment
);
6322 template <typename ParseHandler
>
6323 typename
ParseHandler::Node
6324 Parser
<ParseHandler
>::unaryOpExpr(ParseNodeKind kind
, JSOp op
, uint32_t begin
)
6326 Node kid
= unaryExpr();
6329 return handler
.newUnary(kind
, op
, begin
, kid
);
6332 template <typename ParseHandler
>
6333 typename
ParseHandler::Node
6334 Parser
<ParseHandler
>::unaryExpr()
6338 JS_CHECK_RECURSION(context
, return null());
6341 if (!tokenStream
.getToken(&tt
, TokenStream::Operand
))
6343 uint32_t begin
= pos().begin
;
6346 return unaryOpExpr(PNK_TYPEOF
, JSOP_TYPEOF
, begin
);
6348 return unaryOpExpr(PNK_VOID
, JSOP_VOID
, begin
);
6350 return unaryOpExpr(PNK_NOT
, JSOP_NOT
, begin
);
6352 return unaryOpExpr(PNK_BITNOT
, JSOP_BITNOT
, begin
);
6354 return unaryOpExpr(PNK_POS
, JSOP_POS
, begin
);
6356 return unaryOpExpr(PNK_NEG
, JSOP_NEG
, begin
);
6362 if (!tokenStream
.getToken(&tt2
, TokenStream::Operand
))
6364 pn2
= memberExpr(tt2
, true);
6367 if (!checkAndMarkAsIncOperand(pn2
, tt
, true))
6369 return handler
.newUnary((tt
== TOK_INC
) ? PNK_PREINCREMENT
: PNK_PREDECREMENT
,
6376 Node expr
= unaryExpr();
6380 // Per spec, deleting any unary expression is valid -- it simply
6381 // returns true -- except for one case that is illegal in strict mode.
6382 if (handler
.isName(expr
)) {
6383 if (!report(ParseStrictError
, pc
->sc
->strict
, expr
, JSMSG_DEPRECATED_DELETE_OPERAND
))
6385 pc
->sc
->setBindingsAccessedDynamically();
6388 return handler
.newDelete(begin
, expr
);
6392 pn
= memberExpr(tt
, true);
6396 /* Don't look across a newline boundary for a postfix incop. */
6397 if (!tokenStream
.peekTokenSameLine(&tt
, TokenStream::Operand
))
6399 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
6400 tokenStream
.consumeKnownToken(tt
);
6401 if (!checkAndMarkAsIncOperand(pn
, tt
, false))
6403 return handler
.newUnary((tt
== TOK_INC
) ? PNK_POSTINCREMENT
: PNK_POSTDECREMENT
,
6413 * A dedicated helper for transplanting the legacy comprehension expression E in
6415 * [E for (V in I)] // legacy array comprehension
6416 * (E for (V in I)) // legacy generator expression
6418 * from its initial location in the AST, on the left of the 'for', to its final
6419 * position on the right. To avoid a separate pass we do this by adjusting the
6420 * blockids and name binding links that were established when E was parsed.
6422 * A legacy generator expression desugars like so:
6424 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6426 * so the transplanter must adjust static level as well as blockid. E's source
6427 * coordinates in root->pn_pos are critical to deciding which binding links to
6428 * preserve and which to cut.
6430 * NB: This is not a general tree transplanter -- it knows in particular that
6431 * the one or more bindings induced by V have not yet been created.
6433 class LegacyCompExprTransplanter
6436 Parser
<FullParseHandler
>* parser
;
6437 ParseContext
<FullParseHandler
>* outerpc
;
6438 GeneratorKind comprehensionKind
;
6440 HashSet
<Definition
*> visitedImplicitArguments
;
6443 LegacyCompExprTransplanter(ParseNode
* pn
, Parser
<FullParseHandler
>* parser
,
6444 ParseContext
<FullParseHandler
>* outerpc
,
6445 GeneratorKind kind
, unsigned adj
)
6446 : root(pn
), parser(parser
), outerpc(outerpc
), comprehensionKind(kind
), adjust(adj
),
6447 visitedImplicitArguments(parser
->context
)
6451 return visitedImplicitArguments
.init();
6454 bool transplant(ParseNode
* pn
);
6458 * Any definitions nested within the legacy comprehension expression of a
6459 * generator expression must move "down" one static level, which of course
6460 * increases the upvar-frame-skip count.
6462 template <typename ParseHandler
>
6464 BumpStaticLevel(TokenStream
& ts
, ParseNode
* pn
, ParseContext
<ParseHandler
>* pc
)
6466 if (pn
->pn_cookie
.isFree())
6469 unsigned level
= unsigned(pn
->pn_cookie
.level()) + 1;
6470 MOZ_ASSERT(level
>= pc
->staticLevel
);
6471 return pn
->pn_cookie
.set(ts
, level
, pn
->pn_cookie
.slot());
6474 template <typename ParseHandler
>
6476 AdjustBlockId(TokenStream
& ts
, ParseNode
* pn
, unsigned adjust
, ParseContext
<ParseHandler
>* pc
)
6478 MOZ_ASSERT(pn
->isArity(PN_LIST
) || pn
->isArity(PN_CODE
) || pn
->isArity(PN_NAME
));
6479 if (BlockIdLimit
- pn
->pn_blockid
<= adjust
+ 1) {
6480 ts
.reportError(JSMSG_NEED_DIET
, "program");
6483 pn
->pn_blockid
+= adjust
;
6484 if (pn
->pn_blockid
>= pc
->blockidGen
)
6485 pc
->blockidGen
= pn
->pn_blockid
+ 1;
6490 LegacyCompExprTransplanter::transplant(ParseNode
* pn
)
6492 ParseContext
<FullParseHandler
>* pc
= parser
->pc
;
6494 bool isGenexp
= comprehensionKind
!= NotGenerator
;
6499 switch (pn
->getArity()) {
6501 for (ParseNode
* pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
6502 if (!transplant(pn2
))
6505 if (pn
->pn_pos
>= root
->pn_pos
) {
6506 if (!AdjustBlockId(parser
->tokenStream
, pn
, adjust
, pc
))
6512 if (!transplant(pn
->pn_kid1
) ||
6513 !transplant(pn
->pn_kid2
) ||
6514 !transplant(pn
->pn_kid3
))
6520 if (!transplant(pn
->pn_left
))
6523 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6524 if (pn
->pn_right
!= pn
->pn_left
) {
6525 if (!transplant(pn
->pn_right
))
6531 if (!transplant(pn
->pn_kid
))
6537 if (!transplant(pn
->maybeExpr()))
6541 if (isGenexp
&& !BumpStaticLevel(parser
->tokenStream
, pn
, pc
))
6543 } else if (pn
->isUsed()) {
6544 MOZ_ASSERT(pn
->pn_cookie
.isFree());
6546 Definition
* dn
= pn
->pn_lexdef
;
6547 MOZ_ASSERT(dn
->isDefn());
6550 * Adjust the definition's block id only if it is a placeholder not
6551 * to the left of the root node, and if pn is the last use visited
6552 * in the legacy comprehension expression (to avoid adjusting the
6553 * blockid multiple times).
6555 * Non-placeholder definitions within the legacy comprehension
6556 * expression will be visited further below.
6558 if (dn
->isPlaceholder() && dn
->pn_pos
>= root
->pn_pos
&& dn
->dn_uses
== pn
) {
6559 if (isGenexp
&& !BumpStaticLevel(parser
->tokenStream
, dn
, pc
))
6561 if (!AdjustBlockId(parser
->tokenStream
, dn
, adjust
, pc
))
6565 RootedAtom
atom(parser
->context
, pn
->pn_atom
);
6567 StmtInfoPC
* stmt
= LexicalLookup(pc
, atom
, nullptr, (StmtInfoPC
*)nullptr);
6568 MOZ_ASSERT(!stmt
|| stmt
!= pc
->topStmt
);
6570 if (isGenexp
&& !dn
->isOp(JSOP_CALLEE
)) {
6571 MOZ_ASSERT_IF(!pc
->sc
->isDotVariable(atom
), !pc
->decls().lookupFirst(atom
));
6573 if (pc
->sc
->isDotVariable(atom
)) {
6574 if (dn
->dn_uses
== pn
) {
6575 if (!BumpStaticLevel(parser
->tokenStream
, dn
, pc
))
6577 if (!AdjustBlockId(parser
->tokenStream
, dn
, adjust
, pc
))
6580 } else if (dn
->pn_pos
< root
->pn_pos
) {
6582 * The variable originally appeared to be a use of a
6583 * definition or placeholder outside the generator, but now
6584 * we know it is scoped within the legacy comprehension
6585 * tail's clauses. Make it (along with any other uses within
6586 * the generator) a use of a new placeholder in the
6587 * generator's lexdeps.
6589 Definition
* dn2
= parser
->handler
.newPlaceholder(atom
, parser
->pc
->blockid(),
6593 dn2
->pn_pos
= root
->pn_pos
;
6596 * Change all uses of |dn| that lie within the generator's
6597 * |yield| expression into uses of dn2.
6599 ParseNode
** pnup
= &dn
->dn_uses
;
6601 while ((pnu
= *pnup
) != nullptr && pnu
->pn_pos
>= root
->pn_pos
) {
6602 pnu
->pn_lexdef
= dn2
;
6603 dn2
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
6604 pnup
= &pnu
->pn_link
;
6606 dn2
->dn_uses
= dn
->dn_uses
;
6607 dn
->dn_uses
= *pnup
;
6609 DefinitionSingle def
= DefinitionSingle::new_
<FullParseHandler
>(dn2
);
6610 if (!pc
->lexdeps
->put(atom
, def
))
6613 dn2
->pn_dflags
|= PND_CLOSED
;
6614 } else if (dn
->isPlaceholder()) {
6616 * The variable first occurs free in the 'yield' expression;
6617 * move the existing placeholder node (and all its uses)
6618 * from the parent's lexdeps into the generator's lexdeps.
6620 outerpc
->lexdeps
->remove(atom
);
6621 DefinitionSingle def
= DefinitionSingle::new_
<FullParseHandler
>(dn
);
6622 if (!pc
->lexdeps
->put(atom
, def
))
6624 } else if (dn
->isImplicitArguments()) {
6626 * Implicit 'arguments' Definition nodes (see
6627 * PND_IMPLICITARGUMENTS in Parser::functionBody) are only
6628 * reachable via the lexdefs of their uses. Unfortunately,
6629 * there may be multiple uses, so we need to maintain a set
6630 * to only bump the definition once.
6632 if (isGenexp
&& !visitedImplicitArguments
.has(dn
)) {
6633 if (!BumpStaticLevel(parser
->tokenStream
, dn
, pc
))
6635 if (!AdjustBlockId(parser
->tokenStream
, dn
, adjust
, pc
))
6637 if (!visitedImplicitArguments
.put(dn
))
6644 if (pn
->pn_pos
>= root
->pn_pos
) {
6645 if (!AdjustBlockId(parser
->tokenStream
, pn
, adjust
, pc
))
6657 // Parsing legacy (JS1.7-style) comprehensions is terrible: we parse the head
6658 // expression as if it's part of a comma expression, then when we see the "for"
6659 // we transplant the parsed expression into the inside of a constructed
6660 // for-of/for-in/for-each tail. Transplanting an already-parsed expression is
6661 // tricky, but the LegacyCompExprTransplanter handles most of that.
6663 // The one remaining thing to patch up is the block scope depth. We need to
6664 // compute the maximum block scope depth of a function, so we know how much
6665 // space to reserve in the fixed part of a stack frame. Normally this is done
6666 // whenever we leave a statement, via AccumulateBlockScopeDepth. However if the
6667 // head has a let expression, we need to re-assign that depth to the tail of the
6670 // Thing is, we don't actually know what that depth is, because the only
6671 // information we keep is the maximum nested depth within a statement, so we
6672 // just conservatively propagate the maximum nested depth from the top statement
6673 // to the comprehension tail.
6675 template <typename ParseHandler
>
6677 LegacyComprehensionHeadBlockScopeDepth(ParseContext
<ParseHandler
>* pc
)
6679 return pc
->topStmt
? pc
->topStmt
->innerBlockScopeDepth
: pc
->blockScopeDepth
;
6683 * Starting from a |for| keyword after the first array initialiser element or
6684 * an expression in an open parenthesis, parse the tail of the comprehension
6685 * or generator expression signified by this |for| keyword in context.
6687 * Return null on failure, else return the top-most parse node for the array
6688 * comprehension or generator expression, with a unary node as the body of the
6689 * (possibly nested) for-loop, initialized by |kind, op, kid|.
6693 Parser
<FullParseHandler
>::legacyComprehensionTail(ParseNode
* bodyExpr
, unsigned blockid
,
6694 GeneratorKind comprehensionKind
,
6695 ParseContext
<FullParseHandler
>* outerpc
,
6696 unsigned innerBlockScopeDepth
)
6699 * If we saw any inner functions while processing the generator expression
6700 * then they may have upvars referring to the let vars in this generator
6701 * which were not correctly processed. Bail out and start over without
6702 * allowing lazy parsing.
6704 if (handler
.syntaxParser
) {
6705 handler
.disableSyntaxParser();
6706 abortedSyntaxParse
= true;
6711 ParseNode
* pn
, *pn2
, *pn3
, **pnp
;
6712 StmtInfoPC
stmtInfo(context
);
6713 BindData
<FullParseHandler
> data(context
);
6716 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FOR
));
6718 bool isGenexp
= comprehensionKind
!= NotGenerator
;
6721 MOZ_ASSERT(comprehensionKind
== LegacyGenerator
);
6723 * Generator expression desugars to an immediately applied lambda that
6724 * yields the next value from a for-in loop (possibly nested, and with
6725 * optional if guard). Make pn be the TOK_LC body node.
6727 pn
= pushLexicalScope(&stmtInfo
);
6730 adjust
= pn
->pn_blockid
- blockid
;
6733 * Make a parse-node and literal object representing the block scope of
6734 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
6735 * aka the array initialiser case, has passed the blockid to claim for
6736 * the comprehension's block scope. We allocate that id or one above it
6737 * here, by calling PushLexicalScope.
6739 * In the case of a comprehension expression that has nested blocks
6740 * (e.g., let expressions), we will allocate a higher blockid but then
6741 * slide all blocks "to the right" to make room for the comprehension's
6744 adjust
= pc
->blockid();
6745 pn
= pushLexicalScope(&stmtInfo
);
6749 MOZ_ASSERT(blockid
<= pn
->pn_blockid
);
6750 MOZ_ASSERT(blockid
< pc
->blockidGen
);
6751 MOZ_ASSERT(pc
->bodyid
< blockid
);
6752 pn
->pn_blockid
= stmtInfo
.blockid
= blockid
;
6753 MOZ_ASSERT(adjust
< blockid
);
6754 adjust
= blockid
- adjust
;
6757 handler
.setBeginPosition(pn
, bodyExpr
);
6761 LegacyCompExprTransplanter
transplanter(bodyExpr
, this, outerpc
, comprehensionKind
, adjust
);
6762 if (!transplanter
.init())
6765 if (!transplanter
.transplant(bodyExpr
))
6768 MOZ_ASSERT(pc
->staticScope
&& pc
->staticScope
== pn
->pn_objbox
->object
);
6769 data
.initLexical(HoistVars
, &pc
->staticScope
->as
<StaticBlockObject
>(),
6770 JSMSG_ARRAY_INIT_TOO_BIG
);
6774 * FOR node is binary, left is loop control and right is body. Use
6775 * index to count each block-local let-variable on the left-hand side
6778 pn2
= BinaryNode::create(PNK_FOR
, &handler
);
6782 pn2
->setOp(JSOP_ITER
);
6783 pn2
->pn_iflags
= JSITER_ENUMERATE
;
6784 if (allowsForEachIn()) {
6786 if (!tokenStream
.matchContextualKeyword(&matched
, context
->names().each
))
6789 pn2
->pn_iflags
|= JSITER_FOREACH
;
6790 sawDeprecatedForEach
= true;
6791 if (versionNumber() < JSVERSION_LATEST
) {
6792 if (!report(ParseWarning
, pc
->sc
->strict
, pn2
, JSMSG_DEPRECATED_FOR_EACH
))
6797 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
6799 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
6801 RootedPropertyName
name(context
);
6802 if (!tokenStream
.getToken(&tt
))
6807 pc
->inDeclDestructuring
= true;
6808 pn3
= primaryExpr(tt
);
6809 pc
->inDeclDestructuring
= false;
6815 name
= tokenStream
.currentName();
6818 * Create a name node with pn_op JSOP_GETNAME. We can't set pn_op to
6819 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6820 * in the operand stack frame. The code generator computes that,
6821 * and it tries to bind all names to slots, so we must let it do
6824 pn3
= newBindingNode(name
, false);
6830 report(ParseError
, false, null(), JSMSG_NO_VARIABLE_NAME
);
6834 bool isForIn
, isForOf
;
6835 if (!matchInOrOf(&isForIn
, &isForOf
))
6837 if (!isForIn
&& !isForOf
) {
6838 report(ParseError
, false, null(), JSMSG_IN_AFTER_FOR_NAME
);
6841 ParseNodeKind headKind
= PNK_FORIN
;
6843 if (pn2
->pn_iflags
!= JSITER_ENUMERATE
) {
6844 MOZ_ASSERT(pn2
->pn_iflags
== (JSITER_FOREACH
| JSITER_ENUMERATE
));
6845 report(ParseError
, false, null(), JSMSG_BAD_FOR_EACH_LOOP
);
6849 headKind
= PNK_FOROF
;
6852 ParseNode
* pn4
= expr();
6855 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
6857 if (isGenexp
&& pc
->lastYieldOffset
!= startYieldOffset
) {
6858 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
6859 JSMSG_BAD_GENEXP_BODY
, js_yield_str
);
6866 if (!checkDestructuring(&data
, pn3
))
6869 if (versionNumber() == JSVERSION_1_7
&&
6870 !(pn2
->pn_iflags
& JSITER_FOREACH
) &&
6873 /* Destructuring requires [key, value] enumeration in JS1.7. */
6874 if (!pn3
->isKind(PNK_ARRAY
) || pn3
->pn_count
!= 2) {
6875 report(ParseError
, false, null(), JSMSG_BAD_FOR_LEFTSIDE
);
6879 MOZ_ASSERT(pn2
->isOp(JSOP_ITER
));
6880 MOZ_ASSERT(pn2
->pn_iflags
& JSITER_ENUMERATE
);
6881 MOZ_ASSERT(headKind
== PNK_FORIN
);
6882 pn2
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
6888 if (!data
.binder(&data
, name
, this))
6896 * Synthesize a declaration. Every definition must appear in the parse
6897 * tree in order for ComprehensionTranslator to work.
6899 * These are lets to tell the bytecode emitter to emit initialization
6900 * code for the temporal dead zone.
6902 ParseNode
* lets
= ListNode::create(PNK_LET
, &handler
);
6905 lets
->setOp(JSOP_NOP
);
6906 lets
->pn_pos
= pn3
->pn_pos
;
6909 lets
->pn_xflags
|= PNX_POPVAR
;
6911 /* Definitions can't be passed directly to EmitAssignment as lhs. */
6912 pn3
= cloneLeftHandSide(pn3
);
6916 pn2
->pn_left
= handler
.newTernary(headKind
, lets
, pn3
, pn4
);
6920 pnp
= &pn2
->pn_right
;
6923 if (!tokenStream
.matchToken(&matched
, TOK_FOR
))
6930 if (!tokenStream
.matchToken(&matched
, TOK_IF
))
6933 pn2
= TernaryNode::create(PNK_IF
, &handler
);
6936 pn2
->pn_kid1
= condition();
6940 pnp
= &pn2
->pn_kid2
;
6943 ParseNode
* bodyStmt
;
6945 ParseNode
* yieldExpr
= newYieldExpression(bodyExpr
->pn_pos
.begin
, bodyExpr
);
6948 yieldExpr
->setInParens(true);
6950 bodyStmt
= handler
.newExprStatement(yieldExpr
, bodyExpr
->pn_pos
.end
);
6954 bodyStmt
= handler
.newUnary(PNK_ARRAYPUSH
, JSOP_ARRAYPUSH
,
6955 bodyExpr
->pn_pos
.begin
, bodyExpr
);
6962 pc
->topStmt
->innerBlockScopeDepth
+= innerBlockScopeDepth
;
6963 PopStatementPC(tokenStream
, pc
);
6965 handler
.setEndPosition(pn
, pos().end
);
6971 SyntaxParseHandler::Node
6972 Parser
<SyntaxParseHandler
>::legacyComprehensionTail(SyntaxParseHandler::Node bodyStmt
,
6974 GeneratorKind comprehensionKind
,
6975 ParseContext
<SyntaxParseHandler
>* outerpc
,
6976 unsigned innerBlockScopeDepth
)
6978 abortIfSyntaxParser();
6984 Parser
<FullParseHandler
>::legacyArrayComprehension(ParseNode
* array
)
6986 array
->setKind(PNK_ARRAYCOMP
);
6988 // Remove the single element from array's linked list, leaving us with an
6989 // empty array literal and a comprehension expression.
6990 MOZ_ASSERT(array
->pn_count
== 1);
6991 ParseNode
* bodyExpr
= array
->last();
6992 array
->pn_count
= 0;
6993 array
->pn_tail
= &array
->pn_head
;
6994 *array
->pn_tail
= nullptr;
6996 ParseNode
* comp
= legacyComprehensionTail(bodyExpr
, array
->pn_blockid
, NotGenerator
,
6997 nullptr, LegacyComprehensionHeadBlockScopeDepth(pc
));
7001 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION
);
7003 TokenPos p
= handler
.getPosition(array
);
7005 return handler
.newArrayComprehension(comp
, array
->pn_blockid
, p
);
7009 SyntaxParseHandler::Node
7010 Parser
<SyntaxParseHandler
>::legacyArrayComprehension(Node array
)
7012 abortIfSyntaxParser();
7016 template <typename ParseHandler
>
7017 typename
ParseHandler::Node
7018 Parser
<ParseHandler
>::generatorComprehensionLambda(GeneratorKind comprehensionKind
,
7019 unsigned begin
, Node innerExpr
)
7021 MOZ_ASSERT(comprehensionKind
== LegacyGenerator
|| comprehensionKind
== StarGenerator
);
7022 MOZ_ASSERT(!!innerExpr
== (comprehensionKind
== LegacyGenerator
));
7024 Node genfn
= handler
.newFunctionDefinition();
7027 handler
.setOp(genfn
, JSOP_LAMBDA
);
7029 ParseContext
<ParseHandler
>* outerpc
= pc
;
7031 // If we are off the main thread, the generator meta-objects have
7032 // already been created by js::StartOffThreadParseScript, so cx will not
7034 RootedObject
proto(context
);
7035 if (comprehensionKind
== StarGenerator
) {
7036 JSContext
* cx
= context
->maybeJSContext();
7037 proto
= GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx
, context
->global());
7042 RootedFunction
fun(context
, newFunction(outerpc
, /* atom = */ NullPtr(), Expression
, proto
));
7046 // Create box for fun->object early to root it.
7047 Directives
directives(/* strict = */ outerpc
->sc
->strict
);
7048 FunctionBox
* genFunbox
= newFunctionBox(genfn
, fun
, outerpc
, directives
, comprehensionKind
);
7052 ParseContext
<ParseHandler
> genpc(this, outerpc
, genfn
, genFunbox
,
7053 /* newDirectives = */ nullptr,
7054 outerpc
->staticLevel
+ 1, outerpc
->blockidGen
,
7055 /* blockScopeDepth = */ 0);
7056 if (!genpc
.init(tokenStream
))
7060 * We assume conservatively that any deoptimization flags in pc->sc
7061 * come from the kid. So we propagate these flags into genfn. For code
7062 * simplicity we also do not detect if the flags were only set in the
7063 * kid and could be removed from pc->sc.
7065 genFunbox
->anyCxFlags
= outerpc
->sc
->anyCxFlags
;
7066 if (outerpc
->sc
->isFunctionBox())
7067 genFunbox
->funCxFlags
= outerpc
->sc
->asFunctionBox()->funCxFlags
;
7069 MOZ_ASSERT(genFunbox
->generatorKind() == comprehensionKind
);
7070 genFunbox
->inGenexpLambda
= true;
7071 handler
.setBlockId(genfn
, genpc
.bodyid
);
7073 Node generator
= newName(context
->names().dotGenerator
);
7076 if (!pc
->define(tokenStream
, context
->names().dotGenerator
, generator
, Definition::VAR
))
7079 Node body
= handler
.newStatementList(pc
->blockid(), TokenPos(begin
, pos().end
));
7084 if (comprehensionKind
== StarGenerator
) {
7085 comp
= comprehension(StarGenerator
);
7089 MOZ_ASSERT(comprehensionKind
== LegacyGenerator
);
7090 comp
= legacyComprehensionTail(innerExpr
, outerpc
->blockid(), LegacyGenerator
,
7091 outerpc
, LegacyComprehensionHeadBlockScopeDepth(outerpc
));
7096 if (comprehensionKind
== StarGenerator
)
7097 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
7099 handler
.setBeginPosition(comp
, begin
);
7100 handler
.setEndPosition(comp
, pos().end
);
7101 handler
.addStatementToList(body
, comp
, pc
);
7102 handler
.setEndPosition(body
, pos().end
);
7103 handler
.setBeginPosition(genfn
, begin
);
7104 handler
.setEndPosition(genfn
, pos().end
);
7106 generator
= newName(context
->names().dotGenerator
);
7109 if (!noteNameUse(context
->names().dotGenerator
, generator
))
7111 if (!handler
.prependInitialYield(body
, generator
))
7114 // Note that if we ever start syntax-parsing generators, we will also
7115 // need to propagate the closed-over variable set to the inner
7116 // lazyscript, as in finishFunctionDefinition.
7117 handler
.setFunctionBody(genfn
, body
);
7119 PropagateTransitiveParseFlags(genFunbox
, outerpc
->sc
);
7121 if (!leaveFunction(genfn
, outerpc
))
7127 #if JS_HAS_GENERATOR_EXPRS
7130 * Starting from a |for| keyword after an expression, parse the comprehension
7131 * tail completing this generator expression. Wrap the expression at kid in a
7132 * generator function that is immediately called to evaluate to the generator
7133 * iterator that is the value of this legacy generator expression.
7135 * |kid| must be the expression before the |for| keyword; we return an
7136 * application of a generator function that includes the |for| loops and
7137 * |if| guards, with |kid| as the operand of a |yield| expression as the
7138 * innermost loop body.
7140 * Note how unlike Python, we do not evaluate the expression to the right of
7141 * the first |in| in the chain of |for| heads. Instead, a generator expression
7142 * is merely sugar for a generator function expression and its application.
7146 Parser
<FullParseHandler
>::legacyGeneratorExpr(ParseNode
* expr
)
7148 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FOR
));
7150 /* Make a new node for the desugared generator function. */
7151 ParseNode
* genfn
= generatorComprehensionLambda(LegacyGenerator
, expr
->pn_pos
.begin
, expr
);
7156 * Our result is a call expression that invokes the anonymous generator
7159 ParseNode
* result
= ListNode::create(PNK_GENEXP
, &handler
);
7162 result
->setOp(JSOP_CALL
);
7163 result
->pn_pos
.begin
= genfn
->pn_pos
.begin
;
7164 result
->initList(genfn
);
7169 SyntaxParseHandler::Node
7170 Parser
<SyntaxParseHandler
>::legacyGeneratorExpr(Node kid
)
7172 JS_ALWAYS_FALSE(abortIfSyntaxParser());
7173 return SyntaxParseHandler::NodeFailure
;
7176 static const char js_generator_str
[] = "generator";
7178 #endif /* JS_HAS_GENERATOR_EXPRS */
7180 template <typename ParseHandler
>
7181 typename
ParseHandler::Node
7182 Parser
<ParseHandler
>::comprehensionFor(GeneratorKind comprehensionKind
)
7184 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FOR
));
7186 uint32_t begin
= pos().begin
;
7188 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
7190 // FIXME: Destructuring binding (bug 980828).
7192 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NO_VARIABLE_NAME
);
7193 RootedPropertyName
name(context
, tokenStream
.currentName());
7194 if (name
== context
->names().let
) {
7195 report(ParseError
, false, null(), JSMSG_LET_COMP_BINDING
);
7199 if (!tokenStream
.matchContextualKeyword(&matched
, context
->names().of
))
7202 report(ParseError
, false, null(), JSMSG_OF_AFTER_FOR_NAME
);
7206 Node rhs
= assignExpr();
7210 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE
);
7212 TokenPos
headPos(begin
, pos().end
);
7214 StmtInfoPC
stmtInfo(context
);
7215 BindData
<ParseHandler
> data(context
);
7216 RootedStaticBlockObject
blockObj(context
, StaticBlockObject::create(context
));
7219 data
.initLexical(DontHoistVars
, blockObj
, JSMSG_TOO_MANY_LOCALS
);
7220 Node lhs
= newName(name
);
7223 Node decls
= handler
.newList(PNK_LET
, lhs
, JSOP_NOP
);
7227 if (!data
.binder(&data
, name
, this))
7229 Node letScope
= pushLetScope(blockObj
, &stmtInfo
);
7232 handler
.setLexicalScopeBody(letScope
, decls
);
7234 Node assignLhs
= newName(name
);
7237 if (!noteNameUse(name
, assignLhs
))
7239 handler
.setOp(assignLhs
, JSOP_SETNAME
);
7241 Node head
= handler
.newForHead(PNK_FOROF
, letScope
, assignLhs
, rhs
, headPos
);
7245 Node tail
= comprehensionTail(comprehensionKind
);
7249 PopStatementPC(tokenStream
, pc
);
7251 return handler
.newForStatement(begin
, head
, tail
, JSOP_ITER
);
7254 template <typename ParseHandler
>
7255 typename
ParseHandler::Node
7256 Parser
<ParseHandler
>::comprehensionIf(GeneratorKind comprehensionKind
)
7258 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_IF
));
7260 uint32_t begin
= pos().begin
;
7262 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
7263 Node cond
= assignExpr();
7266 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
7268 /* Check for (a = b) and warn about possible (a == b) mistype. */
7269 if (handler
.isOperationWithoutParens(cond
, PNK_ASSIGN
) &&
7270 !report(ParseExtraWarning
, false, null(), JSMSG_EQUAL_AS_ASSIGN
))
7275 Node then
= comprehensionTail(comprehensionKind
);
7279 return handler
.newIfStatement(begin
, cond
, then
, null());
7282 template <typename ParseHandler
>
7283 typename
ParseHandler::Node
7284 Parser
<ParseHandler
>::comprehensionTail(GeneratorKind comprehensionKind
)
7286 JS_CHECK_RECURSION(context
, return null());
7289 if (!tokenStream
.matchToken(&matched
, TOK_FOR
, TokenStream::Operand
))
7292 return comprehensionFor(comprehensionKind
);
7294 if (!tokenStream
.matchToken(&matched
, TOK_IF
, TokenStream::Operand
))
7297 return comprehensionIf(comprehensionKind
);
7299 uint32_t begin
= pos().begin
;
7301 Node bodyExpr
= assignExpr();
7305 if (comprehensionKind
== NotGenerator
)
7306 return handler
.newUnary(PNK_ARRAYPUSH
, JSOP_ARRAYPUSH
, begin
, bodyExpr
);
7308 MOZ_ASSERT(comprehensionKind
== StarGenerator
);
7309 Node yieldExpr
= newYieldExpression(begin
, bodyExpr
);
7312 handler
.setInParens(yieldExpr
);
7314 return handler
.newExprStatement(yieldExpr
, pos().end
);
7317 // Parse an ES6 generator or array comprehension, starting at the first 'for'.
7318 // The caller is responsible for matching the ending TOK_RP or TOK_RB.
7319 template <typename ParseHandler
>
7320 typename
ParseHandler::Node
7321 Parser
<ParseHandler
>::comprehension(GeneratorKind comprehensionKind
)
7323 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FOR
));
7325 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
7327 Node body
= comprehensionFor(comprehensionKind
);
7331 if (comprehensionKind
!= NotGenerator
&& pc
->lastYieldOffset
!= startYieldOffset
) {
7332 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
7333 JSMSG_BAD_GENEXP_BODY
, js_yield_str
);
7340 template <typename ParseHandler
>
7341 typename
ParseHandler::Node
7342 Parser
<ParseHandler
>::arrayComprehension(uint32_t begin
)
7344 Node inner
= comprehension(NotGenerator
);
7348 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION
);
7350 Node comp
= handler
.newList(PNK_ARRAYCOMP
, inner
);
7354 handler
.setBeginPosition(comp
, begin
);
7355 handler
.setEndPosition(comp
, pos().end
);
7360 template <typename ParseHandler
>
7361 typename
ParseHandler::Node
7362 Parser
<ParseHandler
>::generatorComprehension(uint32_t begin
)
7364 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_FOR
));
7366 // We have no problem parsing generator comprehensions inside lazy
7367 // functions, but the bytecode emitter currently can't handle them that way,
7368 // because when it goes to emit the code for the inner generator function,
7369 // it expects outer functions to have non-lazy scripts.
7370 if (!abortIfSyntaxParser())
7373 Node genfn
= generatorComprehensionLambda(StarGenerator
, begin
, null());
7377 Node result
= handler
.newList(PNK_GENEXP
, genfn
, JSOP_CALL
);
7380 handler
.setBeginPosition(result
, begin
);
7381 handler
.setEndPosition(result
, pos().end
);
7386 template <typename ParseHandler
>
7387 typename
ParseHandler::Node
7388 Parser
<ParseHandler
>::assignExprWithoutYield(unsigned msg
)
7390 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
7391 Node res
= assignExpr();
7392 if (res
&& pc
->lastYieldOffset
!= startYieldOffset
) {
7393 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
7400 template <typename ParseHandler
>
7402 Parser
<ParseHandler
>::argumentList(Node listNode
, bool* isSpread
)
7405 if (!tokenStream
.matchToken(&matched
, TOK_RP
, TokenStream::Operand
))
7408 handler
.setEndPosition(listNode
, pos().end
);
7412 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
7416 bool spread
= false;
7418 if (!tokenStream
.matchToken(&matched
, TOK_TRIPLEDOT
, TokenStream::Operand
))
7422 begin
= pos().begin
;
7426 Node argNode
= assignExpr();
7430 argNode
= handler
.newUnary(PNK_SPREAD
, JSOP_NOP
, begin
, argNode
);
7435 if (handler
.isOperationWithoutParens(argNode
, PNK_YIELD
)) {
7437 if (!tokenStream
.peekToken(&tt
))
7439 if (tt
== TOK_COMMA
) {
7440 report(ParseError
, false, argNode
, JSMSG_BAD_GENERATOR_SYNTAX
, js_yield_str
);
7444 #if JS_HAS_GENERATOR_EXPRS
7446 if (!tokenStream
.matchToken(&matched
, TOK_FOR
))
7449 if (pc
->lastYieldOffset
!= startYieldOffset
) {
7450 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
7451 JSMSG_BAD_GENEXP_BODY
, js_yield_str
);
7454 argNode
= legacyGeneratorExpr(argNode
);
7458 report(ParseError
, false, argNode
, JSMSG_BAD_GENERATOR_SYNTAX
, js_generator_str
);
7462 if (!tokenStream
.peekToken(&tt
))
7464 if (tt
== TOK_COMMA
) {
7465 report(ParseError
, false, argNode
, JSMSG_BAD_GENERATOR_SYNTAX
, js_generator_str
);
7473 handler
.addList(listNode
, argNode
);
7476 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
7483 if (!tokenStream
.getToken(&tt
))
7486 report(ParseError
, false, null(), JSMSG_PAREN_AFTER_ARGS
);
7489 handler
.setEndPosition(listNode
, pos().end
);
7493 template <typename ParseHandler
>
7494 typename
ParseHandler::Node
7495 Parser
<ParseHandler
>::memberExpr(TokenKind tt
, bool allowCallSyntax
)
7497 MOZ_ASSERT(tokenStream
.isCurrentTokenType(tt
));
7501 JS_CHECK_RECURSION(context
, return null());
7503 /* Check for new expression first. */
7504 if (tt
== TOK_NEW
) {
7505 lhs
= handler
.newList(PNK_NEW
, null(), JSOP_NEW
);
7509 if (!tokenStream
.getToken(&tt
, TokenStream::Operand
))
7511 Node ctorExpr
= memberExpr(tt
, false);
7515 handler
.addList(lhs
, ctorExpr
);
7518 if (!tokenStream
.matchToken(&matched
, TOK_LP
))
7521 bool isSpread
= false;
7522 if (!argumentList(lhs
, &isSpread
))
7525 handler
.setOp(lhs
, JSOP_SPREADNEW
);
7528 lhs
= primaryExpr(tt
);
7534 if (!tokenStream
.getToken(&tt
))
7540 if (tt
== TOK_DOT
) {
7541 if (!tokenStream
.getToken(&tt
, TokenStream::KeywordIsName
))
7543 if (tt
== TOK_NAME
) {
7544 PropertyName
* field
= tokenStream
.currentName();
7545 nextMember
= handler
.newPropertyAccess(lhs
, field
, pos().end
);
7549 report(ParseError
, false, null(), JSMSG_NAME_AFTER_DOT
);
7552 } else if (tt
== TOK_LB
) {
7553 Node propExpr
= expr();
7557 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
7559 nextMember
= handler
.newPropertyByValue(lhs
, propExpr
, pos().end
);
7562 } else if ((allowCallSyntax
&& tt
== TOK_LP
) ||
7563 tt
== TOK_TEMPLATE_HEAD
||
7564 tt
== TOK_NO_SUBS_TEMPLATE
)
7566 JSOp op
= JSOP_CALL
;
7568 nextMember
= handler
.newList(PNK_CALL
, null(), JSOP_CALL
);
7570 nextMember
= handler
.newList(PNK_TAGGED_TEMPLATE
, null(), JSOP_CALL
);
7575 if (JSAtom
* atom
= handler
.isName(lhs
)) {
7576 if (tt
== TOK_LP
&& atom
== context
->names().eval
) {
7577 /* Select JSOP_EVAL and flag pc as heavyweight. */
7578 op
= pc
->sc
->strict
? JSOP_STRICTEVAL
: JSOP_EVAL
;
7579 pc
->sc
->setBindingsAccessedDynamically();
7582 * In non-strict mode code, direct calls to eval can add
7583 * variables to the call object.
7585 if (pc
->sc
->isFunctionBox() && !pc
->sc
->strict
)
7586 pc
->sc
->asFunctionBox()->setHasExtensibleScope();
7588 } else if (JSAtom
* atom
= handler
.isGetProp(lhs
)) {
7589 /* Select JSOP_FUNAPPLY given foo.apply(...). */
7590 if (atom
== context
->names().apply
) {
7592 if (pc
->sc
->isFunctionBox())
7593 pc
->sc
->asFunctionBox()->usesApply
= true;
7594 } else if (atom
== context
->names().call
) {
7599 handler
.setBeginPosition(nextMember
, lhs
);
7600 handler
.addList(nextMember
, lhs
);
7603 bool isSpread
= false;
7604 if (!argumentList(nextMember
, &isSpread
))
7607 if (op
== JSOP_EVAL
)
7608 op
= JSOP_SPREADEVAL
;
7609 else if (op
== JSOP_STRICTEVAL
)
7610 op
= JSOP_STRICTSPREADEVAL
;
7612 op
= JSOP_SPREADCALL
;
7615 if (!taggedTemplate(nextMember
, tt
))
7618 handler
.setOp(nextMember
, op
);
7620 tokenStream
.ungetToken();
7629 template <typename ParseHandler
>
7630 typename
ParseHandler::Node
7631 Parser
<ParseHandler
>::newName(PropertyName
* name
)
7633 return handler
.newName(name
, pc
->blockid(), pos());
7636 template <typename ParseHandler
>
7637 typename
ParseHandler::Node
7638 Parser
<ParseHandler
>::identifierName()
7640 RootedPropertyName
name(context
, tokenStream
.currentName());
7641 Node pn
= newName(name
);
7645 if (!pc
->inDeclDestructuring
&& !noteNameUse(name
, pn
))
7651 template <typename ParseHandler
>
7652 typename
ParseHandler::Node
7653 Parser
<ParseHandler
>::stringLiteral()
7655 return handler
.newStringLiteral(stopStringCompression(), pos());
7658 template <typename ParseHandler
>
7659 typename
ParseHandler::Node
7660 Parser
<ParseHandler
>::noSubstitutionTemplate()
7662 return handler
.newTemplateStringLiteral(stopStringCompression(), pos());
7665 template <typename ParseHandler
>
7666 JSAtom
* Parser
<ParseHandler
>::stopStringCompression() {
7667 JSAtom
* atom
= tokenStream
.currentToken().atom();
7669 // Large strings are fast to parse but slow to compress. Stop compression on
7670 // them, so we don't wait for a long time for compression to finish at the
7671 // end of compilation.
7672 const size_t HUGE_STRING
= 50000;
7673 if (sct
&& sct
->active() && atom
->length() >= HUGE_STRING
)
7678 template <typename ParseHandler
>
7679 typename
ParseHandler::Node
7680 Parser
<ParseHandler
>::newRegExp()
7682 // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
7683 const char16_t
* chars
= tokenStream
.getTokenbuf().begin();
7684 size_t length
= tokenStream
.getTokenbuf().length();
7685 RegExpFlag flags
= tokenStream
.currentToken().regExpFlags();
7687 Rooted
<RegExpObject
*> reobj(context
);
7688 RegExpStatics
* res
= context
->global()->getRegExpStatics(context
);
7692 reobj
= RegExpObject::create(context
, res
, chars
, length
, flags
, &tokenStream
, alloc
);
7696 return handler
.newRegExp(reobj
, pos(), *this);
7699 template <typename ParseHandler
>
7700 typename
ParseHandler::Node
7701 Parser
<ParseHandler
>::arrayInitializer()
7703 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_LB
));
7705 uint32_t begin
= pos().begin
;
7706 Node literal
= handler
.newArrayLiteral(begin
, pc
->blockidGen
);
7711 if (!tokenStream
.getToken(&tt
, TokenStream::Operand
))
7715 * Mark empty arrays as non-constant, since we cannot easily
7716 * determine their type.
7718 handler
.setListFlag(literal
, PNX_NONCONST
);
7719 } else if (tt
== TOK_FOR
) {
7720 // ES6 array comprehension.
7721 return arrayComprehension(begin
);
7723 tokenStream
.ungetToken();
7725 bool spread
= false, missingTrailingComma
= false;
7728 if (index
== NativeObject::NELEMENTS_LIMIT
) {
7729 report(ParseError
, false, null(), JSMSG_ARRAY_INIT_TOO_BIG
);
7734 if (!tokenStream
.peekToken(&tt
, TokenStream::Operand
))
7739 if (tt
== TOK_COMMA
) {
7740 tokenStream
.consumeKnownToken(TOK_COMMA
);
7741 if (!handler
.addElision(literal
, pos()))
7743 } else if (tt
== TOK_TRIPLEDOT
) {
7745 tokenStream
.consumeKnownToken(TOK_TRIPLEDOT
);
7746 uint32_t begin
= pos().begin
;
7747 Node inner
= assignExpr();
7750 if (!handler
.addSpreadElement(literal
, begin
, inner
))
7753 Node element
= assignExpr();
7756 if (foldConstants
&& !FoldConstants(context
, &element
, this))
7758 if (!handler
.addArrayElement(literal
, element
))
7762 if (tt
!= TOK_COMMA
) {
7763 /* If we didn't already match TOK_COMMA in above case. */
7765 if (!tokenStream
.matchToken(&matched
, TOK_COMMA
))
7768 missingTrailingComma
= true;
7775 * At this point, (index == 0 && missingTrailingComma) implies one
7776 * element initialiser was parsed.
7778 * A legacy array comprehension of the form:
7780 * [i * j for (i in o) for (j in p) if (i != j)]
7782 * translates to roughly the following let expression:
7784 * let (array = new Array, i, j) {
7785 * for (i in o) let {
7793 * where array is a nameless block-local variable. The "roughly" means
7794 * that an implementation may optimize away the array.push. A legacy
7795 * array comprehension opens exactly one block scope, no matter how many
7796 * for heads it contains.
7798 * Each let () {...} or for (let ...) ... compiles to:
7800 * JSOP_PUSHN <N> // Push space for block-scoped locals.
7801 * (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope
7804 * JSOP_DEBUGLEAVEBLOCK // Invalidate any DebugScope proxies.
7805 * JSOP_POPBLOCKSCOPE? // Pop off scope chain, if needed.
7806 * JSOP_POPN <N> // Pop space for block-scoped locals.
7808 * where <o> is a literal object representing the block scope,
7809 * with <n> properties, naming each var declared in the block.
7811 * Each var declaration in a let-block binds a name in <o> at compile
7812 * time. A block-local var is accessed by the JSOP_GETLOCAL and
7813 * JSOP_SETLOCAL ops. These ops have an immediate operand, the local
7814 * slot's stack index from fp->spbase.
7816 * The legacy array comprehension iteration step, array.push(i * j) in
7817 * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, where
7818 * <array> is the index of array's stack slot.
7820 if (index
== 0 && !spread
) {
7822 if (!tokenStream
.matchToken(&matched
, TOK_FOR
))
7824 if (matched
&& missingTrailingComma
)
7825 return legacyArrayComprehension(literal
);
7828 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
7830 handler
.setEndPosition(literal
, pos().end
);
7835 DoubleToAtom(ExclusiveContext
* cx
, double value
)
7837 // This is safe because doubles can not be moved.
7838 Value tmp
= DoubleValue(value
);
7839 return ToAtom
<CanGC
>(cx
, HandleValue::fromMarkedLocation(&tmp
));
7842 template <typename ParseHandler
>
7843 typename
ParseHandler::Node
7844 Parser
<ParseHandler
>::computedPropertyName(Node literal
)
7846 uint32_t begin
= pos().begin
;
7848 // Turn off the inDeclDestructuring flag when parsing computed property
7849 // names. In short, when parsing 'let {[x + y]: z} = obj;', noteNameUse()
7850 // should be called on x and y, but not on z. See the comment on
7851 // Parser<>::checkDestructuring() for details.
7852 bool saved
= pc
->inDeclDestructuring
;
7853 pc
->inDeclDestructuring
= false;
7854 Node assignNode
= assignExpr();
7855 pc
->inDeclDestructuring
= saved
;
7859 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_COMP_PROP_UNTERM_EXPR
);
7860 Node propname
= handler
.newComputedName(assignNode
, begin
, pos().end
);
7863 handler
.setListFlag(literal
, PNX_NONCONST
);
7867 template <typename ParseHandler
>
7868 typename
ParseHandler::Node
7869 Parser
<ParseHandler
>::objectLiteral()
7871 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_LC
));
7873 Node literal
= handler
.newObjectLiteral(pos().begin
);
7877 bool seenPrototypeMutation
= false;
7878 RootedAtom
atom(context
);
7881 if (!tokenStream
.getToken(<ok
, TokenStream::KeywordIsName
))
7886 bool isGenerator
= false;
7887 if (ltok
== TOK_MUL
) {
7889 if (!tokenStream
.getToken(<ok
, TokenStream::KeywordIsName
))
7895 JSOp op
= JSOP_INITPROP
;
7899 atom
= DoubleToAtom(context
, tokenStream
.currentToken().number());
7902 propname
= newNumber(tokenStream
.currentToken());
7906 propname
= computedPropertyName(literal
);
7913 atom
= tokenStream
.currentName();
7914 if (atom
== context
->names().get
) {
7916 report(ParseError
, false, null(), JSMSG_BAD_PROP_ID
);
7919 op
= JSOP_INITPROP_GETTER
;
7920 } else if (atom
== context
->names().set
) {
7922 report(ParseError
, false, null(), JSMSG_BAD_PROP_ID
);
7925 op
= JSOP_INITPROP_SETTER
;
7927 propname
= handler
.newIdentifier(atom
, pos());
7933 // We have parsed |get| or |set|. Look for an accessor property
7936 if (!tokenStream
.getToken(&tt
, TokenStream::KeywordIsName
))
7938 if (tt
== TOK_NAME
) {
7939 atom
= tokenStream
.currentName();
7940 propname
= newName(atom
->asPropertyName());
7943 } else if (tt
== TOK_STRING
) {
7944 atom
= tokenStream
.currentToken().atom();
7947 if (atom
->isIndex(&index
)) {
7948 propname
= handler
.newNumber(index
, NoDecimal
, pos());
7951 atom
= DoubleToAtom(context
, index
);
7955 propname
= stringLiteral();
7959 } else if (tt
== TOK_NUMBER
) {
7960 atom
= DoubleToAtom(context
, tokenStream
.currentToken().number());
7963 propname
= newNumber(tokenStream
.currentToken());
7966 } else if (tt
== TOK_LB
) {
7967 propname
= computedPropertyName(literal
);
7971 // Not an accessor property after all.
7972 tokenStream
.ungetToken();
7973 propname
= handler
.newIdentifier(atom
, pos());
7980 MOZ_ASSERT(op
== JSOP_INITPROP_GETTER
|| op
== JSOP_INITPROP_SETTER
);
7985 atom
= tokenStream
.currentToken().atom();
7987 if (atom
->isIndex(&index
)) {
7988 propname
= handler
.newNumber(index
, NoDecimal
, pos());
7992 propname
= stringLiteral();
8000 report(ParseError
, false, null(), JSMSG_BAD_PROP_ID
);
8004 if (op
== JSOP_INITPROP
) {
8006 if (!tokenStream
.getToken(&tt
))
8009 if (tt
== TOK_COLON
) {
8011 report(ParseError
, false, null(), JSMSG_BAD_PROP_ID
);
8015 Node propexpr
= assignExpr();
8019 if (foldConstants
&& !FoldConstants(context
, &propexpr
, this))
8022 if (atom
== context
->names().proto
) {
8023 if (seenPrototypeMutation
) {
8024 report(ParseError
, false, propname
, JSMSG_DUPLICATE_PROPERTY
, "__proto__");
8027 seenPrototypeMutation
= true;
8029 // Note: this occurs *only* if we observe TOK_COLON! Only
8030 // __proto__: v mutates [[Prototype]]. Getters, setters,
8031 // method/generator definitions, computed property name
8032 // versions of all of these, and shorthands do not.
8033 uint32_t begin
= handler
.getPosition(propname
).begin
;
8034 if (!handler
.addPrototypeMutation(literal
, begin
, propexpr
))
8037 if (!handler
.isConstant(propexpr
))
8038 handler
.setListFlag(literal
, PNX_NONCONST
);
8040 if (!handler
.addPropertyDefinition(literal
, propname
, propexpr
))
8043 } else if (ltok
== TOK_NAME
&& (tt
== TOK_COMMA
|| tt
== TOK_RC
)) {
8045 * Support, e.g., |var {x, y} = o| as destructuring shorthand
8046 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8049 report(ParseError
, false, null(), JSMSG_BAD_PROP_ID
);
8052 if (!abortIfSyntaxParser())
8054 tokenStream
.ungetToken();
8055 if (!tokenStream
.checkForKeyword(atom
, nullptr))
8057 PropertyName
* name
= handler
.isName(propname
);
8059 propname
= newName(name
);
8062 Node ident
= identifierName();
8063 if (!handler
.addPropertyDefinition(literal
, propname
, ident
, true))
8065 } else if (tt
== TOK_LP
) {
8066 tokenStream
.ungetToken();
8067 if (!methodDefinition(literal
, propname
, Normal
, Method
,
8068 isGenerator
? StarGenerator
: NotGenerator
, op
)) {
8072 report(ParseError
, false, null(), JSMSG_COLON_AFTER_ID
);
8076 /* NB: Getter function in { get x(){} } is unnamed. */
8077 if (!methodDefinition(literal
, propname
, op
== JSOP_INITPROP_GETTER
? Getter
: Setter
,
8078 Expression
, NotGenerator
, op
)) {
8084 if (!tokenStream
.getToken(&tt
))
8088 if (tt
!= TOK_COMMA
) {
8089 report(ParseError
, false, null(), JSMSG_CURLY_AFTER_LIST
);
8094 handler
.setEndPosition(literal
, pos().end
);
8098 template <typename ParseHandler
>
8100 Parser
<ParseHandler
>::methodDefinition(Node literal
, Node propname
, FunctionType type
,
8101 FunctionSyntaxKind kind
, GeneratorKind generatorKind
,
8104 RootedPropertyName
funName(context
);
8105 if (kind
== Method
&& tokenStream
.isCurrentTokenType(TOK_NAME
))
8106 funName
= tokenStream
.currentName();
8110 Node fn
= functionDef(funName
, type
, kind
, generatorKind
);
8113 if (!handler
.addMethodDefinition(literal
, propname
, fn
, op
))
8118 template <typename ParseHandler
>
8119 typename
ParseHandler::Node
8120 Parser
<ParseHandler
>::primaryExpr(TokenKind tt
)
8122 MOZ_ASSERT(tokenStream
.isCurrentTokenType(tt
));
8123 JS_CHECK_RECURSION(context
, return null());
8127 return functionExpr();
8130 return arrayInitializer();
8133 return objectLiteral();
8136 return letBlock(LetExpression
);
8140 if (!tokenStream
.peekToken(&next
, TokenStream::Operand
))
8143 return parenExprOrGeneratorComprehension();
8145 // Not valid expression syntax, but this is valid in an arrow function
8146 // with no params: `() => body`.
8147 tokenStream
.consumeKnownToken(next
);
8149 if (!tokenStream
.peekToken(&next
))
8151 if (next
!= TOK_ARROW
) {
8152 report(ParseError
, false, null(), JSMSG_UNEXPECTED_TOKEN
,
8153 "expression", TokenKindToDesc(TOK_RP
));
8157 // Now just return something that will allow parsing to continue.
8158 // It doesn't matter what; when we reach the =>, we will rewind and
8159 // reparse the whole arrow function. See Parser::assignExpr.
8160 return handler
.newNullLiteral(pos());
8163 case TOK_TEMPLATE_HEAD
:
8164 return templateLiteral();
8166 case TOK_NO_SUBS_TEMPLATE
:
8167 return noSubstitutionTemplate();
8170 return stringLiteral();
8173 if (!checkYieldNameValidity())
8177 return identifierName();
8183 return newNumber(tokenStream
.currentToken());
8186 return handler
.newBooleanLiteral(true, pos());
8188 return handler
.newBooleanLiteral(false, pos());
8190 if (pc
->sc
->isFunctionBox())
8191 pc
->sc
->asFunctionBox()->usesThis
= true;
8192 return handler
.newThisLiteral(pos());
8194 return handler
.newNullLiteral(pos());
8196 case TOK_TRIPLEDOT
: {
8199 // This isn't valid expression syntax, but it's valid in an arrow
8200 // function as a trailing rest param: `(a, b, ...rest) => body`. Check
8201 // for a name, closing parenthesis, and arrow, and allow it only if all
8203 if (!tokenStream
.getToken(&next
))
8205 if (next
!= TOK_NAME
) {
8206 report(ParseError
, false, null(), JSMSG_UNEXPECTED_TOKEN
,
8207 "rest argument name", TokenKindToDesc(next
));
8211 if (!tokenStream
.getToken(&next
))
8213 if (next
!= TOK_RP
) {
8214 report(ParseError
, false, null(), JSMSG_UNEXPECTED_TOKEN
,
8215 "closing parenthesis", TokenKindToDesc(next
));
8219 if (!tokenStream
.peekToken(&next
))
8221 if (next
!= TOK_ARROW
) {
8222 report(ParseError
, false, null(), JSMSG_UNEXPECTED_TOKEN
,
8223 "'=>' after argument list", TokenKindToDesc(next
));
8227 tokenStream
.ungetToken(); // put back right paren
8229 // Return an arbitrary expression node. See case TOK_RP above.
8230 return handler
.newNullLiteral(pos());
8234 report(ParseError
, false, null(), JSMSG_UNEXPECTED_TOKEN
,
8235 "expression", TokenKindToDesc(tt
));
8240 template <typename ParseHandler
>
8241 typename
ParseHandler::Node
8242 Parser
<ParseHandler
>::parenExprOrGeneratorComprehension()
8244 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_LP
));
8245 uint32_t begin
= pos().begin
;
8246 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
8249 if (!tokenStream
.matchToken(&matched
, TOK_FOR
, TokenStream::Operand
))
8252 return generatorComprehension(begin
);
8255 * Always accept the 'in' operator in a parenthesized expression,
8256 * where it's unambiguous, even if we might be parsing the init of a
8259 bool oldParsingForInit
= pc
->parsingForInit
;
8260 pc
->parsingForInit
= false;
8262 pc
->parsingForInit
= oldParsingForInit
;
8267 #if JS_HAS_GENERATOR_EXPRS
8268 if (!tokenStream
.matchToken(&matched
, TOK_FOR
))
8271 if (pc
->lastYieldOffset
!= startYieldOffset
) {
8272 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
8273 JSMSG_BAD_GENEXP_BODY
, js_yield_str
);
8276 if (handler
.isOperationWithoutParens(pn
, PNK_COMMA
)) {
8277 report(ParseError
, false, null(),
8278 JSMSG_BAD_GENERATOR_SYNTAX
, js_generator_str
);
8281 pn
= legacyGeneratorExpr(pn
);
8284 handler
.setBeginPosition(pn
, begin
);
8286 if (!tokenStream
.getToken(&tt
))
8289 report(ParseError
, false, null(),
8290 JSMSG_BAD_GENERATOR_SYNTAX
, js_generator_str
);
8293 handler
.setEndPosition(pn
, pos().end
);
8294 handler
.setInParens(pn
);
8297 #endif /* JS_HAS_GENERATOR_EXPRS */
8299 pn
= handler
.setInParens(pn
);
8301 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
8306 // Legacy generator comprehensions can sometimes appear without parentheses.
8309 // foo(x for (x in bar))
8311 // In this case the parens are part of the call, and not part of the generator
8312 // comprehension. This can happen in these contexts:
8319 // foo(_) // must be first and only argument
8321 // This is not the case for ES6 generator comprehensions; they must always be in
8324 template <typename ParseHandler
>
8325 typename
ParseHandler::Node
8326 Parser
<ParseHandler
>::exprInParens()
8328 MOZ_ASSERT(tokenStream
.isCurrentTokenType(TOK_LP
));
8329 uint32_t begin
= pos().begin
;
8330 uint32_t startYieldOffset
= pc
->lastYieldOffset
;
8333 * Always accept the 'in' operator in a parenthesized expression,
8334 * where it's unambiguous, even if we might be parsing the init of a
8337 bool oldParsingForInit
= pc
->parsingForInit
;
8338 pc
->parsingForInit
= false;
8340 pc
->parsingForInit
= oldParsingForInit
;
8345 #if JS_HAS_GENERATOR_EXPRS
8347 if (!tokenStream
.matchToken(&matched
, TOK_FOR
))
8350 if (pc
->lastYieldOffset
!= startYieldOffset
) {
8351 reportWithOffset(ParseError
, false, pc
->lastYieldOffset
,
8352 JSMSG_BAD_GENEXP_BODY
, js_yield_str
);
8355 if (handler
.isOperationWithoutParens(pn
, PNK_COMMA
)) {
8356 report(ParseError
, false, null(),
8357 JSMSG_BAD_GENERATOR_SYNTAX
, js_generator_str
);
8360 pn
= legacyGeneratorExpr(pn
);
8363 handler
.setBeginPosition(pn
, begin
);
8365 #endif /* JS_HAS_GENERATOR_EXPRS */
8370 template <typename ParseHandler
>
8372 Parser
<ParseHandler
>::accumulateTelemetry()
8374 JSContext
* cx
= context
->maybeJSContext();
8377 const char* filename
= getFilename();
8381 bool isAddon
= !!cx
->compartment()->addonId
;
8382 bool isHTTP
= strncmp(filename
, "http://", 7) == 0 || strncmp(filename
, "https://", 8) == 0;
8384 // Only report telemetry for web content, not add-ons or chrome JS.
8385 if (isAddon
|| !isHTTP
)
8388 enum DeprecatedLanguageExtensions
{
8389 DeprecatedForEach
= 0, // JS 1.6+
8390 DeprecatedDestructuringForIn
= 1, // JS 1.7 only
8391 DeprecatedLegacyGenerator
= 2, // JS 1.7+
8392 DeprecatedExpressionClosure
= 3, // Added in JS 1.8, but not version-gated
8393 DeprecatedLetBlock
= 4, // Added in JS 1.7, but not version-gated
8394 DeprecatedLetExpression
= 5, // Added in JS 1.7, but not version-gated
8397 // Hazard analysis can't tell that the telemetry callbacks don't GC.
8398 JS::AutoSuppressGCAnalysis nogc
;
8400 // Call back into Firefox's Telemetry reporter.
8401 int id
= JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT
;
8402 if (sawDeprecatedForEach
)
8403 cx
->runtime()->addTelemetry(id
, DeprecatedForEach
);
8404 if (sawDeprecatedDestructuringForIn
)
8405 cx
->runtime()->addTelemetry(id
, DeprecatedDestructuringForIn
);
8406 if (sawDeprecatedLegacyGenerator
)
8407 cx
->runtime()->addTelemetry(id
, DeprecatedLegacyGenerator
);
8408 if (sawDeprecatedExpressionClosure
)
8409 cx
->runtime()->addTelemetry(id
, DeprecatedExpressionClosure
);
8410 if (sawDeprecatedLetBlock
)
8411 cx
->runtime()->addTelemetry(id
, DeprecatedLetBlock
);
8412 if (sawDeprecatedLetExpression
)
8413 cx
->runtime()->addTelemetry(id
, DeprecatedLetExpression
);
8416 template class Parser
<FullParseHandler
>;
8417 template class Parser
<SyntaxParseHandler
>;
8419 } /* namespace frontend */
8420 } /* namespace js */