1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 * This is a recursive-descent parser for the JavaScript language specified by
45 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
46 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
47 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
48 * After tree construction, it rewrites trees to fold constants and evaluate
49 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
52 * This parser attempts no error recovery.
59 #include "jsarena.h" /* Added by JSIFY */
60 #include "jsutil.h" /* Added by JSIFY */
65 #include "jsversion.h"
79 #include "jsstaticcheck.h"
80 #include "jslibmath.h"
83 #if JS_HAS_XML_SUPPORT
87 #if JS_HAS_DESTRUCTURING
94 * Asserts to verify assumptions behind pn_ macros.
96 #define pn_offsetof(m) offsetof(JSParseNode, m)
98 JS_STATIC_ASSERT(pn_offsetof(pn_link
) == pn_offsetof(dn_uses
));
99 JS_STATIC_ASSERT(pn_offsetof(pn_u
.name
.atom
) == pn_offsetof(pn_u
.apair
.atom
));
104 * Insist that the next token be of type tt, or report errno and return null.
105 * NB: this macro uses cx and ts from its lexical environment.
107 #define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags) \
109 if (tokenStream.getToken((__flags)) != tt) { \
110 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR, errno); \
114 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
116 #ifdef METER_PARSENODES
117 static uint32 parsenodes
= 0;
118 static uint32 maxparsenodes
= 0;
119 static uint32 recyclednodes
= 0;
123 JSParseNode::become(JSParseNode
*pn2
)
126 JS_ASSERT(!pn2
->pn_defn
);
130 JSParseNode
**pnup
= &pn2
->pn_lexdef
->dn_uses
;
132 pnup
= &(*pnup
)->pn_link
;
134 pn_link
= pn2
->pn_link
;
137 pn2
->pn_used
= false;
140 /* If this is a function node fix up the pn_funbox->node back-pointer. */
141 if (PN_TYPE(pn2
) == TOK_FUNCTION
&& pn2
->pn_arity
== PN_FUNC
)
142 pn2
->pn_funbox
->node
= this;
144 pn_type
= pn2
->pn_type
;
146 pn_arity
= pn2
->pn_arity
;
147 pn_parens
= pn2
->pn_parens
;
157 pn_used
= pn_defn
= false;
158 pn_arity
= PN_NULLARY
;
163 Parser::init(const jschar
*base
, size_t length
,
164 FILE *fp
, const char *filename
, uintN lineno
)
166 JSContext
*cx
= context
;
168 tempPoolMark
= JS_ARENA_MARK(&cx
->tempPool
);
169 if (!tokenStream
.init(base
, length
, fp
, filename
, lineno
)) {
170 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
178 JSContext
*cx
= context
;
181 JSPRINCIPALS_DROP(cx
, principals
);
183 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
187 Parser::setPrincipals(JSPrincipals
*prin
)
189 JS_ASSERT(!principals
);
191 JSPRINCIPALS_HOLD(context
, prin
);
196 Parser::newObjectBox(JSObject
*obj
)
201 * We use JSContext.tempPool to allocate parsed objects and place them on
202 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
203 * containing the entries must be alive until we are done with scanning,
204 * parsing and code generation for the whole script or top-level function.
207 JS_ARENA_ALLOCATE_TYPE(objbox
, JSObjectBox
, &context
->tempPool
);
209 js_ReportOutOfScriptQuota(context
);
212 objbox
->traceLink
= traceListHead
;
213 traceListHead
= objbox
;
214 objbox
->emitLink
= NULL
;
215 objbox
->object
= obj
;
220 Parser::newFunctionBox(JSObject
*obj
, JSParseNode
*fn
, JSTreeContext
*tc
)
223 JS_ASSERT(obj
->isFunction());
226 * We use JSContext.tempPool to allocate parsed objects and place them on
227 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
228 * containing the entries must be alive until we are done with scanning,
229 * parsing and code generation for the whole script or top-level function.
231 JSFunctionBox
*funbox
;
232 JS_ARENA_ALLOCATE_TYPE(funbox
, JSFunctionBox
, &context
->tempPool
);
234 js_ReportOutOfScriptQuota(context
);
237 funbox
->traceLink
= traceListHead
;
238 traceListHead
= funbox
;
239 funbox
->emitLink
= NULL
;
240 funbox
->object
= obj
;
242 funbox
->siblings
= tc
->functionList
;
243 tc
->functionList
= funbox
;
244 ++tc
->parser
->functionCount
;
246 funbox
->parent
= tc
->funbox
;
247 funbox
->methods
= NULL
;
248 funbox
->queued
= false;
249 funbox
->inLoop
= false;
250 for (JSStmtInfo
*stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
251 if (STMT_IS_LOOP(stmt
)) {
252 funbox
->inLoop
= true;
256 funbox
->level
= tc
->staticLevel
;
257 funbox
->tcflags
= (TCF_IN_FUNCTION
| (tc
->flags
& (TCF_COMPILE_N_GO
| TCF_STRICT_MODE_CODE
)));
262 JSFunctionBox::joinable() const
264 return FUN_NULL_CLOSURE((JSFunction
*) object
) &&
265 !(tcflags
& (TCF_FUN_USES_ARGUMENTS
| TCF_FUN_USES_OWN_NAME
));
269 JSFunctionBox::shouldUnbrand(uintN methods
, uintN slowMethods
) const
271 if (slowMethods
!= 0) {
272 for (const JSFunctionBox
*funbox
= this; funbox
; funbox
= funbox
->parent
) {
273 if (!(funbox
->tcflags
& TCF_FUN_MODULE_PATTERN
))
283 Parser::trace(JSTracer
*trc
)
285 JSObjectBox
*objbox
= traceListHead
;
287 JS_CALL_OBJECT_TRACER(trc
, objbox
->object
, "parser.object");
288 objbox
= objbox
->traceLink
;
293 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
);
296 UnlinkFunctionBox(JSParseNode
*pn
, JSTreeContext
*tc
)
298 JSFunctionBox
*funbox
= pn
->pn_funbox
;
300 JS_ASSERT(funbox
->node
== pn
);
303 if (funbox
->parent
&& PN_OP(pn
) == JSOP_LAMBDA
) {
305 * Remove pn from funbox->parent's methods list if it's there. See
306 * the TOK_SEMI case in Statement, near the bottom, the TOK_ASSIGN
307 * sub-case matching a constructor method assignment pattern.
309 JS_ASSERT(!pn
->pn_defn
);
310 JS_ASSERT(!pn
->pn_used
);
311 JSParseNode
**pnp
= &funbox
->parent
->methods
;
312 while (JSParseNode
*method
= *pnp
) {
314 *pnp
= method
->pn_link
;
317 pnp
= &method
->pn_link
;
321 JSFunctionBox
**funboxp
= &tc
->functionList
;
323 if (*funboxp
== funbox
) {
324 *funboxp
= funbox
->siblings
;
327 funboxp
= &(*funboxp
)->siblings
;
330 uint32 oldflags
= tc
->flags
;
331 JSFunctionBox
*oldlist
= tc
->functionList
;
333 tc
->flags
= funbox
->tcflags
;
334 tc
->functionList
= funbox
->kids
;
335 UnlinkFunctionBoxes(pn
->pn_body
, tc
);
336 funbox
->kids
= tc
->functionList
;
337 tc
->flags
= oldflags
;
338 tc
->functionList
= oldlist
;
340 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
341 pn
->pn_funbox
= NULL
;
346 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
)
349 switch (pn
->pn_arity
) {
353 UnlinkFunctionBoxes(pn
->pn_kid
, tc
);
356 UnlinkFunctionBoxes(pn
->pn_left
, tc
);
357 UnlinkFunctionBoxes(pn
->pn_right
, tc
);
360 UnlinkFunctionBoxes(pn
->pn_kid1
, tc
);
361 UnlinkFunctionBoxes(pn
->pn_kid2
, tc
);
362 UnlinkFunctionBoxes(pn
->pn_kid3
, tc
);
365 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
366 UnlinkFunctionBoxes(pn2
, tc
);
369 UnlinkFunctionBox(pn
, tc
);
372 UnlinkFunctionBoxes(pn
->maybeExpr(), tc
);
375 UnlinkFunctionBoxes(pn
->pn_tree
, tc
);
381 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
);
384 RecycleTree(JSParseNode
*pn
, JSTreeContext
*tc
)
386 JSParseNode
*next
, **head
;
391 /* Catch back-to-back dup recycles. */
392 JS_ASSERT(pn
!= tc
->parser
->nodeList
);
394 if (pn
->pn_used
|| pn
->pn_defn
) {
396 * JSAtomLists own definition nodes along with their used-node chains.
397 * Defer recycling such nodes until we unwind to top level to avoid
398 * linkage overhead or (alternatively) unlinking runtime complexity.
399 * Yes, this means dead code can contribute to static analysis results!
401 * Do recycle kids here, since they are no longer needed.
404 RecycleFuncNameKids(pn
, tc
);
406 UnlinkFunctionBoxes(pn
, tc
);
407 head
= &tc
->parser
->nodeList
;
410 #ifdef METER_PARSENODES
418 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
)
420 switch (pn
->pn_arity
) {
422 UnlinkFunctionBox(pn
, tc
);
427 * Only a definition node might have a non-null strong pn_expr link
428 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
429 * Every node with the pn_used flag set has a non-null pn_lexdef
430 * weak reference to its definition node.
432 if (!pn
->pn_used
&& pn
->pn_expr
) {
433 RecycleTree(pn
->pn_expr
, tc
);
439 JS_ASSERT(PN_TYPE(pn
) == TOK_FUNCTION
);
444 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
448 NewOrRecycledNode(JSTreeContext
*tc
)
450 JSParseNode
*pn
, *pn2
;
452 pn
= tc
->parser
->nodeList
;
454 JSContext
*cx
= tc
->parser
->context
;
456 JS_ARENA_ALLOCATE_TYPE(pn
, JSParseNode
, &cx
->tempPool
);
458 js_ReportOutOfScriptQuota(cx
);
460 tc
->parser
->nodeList
= pn
->pn_next
;
462 /* Recycle immediate descendents only, to save work and working set. */
463 switch (pn
->pn_arity
) {
465 RecycleTree(pn
->pn_body
, tc
);
470 while (pn2
&& !pn2
->pn_used
&& !pn2
->pn_defn
)
475 pn2
= RecycleTree(pn2
, tc
);
478 *pn
->pn_tail
= tc
->parser
->nodeList
;
479 tc
->parser
->nodeList
= pn
->pn_head
;
480 #ifdef METER_PARSENODES
481 recyclednodes
+= pn
->pn_count
;
488 RecycleTree(pn
->pn_kid1
, tc
);
489 RecycleTree(pn
->pn_kid2
, tc
);
490 RecycleTree(pn
->pn_kid3
, tc
);
493 if (pn
->pn_left
!= pn
->pn_right
)
494 RecycleTree(pn
->pn_left
, tc
);
495 RecycleTree(pn
->pn_right
, tc
);
498 RecycleTree(pn
->pn_kid
, tc
);
502 RecycleTree(pn
->pn_expr
, tc
);
509 #ifdef METER_PARSENODES
511 if (parsenodes
- recyclednodes
> maxparsenodes
)
512 maxparsenodes
= parsenodes
- recyclednodes
;
514 pn
->pn_used
= pn
->pn_defn
= false;
515 memset(&pn
->pn_u
, 0, sizeof pn
->pn_u
);
521 /* used only by static create methods of subclasses */
524 JSParseNode::create(JSParseNodeArity arity
, JSTreeContext
*tc
)
526 JSParseNode
*pn
= NewOrRecycledNode(tc
);
529 const Token
&tok
= tc
->parser
->tokenStream
.currentToken();
530 pn
->init(tok
.type
, JSOP_NOP
, arity
);
531 pn
->pn_pos
= tok
.pos
;
536 JSParseNode::newBinaryOrAppend(TokenKind tt
, JSOp op
, JSParseNode
*left
, JSParseNode
*right
,
539 JSParseNode
*pn
, *pn1
, *pn2
;
545 * Flatten a left-associative (left-heavy) tree of a given operator into
546 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
548 if (PN_TYPE(left
) == tt
&&
550 (js_CodeSpec
[op
].format
& JOF_LEFTASSOC
)) {
551 if (left
->pn_arity
!= PN_LIST
) {
552 pn1
= left
->pn_left
, pn2
= left
->pn_right
;
553 left
->pn_arity
= PN_LIST
;
554 left
->pn_parens
= false;
557 if (tt
== TOK_PLUS
) {
558 if (pn1
->pn_type
== TOK_STRING
)
559 left
->pn_xflags
|= PNX_STRCAT
;
560 else if (pn1
->pn_type
!= TOK_NUMBER
)
561 left
->pn_xflags
|= PNX_CANTFOLD
;
562 if (pn2
->pn_type
== TOK_STRING
)
563 left
->pn_xflags
|= PNX_STRCAT
;
564 else if (pn2
->pn_type
!= TOK_NUMBER
)
565 left
->pn_xflags
|= PNX_CANTFOLD
;
569 left
->pn_pos
.end
= right
->pn_pos
.end
;
570 if (tt
== TOK_PLUS
) {
571 if (right
->pn_type
== TOK_STRING
)
572 left
->pn_xflags
|= PNX_STRCAT
;
573 else if (right
->pn_type
!= TOK_NUMBER
)
574 left
->pn_xflags
|= PNX_CANTFOLD
;
580 * Fold constant addition immediately, to conserve node space and, what's
581 * more, so js_FoldConstants never sees mixed addition and concatenation
582 * operations with more than one leading non-string operand in a PN_LIST
583 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
584 * to "3pt", not "12pt").
586 if (tt
== TOK_PLUS
&&
587 left
->pn_type
== TOK_NUMBER
&&
588 right
->pn_type
== TOK_NUMBER
) {
589 left
->pn_dval
+= right
->pn_dval
;
590 left
->pn_pos
.end
= right
->pn_pos
.end
;
591 RecycleTree(right
, tc
);
595 pn
= NewOrRecycledNode(tc
);
598 pn
->init(tt
, op
, PN_BINARY
);
599 pn
->pn_pos
.begin
= left
->pn_pos
.begin
;
600 pn
->pn_pos
.end
= right
->pn_pos
.end
;
602 pn
->pn_right
= right
;
603 return (BinaryNode
*)pn
;
609 NameNode::initCommon(JSTreeContext
*tc
)
612 pn_cookie
= FREE_UPVAR_COOKIE
;
613 pn_dflags
= tc
->atTopLevel() ? PND_TOPLEVEL
: 0;
614 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
615 pn_dflags
|= PND_BLOCKCHILD
;
616 pn_blockid
= tc
->blockid();
620 NameNode::create(JSAtom
*atom
, JSTreeContext
*tc
)
624 pn
= JSParseNode::create(PN_NAME
, tc
);
627 ((NameNode
*)pn
)->initCommon(tc
);
629 return (NameNode
*)pn
;
635 GenerateBlockId(JSTreeContext
*tc
, uint32
& blockid
)
637 if (tc
->blockidGen
== JS_BIT(20)) {
638 JS_ReportErrorNumber(tc
->parser
->context
, js_GetErrorMessage
, NULL
,
639 JSMSG_NEED_DIET
, "program");
642 blockid
= tc
->blockidGen
++;
647 GenerateBlockIdForStmtNode(JSParseNode
*pn
, JSTreeContext
*tc
)
649 JS_ASSERT(tc
->topStmt
);
650 JS_ASSERT(STMT_MAYBE_SCOPE(tc
->topStmt
));
651 JS_ASSERT(pn
->pn_type
== TOK_LC
|| pn
->pn_type
== TOK_LEXICALSCOPE
);
652 if (!GenerateBlockId(tc
, tc
->topStmt
->blockid
))
654 pn
->pn_blockid
= tc
->topStmt
->blockid
;
659 * Parse a top-level JS script.
662 Parser::parse(JSObject
*chain
)
665 * Protect atoms from being collected by a GC activation, which might
666 * - nest on this thread due to out of memory (the so-called "last ditch"
667 * GC attempted within js_NewGCThing), or
668 * - run for any reason on another thread if this thread is suspended on
669 * an object lock before it finishes generating bytecode into a script
670 * protected from the GC by a root or a stack frame reference.
672 JSTreeContext
globaltc(this);
673 globaltc
.scopeChain
= chain
;
674 if (!GenerateBlockId(&globaltc
, globaltc
.bodyid
))
677 JSParseNode
*pn
= statements();
679 if (!tokenStream
.matchToken(TOK_EOF
)) {
680 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
684 if (!js_FoldConstants(context
, pn
, &globaltc
))
691 JS_STATIC_ASSERT(FREE_STATIC_LEVEL
== JS_BITMASK(JSFB_LEVEL_BITS
));
694 SetStaticLevel(JSTreeContext
*tc
, uintN staticLevel
)
697 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
698 * (0xffffffff) and other cookies with that level.
700 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
701 * practically speaking it leaves more than enough room for upvars. In fact
702 * we might want to split cookie fields giving fewer bits for skip and more
703 * for slot, but only based on evidence.
705 if (staticLevel
>= FREE_STATIC_LEVEL
) {
706 JS_ReportErrorNumber(tc
->parser
->context
, js_GetErrorMessage
, NULL
,
707 JSMSG_TOO_DEEP
, js_function_str
);
710 tc
->staticLevel
= staticLevel
;
715 * Compile a top-level script.
718 Compiler::compileScript(JSContext
*cx
, JSObject
*scopeChain
, JSStackFrame
*callerFrame
,
719 JSPrincipals
*principals
, uint32 tcflags
,
720 const jschar
*chars
, size_t length
,
721 FILE *file
, const char *filename
, uintN lineno
,
722 JSString
*source
/* = NULL */,
723 unsigned staticLevel
/* = 0 */)
725 JSArenaPool codePool
, notePool
;
728 uint32 scriptGlobals
;
730 bool inDirectivePrologue
;
731 #ifdef METER_PARSENODES
732 void *sbrk(ptrdiff_t), *before
= sbrk(0);
735 JS_ASSERT(!(tcflags
& ~(TCF_COMPILE_N_GO
| TCF_NO_SCRIPT_RVAL
| TCF_NEED_MUTABLE_SCRIPT
)));
738 * The scripted callerFrame can only be given for compile-and-go scripts
739 * and non-zero static level requires callerFrame.
741 JS_ASSERT_IF(callerFrame
, tcflags
& TCF_COMPILE_N_GO
);
742 JS_ASSERT_IF(staticLevel
!= 0, callerFrame
);
744 Compiler
compiler(cx
, principals
, callerFrame
);
745 if (!compiler
.init(chars
, length
, file
, filename
, lineno
))
748 JS_InitArenaPool(&codePool
, "code", 1024, sizeof(jsbytecode
),
749 &cx
->scriptStackQuota
);
750 JS_InitArenaPool(¬ePool
, "note", 1024, sizeof(jssrcnote
),
751 &cx
->scriptStackQuota
);
753 Parser
&parser
= compiler
.parser
;
754 TokenStream
&tokenStream
= parser
.tokenStream
;
756 JSCodeGenerator
cg(&parser
, &codePool
, ¬ePool
, tokenStream
.getLineno());
760 MUST_FLOW_THROUGH("out");
762 /* Null script early in case of error, to reduce our code footprint. */
766 cg
.scopeChain
= scopeChain
;
767 if (!SetStaticLevel(&cg
, staticLevel
))
770 /* If this is a direct call to eval, inherit the caller's strictness. */
772 callerFrame
->script
&&
773 callerFrame
->script
->strictModeCode
) {
774 cg
.flags
|= TCF_STRICT_MODE_CODE
;
775 tokenStream
.setStrictMode();
779 * If funbox is non-null after we create the new script, callerFrame->fun
780 * was saved in the 0th object table entry.
785 if (tcflags
& TCF_COMPILE_N_GO
) {
788 * Save eval program source in script->atomMap.vector[0] for the
789 * eval cache (see obj_eval in jsobj.cpp).
791 JSAtom
*atom
= js_AtomizeString(cx
, source
, 0);
792 if (!atom
|| !cg
.atomList
.add(&parser
, atom
))
796 if (callerFrame
&& callerFrame
->fun
) {
798 * An eval script in a caller frame needs to have its enclosing
799 * function captured in case it refers to an upvar, and someone
800 * wishes to decompile it while it's running.
802 funbox
= parser
.newObjectBox(FUN_OBJECT(callerFrame
->fun
));
805 funbox
->emitLink
= cg
.objectList
.lastbox
;
806 cg
.objectList
.lastbox
= funbox
;
807 cg
.objectList
.length
++;
812 * Inline this->statements to emit as we go to save AST space. We must
813 * generate our script-body blockid since we aren't calling Statements.
816 if (!GenerateBlockId(&cg
, bodyid
))
820 #if JS_HAS_XML_SUPPORT
826 CG_SWITCH_TO_PROLOG(&cg
);
827 if (js_Emit1(cx
, &cg
, JSOP_TRACE
) < 0)
829 CG_SWITCH_TO_MAIN(&cg
);
831 inDirectivePrologue
= true;
833 tt
= tokenStream
.peekToken(TSF_OPERAND
);
837 JS_ASSERT(tt
== TOK_ERROR
);
841 pn
= parser
.statement();
844 JS_ASSERT(!cg
.blockNode
);
846 if (inDirectivePrologue
)
847 inDirectivePrologue
= parser
.recognizeDirectivePrologue(pn
);
849 if (!js_FoldConstants(cx
, pn
, &cg
))
852 if (cg
.functionList
) {
853 if (!parser
.analyzeFunctions(cg
.functionList
, cg
.flags
))
855 cg
.functionList
= NULL
;
858 if (!js_EmitTree(cx
, &cg
, pn
))
860 #if JS_HAS_XML_SUPPORT
861 if (PN_TYPE(pn
) != TOK_SEMI
||
863 !TreeTypeIsXML(PN_TYPE(pn
->pn_kid
))) {
867 RecycleTree(pn
, &cg
);
870 #if JS_HAS_XML_SUPPORT
872 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
873 * For background, see:
875 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
877 if (pn
&& onlyXML
&& (tcflags
& TCF_NO_SCRIPT_RVAL
)) {
878 ReportCompileErrorNumber(cx
, &tokenStream
, NULL
, JSREPORT_ERROR
,
879 JSMSG_XML_WHOLE_PROGRAM
);
885 * Global variables (gvars) share the atom index space with locals. Due to
886 * incremental code generation we need to patch the bytecode to adjust the
887 * local references to skip the globals.
889 scriptGlobals
= cg
.ngvars
;
890 if (scriptGlobals
!= 0 || cg
.hasSharps()) {
891 jsbytecode
*code
, *end
;
893 const JSCodeSpec
*cs
;
896 if (scriptGlobals
>= SLOTNO_LIMIT
)
899 for (end
= code
+ CG_OFFSET(&cg
); code
!= end
; code
+= len
) {
900 JS_ASSERT(code
< end
);
902 cs
= &js_CodeSpec
[op
];
903 len
= (cs
->length
> 0)
905 : js_GetVariableBytecodeLength(code
);
906 if ((cs
->format
& JOF_SHARPSLOT
) ||
907 JOF_TYPE(cs
->format
) == JOF_LOCAL
||
908 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
)) {
910 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
911 * emitted only for a function.
913 JS_ASSERT_IF(!(cs
->format
& JOF_SHARPSLOT
),
914 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ==
915 (op
== JSOP_GETLOCALPROP
));
916 slot
= GET_SLOTNO(code
);
917 slot
+= scriptGlobals
;
918 if (!(cs
->format
& JOF_SHARPSLOT
))
919 slot
+= cg
.sharpSlots();
920 if (slot
>= SLOTNO_LIMIT
)
922 SET_SLOTNO(code
, slot
);
927 #ifdef METER_PARSENODES
928 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
929 (char *)sbrk(0) - (char *)before
,
932 parsenodes
- recyclednodes
);
937 * Nowadays the threaded interpreter needs a stop instruction, so we
938 * do have to emit that here.
940 if (js_Emit1(cx
, &cg
, JSOP_STOP
) < 0)
942 #ifdef METER_PARSENODES
943 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
944 (char *)sbrk(0) - (char *)before
, CG_OFFSET(&cg
), cg
.noteCount
);
947 JS_DumpArenaStats(stdout
);
949 script
= js_NewScriptFromCG(cx
, &cg
);
950 if (script
&& funbox
&& script
!= script
->emptyScript())
951 script
->savedCallerFun
= true;
953 #ifdef JS_SCOPE_DEPTH_METER
955 JSObject
*obj
= scopeChain
;
957 while ((obj
= obj
->getParent()) != NULL
)
959 JS_BASIC_STATS_ACCUM(&cx
->runtime
->hostenvScopeDepthStats
, depth
);
964 JS_FinishArenaPool(&codePool
);
965 JS_FinishArenaPool(¬ePool
);
969 ReportCompileErrorNumber(cx
, &tokenStream
, NULL
, JSREPORT_ERROR
, JSMSG_TOO_MANY_LOCALS
);
975 * Insist on a final return before control flows out of pn. Try to be a bit
976 * smart about loops: do {...; return e2;} while(0) at the end of a function
977 * that contains an early return e1 will get a strict warning. Similarly for
978 * iloops: while (true){...} is treated as though ... returns.
980 #define ENDS_IN_OTHER 0
981 #define ENDS_IN_RETURN 1
982 #define ENDS_IN_BREAK 2
985 HasFinalReturn(JSParseNode
*pn
)
987 JSParseNode
*pn2
, *pn3
;
988 uintN rv
, rv2
, hasDefault
;
990 switch (pn
->pn_type
) {
993 return ENDS_IN_OTHER
;
994 return HasFinalReturn(pn
->last());
998 return ENDS_IN_OTHER
;
999 return HasFinalReturn(pn
->pn_kid2
) & HasFinalReturn(pn
->pn_kid3
);
1003 if (pn2
->pn_type
== TOK_PRIMARY
&& pn2
->pn_op
== JSOP_TRUE
)
1004 return ENDS_IN_RETURN
;
1005 if (pn2
->pn_type
== TOK_NUMBER
&& pn2
->pn_dval
)
1006 return ENDS_IN_RETURN
;
1007 return ENDS_IN_OTHER
;
1011 if (pn2
->pn_type
== TOK_PRIMARY
) {
1012 if (pn2
->pn_op
== JSOP_FALSE
)
1013 return HasFinalReturn(pn
->pn_left
);
1014 if (pn2
->pn_op
== JSOP_TRUE
)
1015 return ENDS_IN_RETURN
;
1017 if (pn2
->pn_type
== TOK_NUMBER
) {
1018 if (pn2
->pn_dval
== 0)
1019 return HasFinalReturn(pn
->pn_left
);
1020 return ENDS_IN_RETURN
;
1022 return ENDS_IN_OTHER
;
1026 if (pn2
->pn_arity
== PN_TERNARY
&& !pn2
->pn_kid2
)
1027 return ENDS_IN_RETURN
;
1028 return ENDS_IN_OTHER
;
1031 rv
= ENDS_IN_RETURN
;
1032 hasDefault
= ENDS_IN_OTHER
;
1034 if (pn2
->pn_type
== TOK_LEXICALSCOPE
)
1036 for (pn2
= pn2
->pn_head
; rv
&& pn2
; pn2
= pn2
->pn_next
) {
1037 if (pn2
->pn_type
== TOK_DEFAULT
)
1038 hasDefault
= ENDS_IN_RETURN
;
1039 pn3
= pn2
->pn_right
;
1040 JS_ASSERT(pn3
->pn_type
== TOK_LC
);
1042 rv2
= HasFinalReturn(pn3
->last());
1043 if (rv2
== ENDS_IN_OTHER
&& pn2
->pn_next
)
1044 /* Falling through to next case or default. */;
1049 /* If a final switch has no default case, we judge it harshly. */
1054 return ENDS_IN_BREAK
;
1057 return HasFinalReturn(pn
->pn_right
);
1060 return ENDS_IN_RETURN
;
1063 case TOK_LEXICALSCOPE
:
1064 return HasFinalReturn(pn
->expr());
1067 return ENDS_IN_RETURN
;
1070 /* If we have a finally block that returns, we are done. */
1072 rv
= HasFinalReturn(pn
->pn_kid3
);
1073 if (rv
== ENDS_IN_RETURN
)
1077 /* Else check the try block and any and all catch statements. */
1078 rv
= HasFinalReturn(pn
->pn_kid1
);
1080 JS_ASSERT(pn
->pn_kid2
->pn_arity
== PN_LIST
);
1081 for (pn2
= pn
->pn_kid2
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
1082 rv
&= HasFinalReturn(pn2
);
1087 /* Check this catch block's body. */
1088 return HasFinalReturn(pn
->pn_kid3
);
1091 /* Non-binary let statements are let declarations. */
1092 if (pn
->pn_arity
!= PN_BINARY
)
1093 return ENDS_IN_OTHER
;
1094 return HasFinalReturn(pn
->pn_right
);
1097 return ENDS_IN_OTHER
;
1102 ReportBadReturn(JSContext
*cx
, JSTreeContext
*tc
, uintN flags
, uintN errnum
,
1107 JS_ASSERT(tc
->inFunction());
1108 if (tc
->fun
->atom
) {
1109 name
= js_AtomToPrintableString(cx
, tc
->fun
->atom
);
1111 errnum
= anonerrnum
;
1114 return ReportCompileErrorNumber(cx
, TS(tc
->parser
), NULL
, flags
, errnum
, name
);
1118 CheckFinalReturn(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*pn
)
1120 JS_ASSERT(tc
->inFunction());
1121 return HasFinalReturn(pn
) == ENDS_IN_RETURN
||
1122 ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
1123 JSMSG_NO_RETURN_VALUE
, JSMSG_ANON_NO_RETURN_VALUE
);
1127 * Check that it is permitted to assign to lhs. Strict mode code may not
1128 * assign to 'eval' or 'arguments'.
1131 CheckStrictAssignment(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*lhs
)
1133 if (tc
->needStrictChecks() &&
1134 lhs
->pn_type
== TOK_NAME
) {
1135 JSAtom
*atom
= lhs
->pn_atom
;
1136 JSAtomState
*atomState
= &cx
->runtime
->atomState
;
1137 if (atom
== atomState
->evalAtom
|| atom
== atomState
->argumentsAtom
) {
1138 const char *name
= js_AtomToPrintableString(cx
, atom
);
1140 !ReportStrictModeError(cx
, TS(tc
->parser
), tc
, lhs
, JSMSG_DEPRECATED_ASSIGN
,
1150 * Check that it is permitted to introduce a binding for atom. Strict
1151 * mode forbids introducing new definitions for 'eval' or 'arguments'.
1152 * Use pn for reporting error locations, or use tc's token stream if
1156 CheckStrictBinding(JSContext
*cx
, JSTreeContext
*tc
, JSAtom
*atom
, JSParseNode
*pn
)
1158 if (!tc
->needStrictChecks())
1161 JSAtomState
*atomState
= &cx
->runtime
->atomState
;
1162 if (atom
== atomState
->evalAtom
|| atom
== atomState
->argumentsAtom
) {
1163 const char *name
= js_AtomToPrintableString(cx
, atom
);
1165 ReportStrictModeError(cx
, TS(tc
->parser
), tc
, pn
, JSMSG_BAD_BINDING
, name
);
1172 * In strict mode code, all formal parameter names must be distinct. If fun's
1173 * formals are legit given fun's strictness level, return true. Otherwise,
1174 * report an error and return false. Use pn for error position reporting,
1175 * unless we can find something more accurate in tc's decls.
1177 * In some cases the code to parse the argument list will already have noticed
1178 * the duplication; we could try to use that knowledge instead of re-checking
1179 * here. But since the strictness of the function's body determines what
1180 * constraints to apply to the argument list, we can't report the error until
1181 * after we've parsed the body. And as it turns out, the function's local name
1182 * list makes it reasonably cheap to find duplicates after the fact.
1185 CheckStrictFormals(JSContext
*cx
, JSTreeContext
*tc
, JSFunction
*fun
,
1190 if (!tc
->needStrictChecks())
1193 atom
= fun
->findDuplicateFormal();
1196 * We have found a duplicate parameter name. If we can find the
1197 * JSDefinition for the argument, that will have a more accurate source
1200 JSDefinition
*dn
= ALE_DEFN(tc
->decls
.lookup(atom
));
1201 if (dn
->pn_op
== JSOP_GETARG
)
1203 const char *name
= js_AtomToPrintableString(cx
, atom
);
1205 !ReportStrictModeError(cx
, TS(tc
->parser
), tc
, pn
, JSMSG_DUPLICATE_FORMAL
, name
)) {
1210 if (tc
->flags
& (TCF_FUN_PARAM_ARGUMENTS
| TCF_FUN_PARAM_EVAL
)) {
1211 JSAtomState
*atoms
= &cx
->runtime
->atomState
;
1212 atom
= (tc
->flags
& TCF_FUN_PARAM_ARGUMENTS
1213 ? atoms
->argumentsAtom
: atoms
->evalAtom
);
1214 /* The definition's source position will be more precise. */
1215 JSDefinition
*dn
= ALE_DEFN(tc
->decls
.lookup(atom
));
1216 JS_ASSERT(dn
->pn_atom
== atom
);
1217 const char *name
= js_AtomToPrintableString(cx
, atom
);
1219 !ReportStrictModeError(cx
, TS(tc
->parser
), tc
, dn
, JSMSG_BAD_BINDING
, name
)) {
1228 Parser::functionBody()
1230 JSStmtInfo stmtInfo
;
1231 uintN oldflags
, firstLine
;
1234 JS_ASSERT(tc
->inFunction());
1235 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
1236 stmtInfo
.flags
= SIF_BODY_BLOCK
;
1238 oldflags
= tc
->flags
;
1239 tc
->flags
&= ~(TCF_RETURN_EXPR
| TCF_RETURN_VOID
);
1242 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1243 * later, because we may have not peeked in tokenStream yet, so statements
1244 * won't acquire a valid pn->pn_pos.begin from the current token.
1246 firstLine
= tokenStream
.getLineno();
1247 #if JS_HAS_EXPR_CLOSURES
1248 if (tokenStream
.currentToken().type
== TOK_LC
) {
1251 pn
= UnaryNode::create(tc
);
1253 pn
->pn_kid
= assignExpr();
1257 if (tc
->flags
& TCF_FUN_IS_GENERATOR
) {
1258 ReportBadReturn(context
, tc
, JSREPORT_ERROR
,
1259 JSMSG_BAD_GENERATOR_RETURN
,
1260 JSMSG_BAD_ANON_GENERATOR_RETURN
);
1263 pn
->pn_type
= TOK_RETURN
;
1264 pn
->pn_op
= JSOP_RETURN
;
1265 pn
->pn_pos
.end
= pn
->pn_kid
->pn_pos
.end
;
1275 JS_ASSERT(!(tc
->topStmt
->flags
& SIF_SCOPE
));
1276 js_PopStatement(tc
);
1277 pn
->pn_pos
.begin
.lineno
= firstLine
;
1279 /* Check for falling off the end of a function that returns a value. */
1280 if (JS_HAS_STRICT_OPTION(context
) && (tc
->flags
& TCF_RETURN_EXPR
) &&
1281 !CheckFinalReturn(context
, tc
, pn
)) {
1286 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
1290 static JSAtomListElement
*
1291 MakePlaceholder(JSParseNode
*pn
, JSTreeContext
*tc
)
1293 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->parser
, pn
->pn_atom
);
1297 JSDefinition
*dn
= (JSDefinition
*)NameNode::create(pn
->pn_atom
, tc
);
1301 ALE_SET_DEFN(ale
, dn
);
1303 dn
->pn_dflags
|= PND_PLACEHOLDER
;
1308 Define(JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
1310 JS_ASSERT(!pn
->pn_used
);
1311 JS_ASSERT_IF(pn
->pn_defn
, pn
->isPlaceholder());
1314 JSAtomListElement
*ale
= NULL
;
1315 JSAtomList
*list
= NULL
;
1318 ale
= (list
= &tc
->decls
)->rawLookup(atom
, hep
);
1320 ale
= (list
= &tc
->lexdeps
)->rawLookup(atom
, hep
);
1323 JSDefinition
*dn
= ALE_DEFN(ale
);
1325 JSParseNode
**pnup
= &dn
->dn_uses
;
1327 uintN start
= let
? pn
->pn_blockid
: tc
->bodyid
;
1329 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_blockid
>= start
) {
1330 JS_ASSERT(pnu
->pn_used
);
1331 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1332 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1333 pnup
= &pnu
->pn_link
;
1336 if (pnu
!= dn
->dn_uses
) {
1337 *pnup
= pn
->dn_uses
;
1338 pn
->dn_uses
= dn
->dn_uses
;
1341 if ((!pnu
|| pnu
->pn_blockid
< tc
->bodyid
) && list
!= &tc
->decls
)
1342 list
->rawRemove(tc
->parser
, ale
, hep
);
1347 ale
= tc
->decls
.add(tc
->parser
, atom
, let
? JSAtomList::SHADOW
: JSAtomList::UNIQUE
);
1350 ALE_SET_DEFN(ale
, pn
);
1352 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
1357 LinkUseToDef(JSParseNode
*pn
, JSDefinition
*dn
, JSTreeContext
*tc
)
1359 JS_ASSERT(!pn
->pn_used
);
1360 JS_ASSERT(!pn
->pn_defn
);
1361 JS_ASSERT(pn
!= dn
->dn_uses
);
1362 pn
->pn_link
= dn
->dn_uses
;
1364 dn
->pn_dflags
|= pn
->pn_dflags
& PND_USE2DEF_FLAGS
;
1370 ForgetUse(JSParseNode
*pn
)
1373 JS_ASSERT(!pn
->pn_defn
);
1377 JSParseNode
**pnup
= &pn
->lexdef()->dn_uses
;
1379 while ((pnu
= *pnup
) != pn
)
1380 pnup
= &pnu
->pn_link
;
1381 *pnup
= pn
->pn_link
;
1382 pn
->pn_used
= false;
1385 static JSParseNode
*
1386 MakeAssignment(JSParseNode
*pn
, JSParseNode
*rhs
, JSTreeContext
*tc
)
1388 JSParseNode
*lhs
= NewOrRecycledNode(tc
);
1394 JSDefinition
*dn
= pn
->pn_lexdef
;
1395 JSParseNode
**pnup
= &dn
->dn_uses
;
1398 pnup
= &(*pnup
)->pn_link
;
1400 lhs
->pn_link
= pn
->pn_link
;
1404 pn
->pn_type
= TOK_ASSIGN
;
1405 pn
->pn_op
= JSOP_NOP
;
1406 pn
->pn_arity
= PN_BINARY
;
1407 pn
->pn_parens
= false;
1408 pn
->pn_used
= pn
->pn_defn
= false;
1414 static JSParseNode
*
1415 MakeDefIntoUse(JSDefinition
*dn
, JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
)
1418 * If dn is var, const, or let, and it has an initializer, then we must
1419 * rewrite it to be an assignment node, whose freshly allocated left-hand
1420 * side becomes a use of pn.
1422 if (dn
->isBindingForm()) {
1423 JSParseNode
*rhs
= dn
->expr();
1425 JSParseNode
*lhs
= MakeAssignment(dn
, rhs
, tc
);
1428 //pn->dn_uses = lhs;
1429 dn
= (JSDefinition
*) lhs
;
1432 dn
->pn_op
= (js_CodeSpec
[dn
->pn_op
].format
& JOF_SET
) ? JSOP_SETNAME
: JSOP_NAME
;
1433 } else if (dn
->kind() == JSDefinition::FUNCTION
) {
1434 JS_ASSERT(dn
->isTopLevel());
1435 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
1436 dn
->pn_type
= TOK_NAME
;
1437 dn
->pn_arity
= PN_NAME
;
1441 /* Now make dn no longer a definition, rather a use of pn. */
1442 JS_ASSERT(dn
->pn_type
== TOK_NAME
);
1443 JS_ASSERT(dn
->pn_arity
== PN_NAME
);
1444 JS_ASSERT(dn
->pn_atom
== atom
);
1446 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
1447 JS_ASSERT(pnu
->pn_used
);
1448 JS_ASSERT(!pnu
->pn_defn
);
1449 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1450 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1452 pn
->pn_dflags
|= dn
->pn_dflags
& PND_USE2DEF_FLAGS
;
1455 dn
->pn_defn
= false;
1457 dn
->pn_lexdef
= (JSDefinition
*) pn
;
1458 dn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1459 dn
->pn_dflags
&= ~PND_BOUND
;
1464 DefineArg(JSParseNode
*pn
, JSAtom
*atom
, uintN i
, JSTreeContext
*tc
)
1466 JSParseNode
*argpn
, *argsbody
;
1468 /* Flag tc so we don't have to lookup arguments on every use. */
1469 if (atom
== tc
->parser
->context
->runtime
->atomState
.argumentsAtom
)
1470 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1471 if (atom
== tc
->parser
->context
->runtime
->atomState
.evalAtom
)
1472 tc
->flags
|= TCF_FUN_PARAM_EVAL
;
1475 * Make an argument definition node, distinguished by being in tc->decls
1476 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1477 * list node returned via pn->pn_body.
1479 argpn
= NameNode::create(atom
, tc
);
1482 JS_ASSERT(PN_TYPE(argpn
) == TOK_NAME
&& PN_OP(argpn
) == JSOP_NOP
);
1484 /* Arguments are initialized by definition. */
1485 argpn
->pn_dflags
|= PND_INITIALIZED
;
1486 if (!Define(argpn
, atom
, tc
))
1489 argsbody
= pn
->pn_body
;
1491 argsbody
= ListNode::create(tc
);
1494 argsbody
->pn_type
= TOK_ARGSBODY
;
1495 argsbody
->pn_op
= JSOP_NOP
;
1496 argsbody
->makeEmpty();
1497 pn
->pn_body
= argsbody
;
1499 argsbody
->append(argpn
);
1501 argpn
->pn_op
= JSOP_GETARG
;
1502 argpn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, i
);
1503 argpn
->pn_dflags
|= PND_BOUND
;
1508 * Compile a JS function body, which might appear as the value of an event
1509 * handler attribute in an HTML <INPUT> tag.
1512 Compiler::compileFunctionBody(JSContext
*cx
, JSFunction
*fun
, JSPrincipals
*principals
,
1513 const jschar
*chars
, size_t length
,
1514 const char *filename
, uintN lineno
)
1516 Compiler
compiler(cx
, principals
);
1518 if (!compiler
.init(chars
, length
, NULL
, filename
, lineno
))
1521 /* No early return from after here until the js_FinishArenaPool calls. */
1522 JSArenaPool codePool
, notePool
;
1523 JS_InitArenaPool(&codePool
, "code", 1024, sizeof(jsbytecode
),
1524 &cx
->scriptStackQuota
);
1525 JS_InitArenaPool(¬ePool
, "note", 1024, sizeof(jssrcnote
),
1526 &cx
->scriptStackQuota
);
1528 Parser
&parser
= compiler
.parser
;
1529 TokenStream
&tokenStream
= parser
.tokenStream
;
1531 JSCodeGenerator
funcg(&parser
, &codePool
, ¬ePool
, tokenStream
.getLineno());
1535 funcg
.flags
|= TCF_IN_FUNCTION
;
1537 if (!GenerateBlockId(&funcg
, funcg
.bodyid
))
1540 /* FIXME: make Function format the source for a function definition. */
1541 tokenStream
.mungeCurrentToken(TOK_NAME
);
1542 JSParseNode
*fn
= FunctionNode::create(&funcg
);
1545 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1547 uintN nargs
= fun
->nargs
;
1549 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
1553 for (uintN i
= 0; i
< nargs
; i
++) {
1554 JSAtom
*name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1555 if (!DefineArg(fn
, name
, i
, &funcg
)) {
1565 * Farble the body so that it looks like a block statement to js_EmitTree,
1566 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1567 * done parsing, we must fold constants, analyze any nested functions, and
1568 * generate code for this function, including a stop opcode at the end.
1570 tokenStream
.mungeCurrentToken(TOK_LC
);
1571 JSParseNode
*pn
= fn
? parser
.functionBody() : NULL
;
1573 if (!CheckStrictFormals(cx
, &funcg
, fun
, pn
)) {
1575 } else if (!tokenStream
.matchToken(TOK_EOF
)) {
1576 ReportCompileErrorNumber(cx
, &tokenStream
, NULL
, JSREPORT_ERROR
,
1577 JSMSG_SYNTAX_ERROR
);
1579 } else if (!js_FoldConstants(cx
, pn
, &funcg
)) {
1580 /* js_FoldConstants reported the error already. */
1582 } else if (funcg
.functionList
&&
1583 !parser
.analyzeFunctions(funcg
.functionList
, funcg
.flags
)) {
1587 JS_ASSERT(PN_TYPE(fn
->pn_body
) == TOK_ARGSBODY
);
1588 fn
->pn_body
->append(pn
);
1589 fn
->pn_body
->pn_pos
= pn
->pn_pos
;
1593 if (!js_EmitFunctionScript(cx
, &funcg
, pn
))
1598 /* Restore saved state and release code generation arenas. */
1599 JS_FinishArenaPool(&codePool
);
1600 JS_FinishArenaPool(¬ePool
);
1605 * Parameter block types for the several Binder functions. We use a common
1606 * helper function signature in order to share code among destructuring and
1607 * simple variable declaration parsers. In the destructuring case, the binder
1608 * function is called indirectly from the variable declaration parser by way
1609 * of CheckDestructuring and its friends.
1612 (*Binder
)(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
);
1615 BindData() : fresh(true) {}
1617 JSParseNode
*pn
; /* name node for definition processing and
1618 error source coordinates */
1619 JSOp op
; /* prolog bytecode or nop */
1620 Binder binder
; /* binder, discriminates u */
1630 BindLocalVariable(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
,
1631 JSLocalKind localKind
, bool isArg
)
1633 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1636 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1637 * Instead 'var arguments' always restates the predefined property of the
1638 * activation objects whose name is 'arguments'. Assignment to such a
1639 * variable must be handled specially.
1641 * Special case: an argument named 'arguments' *does* shadow the predefined
1642 * arguments property.
1644 if (atom
== cx
->runtime
->atomState
.argumentsAtom
&& !isArg
)
1647 return js_AddLocal(cx
, fun
, atom
, localKind
);
1650 #if JS_HAS_DESTRUCTURING
1652 BindDestructuringArg(JSContext
*cx
, BindData
*data
, JSAtom
*atom
,
1657 /* Flag tc so we don't have to lookup arguments on every use. */
1658 if (atom
== tc
->parser
->context
->runtime
->atomState
.argumentsAtom
)
1659 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1660 if (atom
== tc
->parser
->context
->runtime
->atomState
.evalAtom
)
1661 tc
->flags
|= TCF_FUN_PARAM_EVAL
;
1663 JS_ASSERT(tc
->inFunction());
1665 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
1666 if (localKind
!= JSLOCAL_NONE
) {
1667 ReportCompileErrorNumber(cx
, TS(tc
->parser
), NULL
, JSREPORT_ERROR
,
1668 JSMSG_DESTRUCT_DUP_ARG
);
1671 JS_ASSERT(!tc
->decls
.lookup(atom
));
1674 if (!Define(pn
, atom
, tc
))
1677 uintN index
= tc
->fun
->u
.i
.nvars
;
1678 if (!BindLocalVariable(cx
, tc
->fun
, atom
, JSLOCAL_VAR
, true))
1680 pn
->pn_op
= JSOP_SETLOCAL
;
1681 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
1682 pn
->pn_dflags
|= PND_BOUND
;
1685 #endif /* JS_HAS_DESTRUCTURING */
1688 Parser::newFunction(JSTreeContext
*tc
, JSAtom
*atom
, uintN lambda
)
1693 JS_ASSERT((lambda
& ~JSFUN_LAMBDA
) == 0);
1696 * Find the global compilation context in order to pre-set the newborn
1697 * function's parent slot to tc->scopeChain. If the global context is a
1698 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1699 * clear parent and proto.
1703 parent
= tc
->inFunction() ? NULL
: tc
->scopeChain
;
1705 fun
= js_NewFunction(context
, NULL
, NULL
, 0, JSFUN_INTERPRETED
| lambda
,
1708 if (fun
&& !tc
->compileAndGo()) {
1709 FUN_OBJECT(fun
)->clearParent();
1710 FUN_OBJECT(fun
)->clearProto();
1716 MatchOrInsertSemicolon(JSContext
*cx
, TokenStream
*ts
)
1718 TokenKind tt
= ts
->peekTokenSameLine(TSF_OPERAND
);
1719 if (tt
== TOK_ERROR
)
1721 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1722 ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
, JSMSG_SEMI_BEFORE_STMNT
);
1725 (void) ts
->matchToken(TOK_SEMI
);
1730 Parser::analyzeFunctions(JSFunctionBox
*funbox
, uint32
& tcflags
)
1732 if (!markFunArgs(funbox
, tcflags
))
1734 setFunctionKinds(funbox
, tcflags
);
1739 * Mark as funargs any functions that reach up to one or more upvars across an
1740 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1742 * function f(o, p) {
1743 * o.m = function o_m(a) {
1744 * function g() { return p; }
1745 * function h() { return a; }
1750 * but without this extra marking phase, function g will not be marked as a
1751 * funarg since it is called from within its parent scope. But g reaches up to
1752 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1753 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1754 * nor uses an upvar "above" o_m's level.
1756 * If function g itself contained lambdas that contained non-lambdas that reach
1757 * up above its level, then those non-lambdas would have to be marked too. This
1758 * process is potentially exponential in the number of functions, but generally
1759 * not so complex. But it can't be done during a single recursive traversal of
1760 * the funbox tree, so we must use a work queue.
1762 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1763 * between the static level of the bodies of funbox and its peers (which must
1764 * be funbox->level + 1), and the static level of the nearest upvar among all
1765 * the upvars contained by funbox and its peers. If there are no upvars, return
1766 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1769 FindFunArgs(JSFunctionBox
*funbox
, int level
, JSFunctionBoxQueue
*queue
)
1771 uintN allskipmin
= FREE_STATIC_LEVEL
;
1774 JSParseNode
*fn
= funbox
->node
;
1775 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
1776 int fnlevel
= level
;
1779 * An eval can leak funbox, functions along its ancestor line, and its
1780 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1781 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1782 * already been marked as funargs by this point. Therefore we have to
1783 * flag only funbox->node and funbox->kids' nodes here.
1785 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1787 for (JSFunctionBox
*kid
= funbox
->kids
; kid
; kid
= kid
->siblings
)
1788 kid
->node
->setFunArg();
1792 * Compute in skipmin the least distance from fun's static level up to
1793 * an upvar, whether used directly by fun, or indirectly by a function
1796 uintN skipmin
= FREE_STATIC_LEVEL
;
1797 JSParseNode
*pn
= fn
->pn_body
;
1799 if (pn
->pn_type
== TOK_UPVARS
) {
1800 JSAtomList
upvars(pn
->pn_names
);
1801 JS_ASSERT(upvars
.count
!= 0);
1803 JSAtomListIterator
iter(&upvars
);
1804 JSAtomListElement
*ale
;
1806 while ((ale
= iter()) != NULL
) {
1807 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1809 if (!lexdep
->isFreeVar()) {
1810 uintN upvarLevel
= lexdep
->frameLevel();
1812 if (int(upvarLevel
) <= fnlevel
)
1815 uintN skip
= (funbox
->level
+ 1) - upvarLevel
;
1823 * If this function escapes, whether directly (the parser detects such
1824 * escapes) or indirectly (because this non-escaping function uses an
1825 * upvar that reaches across an outer function boundary where the outer
1826 * function escapes), enqueue it for further analysis, and bump fnlevel
1827 * to trap any non-escaping children.
1829 if (fn
->isFunArg()) {
1830 queue
->push(funbox
);
1831 fnlevel
= int(funbox
->level
);
1835 * Now process the current function's children, and recalibrate their
1836 * cumulative skipmin to be relative to the current static level.
1839 uintN kidskipmin
= FindFunArgs(funbox
->kids
, fnlevel
, queue
);
1841 JS_ASSERT(kidskipmin
!= 0);
1842 if (kidskipmin
!= FREE_STATIC_LEVEL
) {
1844 if (kidskipmin
!= 0 && kidskipmin
< skipmin
)
1845 skipmin
= kidskipmin
;
1850 * Finally, after we've traversed all of the current function's kids,
1851 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1852 * with allskipmin, but minimize across funbox and all of its siblings,
1853 * to compute our return value.
1855 if (skipmin
!= FREE_STATIC_LEVEL
) {
1856 fun
->u
.i
.skipmin
= skipmin
;
1857 if (skipmin
< allskipmin
)
1858 allskipmin
= skipmin
;
1860 } while ((funbox
= funbox
->siblings
) != NULL
);
1866 Parser::markFunArgs(JSFunctionBox
*funbox
, uintN tcflags
)
1868 JSFunctionBoxQueue queue
;
1869 if (!queue
.init(functionCount
))
1872 FindFunArgs(funbox
, -1, &queue
);
1873 while ((funbox
= queue
.pull()) != NULL
) {
1874 JSParseNode
*fn
= funbox
->node
;
1875 JS_ASSERT(fn
->isFunArg());
1877 JSParseNode
*pn
= fn
->pn_body
;
1878 if (pn
->pn_type
== TOK_UPVARS
) {
1879 JSAtomList
upvars(pn
->pn_names
);
1880 JS_ASSERT(upvars
.count
!= 0);
1882 JSAtomListIterator
iter(&upvars
);
1883 JSAtomListElement
*ale
;
1885 while ((ale
= iter()) != NULL
) {
1886 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1888 if (!lexdep
->isFreeVar() &&
1889 !lexdep
->isFunArg() &&
1890 (lexdep
->kind() == JSDefinition::FUNCTION
||
1891 PN_OP(lexdep
) == JSOP_CALLEE
)) {
1893 * Mark this formerly-Algol-like function as an escaping
1894 * function (i.e., as a funarg), because it is used from a
1895 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1898 * Progress is guaranteed because we set the funarg flag
1899 * here, which suppresses revisiting this function (thanks
1900 * to the !lexdep->isFunArg() test just above).
1902 lexdep
->setFunArg();
1904 JSFunctionBox
*afunbox
;
1905 if (PN_OP(lexdep
) == JSOP_CALLEE
) {
1907 * A named function expression will not appear to be a
1908 * funarg if it is immediately applied. However, if its
1909 * name is used in an escaping function nested within
1910 * it, then it must become flagged as a funarg again.
1914 uintN calleeLevel
= UPVAR_FRAME_SKIP(lexdep
->pn_cookie
);
1915 uintN staticLevel
= afunbox
->level
+ 1U;
1916 while (staticLevel
!= calleeLevel
) {
1917 afunbox
= afunbox
->parent
;
1920 afunbox
->node
->setFunArg();
1922 afunbox
= lexdep
->pn_funbox
;
1924 queue
.push(afunbox
);
1927 * Walk over nested functions again, now that we have
1928 * changed the level across which it is unsafe to access
1929 * upvars using the runtime dynamic link (frame chain).
1932 FindFunArgs(afunbox
->kids
, afunbox
->level
, &queue
);
1941 MinBlockId(JSParseNode
*fn
, uint32 id
)
1943 if (fn
->pn_blockid
< id
)
1946 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
1947 if (pn
->pn_blockid
< id
)
1955 CanFlattenUpvar(JSDefinition
*dn
, JSFunctionBox
*funbox
, uint32 tcflags
)
1958 * Consider the current function (the lambda, innermost below) using a var
1959 * x defined two static levels up:
1965 * return function () { return x; };
1970 * So long as (1) the initialization in 'var x = 42' dominates all uses of
1971 * g and (2) x is not reassigned, it is safe to optimize the lambda to a
1972 * flat closure. Uncommenting the early call to g makes this optimization
1973 * unsafe (z could name a global setter that calls its argument).
1975 JSFunctionBox
*afunbox
= funbox
;
1976 uintN dnLevel
= dn
->frameLevel();
1978 JS_ASSERT(dnLevel
<= funbox
->level
);
1979 while (afunbox
->level
!= dnLevel
) {
1980 afunbox
= afunbox
->parent
;
1983 * NB: afunbox can't be null because we are sure to find a function box
1984 * whose level == dnLevel before we would try to walk above the root of
1985 * the funbox tree. See bug 493260 comments 16-18.
1987 * Assert but check anyway, to protect future changes that bind eval
1988 * upvars in the parser.
1993 * If this function is reaching up across an enclosing funarg, then we
1994 * cannot copy dn's value into a flat closure slot (the display stops
1995 * working once the funarg escapes).
1997 if (!afunbox
|| afunbox
->node
->isFunArg())
2001 * Reaching up for dn across a generator also means we can't flatten,
2002 * since the generator iterator does not run until later, in general.
2005 if (afunbox
->tcflags
& TCF_FUN_IS_GENERATOR
)
2010 * If afunbox's function (which is at the same level as dn) is in a loop,
2011 * pessimistically assume the variable initializer may be in the same loop.
2012 * A flat closure would then be unsafe, as the captured variable could be
2013 * assigned after the closure is created. See bug 493232.
2015 if (afunbox
->inLoop
)
2019 * |with| and eval used as an operator defeat lexical scoping: they can be
2020 * used to assign to any in-scope variable. Therefore they must disable
2021 * flat closures that use such upvars. The parser detects these as special
2022 * forms and marks the function heavyweight.
2024 if ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
) & TCF_FUN_HEAVYWEIGHT
)
2028 * If afunbox's function is not a lambda, it will be hoisted, so it could
2029 * capture the undefined value that by default initializes var, let, and
2030 * const bindings. And if dn is a function that comes at (meaning a
2031 * function refers to its own name) or strictly after afunbox, we also
2032 * defeat the flat closure optimization for this dn.
2034 JSFunction
*afun
= (JSFunction
*) afunbox
->object
;
2035 if (!(afun
->flags
& JSFUN_LAMBDA
)) {
2036 if (dn
->isBindingForm() || dn
->pn_pos
>= afunbox
->node
->pn_pos
)
2040 if (!dn
->isInitialized())
2043 JSDefinition::Kind dnKind
= dn
->kind();
2044 if (dnKind
!= JSDefinition::CONST
) {
2045 if (dn
->isAssigned())
2049 * Any formal could be mutated behind our back via the arguments
2050 * object, so deoptimize if the outer function uses arguments.
2052 * In a Function constructor call where the final argument -- the body
2053 * source for the function to create -- contains a nested function
2054 * definition or expression, afunbox->parent will be null. The body
2055 * source might use |arguments| outside of any nested functions it may
2056 * contain, so we have to check the tcflags parameter that was passed
2057 * in from Compiler::compileFunctionBody.
2059 if (dnKind
== JSDefinition::ARG
&&
2060 ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
) & TCF_FUN_USES_ARGUMENTS
)) {
2066 * Check quick-and-dirty dominance relation. Function definitions dominate
2067 * their uses thanks to hoisting. Other binding forms hoist as undefined,
2068 * of course, so check forward-reference and blockid relations.
2070 if (dnKind
!= JSDefinition::FUNCTION
) {
2072 * Watch out for code such as
2076 * var jQuery = ... = function (...) {
2077 * return new jQuery.foo.bar(baz);
2082 * where the jQuery variable is not reassigned, but of course is not
2083 * initialized at the time that the would-be-flat closure containing
2084 * the jQuery upvar is formed.
2086 if (dn
->pn_pos
.end
>= afunbox
->node
->pn_pos
.end
)
2088 if (!MinBlockId(afunbox
->node
, dn
->pn_blockid
))
2095 FlagHeavyweights(JSDefinition
*dn
, JSFunctionBox
*funbox
, uint32
& tcflags
)
2097 uintN dnLevel
= dn
->frameLevel();
2099 while ((funbox
= funbox
->parent
) != NULL
) {
2101 * Notice that funbox->level is the static level of the definition or
2102 * expression of the function parsed into funbox, not the static level
2103 * of its body. Therefore we must add 1 to match dn's level to find the
2104 * funbox whose body contains the dn definition.
2106 if (funbox
->level
+ 1U == dnLevel
|| (dnLevel
== 0 && dn
->isLet())) {
2107 funbox
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2110 funbox
->tcflags
|= TCF_FUN_ENTRAINS_SCOPES
;
2113 if (!funbox
&& (tcflags
& TCF_IN_FUNCTION
))
2114 tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2118 DeoptimizeUsesWithin(JSDefinition
*dn
, JSFunctionBox
*funbox
, uint32
& tcflags
)
2120 uintN ndeoptimized
= 0;
2121 const TokenPos
&pos
= funbox
->node
->pn_body
->pn_pos
;
2123 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
2124 JS_ASSERT(pnu
->pn_used
);
2125 JS_ASSERT(!pnu
->pn_defn
);
2126 if (pnu
->pn_pos
.begin
>= pos
.begin
&& pnu
->pn_pos
.end
<= pos
.end
) {
2127 pnu
->pn_dflags
|= PND_DEOPTIMIZED
;
2132 if (ndeoptimized
!= 0)
2133 FlagHeavyweights(dn
, funbox
, tcflags
);
2137 Parser::setFunctionKinds(JSFunctionBox
*funbox
, uint32
& tcflags
)
2139 #ifdef JS_FUNCTION_METERING
2140 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
2142 # define FUN_METER(x) ((void)0)
2146 JSParseNode
*fn
= funbox
->node
;
2147 JSParseNode
*pn
= fn
->pn_body
;
2150 setFunctionKinds(funbox
->kids
, tcflags
);
2153 * We've unwound from recursively setting our kids' kinds, which
2154 * also classifies enclosing functions holding upvars referenced in
2155 * those descendants' bodies. So now we can check our "methods".
2157 * Despecialize from branded method-identity-based shape to sprop-
2158 * or slot-based shape if this function smells like a constructor
2159 * and too many of its methods are *not* joinable null closures
2160 * (i.e., they have one or more upvars fetched via the display).
2162 JSParseNode
*pn2
= pn
;
2163 if (PN_TYPE(pn2
) == TOK_UPVARS
)
2165 if (PN_TYPE(pn2
) == TOK_ARGSBODY
)
2168 #if JS_HAS_EXPR_CLOSURES
2169 if (PN_TYPE(pn2
) == TOK_LC
)
2171 if (!(funbox
->tcflags
& TCF_RETURN_EXPR
)) {
2172 uintN methodSets
= 0, slowMethodSets
= 0;
2174 for (JSParseNode
*method
= funbox
->methods
; method
; method
= method
->pn_link
) {
2175 JS_ASSERT(PN_OP(method
) == JSOP_LAMBDA
|| PN_OP(method
) == JSOP_LAMBDA_FC
);
2177 if (!method
->pn_funbox
->joinable())
2181 if (funbox
->shouldUnbrand(methodSets
, slowMethodSets
))
2182 funbox
->tcflags
|= TCF_FUN_UNBRAND_THIS
;
2186 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
2188 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
2191 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
2193 } else if (pn
->pn_type
!= TOK_UPVARS
) {
2195 * No lexical dependencies => null closure, for best performance.
2196 * A null closure needs no scope chain, but alas we've coupled
2197 * principals-finding to scope (for good fundamental reasons, but
2198 * the implementation overloads the parent slot and we should fix
2199 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2201 * In more detail: the ES3 spec allows the implementation to create
2202 * "joined function objects", or not, at its discretion. But real-
2203 * world implementations always create unique function objects for
2204 * closures, and this can be detected via mutation. Open question:
2205 * do popular implementations create unique function objects for
2208 * FIXME: bug 476950.
2210 FUN_METER(nofreeupvar
);
2211 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2213 JSAtomList
upvars(pn
->pn_names
);
2214 JS_ASSERT(upvars
.count
!= 0);
2216 JSAtomListIterator
iter(&upvars
);
2217 JSAtomListElement
*ale
;
2219 if (!fn
->isFunArg()) {
2221 * This function is Algol-like, it never escapes. So long as it
2222 * does not assign to outer variables, it needs only an upvars
2223 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
2224 * bytecode to reach up the frame stack at runtime based on
2225 * those upvars' cookies.
2227 * Any assignments to upvars from functions called by this one
2228 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
2229 * which load from stack homes when interpreting or from native
2230 * stack slots when executing a trace.
2232 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
2233 * nested function to assign to an outer lexical variable, so
2234 * we defer adding yet more code footprint in the absence of
2235 * evidence motivating these opcodes.
2237 bool mutation
= !!(funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
);
2241 * Check that at least one outer lexical binding was assigned
2242 * to (global variables don't count). This is conservative: we
2243 * could limit assignments to those in the current function,
2244 * but that's too much work. As with flat closures (handled
2245 * below), we optimize for the case where outer bindings are
2246 * not reassigned anywhere.
2248 while ((ale
= iter()) != NULL
) {
2249 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2251 if (!lexdep
->isFreeVar()) {
2252 JS_ASSERT(lexdep
->frameLevel() <= funbox
->level
);
2254 if (lexdep
->isAssigned())
2262 FUN_METER(onlyfreevar
);
2263 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2264 } else if (!mutation
&&
2265 !(funbox
->tcflags
& (TCF_FUN_IS_GENERATOR
| TCF_FUN_ENTRAINS_SCOPES
))) {
2267 * Algol-like functions can read upvars using the dynamic
2268 * link (cx->fp/fp->down), optimized using the cx->display
2269 * lookup table indexed by static level. They do not need
2270 * to entrain and search their environment objects.
2273 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2275 if (!(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
))
2276 FUN_METER(setupvar
);
2279 uintN nupvars
= 0, nflattened
= 0;
2282 * For each lexical dependency from this closure to an outer
2283 * binding, analyze whether it is safe to copy the binding's
2284 * value into a flat closure slot when the closure is formed.
2286 while ((ale
= iter()) != NULL
) {
2287 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2289 if (!lexdep
->isFreeVar()) {
2291 if (CanFlattenUpvar(lexdep
, funbox
, tcflags
)) {
2295 DeoptimizeUsesWithin(lexdep
, funbox
, tcflags
);
2300 FUN_METER(onlyfreevar
);
2301 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2302 } else if (nflattened
== nupvars
) {
2303 /* FIXME bug 545759: to test nflattened != 0 */
2305 * We made it all the way through the upvar loop, so it's
2306 * safe to optimize to a flat closure.
2309 FUN_SET_KIND(fun
, JSFUN_FLAT_CLOSURE
);
2310 switch (PN_OP(fn
)) {
2312 fn
->pn_op
= JSOP_DEFFUN_FC
;
2314 case JSOP_DEFLOCALFUN
:
2315 fn
->pn_op
= JSOP_DEFLOCALFUN_FC
;
2318 fn
->pn_op
= JSOP_LAMBDA_FC
;
2321 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2322 JS_ASSERT(PN_OP(fn
) == JSOP_NOP
);
2325 FUN_METER(badfunarg
);
2330 if (FUN_KIND(fun
) == JSFUN_INTERPRETED
&& pn
->pn_type
== TOK_UPVARS
) {
2332 * One or more upvars cannot be safely snapshot into a flat
2333 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2334 * all upvars, and for each non-free upvar, ensure that its
2335 * containing function has been flagged as heavyweight.
2337 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2338 * generating any code for a tree of nested functions.
2340 JSAtomList
upvars(pn
->pn_names
);
2341 JS_ASSERT(upvars
.count
!= 0);
2343 JSAtomListIterator
iter(&upvars
);
2344 JSAtomListElement
*ale
;
2346 while ((ale
= iter()) != NULL
) {
2347 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2348 if (!lexdep
->isFreeVar())
2349 FlagHeavyweights(lexdep
, funbox
, tcflags
);
2353 funbox
= funbox
->siblings
;
2361 const char js_argument_str
[] = "argument";
2362 const char js_variable_str
[] = "variable";
2363 const char js_unknown_str
[] = "unknown";
2366 JSDefinition::kindString(Kind kind
)
2368 static const char *table
[] = {
2369 js_var_str
, js_const_str
, js_let_str
,
2370 js_function_str
, js_argument_str
, js_unknown_str
2373 JS_ASSERT(unsigned(kind
) <= unsigned(ARG
));
2377 static JSFunctionBox
*
2378 EnterFunction(JSParseNode
*fn
, JSTreeContext
*funtc
, JSAtom
*funAtom
= NULL
,
2379 uintN lambda
= JSFUN_LAMBDA
)
2381 JSTreeContext
*tc
= funtc
->parent
;
2382 JSFunction
*fun
= tc
->parser
->newFunction(tc
, funAtom
, lambda
);
2386 /* Create box for fun->object early to protect against last-ditch GC. */
2387 JSFunctionBox
*funbox
= tc
->parser
->newFunctionBox(FUN_OBJECT(fun
), fn
, tc
);
2391 /* Initialize non-default members of funtc. */
2392 funtc
->flags
|= funbox
->tcflags
;
2393 funtc
->blockidGen
= tc
->blockidGen
;
2394 if (!GenerateBlockId(funtc
, funtc
->bodyid
))
2397 funtc
->funbox
= funbox
;
2398 if (!SetStaticLevel(funtc
, tc
->staticLevel
+ 1))
2405 LeaveFunction(JSParseNode
*fn
, JSTreeContext
*funtc
, JSAtom
*funAtom
= NULL
,
2406 uintN lambda
= JSFUN_LAMBDA
)
2408 JSTreeContext
*tc
= funtc
->parent
;
2409 tc
->blockidGen
= funtc
->blockidGen
;
2411 JSFunctionBox
*funbox
= fn
->pn_funbox
;
2412 funbox
->tcflags
|= funtc
->flags
& (TCF_FUN_FLAGS
| TCF_COMPILE_N_GO
| TCF_RETURN_EXPR
);
2414 fn
->pn_dflags
|= PND_INITIALIZED
;
2415 JS_ASSERT_IF(tc
->atTopLevel() && lambda
== 0 && funAtom
,
2416 fn
->pn_dflags
& PND_TOPLEVEL
);
2417 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
2418 fn
->pn_dflags
|= PND_BLOCKCHILD
;
2421 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2422 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2423 * params and body. We do this only if there are lexical dependencies not
2424 * satisfied by the function's declarations, to avoid penalizing functions
2425 * that use only their arguments and other local bindings.
2427 if (funtc
->lexdeps
.count
!= 0) {
2428 JSAtomListIterator
iter(&funtc
->lexdeps
);
2429 JSAtomListElement
*ale
;
2430 int foundCallee
= 0;
2432 while ((ale
= iter()) != NULL
) {
2433 JSAtom
*atom
= ALE_ATOM(ale
);
2434 JSDefinition
*dn
= ALE_DEFN(ale
);
2435 JS_ASSERT(dn
->isPlaceholder());
2437 if (atom
== funAtom
&& lambda
!= 0) {
2438 dn
->pn_op
= JSOP_CALLEE
;
2439 dn
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
->staticLevel
, CALLEE_UPVAR_SLOT
);
2440 dn
->pn_dflags
|= PND_BOUND
;
2443 * If this named function expression uses its own name other
2444 * than to call itself, flag this function specially.
2447 funbox
->tcflags
|= TCF_FUN_USES_OWN_NAME
;
2452 if (!(funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
) &&
2455 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2456 * any use of dn in funtc assigns. See NoteLValue for the easy
2457 * backward-reference case; this is the hard forward-reference
2458 * case where we pay a higher price.
2460 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
2461 if (pnu
->isAssigned() && pnu
->pn_blockid
>= funtc
->bodyid
) {
2462 funbox
->tcflags
|= TCF_FUN_SETS_OUTER_NAME
;
2468 JSAtomListElement
*outer_ale
= tc
->decls
.lookup(atom
);
2470 outer_ale
= tc
->lexdeps
.lookup(atom
);
2473 * Insert dn's uses list at the front of outer_dn's list.
2475 * Without loss of generality or correctness, we allow a dn to
2476 * be in inner and outer lexdeps, since the purpose of lexdeps
2477 * is one-pass coordination of name use and definition across
2478 * functions, and if different dn's are used we'll merge lists
2479 * when leaving the inner function.
2481 * The dn == outer_dn case arises with generator expressions
2482 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2483 * case), and nowhere else, currently.
2485 JSDefinition
*outer_dn
= ALE_DEFN(outer_ale
);
2487 if (dn
!= outer_dn
) {
2488 JSParseNode
**pnup
= &dn
->dn_uses
;
2491 while ((pnu
= *pnup
) != NULL
) {
2492 pnu
->pn_lexdef
= outer_dn
;
2493 pnup
= &pnu
->pn_link
;
2497 * Make dn be a use that redirects to outer_dn, because we
2498 * can't replace dn with outer_dn in all the pn_namesets in
2499 * the AST where it may be. Instead we make it forward to
2500 * outer_dn. See JSDefinition::resolve.
2502 *pnup
= outer_dn
->dn_uses
;
2503 outer_dn
->dn_uses
= dn
;
2504 outer_dn
->pn_dflags
|= dn
->pn_dflags
& ~PND_PLACEHOLDER
;
2505 dn
->pn_defn
= false;
2507 dn
->pn_lexdef
= outer_dn
;
2510 /* Add an outer lexical dependency for ale's definition. */
2511 outer_ale
= tc
->lexdeps
.add(tc
->parser
, atom
);
2514 ALE_SET_DEFN(outer_ale
, ALE_DEFN(ale
));
2518 if (funtc
->lexdeps
.count
- foundCallee
!= 0) {
2519 JSParseNode
*body
= fn
->pn_body
;
2521 fn
->pn_body
= NameSetNode::create(tc
);
2525 fn
->pn_body
->pn_type
= TOK_UPVARS
;
2526 fn
->pn_body
->pn_pos
= body
->pn_pos
;
2528 funtc
->lexdeps
.remove(tc
->parser
, funAtom
);
2529 fn
->pn_body
->pn_names
= funtc
->lexdeps
;
2530 fn
->pn_body
->pn_tree
= body
;
2533 funtc
->lexdeps
.clear();
2540 Parser::functionDef(uintN lambda
, bool namePermitted
)
2542 JSParseNode
*pn
, *body
, *result
;
2544 JSAtomListElement
*ale
;
2545 #if JS_HAS_DESTRUCTURING
2546 JSParseNode
*item
, *list
= NULL
;
2547 bool destructuringArg
= false;
2548 JSAtom
*duplicatedArg
= NULL
;
2552 * Save the current op for later so we can tag the created function as a
2553 * getter/setter if necessary.
2555 JSOp op
= tokenStream
.currentToken().t_op
;
2557 /* Make a TOK_FUNCTION node. */
2558 pn
= FunctionNode::create(tc
);
2562 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2565 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2566 * is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
2568 * Also treat function sub-statements (non-lambda, non-top-level functions)
2569 * as escaping funargs, since we can't statically analyze their definitions
2572 bool topLevel
= tc
->atTopLevel();
2573 pn
->pn_dflags
= (lambda
|| !topLevel
) ? PND_FUNARG
: 0;
2575 /* Scan the optional function name into funAtom. */
2576 JSAtom
*funAtom
= NULL
;
2577 if (namePermitted
) {
2578 tt
= tokenStream
.getToken(TSF_KEYWORD_IS_NAME
);
2579 if (tt
== TOK_NAME
) {
2580 funAtom
= tokenStream
.currentToken().t_atom
;
2582 if (lambda
== 0 && (context
->options
& JSOPTION_ANONFUNFIX
)) {
2583 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
2584 JSMSG_SYNTAX_ERROR
);
2587 tokenStream
.ungetToken();
2592 * Record names for function statements in tc->decls so we know when to
2593 * avoid optimizing variable references that might name a function.
2595 if (lambda
== 0 && funAtom
) {
2596 ale
= tc
->decls
.lookup(funAtom
);
2598 JSDefinition
*dn
= ALE_DEFN(ale
);
2599 JSDefinition::Kind dn_kind
= dn
->kind();
2601 JS_ASSERT(!dn
->pn_used
);
2602 JS_ASSERT(dn
->pn_defn
);
2604 if (JS_HAS_STRICT_OPTION(context
) || dn_kind
== JSDefinition::CONST
) {
2605 const char *name
= js_AtomToPrintableString(context
, funAtom
);
2607 !ReportCompileErrorNumber(context
, &tokenStream
, NULL
,
2608 (dn_kind
!= JSDefinition::CONST
)
2609 ? JSREPORT_WARNING
| JSREPORT_STRICT
2611 JSMSG_REDECLARED_VAR
,
2612 JSDefinition::kindString(dn_kind
),
2619 ALE_SET_DEFN(ale
, pn
);
2621 pn
->dn_uses
= dn
; /* dn->dn_uses is now pn_link */
2623 if (!MakeDefIntoUse(dn
, pn
, funAtom
, tc
))
2626 } else if (topLevel
) {
2628 * If this function was used before it was defined, claim the
2629 * pre-created definition node for this function that primaryExpr
2630 * put in tc->lexdeps on first forward reference, and recycle pn.
2634 ale
= tc
->lexdeps
.rawLookup(funAtom
, hep
);
2636 JSDefinition
*fn
= ALE_DEFN(ale
);
2638 JS_ASSERT(fn
->pn_defn
);
2639 fn
->pn_type
= TOK_FUNCTION
;
2640 fn
->pn_arity
= PN_FUNC
;
2641 fn
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2643 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2645 tc
->lexdeps
.rawRemove(tc
->parser
, ale
, hep
);
2646 RecycleTree(pn
, tc
);
2650 if (!Define(pn
, funAtom
, tc
))
2655 * A function nested at top level inside another's body needs only a
2656 * local variable to bind its name to its value, and not an activation
2657 * object property (it might also need the activation property, if the
2658 * outer function contains with statements, e.g., but the stack slot
2659 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2660 * JSOP_GETLOCAL bytecode).
2663 pn
->pn_dflags
|= PND_TOPLEVEL
;
2665 if (tc
->inFunction()) {
2666 JSLocalKind localKind
;
2670 * Define a local in the outer function so that BindNameToSlot
2671 * can properly optimize accesses. Note that we need a local
2672 * variable, not an argument, for the function statement. Thus
2673 * we add a variable even if a parameter with the given name
2676 localKind
= js_LookupLocal(context
, tc
->fun
, funAtom
, &index
);
2677 switch (localKind
) {
2680 index
= tc
->fun
->u
.i
.nvars
;
2681 if (!js_AddLocal(context
, tc
->fun
, funAtom
, JSLOCAL_VAR
))
2686 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
2687 pn
->pn_dflags
|= PND_BOUND
;
2696 JSTreeContext
*outertc
= tc
;
2698 /* Initialize early for possible flags mutation via destructuringExpr. */
2699 JSTreeContext
funtc(tc
->parser
);
2701 JSFunctionBox
*funbox
= EnterFunction(pn
, &funtc
, funAtom
, lambda
);
2705 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
2708 fun
->flags
|= (op
== JSOP_GETTER
) ? JSPROP_GETTER
: JSPROP_SETTER
;
2710 /* Now parse formal argument list and compute fun->nargs. */
2711 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_FORMAL
);
2712 if (!tokenStream
.matchToken(TOK_RP
)) {
2714 tt
= tokenStream
.getToken();
2716 #if JS_HAS_DESTRUCTURING
2721 JSParseNode
*lhs
, *rhs
;
2724 /* See comment below in the TOK_NAME case. */
2726 goto report_dup_and_destructuring
;
2727 destructuringArg
= true;
2730 * A destructuring formal parameter turns into one or more
2731 * local variables initialized from properties of a single
2732 * anonymous positional parameter, so here we must tweak our
2733 * binder and its data.
2736 data
.op
= JSOP_DEFVAR
;
2737 data
.binder
= BindDestructuringArg
;
2738 lhs
= destructuringExpr(&data
, tt
);
2743 * Adjust fun->nargs to count the single anonymous positional
2744 * parameter that is to be destructured.
2747 if (!js_AddLocal(context
, fun
, NULL
, JSLOCAL_ARG
))
2751 * Synthesize a destructuring assignment from the single
2752 * anonymous positional parameter into the destructuring
2753 * left-hand-side expression and accumulate it in list.
2755 rhs
= NameNode::create(context
->runtime
->atomState
.emptyAtom
, &funtc
);
2758 rhs
->pn_type
= TOK_NAME
;
2759 rhs
->pn_op
= JSOP_GETARG
;
2760 rhs
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
.staticLevel
, slot
);
2761 rhs
->pn_dflags
|= PND_BOUND
;
2763 item
= JSParseNode::newBinaryOrAppend(TOK_ASSIGN
, JSOP_NOP
, lhs
, rhs
, &funtc
);
2767 list
= ListNode::create(&funtc
);
2770 list
->pn_type
= TOK_COMMA
;
2776 #endif /* JS_HAS_DESTRUCTURING */
2780 JSAtom
*atom
= tokenStream
.currentToken().t_atom
;
2781 if (!DefineArg(pn
, atom
, fun
->nargs
, &funtc
))
2783 #ifdef JS_HAS_DESTRUCTURING
2785 * ECMA-262 requires us to support duplicate parameter names, but if the
2786 * parameter list includes destructuring, we consider the code to have
2787 * opted in to higher standards, and forbid duplicates. We may see a
2788 * destructuring parameter later, so always note duplicates now.
2790 * Duplicates are warned about (strict option) or cause errors (strict
2791 * mode code), but we do those tests in one place below, after having
2794 if (js_LookupLocal(context
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2795 duplicatedArg
= atom
;
2796 if (destructuringArg
)
2797 goto report_dup_and_destructuring
;
2800 if (!js_AddLocal(context
, fun
, atom
, JSLOCAL_ARG
))
2806 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
2807 JSMSG_MISSING_FORMAL
);
2812 #if JS_HAS_DESTRUCTURING
2813 report_dup_and_destructuring
:
2814 JSDefinition
*dn
= ALE_DEFN(funtc
.decls
.lookup(duplicatedArg
));
2815 ReportCompileErrorNumber(context
, &tokenStream
, dn
, JSREPORT_ERROR
,
2816 JSMSG_DESTRUCT_DUP_ARG
);
2820 } while (tokenStream
.matchToken(TOK_COMMA
));
2822 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FORMAL
);
2825 #if JS_HAS_EXPR_CLOSURES
2826 tt
= tokenStream
.getToken(TSF_OPERAND
);
2828 tokenStream
.ungetToken();
2829 fun
->flags
|= JSFUN_EXPR_CLOSURE
;
2832 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_BODY
);
2835 body
= functionBody();
2839 if (!CheckStrictBinding(context
, &funtc
, funAtom
, pn
))
2842 if (!CheckStrictFormals(context
, &funtc
, fun
, pn
))
2845 #if JS_HAS_EXPR_CLOSURES
2847 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2848 else if (lambda
== 0 && !MatchOrInsertSemicolon(context
, &tokenStream
))
2851 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2853 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
2855 #if JS_HAS_DESTRUCTURING
2857 * If there were destructuring formal parameters, prepend the initializing
2858 * comma expression that we synthesized to body. If the body is a lexical
2859 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2860 * parameter destructuring code without bracing the decompilation of the
2861 * function body's lexical scope.
2864 if (body
->pn_arity
!= PN_LIST
) {
2867 block
= ListNode::create(outertc
);
2870 block
->pn_type
= TOK_SEQ
;
2871 block
->pn_pos
= body
->pn_pos
;
2872 block
->initList(body
);
2877 item
= UnaryNode::create(outertc
);
2881 item
->pn_type
= TOK_SEMI
;
2882 item
->pn_pos
.begin
= item
->pn_pos
.end
= body
->pn_pos
.begin
;
2883 item
->pn_kid
= list
;
2884 item
->pn_next
= body
->pn_head
;
2885 body
->pn_head
= item
;
2886 if (body
->pn_tail
== &body
->pn_head
)
2887 body
->pn_tail
= &item
->pn_next
;
2889 body
->pn_xflags
|= PNX_DESTRUCT
;
2894 * If we collected flags that indicate nested heavyweight functions, or
2895 * this function contains heavyweight-making statements (with statement,
2896 * visible eval call, or assignment to 'arguments'), flag the function as
2897 * heavyweight (requiring a call object per invocation).
2899 if (funtc
.flags
& TCF_FUN_HEAVYWEIGHT
) {
2900 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
2901 outertc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2904 * If this function is a named statement function not at top-level
2905 * (i.e. not a top-level function definiton or expression), then our
2906 * enclosing function, if any, must be heavyweight.
2908 if (!topLevel
&& lambda
== 0 && funAtom
)
2909 outertc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2915 * ECMA ed. 3 standard: function expression, possibly anonymous.
2918 } else if (!funAtom
) {
2920 * If this anonymous function definition is *not* embedded within a
2921 * larger expression, we treat it as an expression statement, not as
2922 * a function declaration -- and not as a syntax error (as ECMA-262
2923 * Edition 3 would have it). Backward compatibility must trump all,
2924 * unless JSOPTION_ANONFUNFIX is set.
2926 result
= UnaryNode::create(outertc
);
2929 result
->pn_type
= TOK_SEMI
;
2930 result
->pn_pos
= pn
->pn_pos
;
2931 result
->pn_kid
= pn
;
2933 } else if (!topLevel
) {
2935 * ECMA ed. 3 extension: a function expression statement not at the
2936 * top level, e.g., in a compound statement such as the "then" part
2937 * of an "if" statement, binds a closure only if control reaches that
2945 funbox
->kids
= funtc
.functionList
;
2947 pn
->pn_funbox
= funbox
;
2950 pn
->pn_body
->append(body
);
2951 pn
->pn_body
->pn_pos
= body
->pn_pos
;
2956 pn
->pn_blockid
= outertc
->blockid();
2958 if (!LeaveFunction(pn
, &funtc
, funAtom
, lambda
))
2961 /* If the surrounding function is not strict code, reset the lexer. */
2962 if (!(outertc
->flags
& TCF_STRICT_MODE_CODE
))
2963 tokenStream
.setStrictMode(false);
2969 Parser::functionStmt()
2971 return functionDef(0, true);
2975 Parser::functionExpr()
2977 return functionDef(JSFUN_LAMBDA
, true);
2981 * Recognize Directive Prologue members and directives. Assuming pn
2982 * is a candidate for membership in a directive prologue, return
2983 * true if it is in fact a member. Recognize directives and set
2984 * tc's flags accordingly.
2986 * Note that the following is a strict mode function:
2989 * "blah" // inserted semi colon
2995 * That is, a statement can be a Directive Prologue member, even
2996 * if it can't possibly be a directive, now or in the future.
2999 Parser::recognizeDirectivePrologue(JSParseNode
*pn
)
3001 if (!pn
->isDirectivePrologueMember())
3003 if (pn
->isDirective()) {
3004 JSAtom
*directive
= pn
->pn_kid
->pn_atom
;
3005 if (directive
== context
->runtime
->atomState
.useStrictAtom
) {
3006 tc
->flags
|= TCF_STRICT_MODE_CODE
;
3007 tokenStream
.setStrictMode();
3014 * Parse the statements in a block, creating a TOK_LC node that lists the
3015 * statements' trees. If called from block-parsing code, the caller must
3016 * match { before and } after.
3019 Parser::statements()
3021 JSParseNode
*pn
, *pn2
, *saveBlock
;
3023 bool inDirectivePrologue
= tc
->atTopLevel();
3025 JS_CHECK_RECURSION(context
, return NULL
);
3027 pn
= ListNode::create(tc
);
3030 pn
->pn_type
= TOK_LC
;
3032 pn
->pn_blockid
= tc
->blockid();
3033 saveBlock
= tc
->blockNode
;
3037 tt
= tokenStream
.peekToken(TSF_OPERAND
);
3038 if (tt
<= TOK_EOF
|| tt
== TOK_RC
) {
3039 if (tt
== TOK_ERROR
) {
3040 if (tokenStream
.isEOF())
3041 tokenStream
.setUnexpectedEOF();
3048 if (tokenStream
.isEOF())
3049 tokenStream
.setUnexpectedEOF();
3053 if (inDirectivePrologue
)
3054 inDirectivePrologue
= recognizeDirectivePrologue(pn2
);
3056 if (pn2
->pn_type
== TOK_FUNCTION
) {
3058 * PNX_FUNCDEFS notifies the emitter that the block contains top-
3059 * level function definitions that should be processed before the
3062 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3063 * is relevant only for function definitions not at top-level,
3064 * which we call function statements.
3066 if (tc
->atTopLevel())
3067 pn
->pn_xflags
|= PNX_FUNCDEFS
;
3069 tc
->flags
|= TCF_HAS_FUNCTION_STMT
;
3075 * Handle the case where there was a let declaration under this block. If
3076 * it replaced tc->blockNode with a new block node then we must refresh pn
3077 * and then restore tc->blockNode.
3079 if (tc
->blockNode
!= pn
)
3081 tc
->blockNode
= saveBlock
;
3083 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
3092 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
3093 pn
= parenExpr(NULL
, NULL
);
3096 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
3098 /* Check for (a = b) and warn about possible (a == b) mistype. */
3099 if (pn
->pn_type
== TOK_ASSIGN
&&
3100 pn
->pn_op
== JSOP_NOP
&&
3102 !ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_WARNING
| JSREPORT_STRICT
,
3103 JSMSG_EQUAL_AS_ASSIGN
, "")) {
3110 MatchLabel(JSContext
*cx
, TokenStream
*ts
, JSParseNode
*pn
)
3115 tt
= ts
->peekTokenSameLine();
3116 if (tt
== TOK_ERROR
)
3118 if (tt
== TOK_NAME
) {
3119 (void) ts
->getToken();
3120 label
= ts
->currentToken().t_atom
;
3124 pn
->pn_atom
= label
;
3129 BindLet(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3133 JSAtomListElement
*ale
;
3137 * Top-level 'let' is the same as 'var' currently -- this may change in a
3138 * successor standard to ES3.1 that specifies 'let'.
3140 JS_ASSERT(!tc
->atTopLevel());
3143 if (!CheckStrictBinding(cx
, tc
, atom
, pn
))
3146 blockObj
= tc
->blockChain
;
3147 ale
= tc
->decls
.lookup(atom
);
3148 if (ale
&& ALE_DEFN(ale
)->pn_blockid
== tc
->blockid()) {
3149 const char *name
= js_AtomToPrintableString(cx
, atom
);
3151 ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
,
3152 JSREPORT_ERROR
, JSMSG_REDECLARED_VAR
,
3153 (ale
&& ALE_DEFN(ale
)->isConst())
3161 n
= OBJ_BLOCK_COUNT(cx
, blockObj
);
3162 if (n
== JS_BIT(16)) {
3163 ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
,
3164 JSREPORT_ERROR
, data
->let
.overflow
);
3169 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3170 * This is balanced by PopStatement, defined immediately below.
3172 if (!Define(pn
, atom
, tc
, true))
3176 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3177 * upvar cookie whose skip tells the current static level. The emitter will
3178 * adjust the node's slot based on its stack depth model -- and, for global
3179 * and eval code, Compiler::compileScript will adjust the slot again to
3180 * include script->nfixed.
3182 pn
->pn_op
= JSOP_GETLOCAL
;
3183 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, n
);
3184 pn
->pn_dflags
|= PND_LET
| PND_BOUND
;
3187 * Define the let binding's property before storing pn in a reserved slot,
3188 * since block_reserveSlots depends on blockObj->scope()->entryCount.
3190 if (!js_DefineBlockVariable(cx
, blockObj
, ATOM_TO_JSID(atom
), n
))
3194 * Store pn temporarily in what would be reserved slots in a cloned block
3195 * object (once the prototype's final population is known, after all 'let'
3196 * bindings for this block have been parsed). We will free these reserved
3197 * slots in jsemit.cpp:EmitEnterBlock.
3199 uintN slot
= JSSLOT_FREE(&js_BlockClass
) + n
;
3200 if (slot
>= blockObj
->numSlots() &&
3201 !blockObj
->growSlots(cx
, slot
+ 1)) {
3204 blockObj
->scope()->freeslot
= slot
+ 1;
3205 blockObj
->setSlot(slot
, PRIVATE_TO_JSVAL(pn
));
3210 PopStatement(JSTreeContext
*tc
)
3212 JSStmtInfo
*stmt
= tc
->topStmt
;
3214 if (stmt
->flags
& SIF_SCOPE
) {
3215 JSObject
*obj
= stmt
->blockObj
;
3216 JSScope
*scope
= obj
->scope();
3217 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj
));
3219 for (JSScopeProperty
*sprop
= scope
->lastProperty(); sprop
; sprop
= sprop
->parent
) {
3220 JSAtom
*atom
= JSID_TO_ATOM(sprop
->id
);
3222 /* Beware the empty destructuring dummy. */
3223 if (atom
== tc
->parser
->context
->runtime
->atomState
.emptyAtom
)
3225 tc
->decls
.remove(tc
->parser
, atom
);
3228 js_PopStatement(tc
);
3232 OuterLet(JSTreeContext
*tc
, JSStmtInfo
*stmt
, JSAtom
*atom
)
3234 while (stmt
->downScope
) {
3235 stmt
= js_LexicalLookup(tc
, atom
, NULL
, stmt
->downScope
);
3238 if (stmt
->type
== STMT_BLOCK
)
3245 * If we are generating global or eval-called-from-global code, bind a "gvar"
3246 * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
3247 * global variable access by memoizing name-to-slot mappings during execution
3248 * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3249 * can't be done due to a pre-existing property of the same name as the var or
3250 * const but incompatible attributes/getter/setter/etc, these ops devolve to
3253 * For now, don't try to lookup eval frame variables at compile time. This is
3254 * sub-optimal: we could handle eval-called-from-global-code gvars since eval
3255 * gets its own script and frame. The eval-from-function-code case is harder,
3256 * since functions do not atomize gvars and then reserve their atom indexes as
3257 * stack frame slots.
3260 BindGvar(JSParseNode
*pn
, JSTreeContext
*tc
, bool inWith
= false)
3262 JS_ASSERT(pn
->pn_op
== JSOP_NAME
);
3263 JS_ASSERT(!tc
->inFunction());
3265 if (tc
->compiling() && !tc
->parser
->callerFrame
) {
3266 JSCodeGenerator
*cg
= (JSCodeGenerator
*) tc
;
3268 /* Index pn->pn_atom so we can map fast global number to name. */
3269 JSAtomListElement
*ale
= cg
->atomList
.add(tc
->parser
, pn
->pn_atom
);
3273 /* Defend against cg->ngvars 16-bit overflow. */
3274 uintN slot
= ALE_INDEX(ale
);
3275 if ((slot
+ 1) >> 16)
3278 if ((uint16
)(slot
+ 1) > cg
->ngvars
)
3279 cg
->ngvars
= (uint16
)(slot
+ 1);
3282 pn
->pn_op
= JSOP_GETGVAR
;
3283 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, slot
);
3284 pn
->pn_dflags
|= PND_BOUND
| PND_GVAR
;
3292 BindVarOrConst(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3294 JSParseNode
*pn
= data
->pn
;
3296 /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
3297 pn
->pn_op
= JSOP_NAME
;
3299 if (!CheckStrictBinding(cx
, tc
, atom
, pn
))
3302 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
3304 if (stmt
&& stmt
->type
== STMT_WITH
) {
3305 data
->fresh
= false;
3306 return tc
->inFunction() || BindGvar(pn
, tc
, true);
3309 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
3313 JSDefinition
*dn
= ale
? ALE_DEFN(ale
) : NULL
;
3314 JSDefinition::Kind dn_kind
= dn
? dn
->kind() : JSDefinition::VAR
;
3317 if (dn_kind
== JSDefinition::ARG
) {
3318 name
= js_AtomToPrintableString(cx
, atom
);
3322 if (op
== JSOP_DEFCONST
) {
3323 ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
,
3324 JSREPORT_ERROR
, JSMSG_REDECLARED_PARAM
,
3328 if (!ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
,
3329 JSREPORT_WARNING
| JSREPORT_STRICT
,
3330 JSMSG_VAR_HIDES_ARG
, name
)) {
3334 bool error
= (op
== JSOP_DEFCONST
||
3335 dn_kind
== JSDefinition::CONST
||
3336 (dn_kind
== JSDefinition::LET
&&
3337 (stmt
->type
!= STMT_CATCH
|| OuterLet(tc
, stmt
, atom
))));
3339 if (JS_HAS_STRICT_OPTION(cx
)
3340 ? op
!= JSOP_DEFVAR
|| dn_kind
!= JSDefinition::VAR
3342 name
= js_AtomToPrintableString(cx
, atom
);
3344 !ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
,
3346 ? JSREPORT_WARNING
| JSREPORT_STRICT
3348 JSMSG_REDECLARED_VAR
,
3349 JSDefinition::kindString(dn_kind
),
3358 if (!Define(pn
, atom
, tc
))
3362 * A var declaration never recreates an existing binding, it restates
3363 * it and possibly reinitializes its value. Beware that if pn becomes a
3364 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3365 * const (typically a const would ;-), then pn must be rewritten into a
3366 * TOK_ASSIGN node. See Variables, further below.
3368 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3369 * There the x definition is hoisted but the x = 2 assignment mutates
3370 * the block-local binding of x.
3372 JSDefinition
*dn
= ALE_DEFN(ale
);
3374 data
->fresh
= false;
3377 /* Make pnu be a fresh name node that uses dn. */
3378 JSParseNode
*pnu
= pn
;
3381 pnu
= NameNode::create(atom
, tc
);
3386 LinkUseToDef(pnu
, dn
, tc
);
3387 pnu
->pn_op
= JSOP_NAME
;
3390 while (dn
->kind() == JSDefinition::LET
) {
3392 ale
= ALE_NEXT(ale
);
3393 } while (ale
&& ALE_ATOM(ale
) != atom
);
3400 JS_ASSERT_IF(data
->op
== JSOP_DEFCONST
,
3401 dn
->kind() == JSDefinition::CONST
);
3406 * A var or const that is shadowed by one or more let bindings of the
3407 * same name, but that has not been declared until this point, must be
3408 * hoisted above the let bindings.
3413 ale
= tc
->lexdeps
.rawLookup(atom
, hep
);
3416 tc
->lexdeps
.rawRemove(tc
->parser
, ale
, hep
);
3418 JSParseNode
*pn2
= NameNode::create(atom
, tc
);
3422 /* The token stream may be past the location for pn. */
3423 pn2
->pn_type
= TOK_NAME
;
3424 pn2
->pn_pos
= pn
->pn_pos
;
3427 pn
->pn_op
= JSOP_NAME
;
3430 ale
= tc
->decls
.add(tc
->parser
, atom
, JSAtomList::HOIST
);
3433 ALE_SET_DEFN(ale
, pn
);
3435 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
3438 if (data
->op
== JSOP_DEFCONST
)
3439 pn
->pn_dflags
|= PND_CONST
;
3441 if (!tc
->inFunction())
3442 return BindGvar(pn
, tc
);
3444 if (atom
== cx
->runtime
->atomState
.argumentsAtom
) {
3445 pn
->pn_op
= JSOP_ARGUMENTS
;
3446 pn
->pn_dflags
|= PND_BOUND
;
3450 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
3451 if (localKind
== JSLOCAL_NONE
) {
3453 * Property not found in current variable scope: we have not seen this
3454 * variable before. Define a new local variable by adding a property to
3455 * the function's scope and allocating one slot in the function's vars
3456 * frame. Any locals declared in a with statement body are handled at
3457 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3458 * and heavyweight-function-local vars.
3460 localKind
= (data
->op
== JSOP_DEFCONST
) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
3462 uintN index
= tc
->fun
->u
.i
.nvars
;
3463 if (!BindLocalVariable(cx
, tc
->fun
, atom
, localKind
, false))
3465 pn
->pn_op
= JSOP_GETLOCAL
;
3466 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
3467 pn
->pn_dflags
|= PND_BOUND
;
3471 if (localKind
== JSLOCAL_ARG
) {
3472 /* We checked errors and strict warnings earlier -- see above. */
3473 JS_ASSERT(ale
&& ALE_DEFN(ale
)->kind() == JSDefinition::ARG
);
3475 /* Not an argument, must be a redeclared local var. */
3476 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
3482 MakeSetCall(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN msg
)
3486 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
3487 JS_ASSERT(pn
->pn_op
== JSOP_CALL
|| pn
->pn_op
== JSOP_EVAL
|| pn
->pn_op
== JSOP_APPLY
);
3489 if (pn2
->pn_type
== TOK_FUNCTION
&& (pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
)) {
3490 ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
, JSREPORT_ERROR
, msg
);
3493 pn
->pn_op
= JSOP_SETCALL
;
3498 NoteLValue(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN dflag
= PND_ASSIGNED
)
3501 JSDefinition
*dn
= pn
->pn_lexdef
;
3504 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3505 * occur as direct kids of the same block with no forward refs to x.
3507 if (!(dn
->pn_dflags
& (PND_INITIALIZED
| PND_CONST
| PND_PLACEHOLDER
)) &&
3508 dn
->isBlockChild() &&
3509 pn
->isBlockChild() &&
3510 dn
->pn_blockid
== pn
->pn_blockid
&&
3511 dn
->pn_pos
.end
<= pn
->pn_pos
.begin
&&
3512 dn
->dn_uses
== pn
) {
3513 dflag
= PND_INITIALIZED
;
3516 dn
->pn_dflags
|= dflag
;
3518 if (dn
->frameLevel() != tc
->staticLevel
) {
3520 * The above condition takes advantage of the all-ones nature of
3521 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3522 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3524 JS_ASSERT_IF(dn
->pn_cookie
!= FREE_UPVAR_COOKIE
,
3525 dn
->frameLevel() < tc
->staticLevel
);
3526 tc
->flags
|= TCF_FUN_SETS_OUTER_NAME
;
3530 pn
->pn_dflags
|= dflag
;
3532 if (pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
3533 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3536 #if JS_HAS_DESTRUCTURING
3539 BindDestructuringVar(JSContext
*cx
, BindData
*data
, JSParseNode
*pn
,
3545 * Destructuring is a form of assignment, so just as for an initialized
3546 * simple variable, we must check for assignment to 'arguments' and flag
3547 * the enclosing function (if any) as heavyweight.
3549 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
3551 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
3552 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3555 if (!data
->binder(cx
, data
, atom
, tc
))
3559 * Select the appropriate name-setting opcode, respecting eager selection
3560 * done by the data->binder function.
3562 if (pn
->pn_dflags
& PND_BOUND
) {
3563 pn
->pn_op
= (pn
->pn_op
== JSOP_ARGUMENTS
)
3565 : (pn
->pn_dflags
& PND_GVAR
)
3569 pn
->pn_op
= (data
->op
== JSOP_DEFCONST
)
3574 if (data
->op
== JSOP_DEFCONST
)
3575 pn
->pn_dflags
|= PND_CONST
;
3577 NoteLValue(cx
, pn
, tc
, PND_INITIALIZED
);
3582 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3583 * LHS expression except a destructuring initialiser, and R is on the stack.
3584 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3585 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3586 * then push its property name QN. At this point the stack looks like
3588 * [... R, R[P], QB, QN]
3590 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3591 * its operands with left-hand side above right-hand side:
3593 * [rval, lval, xval]
3595 * and pops all three values, setting lval[xval] = rval. But we cannot select
3596 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3597 * which can be optimized further. So we select JSOP_SETNAME.
3600 BindDestructuringLHS(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
3602 switch (pn
->pn_type
) {
3604 NoteLValue(cx
, pn
, tc
);
3609 pn
->pn_op
= JSOP_SETNAME
;
3613 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
3617 #if JS_HAS_XML_SUPPORT
3619 if (pn
->pn_op
== JSOP_XMLNAME
) {
3620 pn
->pn_op
= JSOP_BINDXMLNAME
;
3627 ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
,
3628 JSREPORT_ERROR
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
3635 typedef struct FindPropValData
{
3636 uint32 numvars
; /* # of destructuring vars in left side */
3637 uint32 maxstep
; /* max # of steps searching right side */
3638 JSDHashTable table
; /* hash table for O(1) right side search */
3641 typedef struct FindPropValEntry
{
3642 JSDHashEntryHdr hdr
;
3647 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3648 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3649 ((pnkey)->pn_type == TOK_NUMBER || \
3650 (pnkey)->pn_type == TOK_STRING || \
3651 (pnkey)->pn_type == TOK_NAME)) || \
3652 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3654 static JSDHashNumber
3655 HashFindPropValKey(JSDHashTable
*table
, const void *key
)
3657 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3659 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3660 return (pnkey
->pn_type
== TOK_NUMBER
)
3661 ? (JSDHashNumber
) JS_HASH_DOUBLE(pnkey
->pn_dval
)
3662 : ATOM_HASH(pnkey
->pn_atom
);
3666 MatchFindPropValEntry(JSDHashTable
*table
,
3667 const JSDHashEntryHdr
*entry
,
3670 const FindPropValEntry
*fpve
= (const FindPropValEntry
*)entry
;
3671 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3673 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3674 return pnkey
->pn_type
== fpve
->pnkey
->pn_type
&&
3675 ((pnkey
->pn_type
== TOK_NUMBER
)
3676 ? pnkey
->pn_dval
== fpve
->pnkey
->pn_dval
3677 : pnkey
->pn_atom
== fpve
->pnkey
->pn_atom
);
3680 static const JSDHashTableOps FindPropValOps
= {
3684 MatchFindPropValEntry
,
3685 JS_DHashMoveEntryStub
,
3686 JS_DHashClearEntryStub
,
3687 JS_DHashFinalizeStub
,
3691 #define STEP_HASH_THRESHOLD 10
3692 #define BIG_DESTRUCTURING 5
3693 #define BIG_OBJECT_INIT 20
3695 static JSParseNode
*
3696 FindPropertyValue(JSParseNode
*pn
, JSParseNode
*pnid
, FindPropValData
*data
)
3698 FindPropValEntry
*entry
;
3699 JSParseNode
*pnhit
, *pnhead
, *pnprop
, *pnkey
;
3702 /* If we have a hash table, use it as the sole source of truth. */
3703 if (data
->table
.ops
) {
3704 entry
= (FindPropValEntry
*)
3705 JS_DHashTableOperate(&data
->table
, pnid
, JS_DHASH_LOOKUP
);
3706 return JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
) ? entry
->pnval
: NULL
;
3709 /* If pn is not an object initialiser node, we can't do anything here. */
3710 if (pn
->pn_type
!= TOK_RC
)
3714 * We must search all the way through pn's list, to handle the case of an
3715 * id duplicated for two or more property initialisers.
3719 ASSERT_VALID_PROPERTY_KEY(pnid
);
3720 pnhead
= pn
->pn_head
;
3721 if (pnid
->pn_type
== TOK_NUMBER
) {
3722 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3723 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3724 if (pnprop
->pn_op
== JSOP_NOP
) {
3725 pnkey
= pnprop
->pn_left
;
3726 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3727 if (pnkey
->pn_type
== TOK_NUMBER
&&
3728 pnkey
->pn_dval
== pnid
->pn_dval
) {
3735 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3736 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3737 if (pnprop
->pn_op
== JSOP_NOP
) {
3738 pnkey
= pnprop
->pn_left
;
3739 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3740 if (pnkey
->pn_type
== pnid
->pn_type
&&
3741 pnkey
->pn_atom
== pnid
->pn_atom
) {
3751 /* Hit via full search -- see whether it's time to create the hash table. */
3752 JS_ASSERT(!data
->table
.ops
);
3753 if (step
> data
->maxstep
) {
3754 data
->maxstep
= step
;
3755 if (step
>= STEP_HASH_THRESHOLD
&&
3756 data
->numvars
>= BIG_DESTRUCTURING
&&
3757 pn
->pn_count
>= BIG_OBJECT_INIT
&&
3758 JS_DHashTableInit(&data
->table
, &FindPropValOps
, pn
,
3759 sizeof(FindPropValEntry
),
3760 JS_DHASH_DEFAULT_CAPACITY(pn
->pn_count
)))
3762 for (pn
= pnhead
; pn
; pn
= pn
->pn_next
) {
3763 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3764 ASSERT_VALID_PROPERTY_KEY(pn
->pn_left
);
3765 entry
= (FindPropValEntry
*)
3766 JS_DHashTableOperate(&data
->table
, pn
->pn_left
,
3768 entry
->pnval
= pn
->pn_right
;
3772 return pnhit
->pn_right
;
3776 * Destructuring patterns can appear in two kinds of contexts:
3778 * - assignment-like: assignment expressions and |for| loop heads. In
3779 * these cases, the patterns' property value positions can be
3780 * arbitrary lvalue expressions; the destructuring is just a fancy
3783 * - declaration-like: |var| and |let| declarations, functions' formal
3784 * parameter lists, |catch| clauses, and comprehension tails. In
3785 * these cases, the patterns' property value positions must be
3786 * simple names; the destructuring defines them as new variables.
3788 * In both cases, other code parses the pattern as an arbitrary
3789 * primaryExpr, and then, here in CheckDestructuring, verify that the
3790 * tree is a valid destructuring expression.
3792 * In assignment-like contexts, we parse the pattern with the
3793 * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
3794 * pattern are parsed normally. primaryExpr links variable references
3795 * into the appropriate use chains; creates placeholder definitions;
3796 * and so on. CheckDestructuring is called with |data| NULL (since we
3797 * won't be binding any new names), and we specialize lvalues as
3798 * appropriate. If right is NULL, we just check for well-formed lvalues.
3800 * In declaration-like contexts, the normal variable reference
3801 * processing would just be an obstruction, because we're going to
3802 * define the names that appear in the property value positions as new
3803 * variables anyway. In this case, we parse the pattern with
3804 * TCF_DECL_DESTRUCTURING set, which directs primaryExpr to leave
3805 * whatever name nodes it creates unconnected. Then, here in
3806 * CheckDestructuring, we require the pattern's property value
3807 * positions to be simple names, and define them as appropriate to the
3808 * context. For these calls, |data| points to the right sort of
3811 * See also UndominateInitializers, immediately below. If you change
3812 * either of these functions, you might have to change the other to
3816 CheckDestructuring(JSContext
*cx
, BindData
*data
,
3817 JSParseNode
*left
, JSParseNode
*right
,
3821 FindPropValData fpvd
;
3822 JSParseNode
*lhs
, *rhs
, *pn
, *pn2
;
3824 if (left
->pn_type
== TOK_ARRAYCOMP
) {
3825 ReportCompileErrorNumber(cx
, TS(tc
->parser
), left
, JSREPORT_ERROR
,
3826 JSMSG_ARRAY_COMP_LEFTSIDE
);
3830 #if JS_HAS_DESTRUCTURING_SHORTHAND
3831 if (right
&& right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3832 ReportCompileErrorNumber(cx
, TS(tc
->parser
), right
, JSREPORT_ERROR
,
3833 JSMSG_BAD_OBJECT_INIT
);
3838 fpvd
.table
.ops
= NULL
;
3839 lhs
= left
->pn_head
;
3840 if (left
->pn_type
== TOK_RB
) {
3841 rhs
= (right
&& right
->pn_type
== left
->pn_type
)
3846 pn
= lhs
, pn2
= rhs
;
3848 /* Nullary comma is an elision; binary comma is an expression.*/
3849 if (pn
->pn_type
!= TOK_COMMA
|| pn
->pn_arity
!= PN_NULLARY
) {
3850 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3851 ok
= CheckDestructuring(cx
, data
, pn
, pn2
, tc
);
3854 if (pn
->pn_type
!= TOK_NAME
)
3857 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3859 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3871 JS_ASSERT(left
->pn_type
== TOK_RC
);
3872 fpvd
.numvars
= left
->pn_count
;
3877 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
3880 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3882 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
3883 ok
= CheckDestructuring(cx
, data
, pn
, rhs
, tc
);
3885 if (pn
->pn_type
!= TOK_NAME
)
3888 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3890 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3900 * The catch/finally handler implementation in the interpreter assumes
3901 * that any operation that introduces a new scope (like a "let" or "with"
3902 * block) increases the stack depth. This way, it is possible to restore
3903 * the scope chain based on stack depth of the handler alone. "let" with
3904 * an empty destructuring pattern like in
3908 * would violate this assumption as the there would be no let locals to
3909 * store on the stack. To satisfy it we add an empty property to such
3910 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3911 * slots, would be always positive.
3913 * Note that we add such a property even if the block has locals due to
3914 * later let declarations in it. We optimize for code simplicity here,
3915 * not the fastest runtime performance with empty [] or {}.
3918 data
->binder
== BindLet
&&
3919 OBJ_BLOCK_COUNT(cx
, tc
->blockChain
) == 0) {
3920 ok
= !!js_DefineNativeProperty(cx
, tc
->blockChain
,
3921 ATOM_TO_JSID(cx
->runtime
->
3922 atomState
.emptyAtom
),
3923 JSVAL_VOID
, NULL
, NULL
,
3927 JSScopeProperty::HAS_SHORTID
, 0, NULL
);
3936 JS_DHashTableFinish(&fpvd
.table
);
3940 ReportCompileErrorNumber(cx
, TS(tc
->parser
), pn
, JSREPORT_ERROR
,
3941 JSMSG_NO_VARIABLE_NAME
);
3947 * This is a greatly pared down version of CheckDestructuring that extends the
3948 * pn_pos.end source coordinate of each name in a destructuring binding such as
3950 * var [x, y] = [function () y, 42];
3952 * to cover its corresponding initializer, so that the initialized binding does
3953 * not appear to dominate any closures in its initializer. See bug 496134.
3955 * The quick-and-dirty dominance computation in Parser::setFunctionKinds is not
3956 * very precise. With one-pass SSA construction from structured source code
3957 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3958 * Languages", Brandis and Mössenböck), we could do much better.
3960 * See CheckDestructuring, immediately above. If you change either of these
3961 * functions, you might have to change the other to match.
3964 UndominateInitializers(JSParseNode
*left
, JSParseNode
*right
, JSTreeContext
*tc
)
3966 FindPropValData fpvd
;
3967 JSParseNode
*lhs
, *rhs
;
3969 JS_ASSERT(left
->pn_type
!= TOK_ARRAYCOMP
);
3972 #if JS_HAS_DESTRUCTURING_SHORTHAND
3973 if (right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3974 ReportCompileErrorNumber(tc
->parser
->context
, TS(tc
->parser
), right
, JSREPORT_ERROR
,
3975 JSMSG_BAD_OBJECT_INIT
);
3980 if (right
->pn_type
!= left
->pn_type
)
3983 fpvd
.table
.ops
= NULL
;
3984 lhs
= left
->pn_head
;
3985 if (left
->pn_type
== TOK_RB
) {
3986 rhs
= right
->pn_head
;
3988 while (lhs
&& rhs
) {
3989 /* Nullary comma is an elision; binary comma is an expression.*/
3990 if (lhs
->pn_type
!= TOK_COMMA
|| lhs
->pn_arity
!= PN_NULLARY
) {
3991 if (lhs
->pn_type
== TOK_RB
|| lhs
->pn_type
== TOK_RC
) {
3992 if (!UndominateInitializers(lhs
, rhs
, tc
))
3995 lhs
->pn_pos
.end
= rhs
->pn_pos
.end
;
4003 JS_ASSERT(left
->pn_type
== TOK_RC
);
4004 fpvd
.numvars
= left
->pn_count
;
4008 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
4009 JSParseNode
*pn
= lhs
->pn_right
;
4011 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
4012 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
4013 if (rhs
&& !UndominateInitializers(pn
, rhs
, tc
))
4017 pn
->pn_pos
.end
= rhs
->pn_pos
.end
;
4027 Parser::destructuringExpr(BindData
*data
, TokenKind tt
)
4031 tc
->flags
|= TCF_DECL_DESTRUCTURING
;
4032 pn
= primaryExpr(tt
, JS_FALSE
);
4033 tc
->flags
&= ~TCF_DECL_DESTRUCTURING
;
4036 if (!CheckDestructuring(context
, data
, pn
, NULL
, tc
))
4042 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4043 * This function assumes the cloned tree is for use in the same statement and
4044 * binding context as the original tree.
4046 static JSParseNode
*
4047 CloneParseTree(JSParseNode
*opn
, JSTreeContext
*tc
)
4049 JSParseNode
*pn
, *pn2
, *opn2
;
4051 pn
= NewOrRecycledNode(tc
);
4054 pn
->pn_type
= opn
->pn_type
;
4055 pn
->pn_pos
= opn
->pn_pos
;
4056 pn
->pn_op
= opn
->pn_op
;
4057 pn
->pn_used
= opn
->pn_used
;
4058 pn
->pn_defn
= opn
->pn_defn
;
4059 pn
->pn_arity
= opn
->pn_arity
;
4060 pn
->pn_parens
= opn
->pn_parens
;
4062 switch (pn
->pn_arity
) {
4063 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4066 NULLCHECK(pn
->pn_funbox
=
4067 tc
->parser
->newFunctionBox(opn
->pn_funbox
->object
, pn
, tc
));
4068 NULLCHECK(pn
->pn_body
= CloneParseTree(opn
->pn_body
, tc
));
4069 pn
->pn_cookie
= opn
->pn_cookie
;
4070 pn
->pn_dflags
= opn
->pn_dflags
;
4071 pn
->pn_blockid
= opn
->pn_blockid
;
4076 for (opn2
= opn
->pn_head
; opn2
; opn2
= opn2
->pn_next
) {
4077 NULLCHECK(pn2
= CloneParseTree(opn2
, tc
));
4080 pn
->pn_xflags
= opn
->pn_xflags
;
4084 NULLCHECK(pn
->pn_kid1
= CloneParseTree(opn
->pn_kid1
, tc
));
4085 NULLCHECK(pn
->pn_kid2
= CloneParseTree(opn
->pn_kid2
, tc
));
4086 NULLCHECK(pn
->pn_kid3
= CloneParseTree(opn
->pn_kid3
, tc
));
4090 NULLCHECK(pn
->pn_left
= CloneParseTree(opn
->pn_left
, tc
));
4091 if (opn
->pn_right
!= opn
->pn_left
)
4092 NULLCHECK(pn
->pn_right
= CloneParseTree(opn
->pn_right
, tc
));
4094 pn
->pn_right
= pn
->pn_left
;
4095 pn
->pn_val
= opn
->pn_val
;
4096 pn
->pn_iflags
= opn
->pn_iflags
;
4100 NULLCHECK(pn
->pn_kid
= CloneParseTree(opn
->pn_kid
, tc
));
4101 pn
->pn_num
= opn
->pn_num
;
4102 pn
->pn_hidden
= opn
->pn_hidden
;
4106 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4107 pn
->pn_u
= opn
->pn_u
;
4110 * The old name is a use of its pn_lexdef. Make the clone also be a
4111 * use of that definition.
4113 JSDefinition
*dn
= pn
->pn_lexdef
;
4115 pn
->pn_link
= dn
->dn_uses
;
4117 } else if (opn
->pn_expr
) {
4118 NULLCHECK(pn
->pn_expr
= CloneParseTree(opn
->pn_expr
, tc
));
4121 * If the old name is a definition, the new one has pn_defn set.
4122 * Make the old name a use of the new node.
4125 opn
->pn_defn
= false;
4126 LinkUseToDef(opn
, (JSDefinition
*) pn
, tc
);
4132 pn
->pn_names
= opn
->pn_names
;
4133 NULLCHECK(pn
->pn_tree
= CloneParseTree(opn
->pn_tree
, tc
));
4137 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4138 pn
->pn_u
= opn
->pn_u
;
4146 #endif /* JS_HAS_DESTRUCTURING */
4148 extern const char js_with_statement_str
[];
4150 static JSParseNode
*
4151 ContainsStmt(JSParseNode
*pn
, TokenKind tt
)
4153 JSParseNode
*pn2
, *pnt
;
4157 if (PN_TYPE(pn
) == tt
)
4159 switch (pn
->pn_arity
) {
4161 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
4162 pnt
= ContainsStmt(pn2
, tt
);
4168 pnt
= ContainsStmt(pn
->pn_kid1
, tt
);
4171 pnt
= ContainsStmt(pn
->pn_kid2
, tt
);
4174 return ContainsStmt(pn
->pn_kid3
, tt
);
4177 * Limit recursion if pn is a binary expression, which can't contain a
4180 if (pn
->pn_op
!= JSOP_NOP
)
4182 pnt
= ContainsStmt(pn
->pn_left
, tt
);
4185 return ContainsStmt(pn
->pn_right
, tt
);
4187 if (pn
->pn_op
!= JSOP_NOP
)
4189 return ContainsStmt(pn
->pn_kid
, tt
);
4191 return ContainsStmt(pn
->maybeExpr(), tt
);
4193 return ContainsStmt(pn
->pn_tree
, tt
);
4200 Parser::returnOrYield(bool useAssignExpr
)
4203 JSParseNode
*pn
, *pn2
;
4205 tt
= tokenStream
.currentToken().type
;
4206 if (tt
== TOK_RETURN
&& !tc
->inFunction()) {
4207 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
4208 JSMSG_BAD_RETURN_OR_YIELD
, js_return_str
);
4212 pn
= UnaryNode::create(tc
);
4216 #if JS_HAS_GENERATORS
4217 if (tt
== TOK_YIELD
)
4218 tc
->flags
|= TCF_FUN_IS_GENERATOR
;
4221 /* This is ugly, but we don't want to require a semicolon. */
4222 tt2
= tokenStream
.peekTokenSameLine(TSF_OPERAND
);
4223 if (tt2
== TOK_ERROR
)
4226 if (tt2
!= TOK_EOF
&& tt2
!= TOK_EOL
&& tt2
!= TOK_SEMI
&& tt2
!= TOK_RC
4227 #if JS_HAS_GENERATORS
4228 && (tt
!= TOK_YIELD
||
4229 (tt2
!= tt
&& tt2
!= TOK_RB
&& tt2
!= TOK_RP
&&
4230 tt2
!= TOK_COLON
&& tt2
!= TOK_COMMA
))
4233 pn2
= useAssignExpr
? assignExpr() : expr();
4236 #if JS_HAS_GENERATORS
4237 if (tt
== TOK_RETURN
)
4239 tc
->flags
|= TCF_RETURN_EXPR
;
4240 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4243 #if JS_HAS_GENERATORS
4244 if (tt
== TOK_RETURN
)
4246 tc
->flags
|= TCF_RETURN_VOID
;
4249 if ((~tc
->flags
& (TCF_RETURN_EXPR
| TCF_FUN_IS_GENERATOR
)) == 0) {
4250 /* As in Python (see PEP-255), disallow return v; in generators. */
4251 ReportBadReturn(context
, tc
, JSREPORT_ERROR
,
4252 JSMSG_BAD_GENERATOR_RETURN
,
4253 JSMSG_BAD_ANON_GENERATOR_RETURN
);
4257 if (JS_HAS_STRICT_OPTION(context
) &&
4258 (~tc
->flags
& (TCF_RETURN_EXPR
| TCF_RETURN_VOID
)) == 0 &&
4259 !ReportBadReturn(context
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
4260 JSMSG_NO_RETURN_VALUE
,
4261 JSMSG_ANON_NO_RETURN_VALUE
)) {
4268 static JSParseNode
*
4269 PushLexicalScope(JSContext
*cx
, TokenStream
*ts
, JSTreeContext
*tc
,
4274 JSObjectBox
*blockbox
;
4276 pn
= LexicalScopeNode::create(tc
);
4280 obj
= js_NewBlockObject(cx
);
4284 blockbox
= tc
->parser
->newObjectBox(obj
);
4288 js_PushBlockScope(tc
, stmt
, obj
, -1);
4289 pn
->pn_type
= TOK_LEXICALSCOPE
;
4290 pn
->pn_op
= JSOP_LEAVEBLOCK
;
4291 pn
->pn_objbox
= blockbox
;
4292 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
4294 if (!GenerateBlockId(tc
, stmt
->blockid
))
4296 pn
->pn_blockid
= stmt
->blockid
;
4300 #if JS_HAS_BLOCK_SCOPE
4303 Parser::letBlock(JSBool statement
)
4305 JSParseNode
*pn
, *pnblock
, *pnlet
;
4306 JSStmtInfo stmtInfo
;
4308 JS_ASSERT(tokenStream
.currentToken().type
== TOK_LET
);
4310 /* Create the let binary node. */
4311 pnlet
= BinaryNode::create(tc
);
4315 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_LET
);
4317 /* This is a let block or expression of the form: let (a, b, c) .... */
4318 pnblock
= PushLexicalScope(context
, &tokenStream
, tc
, &stmtInfo
);
4322 pn
->pn_expr
= pnlet
;
4324 pnlet
->pn_left
= variables(true);
4325 if (!pnlet
->pn_left
)
4327 pnlet
->pn_left
->pn_xflags
= PNX_POPVAR
;
4329 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_LET
);
4331 if (statement
&& !tokenStream
.matchToken(TOK_LC
, TSF_OPERAND
)) {
4333 * If this is really an expression in let statement guise, then we
4334 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4335 * the return value of the expression.
4337 pn
= UnaryNode::create(tc
);
4340 pn
->pn_type
= TOK_SEMI
;
4342 pn
->pn_kid
= pnblock
;
4344 statement
= JS_FALSE
;
4348 pnlet
->pn_right
= statements();
4349 if (!pnlet
->pn_right
)
4351 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LET
);
4354 * Change pnblock's opcode to the variant that propagates the last
4355 * result down after popping the block, and clear statement.
4357 pnblock
->pn_op
= JSOP_LEAVEBLOCKEXPR
;
4358 pnlet
->pn_right
= assignExpr();
4359 if (!pnlet
->pn_right
)
4367 #endif /* JS_HAS_BLOCK_SCOPE */
4370 PushBlocklikeStatement(JSStmtInfo
*stmt
, JSStmtType type
, JSTreeContext
*tc
)
4372 js_PushStatement(tc
, stmt
, type
, -1);
4373 return GenerateBlockId(tc
, stmt
->blockid
);
4376 static JSParseNode
*
4377 NewBindingNode(JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
4379 JSParseNode
*pn
= NULL
;
4381 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
4384 JS_ASSERT(!pn
->isPlaceholder());
4386 ale
= tc
->lexdeps
.lookup(atom
);
4389 JS_ASSERT(pn
->isPlaceholder());
4394 JS_ASSERT(pn
->pn_defn
);
4397 * A let binding at top level becomes a var before we get here, so if
4398 * pn and tc have the same blockid then that id must not be the bodyid.
4399 * If pn is a forward placeholder definition from the same or a higher
4400 * block then we claim it.
4402 JS_ASSERT_IF(let
&& pn
->pn_blockid
== tc
->blockid(),
4403 pn
->pn_blockid
!= tc
->bodyid
);
4405 if (pn
->isPlaceholder() && pn
->pn_blockid
>= (let
? tc
->blockid() : tc
->bodyid
)) {
4407 pn
->pn_blockid
= tc
->blockid();
4409 tc
->lexdeps
.remove(tc
->parser
, atom
);
4414 /* Make a new node for this declarator name (or destructuring pattern). */
4415 pn
= NameNode::create(atom
, tc
);
4421 #if JS_HAS_BLOCK_SCOPE
4423 RebindLets(JSParseNode
*pn
, JSTreeContext
*tc
)
4428 switch (pn
->pn_arity
) {
4430 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
4431 RebindLets(pn2
, tc
);
4435 RebindLets(pn
->pn_kid1
, tc
);
4436 RebindLets(pn
->pn_kid2
, tc
);
4437 RebindLets(pn
->pn_kid3
, tc
);
4441 RebindLets(pn
->pn_left
, tc
);
4442 RebindLets(pn
->pn_right
, tc
);
4446 RebindLets(pn
->pn_kid
, tc
);
4450 RebindLets(pn
->pn_body
, tc
);
4454 RebindLets(pn
->maybeExpr(), tc
);
4457 JS_ASSERT(pn
->pn_blockid
> tc
->topStmt
->blockid
);
4458 } else if (pn
->pn_used
) {
4459 if (pn
->pn_lexdef
->pn_blockid
== tc
->topStmt
->blockid
) {
4462 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
4464 while ((ale
= ALE_NEXT(ale
)) != NULL
) {
4465 if (ALE_ATOM(ale
) == pn
->pn_atom
) {
4466 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4472 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
4474 ale
= MakePlaceholder(pn
, tc
);
4478 JSDefinition
*dn
= ALE_DEFN(ale
);
4479 dn
->pn_type
= TOK_NAME
;
4480 dn
->pn_op
= JSOP_NOP
;
4482 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4488 RebindLets(pn
->pn_tree
, tc
);
4494 #endif /* JS_HAS_BLOCK_SCOPE */
4500 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
, *pn4
;
4501 JSStmtInfo stmtInfo
, *stmt
, *stmt2
;
4504 JS_CHECK_RECURSION(context
, return NULL
);
4506 tt
= tokenStream
.getToken(TSF_OPERAND
);
4510 #if JS_HAS_XML_SUPPORT
4511 tt
= tokenStream
.peekToken(TSF_KEYWORD_IS_NAME
);
4512 if (tt
== TOK_DBLCOLON
)
4515 return functionStmt();
4518 /* An IF node has three kids: condition, then, and optional else. */
4519 pn
= TernaryNode::create(tc
);
4525 js_PushStatement(tc
, &stmtInfo
, STMT_IF
, -1);
4529 if (tokenStream
.matchToken(TOK_ELSE
, TSF_OPERAND
)) {
4530 stmtInfo
.type
= STMT_ELSE
;
4534 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
4537 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4547 JSParseNode
*pn5
, *saveBlock
;
4548 JSBool seenDefault
= JS_FALSE
;
4550 pn
= BinaryNode::create(tc
);
4553 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
4555 /* pn1 points to the switch's discriminant. */
4556 pn1
= parenExpr(NULL
, NULL
);
4560 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
4561 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
4564 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4565 * because that function states tc->topStmt->blockid.
4567 js_PushStatement(tc
, &stmtInfo
, STMT_SWITCH
, -1);
4569 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4570 pn2
= ListNode::create(tc
);
4574 if (!GenerateBlockIdForStmtNode(pn2
, tc
))
4576 saveBlock
= tc
->blockNode
;
4577 tc
->blockNode
= pn2
;
4579 while ((tt
= tokenStream
.getToken()) != TOK_RC
) {
4583 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
4584 JSMSG_TOO_MANY_DEFAULTS
);
4587 seenDefault
= JS_TRUE
;
4591 pn3
= BinaryNode::create(tc
);
4594 if (tt
== TOK_CASE
) {
4595 pn3
->pn_left
= expr();
4600 if (pn2
->pn_count
== JS_BIT(16)) {
4601 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
4602 JSMSG_TOO_MANY_CASES
);
4611 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
4615 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
4617 pn4
= ListNode::create(tc
);
4620 pn4
->pn_type
= TOK_LC
;
4622 while ((tt
= tokenStream
.peekToken(TSF_OPERAND
)) != TOK_RC
&&
4623 tt
!= TOK_CASE
&& tt
!= TOK_DEFAULT
) {
4624 if (tt
== TOK_ERROR
)
4629 pn4
->pn_pos
.end
= pn5
->pn_pos
.end
;
4633 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4635 pn4
->pn_pos
.begin
= pn4
->pn_head
->pn_pos
.begin
;
4636 pn3
->pn_pos
.end
= pn4
->pn_pos
.end
;
4637 pn3
->pn_right
= pn4
;
4641 * Handle the case where there was a let declaration in any case in
4642 * the switch body, but not within an inner block. If it replaced
4643 * tc->blockNode with a new block node then we must refresh pn2 and
4644 * then restore tc->blockNode.
4646 if (tc
->blockNode
!= pn2
)
4647 pn2
= tc
->blockNode
;
4648 tc
->blockNode
= saveBlock
;
4651 pn
->pn_pos
.end
= pn2
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
4658 pn
= BinaryNode::create(tc
);
4661 js_PushStatement(tc
, &stmtInfo
, STMT_WHILE_LOOP
, -1);
4670 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4675 pn
= BinaryNode::create(tc
);
4678 js_PushStatement(tc
, &stmtInfo
, STMT_DO_LOOP
, -1);
4683 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
4688 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4690 if (JSVERSION_NUMBER(context
) != JSVERSION_ECMA_3
) {
4692 * All legacy and extended versions must do automatic semicolon
4693 * insertion after do-while. See the testcase and discussion in
4694 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4696 (void) tokenStream
.matchToken(TOK_SEMI
);
4703 JSParseNode
*pnseq
= NULL
;
4704 #if JS_HAS_BLOCK_SCOPE
4705 JSParseNode
*pnlet
= NULL
;
4706 JSStmtInfo blockInfo
;
4709 /* A FOR node is binary, left is loop control and right is the body. */
4710 pn
= BinaryNode::create(tc
);
4713 js_PushStatement(tc
, &stmtInfo
, STMT_FOR_LOOP
, -1);
4715 pn
->pn_op
= JSOP_ITER
;
4717 if (tokenStream
.matchToken(TOK_NAME
)) {
4718 if (tokenStream
.currentToken().t_atom
== context
->runtime
->atomState
.eachAtom
)
4719 pn
->pn_iflags
= JSITER_FOREACH
;
4721 tokenStream
.ungetToken();
4724 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4725 tt
= tokenStream
.peekToken(TSF_OPERAND
);
4727 #if JS_HAS_BLOCK_SCOPE
4731 if (tt
== TOK_SEMI
) {
4732 if (pn
->pn_iflags
& JSITER_FOREACH
)
4735 /* No initializer -- set first kid of left sub-node to null. */
4739 * Set pn1 to a var list or an initializing expression.
4741 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4742 * of the for statement. This flag will be used by the RelExpr
4743 * production; if it is set, then the 'in' keyword will not be
4744 * recognized as an operator, leaving it available to be parsed as
4745 * part of a for/in loop.
4747 * A side effect of this restriction is that (unparenthesized)
4748 * expressions involving an 'in' operator are illegal in the init
4749 * clause of an ordinary for loop.
4751 tc
->flags
|= TCF_IN_FOR_INIT
;
4752 if (tt
== TOK_VAR
) {
4753 (void) tokenStream
.getToken();
4754 pn1
= variables(false);
4755 #if JS_HAS_BLOCK_SCOPE
4756 } else if (tt
== TOK_LET
) {
4758 (void) tokenStream
.getToken();
4759 if (tokenStream
.peekToken() == TOK_LP
) {
4760 pn1
= letBlock(JS_FALSE
);
4761 tt
= TOK_LEXICALSCOPE
;
4763 pnlet
= PushLexicalScope(context
, &tokenStream
, tc
, &blockInfo
);
4766 blockInfo
.flags
|= SIF_FOR_BLOCK
;
4767 pn1
= variables(false);
4773 tc
->flags
&= ~TCF_IN_FOR_INIT
;
4779 * We can be sure that it's a for/in loop if there's still an 'in'
4780 * keyword here, even if JavaScript recognizes 'in' as an operator,
4781 * as we've excluded 'in' from being parsed in RelExpr by setting
4782 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4784 if (pn1
&& tokenStream
.matchToken(TOK_IN
)) {
4785 pn
->pn_iflags
|= JSITER_ENUMERATE
;
4786 stmtInfo
.type
= STMT_FOR_IN_LOOP
;
4788 /* Check that the left side of the 'in' is valid. */
4789 JS_ASSERT(!TokenKindIsDecl(tt
) || PN_TYPE(pn1
) == tt
);
4790 if (TokenKindIsDecl(tt
)
4791 ? (pn1
->pn_count
> 1 || pn1
->pn_op
== JSOP_DEFCONST
4792 #if JS_HAS_DESTRUCTURING
4793 || (JSVERSION_NUMBER(context
) == JSVERSION_1_7
&&
4794 pn
->pn_op
== JSOP_ITER
&&
4795 !(pn
->pn_iflags
& JSITER_FOREACH
) &&
4796 (pn1
->pn_head
->pn_type
== TOK_RC
||
4797 (pn1
->pn_head
->pn_type
== TOK_RB
&&
4798 pn1
->pn_head
->pn_count
!= 2) ||
4799 (pn1
->pn_head
->pn_type
== TOK_ASSIGN
&&
4800 (pn1
->pn_head
->pn_left
->pn_type
!= TOK_RB
||
4801 pn1
->pn_head
->pn_left
->pn_count
!= 2))))
4804 : (pn1
->pn_type
!= TOK_NAME
&&
4805 pn1
->pn_type
!= TOK_DOT
&&
4806 #if JS_HAS_DESTRUCTURING
4807 ((JSVERSION_NUMBER(context
) == JSVERSION_1_7
&&
4808 pn
->pn_op
== JSOP_ITER
&&
4809 !(pn
->pn_iflags
& JSITER_FOREACH
))
4810 ? (pn1
->pn_type
!= TOK_RB
|| pn1
->pn_count
!= 2)
4811 : (pn1
->pn_type
!= TOK_RB
&& pn1
->pn_type
!= TOK_RC
)) &&
4813 pn1
->pn_type
!= TOK_LP
&&
4814 #if JS_HAS_XML_SUPPORT
4815 (pn1
->pn_type
!= TOK_UNARYOP
||
4816 pn1
->pn_op
!= JSOP_XMLNAME
) &&
4818 pn1
->pn_type
!= TOK_LB
)) {
4819 ReportCompileErrorNumber(context
, &tokenStream
, pn1
, JSREPORT_ERROR
,
4820 JSMSG_BAD_FOR_LEFTSIDE
);
4824 /* pn2 points to the name or destructuring pattern on in's left. */
4826 uintN dflag
= PND_ASSIGNED
;
4828 if (TokenKindIsDecl(tt
)) {
4829 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4830 pn1
->pn_xflags
|= PNX_FORINVAR
;
4833 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4834 * 'var', or 'const' to hoist the initializer or the entire
4835 * decl out of the loop head. TOK_VAR is the type for both
4836 * 'var' and 'const'.
4839 if ((pn2
->pn_type
== TOK_NAME
&& pn2
->maybeExpr())
4840 #if JS_HAS_DESTRUCTURING
4841 || pn2
->pn_type
== TOK_ASSIGN
4844 pnseq
= ListNode::create(tc
);
4847 pnseq
->pn_type
= TOK_SEQ
;
4848 pnseq
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4850 #if JS_HAS_BLOCK_SCOPE
4851 if (tt
== TOK_LET
) {
4853 * Hoist just the 'i' from 'for (let x = i in o)' to
4854 * before the loop, glued together via pnseq.
4856 pn3
= UnaryNode::create(tc
);
4859 pn3
->pn_type
= TOK_SEMI
;
4860 pn3
->pn_op
= JSOP_NOP
;
4861 #if JS_HAS_DESTRUCTURING
4862 if (pn2
->pn_type
== TOK_ASSIGN
) {
4863 pn4
= pn2
->pn_right
;
4864 pn2
= pn1
->pn_head
= pn2
->pn_left
;
4869 pn2
->pn_expr
= NULL
;
4871 if (!RebindLets(pn4
, tc
))
4873 pn3
->pn_pos
= pn4
->pn_pos
;
4875 pnseq
->initList(pn3
);
4877 #endif /* JS_HAS_BLOCK_SCOPE */
4879 dflag
= PND_INITIALIZED
;
4882 * All of 'var x = i' is hoisted above 'for (x in o)',
4883 * so clear PNX_FORINVAR.
4885 * Request JSOP_POP here since the var is for a simple
4886 * name (it is not a destructuring binding's left-hand
4887 * side) and it has an initializer.
4889 pn1
->pn_xflags
&= ~PNX_FORINVAR
;
4890 pn1
->pn_xflags
|= PNX_POPVAR
;
4891 pnseq
->initList(pn1
);
4893 #if JS_HAS_DESTRUCTURING
4894 if (pn2
->pn_type
== TOK_ASSIGN
) {
4895 pn1
= CloneParseTree(pn2
->pn_left
, tc
);
4901 JS_ASSERT(pn2
->pn_type
== TOK_NAME
);
4902 pn1
= NameNode::create(pn2
->pn_atom
, tc
);
4905 pn1
->pn_type
= TOK_NAME
;
4906 pn1
->pn_op
= JSOP_NAME
;
4907 pn1
->pn_pos
= pn2
->pn_pos
;
4909 LinkUseToDef(pn1
, (JSDefinition
*) pn2
, tc
);
4918 if (pn2
->pn_type
== TOK_LP
&&
4919 !MakeSetCall(context
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
)) {
4922 #if JS_HAS_XML_SUPPORT
4923 if (pn2
->pn_type
== TOK_UNARYOP
)
4924 pn2
->pn_op
= JSOP_BINDXMLNAME
;
4928 switch (pn2
->pn_type
) {
4930 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4931 NoteLValue(context
, pn2
, tc
, dflag
);
4934 #if JS_HAS_DESTRUCTURING
4937 JS_ASSERT(pn2
->pn_type
== TOK_RB
|| pn2
->pn_type
== TOK_RC
);
4941 /* Check for valid lvalues in var-less destructuring for-in. */
4942 if (pn1
== pn2
&& !CheckDestructuring(context
, NULL
, pn2
, NULL
, tc
))
4945 if (JSVERSION_NUMBER(context
) == JSVERSION_1_7
) {
4947 * Destructuring for-in requires [key, value] enumeration
4950 JS_ASSERT(pn
->pn_op
== JSOP_ITER
);
4951 if (!(pn
->pn_iflags
& JSITER_FOREACH
))
4952 pn
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
4961 * Parse the object expression as the right operand of 'in', first
4962 * removing the top statement from the statement-stack if this is a
4963 * 'for (let x in y)' loop.
4965 #if JS_HAS_BLOCK_SCOPE
4966 JSStmtInfo
*save
= tc
->topStmt
;
4968 tc
->topStmt
= save
->down
;
4971 #if JS_HAS_BLOCK_SCOPE
4976 pn2
= JSParseNode::newBinaryOrAppend(TOK_IN
, JSOP_NOP
, pn1
, pn2
, tc
);
4981 if (pn
->pn_iflags
& JSITER_FOREACH
)
4983 pn
->pn_op
= JSOP_NOP
;
4985 /* Parse the loop condition or null into pn2. */
4986 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
4987 tt
= tokenStream
.peekToken(TSF_OPERAND
);
4988 if (tt
== TOK_SEMI
) {
4996 /* Parse the update expression or null into pn3. */
4997 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
4998 tt
= tokenStream
.peekToken(TSF_OPERAND
);
5007 /* Build the FORHEAD node to use as the left kid of pn. */
5008 pn4
= TernaryNode::create(tc
);
5011 pn4
->pn_type
= TOK_FORHEAD
;
5012 pn4
->pn_op
= JSOP_NOP
;
5019 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
5021 /* Parse the loop body into pn->pn_right. */
5027 /* Record the absolute line number for source note emission. */
5028 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5030 #if JS_HAS_BLOCK_SCOPE
5033 pnlet
->pn_expr
= pn
;
5038 pnseq
->pn_pos
.end
= pn
->pn_pos
.end
;
5046 ReportCompileErrorNumber(context
, &tokenStream
, pn
, JSREPORT_ERROR
,
5047 JSMSG_BAD_FOR_EACH_LOOP
);
5052 JSParseNode
*catchList
, *lastCatch
;
5055 * try nodes are ternary.
5056 * kid1 is the try statement
5057 * kid2 is the catch node list or null
5058 * kid3 is the finally statement
5060 * catch nodes are ternary.
5061 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5062 * kid2 is the catch guard or null if no guard
5063 * kid3 is the catch block
5065 * catch lvalue nodes are either:
5066 * TOK_NAME for a single identifier
5067 * TOK_RB or TOK_RC for a destructuring left-hand side
5069 * finally nodes are TOK_LC statement lists.
5071 pn
= TernaryNode::create(tc
);
5074 pn
->pn_op
= JSOP_NOP
;
5076 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
5077 if (!PushBlocklikeStatement(&stmtInfo
, STMT_TRY
, tc
))
5079 pn
->pn_kid1
= statements();
5082 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
5086 tt
= tokenStream
.getToken();
5087 if (tt
== TOK_CATCH
) {
5088 catchList
= ListNode::create(tc
);
5091 catchList
->pn_type
= TOK_RESERVED
;
5092 catchList
->makeEmpty();
5096 JSParseNode
*pnblock
;
5099 /* Check for another catch after unconditional catch. */
5100 if (lastCatch
&& !lastCatch
->pn_kid2
) {
5101 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5102 JSMSG_CATCH_AFTER_GENERAL
);
5107 * Create a lexical scope node around the whole catch clause,
5108 * including the head.
5110 pnblock
= PushLexicalScope(context
, &tokenStream
, tc
, &stmtInfo
);
5113 stmtInfo
.type
= STMT_CATCH
;
5116 * Legal catch forms are:
5118 * catch (lhs if <boolean_expression>)
5119 * where lhs is a name or a destructuring left-hand side.
5120 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5122 pn2
= TernaryNode::create(tc
);
5125 pnblock
->pn_expr
= pn2
;
5126 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
5129 * Contrary to ECMA Ed. 3, the catch variable is lexically
5130 * scoped, not a property of a new Object instance. This is
5131 * an intentional change that anticipates ECMA Ed. 4.
5135 data
.binder
= BindLet
;
5136 data
.let
.overflow
= JSMSG_TOO_MANY_CATCH_VARS
;
5138 tt
= tokenStream
.getToken();
5140 #if JS_HAS_DESTRUCTURING
5143 pn3
= destructuringExpr(&data
, tt
);
5150 label
= tokenStream
.currentToken().t_atom
;
5151 pn3
= NewBindingNode(label
, tc
, true);
5155 if (!data
.binder(context
, &data
, label
, tc
))
5160 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5161 JSMSG_CATCH_IDENTIFIER
);
5166 #if JS_HAS_CATCH_GUARD
5168 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5169 * to avoid conflicting with the JS2/ECMAv4 type annotation
5170 * catchguard syntax.
5172 if (tokenStream
.matchToken(TOK_IF
)) {
5173 pn2
->pn_kid2
= expr();
5178 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
5180 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
5181 pn2
->pn_kid3
= statements();
5184 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
5187 catchList
->append(pnblock
);
5189 tt
= tokenStream
.getToken(TSF_OPERAND
);
5190 } while (tt
== TOK_CATCH
);
5192 pn
->pn_kid2
= catchList
;
5194 if (tt
== TOK_FINALLY
) {
5195 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
5196 if (!PushBlocklikeStatement(&stmtInfo
, STMT_FINALLY
, tc
))
5198 pn
->pn_kid3
= statements();
5201 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
5204 tokenStream
.ungetToken();
5206 if (!catchList
&& !pn
->pn_kid3
) {
5207 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5208 JSMSG_CATCH_OR_FINALLY
);
5215 pn
= UnaryNode::create(tc
);
5219 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5220 tt
= tokenStream
.peekTokenSameLine(TSF_OPERAND
);
5221 if (tt
== TOK_ERROR
)
5223 if (tt
== TOK_EOF
|| tt
== TOK_EOL
|| tt
== TOK_SEMI
|| tt
== TOK_RC
) {
5224 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5225 JSMSG_SYNTAX_ERROR
);
5232 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5233 pn
->pn_op
= JSOP_THROW
;
5237 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5239 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5240 JSMSG_CATCH_WITHOUT_TRY
);
5244 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5245 JSMSG_FINALLY_WITHOUT_TRY
);
5249 pn
= NullaryNode::create(tc
);
5252 if (!MatchLabel(context
, &tokenStream
, pn
))
5255 label
= pn
->pn_atom
;
5257 for (; ; stmt
= stmt
->down
) {
5259 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5260 JSMSG_LABEL_NOT_FOUND
);
5263 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
)
5267 for (; ; stmt
= stmt
->down
) {
5269 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5273 if (STMT_IS_LOOP(stmt
) || stmt
->type
== STMT_SWITCH
)
5278 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
5282 pn
= NullaryNode::create(tc
);
5285 if (!MatchLabel(context
, &tokenStream
, pn
))
5288 label
= pn
->pn_atom
;
5290 for (stmt2
= NULL
; ; stmt
= stmt
->down
) {
5292 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5293 JSMSG_LABEL_NOT_FOUND
);
5296 if (stmt
->type
== STMT_LABEL
) {
5297 if (stmt
->label
== label
) {
5298 if (!stmt2
|| !STMT_IS_LOOP(stmt2
)) {
5299 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5300 JSMSG_BAD_CONTINUE
);
5310 for (; ; stmt
= stmt
->down
) {
5312 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5313 JSMSG_BAD_CONTINUE
);
5316 if (STMT_IS_LOOP(stmt
))
5321 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
5326 * In most cases, we want the constructs forbidden in strict mode
5327 * code to be a subset of those that JSOPTION_STRICT warns about, and
5328 * we should use ReportStrictModeError. However, 'with' is the sole
5329 * instance of a construct that is forbidden in strict mode code, but
5330 * doesn't even merit a warning under JSOPTION_STRICT. See
5331 * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5333 if (tc
->flags
& TCF_STRICT_MODE_CODE
) {
5334 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5335 JSMSG_STRICT_CODE_WITH
);
5339 pn
= BinaryNode::create(tc
);
5342 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
5343 pn2
= parenExpr(NULL
, NULL
);
5346 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
5349 js_PushStatement(tc
, &stmtInfo
, STMT_WITH
, -1);
5355 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5357 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5361 pn
= variables(false);
5365 /* Tell js_EmitTree to generate a final POP. */
5366 pn
->pn_xflags
|= PNX_POPVAR
;
5369 #if JS_HAS_BLOCK_SCOPE
5373 JSObjectBox
*blockbox
;
5375 /* Check for a let statement or let expression. */
5376 if (tokenStream
.peekToken() == TOK_LP
) {
5377 pn
= letBlock(JS_TRUE
);
5378 if (!pn
|| pn
->pn_op
== JSOP_LEAVEBLOCK
)
5381 /* Let expressions require automatic semicolon insertion. */
5382 JS_ASSERT(pn
->pn_type
== TOK_SEMI
||
5383 pn
->pn_op
== JSOP_LEAVEBLOCKEXPR
);
5388 * This is a let declaration. We must be directly under a block per
5389 * the proposed ES4 specs, but not an implicit block created due to
5390 * 'for (let ...)'. If we pass this error test, make the enclosing
5391 * JSStmtInfo be our scope. Further let declarations in this block
5392 * will find this scope statement and use the same block object.
5394 * If we are the first let declaration in this block (i.e., when the
5395 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5396 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5400 (!STMT_MAYBE_SCOPE(stmt
) || (stmt
->flags
& SIF_FOR_BLOCK
))) {
5401 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5402 JSMSG_LET_DECL_NOT_IN_BLOCK
);
5406 if (stmt
&& (stmt
->flags
& SIF_SCOPE
)) {
5407 JS_ASSERT(tc
->blockChain
== stmt
->blockObj
);
5408 obj
= tc
->blockChain
;
5410 if (!stmt
|| (stmt
->flags
& SIF_BODY_BLOCK
)) {
5412 * ES4 specifies that let at top level and at body-block scope
5413 * does not shadow var, so convert back to var.
5415 tokenStream
.mungeCurrentToken(TOK_VAR
, JSOP_DEFVAR
);
5417 pn
= variables(false);
5420 pn
->pn_xflags
|= PNX_POPVAR
;
5425 * Some obvious assertions here, but they may help clarify the
5426 * situation. This stmt is not yet a scope, so it must not be a
5427 * catch block (catch is a lexical scope by definition).
5429 JS_ASSERT(!(stmt
->flags
& SIF_SCOPE
));
5430 JS_ASSERT(stmt
!= tc
->topScopeStmt
);
5431 JS_ASSERT(stmt
->type
== STMT_BLOCK
||
5432 stmt
->type
== STMT_SWITCH
||
5433 stmt
->type
== STMT_TRY
||
5434 stmt
->type
== STMT_FINALLY
);
5435 JS_ASSERT(!stmt
->downScope
);
5437 /* Convert the block statement into a scope statement. */
5438 JSObject
*obj
= js_NewBlockObject(tc
->parser
->context
);
5442 blockbox
= tc
->parser
->newObjectBox(obj
);
5447 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5448 * list stack, if it isn't already there. If it is there, but it
5449 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5452 stmt
->flags
|= SIF_SCOPE
;
5453 stmt
->downScope
= tc
->topScopeStmt
;
5454 tc
->topScopeStmt
= stmt
;
5455 JS_SCOPE_DEPTH_METERING(++tc
->scopeDepth
> tc
->maxScopeDepth
&&
5456 (tc
->maxScopeDepth
= tc
->scopeDepth
));
5458 obj
->setParent(tc
->blockChain
);
5459 tc
->blockChain
= obj
;
5460 stmt
->blockObj
= obj
;
5463 pn1
= tc
->blockNode
;
5464 JS_ASSERT(!pn1
|| pn1
->pn_type
!= TOK_LEXICALSCOPE
);
5467 /* Create a new lexical scope node for these statements. */
5468 pn1
= LexicalScopeNode::create(tc
);
5472 pn1
->pn_type
= TOK_LEXICALSCOPE
;
5473 pn1
->pn_op
= JSOP_LEAVEBLOCK
;
5474 pn1
->pn_pos
= tc
->blockNode
->pn_pos
;
5475 pn1
->pn_objbox
= blockbox
;
5476 pn1
->pn_expr
= tc
->blockNode
;
5477 pn1
->pn_blockid
= tc
->blockNode
->pn_blockid
;
5478 tc
->blockNode
= pn1
;
5481 pn
= variables(false);
5484 pn
->pn_xflags
= PNX_POPVAR
;
5487 #endif /* JS_HAS_BLOCK_SCOPE */
5490 pn
= returnOrYield(false);
5499 oldflags
= tc
->flags
;
5500 tc
->flags
= oldflags
& ~TCF_HAS_FUNCTION_STMT
;
5501 if (!PushBlocklikeStatement(&stmtInfo
, STMT_BLOCK
, tc
))
5507 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
5511 * If we contain a function statement and our container is top-level
5512 * or another block, flag pn to preserve braces when decompiling.
5514 if ((tc
->flags
& TCF_HAS_FUNCTION_STMT
) &&
5515 (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)) {
5516 pn
->pn_xflags
|= PNX_NEEDBRACES
;
5518 tc
->flags
= oldflags
| (tc
->flags
& (TCF_FUN_FLAGS
| TCF_RETURN_FLAGS
));
5524 pn
= UnaryNode::create(tc
);
5527 pn
->pn_type
= TOK_SEMI
;
5530 #if JS_HAS_DEBUGGER_KEYWORD
5532 pn
= NullaryNode::create(tc
);
5535 pn
->pn_type
= TOK_DEBUGGER
;
5536 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5538 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5540 #if JS_HAS_XML_SUPPORT
5542 pn
= UnaryNode::create(tc
);
5545 if (!tokenStream
.matchToken(TOK_NAME
) ||
5546 tokenStream
.currentToken().t_atom
!= context
->runtime
->atomState
.xmlAtom
||
5547 !tokenStream
.matchToken(TOK_NAME
) ||
5548 tokenStream
.currentToken().t_atom
!= context
->runtime
->atomState
.namespaceAtom
||
5549 !tokenStream
.matchToken(TOK_ASSIGN
) ||
5550 tokenStream
.currentToken().t_op
!= JSOP_NOP
) {
5551 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5552 JSMSG_BAD_DEFAULT_XML_NAMESPACE
);
5556 /* Is this an E4X dagger I see before me? */
5557 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5561 pn
->pn_op
= JSOP_DEFXMLNS
;
5562 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5571 #if JS_HAS_XML_SUPPORT
5574 tokenStream
.ungetToken();
5579 if (tokenStream
.peekToken() == TOK_COLON
) {
5580 if (pn2
->pn_type
!= TOK_NAME
) {
5581 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5585 label
= pn2
->pn_atom
;
5586 for (stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
5587 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
) {
5588 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5589 JSMSG_DUPLICATE_LABEL
);
5595 (void) tokenStream
.getToken();
5597 /* Push a label struct and parse the statement. */
5598 js_PushStatement(tc
, &stmtInfo
, STMT_LABEL
, -1);
5599 stmtInfo
.label
= label
;
5604 /* Normalize empty statement to empty block for the decompiler. */
5605 if (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
) {
5606 pn
->pn_type
= TOK_LC
;
5607 pn
->pn_arity
= PN_LIST
;
5611 /* Pop the label, set pn_expr, and return early. */
5613 pn2
->pn_type
= TOK_COLON
;
5614 pn2
->pn_pos
.end
= pn
->pn_pos
.end
;
5619 pn
= UnaryNode::create(tc
);
5622 pn
->pn_type
= TOK_SEMI
;
5623 pn
->pn_pos
= pn2
->pn_pos
;
5626 switch (PN_TYPE(pn2
)) {
5629 * Flag lambdas immediately applied as statements as instances of
5630 * the JS "module pattern". See CheckForImmediatelyAppliedLambda.
5632 if (PN_TYPE(pn2
->pn_head
) == TOK_FUNCTION
&&
5633 !pn2
->pn_head
->pn_funbox
->node
->isFunArg()) {
5634 pn2
->pn_head
->pn_funbox
->tcflags
|= TCF_FUN_MODULE_PATTERN
;
5639 * Keep track of all apparent methods created by assignments such
5640 * as this.foo = function (...) {...} in a function that could end
5641 * up a constructor function. See Parser::setFunctionKinds.
5644 PN_OP(pn2
) == JSOP_NOP
&&
5645 PN_OP(pn2
->pn_left
) == JSOP_SETPROP
&&
5646 PN_OP(pn2
->pn_left
->pn_expr
) == JSOP_THIS
&&
5647 PN_OP(pn2
->pn_right
) == JSOP_LAMBDA
) {
5648 JS_ASSERT(!pn2
->pn_defn
);
5649 JS_ASSERT(!pn2
->pn_used
);
5650 pn2
->pn_right
->pn_link
= tc
->funbox
->methods
;
5651 tc
->funbox
->methods
= pn2
->pn_right
;
5659 /* Check termination of this primitive statement. */
5660 return MatchOrInsertSemicolon(context
, &tokenStream
) ? pn
: NULL
;
5664 NoteArgumentsUse(JSTreeContext
*tc
)
5666 JS_ASSERT(tc
->inFunction());
5667 tc
->flags
|= TCF_FUN_USES_ARGUMENTS
;
5669 tc
->funbox
->node
->pn_dflags
|= PND_FUNARG
;
5673 Parser::variables(bool inLetHead
)
5677 JSStmtInfo
*scopeStmt
;
5679 JSParseNode
*pn
, *pn2
;
5683 * The three options here are:
5684 * - TOK_LET: We are parsing a let declaration.
5685 * - TOK_LP: We are parsing the head of a let block.
5686 * - Otherwise, we're parsing var declarations.
5688 tt
= tokenStream
.currentToken().type
;
5689 let
= (tt
== TOK_LET
|| tt
== TOK_LP
);
5690 JS_ASSERT(let
|| tt
== TOK_VAR
);
5692 #if JS_HAS_BLOCK_SCOPE
5693 bool popScope
= (inLetHead
|| (let
&& (tc
->flags
& TCF_IN_FOR_INIT
)));
5694 JSStmtInfo
*save
= tc
->topStmt
, *saveScope
= tc
->topScopeStmt
;
5697 /* Make sure that statement set up the tree context correctly. */
5698 scopeStmt
= tc
->topScopeStmt
;
5700 while (scopeStmt
&& !(scopeStmt
->flags
& SIF_SCOPE
)) {
5701 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt
));
5702 scopeStmt
= scopeStmt
->downScope
;
5704 JS_ASSERT(scopeStmt
);
5707 data
.op
= let
? JSOP_NOP
: tokenStream
.currentToken().t_op
;
5708 pn
= ListNode::create(tc
);
5711 pn
->pn_op
= data
.op
;
5715 * SpiderMonkey const is really "write once per initialization evaluation"
5716 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5717 * this code will change soon.
5720 JS_ASSERT(tc
->blockChain
== scopeStmt
->blockObj
);
5721 data
.binder
= BindLet
;
5722 data
.let
.overflow
= JSMSG_TOO_MANY_LOCALS
;
5724 data
.binder
= BindVarOrConst
;
5728 tt
= tokenStream
.getToken();
5729 #if JS_HAS_DESTRUCTURING
5730 if (tt
== TOK_LB
|| tt
== TOK_LC
) {
5731 tc
->flags
|= TCF_DECL_DESTRUCTURING
;
5732 pn2
= primaryExpr(tt
, JS_FALSE
);
5733 tc
->flags
&= ~TCF_DECL_DESTRUCTURING
;
5737 if (!CheckDestructuring(context
, &data
, pn2
, NULL
, tc
))
5739 if ((tc
->flags
& TCF_IN_FOR_INIT
) &&
5740 tokenStream
.peekToken() == TOK_IN
) {
5745 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_BAD_DESTRUCT_DECL
);
5746 if (tokenStream
.currentToken().t_op
!= JSOP_NOP
)
5749 #if JS_HAS_BLOCK_SCOPE
5751 tc
->topStmt
= save
->down
;
5752 tc
->topScopeStmt
= saveScope
->downScope
;
5755 JSParseNode
*init
= assignExpr();
5756 #if JS_HAS_BLOCK_SCOPE
5759 tc
->topScopeStmt
= saveScope
;
5763 if (!init
|| !UndominateInitializers(pn2
, init
, tc
))
5766 pn2
= JSParseNode::newBinaryOrAppend(TOK_ASSIGN
, JSOP_NOP
, pn2
, init
, tc
);
5772 #endif /* JS_HAS_DESTRUCTURING */
5774 if (tt
!= TOK_NAME
) {
5775 if (tt
!= TOK_ERROR
) {
5776 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5777 JSMSG_NO_VARIABLE_NAME
);
5782 atom
= tokenStream
.currentToken().t_atom
;
5783 pn2
= NewBindingNode(atom
, tc
, let
);
5786 if (data
.op
== JSOP_DEFCONST
)
5787 pn2
->pn_dflags
|= PND_CONST
;
5789 if (!data
.binder(context
, &data
, atom
, tc
))
5793 if (tokenStream
.matchToken(TOK_ASSIGN
)) {
5794 if (tokenStream
.currentToken().t_op
!= JSOP_NOP
)
5797 #if JS_HAS_BLOCK_SCOPE
5799 tc
->topStmt
= save
->down
;
5800 tc
->topScopeStmt
= saveScope
->downScope
;
5803 JSParseNode
*init
= assignExpr();
5804 #if JS_HAS_BLOCK_SCOPE
5807 tc
->topScopeStmt
= saveScope
;
5814 pn2
= MakeAssignment(pn2
, init
, tc
);
5818 pn2
->pn_expr
= init
;
5821 pn2
->pn_op
= (PN_OP(pn2
) == JSOP_ARGUMENTS
)
5823 : (pn2
->pn_dflags
& PND_GVAR
)
5825 : (pn2
->pn_dflags
& PND_BOUND
)
5827 : (data
.op
== JSOP_DEFCONST
)
5831 NoteLValue(context
, pn2
, tc
, data
.fresh
? PND_INITIALIZED
: PND_ASSIGNED
);
5833 /* The declarator's position must include the initializer. */
5834 pn2
->pn_pos
.end
= init
->pn_pos
.end
;
5836 if (tc
->inFunction() &&
5837 atom
== context
->runtime
->atomState
.argumentsAtom
) {
5838 NoteArgumentsUse(tc
);
5840 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5843 } while (tokenStream
.matchToken(TOK_COMMA
));
5845 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5849 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5850 JSMSG_BAD_VAR_INIT
);
5857 JSParseNode
*pn
, *pn2
;
5860 if (pn
&& tokenStream
.matchToken(TOK_COMMA
)) {
5861 pn2
= ListNode::create(tc
);
5864 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
5868 #if JS_HAS_GENERATORS
5870 if (pn2
->pn_type
== TOK_YIELD
&& !pn2
->pn_parens
) {
5871 ReportCompileErrorNumber(context
, &tokenStream
, pn2
, JSREPORT_ERROR
,
5872 JSMSG_BAD_GENERATOR_SYNTAX
,
5881 } while (tokenStream
.matchToken(TOK_COMMA
));
5882 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5888 Parser::assignExpr()
5890 JSParseNode
*pn
, *rhs
;
5894 JS_CHECK_RECURSION(context
, return NULL
);
5896 #if JS_HAS_GENERATORS
5897 if (tokenStream
.matchToken(TOK_YIELD
, TSF_OPERAND
))
5898 return returnOrYield(true);
5905 tt
= tokenStream
.getToken();
5906 if (tt
!= TOK_ASSIGN
) {
5907 tokenStream
.ungetToken();
5911 op
= tokenStream
.currentToken().t_op
;
5912 switch (pn
->pn_type
) {
5914 if (!CheckStrictAssignment(context
, tc
, pn
))
5916 pn
->pn_op
= JSOP_SETNAME
;
5917 NoteLValue(context
, pn
, tc
);
5920 pn
->pn_op
= JSOP_SETPROP
;
5923 pn
->pn_op
= JSOP_SETELEM
;
5925 #if JS_HAS_DESTRUCTURING
5928 if (op
!= JSOP_NOP
) {
5929 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5930 JSMSG_BAD_DESTRUCT_ASS
);
5934 if (!rhs
|| !CheckDestructuring(context
, NULL
, pn
, rhs
, tc
))
5936 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN
, op
, pn
, rhs
, tc
);
5939 if (!MakeSetCall(context
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
5942 #if JS_HAS_XML_SUPPORT
5944 if (pn
->pn_op
== JSOP_XMLNAME
) {
5945 pn
->pn_op
= JSOP_SETXMLNAME
;
5951 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
5952 JSMSG_BAD_LEFTSIDE_OF_ASS
);
5957 if (rhs
&& PN_TYPE(pn
) == TOK_NAME
&& pn
->pn_used
) {
5958 JSDefinition
*dn
= pn
->pn_lexdef
;
5961 * If the definition is not flagged as assigned, we must have imputed
5962 * the initialized flag to it, to optimize for flat closures. But that
5963 * optimization uses source coordinates to check dominance relations,
5964 * so we must extend the end of the definition to cover the right-hand
5965 * side of this assignment, i.e., the initializer.
5967 if (!dn
->isAssigned()) {
5968 JS_ASSERT(dn
->isInitialized());
5969 dn
->pn_pos
.end
= rhs
->pn_pos
.end
;
5973 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN
, op
, pn
, rhs
, tc
);
5979 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
;
5983 if (pn
&& tokenStream
.matchToken(TOK_HOOK
)) {
5985 pn
= TernaryNode::create(tc
);
5990 * Always accept the 'in' operator in the middle clause of a ternary,
5991 * where it's unambiguous, even if we might be parsing the init of a
5994 oldflags
= tc
->flags
;
5995 tc
->flags
&= ~TCF_IN_FOR_INIT
;
5997 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
6001 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
6005 pn
->pn_pos
.begin
= pn1
->pn_pos
.begin
;
6006 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
6020 while (pn
&& tokenStream
.matchToken(TOK_OR
))
6021 pn
= JSParseNode::newBinaryOrAppend(TOK_OR
, JSOP_OR
, pn
, andExpr(), tc
);
6031 while (pn
&& tokenStream
.matchToken(TOK_AND
))
6032 pn
= JSParseNode::newBinaryOrAppend(TOK_AND
, JSOP_AND
, pn
, bitOrExpr(), tc
);
6042 while (pn
&& tokenStream
.matchToken(TOK_BITOR
)) {
6043 pn
= JSParseNode::newBinaryOrAppend(TOK_BITOR
, JSOP_BITOR
, pn
, bitXorExpr(), tc
);
6049 Parser::bitXorExpr()
6054 while (pn
&& tokenStream
.matchToken(TOK_BITXOR
)) {
6055 pn
= JSParseNode::newBinaryOrAppend(TOK_BITXOR
, JSOP_BITXOR
, pn
, bitAndExpr(), tc
);
6061 Parser::bitAndExpr()
6066 while (pn
&& tokenStream
.matchToken(TOK_BITAND
))
6067 pn
= JSParseNode::newBinaryOrAppend(TOK_BITAND
, JSOP_BITAND
, pn
, eqExpr(), tc
);
6078 while (pn
&& tokenStream
.matchToken(TOK_EQOP
)) {
6079 op
= tokenStream
.currentToken().t_op
;
6080 pn
= JSParseNode::newBinaryOrAppend(TOK_EQOP
, op
, pn
, relExpr(), tc
);
6091 uintN inForInitFlag
= tc
->flags
& TCF_IN_FOR_INIT
;
6094 * Uses of the in operator in shiftExprs are always unambiguous,
6095 * so unset the flag that prohibits recognizing it.
6097 tc
->flags
&= ~TCF_IN_FOR_INIT
;
6101 (tokenStream
.matchToken(TOK_RELOP
) ||
6103 * Recognize the 'in' token as an operator only if we're not
6104 * currently in the init expr of a for loop.
6106 (inForInitFlag
== 0 && tokenStream
.matchToken(TOK_IN
)) ||
6107 tokenStream
.matchToken(TOK_INSTANCEOF
))) {
6108 tt
= tokenStream
.currentToken().type
;
6109 op
= tokenStream
.currentToken().t_op
;
6110 pn
= JSParseNode::newBinaryOrAppend(tt
, op
, pn
, shiftExpr(), tc
);
6112 /* Restore previous state of inForInit flag. */
6113 tc
->flags
|= inForInitFlag
;
6125 while (pn
&& tokenStream
.matchToken(TOK_SHOP
)) {
6126 op
= tokenStream
.currentToken().t_op
;
6127 pn
= JSParseNode::newBinaryOrAppend(TOK_SHOP
, op
, pn
, addExpr(), tc
);
6141 (tokenStream
.matchToken(TOK_PLUS
) ||
6142 tokenStream
.matchToken(TOK_MINUS
))) {
6143 tt
= tokenStream
.currentToken().type
;
6144 op
= (tt
== TOK_PLUS
) ? JSOP_ADD
: JSOP_SUB
;
6145 pn
= JSParseNode::newBinaryOrAppend(tt
, op
, pn
, mulExpr(), tc
);
6159 (tokenStream
.matchToken(TOK_STAR
) ||
6160 tokenStream
.matchToken(TOK_DIVOP
))) {
6161 tt
= tokenStream
.currentToken().type
;
6162 op
= tokenStream
.currentToken().t_op
;
6163 pn
= JSParseNode::newBinaryOrAppend(tt
, op
, pn
, unaryExpr(), tc
);
6168 static JSParseNode
*
6169 SetLvalKid(JSContext
*cx
, TokenStream
*ts
, JSTreeContext
*tc
,
6170 JSParseNode
*pn
, JSParseNode
*kid
, const char *name
)
6172 if (kid
->pn_type
!= TOK_NAME
&&
6173 kid
->pn_type
!= TOK_DOT
&&
6174 (kid
->pn_type
!= TOK_LP
||
6175 (kid
->pn_op
!= JSOP_CALL
&& kid
->pn_op
!= JSOP_EVAL
&& kid
->pn_op
!= JSOP_APPLY
)) &&
6176 #if JS_HAS_XML_SUPPORT
6177 (kid
->pn_type
!= TOK_UNARYOP
|| kid
->pn_op
!= JSOP_XMLNAME
) &&
6179 kid
->pn_type
!= TOK_LB
) {
6180 ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
, JSMSG_BAD_OPERAND
, name
);
6183 if (!CheckStrictAssignment(cx
, tc
, kid
))
6189 static const char incop_name_str
[][10] = {"increment", "decrement"};
6192 SetIncOpKid(JSContext
*cx
, TokenStream
*ts
, JSTreeContext
*tc
,
6193 JSParseNode
*pn
, JSParseNode
*kid
,
6194 TokenKind tt
, JSBool preorder
)
6198 kid
= SetLvalKid(cx
, ts
, tc
, pn
, kid
, incop_name_str
[tt
== TOK_DEC
]);
6201 switch (kid
->pn_type
) {
6203 op
= (tt
== TOK_INC
)
6204 ? (preorder
? JSOP_INCNAME
: JSOP_NAMEINC
)
6205 : (preorder
? JSOP_DECNAME
: JSOP_NAMEDEC
);
6206 NoteLValue(cx
, kid
, tc
);
6210 op
= (tt
== TOK_INC
)
6211 ? (preorder
? JSOP_INCPROP
: JSOP_PROPINC
)
6212 : (preorder
? JSOP_DECPROP
: JSOP_PROPDEC
);
6216 if (!MakeSetCall(cx
, kid
, tc
, JSMSG_BAD_INCOP_OPERAND
))
6219 #if JS_HAS_XML_SUPPORT
6221 if (kid
->pn_op
== JSOP_XMLNAME
)
6222 kid
->pn_op
= JSOP_SETXMLNAME
;
6226 op
= (tt
== TOK_INC
)
6227 ? (preorder
? JSOP_INCELEM
: JSOP_ELEMINC
)
6228 : (preorder
? JSOP_DECELEM
: JSOP_ELEMDEC
);
6242 JSParseNode
*pn
, *pn2
;
6244 JS_CHECK_RECURSION(context
, return NULL
);
6246 TokenKind tt
= tokenStream
.getToken(TSF_OPERAND
);
6251 pn
= UnaryNode::create(tc
);
6254 pn
->pn_type
= TOK_UNARYOP
; /* PLUS and MINUS are binary */
6255 pn
->pn_op
= tokenStream
.currentToken().t_op
;
6259 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6265 pn
= UnaryNode::create(tc
);
6268 pn2
= memberExpr(JS_TRUE
);
6271 if (!SetIncOpKid(context
, &tokenStream
, tc
, pn
, pn2
, tt
, JS_TRUE
))
6273 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6278 pn
= UnaryNode::create(tc
);
6284 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6287 * Under ECMA3, deleting any unary expression is valid -- it simply
6288 * returns true. Here we fold constants before checking for a call
6289 * expression, in order to rule out delete of a generator expression.
6291 if (!js_FoldConstants(context
, pn2
, tc
))
6293 switch (pn2
->pn_type
) {
6295 if (pn2
->pn_op
!= JSOP_SETCALL
&&
6296 !MakeSetCall(context
, pn2
, tc
, JSMSG_BAD_DELETE_OPERAND
)) {
6301 if (!ReportStrictModeError(context
, &tokenStream
, tc
, pn
,
6302 JSMSG_DEPRECATED_DELETE_OPERAND
))
6304 pn2
->pn_op
= JSOP_DELNAME
;
6305 if (pn2
->pn_atom
== context
->runtime
->atomState
.argumentsAtom
)
6306 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6317 tokenStream
.ungetToken();
6318 pn
= memberExpr(JS_TRUE
);
6322 /* Don't look across a newline boundary for a postfix incop. */
6323 if (tokenStream
.onCurrentLine(pn
->pn_pos
)) {
6324 tt
= tokenStream
.peekTokenSameLine(TSF_OPERAND
);
6325 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
6326 (void) tokenStream
.getToken();
6327 pn2
= UnaryNode::create(tc
);
6330 if (!SetIncOpKid(context
, &tokenStream
, tc
, pn2
, pn
, tt
, JS_FALSE
))
6332 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6341 #if JS_HAS_GENERATORS
6344 * A dedicated helper for transplanting the comprehension expression E in
6346 * [E for (V in I)] // array comprehension
6347 * (E for (V in I)) // generator expression
6349 * from its initial location in the AST, on the left of the 'for', to its final
6350 * position on the right. To avoid a separate pass we do this by adjusting the
6351 * blockids and name binding links that were established when E was parsed.
6353 * A generator expression desugars like so:
6355 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6357 * so the transplanter must adjust static level as well as blockid. E's source
6358 * coordinates in root->pn_pos are critical to deciding which binding links to
6359 * preserve and which to cut.
6361 * NB: This is not a general tree transplanter -- it knows in particular that
6362 * the one or more bindings induced by V have not yet been created.
6364 class CompExprTransplanter
{
6372 CompExprTransplanter(JSParseNode
*pn
, JSTreeContext
*tc
, bool ge
, uintN adj
)
6373 : root(pn
), tc(tc
), genexp(ge
), adjust(adj
), funcLevel(0)
6377 bool transplant(JSParseNode
*pn
);
6381 * Any definitions nested within the comprehension expression of a generator
6382 * expression must move "down" one static level, which of course increases the
6383 * upvar-frame-skip count.
6386 BumpStaticLevel(JSParseNode
*pn
, JSTreeContext
*tc
)
6388 if (pn
->pn_cookie
!= FREE_UPVAR_COOKIE
) {
6389 uintN level
= UPVAR_FRAME_SKIP(pn
->pn_cookie
) + 1;
6391 JS_ASSERT(level
>= tc
->staticLevel
);
6392 if (level
>= FREE_STATIC_LEVEL
) {
6393 JS_ReportErrorNumber(tc
->parser
->context
, js_GetErrorMessage
, NULL
,
6394 JSMSG_TOO_DEEP
, js_function_str
);
6398 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(level
, UPVAR_FRAME_SLOT(pn
->pn_cookie
));
6404 AdjustBlockId(JSParseNode
*pn
, uintN adjust
, JSTreeContext
*tc
)
6406 JS_ASSERT(pn
->pn_arity
== PN_LIST
|| pn
->pn_arity
== PN_FUNC
|| pn
->pn_arity
== PN_NAME
);
6407 pn
->pn_blockid
+= adjust
;
6408 if (pn
->pn_blockid
>= tc
->blockidGen
)
6409 tc
->blockidGen
= pn
->pn_blockid
+ 1;
6413 CompExprTransplanter::transplant(JSParseNode
*pn
)
6418 switch (pn
->pn_arity
) {
6420 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
6422 if (pn
->pn_pos
>= root
->pn_pos
)
6423 AdjustBlockId(pn
, adjust
, tc
);
6427 transplant(pn
->pn_kid1
);
6428 transplant(pn
->pn_kid2
);
6429 transplant(pn
->pn_kid3
);
6433 transplant(pn
->pn_left
);
6435 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6436 if (pn
->pn_right
!= pn
->pn_left
)
6437 transplant(pn
->pn_right
);
6441 transplant(pn
->pn_kid
);
6447 * Only the first level of transplant recursion through functions needs
6448 * to reparent the funbox, since all descendant functions are correctly
6449 * linked under the top-most funbox. But every visit to this case needs
6450 * to update funbox->level.
6452 * Recall that funbox->level is the static level of the code containing
6453 * the definition or expression of the function and not the static level
6454 * of the function's body.
6456 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6458 funbox
->level
= tc
->staticLevel
+ funcLevel
;
6459 if (++funcLevel
== 1 && genexp
) {
6460 JSFunctionBox
*parent
= tc
->funbox
;
6462 JSFunctionBox
**funboxp
= &tc
->parent
->functionList
;
6463 while (*funboxp
!= funbox
)
6464 funboxp
= &(*funboxp
)->siblings
;
6465 *funboxp
= funbox
->siblings
;
6467 funbox
->parent
= parent
;
6468 funbox
->siblings
= parent
->kids
;
6469 parent
->kids
= funbox
;
6470 funbox
->level
= tc
->staticLevel
;
6476 transplant(pn
->maybeExpr());
6477 if (pn
->pn_arity
== PN_FUNC
)
6481 if (genexp
&& !BumpStaticLevel(pn
, tc
))
6483 } else if (pn
->pn_used
) {
6484 JS_ASSERT(pn
->pn_op
!= JSOP_NOP
);
6485 JS_ASSERT(pn
->pn_cookie
== FREE_UPVAR_COOKIE
);
6487 JSDefinition
*dn
= pn
->pn_lexdef
;
6488 JS_ASSERT(dn
->pn_defn
);
6491 * Adjust the definition's block id only if it is a placeholder not
6492 * to the left of the root node, and if pn is the last use visited
6493 * in the comprehension expression (to avoid adjusting the blockid
6496 * Non-placeholder definitions within the comprehension expression
6497 * will be visited further below.
6499 if (dn
->isPlaceholder() && dn
->pn_pos
>= root
->pn_pos
&& dn
->dn_uses
== pn
) {
6500 if (genexp
&& !BumpStaticLevel(dn
, tc
))
6502 AdjustBlockId(dn
, adjust
, tc
);
6505 JSAtom
*atom
= pn
->pn_atom
;
6507 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
6508 JS_ASSERT(!stmt
|| stmt
!= tc
->topStmt
);
6510 if (genexp
&& PN_OP(dn
) != JSOP_CALLEE
) {
6511 JS_ASSERT(!tc
->decls
.lookup(atom
));
6513 if (dn
->pn_pos
< root
->pn_pos
|| dn
->isPlaceholder()) {
6514 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->parser
, dn
->pn_atom
);
6518 if (dn
->pn_pos
>= root
->pn_pos
) {
6519 tc
->parent
->lexdeps
.remove(tc
->parser
, atom
);
6521 JSDefinition
*dn2
= (JSDefinition
*)NameNode::create(dn
->pn_atom
, tc
);
6525 dn2
->pn_type
= dn
->pn_type
;
6526 dn2
->pn_pos
= root
->pn_pos
;
6527 dn2
->pn_defn
= true;
6528 dn2
->pn_dflags
|= PND_PLACEHOLDER
;
6530 JSParseNode
**pnup
= &dn
->dn_uses
;
6532 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_pos
>= root
->pn_pos
) {
6533 pnu
->pn_lexdef
= dn2
;
6534 dn2
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
6535 pnup
= &pnu
->pn_link
;
6537 dn2
->dn_uses
= dn
->dn_uses
;
6538 dn
->dn_uses
= *pnup
;
6544 ALE_SET_DEFN(ale
, dn
);
6549 if (pn
->pn_pos
>= root
->pn_pos
)
6550 AdjustBlockId(pn
, adjust
, tc
);
6554 transplant(pn
->pn_tree
);
6561 * Starting from a |for| keyword after the first array initialiser element or
6562 * an expression in an open parenthesis, parse the tail of the comprehension
6563 * or generator expression signified by this |for| keyword in context.
6565 * Return null on failure, else return the top-most parse node for the array
6566 * comprehension or generator expression, with a unary node as the body of the
6567 * (possibly nested) for-loop, initialized by |type, op, kid|.
6570 Parser::comprehensionTail(JSParseNode
*kid
, uintN blockid
,
6571 TokenKind type
, JSOp op
)
6574 JSParseNode
*pn
, *pn2
, *pn3
, **pnp
;
6575 JSStmtInfo stmtInfo
;
6580 JS_ASSERT(tokenStream
.currentToken().type
== TOK_FOR
);
6582 if (type
== TOK_SEMI
) {
6584 * Generator expression desugars to an immediately applied lambda that
6585 * yields the next value from a for-in loop (possibly nested, and with
6586 * optional if guard). Make pn be the TOK_LC body node.
6588 pn
= PushLexicalScope(context
, &tokenStream
, tc
, &stmtInfo
);
6591 adjust
= pn
->pn_blockid
- blockid
;
6593 JS_ASSERT(type
== TOK_ARRAYPUSH
);
6596 * Make a parse-node and literal object representing the block scope of
6597 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
6598 * aka the array initialiser case, has passed the blockid to claim for
6599 * the comprehension's block scope. We allocate that id or one above it
6600 * here, by calling js_PushLexicalScope.
6602 * In the case of a comprehension expression that has nested blocks
6603 * (e.g., let expressions), we will allocate a higher blockid but then
6604 * slide all blocks "to the right" to make room for the comprehension's
6607 adjust
= tc
->blockid();
6608 pn
= PushLexicalScope(context
, &tokenStream
, tc
, &stmtInfo
);
6612 JS_ASSERT(blockid
<= pn
->pn_blockid
);
6613 JS_ASSERT(blockid
< tc
->blockidGen
);
6614 JS_ASSERT(tc
->bodyid
< blockid
);
6615 pn
->pn_blockid
= stmtInfo
.blockid
= blockid
;
6616 JS_ASSERT(adjust
< blockid
);
6617 adjust
= blockid
- adjust
;
6622 CompExprTransplanter
transplanter(kid
, tc
, type
== TOK_SEMI
, adjust
);
6623 transplanter
.transplant(kid
);
6627 data
.binder
= BindLet
;
6628 data
.let
.overflow
= JSMSG_ARRAY_INIT_TOO_BIG
;
6632 * FOR node is binary, left is loop control and right is body. Use
6633 * index to count each block-local let-variable on the left-hand side
6636 pn2
= BinaryNode::create(tc
);
6640 pn2
->pn_op
= JSOP_ITER
;
6641 pn2
->pn_iflags
= JSITER_ENUMERATE
;
6642 if (tokenStream
.matchToken(TOK_NAME
)) {
6643 if (tokenStream
.currentToken().t_atom
== context
->runtime
->atomState
.eachAtom
)
6644 pn2
->pn_iflags
|= JSITER_FOREACH
;
6646 tokenStream
.ungetToken();
6648 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
6651 tt
= tokenStream
.getToken();
6653 #if JS_HAS_DESTRUCTURING
6656 tc
->flags
|= TCF_DECL_DESTRUCTURING
;
6657 pn3
= primaryExpr(tt
, JS_FALSE
);
6658 tc
->flags
&= ~TCF_DECL_DESTRUCTURING
;
6665 atom
= tokenStream
.currentToken().t_atom
;
6668 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6669 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6670 * in the operand stack frame. The code generator computes that,
6671 * and it tries to bind all names to slots, so we must let it do
6674 pn3
= NewBindingNode(atom
, tc
, true);
6680 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
6681 JSMSG_NO_VARIABLE_NAME
);
6687 MUST_MATCH_TOKEN(TOK_IN
, JSMSG_IN_AFTER_FOR_NAME
);
6688 JSParseNode
*pn4
= expr();
6691 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
6694 #if JS_HAS_DESTRUCTURING
6697 if (!CheckDestructuring(context
, &data
, pn3
, NULL
, tc
))
6700 if (JSVERSION_NUMBER(context
) == JSVERSION_1_7
) {
6701 /* Destructuring requires [key, value] enumeration in JS1.7. */
6702 if (pn3
->pn_type
!= TOK_RB
|| pn3
->pn_count
!= 2) {
6703 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
6704 JSMSG_BAD_FOR_LEFTSIDE
);
6708 JS_ASSERT(pn2
->pn_op
== JSOP_ITER
);
6709 JS_ASSERT(pn2
->pn_iflags
& JSITER_ENUMERATE
);
6710 if (!(pn2
->pn_iflags
& JSITER_FOREACH
))
6711 pn2
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
6718 if (!data
.binder(context
, &data
, atom
, tc
))
6725 pn2
->pn_left
= JSParseNode::newBinaryOrAppend(TOK_IN
, JSOP_NOP
, pn3
, pn4
, tc
);
6729 pnp
= &pn2
->pn_right
;
6730 } while (tokenStream
.matchToken(TOK_FOR
));
6732 if (tokenStream
.matchToken(TOK_IF
)) {
6733 pn2
= TernaryNode::create(tc
);
6736 pn2
->pn_kid1
= condition();
6740 pnp
= &pn2
->pn_kid2
;
6743 pn2
= UnaryNode::create(tc
);
6746 pn2
->pn_type
= type
;
6755 #if JS_HAS_GENERATOR_EXPRS
6758 * Starting from a |for| keyword after an expression, parse the comprehension
6759 * tail completing this generator expression. Wrap the expression at kid in a
6760 * generator function that is immediately called to evaluate to the generator
6761 * iterator that is the value of this generator expression.
6763 * Callers pass a blank unary node via pn, which generatorExpr fills in as the
6764 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6765 * expression-statement node that constitutes the body of the |for| loop(s) in
6766 * the generator function.
6768 * Note how unlike Python, we do not evaluate the expression to the right of
6769 * the first |in| in the chain of |for| heads. Instead, a generator expression
6770 * is merely sugar for a generator function expression and its application.
6773 Parser::generatorExpr(JSParseNode
*pn
, JSParseNode
*kid
)
6775 /* Initialize pn, connecting it to kid. */
6776 JS_ASSERT(pn
->pn_arity
== PN_UNARY
);
6777 pn
->pn_type
= TOK_YIELD
;
6778 pn
->pn_op
= JSOP_YIELD
;
6779 pn
->pn_parens
= true;
6780 pn
->pn_pos
= kid
->pn_pos
;
6782 pn
->pn_hidden
= true;
6784 /* Make a new node for the desugared generator function. */
6785 JSParseNode
*genfn
= FunctionNode::create(tc
);
6788 genfn
->pn_type
= TOK_FUNCTION
;
6789 genfn
->pn_op
= JSOP_LAMBDA
;
6790 JS_ASSERT(!genfn
->pn_body
);
6791 genfn
->pn_dflags
= PND_FUNARG
;
6794 JSTreeContext
*outertc
= tc
;
6795 JSTreeContext
gentc(tc
->parser
);
6797 JSFunctionBox
*funbox
= EnterFunction(genfn
, &gentc
);
6802 * We have to dance around a bit to propagate sharp variables from
6803 * outertc to gentc before setting TCF_HAS_SHARPS implicitly by
6804 * propagating all of outertc's TCF_FUN_FLAGS flags. As below, we have
6805 * to be conservative by leaving TCF_HAS_SHARPS set in outertc if we
6806 * do propagate to gentc.
6808 if (outertc
->flags
& TCF_HAS_SHARPS
) {
6809 gentc
.flags
|= TCF_IN_FUNCTION
;
6810 if (!gentc
.ensureSharpSlots())
6815 * We assume conservatively that any deoptimization flag in tc->flags
6816 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6817 * propagate these flags into genfn. For code simplicity we also do
6818 * not detect if the flags were only set in the kid and could be
6819 * removed from tc->flags.
6821 gentc
.flags
|= TCF_FUN_IS_GENERATOR
| TCF_GENEXP_LAMBDA
|
6822 (tc
->flags
& (TCF_FUN_FLAGS
& ~TCF_FUN_PARAM_ARGUMENTS
));
6823 funbox
->tcflags
|= gentc
.flags
;
6824 genfn
->pn_funbox
= funbox
;
6825 genfn
->pn_blockid
= gentc
.bodyid
;
6827 JSParseNode
*body
= comprehensionTail(pn
, outertc
->blockid());
6830 JS_ASSERT(!genfn
->pn_body
);
6831 genfn
->pn_body
= body
;
6832 genfn
->pn_pos
.begin
= body
->pn_pos
.begin
= kid
->pn_pos
.begin
;
6833 genfn
->pn_pos
.end
= body
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
6835 if (!LeaveFunction(genfn
, &gentc
))
6840 * Our result is a call expression that invokes the anonymous generator
6843 JSParseNode
*result
= ListNode::create(tc
);
6846 result
->pn_type
= TOK_LP
;
6847 result
->pn_op
= JSOP_CALL
;
6848 result
->pn_pos
.begin
= genfn
->pn_pos
.begin
;
6849 result
->initList(genfn
);
6853 static const char js_generator_str
[] = "generator";
6855 #endif /* JS_HAS_GENERATOR_EXPRS */
6856 #endif /* JS_HAS_GENERATORS */
6859 Parser::argumentList(JSParseNode
*listNode
)
6861 if (tokenStream
.matchToken(TOK_RP
, TSF_OPERAND
))
6865 JSParseNode
*argNode
= assignExpr();
6868 #if JS_HAS_GENERATORS
6869 if (argNode
->pn_type
== TOK_YIELD
&&
6870 !argNode
->pn_parens
&&
6871 tokenStream
.peekToken() == TOK_COMMA
) {
6872 ReportCompileErrorNumber(context
, &tokenStream
, argNode
, JSREPORT_ERROR
,
6873 JSMSG_BAD_GENERATOR_SYNTAX
,
6878 #if JS_HAS_GENERATOR_EXPRS
6879 if (tokenStream
.matchToken(TOK_FOR
)) {
6880 JSParseNode
*pn
= UnaryNode::create(tc
);
6883 argNode
= generatorExpr(pn
, argNode
);
6886 if (listNode
->pn_count
> 1 ||
6887 tokenStream
.peekToken() == TOK_COMMA
) {
6888 ReportCompileErrorNumber(context
, &tokenStream
, argNode
, JSREPORT_ERROR
,
6889 JSMSG_BAD_GENERATOR_SYNTAX
,
6895 listNode
->append(argNode
);
6896 } while (tokenStream
.matchToken(TOK_COMMA
));
6898 if (tokenStream
.getToken() != TOK_RP
) {
6899 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
6900 JSMSG_PAREN_AFTER_ARGS
);
6906 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6907 static JSParseNode
*
6908 CheckForImmediatelyAppliedLambda(JSParseNode
*pn
)
6910 if (pn
->pn_type
== TOK_FUNCTION
) {
6911 JS_ASSERT(pn
->pn_arity
== PN_FUNC
);
6913 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6914 JS_ASSERT(((JSFunction
*) funbox
->object
)->flags
& JSFUN_LAMBDA
);
6915 if (!(funbox
->tcflags
& (TCF_FUN_USES_ARGUMENTS
| TCF_FUN_USES_OWN_NAME
)))
6916 pn
->pn_dflags
&= ~PND_FUNARG
;
6922 Parser::memberExpr(JSBool allowCallSyntax
)
6924 JSParseNode
*pn
, *pn2
, *pn3
;
6926 JS_CHECK_RECURSION(context
, return NULL
);
6928 /* Check for new expression first. */
6929 TokenKind tt
= tokenStream
.getToken(TSF_OPERAND
);
6930 if (tt
== TOK_NEW
) {
6931 pn
= ListNode::create(tc
);
6934 pn2
= memberExpr(JS_FALSE
);
6937 pn2
= CheckForImmediatelyAppliedLambda(pn2
);
6938 pn
->pn_op
= JSOP_NEW
;
6940 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
6942 if (tokenStream
.matchToken(TOK_LP
) && !argumentList(pn
))
6944 if (pn
->pn_count
> ARGC_LIMIT
) {
6945 JS_ReportErrorNumber(context
, js_GetErrorMessage
, NULL
,
6946 JSMSG_TOO_MANY_CON_ARGS
);
6949 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
6951 pn
= primaryExpr(tt
, JS_FALSE
);
6955 if (pn
->pn_type
== TOK_ANYNAME
||
6956 pn
->pn_type
== TOK_AT
||
6957 pn
->pn_type
== TOK_DBLCOLON
) {
6958 pn2
= NewOrRecycledNode(tc
);
6961 pn2
->pn_type
= TOK_UNARYOP
;
6962 pn2
->pn_pos
= pn
->pn_pos
;
6963 pn2
->pn_op
= JSOP_XMLNAME
;
6964 pn2
->pn_arity
= PN_UNARY
;
6965 pn2
->pn_parens
= false;
6971 while ((tt
= tokenStream
.getToken()) > TOK_EOF
) {
6972 if (tt
== TOK_DOT
) {
6973 pn2
= NameNode::create(NULL
, tc
);
6976 #if JS_HAS_XML_SUPPORT
6977 tt
= tokenStream
.getToken(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
6978 pn3
= primaryExpr(tt
, JS_TRUE
);
6982 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
6983 if (tt
== TOK_NAME
&& pn3
->pn_type
== TOK_NAME
) {
6984 pn2
->pn_op
= JSOP_GETPROP
;
6986 pn2
->pn_atom
= pn3
->pn_atom
;
6987 RecycleTree(pn3
, tc
);
6990 pn2
->pn_type
= TOK_FILTER
;
6991 pn2
->pn_op
= JSOP_FILTER
;
6993 /* A filtering predicate is like a with statement. */
6994 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6995 } else if (TokenKindIsXML(PN_TYPE(pn3
))) {
6996 pn2
->pn_type
= TOK_LB
;
6997 pn2
->pn_op
= JSOP_GETELEM
;
6999 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7000 JSMSG_NAME_AFTER_DOT
);
7003 pn2
->pn_arity
= PN_BINARY
;
7005 pn2
->pn_right
= pn3
;
7008 MUST_MATCH_TOKEN_WITH_FLAGS(TOK_NAME
, JSMSG_NAME_AFTER_DOT
, TSF_KEYWORD_IS_NAME
);
7009 pn2
->pn_op
= JSOP_GETPROP
;
7011 pn2
->pn_atom
= tokenStream
.currentToken().t_atom
;
7013 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7014 pn2
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
7015 #if JS_HAS_XML_SUPPORT
7016 } else if (tt
== TOK_DBLDOT
) {
7017 pn2
= BinaryNode::create(tc
);
7020 tt
= tokenStream
.getToken(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
7021 pn3
= primaryExpr(tt
, JS_TRUE
);
7025 if (tt
== TOK_NAME
&& !pn3
->pn_parens
) {
7026 pn3
->pn_type
= TOK_STRING
;
7027 pn3
->pn_arity
= PN_NULLARY
;
7028 pn3
->pn_op
= JSOP_QNAMEPART
;
7029 } else if (!TokenKindIsXML(tt
)) {
7030 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7031 JSMSG_NAME_AFTER_DOT
);
7034 pn2
->pn_op
= JSOP_DESCENDANTS
;
7036 pn2
->pn_right
= pn3
;
7037 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7038 pn2
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
7040 } else if (tt
== TOK_LB
) {
7041 pn2
= BinaryNode::create(tc
);
7048 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
7049 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7050 pn2
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
7053 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
7054 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
7055 * the interpreter from fast property access. However, if the
7056 * bracketed string is a uint32, we rewrite pn3 to be a number
7057 * instead of a string.
7060 if (pn3
->pn_type
== TOK_STRING
) {
7063 if (!js_IdIsIndex(ATOM_TO_JSID(pn3
->pn_atom
), &index
)) {
7064 pn2
->pn_type
= TOK_DOT
;
7065 pn2
->pn_op
= JSOP_GETPROP
;
7066 pn2
->pn_arity
= PN_NAME
;
7068 pn2
->pn_atom
= pn3
->pn_atom
;
7071 pn3
->pn_type
= TOK_NUMBER
;
7072 pn3
->pn_op
= JSOP_DOUBLE
;
7073 pn3
->pn_dval
= index
;
7075 pn2
->pn_op
= JSOP_GETELEM
;
7077 pn2
->pn_right
= pn3
;
7079 } else if (allowCallSyntax
&& tt
== TOK_LP
) {
7080 pn2
= ListNode::create(tc
);
7083 pn2
->pn_op
= JSOP_CALL
;
7085 pn
= CheckForImmediatelyAppliedLambda(pn
);
7086 if (pn
->pn_op
== JSOP_NAME
) {
7087 if (pn
->pn_atom
== context
->runtime
->atomState
.evalAtom
) {
7088 /* Select JSOP_EVAL and flag tc as heavyweight. */
7089 pn2
->pn_op
= JSOP_EVAL
;
7090 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
7092 } else if (pn
->pn_op
== JSOP_GETPROP
) {
7093 if (pn
->pn_atom
== context
->runtime
->atomState
.applyAtom
||
7094 pn
->pn_atom
== context
->runtime
->atomState
.callAtom
) {
7095 /* Select JSOP_APPLY given foo.apply(...). */
7096 pn2
->pn_op
= JSOP_APPLY
;
7101 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7103 if (!argumentList(pn2
))
7105 if (pn2
->pn_count
> ARGC_LIMIT
) {
7106 JS_ReportErrorNumber(context
, js_GetErrorMessage
, NULL
,
7107 JSMSG_TOO_MANY_FUN_ARGS
);
7110 pn2
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
7112 tokenStream
.ungetToken();
7118 if (tt
== TOK_ERROR
)
7124 Parser::bracketedExpr()
7130 * Always accept the 'in' operator in a parenthesized expression,
7131 * where it's unambiguous, even if we might be parsing the init of a
7134 oldflags
= tc
->flags
;
7135 tc
->flags
&= ~TCF_IN_FOR_INIT
;
7137 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
7141 #if JS_HAS_XML_SUPPORT
7144 Parser::endBracketedExpr()
7148 pn
= bracketedExpr();
7152 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ATTR_EXPR
);
7157 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7159 * AttributeIdentifier:
7160 * @ PropertySelector
7161 * @ QualifiedIdentifier
7168 * QualifiedIdentifier:
7169 * PropertySelector :: PropertySelector
7170 * PropertySelector :: [ Expression ]
7172 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7174 * AttributeIdentifier:
7175 * @ QualifiedIdentifier
7182 * QualifiedIdentifier:
7183 * PropertySelector :: PropertySelector
7184 * PropertySelector :: [ Expression ]
7187 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7188 * for that rule to result in a name node, but ECMA-357 extends the grammar
7189 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7191 * QualifiedIdentifier:
7192 * PropertySelector QualifiedSuffix
7195 * :: PropertySelector
7199 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7201 * PrimaryExpression:
7202 * Identifier QualifiedSuffix
7204 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7205 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7208 Parser::propertySelector()
7212 pn
= NullaryNode::create(tc
);
7215 if (pn
->pn_type
== TOK_STAR
) {
7216 pn
->pn_type
= TOK_ANYNAME
;
7217 pn
->pn_op
= JSOP_ANYNAME
;
7218 pn
->pn_atom
= context
->runtime
->atomState
.starAtom
;
7220 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
7221 pn
->pn_op
= JSOP_QNAMEPART
;
7222 pn
->pn_arity
= PN_NAME
;
7223 pn
->pn_atom
= tokenStream
.currentToken().t_atom
;
7224 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
7230 Parser::qualifiedSuffix(JSParseNode
*pn
)
7232 JSParseNode
*pn2
, *pn3
;
7235 JS_ASSERT(tokenStream
.currentToken().type
== TOK_DBLCOLON
);
7236 pn2
= NameNode::create(NULL
, tc
);
7240 /* Left operand of :: must be evaluated if it is an identifier. */
7241 if (pn
->pn_op
== JSOP_QNAMEPART
)
7242 pn
->pn_op
= JSOP_NAME
;
7244 tt
= tokenStream
.getToken(TSF_KEYWORD_IS_NAME
);
7245 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7246 /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
7247 pn2
->pn_op
= JSOP_QNAMECONST
;
7248 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7249 pn2
->pn_atom
= (tt
== TOK_STAR
)
7250 ? context
->runtime
->atomState
.starAtom
7251 : tokenStream
.currentToken().t_atom
;
7253 pn2
->pn_cookie
= FREE_UPVAR_COOKIE
;
7258 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7259 JSMSG_SYNTAX_ERROR
);
7262 pn3
= endBracketedExpr();
7266 pn2
->pn_op
= JSOP_QNAME
;
7267 pn2
->pn_arity
= PN_BINARY
;
7268 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7269 pn2
->pn_pos
.end
= pn3
->pn_pos
.end
;
7271 pn2
->pn_right
= pn3
;
7276 Parser::qualifiedIdentifier()
7280 pn
= propertySelector();
7283 if (tokenStream
.matchToken(TOK_DBLCOLON
)) {
7284 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7285 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
7286 pn
= qualifiedSuffix(pn
);
7292 Parser::attributeIdentifier()
7294 JSParseNode
*pn
, *pn2
;
7297 JS_ASSERT(tokenStream
.currentToken().type
== TOK_AT
);
7298 pn
= UnaryNode::create(tc
);
7301 pn
->pn_op
= JSOP_TOATTRNAME
;
7302 tt
= tokenStream
.getToken(TSF_KEYWORD_IS_NAME
);
7303 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7304 pn2
= qualifiedIdentifier();
7305 } else if (tt
== TOK_LB
) {
7306 pn2
= endBracketedExpr();
7308 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7309 JSMSG_SYNTAX_ERROR
);
7319 * Make a TOK_LC unary node whose pn_kid is an expression.
7322 Parser::xmlExpr(JSBool inTag
)
7324 JSParseNode
*pn
, *pn2
;
7326 JS_ASSERT(tokenStream
.currentToken().type
== TOK_LC
);
7327 pn
= UnaryNode::create(tc
);
7332 * Turn off XML tag mode. We save the old value of the flag because it may
7333 * already be off: XMLExpr is called both from within a tag, and from
7334 * within text contained in an element, but outside of any start, end, or
7337 bool oldflag
= tokenStream
.isXMLTagMode();
7338 tokenStream
.setXMLTagMode(false);
7343 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_XML_EXPR
);
7344 tokenStream
.setXMLTagMode(oldflag
);
7346 pn
->pn_op
= inTag
? JSOP_XMLTAGEXPR
: JSOP_XMLELTEXPR
;
7351 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7352 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7353 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7354 * child of a container tag.
7357 Parser::xmlAtomNode()
7359 JSParseNode
*pn
= NullaryNode::create(tc
);
7362 const Token
&tok
= tokenStream
.currentToken();
7363 pn
->pn_op
= tok
.t_op
;
7364 pn
->pn_atom
= tok
.t_atom
;
7365 if (tok
.type
== TOK_XMLPI
)
7366 pn
->pn_atom2
= tok
.t_atom2
;
7371 * Parse the productions:
7374 * XMLName XMLNameExpr?
7375 * { Expr } XMLNameExpr?
7377 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7378 * a list of names and/or expressions, a single expression, or a single name.
7379 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7383 Parser::xmlNameExpr()
7385 JSParseNode
*pn
, *pn2
, *list
;
7390 tt
= tokenStream
.currentToken().type
;
7392 pn2
= xmlExpr(JS_TRUE
);
7396 JS_ASSERT(tt
== TOK_XMLNAME
);
7397 pn2
= xmlAtomNode();
7406 list
= ListNode::create(tc
);
7409 list
->pn_type
= TOK_XMLNAME
;
7410 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7412 list
->pn_xflags
= PNX_CANTFOLD
;
7415 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7418 } while ((tt
= tokenStream
.getToken()) == TOK_XMLNAME
|| tt
== TOK_LC
);
7420 tokenStream
.ungetToken();
7425 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7426 * at compile time into a JSXML tree.
7428 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7429 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7430 : (pn)->pn_type != TOK_LC)
7433 * Parse the productions:
7437 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7438 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7440 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7441 * produces a list of name and attribute values and/or braced expressions, a
7442 * single expression, or a single name.
7444 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7445 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7446 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7447 * we parsed exactly one expression.
7450 Parser::xmlTagContent(TokenKind tagtype
, JSAtom
**namep
)
7452 JSParseNode
*pn
, *pn2
, *list
;
7458 *namep
= (pn
->pn_arity
== PN_NULLARY
) ? pn
->pn_atom
: NULL
;
7461 while (tokenStream
.matchToken(TOK_XMLSPACE
)) {
7462 tt
= tokenStream
.getToken();
7463 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7464 tokenStream
.ungetToken();
7468 pn2
= xmlNameExpr();
7472 list
= ListNode::create(tc
);
7475 list
->pn_type
= tagtype
;
7476 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7481 if (!XML_FOLDABLE(pn2
))
7482 pn
->pn_xflags
|= PNX_CANTFOLD
;
7484 tokenStream
.matchToken(TOK_XMLSPACE
);
7485 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_NO_ASSIGN_IN_XML_ATTR
);
7486 tokenStream
.matchToken(TOK_XMLSPACE
);
7488 tt
= tokenStream
.getToken();
7489 if (tt
== TOK_XMLATTR
) {
7490 pn2
= xmlAtomNode();
7491 } else if (tt
== TOK_LC
) {
7492 pn2
= xmlExpr(JS_TRUE
);
7493 pn
->pn_xflags
|= PNX_CANTFOLD
;
7495 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7496 JSMSG_BAD_XML_ATTR_VALUE
);
7501 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7508 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7510 if ((tt) <= TOK_EOF) { \
7511 if ((tt) == TOK_EOF) { \
7512 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR, \
7513 JSMSG_END_OF_XML_SOURCE); \
7520 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7521 * that opens the end tag for the container.
7524 Parser::xmlElementContent(JSParseNode
*pn
)
7526 tokenStream
.setXMLTagMode(false);
7528 TokenKind tt
= tokenStream
.getToken(TSF_XMLTEXTMODE
);
7529 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7531 JS_ASSERT(tt
== TOK_XMLSPACE
|| tt
== TOK_XMLTEXT
);
7532 JSAtom
*textAtom
= tokenStream
.currentToken().t_atom
;
7534 /* Non-zero-length XML text scanned. */
7535 JSParseNode
*pn2
= xmlAtomNode();
7538 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7542 tt
= tokenStream
.getToken(TSF_OPERAND
);
7543 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7544 if (tt
== TOK_XMLETAGO
)
7549 pn2
= xmlExpr(JS_FALSE
);
7550 pn
->pn_xflags
|= PNX_CANTFOLD
;
7551 } else if (tt
== TOK_XMLSTAGO
) {
7552 pn2
= xmlElementOrList(JS_FALSE
);
7554 pn2
->pn_xflags
&= ~PNX_XMLROOT
;
7555 pn
->pn_xflags
|= pn2
->pn_xflags
;
7558 JS_ASSERT(tt
== TOK_XMLCDATA
|| tt
== TOK_XMLCOMMENT
||
7560 pn2
= xmlAtomNode();
7564 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7567 tokenStream
.setXMLTagMode(true);
7569 JS_ASSERT(tokenStream
.currentToken().type
== TOK_XMLETAGO
);
7574 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7577 Parser::xmlElementOrList(JSBool allowList
)
7579 JSParseNode
*pn
, *pn2
, *list
;
7581 JSAtom
*startAtom
, *endAtom
;
7583 JS_CHECK_RECURSION(context
, return NULL
);
7585 JS_ASSERT(tokenStream
.currentToken().type
== TOK_XMLSTAGO
);
7586 pn
= ListNode::create(tc
);
7590 tokenStream
.setXMLTagMode(true);
7591 tt
= tokenStream
.getToken();
7592 if (tt
== TOK_ERROR
)
7595 if (tt
== TOK_XMLNAME
|| tt
== TOK_LC
) {
7597 * XMLElement. Append the tag and its contents, if any, to pn.
7599 pn2
= xmlTagContent(TOK_XMLSTAGO
, &startAtom
);
7602 tokenStream
.matchToken(TOK_XMLSPACE
);
7604 tt
= tokenStream
.getToken();
7605 if (tt
== TOK_XMLPTAGC
) {
7606 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7607 if (pn2
->pn_type
== TOK_XMLSTAGO
) {
7609 RecycleTree(pn
, tc
);
7612 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
||
7613 pn2
->pn_type
== TOK_LC
);
7615 if (!XML_FOLDABLE(pn2
))
7616 pn
->pn_xflags
|= PNX_CANTFOLD
;
7618 pn
->pn_type
= TOK_XMLPTAGC
;
7619 pn
->pn_xflags
|= PNX_XMLROOT
;
7621 /* We had better have a tag-close (>) at this point. */
7622 if (tt
!= TOK_XMLTAGC
) {
7623 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7624 JSMSG_BAD_XML_TAG_SYNTAX
);
7627 pn2
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
7629 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7630 if (pn2
->pn_type
!= TOK_XMLSTAGO
) {
7632 if (!XML_FOLDABLE(pn2
))
7633 pn
->pn_xflags
|= PNX_CANTFOLD
;
7635 pn
= ListNode::create(tc
);
7640 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7641 pn
->pn_type
= TOK_XMLELEM
;
7642 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
7644 if (!XML_FOLDABLE(pn2
))
7645 pn
->pn_xflags
|= PNX_CANTFOLD
;
7646 pn
->pn_xflags
|= PNX_XMLROOT
;
7648 /* Get element contents and delimiting end-tag-open sequence. */
7649 if (!xmlElementContent(pn
))
7652 tt
= tokenStream
.getToken();
7653 XML_CHECK_FOR_ERROR_AND_EOF(tt
, NULL
);
7654 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7655 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7656 JSMSG_BAD_XML_TAG_SYNTAX
);
7660 /* Parse end tag; check mismatch at compile-time if we can. */
7661 pn2
= xmlTagContent(TOK_XMLETAGO
, &endAtom
);
7664 if (pn2
->pn_type
== TOK_XMLETAGO
) {
7665 /* Oops, end tag has attributes! */
7666 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7667 JSMSG_BAD_XML_TAG_SYNTAX
);
7670 if (endAtom
&& startAtom
&& endAtom
!= startAtom
) {
7671 JSString
*str
= ATOM_TO_STRING(startAtom
);
7673 /* End vs. start tag name mismatch: point to the tag name. */
7674 ReportCompileErrorNumber(context
, &tokenStream
, pn2
, JSREPORT_UC
| JSREPORT_ERROR
,
7675 JSMSG_XML_TAG_NAME_MISMATCH
, str
->chars());
7679 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7680 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
|| pn2
->pn_type
== TOK_LC
);
7681 list
= ListNode::create(tc
);
7684 list
->pn_type
= TOK_XMLETAGO
;
7685 list
->initList(pn2
);
7687 if (!XML_FOLDABLE(pn2
)) {
7688 list
->pn_xflags
|= PNX_CANTFOLD
;
7689 pn
->pn_xflags
|= PNX_CANTFOLD
;
7692 tokenStream
.matchToken(TOK_XMLSPACE
);
7693 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_TAG_SYNTAX
);
7696 /* Set pn_op now that pn has been updated to its final value. */
7697 pn
->pn_op
= JSOP_TOXML
;
7698 } else if (allowList
&& tt
== TOK_XMLTAGC
) {
7699 /* XMLList Initialiser. */
7700 pn
->pn_type
= TOK_XMLLIST
;
7701 pn
->pn_op
= JSOP_TOXMLLIST
;
7703 pn
->pn_xflags
|= PNX_XMLROOT
;
7704 if (!xmlElementContent(pn
))
7707 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_LIST_SYNTAX
);
7709 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7710 JSMSG_BAD_XML_NAME_SYNTAX
);
7713 tokenStream
.setXMLTagMode(false);
7715 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
7720 Parser::xmlElementOrListRoot(JSBool allowList
)
7726 * Force XML support to be enabled so that comments and CDATA literals
7727 * are recognized, instead of <! followed by -- starting an HTML comment
7728 * to end of line (used in script tags to hide content from old browsers
7729 * that don't recognize <script>).
7731 oldopts
= JS_SetOptions(context
, context
->options
| JSOPTION_XML
);
7732 pn
= xmlElementOrList(allowList
);
7733 JS_SetOptions(context
, oldopts
);
7738 Parser::parseXMLText(JSObject
*chain
, bool allowList
)
7741 * Push a compiler frame if we have no frames, or if the top frame is a
7742 * lightweight function activation, or if its scope chain doesn't match
7743 * the one passed to us.
7745 JSTreeContext
xmltc(this);
7746 xmltc
.scopeChain
= chain
;
7748 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7749 tokenStream
.setXMLOnlyMode();
7750 TokenKind tt
= tokenStream
.getToken(TSF_OPERAND
);
7753 if (tt
!= TOK_XMLSTAGO
) {
7754 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7755 JSMSG_BAD_XML_MARKUP
);
7758 pn
= xmlElementOrListRoot(allowList
);
7760 tokenStream
.setXMLOnlyMode(false);
7765 #endif /* JS_HAS_XMLSUPPORT */
7767 #if JS_HAS_BLOCK_SCOPE
7769 * Check whether blockid is an active scoping statement in tc. This code is
7770 * necessary to qualify tc->decls.lookup() hits in primaryExpr's TOK_NAME case
7771 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7772 * and let blocks and expressions (not let declarations).
7774 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7775 * due to hoisting, let in a for loop head, let block, or let expression acts
7776 * like Scheme's let: initializers are evaluated without the new let bindings
7779 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7780 * bindings push on the front of the tc->decls JSAtomList (either the singular
7781 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7782 * scope bindings of the same name.
7784 * This simplifies binding lookup code at the price of a linear search here,
7785 * but only if code uses let (var predominates), and even then this function's
7786 * loop iterates more than once only in crazy cases.
7789 BlockIdInScope(uintN blockid
, JSTreeContext
*tc
)
7791 if (blockid
> tc
->blockid())
7793 for (JSStmtInfo
*stmt
= tc
->topScopeStmt
; stmt
; stmt
= stmt
->downScope
) {
7794 if (stmt
->blockid
== blockid
)
7802 Parser::primaryExpr(TokenKind tt
, JSBool afterDot
)
7804 JSParseNode
*pn
, *pn2
, *pn3
;
7807 JS_CHECK_RECURSION(context
, return NULL
);
7811 #if JS_HAS_XML_SUPPORT
7812 if (tokenStream
.matchToken(TOK_DBLCOLON
, TSF_KEYWORD_IS_NAME
)) {
7813 pn2
= NullaryNode::create(tc
);
7816 pn2
->pn_type
= TOK_FUNCTION
;
7817 pn
= qualifiedSuffix(pn2
);
7823 pn
= functionExpr();
7833 pn
= ListNode::create(tc
);
7836 pn
->pn_type
= TOK_RB
;
7837 pn
->pn_op
= JSOP_NEWINIT
;
7840 #if JS_HAS_GENERATORS
7841 pn
->pn_blockid
= tc
->blockidGen
;
7844 matched
= tokenStream
.matchToken(TOK_RB
, TSF_OPERAND
);
7846 for (index
= 0; ; index
++) {
7847 if (index
== JS_ARGS_LENGTH_MAX
) {
7848 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
7849 JSMSG_ARRAY_INIT_TOO_BIG
);
7853 tt
= tokenStream
.peekToken(TSF_OPERAND
);
7855 pn
->pn_xflags
|= PNX_ENDCOMMA
;
7859 if (tt
== TOK_COMMA
) {
7860 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7861 tokenStream
.matchToken(TOK_COMMA
);
7862 pn2
= NullaryNode::create(tc
);
7863 pn
->pn_xflags
|= PNX_HOLEY
;
7871 if (tt
!= TOK_COMMA
) {
7872 /* If we didn't already match TOK_COMMA in above case. */
7873 if (!tokenStream
.matchToken(TOK_COMMA
))
7878 #if JS_HAS_GENERATORS
7880 * At this point, (index == 0 && pn->pn_count != 0) implies one
7881 * element initialiser was parsed.
7883 * An array comprehension of the form:
7885 * [i * j for (i in o) for (j in p) if (i != j)]
7887 * translates to roughly the following let expression:
7889 * let (array = new Array, i, j) {
7890 * for (i in o) let {
7898 * where array is a nameless block-local variable. The "roughly"
7899 * means that an implementation may optimize away the array.push.
7900 * An array comprehension opens exactly one block scope, no matter
7901 * how many for heads it contains.
7903 * Each let () {...} or for (let ...) ... compiles to:
7905 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7907 * where <o> is a literal object representing the block scope,
7908 * with <n> properties, naming each var declared in the block.
7910 * Each var declaration in a let-block binds a name in <o> at
7911 * compile time, and allocates a slot on the operand stack at
7912 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7913 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7914 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7915 * local slot's stack index from fp->spbase.
7917 * The array comprehension iteration step, array.push(i * j) in
7918 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7919 * where <array> is the index of array's stack slot.
7921 if (index
== 0 && pn
->pn_count
!= 0 && tokenStream
.matchToken(TOK_FOR
)) {
7922 JSParseNode
*pnexp
, *pntop
;
7924 /* Relabel pn as an array comprehension node. */
7925 pn
->pn_type
= TOK_ARRAYCOMP
;
7928 * Remove the comprehension expression from pn's linked list
7929 * and save it via pnexp. We'll re-install it underneath the
7930 * ARRAYPUSH node after we parse the rest of the comprehension.
7933 JS_ASSERT(pn
->pn_count
== 1 || pn
->pn_count
== 2);
7934 pn
->pn_tail
= (--pn
->pn_count
== 1)
7935 ? &pn
->pn_head
->pn_next
7937 *pn
->pn_tail
= NULL
;
7939 pntop
= comprehensionTail(pnexp
, pn
->pn_blockid
,
7940 TOK_ARRAYPUSH
, JSOP_ARRAYPUSH
);
7945 #endif /* JS_HAS_GENERATORS */
7947 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
7949 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
7959 * A map from property names we've seen thus far to bit masks.
7960 * (We use ALE_INDEX/ALE_SET_INDEX). An atom's mask includes
7961 * JSPROP_SETTER if we've seen a setter for it, JSPROP_GETTER
7962 * if we've seen as getter, and both of those if we've just
7963 * seen an ordinary value.
7965 JSAutoAtomList
seen(tc
->parser
);
7967 pn
= ListNode::create(tc
);
7970 pn
->pn_type
= TOK_RC
;
7971 pn
->pn_op
= JSOP_NEWINIT
;
7974 afterComma
= JS_FALSE
;
7977 tt
= tokenStream
.getToken(TSF_KEYWORD_IS_NAME
);
7980 pn3
= NullaryNode::create(tc
);
7983 pn3
->pn_dval
= tokenStream
.currentToken().t_dval
;
7984 if (tc
->needStrictChecks()) {
7985 atom
= js_AtomizeDouble(context
, pn3
->pn_dval
);
7989 atom
= NULL
; /* for the compiler */
7994 atom
= tokenStream
.currentToken().t_atom
;
7995 if (atom
== context
->runtime
->atomState
.getAtom
)
7997 else if (atom
== context
->runtime
->atomState
.setAtom
)
8002 tt
= tokenStream
.getToken(TSF_KEYWORD_IS_NAME
);
8003 if (tt
== TOK_NAME
|| tt
== TOK_STRING
) {
8004 atom
= tokenStream
.currentToken().t_atom
;
8005 pn3
= NameNode::create(atom
, tc
);
8008 } else if (tt
== TOK_NUMBER
) {
8009 pn3
= NullaryNode::create(tc
);
8012 pn3
->pn_dval
= tokenStream
.currentToken().t_dval
;
8013 if (tc
->needStrictChecks()) {
8014 atom
= js_AtomizeDouble(context
, pn3
->pn_dval
);
8018 atom
= NULL
; /* for the compiler */
8021 tokenStream
.ungetToken();
8025 /* We have to fake a 'function' token here. */
8026 tokenStream
.mungeCurrentToken(TOK_FUNCTION
, JSOP_NOP
);
8027 pn2
= functionDef(JSFUN_LAMBDA
, false);
8028 pn2
= JSParseNode::newBinaryOrAppend(TOK_COLON
, op
, pn3
, pn2
, tc
);
8033 atom
= tokenStream
.currentToken().t_atom
;
8034 pn3
= NullaryNode::create(tc
);
8037 pn3
->pn_atom
= atom
;
8042 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
8048 tt
= tokenStream
.getToken();
8049 if (tt
== TOK_COLON
) {
8050 pnval
= assignExpr();
8052 #if JS_HAS_DESTRUCTURING_SHORTHAND
8053 if (tt
!= TOK_COMMA
&& tt
!= TOK_RC
) {
8055 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
8056 JSMSG_COLON_AFTER_ID
);
8058 #if JS_HAS_DESTRUCTURING_SHORTHAND
8062 * Support, e.g., |var {x, y} = o| as destructuring shorthand
8063 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8065 tokenStream
.ungetToken();
8066 pn
->pn_xflags
|= PNX_DESTRUCT
;
8068 if (pnval
->pn_type
== TOK_NAME
) {
8069 pnval
->pn_arity
= PN_NAME
;
8070 ((NameNode
*)pnval
)->initCommon(tc
);
8075 pn2
= JSParseNode::newBinaryOrAppend(TOK_COLON
, op
, pn3
, pnval
, tc
);
8082 * In strict mode code, check for duplicate property names. Treat
8083 * getters and setters as distinct attributes of each property. A
8084 * plain old value conflicts with a getter or a setter.
8086 if (tc
->needStrictChecks()) {
8087 unsigned attributesMask
;
8088 if (op
== JSOP_INITPROP
) {
8089 attributesMask
= JSPROP_GETTER
| JSPROP_SETTER
;
8090 } else if (op
== JSOP_GETTER
) {
8091 attributesMask
= JSPROP_GETTER
;
8092 } else if (op
== JSOP_SETTER
) {
8093 attributesMask
= JSPROP_SETTER
;
8095 JS_NOT_REACHED("bad opcode in object initializer");
8100 * Use only string-valued atoms for detecting duplicate
8101 * properties so that 1 and "1" properly collide.
8103 if (ATOM_IS_DOUBLE(atom
)) {
8104 JSString
*str
= js_NumberToString(context
, pn3
->pn_dval
);
8107 atom
= js_AtomizeString(context
, str
, 0);
8112 JSAtomListElement
*ale
= seen
.lookup(atom
);
8114 if (ALE_INDEX(ale
) & attributesMask
) {
8115 const char *name
= js_AtomToPrintableString(context
, atom
);
8117 !ReportStrictModeError(context
, &tokenStream
, tc
, NULL
,
8118 JSMSG_DUPLICATE_PROPERTY
, name
)) {
8122 ALE_SET_INDEX(ale
, attributesMask
| ALE_INDEX(ale
));
8124 ale
= seen
.add(tc
->parser
, atom
);
8127 ALE_SET_INDEX(ale
, attributesMask
);
8131 tt
= tokenStream
.getToken();
8134 if (tt
!= TOK_COMMA
) {
8135 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
8136 JSMSG_CURLY_AFTER_LIST
);
8139 afterComma
= JS_TRUE
;
8143 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
8147 #if JS_HAS_BLOCK_SCOPE
8149 pn
= letBlock(JS_FALSE
);
8155 #if JS_HAS_SHARP_VARS
8157 pn
= UnaryNode::create(tc
);
8160 pn
->pn_num
= (jsint
) tokenStream
.currentToken().t_dval
;
8161 tt
= tokenStream
.getToken(TSF_OPERAND
);
8162 if (tt
== TOK_USESHARP
|| tt
== TOK_DEFSHARP
||
8163 #if JS_HAS_XML_SUPPORT
8164 tt
== TOK_STAR
|| tt
== TOK_AT
||
8165 tt
== TOK_XMLSTAGO
/* XXXbe could be sharp? */ ||
8167 tt
== TOK_STRING
|| tt
== TOK_NUMBER
|| tt
== TOK_PRIMARY
) {
8168 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
8169 JSMSG_BAD_SHARP_VAR_DEF
);
8172 pn
->pn_kid
= primaryExpr(tt
, JS_FALSE
);
8175 if (!tc
->ensureSharpSlots())
8180 /* Check for forward/dangling references at runtime, to allow eval. */
8181 pn
= NullaryNode::create(tc
);
8184 if (!tc
->ensureSharpSlots())
8186 pn
->pn_num
= (jsint
) tokenStream
.currentToken().t_dval
;
8188 #endif /* JS_HAS_SHARP_VARS */
8194 pn
= parenExpr(NULL
, &genexp
);
8197 pn
->pn_parens
= true;
8199 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
8203 #if JS_HAS_XML_SUPPORT
8205 pn
= qualifiedIdentifier();
8211 pn
= attributeIdentifier();
8217 pn
= xmlElementOrListRoot(JS_TRUE
);
8221 #endif /* JS_HAS_XML_SUPPORT */
8224 #if JS_HAS_SHARP_VARS
8228 #if JS_HAS_XML_SUPPORT
8230 case TOK_XMLCOMMENT
:
8233 pn
= NullaryNode::create(tc
);
8236 pn
->pn_atom
= tokenStream
.currentToken().t_atom
;
8237 #if JS_HAS_XML_SUPPORT
8238 if (tt
== TOK_XMLPI
)
8239 pn
->pn_atom2
= tokenStream
.currentToken().t_atom2
;
8242 pn
->pn_op
= tokenStream
.currentToken().t_op
;
8246 pn
= NameNode::create(tokenStream
.currentToken().t_atom
, tc
);
8249 JS_ASSERT(tokenStream
.currentToken().t_op
== JSOP_NAME
);
8250 pn
->pn_op
= JSOP_NAME
;
8252 if ((tc
->flags
& (TCF_IN_FUNCTION
| TCF_FUN_PARAM_ARGUMENTS
)) == TCF_IN_FUNCTION
&&
8253 pn
->pn_atom
== context
->runtime
->atomState
.argumentsAtom
) {
8255 * Flag arguments usage so we can avoid unsafe optimizations such
8256 * as formal parameter assignment analysis (because of the hated
8257 * feature whereby arguments alias formals). We do this even for
8258 * a reference of the form foo.arguments, which ancient code may
8259 * still use instead of arguments (more hate).
8261 NoteArgumentsUse(tc
);
8264 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8265 * to do this work (new rule for the emitter to count on).
8267 if (!afterDot
&& !(tc
->flags
& TCF_DECL_DESTRUCTURING
) && !tc
->inStatement(STMT_WITH
)) {
8268 pn
->pn_op
= JSOP_ARGUMENTS
;
8269 pn
->pn_dflags
|= PND_BOUND
;
8271 } else if ((!afterDot
8272 #if JS_HAS_XML_SUPPORT
8273 || tokenStream
.peekToken() == TOK_DBLCOLON
8275 ) && !(tc
->flags
& TCF_DECL_DESTRUCTURING
)) {
8276 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, pn
->pn_atom
, NULL
);
8277 if (!stmt
|| stmt
->type
!= STMT_WITH
) {
8280 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
8283 #if JS_HAS_BLOCK_SCOPE
8285 * Skip out-of-scope let bindings along an ALE list or hash
8286 * chain. These can happen due to |let (x = x) x| block and
8287 * expression bindings, where the x on the right of = comes
8288 * from an outer scope. See bug 496532.
8290 while (dn
->isLet() && !BlockIdInScope(dn
->pn_blockid
, tc
)) {
8292 ale
= ALE_NEXT(ale
);
8293 } while (ale
&& ALE_ATOM(ale
) != pn
->pn_atom
);
8304 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
8309 * No definition before this use in any lexical scope.
8310 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8311 * new node for the forward-referenced definition. This
8312 * placeholder definition node will be adopted when we
8313 * parse the real defining declaration form, or left as
8314 * a free variable definition if we never see the real
8317 ale
= MakePlaceholder(pn
, tc
);
8323 * In case this is a forward reference to a function,
8324 * we pessimistically set PND_FUNARG if the next token
8325 * is not a left parenthesis.
8327 * If the definition eventually parsed into dn is not a
8328 * function, this flag won't hurt, and if we do parse a
8329 * function with pn's name, then the PND_FUNARG flag is
8330 * necessary for safe context->display-based optimiza-
8331 * tion of the closure's static link.
8333 JS_ASSERT(PN_TYPE(dn
) == TOK_NAME
);
8334 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
8335 if (tokenStream
.peekToken() != TOK_LP
)
8336 dn
->pn_dflags
|= PND_FUNARG
;
8340 JS_ASSERT(dn
->pn_defn
);
8341 LinkUseToDef(pn
, dn
, tc
);
8343 /* Here we handle the backward function reference case. */
8344 if (tokenStream
.peekToken() != TOK_LP
)
8345 dn
->pn_dflags
|= PND_FUNARG
;
8347 pn
->pn_dflags
|= (dn
->pn_dflags
& PND_FUNARG
);
8351 #if JS_HAS_XML_SUPPORT
8352 if (tokenStream
.matchToken(TOK_DBLCOLON
)) {
8357 * Here primaryExpr is called after . or .. followed by a name
8358 * followed by ::. This is the only case where a keyword after
8359 * . or .. is not treated as a property name.
8361 str
= ATOM_TO_STRING(pn
->pn_atom
);
8362 tt
= js_CheckKeyword(str
->chars(), str
->length());
8363 if (tt
== TOK_FUNCTION
) {
8364 pn
->pn_arity
= PN_NULLARY
;
8365 pn
->pn_type
= TOK_FUNCTION
;
8366 } else if (tt
!= TOK_EOF
) {
8367 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
8368 JSMSG_KEYWORD_NOT_NS
);
8372 pn
= qualifiedSuffix(pn
);
8383 pn
= NullaryNode::create(tc
);
8387 obj
= js_NewRegExpObject(context
, &tokenStream
,
8388 tokenStream
.getTokenbuf().begin(),
8389 tokenStream
.getTokenbuf().length(),
8390 tokenStream
.currentToken().t_reflags
);
8393 if (!tc
->compileAndGo()) {
8398 pn
->pn_objbox
= tc
->parser
->newObjectBox(obj
);
8402 pn
->pn_op
= JSOP_REGEXP
;
8407 pn
= NullaryNode::create(tc
);
8410 pn
->pn_op
= JSOP_DOUBLE
;
8411 pn
->pn_dval
= tokenStream
.currentToken().t_dval
;
8415 pn
= NullaryNode::create(tc
);
8418 pn
->pn_op
= tokenStream
.currentToken().t_op
;
8422 /* The scanner or one of its subroutines reported the error. */
8426 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
8427 JSMSG_SYNTAX_ERROR
);
8434 Parser::parenExpr(JSParseNode
*pn1
, JSBool
*genexp
)
8439 JS_ASSERT(tokenStream
.currentToken().type
== TOK_LP
);
8440 begin
= tokenStream
.currentToken().pos
.begin
;
8444 pn
= bracketedExpr();
8448 #if JS_HAS_GENERATOR_EXPRS
8449 if (tokenStream
.matchToken(TOK_FOR
)) {
8450 if (pn
->pn_type
== TOK_YIELD
&& !pn
->pn_parens
) {
8451 ReportCompileErrorNumber(context
, &tokenStream
, pn
, JSREPORT_ERROR
,
8452 JSMSG_BAD_GENERATOR_SYNTAX
, js_yield_str
);
8455 if (pn
->pn_type
== TOK_COMMA
&& !pn
->pn_parens
) {
8456 ReportCompileErrorNumber(context
, &tokenStream
, pn
->last(), JSREPORT_ERROR
,
8457 JSMSG_BAD_GENERATOR_SYNTAX
, js_generator_str
);
8461 pn1
= UnaryNode::create(tc
);
8465 pn
= generatorExpr(pn1
, pn
);
8468 pn
->pn_pos
.begin
= begin
;
8470 if (tokenStream
.getToken() != TOK_RP
) {
8471 ReportCompileErrorNumber(context
, &tokenStream
, NULL
, JSREPORT_ERROR
,
8472 JSMSG_BAD_GENERATOR_SYNTAX
, js_generator_str
);
8475 pn
->pn_pos
.end
= tokenStream
.currentToken().pos
.end
;
8479 #endif /* JS_HAS_GENERATOR_EXPRS */
8485 * Fold from one constant type to another.
8486 * XXX handles only strings and numbers for now
8489 FoldType(JSContext
*cx
, JSParseNode
*pn
, TokenKind type
)
8491 if (PN_TYPE(pn
) != type
) {
8494 if (pn
->pn_type
== TOK_STRING
) {
8496 if (!JS_ValueToNumber(cx
, ATOM_KEY(pn
->pn_atom
), &d
))
8499 pn
->pn_type
= TOK_NUMBER
;
8500 pn
->pn_op
= JSOP_DOUBLE
;
8505 if (pn
->pn_type
== TOK_NUMBER
) {
8506 JSString
*str
= js_NumberToString(cx
, pn
->pn_dval
);
8509 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8512 pn
->pn_type
= TOK_STRING
;
8513 pn
->pn_op
= JSOP_STRING
;
8524 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8525 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8526 * a successful call to this function.
8529 FoldBinaryNumeric(JSContext
*cx
, JSOp op
, JSParseNode
*pn1
, JSParseNode
*pn2
,
8530 JSParseNode
*pn
, JSTreeContext
*tc
)
8535 JS_ASSERT(pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
);
8541 i
= js_DoubleToECMAInt32(d
);
8542 j
= js_DoubleToECMAInt32(d2
);
8544 d
= (op
== JSOP_LSH
) ? i
<< j
: i
>> j
;
8548 j
= js_DoubleToECMAInt32(d2
);
8550 d
= js_DoubleToECMAUint32(d
) >> j
;
8568 /* XXX MSVC miscompiles such that (NaN == 0) */
8569 if (JSDOUBLE_IS_NaN(d2
))
8573 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
8575 else if (JSDOUBLE_IS_NEG(d
) != JSDOUBLE_IS_NEG(d2
))
8576 d
= js_NegativeInfinity
;
8578 d
= js_PositiveInfinity
;
8595 /* Take care to allow pn1 or pn2 to alias pn. */
8597 RecycleTree(pn1
, tc
);
8599 RecycleTree(pn2
, tc
);
8600 pn
->pn_type
= TOK_NUMBER
;
8601 pn
->pn_op
= JSOP_DOUBLE
;
8602 pn
->pn_arity
= PN_NULLARY
;
8607 #if JS_HAS_XML_SUPPORT
8610 FoldXMLConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
8613 JSParseNode
**pnp
, *pn1
, *pn2
;
8614 JSString
*accum
, *str
;
8617 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
8622 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8623 if (tt
== TOK_XMLETAGO
)
8624 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.etagoAtom
);
8625 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
)
8626 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.stagoAtom
);
8630 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8631 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8632 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8633 * Therefore, we have to add additonal protection from GC nesting under
8636 for (pn2
= pn1
, i
= j
= 0; pn2
; pn2
= pn2
->pn_next
, i
++) {
8637 /* The parser already rejected end-tags with attributes. */
8638 JS_ASSERT(tt
!= TOK_XMLETAGO
|| i
== 0);
8639 switch (pn2
->pn_type
) {
8648 if (pn2
->pn_arity
== PN_LIST
)
8650 str
= ATOM_TO_STRING(pn2
->pn_atom
);
8654 str
= js_MakeXMLCDATAString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8659 case TOK_XMLCOMMENT
:
8660 str
= js_MakeXMLCommentString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8666 str
= js_MakeXMLPIString(cx
, ATOM_TO_STRING(pn2
->pn_atom
),
8667 ATOM_TO_STRING(pn2
->pn_atom2
));
8674 JS_ASSERT(*pnp
== pn1
);
8675 if ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) &&
8676 (i
& 1) ^ (j
& 1)) {
8677 #ifdef DEBUG_brendanXXX
8678 printf("1: %d, %d => ", i
, j
);
8680 js_FileEscapedString(stdout
, accum
, 0);
8682 fputs("NULL", stdout
);
8683 fputc('\n', stdout
);
8685 } else if (accum
&& pn1
!= pn2
) {
8686 while (pn1
->pn_next
!= pn2
) {
8687 pn1
= RecycleTree(pn1
, tc
);
8690 pn1
->pn_type
= TOK_XMLTEXT
;
8691 pn1
->pn_op
= JSOP_STRING
;
8692 pn1
->pn_arity
= PN_NULLARY
;
8693 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8696 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8699 pnp
= &pn2
->pn_next
;
8707 AutoValueRooter
tvr(cx
, accum
);
8708 str
= ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) && i
!= 0)
8709 ? js_AddAttributePart(cx
, i
& 1, accum
, str
)
8710 : js_ConcatStrings(cx
, accum
, str
);
8714 #ifdef DEBUG_brendanXXX
8715 printf("2: %d, %d => ", i
, j
);
8716 js_FileEscapedString(stdout
, str
, 0);
8717 printf(" (%u)\n", str
->length());
8726 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8727 if (tt
== TOK_XMLPTAGC
)
8728 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.ptagcAtom
);
8729 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLETAGO
)
8730 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.tagcAtom
);
8733 accum
= js_ConcatStrings(cx
, accum
, str
);
8738 JS_ASSERT(*pnp
== pn1
);
8739 while (pn1
->pn_next
) {
8740 pn1
= RecycleTree(pn1
, tc
);
8743 pn1
->pn_type
= TOK_XMLTEXT
;
8744 pn1
->pn_op
= JSOP_STRING
;
8745 pn1
->pn_arity
= PN_NULLARY
;
8746 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8749 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8753 if (pn1
&& pn
->pn_count
== 1) {
8755 * Only one node under pn, and it has been folded: move pn1 onto pn
8756 * unless pn is an XML root (in which case we need it to tell the code
8757 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8758 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8759 * extra "<" and "/>" bracketing at runtime.
8761 if (!(pn
->pn_xflags
& PNX_XMLROOT
)) {
8763 } else if (tt
== TOK_XMLPTAGC
) {
8764 pn
->pn_type
= TOK_XMLELEM
;
8765 pn
->pn_op
= JSOP_TOXML
;
8771 #endif /* JS_HAS_XML_SUPPORT */
8774 Boolish(JSParseNode
*pn
)
8776 switch (pn
->pn_op
) {
8778 return pn
->pn_dval
!= 0 && !JSDOUBLE_IS_NaN(pn
->pn_dval
);
8781 return ATOM_TO_STRING(pn
->pn_atom
)->length() != 0;
8783 #if JS_HAS_GENERATOR_EXPRS
8787 * A generator expression as an if or loop condition has no effects, it
8788 * simply results in a truthy object reference. This condition folding
8789 * is needed for the decompiler. See bug 442342 and bug 443074.
8791 if (pn
->pn_count
!= 1)
8793 JSParseNode
*pn2
= pn
->pn_head
;
8794 if (pn2
->pn_type
!= TOK_FUNCTION
)
8796 if (!(pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
))
8818 js_FoldConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, bool inCond
)
8820 JSParseNode
*pn1
= NULL
, *pn2
= NULL
, *pn3
= NULL
;
8822 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
8824 switch (pn
->pn_arity
) {
8827 uint32 oldflags
= tc
->flags
;
8828 JSFunctionBox
*oldlist
= tc
->functionList
;
8830 tc
->flags
= pn
->pn_funbox
->tcflags
;
8831 tc
->functionList
= pn
->pn_funbox
->kids
;
8832 if (!js_FoldConstants(cx
, pn
->pn_body
, tc
))
8834 pn
->pn_funbox
->kids
= tc
->functionList
;
8835 tc
->flags
= oldflags
;
8836 tc
->functionList
= oldlist
;
8842 /* Propagate inCond through logical connectives. */
8843 bool cond
= inCond
&& (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
);
8845 /* Don't fold a parenthesized call expression. See bug 537673. */
8846 pn1
= pn2
= pn
->pn_head
;
8847 if ((pn
->pn_type
== TOK_LP
|| pn
->pn_type
== TOK_NEW
) && pn2
->pn_parens
)
8850 /* Save the list head in pn1 for later use. */
8851 for (; pn2
; pn2
= pn2
->pn_next
) {
8852 if (!js_FoldConstants(cx
, pn2
, tc
, cond
))
8859 /* Any kid may be null (e.g. for (;;)). */
8863 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_IF
))
8866 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_FORHEAD
))
8868 if (pn
->pn_type
== TOK_FORHEAD
&& pn2
->pn_op
== JSOP_TRUE
) {
8869 RecycleTree(pn2
, tc
);
8873 if (pn3
&& !js_FoldConstants(cx
, pn3
, tc
))
8881 /* Propagate inCond through logical connectives. */
8882 if (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
) {
8883 if (!js_FoldConstants(cx
, pn1
, tc
, inCond
))
8885 if (!js_FoldConstants(cx
, pn2
, tc
, inCond
))
8890 /* First kid may be null (for default case in switch). */
8891 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_WHILE
))
8893 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_DO
))
8901 * Kludge to deal with typeof expressions: because constant folding
8902 * can turn an expression into a name node, we have to check here,
8903 * before folding, to see if we should throw undefined name errors.
8905 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
8906 * null. This assumption does not hold true for other unary
8909 if (pn
->pn_op
== JSOP_TYPEOF
&& pn1
->pn_type
!= TOK_NAME
)
8910 pn
->pn_op
= JSOP_TYPEOFEXPR
;
8912 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_op
== JSOP_NOT
))
8918 * Skip pn1 down along a chain of dotted member expressions to avoid
8919 * excessive recursion. Our only goal here is to fold constants (if
8920 * any) in the primary expression operand to the left of the first
8925 while (pn1
&& pn1
->pn_arity
== PN_NAME
&& !pn1
->pn_used
)
8927 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
8934 if (!js_FoldConstants(cx
, pn1
, tc
))
8942 switch (pn
->pn_type
) {
8944 if (ContainsStmt(pn2
, TOK_VAR
) || ContainsStmt(pn3
, TOK_VAR
))
8949 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8950 switch (pn1
->pn_type
) {
8952 if (pn1
->pn_dval
== 0 || JSDOUBLE_IS_NaN(pn1
->pn_dval
))
8956 if (ATOM_TO_STRING(pn1
->pn_atom
)->length() == 0)
8960 if (pn1
->pn_op
== JSOP_TRUE
)
8962 if (pn1
->pn_op
== JSOP_FALSE
|| pn1
->pn_op
== JSOP_NULL
) {
8968 /* Early return to dodge common code that copies pn2 to pn. */
8972 #if JS_HAS_GENERATOR_EXPRS
8973 /* Don't fold a trailing |if (0)| in a generator expression. */
8974 if (!pn2
&& (tc
->flags
& TCF_GENEXP_LAMBDA
))
8978 if (pn2
&& !pn2
->pn_defn
)
8980 if (!pn2
|| (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
)) {
8982 * False condition and no else, or an empty then-statement was
8983 * moved up over pn. Either way, make pn an empty block (not an
8984 * empty statement, which does not decompile, even when labeled).
8985 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8986 * or an empty statement for a child.
8988 pn
->pn_type
= TOK_LC
;
8989 pn
->pn_arity
= PN_LIST
;
8992 RecycleTree(pn2
, tc
);
8993 if (pn3
&& pn3
!= pn2
)
8994 RecycleTree(pn3
, tc
);
9000 if (pn
->pn_arity
== PN_LIST
) {
9001 JSParseNode
**pnp
= &pn
->pn_head
;
9002 JS_ASSERT(*pnp
== pn1
);
9004 int cond
= Boolish(pn1
);
9005 if (cond
== (pn
->pn_type
== TOK_OR
)) {
9006 for (pn2
= pn1
->pn_next
; pn2
; pn2
= pn3
) {
9008 RecycleTree(pn2
, tc
);
9011 pn1
->pn_next
= NULL
;
9015 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
9016 if (pn
->pn_count
== 1)
9018 *pnp
= pn1
->pn_next
;
9019 RecycleTree(pn1
, tc
);
9022 pnp
= &pn1
->pn_next
;
9024 } while ((pn1
= *pnp
) != NULL
);
9026 // We may have to change arity from LIST to BINARY.
9028 if (pn
->pn_count
== 2) {
9030 pn1
->pn_next
= NULL
;
9031 JS_ASSERT(!pn2
->pn_next
);
9032 pn
->pn_arity
= PN_BINARY
;
9035 } else if (pn
->pn_count
== 1) {
9037 RecycleTree(pn1
, tc
);
9040 int cond
= Boolish(pn1
);
9041 if (cond
== (pn
->pn_type
== TOK_OR
)) {
9042 RecycleTree(pn2
, tc
);
9044 } else if (cond
!= -1) {
9045 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
9046 RecycleTree(pn1
, tc
);
9055 * Compound operators such as *= should be subject to folding, in case
9056 * the left-hand side is constant, and so that the decompiler produces
9057 * the same string that you get from decompiling a script or function
9058 * compiled from that same string. As with +, += is special.
9060 if (pn
->pn_op
== JSOP_NOP
)
9062 if (pn
->pn_op
!= JSOP_ADD
)
9067 if (pn
->pn_arity
== PN_LIST
) {
9068 size_t length
, length2
;
9070 JSString
*str
, *str2
;
9073 * Any string literal term with all others number or string means
9074 * this is a concatenation. If any term is not a string or number
9075 * literal, we can't fold.
9077 JS_ASSERT(pn
->pn_count
> 2);
9078 if (pn
->pn_xflags
& PNX_CANTFOLD
)
9080 if (pn
->pn_xflags
!= PNX_STRCAT
)
9083 /* Ok, we're concatenating: convert non-string constant operands. */
9085 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9086 if (!FoldType(cx
, pn2
, TOK_STRING
))
9088 /* XXX fold only if all operands convert to string */
9089 if (pn2
->pn_type
!= TOK_STRING
)
9091 length
+= ATOM_TO_STRING(pn2
->pn_atom
)->flatLength();
9094 /* Allocate a new buffer and string descriptor for the result. */
9095 chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
9098 str
= js_NewString(cx
, chars
, length
);
9104 /* Fill the buffer, advancing chars and recycling kids as we go. */
9105 for (pn2
= pn1
; pn2
; pn2
= RecycleTree(pn2
, tc
)) {
9106 str2
= ATOM_TO_STRING(pn2
->pn_atom
);
9107 length2
= str2
->flatLength();
9108 js_strncpy(chars
, str2
->flatChars(), length2
);
9113 /* Atomize the result string and mutate pn to refer to it. */
9114 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
9117 pn
->pn_type
= TOK_STRING
;
9118 pn
->pn_op
= JSOP_STRING
;
9119 pn
->pn_arity
= PN_NULLARY
;
9123 /* Handle a binary string concatenation. */
9124 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
9125 if (pn1
->pn_type
== TOK_STRING
|| pn2
->pn_type
== TOK_STRING
) {
9126 JSString
*left
, *right
, *str
;
9128 if (!FoldType(cx
, (pn1
->pn_type
!= TOK_STRING
) ? pn1
: pn2
,
9132 if (pn1
->pn_type
!= TOK_STRING
|| pn2
->pn_type
!= TOK_STRING
)
9134 left
= ATOM_TO_STRING(pn1
->pn_atom
);
9135 right
= ATOM_TO_STRING(pn2
->pn_atom
);
9136 str
= js_ConcatStrings(cx
, left
, right
);
9139 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
9142 pn
->pn_type
= TOK_STRING
;
9143 pn
->pn_op
= JSOP_STRING
;
9144 pn
->pn_arity
= PN_NULLARY
;
9145 RecycleTree(pn1
, tc
);
9146 RecycleTree(pn2
, tc
);
9150 /* Can't concatenate string literals, let's try numbers. */
9158 if (pn
->pn_arity
== PN_LIST
) {
9159 JS_ASSERT(pn
->pn_count
> 2);
9160 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9161 if (!FoldType(cx
, pn2
, TOK_NUMBER
))
9164 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
9165 /* XXX fold only if all operands convert to number */
9166 if (pn2
->pn_type
!= TOK_NUMBER
)
9170 JSOp op
= PN_OP(pn
);
9174 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
, tc
))
9176 while ((pn2
= pn3
) != NULL
) {
9178 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
, tc
))
9183 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
9184 if (!FoldType(cx
, pn1
, TOK_NUMBER
) ||
9185 !FoldType(cx
, pn2
, TOK_NUMBER
)) {
9188 if (pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
) {
9189 if (!FoldBinaryNumeric(cx
, PN_OP(pn
), pn1
, pn2
, pn
, tc
))
9196 if (pn1
->pn_type
== TOK_NUMBER
) {
9199 /* Operate on one numeric constant. */
9201 switch (pn
->pn_op
) {
9203 d
= ~js_DoubleToECMAInt32(d
);
9214 pn
->pn_type
= TOK_PRIMARY
;
9215 pn
->pn_op
= (d
== 0 || JSDOUBLE_IS_NaN(d
)) ? JSOP_TRUE
: JSOP_FALSE
;
9216 pn
->pn_arity
= PN_NULLARY
;
9220 /* Return early to dodge the common TOK_NUMBER code. */
9223 pn
->pn_type
= TOK_NUMBER
;
9224 pn
->pn_op
= JSOP_DOUBLE
;
9225 pn
->pn_arity
= PN_NULLARY
;
9227 RecycleTree(pn1
, tc
);
9228 } else if (pn1
->pn_type
== TOK_PRIMARY
) {
9229 if (pn
->pn_op
== JSOP_NOT
&&
9230 (pn1
->pn_op
== JSOP_TRUE
||
9231 pn1
->pn_op
== JSOP_FALSE
)) {
9233 pn
->pn_op
= (pn
->pn_op
== JSOP_TRUE
) ? JSOP_FALSE
: JSOP_TRUE
;
9234 RecycleTree(pn1
, tc
);
9239 #if JS_HAS_XML_SUPPORT
9246 if (pn
->pn_arity
== PN_LIST
) {
9247 JS_ASSERT(pn
->pn_type
== TOK_XMLLIST
|| pn
->pn_count
!= 0);
9248 if (!FoldXMLConstants(cx
, pn
, tc
))
9254 if (pn1
->pn_type
== TOK_XMLNAME
) {
9256 JSObjectBox
*xmlbox
;
9258 v
= ATOM_KEY(pn1
->pn_atom
);
9259 if (!js_ToAttributeName(cx
, &v
))
9261 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
9263 xmlbox
= tc
->parser
->newObjectBox(JSVAL_TO_OBJECT(v
));
9267 pn
->pn_type
= TOK_XMLNAME
;
9268 pn
->pn_op
= JSOP_OBJECT
;
9269 pn
->pn_arity
= PN_NULLARY
;
9270 pn
->pn_objbox
= xmlbox
;
9271 RecycleTree(pn1
, tc
);
9274 #endif /* JS_HAS_XML_SUPPORT */
9280 int cond
= Boolish(pn
);
9282 switch (pn
->pn_arity
) {
9287 RecycleTree(pn2
, tc
);
9288 } while ((pn2
= pn3
) != NULL
);
9291 RecycleFuncNameKids(pn
, tc
);
9296 JS_NOT_REACHED("unhandled arity");
9298 pn
->pn_type
= TOK_PRIMARY
;
9299 pn
->pn_op
= cond
? JSOP_TRUE
: JSOP_FALSE
;
9300 pn
->pn_arity
= PN_NULLARY
;