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"
81 #if JS_HAS_XML_SUPPORT
85 #if JS_HAS_DESTRUCTURING
90 * Asserts to verify assumptions behind pn_ macros.
92 #define pn_offsetof(m) offsetof(JSParseNode, m)
94 JS_STATIC_ASSERT(pn_offsetof(pn_link
) == pn_offsetof(dn_uses
));
95 JS_STATIC_ASSERT(pn_offsetof(pn_u
.name
.atom
) == pn_offsetof(pn_u
.apair
.atom
));
100 * JS parsers, from lowest to highest precedence.
102 * Each parser takes a context, a token stream, and a tree context struct.
103 * Each returns a parse node tree or null on error.
106 typedef JSParseNode
*
107 JSParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
);
109 typedef JSParseNode
*
110 JSVariablesParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
113 typedef JSParseNode
*
114 JSMemberParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
115 JSBool allowCallSyntax
);
117 typedef JSParseNode
*
118 JSPrimaryParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
119 JSTokenType tt
, JSBool afterDot
);
121 typedef JSParseNode
*
122 JSParenParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
123 JSParseNode
*pn1
, JSBool
*genexp
);
125 static JSParser FunctionStmt
;
126 static JSParser FunctionExpr
;
127 static JSParser Statements
;
128 static JSParser Statement
;
129 static JSVariablesParser Variables
;
130 static JSParser Expr
;
131 static JSParser AssignExpr
;
132 static JSParser CondExpr
;
133 static JSParser OrExpr
;
134 static JSParser AndExpr
;
135 static JSParser BitOrExpr
;
136 static JSParser BitXorExpr
;
137 static JSParser BitAndExpr
;
138 static JSParser EqExpr
;
139 static JSParser RelExpr
;
140 static JSParser ShiftExpr
;
141 static JSParser AddExpr
;
142 static JSParser MulExpr
;
143 static JSParser UnaryExpr
;
144 static JSMemberParser MemberExpr
;
145 static JSPrimaryParser PrimaryExpr
;
146 static JSParenParser ParenExpr
;
149 * Insist that the next token be of type tt, or report errno and return null.
150 * NB: this macro uses cx and ts from its lexical environment.
152 #define MUST_MATCH_TOKEN(tt, errno) \
154 if (js_GetToken(cx, ts) != tt) { \
155 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
160 #ifdef METER_PARSENODES
161 static uint32 parsenodes
= 0;
162 static uint32 maxparsenodes
= 0;
163 static uint32 recyclednodes
= 0;
167 JSParseNode::become(JSParseNode
*pn2
)
170 JS_ASSERT(!pn2
->pn_defn
);
174 JSParseNode
**pnup
= &pn2
->pn_lexdef
->dn_uses
;
176 pnup
= &(*pnup
)->pn_link
;
178 pn_link
= pn2
->pn_link
;
181 pn2
->pn_used
= false;
184 /* If this is a function node fix up the pn_funbox->node back-pointer. */
185 if (PN_TYPE(pn2
) == TOK_FUNCTION
&& pn2
->pn_arity
== PN_FUNC
)
186 pn2
->pn_funbox
->node
= this;
188 pn_type
= pn2
->pn_type
;
190 pn_arity
= pn2
->pn_arity
;
200 pn_used
= pn_defn
= false;
201 pn_arity
= PN_NULLARY
;
205 JSCompiler::init(const jschar
*base
, size_t length
,
206 FILE *fp
, const char *filename
, uintN lineno
)
208 JSContext
*cx
= context
;
210 tempPoolMark
= JS_ARENA_MARK(&cx
->tempPool
);
211 if (!js_InitTokenStream(cx
, TS(this), base
, length
, fp
, filename
, lineno
)) {
212 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
216 /* Root atoms and objects allocated for the parsed tree. */
217 JS_KEEP_ATOMS(cx
->runtime
);
218 JS_PUSH_TEMP_ROOT_COMPILER(cx
, this, &tempRoot
);
222 JSCompiler::~JSCompiler()
224 JSContext
*cx
= context
;
227 JSPRINCIPALS_DROP(cx
, principals
);
228 JS_ASSERT(tempRoot
.u
.compiler
== this);
229 JS_POP_TEMP_ROOT(cx
, &tempRoot
);
230 JS_UNKEEP_ATOMS(cx
->runtime
);
231 js_CloseTokenStream(cx
, TS(this));
232 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
236 JSCompiler::setPrincipals(JSPrincipals
*prin
)
238 JS_ASSERT(!principals
);
240 JSPRINCIPALS_HOLD(context
, prin
);
245 JSCompiler::newObjectBox(JSObject
*obj
)
250 * We use JSContext.tempPool to allocate parsed objects and place them on
251 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
252 * containing the entries must be alive until we are done with scanning,
253 * parsing and code generation for the whole script or top-level function.
256 JS_ARENA_ALLOCATE_TYPE(objbox
, JSObjectBox
, &context
->tempPool
);
258 js_ReportOutOfScriptQuota(context
);
261 objbox
->traceLink
= traceListHead
;
262 traceListHead
= objbox
;
263 objbox
->emitLink
= NULL
;
264 objbox
->object
= obj
;
269 JSCompiler::newFunctionBox(JSObject
*obj
, JSParseNode
*fn
, JSTreeContext
*tc
)
272 JS_ASSERT(HAS_FUNCTION_CLASS(obj
));
275 * We use JSContext.tempPool to allocate parsed objects and place them on
276 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
277 * containing the entries must be alive until we are done with scanning,
278 * parsing and code generation for the whole script or top-level function.
280 JSFunctionBox
*funbox
;
281 JS_ARENA_ALLOCATE_TYPE(funbox
, JSFunctionBox
, &context
->tempPool
);
283 js_ReportOutOfScriptQuota(context
);
286 funbox
->traceLink
= traceListHead
;
287 traceListHead
= funbox
;
288 funbox
->emitLink
= NULL
;
289 funbox
->object
= obj
;
291 funbox
->siblings
= tc
->functionList
;
292 tc
->functionList
= funbox
;
293 ++tc
->compiler
->functionCount
;
295 funbox
->parent
= tc
->funbox
;
296 funbox
->queued
= false;
297 funbox
->level
= tc
->staticLevel
;
298 funbox
->tcflags
= TCF_IN_FUNCTION
| (tc
->flags
& TCF_COMPILE_N_GO
);
303 JSCompiler::trace(JSTracer
*trc
)
307 JS_ASSERT(tempRoot
.u
.compiler
== this);
308 objbox
= traceListHead
;
310 JS_CALL_OBJECT_TRACER(trc
, objbox
->object
, "parser.object");
311 objbox
= objbox
->traceLink
;
316 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
);
319 UnlinkFunctionBox(JSParseNode
*pn
, JSTreeContext
*tc
)
321 JSFunctionBox
*funbox
= pn
->pn_funbox
;
323 JS_ASSERT(funbox
->node
== pn
);
326 JSFunctionBox
**funboxp
= &tc
->functionList
;
328 if (*funboxp
== funbox
) {
329 *funboxp
= funbox
->siblings
;
332 funboxp
= &(*funboxp
)->siblings
;
335 uint16 oldflags
= tc
->flags
;
336 JSFunctionBox
*oldlist
= tc
->functionList
;
338 tc
->flags
= (uint16
) funbox
->tcflags
;
339 tc
->functionList
= funbox
->kids
;
340 UnlinkFunctionBoxes(pn
->pn_body
, tc
);
341 funbox
->kids
= tc
->functionList
;
342 tc
->flags
= oldflags
;
343 tc
->functionList
= oldlist
;
345 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
346 pn
->pn_funbox
= NULL
;
351 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
)
354 switch (pn
->pn_arity
) {
358 UnlinkFunctionBoxes(pn
->pn_kid
, tc
);
361 UnlinkFunctionBoxes(pn
->pn_left
, tc
);
362 UnlinkFunctionBoxes(pn
->pn_right
, tc
);
365 UnlinkFunctionBoxes(pn
->pn_kid1
, tc
);
366 UnlinkFunctionBoxes(pn
->pn_kid2
, tc
);
367 UnlinkFunctionBoxes(pn
->pn_kid3
, tc
);
370 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
371 UnlinkFunctionBoxes(pn2
, tc
);
374 UnlinkFunctionBox(pn
, tc
);
377 UnlinkFunctionBoxes(pn
->maybeExpr(), tc
);
380 UnlinkFunctionBoxes(pn
->pn_tree
, tc
);
386 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
);
389 RecycleTree(JSParseNode
*pn
, JSTreeContext
*tc
)
391 JSParseNode
*next
, **head
;
396 /* Catch back-to-back dup recycles. */
397 JS_ASSERT(pn
!= tc
->compiler
->nodeList
);
399 if (pn
->pn_used
|| pn
->pn_defn
) {
401 * JSAtomLists own definition nodes along with their used-node chains.
402 * Defer recycling such nodes until we unwind to top level to avoid
403 * linkage overhead or (alternatively) unlinking runtime complexity.
404 * Yes, this means dead code can contribute to static analysis results!
406 * Do recycle kids here, since they are no longer needed.
409 RecycleFuncNameKids(pn
, tc
);
411 UnlinkFunctionBoxes(pn
, tc
);
412 head
= &tc
->compiler
->nodeList
;
415 #ifdef METER_PARSENODES
423 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
)
425 switch (pn
->pn_arity
) {
427 UnlinkFunctionBox(pn
, tc
);
432 * Only a definition node might have a non-null strong pn_expr link
433 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
434 * Every node with the pn_used flag set has a non-null pn_lexdef
435 * weak reference to its definition node.
437 if (!pn
->pn_used
&& pn
->pn_expr
) {
438 RecycleTree(pn
->pn_expr
, tc
);
444 JS_ASSERT(PN_TYPE(pn
) == TOK_FUNCTION
);
449 NewOrRecycledNode(JSTreeContext
*tc
)
451 JSParseNode
*pn
, *pn2
;
453 pn
= tc
->compiler
->nodeList
;
455 JSContext
*cx
= tc
->compiler
->context
;
457 JS_ARENA_ALLOCATE_TYPE(pn
, JSParseNode
, &cx
->tempPool
);
459 js_ReportOutOfScriptQuota(cx
);
461 tc
->compiler
->nodeList
= pn
->pn_next
;
463 /* Recycle immediate descendents only, to save work and working set. */
464 switch (pn
->pn_arity
) {
466 RecycleTree(pn
->pn_body
, tc
);
471 while (pn2
&& !pn2
->pn_used
&& !pn2
->pn_defn
)
476 pn2
= RecycleTree(pn2
, tc
);
479 *pn
->pn_tail
= tc
->compiler
->nodeList
;
480 tc
->compiler
->nodeList
= pn
->pn_head
;
481 #ifdef METER_PARSENODES
482 recyclednodes
+= pn
->pn_count
;
489 RecycleTree(pn
->pn_kid1
, tc
);
490 RecycleTree(pn
->pn_kid2
, tc
);
491 RecycleTree(pn
->pn_kid3
, tc
);
494 if (pn
->pn_left
!= pn
->pn_right
)
495 RecycleTree(pn
->pn_left
, tc
);
496 RecycleTree(pn
->pn_right
, tc
);
499 RecycleTree(pn
->pn_kid
, tc
);
503 RecycleTree(pn
->pn_expr
, tc
);
510 #ifdef METER_PARSENODES
512 if (parsenodes
- recyclednodes
> maxparsenodes
)
513 maxparsenodes
= parsenodes
- recyclednodes
;
515 pn
->pn_used
= pn
->pn_defn
= false;
516 memset(&pn
->pn_u
, 0, sizeof pn
->pn_u
);
523 InitParseNode(JSParseNode
*pn
, JSTokenType type
, JSOp op
, JSParseNodeArity arity
)
527 pn
->pn_arity
= arity
;
528 JS_ASSERT(!pn
->pn_used
);
529 JS_ASSERT(!pn
->pn_defn
);
530 pn
->pn_next
= pn
->pn_link
= NULL
;
534 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
538 NewParseNode(JSParseNodeArity arity
, JSTreeContext
*tc
)
543 pn
= NewOrRecycledNode(tc
);
546 tp
= &CURRENT_TOKEN(&tc
->compiler
->tokenStream
);
547 InitParseNode(pn
, tp
->type
, JSOP_NOP
, arity
);
548 pn
->pn_pos
= tp
->pos
;
553 InitNameNodeCommon(JSParseNode
*pn
, JSTreeContext
*tc
)
556 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
557 pn
->pn_dflags
= tc
->atTopLevel() ? PND_TOPLEVEL
: 0;
558 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
559 pn
->pn_dflags
|= PND_BLOCKCHILD
;
560 pn
->pn_blockid
= tc
->blockid();
564 NewNameNode(JSContext
*cx
, JSTokenStream
*ts
, JSAtom
*atom
, JSTreeContext
*tc
)
568 pn
= NewParseNode(PN_NAME
, tc
);
571 InitNameNodeCommon(pn
, tc
);
577 NewBinary(JSTokenType tt
, JSOp op
, JSParseNode
*left
, JSParseNode
*right
,
580 JSParseNode
*pn
, *pn1
, *pn2
;
586 * Flatten a left-associative (left-heavy) tree of a given operator into
587 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
589 if (PN_TYPE(left
) == tt
&&
591 (js_CodeSpec
[op
].format
& JOF_LEFTASSOC
)) {
592 if (left
->pn_arity
!= PN_LIST
) {
593 pn1
= left
->pn_left
, pn2
= left
->pn_right
;
594 left
->pn_arity
= PN_LIST
;
597 if (tt
== TOK_PLUS
) {
598 if (pn1
->pn_type
== TOK_STRING
)
599 left
->pn_xflags
|= PNX_STRCAT
;
600 else if (pn1
->pn_type
!= TOK_NUMBER
)
601 left
->pn_xflags
|= PNX_CANTFOLD
;
602 if (pn2
->pn_type
== TOK_STRING
)
603 left
->pn_xflags
|= PNX_STRCAT
;
604 else if (pn2
->pn_type
!= TOK_NUMBER
)
605 left
->pn_xflags
|= PNX_CANTFOLD
;
609 left
->pn_pos
.end
= right
->pn_pos
.end
;
610 if (tt
== TOK_PLUS
) {
611 if (right
->pn_type
== TOK_STRING
)
612 left
->pn_xflags
|= PNX_STRCAT
;
613 else if (right
->pn_type
!= TOK_NUMBER
)
614 left
->pn_xflags
|= PNX_CANTFOLD
;
620 * Fold constant addition immediately, to conserve node space and, what's
621 * more, so js_FoldConstants never sees mixed addition and concatenation
622 * operations with more than one leading non-string operand in a PN_LIST
623 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
624 * to "3pt", not "12pt").
626 if (tt
== TOK_PLUS
&&
627 left
->pn_type
== TOK_NUMBER
&&
628 right
->pn_type
== TOK_NUMBER
) {
629 left
->pn_dval
+= right
->pn_dval
;
630 left
->pn_pos
.end
= right
->pn_pos
.end
;
631 RecycleTree(right
, tc
);
635 pn
= NewOrRecycledNode(tc
);
638 InitParseNode(pn
, tt
, op
, PN_BINARY
);
639 pn
->pn_pos
.begin
= left
->pn_pos
.begin
;
640 pn
->pn_pos
.end
= right
->pn_pos
.end
;
642 pn
->pn_right
= right
;
646 #if JS_HAS_GETTER_SETTER
648 CheckGetterOrSetter(JSContext
*cx
, JSTokenStream
*ts
, JSTokenType tt
)
655 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_NAME
);
656 atom
= CURRENT_TOKEN(ts
).t_atom
;
658 if (atom
== rt
->atomState
.getterAtom
)
660 else if (atom
== rt
->atomState
.setterAtom
)
664 if (js_PeekTokenSameLine(cx
, ts
) != tt
)
666 (void) js_GetToken(cx
, ts
);
667 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
668 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
669 JSMSG_BAD_GETTER_OR_SETTER
,
675 CURRENT_TOKEN(ts
).t_op
= op
;
676 if (JS_HAS_STRICT_OPTION(cx
)) {
677 name
= js_AtomToPrintableString(cx
, atom
);
679 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
680 JSREPORT_WARNING
| JSREPORT_STRICT
,
681 JSMSG_DEPRECATED_USAGE
,
691 GenerateBlockId(JSTreeContext
*tc
, uint32
& blockid
)
693 if (tc
->blockidGen
== JS_BIT(20)) {
694 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
695 JSMSG_NEED_DIET
, "program");
698 blockid
= tc
->blockidGen
++;
703 GenerateBlockIdForStmtNode(JSParseNode
*pn
, JSTreeContext
*tc
)
705 JS_ASSERT(tc
->topStmt
);
706 JS_ASSERT(STMT_MAYBE_SCOPE(tc
->topStmt
));
707 JS_ASSERT(pn
->pn_type
== TOK_LC
|| pn
->pn_type
== TOK_LEXICALSCOPE
);
708 if (!GenerateBlockId(tc
, tc
->topStmt
->blockid
))
710 pn
->pn_blockid
= tc
->topStmt
->blockid
;
715 * Parse a top-level JS script.
718 JSCompiler::parse(JSObject
*chain
)
721 * Protect atoms from being collected by a GC activation, which might
722 * - nest on this thread due to out of memory (the so-called "last ditch"
723 * GC attempted within js_NewGCThing), or
724 * - run for any reason on another thread if this thread is suspended on
725 * an object lock before it finishes generating bytecode into a script
726 * protected from the GC by a root or a stack frame reference.
728 JSTreeContext
tc(this);
729 tc
.scopeChain
= chain
;
730 if (!GenerateBlockId(&tc
, tc
.bodyid
))
733 JSParseNode
*pn
= Statements(context
, TS(this), &tc
);
735 if (!js_MatchToken(context
, TS(this), TOK_EOF
)) {
736 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
740 if (!js_FoldConstants(context
, pn
, &tc
))
748 SetStaticLevel(JSTreeContext
*tc
, uintN staticLevel
)
751 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
752 * (0xffffffff) and other cookies with that level.
754 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
755 * practically speaking it leaves more than enough room for upvars. In fact
756 * we might want to split cookie fields giving fewer bits for skip and more
757 * for slot, but only based on evidence.
759 if (staticLevel
>= FREE_STATIC_LEVEL
) {
760 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
761 JSMSG_TOO_DEEP
, js_function_str
);
764 tc
->staticLevel
= staticLevel
;
769 * Compile a top-level script.
772 JSCompiler::compileScript(JSContext
*cx
, JSObject
*scopeChain
, JSStackFrame
*callerFrame
,
773 JSPrincipals
*principals
, uint32 tcflags
,
774 const jschar
*chars
, size_t length
,
775 FILE *file
, const char *filename
, uintN lineno
,
776 JSString
*source
/* = NULL */)
778 JSCompiler
jsc(cx
, principals
, callerFrame
);
779 JSArenaPool codePool
, notePool
;
782 uint32 scriptGlobals
;
784 #ifdef METER_PARSENODES
785 void *sbrk(ptrdiff_t), *before
= sbrk(0);
788 JS_ASSERT(!(tcflags
& ~(TCF_COMPILE_N_GO
| TCF_NO_SCRIPT_RVAL
|
789 TCF_STATIC_LEVEL_MASK
)));
792 * The scripted callerFrame can only be given for compile-and-go scripts
793 * and non-zero static level requires callerFrame.
795 JS_ASSERT_IF(callerFrame
, tcflags
& TCF_COMPILE_N_GO
);
796 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags
) != 0, callerFrame
);
798 if (!jsc
.init(chars
, length
, file
, filename
, lineno
))
801 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
802 &cx
->scriptStackQuota
);
803 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
804 &cx
->scriptStackQuota
);
806 JSCodeGenerator
cg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
808 MUST_FLOW_THROUGH("out");
809 cg
.flags
|= (uint16
) tcflags
;
810 cg
.scopeChain
= scopeChain
;
811 if (!SetStaticLevel(&cg
, TCF_GET_STATIC_LEVEL(tcflags
)))
815 * If funbox is non-null after we create the new script, callerFrame->fun
816 * was saved in the 0th object table entry.
818 JSObjectBox
*funbox
= NULL
;
820 if (tcflags
& TCF_COMPILE_N_GO
) {
823 * Save eval program source in script->atomMap.vector[0] for the
824 * eval cache (see obj_eval in jsobj.cpp).
826 JSAtom
*atom
= js_AtomizeString(cx
, source
, 0);
827 if (!atom
|| !cg
.atomList
.add(&jsc
, atom
))
831 if (callerFrame
&& callerFrame
->fun
) {
833 * An eval script in a caller frame needs to have its enclosing
834 * function captured in case it refers to an upvar, and someone
835 * wishes to decompile it while it's running.
837 funbox
= jsc
.newObjectBox(FUN_OBJECT(callerFrame
->fun
));
840 funbox
->emitLink
= cg
.objectList
.lastbox
;
841 cg
.objectList
.lastbox
= funbox
;
842 cg
.objectList
.length
++;
847 * Inline Statements to emit as we go to save AST space. We must generate
848 * our script-body blockid since we aren't calling Statements.
851 if (!GenerateBlockId(&cg
, bodyid
))
855 /* Null script early in case of error, to reduce our code footprint. */
857 #if JS_HAS_XML_SUPPORT
863 jsc
.tokenStream
.flags
|= TSF_OPERAND
;
864 tt
= js_PeekToken(cx
, &jsc
.tokenStream
);
865 jsc
.tokenStream
.flags
&= ~TSF_OPERAND
;
869 JS_ASSERT(tt
== TOK_ERROR
);
873 pn
= Statement(cx
, &jsc
.tokenStream
, &cg
);
876 JS_ASSERT(!cg
.blockNode
);
878 if (!js_FoldConstants(cx
, pn
, &cg
))
881 if (cg
.functionList
) {
882 if (!jsc
.analyzeFunctions(cg
.functionList
, cg
.flags
))
884 cg
.functionList
= NULL
;
887 if (!js_EmitTree(cx
, &cg
, pn
))
889 #if JS_HAS_XML_SUPPORT
890 if (PN_TYPE(pn
) != TOK_SEMI
||
892 !TREE_TYPE_IS_XML(PN_TYPE(pn
->pn_kid
))) {
896 RecycleTree(pn
, &cg
);
899 #if JS_HAS_XML_SUPPORT
901 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
902 * For background, see:
904 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
906 if (pn
&& onlyXML
&& (tcflags
& TCF_NO_SCRIPT_RVAL
)) {
907 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
, JSREPORT_ERROR
,
908 JSMSG_XML_WHOLE_PROGRAM
);
914 * Global variables and regexps share the index space with locals. Due to
915 * incremental code generation we need to patch the bytecode to adjust the
916 * local references to skip the globals.
918 scriptGlobals
= cg
.ngvars
+ cg
.regexpList
.length
;
919 if (scriptGlobals
!= 0) {
920 jsbytecode
*code
, *end
;
922 const JSCodeSpec
*cs
;
925 if (scriptGlobals
>= SLOTNO_LIMIT
)
928 for (end
= code
+ CG_OFFSET(&cg
); code
!= end
; code
+= len
) {
929 JS_ASSERT(code
< end
);
931 cs
= &js_CodeSpec
[op
];
932 len
= (cs
->length
> 0)
934 : js_GetVariableBytecodeLength(code
);
935 if (JOF_TYPE(cs
->format
) == JOF_LOCAL
||
936 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
)) {
938 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
939 * emitted only for a function.
941 JS_ASSERT((JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ==
942 (op
== JSOP_GETLOCALPROP
));
943 slot
= GET_SLOTNO(code
);
944 slot
+= scriptGlobals
;
945 if (slot
>= SLOTNO_LIMIT
)
947 SET_SLOTNO(code
, slot
);
952 #ifdef METER_PARSENODES
953 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
954 (char *)sbrk(0) - (char *)before
,
957 parsenodes
- recyclednodes
);
962 * Nowadays the threaded interpreter needs a stop instruction, so we
963 * do have to emit that here.
965 if (js_Emit1(cx
, &cg
, JSOP_STOP
) < 0)
967 #ifdef METER_PARSENODES
968 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
969 (char *)sbrk(0) - (char *)before
, CG_OFFSET(&cg
), cg
.noteCount
);
972 JS_DumpArenaStats(stdout
);
974 script
= js_NewScriptFromCG(cx
, &cg
);
975 if (script
&& funbox
)
976 script
->flags
|= JSSF_SAVED_CALLER_FUN
;
978 #ifdef JS_SCOPE_DEPTH_METER
980 JSObject
*obj
= scopeChain
;
982 while ((obj
= OBJ_GET_PARENT(cx
, obj
)) != NULL
)
984 JS_BASIC_STATS_ACCUM(&cx
->runtime
->hostenvScopeDepthStats
, depth
);
989 JS_FinishArenaPool(&codePool
);
990 JS_FinishArenaPool(¬ePool
);
994 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
995 JSREPORT_ERROR
, JSMSG_TOO_MANY_LOCALS
);
1001 * Insist on a final return before control flows out of pn. Try to be a bit
1002 * smart about loops: do {...; return e2;} while(0) at the end of a function
1003 * that contains an early return e1 will get a strict warning. Similarly for
1004 * iloops: while (true){...} is treated as though ... returns.
1006 #define ENDS_IN_OTHER 0
1007 #define ENDS_IN_RETURN 1
1008 #define ENDS_IN_BREAK 2
1011 HasFinalReturn(JSParseNode
*pn
)
1013 JSParseNode
*pn2
, *pn3
;
1014 uintN rv
, rv2
, hasDefault
;
1016 switch (pn
->pn_type
) {
1019 return ENDS_IN_OTHER
;
1020 return HasFinalReturn(pn
->last());
1024 return ENDS_IN_OTHER
;
1025 return HasFinalReturn(pn
->pn_kid2
) & HasFinalReturn(pn
->pn_kid3
);
1029 if (pn2
->pn_type
== TOK_PRIMARY
&& pn2
->pn_op
== JSOP_TRUE
)
1030 return ENDS_IN_RETURN
;
1031 if (pn2
->pn_type
== TOK_NUMBER
&& pn2
->pn_dval
)
1032 return ENDS_IN_RETURN
;
1033 return ENDS_IN_OTHER
;
1037 if (pn2
->pn_type
== TOK_PRIMARY
) {
1038 if (pn2
->pn_op
== JSOP_FALSE
)
1039 return HasFinalReturn(pn
->pn_left
);
1040 if (pn2
->pn_op
== JSOP_TRUE
)
1041 return ENDS_IN_RETURN
;
1043 if (pn2
->pn_type
== TOK_NUMBER
) {
1044 if (pn2
->pn_dval
== 0)
1045 return HasFinalReturn(pn
->pn_left
);
1046 return ENDS_IN_RETURN
;
1048 return ENDS_IN_OTHER
;
1052 if (pn2
->pn_arity
== PN_TERNARY
&& !pn2
->pn_kid2
)
1053 return ENDS_IN_RETURN
;
1054 return ENDS_IN_OTHER
;
1057 rv
= ENDS_IN_RETURN
;
1058 hasDefault
= ENDS_IN_OTHER
;
1060 if (pn2
->pn_type
== TOK_LEXICALSCOPE
)
1062 for (pn2
= pn2
->pn_head
; rv
&& pn2
; pn2
= pn2
->pn_next
) {
1063 if (pn2
->pn_type
== TOK_DEFAULT
)
1064 hasDefault
= ENDS_IN_RETURN
;
1065 pn3
= pn2
->pn_right
;
1066 JS_ASSERT(pn3
->pn_type
== TOK_LC
);
1068 rv2
= HasFinalReturn(pn3
->last());
1069 if (rv2
== ENDS_IN_OTHER
&& pn2
->pn_next
)
1070 /* Falling through to next case or default. */;
1075 /* If a final switch has no default case, we judge it harshly. */
1080 return ENDS_IN_BREAK
;
1083 return HasFinalReturn(pn
->pn_right
);
1086 return ENDS_IN_RETURN
;
1089 case TOK_LEXICALSCOPE
:
1090 return HasFinalReturn(pn
->expr());
1093 return ENDS_IN_RETURN
;
1096 /* If we have a finally block that returns, we are done. */
1098 rv
= HasFinalReturn(pn
->pn_kid3
);
1099 if (rv
== ENDS_IN_RETURN
)
1103 /* Else check the try block and any and all catch statements. */
1104 rv
= HasFinalReturn(pn
->pn_kid1
);
1106 JS_ASSERT(pn
->pn_kid2
->pn_arity
== PN_LIST
);
1107 for (pn2
= pn
->pn_kid2
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
1108 rv
&= HasFinalReturn(pn2
);
1113 /* Check this catch block's body. */
1114 return HasFinalReturn(pn
->pn_kid3
);
1117 /* Non-binary let statements are let declarations. */
1118 if (pn
->pn_arity
!= PN_BINARY
)
1119 return ENDS_IN_OTHER
;
1120 return HasFinalReturn(pn
->pn_right
);
1123 return ENDS_IN_OTHER
;
1128 ReportBadReturn(JSContext
*cx
, JSTreeContext
*tc
, uintN flags
, uintN errnum
,
1133 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1134 if (tc
->fun
->atom
) {
1135 name
= js_AtomToPrintableString(cx
, tc
->fun
->atom
);
1137 errnum
= anonerrnum
;
1140 return js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
, flags
,
1145 CheckFinalReturn(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*pn
)
1147 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1148 return HasFinalReturn(pn
) == ENDS_IN_RETURN
||
1149 ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
1150 JSMSG_NO_RETURN_VALUE
, JSMSG_ANON_NO_RETURN_VALUE
);
1153 static JSParseNode
*
1154 FunctionBody(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1156 JSStmtInfo stmtInfo
;
1157 uintN oldflags
, firstLine
;
1160 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1161 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
1162 stmtInfo
.flags
= SIF_BODY_BLOCK
;
1164 oldflags
= tc
->flags
;
1165 tc
->flags
&= ~(TCF_RETURN_EXPR
| TCF_RETURN_VOID
);
1168 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1169 * later, because we may have not peeked in ts yet, so Statements won't
1170 * acquire a valid pn->pn_pos.begin from the current token.
1172 firstLine
= ts
->lineno
;
1173 #if JS_HAS_EXPR_CLOSURES
1174 if (CURRENT_TOKEN(ts
).type
== TOK_LC
) {
1175 pn
= Statements(cx
, ts
, tc
);
1177 pn
= NewParseNode(PN_UNARY
, tc
);
1179 pn
->pn_kid
= AssignExpr(cx
, ts
, tc
);
1183 if (tc
->flags
& TCF_FUN_IS_GENERATOR
) {
1184 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
1185 JSMSG_BAD_GENERATOR_RETURN
,
1186 JSMSG_BAD_ANON_GENERATOR_RETURN
);
1189 pn
->pn_type
= TOK_RETURN
;
1190 pn
->pn_op
= JSOP_RETURN
;
1191 pn
->pn_pos
.end
= pn
->pn_kid
->pn_pos
.end
;
1197 pn
= Statements(cx
, ts
, tc
);
1201 JS_ASSERT(!(tc
->topStmt
->flags
& SIF_SCOPE
));
1202 js_PopStatement(tc
);
1203 pn
->pn_pos
.begin
.lineno
= firstLine
;
1205 /* Check for falling off the end of a function that returns a value. */
1206 if (JS_HAS_STRICT_OPTION(cx
) && (tc
->flags
& TCF_RETURN_EXPR
) &&
1207 !CheckFinalReturn(cx
, tc
, pn
)) {
1212 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
1216 static JSAtomListElement
*
1217 MakePlaceholder(JSParseNode
*pn
, JSTreeContext
*tc
)
1219 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, pn
->pn_atom
);
1223 JSDefinition
*dn
= (JSDefinition
*)
1224 NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), pn
->pn_atom
, tc
);
1228 ALE_SET_DEFN(ale
, dn
);
1230 dn
->pn_dflags
|= PND_FORWARD
| PND_PLACEHOLDER
;
1231 pn
->pn_dflags
|= PND_FORWARD
;
1236 Define(JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
1238 JS_ASSERT(!pn
->pn_used
);
1239 JS_ASSERT_IF(pn
->pn_defn
, pn
->isPlaceholder());
1242 JSAtomListElement
*ale
= NULL
;
1243 JSAtomList
*list
= NULL
;
1246 ale
= (list
= &tc
->decls
)->rawLookup(atom
, hep
);
1248 ale
= (list
= &tc
->lexdeps
)->rawLookup(atom
, hep
);
1251 JSDefinition
*dn
= ALE_DEFN(ale
);
1253 JSParseNode
**pnup
= &dn
->dn_uses
;
1255 uintN start
= let
? pn
->pn_blockid
: tc
->bodyid
;
1257 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_blockid
>= start
) {
1258 JS_ASSERT(pnu
->pn_used
);
1259 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1260 pn
->pn_dflags
|= pnu
->pn_dflags
& (PND_ASSIGNED
| PND_FUNARG
);
1261 pnup
= &pnu
->pn_link
;
1264 if (pnu
!= dn
->dn_uses
) {
1265 *pnup
= pn
->dn_uses
;
1266 pn
->dn_uses
= dn
->dn_uses
;
1269 if ((!pnu
|| pnu
->pn_blockid
< tc
->bodyid
) && list
!= &tc
->decls
)
1270 list
->rawRemove(tc
->compiler
, ale
, hep
);
1275 ale
= tc
->decls
.add(tc
->compiler
, atom
, let
? JSAtomList::SHADOW
: JSAtomList::UNIQUE
);
1278 ALE_SET_DEFN(ale
, pn
);
1280 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
1285 LinkUseToDef(JSParseNode
*pn
, JSDefinition
*dn
, JSTreeContext
*tc
)
1287 JS_ASSERT(!pn
->pn_used
);
1288 JS_ASSERT(!pn
->pn_defn
);
1289 JS_ASSERT(pn
!= dn
->dn_uses
);
1290 pn
->pn_link
= dn
->dn_uses
;
1297 ForgetUse(JSParseNode
*pn
)
1300 JS_ASSERT(!pn
->pn_defn
);
1304 JSParseNode
**pnup
= &pn
->lexdef()->dn_uses
;
1306 while ((pnu
= *pnup
) != pn
)
1307 pnup
= &pnu
->pn_link
;
1308 *pnup
= pn
->pn_link
;
1309 pn
->pn_used
= false;
1312 static JSParseNode
*
1313 MakeAssignment(JSParseNode
*pn
, JSParseNode
*rhs
, JSTreeContext
*tc
)
1315 JSParseNode
*lhs
= NewOrRecycledNode(tc
);
1321 JSDefinition
*dn
= pn
->pn_lexdef
;
1322 JSParseNode
**pnup
= &dn
->dn_uses
;
1325 pnup
= &(*pnup
)->pn_link
;
1327 lhs
->pn_link
= pn
->pn_link
;
1331 pn
->pn_type
= TOK_ASSIGN
;
1332 pn
->pn_op
= JSOP_NOP
;
1333 pn
->pn_arity
= PN_BINARY
;
1334 pn
->pn_used
= pn
->pn_defn
= false;
1340 static JSParseNode
*
1341 MakeDefIntoUse(JSDefinition
*dn
, JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
)
1344 * If dn is var, const, or let, and it has an initializer, then we must
1345 * rewrite it to be an assignment node, whose freshly allocated left-hand
1346 * side becomes a use of pn.
1348 if (dn
->isBindingForm()) {
1349 JSParseNode
*rhs
= dn
->expr();
1351 JSParseNode
*lhs
= MakeAssignment(dn
, rhs
, tc
);
1354 //pn->dn_uses = lhs;
1355 dn
= (JSDefinition
*) lhs
;
1358 dn
->pn_op
= (js_CodeSpec
[dn
->pn_op
].format
& JOF_SET
) ? JSOP_SETNAME
: JSOP_NAME
;
1359 } else if (dn
->kind() == JSDefinition::FUNCTION
) {
1360 JS_ASSERT(dn
->isTopLevel());
1361 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
1362 dn
->pn_type
= TOK_NAME
;
1363 dn
->pn_arity
= PN_NAME
;
1367 /* Now make dn no longer a definition, rather a use of pn. */
1368 JS_ASSERT(dn
->pn_type
== TOK_NAME
);
1369 JS_ASSERT(dn
->pn_arity
== PN_NAME
);
1370 JS_ASSERT(dn
->pn_atom
== atom
);
1372 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
1373 JS_ASSERT(pnu
->pn_used
);
1374 JS_ASSERT(!pnu
->pn_defn
);
1375 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1376 pn
->pn_dflags
|= pnu
->pn_dflags
& (PND_ASSIGNED
| PND_FUNARG
);
1378 pn
->pn_dflags
|= dn
->pn_dflags
& (PND_ASSIGNED
| PND_FUNARG
);
1381 dn
->pn_defn
= false;
1383 dn
->pn_lexdef
= (JSDefinition
*) pn
;
1384 dn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1385 dn
->pn_dflags
&= ~PND_BOUND
;
1390 DefineArg(JSParseNode
*pn
, JSAtom
*atom
, uintN i
, JSTreeContext
*tc
)
1392 JSParseNode
*argpn
, *argsbody
;
1394 /* Flag tc so we don't have to lookup arguments on every use. */
1395 if (atom
== tc
->compiler
->context
->runtime
->atomState
.argumentsAtom
)
1396 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1399 * Make an argument definition node, distinguished by being in tc->decls
1400 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1401 * list node returned via pn->pn_body.
1403 argpn
= NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), atom
, tc
);
1406 JS_ASSERT(PN_TYPE(argpn
) == TOK_NAME
&& PN_OP(argpn
) == JSOP_NOP
);
1408 /* Arguments are initialized by definition. */
1409 argpn
->pn_dflags
|= PND_INITIALIZED
;
1410 if (!Define(argpn
, atom
, tc
))
1413 argsbody
= pn
->pn_body
;
1415 argsbody
= NewParseNode(PN_LIST
, tc
);
1418 argsbody
->pn_type
= TOK_ARGSBODY
;
1419 argsbody
->pn_op
= JSOP_NOP
;
1420 argsbody
->makeEmpty();
1421 pn
->pn_body
= argsbody
;
1423 argsbody
->append(argpn
);
1425 argpn
->pn_op
= JSOP_GETARG
;
1426 argpn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, i
);
1427 argpn
->pn_dflags
|= PND_BOUND
;
1432 * Compile a JS function body, which might appear as the value of an event
1433 * handler attribute in an HTML <INPUT> tag.
1436 JSCompiler::compileFunctionBody(JSContext
*cx
, JSFunction
*fun
, JSPrincipals
*principals
,
1437 const jschar
*chars
, size_t length
,
1438 const char *filename
, uintN lineno
)
1440 JSCompiler
jsc(cx
, principals
);
1442 if (!jsc
.init(chars
, length
, NULL
, filename
, lineno
))
1445 /* No early return from after here until the js_FinishArenaPool calls. */
1446 JSArenaPool codePool
, notePool
;
1447 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
1448 &cx
->scriptStackQuota
);
1449 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
1450 &cx
->scriptStackQuota
);
1452 JSCodeGenerator
funcg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
1453 funcg
.flags
|= TCF_IN_FUNCTION
;
1455 if (!GenerateBlockId(&funcg
, funcg
.bodyid
))
1458 /* FIXME: make Function format the source for a function definition. */
1459 jsc
.tokenStream
.tokens
[0].type
= TOK_NAME
;
1460 JSParseNode
*fn
= NewParseNode(PN_FUNC
, &funcg
);
1463 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1465 uintN nargs
= fun
->nargs
;
1467 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
1471 for (uintN i
= 0; i
< nargs
; i
++) {
1472 JSAtom
*name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1473 if (!DefineArg(fn
, name
, i
, &funcg
)) {
1483 * Farble the body so that it looks like a block statement to js_EmitTree,
1484 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1485 * done parsing, we must fold constants, analyze any nested functions, and
1486 * generate code for this function, including a stop opcode at the end.
1488 CURRENT_TOKEN(&jsc
.tokenStream
).type
= TOK_LC
;
1489 JSParseNode
*pn
= fn
? FunctionBody(cx
, &jsc
.tokenStream
, &funcg
) : NULL
;
1491 if (!js_MatchToken(cx
, &jsc
.tokenStream
, TOK_EOF
)) {
1492 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
1493 JSREPORT_ERROR
, JSMSG_SYNTAX_ERROR
);
1495 } else if (!js_FoldConstants(cx
, pn
, &funcg
)) {
1496 /* js_FoldConstants reported the error already. */
1498 } else if (funcg
.functionList
&&
1499 !jsc
.analyzeFunctions(funcg
.functionList
, funcg
.flags
)) {
1503 JS_ASSERT(PN_TYPE(fn
->pn_body
) == TOK_ARGSBODY
);
1504 fn
->pn_body
->append(pn
);
1508 if (!js_EmitFunctionScript(cx
, &funcg
, pn
))
1513 /* Restore saved state and release code generation arenas. */
1514 JS_FinishArenaPool(&codePool
);
1515 JS_FinishArenaPool(¬ePool
);
1520 * Parameter block types for the several Binder functions. We use a common
1521 * helper function signature in order to share code among destructuring and
1522 * simple variable declaration parsers. In the destructuring case, the binder
1523 * function is called indirectly from the variable declaration parser by way
1524 * of CheckDestructuring and its friends.
1526 typedef struct BindData BindData
;
1529 (*Binder
)(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
);
1532 JSParseNode
*pn
; /* name node for definition processing and
1533 error source coordinates */
1534 JSOp op
; /* prolog bytecode or nop */
1535 Binder binder
; /* binder, discriminates u */
1544 BindLocalVariable(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
,
1545 JSLocalKind localKind
)
1547 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1550 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1551 * Instead 'var arguments' always restates the predefined property of the
1552 * activation objects whose name is 'arguments'. Assignment to such a
1553 * variable must be handled specially.
1555 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
1558 return js_AddLocal(cx
, fun
, atom
, localKind
);
1561 #if JS_HAS_DESTRUCTURING
1563 * Forward declaration to maintain top-down presentation.
1565 static JSParseNode
*
1566 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
1570 BindDestructuringArg(JSContext
*cx
, BindData
*data
, JSAtom
*atom
,
1573 JSAtomListElement
*ale
;
1576 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1577 ale
= tc
->decls
.lookup(atom
);
1579 if (!ale
&& !Define(pn
, atom
, tc
))
1582 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
1583 if (localKind
!= JSLOCAL_NONE
) {
1584 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
,
1585 JSREPORT_ERROR
, JSMSG_DESTRUCT_DUP_ARG
);
1589 uintN index
= tc
->fun
->u
.i
.nvars
;
1590 if (!BindLocalVariable(cx
, tc
->fun
, atom
, JSLOCAL_VAR
))
1592 pn
->pn_op
= JSOP_SETLOCAL
;
1593 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
1594 pn
->pn_dflags
|= PND_BOUND
;
1597 #endif /* JS_HAS_DESTRUCTURING */
1600 JSCompiler::newFunction(JSTreeContext
*tc
, JSAtom
*atom
, uintN lambda
)
1605 JS_ASSERT((lambda
& ~JSFUN_LAMBDA
) == 0);
1608 * Find the global compilation context in order to pre-set the newborn
1609 * function's parent slot to tc->scopeChain. If the global context is a
1610 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1611 * clear parent and proto.
1615 parent
= (tc
->flags
& TCF_IN_FUNCTION
) ? NULL
: tc
->scopeChain
;
1617 fun
= js_NewFunction(context
, NULL
, NULL
, 0, JSFUN_INTERPRETED
| lambda
,
1620 if (fun
&& !(tc
->flags
& TCF_COMPILE_N_GO
)) {
1621 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun
));
1622 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun
));
1628 MatchOrInsertSemicolon(JSContext
*cx
, JSTokenStream
*ts
)
1632 ts
->flags
|= TSF_OPERAND
;
1633 tt
= js_PeekTokenSameLine(cx
, ts
);
1634 ts
->flags
&= ~TSF_OPERAND
;
1635 if (tt
== TOK_ERROR
)
1637 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1638 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1639 JSMSG_SEMI_BEFORE_STMNT
);
1642 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
1647 JSCompiler::analyzeFunctions(JSFunctionBox
*funbox
, uint16
& tcflags
)
1649 if (!markFunArgs(funbox
, tcflags
))
1651 setFunctionKinds(funbox
, tcflags
);
1656 * Mark as funargs any functions that reach up to one or more upvars across an
1657 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1659 * function f(o, p) {
1660 * o.m = function o_m(a) {
1661 * function g() { return p; }
1662 * function h() { return a; }
1667 * but without this extra marking phase, function g will not be marked as a
1668 * funarg since it is called from within its parent scope. But g reaches up to
1669 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1670 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1671 * nor uses an upvar "above" o_m's level.
1673 * If function g itself contained lambdas that contained non-lambdas that reach
1674 * up above its level, then those non-lambdas would have to be marked too. This
1675 * process is potentially exponential in the number of functions, but generally
1676 * not so complex. But it can't be done during a single recursive traversal of
1677 * the funbox tree, so we must use a work queue.
1680 FindFunArgs(JSFunctionBox
*funbox
, int level
, JSFunctionBoxQueue
*queue
)
1683 JSParseNode
*fn
= funbox
->node
;
1684 int fnlevel
= level
;
1687 * An eval can leak funbox, functions along its ancestor line, and its
1688 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1689 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1690 * already been marked as funargs by this point. Therefore we have to
1691 * flag only funbox->node and funbox->kids' nodes here.
1693 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1695 for (JSFunctionBox
*kid
= funbox
->kids
; kid
; kid
= kid
->siblings
)
1696 kid
->node
->setFunArg();
1699 if (fn
->isFunArg()) {
1700 queue
->push(funbox
);
1701 fnlevel
= int(funbox
->level
);
1703 JSParseNode
*pn
= fn
->pn_body
;
1705 if (pn
->pn_type
== TOK_UPVARS
) {
1706 JSAtomList
upvars(pn
->pn_names
);
1707 JS_ASSERT(upvars
.count
!= 0);
1709 JSAtomListIterator
iter(&upvars
);
1710 JSAtomListElement
*ale
;
1712 while ((ale
= iter()) != NULL
) {
1713 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1715 if (!lexdep
->isFreeVar() && int(lexdep
->frameLevel()) <= fnlevel
) {
1717 queue
->push(funbox
);
1718 fnlevel
= int(funbox
->level
);
1726 FindFunArgs(funbox
->kids
, fnlevel
, queue
);
1727 } while ((funbox
= funbox
->siblings
) != NULL
);
1731 JSCompiler::markFunArgs(JSFunctionBox
*funbox
, uintN tcflags
)
1733 JSFunctionBoxQueue queue
;
1734 if (!queue
.init(functionCount
))
1737 FindFunArgs(funbox
, -1, &queue
);
1738 while ((funbox
= queue
.pull()) != NULL
) {
1739 JSParseNode
*fn
= funbox
->node
;
1740 JS_ASSERT(fn
->isFunArg());
1742 JSParseNode
*pn
= fn
->pn_body
;
1743 if (pn
->pn_type
== TOK_UPVARS
) {
1744 JSAtomList
upvars(pn
->pn_names
);
1745 JS_ASSERT(upvars
.count
!= 0);
1747 JSAtomListIterator
iter(&upvars
);
1748 JSAtomListElement
*ale
;
1750 while ((ale
= iter()) != NULL
) {
1751 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1753 if (!lexdep
->isFreeVar() &&
1754 !lexdep
->isFunArg() &&
1755 lexdep
->kind() == JSDefinition::FUNCTION
) {
1757 * Mark this formerly-Algol-like function as a funarg,
1758 * since it is referenced from a funarg and can no longer
1759 * use JSOP_{GET,CALL}UPVAR to access upvars.
1761 * Progress is guaranteed since we set PND_FUNARG here,
1762 * which suppresses revisiting this function (namely the
1763 * !lexdep->isFunArg() test just above).
1765 lexdep
->setFunArg();
1767 JSFunctionBox
*afunbox
= lexdep
->pn_funbox
;
1768 queue
.push(afunbox
);
1771 * Walk over nested functions again, now that we have
1772 * changed the level across which it is unsafe to access
1773 * upvars using the runtime dynamic link (frame chain).
1776 FindFunArgs(afunbox
->kids
, afunbox
->level
, &queue
);
1785 MinBlockId(JSParseNode
*fn
, uint32 id
)
1787 if (fn
->pn_blockid
< id
)
1790 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
1791 if (pn
->pn_blockid
< id
)
1799 OneBlockId(JSParseNode
*fn
, uint32 id
)
1801 if (fn
->pn_blockid
!= id
)
1804 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
1805 if (pn
->pn_blockid
!= id
)
1813 JSCompiler::setFunctionKinds(JSFunctionBox
*funbox
, uint16
& tcflags
)
1815 #ifdef JS_FUNCTION_METERING
1816 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
1818 # define FUN_METER(x) ((void)0)
1820 JSFunctionBox
*parent
= funbox
->parent
;
1823 JSParseNode
*fn
= funbox
->node
;
1826 setFunctionKinds(funbox
->kids
, tcflags
);
1828 JSParseNode
*pn
= fn
->pn_body
;
1829 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
1832 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1834 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
1835 } else if (pn
->pn_type
!= TOK_UPVARS
) {
1837 * No lexical dependencies => null closure, for best performance.
1838 * A null closure needs no scope chain, but alas we've coupled
1839 * principals-finding to scope (for good fundamental reasons, but
1840 * the implementation overloads the parent slot and we should fix
1841 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1843 * In more detail: the ES3 spec allows the implementation to create
1844 * "joined function objects", or not, at its discretion. But real-
1845 * world implementations always create unique function objects for
1846 * closures, and this can be detected via mutation. Open question:
1847 * do popular implementations create unique function objects for
1850 * FIXME: bug 476950.
1852 FUN_METER(nofreeupvar
);
1853 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1855 JSAtomList
upvars(pn
->pn_names
);
1856 JS_ASSERT(upvars
.count
!= 0);
1858 JSAtomListIterator
iter(&upvars
);
1859 JSAtomListElement
*ale
;
1861 if (!fn
->isFunArg()) {
1863 * This function is Algol-like, it never escapes. So long as it
1864 * does not assign to outer variables, it needs only an upvars
1865 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1866 * bytecode to reach up the frame stack at runtime based on
1867 * those upvars' cookies.
1869 * Any assignments to upvars from functions called by this one
1870 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1871 * which load from stack homes when interpreting or from native
1872 * stack slots when executing a trace.
1874 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1875 * nested function to assign to an outer lexical variable, so
1876 * we defer adding yet more code footprint in the absence of
1877 * evidence motivating these opcodes.
1879 bool mutation
= !!(funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
);
1883 * Check that at least one outer lexical binding was assigned
1884 * to (global variables don't count). This is conservative: we
1885 * could limit assignments to those in the current function,
1886 * but that's too much work. As with flat closures (handled
1887 * below), we optimize for the case where outer bindings are
1888 * not reassigned anywhere.
1890 while ((ale
= iter()) != NULL
) {
1891 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1893 if (!lexdep
->isFreeVar()) {
1894 JS_ASSERT(lexdep
->frameLevel() <= funbox
->level
);
1896 if (lexdep
->isAssigned())
1904 FUN_METER(onlyfreevar
);
1905 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1906 } else if (!mutation
&& !(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
)) {
1908 * Algol-like functions can read upvars using the dynamic
1909 * link (cx->fp/fp->down). They do not need to entrain and
1910 * search their environment.
1913 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1915 if (!(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
))
1916 FUN_METER(setupvar
);
1922 * For each lexical dependency from this closure to an outer
1923 * binding, analyze whether it is safe to copy the binding's
1924 * value into a flat closure slot when the closure is formed.
1926 while ((ale
= iter()) != NULL
) {
1927 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1929 if (!lexdep
->isFreeVar()) {
1933 * Consider the current function (the lambda, innermost
1934 * below) using a var x defined two static levels up:
1940 * return function () { return x; };
1945 * So long as (1) the initialization in 'var x = 42'
1946 * dominates all uses of g and (2) x is not reassigned,
1947 * it is safe to optimize the lambda to a flat closure.
1948 * Uncommenting the early call to g makes it unsafe to
1949 * so optimize (z could name a global setter that calls
1952 JSFunctionBox
*afunbox
= funbox
;
1953 uintN lexdepLevel
= lexdep
->frameLevel();
1955 JS_ASSERT(lexdepLevel
<= funbox
->level
);
1956 while (afunbox
->level
!= lexdepLevel
) {
1957 afunbox
= afunbox
->parent
;
1960 * We can't form a flat closure that reaches up
1961 * across a funarg that encloses the closure, or
1962 * into the top level (to a 'let' variable in an
1963 * enclosing block in global code; this is the
1966 if (!afunbox
|| afunbox
->node
->isFunArg()) {
1967 JS_ASSERT_IF(!afunbox
,
1969 (!(tcflags
& TCF_IN_FUNCTION
) &&
1970 callerFrame
&& callerFrame
->fun
));
1976 * If afunbox's function (which is at the same level as
1977 * lexdep) is not a lambda, it will be hoisted, so it
1978 * could capture the undefined value that by default
1979 * initializes var/let/const bindings. And if lexdep is
1980 * a function that comes at (meaning a function refers
1981 * to its own name) or strictly after afunbox, we also
1982 * break to defeat the flat closure optimization.
1984 JSFunction
*afun
= (JSFunction
*) afunbox
->object
;
1985 if (!(afun
->flags
& JSFUN_LAMBDA
)) {
1986 if (lexdep
->isBindingForm())
1988 if (lexdep
->pn_pos
>= afunbox
->node
->pn_pos
)
1992 if (!lexdep
->isInitialized())
1995 JSDefinition::Kind lexdepKind
= lexdep
->kind();
1996 if (lexdepKind
!= JSDefinition::CONST
) {
1997 if (lexdep
->isAssigned())
2001 * Any formal could be mutated behind our back via
2002 * the arguments object, so deoptimize if the outer
2003 * function uses arguments.
2005 * In a Function constructor call where the final
2006 * argument -- the body source for the function to
2007 * create -- contains a nested function definition
2008 * or expression, afunbox->parent will be null. The
2009 * body source might use |arguments| outside of any
2010 * nested functions it may contain, so we have to
2011 * check the tcflags parameter that was passed in
2012 * from JSCompiler::compileFunctionBody.
2014 if (lexdepKind
== JSDefinition::ARG
&&
2015 ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
) &
2016 TCF_FUN_USES_ARGUMENTS
)) {
2022 * Check quick-and-dirty dominance relation. Function
2023 * definitions dominate their uses thanks to hoisting.
2024 * Other binding forms hoist as undefined, of course,
2025 * so check forward-reference and blockid relations.
2027 if (lexdepKind
!= JSDefinition::FUNCTION
) {
2028 if (lexdep
->isForward())
2032 * Watch out for code such as
2036 * var jQuery = ... = function (...) {
2037 * return new jQuery.foo.bar(baz);
2042 * where the jQuery var is not reassigned, but of
2043 * course is not initialized at the time that the
2044 * would-be-flat closure containing the jQuery
2047 if (lexdep
->pn_pos
.end
>= afunbox
->node
->pn_pos
.end
)
2050 if (lexdep
->isTopLevel()
2051 ? !MinBlockId(afunbox
->node
, lexdep
->pn_blockid
)
2052 : !lexdep
->isBlockChild() ||
2053 !afunbox
->node
->isBlockChild() ||
2054 !OneBlockId(afunbox
->node
, lexdep
->pn_blockid
)) {
2063 FUN_METER(onlyfreevar
);
2064 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2067 * We made it all the way through the upvar loop, so it's
2068 * safe to optimize to a flat closure.
2071 FUN_SET_KIND(fun
, JSFUN_FLAT_CLOSURE
);
2072 switch (PN_OP(fn
)) {
2074 fn
->pn_op
= JSOP_DEFFUN_FC
;
2076 case JSOP_DEFLOCALFUN
:
2077 fn
->pn_op
= JSOP_DEFLOCALFUN_FC
;
2080 fn
->pn_op
= JSOP_LAMBDA_FC
;
2083 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2084 JS_ASSERT(PN_OP(fn
) == JSOP_NOP
);
2087 FUN_METER(badfunarg
);
2092 if (FUN_KIND(fun
) == JSFUN_INTERPRETED
) {
2093 if (pn
->pn_type
!= TOK_UPVARS
) {
2095 parent
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2097 JSAtomList
upvars(pn
->pn_names
);
2098 JS_ASSERT(upvars
.count
!= 0);
2100 JSAtomListIterator
iter(&upvars
);
2101 JSAtomListElement
*ale
;
2104 * One or more upvars cannot be safely snapshot into a flat
2105 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2106 * all upvars, and for each non-free upvar, ensure that its
2107 * containing function has been flagged as heavyweight.
2109 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2110 * generating any code for a tree of nested functions.
2112 while ((ale
= iter()) != NULL
) {
2113 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2115 if (!lexdep
->isFreeVar()) {
2116 JSFunctionBox
*afunbox
= funbox
->parent
;
2117 uintN lexdepLevel
= lexdep
->frameLevel();
2121 * NB: afunbox->level is the static level of
2122 * the definition or expression of the function
2123 * parsed into afunbox, not the static level of
2124 * its body. Therefore we must add 1 to match
2125 * lexdep's level to find the afunbox whose
2126 * body contains the lexdep definition.
2128 if (afunbox
->level
+ 1U == lexdepLevel
) {
2129 afunbox
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2132 afunbox
= afunbox
->parent
;
2134 if (!afunbox
&& (tcflags
& TCF_IN_FUNCTION
))
2135 tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2141 funbox
= funbox
->siblings
;
2144 JS_ASSERT(funbox
->parent
== parent
);
2149 const char js_argument_str
[] = "argument";
2150 const char js_variable_str
[] = "variable";
2151 const char js_unknown_str
[] = "unknown";
2154 JSDefinition::kindString(Kind kind
)
2156 static const char *table
[] = {
2157 js_var_str
, js_const_str
, js_let_str
,
2158 js_function_str
, js_argument_str
, js_unknown_str
2161 JS_ASSERT(unsigned(kind
) <= unsigned(ARG
));
2165 static JSFunctionBox
*
2166 EnterFunction(JSParseNode
*fn
, JSTreeContext
*tc
, JSTreeContext
*funtc
,
2167 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2169 JSFunction
*fun
= tc
->compiler
->newFunction(tc
, funAtom
, lambda
);
2173 /* Create box for fun->object early to protect against last-ditch GC. */
2174 JSFunctionBox
*funbox
= tc
->compiler
->newFunctionBox(FUN_OBJECT(fun
), fn
, tc
);
2178 /* Initialize non-default members of funtc. */
2179 funtc
->flags
|= funbox
->tcflags
;
2180 funtc
->blockidGen
= tc
->blockidGen
;
2181 if (!GenerateBlockId(funtc
, funtc
->bodyid
))
2184 funtc
->funbox
= funbox
;
2186 if (!SetStaticLevel(funtc
, tc
->staticLevel
+ 1))
2193 LeaveFunction(JSParseNode
*fn
, JSTreeContext
*funtc
, JSTreeContext
*tc
,
2194 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2196 tc
->blockidGen
= funtc
->blockidGen
;
2198 fn
->pn_funbox
->tcflags
|= funtc
->flags
& (TCF_FUN_FLAGS
| TCF_COMPILE_N_GO
);
2200 fn
->pn_dflags
|= PND_INITIALIZED
;
2201 JS_ASSERT_IF(tc
->atTopLevel() && lambda
== 0 && funAtom
,
2202 fn
->pn_dflags
& PND_TOPLEVEL
);
2203 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
2204 fn
->pn_dflags
|= PND_BLOCKCHILD
;
2207 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2208 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2209 * params and body. We do this only if there are lexical dependencies not
2210 * satisfied by the function's declarations, to avoid penalizing functions
2211 * that use only their arguments and other local bindings.
2213 if (funtc
->lexdeps
.count
!= 0) {
2214 JSAtomListIterator
iter(&funtc
->lexdeps
);
2215 JSAtomListElement
*ale
;
2216 int foundCallee
= 0;
2218 while ((ale
= iter()) != NULL
) {
2219 JSAtom
*atom
= ALE_ATOM(ale
);
2220 JSDefinition
*dn
= ALE_DEFN(ale
);
2221 JS_ASSERT(dn
->isPlaceholder());
2223 if (atom
== funAtom
&& lambda
!= 0) {
2224 dn
->pn_op
= JSOP_CALLEE
;
2225 dn
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
->staticLevel
, CALLEE_UPVAR_SLOT
);
2226 dn
->pn_dflags
|= PND_BOUND
;
2229 * If this named function expression uses its own name other
2230 * than to call itself, flag this function as using arguments,
2231 * as if it had used arguments.callee instead of its own name.
2233 * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2234 * we are out of tcflags bits at the moment. If it deoptimizes
2235 * code unfairly (see JSCompiler::setFunctionKinds, where this
2236 * flag is interpreted in its broader sense, not only to mean
2237 * "this function might leak arguments.callee"), we can perhaps
2238 * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2239 * use that more precisely, both here and for unnamed function
2243 fn
->pn_funbox
->tcflags
|= TCF_FUN_USES_ARGUMENTS
;
2248 if (!(fn
->pn_funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
) &&
2251 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2252 * any use of dn in funtc assigns. See NoteLValue for the easy
2253 * backward-reference case; this is the hard forward-reference
2254 * case where we pay a higher price.
2256 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
2257 if (pnu
->isAssigned() && pnu
->pn_blockid
>= funtc
->bodyid
) {
2258 fn
->pn_funbox
->tcflags
|= TCF_FUN_SETS_OUTER_NAME
;
2264 JSAtomListElement
*outer_ale
= tc
->decls
.lookup(atom
);
2266 outer_ale
= tc
->lexdeps
.lookup(atom
);
2269 * Insert dn's uses list at the front of outer_dn's list.
2271 * Without loss of generality or correctness, we allow a dn to
2272 * be in inner and outer lexdeps, since the purpose of lexdeps
2273 * is one-pass coordination of name use and definition across
2274 * functions, and if different dn's are used we'll merge lists
2275 * when leaving the inner function.
2277 * The dn == outer_dn case arises with generator expressions
2278 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2279 * case), and nowhere else, currently.
2281 JSDefinition
*outer_dn
= ALE_DEFN(outer_ale
);
2283 if (dn
!= outer_dn
) {
2284 JSParseNode
**pnup
= &dn
->dn_uses
;
2287 while ((pnu
= *pnup
) != NULL
) {
2288 pnu
->pn_lexdef
= outer_dn
;
2289 pnup
= &pnu
->pn_link
;
2293 * Make dn be a use that redirects to outer_dn, because we
2294 * can't replace dn with outer_dn in all the pn_namesets in
2295 * the AST where it may be. Instead we make it forward to
2296 * outer_dn. See JSDefinition::resolve.
2298 *pnup
= outer_dn
->dn_uses
;
2299 outer_dn
->dn_uses
= dn
;
2300 outer_dn
->pn_dflags
|= (dn
->pn_dflags
& ~PND_PLACEHOLDER
);
2301 dn
->pn_defn
= false;
2303 dn
->pn_lexdef
= outer_dn
;
2306 /* Add an outer lexical dependency for ale's definition. */
2307 outer_ale
= tc
->lexdeps
.add(tc
->compiler
, atom
);
2310 ALE_SET_DEFN(outer_ale
, ALE_DEFN(ale
));
2314 if (funtc
->lexdeps
.count
- foundCallee
!= 0) {
2315 JSParseNode
*body
= fn
->pn_body
;
2317 fn
->pn_body
= NewParseNode(PN_NAMESET
, tc
);
2321 fn
->pn_body
->pn_type
= TOK_UPVARS
;
2322 fn
->pn_body
->pn_pos
= body
->pn_pos
;
2324 funtc
->lexdeps
.remove(tc
->compiler
, funAtom
);
2325 fn
->pn_body
->pn_names
= funtc
->lexdeps
;
2326 fn
->pn_body
->pn_tree
= body
;
2329 funtc
->lexdeps
.clear();
2335 static JSParseNode
*
2336 FunctionDef(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2340 JSParseNode
*pn
, *body
, *result
;
2343 JSAtomListElement
*ale
;
2344 #if JS_HAS_DESTRUCTURING
2345 JSParseNode
*item
, *list
= NULL
;
2346 bool destructuringArg
= false, duplicatedArg
= false;
2349 /* Make a TOK_FUNCTION node. */
2350 #if JS_HAS_GETTER_SETTER
2351 op
= CURRENT_TOKEN(ts
).t_op
;
2353 pn
= NewParseNode(PN_FUNC
, tc
);
2357 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2360 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2361 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2363 * Also treat function sub-statements (non-lambda, non-top-level functions)
2364 * as escaping funargs, since we can't statically analyze their definitions
2367 bool topLevel
= tc
->atTopLevel();
2368 pn
->pn_dflags
= (lambda
|| !topLevel
) ? PND_FUNARG
: 0;
2370 /* Scan the optional function name into funAtom. */
2371 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
2372 tt
= js_GetToken(cx
, ts
);
2373 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
2374 if (tt
== TOK_NAME
) {
2375 funAtom
= CURRENT_TOKEN(ts
).t_atom
;
2377 if (lambda
== 0 && (cx
->options
& JSOPTION_ANONFUNFIX
)) {
2378 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2379 JSMSG_SYNTAX_ERROR
);
2387 * Record names for function statements in tc->decls so we know when to
2388 * avoid optimizing variable references that might name a function.
2390 if (lambda
== 0 && funAtom
) {
2391 ale
= tc
->decls
.lookup(funAtom
);
2393 JSDefinition
*dn
= ALE_DEFN(ale
);
2394 JSDefinition::Kind dn_kind
= dn
->kind();
2396 JS_ASSERT(!dn
->pn_used
);
2397 JS_ASSERT(dn
->pn_defn
);
2399 if (JS_HAS_STRICT_OPTION(cx
) || dn_kind
== JSDefinition::CONST
) {
2400 const char *name
= js_AtomToPrintableString(cx
, funAtom
);
2402 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2403 (dn_kind
!= JSDefinition::CONST
)
2404 ? JSREPORT_WARNING
| JSREPORT_STRICT
2406 JSMSG_REDECLARED_VAR
,
2407 JSDefinition::kindString(dn_kind
),
2414 ALE_SET_DEFN(ale
, pn
);
2416 pn
->dn_uses
= dn
; /* dn->dn_uses is now pn_link */
2418 if (!MakeDefIntoUse(dn
, pn
, funAtom
, tc
))
2421 } else if (topLevel
) {
2423 * If this function was used before it was defined, claim the
2424 * pre-created definition node for this function that PrimaryExpr
2425 * put in tc->lexdeps on first forward reference, and recycle pn.
2429 ale
= tc
->lexdeps
.rawLookup(funAtom
, hep
);
2431 JSDefinition
*fn
= ALE_DEFN(ale
);
2433 JS_ASSERT(fn
->pn_defn
);
2434 fn
->pn_type
= TOK_FUNCTION
;
2435 fn
->pn_arity
= PN_FUNC
;
2436 fn
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2438 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2440 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
2441 RecycleTree(pn
, tc
);
2445 if (!Define(pn
, funAtom
, tc
))
2450 * A function nested at top level inside another's body needs only a
2451 * local variable to bind its name to its value, and not an activation
2452 * object property (it might also need the activation property, if the
2453 * outer function contains with statements, e.g., but the stack slot
2454 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2455 * JSOP_GETLOCAL bytecode).
2458 pn
->pn_dflags
|= PND_TOPLEVEL
;
2460 if (tc
->flags
& TCF_IN_FUNCTION
) {
2461 JSLocalKind localKind
;
2465 * Define a local in the outer function so that BindNameToSlot
2466 * can properly optimize accesses. Note that we need a local
2467 * variable, not an argument, for the function statement. Thus
2468 * we add a variable even if a parameter with the given name
2471 localKind
= js_LookupLocal(cx
, tc
->fun
, funAtom
, &index
);
2472 switch (localKind
) {
2475 index
= tc
->fun
->u
.i
.nvars
;
2476 if (!js_AddLocal(cx
, tc
->fun
, funAtom
, JSLOCAL_VAR
))
2481 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
2482 pn
->pn_dflags
|= PND_BOUND
;
2491 /* Initialize early for possible flags mutation via DestructuringExpr. */
2492 JSTreeContext
funtc(tc
->compiler
);
2494 JSFunctionBox
*funbox
= EnterFunction(pn
, tc
, &funtc
, funAtom
, lambda
);
2498 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
2500 #if JS_HAS_GETTER_SETTER
2502 fun
->flags
|= (op
== JSOP_GETTER
) ? JSPROP_GETTER
: JSPROP_SETTER
;
2505 /* Now parse formal argument list and compute fun->nargs. */
2506 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_FORMAL
);
2507 if (!js_MatchToken(cx
, ts
, TOK_RP
)) {
2509 tt
= js_GetToken(cx
, ts
);
2511 #if JS_HAS_DESTRUCTURING
2516 JSParseNode
*lhs
, *rhs
;
2519 /* See comment below in the TOK_NAME case. */
2521 goto report_dup_and_destructuring
;
2522 destructuringArg
= true;
2525 * A destructuring formal parameter turns into one or more
2526 * local variables initialized from properties of a single
2527 * anonymous positional parameter, so here we must tweak our
2528 * binder and its data.
2531 data
.op
= JSOP_DEFVAR
;
2532 data
.binder
= BindDestructuringArg
;
2533 lhs
= DestructuringExpr(cx
, &data
, &funtc
, tt
);
2538 * Adjust fun->nargs to count the single anonymous positional
2539 * parameter that is to be destructured.
2542 if (!js_AddLocal(cx
, fun
, NULL
, JSLOCAL_ARG
))
2546 * Synthesize a destructuring assignment from the single
2547 * anonymous positional parameter into the destructuring
2548 * left-hand-side expression and accumulate it in list.
2550 rhs
= NewNameNode(cx
, ts
, cx
->runtime
->atomState
.emptyAtom
, &funtc
);
2553 rhs
->pn_type
= TOK_NAME
;
2554 rhs
->pn_op
= JSOP_GETARG
;
2555 rhs
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
.staticLevel
, slot
);
2556 rhs
->pn_dflags
|= PND_BOUND
;
2558 item
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, lhs
, rhs
, &funtc
);
2562 list
= NewParseNode(PN_LIST
, &funtc
);
2565 list
->pn_type
= TOK_COMMA
;
2571 #endif /* JS_HAS_DESTRUCTURING */
2576 * Check for a duplicate parameter name, a "feature" that
2577 * ECMA-262 requires. This is a SpiderMonkey strict warning,
2578 * soon to be an ES3.1 strict error.
2580 * Further, if any argument is a destructuring pattern, forbid
2581 * duplicates. We will report the error either now if we have
2582 * seen a destructuring pattern already, or later when we find
2583 * the first pattern.
2585 JSAtom
*atom
= CURRENT_TOKEN(ts
).t_atom
;
2586 if (JS_HAS_STRICT_OPTION(cx
) &&
2587 js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2588 #if JS_HAS_DESTRUCTURING
2589 if (destructuringArg
)
2590 goto report_dup_and_destructuring
;
2591 duplicatedArg
= true;
2593 const char *name
= js_AtomToPrintableString(cx
, atom
);
2595 !js_ReportCompileErrorNumber(cx
, TS(funtc
.compiler
),
2599 JSMSG_DUPLICATE_FORMAL
,
2604 if (!DefineArg(pn
, atom
, fun
->nargs
, &funtc
))
2606 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2612 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2613 JSMSG_MISSING_FORMAL
);
2618 #if JS_HAS_DESTRUCTURING
2619 report_dup_and_destructuring
:
2620 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
,
2622 JSMSG_DESTRUCT_DUP_ARG
);
2626 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2628 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FORMAL
);
2631 #if JS_HAS_EXPR_CLOSURES
2632 ts
->flags
|= TSF_OPERAND
;
2633 tt
= js_GetToken(cx
, ts
);
2634 ts
->flags
&= ~TSF_OPERAND
;
2637 fun
->flags
|= JSFUN_EXPR_CLOSURE
;
2640 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_BODY
);
2643 body
= FunctionBody(cx
, ts
, &funtc
);
2647 #if JS_HAS_EXPR_CLOSURES
2649 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2650 else if (lambda
== 0 && !MatchOrInsertSemicolon(cx
, ts
))
2653 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2655 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2657 #if JS_HAS_DESTRUCTURING
2659 * If there were destructuring formal parameters, prepend the initializing
2660 * comma expression that we synthesized to body. If the body is a lexical
2661 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2662 * parameter destructuring code without bracing the decompilation of the
2663 * function body's lexical scope.
2666 if (body
->pn_arity
!= PN_LIST
) {
2669 block
= NewParseNode(PN_LIST
, tc
);
2672 block
->pn_type
= TOK_SEQ
;
2673 block
->pn_pos
= body
->pn_pos
;
2674 block
->initList(body
);
2679 item
= NewParseNode(PN_UNARY
, tc
);
2683 item
->pn_type
= TOK_SEMI
;
2684 item
->pn_pos
.begin
= item
->pn_pos
.end
= body
->pn_pos
.begin
;
2685 item
->pn_kid
= list
;
2686 item
->pn_next
= body
->pn_head
;
2687 body
->pn_head
= item
;
2688 if (body
->pn_tail
== &body
->pn_head
)
2689 body
->pn_tail
= &item
->pn_next
;
2691 body
->pn_xflags
|= PNX_DESTRUCT
;
2696 * If we collected flags that indicate nested heavyweight functions, or
2697 * this function contains heavyweight-making statements (with statement,
2698 * visible eval call, or assignment to 'arguments'), flag the function as
2699 * heavyweight (requiring a call object per invocation).
2701 if (funtc
.flags
& TCF_FUN_HEAVYWEIGHT
) {
2702 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
2703 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2706 * If this function is a named statement function not at top-level
2707 * (i.e. not a top-level function definiton or expression), then our
2708 * enclosing function, if any, must be heavyweight.
2710 if (!topLevel
&& lambda
== 0 && funAtom
)
2711 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2717 * ECMA ed. 3 standard: function expression, possibly anonymous.
2720 } else if (!funAtom
) {
2722 * If this anonymous function definition is *not* embedded within a
2723 * larger expression, we treat it as an expression statement, not as
2724 * a function declaration -- and not as a syntax error (as ECMA-262
2725 * Edition 3 would have it). Backward compatibility must trump all,
2726 * unless JSOPTION_ANONFUNFIX is set.
2728 result
= NewParseNode(PN_UNARY
, tc
);
2731 result
->pn_type
= TOK_SEMI
;
2732 result
->pn_pos
= pn
->pn_pos
;
2733 result
->pn_kid
= pn
;
2735 } else if (!topLevel
) {
2737 * ECMA ed. 3 extension: a function expression statement not at the
2738 * top level, e.g., in a compound statement such as the "then" part
2739 * of an "if" statement, binds a closure only if control reaches that
2747 funbox
->kids
= funtc
.functionList
;
2749 pn
->pn_funbox
= funbox
;
2752 pn
->pn_body
->append(body
);
2756 pn
->pn_blockid
= tc
->blockid();
2758 if (!LeaveFunction(pn
, &funtc
, tc
, funAtom
, lambda
))
2764 static JSParseNode
*
2765 FunctionStmt(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2767 return FunctionDef(cx
, ts
, tc
, 0);
2770 static JSParseNode
*
2771 FunctionExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2773 return FunctionDef(cx
, ts
, tc
, JSFUN_LAMBDA
);
2777 * Parse the statements in a block, creating a TOK_LC node that lists the
2778 * statements' trees. If called from block-parsing code, the caller must
2779 * match { before and } after.
2781 static JSParseNode
*
2782 Statements(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2784 JSParseNode
*pn
, *pn2
, *saveBlock
;
2787 JS_CHECK_RECURSION(cx
, return NULL
);
2789 pn
= NewParseNode(PN_LIST
, tc
);
2792 pn
->pn_type
= TOK_LC
;
2794 pn
->pn_blockid
= tc
->blockid();
2795 saveBlock
= tc
->blockNode
;
2799 ts
->flags
|= TSF_OPERAND
;
2800 tt
= js_PeekToken(cx
, ts
);
2801 ts
->flags
&= ~TSF_OPERAND
;
2802 if (tt
<= TOK_EOF
|| tt
== TOK_RC
) {
2803 if (tt
== TOK_ERROR
) {
2804 if (ts
->flags
& TSF_EOF
)
2805 ts
->flags
|= TSF_UNEXPECTED_EOF
;
2810 pn2
= Statement(cx
, ts
, tc
);
2812 if (ts
->flags
& TSF_EOF
)
2813 ts
->flags
|= TSF_UNEXPECTED_EOF
;
2817 if (pn2
->pn_type
== TOK_FUNCTION
) {
2819 * PNX_FUNCDEFS notifies the emitter that the block contains top-
2820 * level function definitions that should be processed before the
2823 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
2824 * is relevant only for function definitions not at top-level,
2825 * which we call function statements.
2827 if (tc
->atTopLevel())
2828 pn
->pn_xflags
|= PNX_FUNCDEFS
;
2830 tc
->flags
|= TCF_HAS_FUNCTION_STMT
;
2836 * Handle the case where there was a let declaration under this block. If
2837 * it replaced tc->blockNode with a new block node then we must refresh pn
2838 * and then restore tc->blockNode.
2840 if (tc
->blockNode
!= pn
)
2842 tc
->blockNode
= saveBlock
;
2844 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2848 static JSParseNode
*
2849 Condition(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2853 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
2854 pn
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
2857 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
2860 * Check for (a = b) and warn about possible (a == b) mistype iff b's
2861 * operator has greater precedence than ==.
2863 if (pn
->pn_type
== TOK_ASSIGN
&&
2864 pn
->pn_op
== JSOP_NOP
&&
2865 pn
->pn_right
->pn_type
> TOK_EQOP
)
2867 if (!js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2868 JSREPORT_WARNING
| JSREPORT_STRICT
,
2869 JSMSG_EQUAL_AS_ASSIGN
,
2878 MatchLabel(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
)
2883 tt
= js_PeekTokenSameLine(cx
, ts
);
2884 if (tt
== TOK_ERROR
)
2886 if (tt
== TOK_NAME
) {
2887 (void) js_GetToken(cx
, ts
);
2888 label
= CURRENT_TOKEN(ts
).t_atom
;
2892 pn
->pn_atom
= label
;
2897 BindLet(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
2901 JSAtomListElement
*ale
;
2905 * Top-level 'let' is the same as 'var' currently -- this may change in a
2906 * successor standard to ES3.1 that specifies 'let'.
2908 JS_ASSERT(!tc
->atTopLevel());
2911 blockObj
= tc
->blockChain
;
2912 ale
= tc
->decls
.lookup(atom
);
2913 if (ale
&& ALE_DEFN(ale
)->pn_blockid
== tc
->blockid()) {
2914 const char *name
= js_AtomToPrintableString(cx
, atom
);
2916 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
2917 JSREPORT_ERROR
, JSMSG_REDECLARED_VAR
,
2918 (ale
&& ALE_DEFN(ale
)->isConst())
2926 n
= OBJ_BLOCK_COUNT(cx
, blockObj
);
2927 if (n
== JS_BIT(16)) {
2928 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
2929 JSREPORT_ERROR
, data
->let
.overflow
);
2934 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
2935 * This is balanced by PopStatement, defined immediately below.
2937 if (!Define(pn
, atom
, tc
, true))
2941 * Assign block-local index to pn->pn_cookie right away, encoding it as an
2942 * upvar cookie whose skip tells the current static level. The emitter will
2943 * adjust the node's slot based on its stack depth model -- and, for global
2944 * and eval code, JSCompiler::compileScript will adjust the slot again to
2945 * include script->nfixed.
2947 pn
->pn_op
= JSOP_GETLOCAL
;
2948 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, n
);
2949 pn
->pn_dflags
|= PND_LET
| PND_BOUND
;
2952 * Use JSPROP_ENUMERATE to aid the disassembler. Define the let binding's
2953 * property before storing pn in a reserved slot, since block_reserveSlots
2954 * depends on OBJ_SCOPE(blockObj)->entryCount.
2956 if (!js_DefineNativeProperty(cx
, blockObj
, ATOM_TO_JSID(atom
), JSVAL_VOID
,
2961 SPROP_HAS_SHORTID
, (int16
) n
, NULL
)) {
2966 * Store pn temporarily in what would be reserved slots in a cloned block
2967 * object (once the prototype's final population is known, after all 'let'
2968 * bindings for this block have been parsed). We will free these reserved
2969 * slots in jsemit.cpp:EmitEnterBlock.
2971 uintN slot
= JSSLOT_FREE(&js_BlockClass
) + n
;
2972 if (slot
>= STOBJ_NSLOTS(blockObj
) &&
2973 !js_ReallocSlots(cx
, blockObj
, slot
+ 1, JS_FALSE
)) {
2976 blockObj
->map
->freeslot
= slot
+ 1;
2977 STOBJ_SET_SLOT(blockObj
, slot
, PRIVATE_TO_JSVAL(pn
));
2982 PopStatement(JSTreeContext
*tc
)
2984 JSStmtInfo
*stmt
= tc
->topStmt
;
2986 if (stmt
->flags
& SIF_SCOPE
) {
2987 JSObject
*obj
= stmt
->blockObj
;
2988 JSScope
*scope
= OBJ_SCOPE(obj
);
2989 JS_ASSERT(scope
->object
== obj
);
2991 for (JSScopeProperty
*sprop
= scope
->lastProp
; sprop
; sprop
= sprop
->parent
) {
2992 JSAtom
*atom
= JSID_TO_ATOM(sprop
->id
);
2994 /* Beware the empty destructuring dummy. */
2995 if (atom
== tc
->compiler
->context
->runtime
->atomState
.emptyAtom
)
2997 tc
->decls
.remove(tc
->compiler
, atom
);
3000 js_PopStatement(tc
);
3004 OuterLet(JSTreeContext
*tc
, JSStmtInfo
*stmt
, JSAtom
*atom
)
3006 while (stmt
->downScope
) {
3007 stmt
= js_LexicalLookup(tc
, atom
, NULL
, stmt
->downScope
);
3010 if (stmt
->type
== STMT_BLOCK
)
3017 BindVarOrConst(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3019 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
3020 JSParseNode
*pn
= data
->pn
;
3022 if (stmt
&& stmt
->type
== STMT_WITH
) {
3023 pn
->pn_op
= JSOP_NAME
;
3027 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
3031 JSDefinition
*dn
= ale
? ALE_DEFN(ale
) : NULL
;
3032 JSDefinition::Kind dn_kind
= dn
? dn
->kind() : JSDefinition::VAR
;
3035 if (dn_kind
== JSDefinition::ARG
) {
3036 name
= js_AtomToPrintableString(cx
, atom
);
3040 if (op
== JSOP_DEFCONST
) {
3041 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3042 JSREPORT_ERROR
, JSMSG_REDECLARED_PARAM
,
3046 if (!js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3047 JSREPORT_WARNING
| JSREPORT_STRICT
,
3048 JSMSG_VAR_HIDES_ARG
, name
)) {
3052 if (JS_HAS_STRICT_OPTION(cx
)
3053 ? op
!= JSOP_DEFVAR
|| dn_kind
!= JSDefinition::VAR
3054 : op
== JSOP_DEFCONST
||
3055 dn_kind
== JSDefinition::CONST
||
3056 (dn_kind
== JSDefinition::LET
&&
3057 (stmt
->type
!= STMT_CATCH
|| OuterLet(tc
, stmt
, atom
)))) {
3058 name
= js_AtomToPrintableString(cx
, atom
);
3060 !js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3061 (op
!= JSOP_DEFCONST
&&
3062 dn_kind
!= JSDefinition::CONST
&&
3063 dn_kind
!= JSDefinition::LET
)
3064 ? JSREPORT_WARNING
| JSREPORT_STRICT
3066 JSMSG_REDECLARED_VAR
,
3067 JSDefinition::kindString(dn_kind
),
3076 if (!Define(pn
, atom
, tc
))
3080 * A var declaration never recreates an existing binding, it restates
3081 * it and possibly reinitializes its value. Beware that if pn becomes a
3082 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3083 * const (typically a const would ;-), then pn must be rewritten into a
3084 * TOK_ASSIGN node. See Variables, further below.
3086 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3087 * There the x definition is hoisted but the x = 2 assignment mutates
3088 * the block-local binding of x.
3090 JSDefinition
*dn
= ALE_DEFN(ale
);
3093 /* Make pnu be a fresh name node that uses dn. */
3094 JSParseNode
*pnu
= pn
;
3097 pnu
= NewNameNode(cx
, TS(tc
->compiler
), atom
, tc
);
3102 LinkUseToDef(pnu
, dn
, tc
);
3103 pnu
->pn_op
= JSOP_NAME
;
3106 while (dn
->kind() == JSDefinition::LET
) {
3108 ale
= ALE_NEXT(ale
);
3109 } while (ale
&& ALE_ATOM(ale
) != atom
);
3116 JS_ASSERT_IF(data
->op
== JSOP_DEFCONST
,
3117 dn
->kind() == JSDefinition::CONST
);
3122 * A var or const that is shadowed by one or more let bindings of the
3123 * same name, but that has not been declared until this point, must be
3124 * hoisted above the let bindings.
3129 ale
= tc
->lexdeps
.rawLookup(atom
, hep
);
3132 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
3134 JSParseNode
*pn2
= NewNameNode(cx
, TS(tc
->compiler
), atom
, tc
);
3138 /* The token stream may be past the location for pn. */
3139 pn2
->pn_type
= TOK_NAME
;
3140 pn2
->pn_pos
= pn
->pn_pos
;
3143 pn
->pn_op
= JSOP_NAME
;
3146 ale
= tc
->decls
.add(tc
->compiler
, atom
, JSAtomList::HOIST
);
3149 ALE_SET_DEFN(ale
, pn
);
3151 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
3154 if (data
->op
== JSOP_DEFCONST
)
3155 pn
->pn_dflags
|= PND_CONST
;
3157 if (!(tc
->flags
& TCF_IN_FUNCTION
)) {
3159 * If we are generating global or eval-called-from-global code, bind a
3160 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3161 * up global variable access by memoizing name-to-slot mappings in the
3162 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3163 * can't be done due to a pre-existing property of the same name as the
3164 * var or const but incompatible attributes/getter/setter/etc, these
3165 * ops devolve to JSOP_NAME, etc.
3167 * For now, don't try to lookup eval frame variables at compile time.
3168 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3169 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3171 pn
->pn_op
= JSOP_NAME
;
3172 if ((tc
->flags
& TCF_COMPILING
) && !tc
->compiler
->callerFrame
) {
3173 JSCodeGenerator
*cg
= (JSCodeGenerator
*) tc
;
3175 /* Index atom so we can map fast global number to name. */
3176 ale
= cg
->atomList
.add(tc
->compiler
, atom
);
3180 /* Defend against cg->ngvars 16-bit overflow. */
3181 uintN slot
= ALE_INDEX(ale
);
3182 if ((slot
+ 1) >> 16)
3185 if ((uint16
)(slot
+ 1) > cg
->ngvars
)
3186 cg
->ngvars
= (uint16
)(slot
+ 1);
3188 pn
->pn_op
= JSOP_GETGVAR
;
3189 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, slot
);
3190 pn
->pn_dflags
|= PND_BOUND
| PND_GVAR
;
3195 if (atom
== cx
->runtime
->atomState
.argumentsAtom
) {
3196 pn
->pn_op
= JSOP_ARGUMENTS
;
3197 pn
->pn_dflags
|= PND_BOUND
;
3201 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
3202 if (localKind
== JSLOCAL_NONE
) {
3204 * Property not found in current variable scope: we have not seen this
3205 * variable before. Define a new local variable by adding a property to
3206 * the function's scope and allocating one slot in the function's vars
3207 * frame. Any locals declared in a with statement body are handled at
3208 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3209 * and heavyweight-function-local vars.
3211 localKind
= (data
->op
== JSOP_DEFCONST
) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
3213 uintN index
= tc
->fun
->u
.i
.nvars
;
3214 if (!BindLocalVariable(cx
, tc
->fun
, atom
, localKind
))
3216 pn
->pn_op
= JSOP_GETLOCAL
;
3217 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
3218 pn
->pn_dflags
|= PND_BOUND
;
3222 if (localKind
== JSLOCAL_ARG
) {
3223 /* We checked errors and strict warnings earlier -- see above. */
3224 JS_ASSERT(ale
&& ALE_DEFN(ale
)->kind() == JSDefinition::ARG
);
3226 /* Not an argument, must be a redeclared local var. */
3227 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
3229 pn
->pn_op
= JSOP_NAME
;
3234 MakeSetCall(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN msg
)
3238 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
3239 JS_ASSERT(pn
->pn_op
== JSOP_CALL
|| pn
->pn_op
== JSOP_EVAL
|| pn
->pn_op
== JSOP_APPLY
);
3241 if (pn2
->pn_type
== TOK_FUNCTION
&& (pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
)) {
3242 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
, msg
);
3245 pn
->pn_op
= JSOP_SETCALL
;
3250 NoteLValue(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN dflag
= PND_ASSIGNED
)
3253 JSDefinition
*dn
= pn
->pn_lexdef
;
3256 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3257 * occur as direct kids of the same block with no forward refs to x.
3259 if (dn
->isBlockChild() &&
3260 pn
->isBlockChild() &&
3261 dn
->pn_blockid
== pn
->pn_blockid
&&
3262 !(~dn
->pn_dflags
& (PND_INITIALIZED
| PND_FORWARD
)) &&
3263 dn
->dn_uses
== pn
) {
3264 dflag
= PND_INITIALIZED
;
3267 dn
->pn_dflags
|= dflag
;
3269 if (dn
->frameLevel() != tc
->staticLevel
) {
3271 * The above condition takes advantage of the all-ones nature of
3272 * FREE_UPVAR_COOKIE, and the reserved frame level JS_BITMASK(16).
3273 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3275 JS_ASSERT_IF(dn
->pn_cookie
!= FREE_UPVAR_COOKIE
,
3276 dn
->frameLevel() < tc
->staticLevel
);
3277 tc
->flags
|= TCF_FUN_SETS_OUTER_NAME
;
3281 pn
->pn_dflags
|= dflag
;
3283 if (pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
3284 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3287 #if JS_HAS_DESTRUCTURING
3290 BindDestructuringVar(JSContext
*cx
, BindData
*data
, JSParseNode
*pn
,
3296 * Destructuring is a form of assignment, so just as for an initialized
3297 * simple variable, we must check for assignment to 'arguments' and flag
3298 * the enclosing function (if any) as heavyweight.
3300 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
3302 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
3303 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3306 if (!data
->binder(cx
, data
, atom
, tc
))
3310 * Select the appropriate name-setting opcode, respecting eager selection
3311 * done by the data->binder function.
3313 if (pn
->pn_dflags
& PND_BOUND
) {
3314 pn
->pn_op
= (pn
->pn_op
== JSOP_ARGUMENTS
)
3316 : (pn
->pn_dflags
& PND_GVAR
)
3320 pn
->pn_op
= (data
->op
== JSOP_DEFCONST
)
3325 if (data
->op
== JSOP_DEFCONST
)
3326 pn
->pn_dflags
|= PND_CONST
;
3328 NoteLValue(cx
, pn
, tc
, PND_INITIALIZED
);
3333 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3334 * LHS expression except a destructuring initialiser, and R is on the stack.
3335 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3336 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3337 * then push its property name QN. At this point the stack looks like
3339 * [... R, R[P], QB, QN]
3341 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3342 * its operands with left-hand side above right-hand side:
3344 * [rval, lval, xval]
3346 * and pops all three values, setting lval[xval] = rval. But we cannot select
3347 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3348 * which can be optimized further. So we select JSOP_SETNAME.
3351 BindDestructuringLHS(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
3353 while (pn
->pn_type
== TOK_RP
)
3356 switch (pn
->pn_type
) {
3358 NoteLValue(cx
, pn
, tc
);
3363 pn
->pn_op
= JSOP_SETNAME
;
3366 #if JS_HAS_LVALUE_RETURN
3368 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
3373 #if JS_HAS_XML_SUPPORT
3375 if (pn
->pn_op
== JSOP_XMLNAME
) {
3376 pn
->pn_op
= JSOP_BINDXMLNAME
;
3383 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3384 JSREPORT_ERROR
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
3391 typedef struct FindPropValData
{
3392 uint32 numvars
; /* # of destructuring vars in left side */
3393 uint32 maxstep
; /* max # of steps searching right side */
3394 JSDHashTable table
; /* hash table for O(1) right side search */
3397 typedef struct FindPropValEntry
{
3398 JSDHashEntryHdr hdr
;
3403 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3404 JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
3405 ((pnkey)->pn_type == TOK_NUMBER || \
3406 (pnkey)->pn_type == TOK_STRING || \
3407 (pnkey)->pn_type == TOK_NAME))
3409 static JSDHashNumber
3410 HashFindPropValKey(JSDHashTable
*table
, const void *key
)
3412 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3414 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3415 return (pnkey
->pn_type
== TOK_NUMBER
)
3416 ? (JSDHashNumber
) (JSDOUBLE_HI32(pnkey
->pn_dval
) ^
3417 JSDOUBLE_LO32(pnkey
->pn_dval
))
3418 : ATOM_HASH(pnkey
->pn_atom
);
3422 MatchFindPropValEntry(JSDHashTable
*table
,
3423 const JSDHashEntryHdr
*entry
,
3426 const FindPropValEntry
*fpve
= (const FindPropValEntry
*)entry
;
3427 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3429 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3430 return pnkey
->pn_type
== fpve
->pnkey
->pn_type
&&
3431 ((pnkey
->pn_type
== TOK_NUMBER
)
3432 ? pnkey
->pn_dval
== fpve
->pnkey
->pn_dval
3433 : pnkey
->pn_atom
== fpve
->pnkey
->pn_atom
);
3436 static const JSDHashTableOps FindPropValOps
= {
3440 MatchFindPropValEntry
,
3441 JS_DHashMoveEntryStub
,
3442 JS_DHashClearEntryStub
,
3443 JS_DHashFinalizeStub
,
3447 #define STEP_HASH_THRESHOLD 10
3448 #define BIG_DESTRUCTURING 5
3449 #define BIG_OBJECT_INIT 20
3451 static JSParseNode
*
3452 FindPropertyValue(JSParseNode
*pn
, JSParseNode
*pnid
, FindPropValData
*data
)
3454 FindPropValEntry
*entry
;
3455 JSParseNode
*pnhit
, *pnhead
, *pnprop
, *pnkey
;
3458 /* If we have a hash table, use it as the sole source of truth. */
3459 if (data
->table
.ops
) {
3460 entry
= (FindPropValEntry
*)
3461 JS_DHashTableOperate(&data
->table
, pnid
, JS_DHASH_LOOKUP
);
3462 return JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
) ? entry
->pnval
: NULL
;
3465 /* If pn is not an object initialiser node, we can't do anything here. */
3466 if (pn
->pn_type
!= TOK_RC
)
3470 * We must search all the way through pn's list, to handle the case of an
3471 * id duplicated for two or more property initialisers.
3475 ASSERT_VALID_PROPERTY_KEY(pnid
);
3476 pnhead
= pn
->pn_head
;
3477 if (pnid
->pn_type
== TOK_NUMBER
) {
3478 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3479 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3480 if (pnprop
->pn_op
== JSOP_NOP
) {
3481 pnkey
= pnprop
->pn_left
;
3482 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3483 if (pnkey
->pn_type
== TOK_NUMBER
&&
3484 pnkey
->pn_dval
== pnid
->pn_dval
) {
3491 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3492 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3493 if (pnprop
->pn_op
== JSOP_NOP
) {
3494 pnkey
= pnprop
->pn_left
;
3495 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3496 if (pnkey
->pn_type
== pnid
->pn_type
&&
3497 pnkey
->pn_atom
== pnid
->pn_atom
) {
3507 /* Hit via full search -- see whether it's time to create the hash table. */
3508 JS_ASSERT(!data
->table
.ops
);
3509 if (step
> data
->maxstep
) {
3510 data
->maxstep
= step
;
3511 if (step
>= STEP_HASH_THRESHOLD
&&
3512 data
->numvars
>= BIG_DESTRUCTURING
&&
3513 pn
->pn_count
>= BIG_OBJECT_INIT
&&
3514 JS_DHashTableInit(&data
->table
, &FindPropValOps
, pn
,
3515 sizeof(FindPropValEntry
),
3516 JS_DHASH_DEFAULT_CAPACITY(pn
->pn_count
)))
3518 for (pn
= pnhead
; pn
; pn
= pn
->pn_next
) {
3519 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3520 ASSERT_VALID_PROPERTY_KEY(pn
->pn_left
);
3521 entry
= (FindPropValEntry
*)
3522 JS_DHashTableOperate(&data
->table
, pn
->pn_left
,
3524 entry
->pnval
= pn
->pn_right
;
3528 return pnhit
->pn_right
;
3532 * If data is null, the caller is AssignExpr and instead of binding variables,
3533 * we specialize lvalues in the propery value positions of the left-hand side.
3534 * If right is null, just check for well-formed lvalues.
3537 CheckDestructuring(JSContext
*cx
, BindData
*data
,
3538 JSParseNode
*left
, JSParseNode
*right
,
3542 FindPropValData fpvd
;
3543 JSParseNode
*lhs
, *rhs
, *pn
, *pn2
;
3545 if (left
->pn_type
== TOK_ARRAYCOMP
) {
3546 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), left
,
3547 JSREPORT_ERROR
, JSMSG_ARRAY_COMP_LEFTSIDE
);
3551 #if JS_HAS_DESTRUCTURING_SHORTHAND
3552 if (right
&& right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3553 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), right
,
3554 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
3559 fpvd
.table
.ops
= NULL
;
3560 lhs
= left
->pn_head
;
3561 if (left
->pn_type
== TOK_RB
) {
3562 rhs
= (right
&& right
->pn_type
== left
->pn_type
)
3567 pn
= lhs
, pn2
= rhs
;
3569 /* Skip parenthesization if not in a variable declaration. */
3570 while (pn
->pn_type
== TOK_RP
)
3573 while (pn2
->pn_type
== TOK_RP
)
3578 /* Nullary comma is an elision; binary comma is an expression.*/
3579 if (pn
->pn_type
!= TOK_COMMA
|| pn
->pn_arity
!= PN_NULLARY
) {
3580 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3581 ok
= CheckDestructuring(cx
, data
, pn
, pn2
, tc
);
3584 if (pn
->pn_type
!= TOK_NAME
)
3587 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3589 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3601 JS_ASSERT(left
->pn_type
== TOK_RC
);
3602 fpvd
.numvars
= left
->pn_count
;
3607 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
3610 /* Skip parenthesization if not in a variable declaration. */
3611 while (pn
->pn_type
== TOK_RP
)
3615 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3617 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
3619 while (rhs
->pn_type
== TOK_RP
)
3624 ok
= CheckDestructuring(cx
, data
, pn
, rhs
, tc
);
3626 if (pn
->pn_type
!= TOK_NAME
)
3629 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3631 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3641 * The catch/finally handler implementation in the interpreter assumes
3642 * that any operation that introduces a new scope (like a "let" or "with"
3643 * block) increases the stack depth. This way, it is possible to restore
3644 * the scope chain based on stack depth of the handler alone. "let" with
3645 * an empty destructuring pattern like in
3649 * would violate this assumption as the there would be no let locals to
3650 * store on the stack. To satisfy it we add an empty property to such
3651 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3652 * slots, would be always positive.
3654 * Note that we add such a property even if the block has locals due to
3655 * later let declarations in it. We optimize for code simplicity here,
3656 * not the fastest runtime performance with empty [] or {}.
3659 data
->binder
== BindLet
&&
3660 OBJ_BLOCK_COUNT(cx
, tc
->blockChain
) == 0) {
3661 ok
= !!js_DefineNativeProperty(cx
, tc
->blockChain
,
3662 ATOM_TO_JSID(cx
->runtime
->
3663 atomState
.emptyAtom
),
3664 JSVAL_VOID
, NULL
, NULL
,
3668 SPROP_HAS_SHORTID
, 0, NULL
);
3677 JS_DHashTableFinish(&fpvd
.table
);
3681 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
,
3682 JSMSG_NO_VARIABLE_NAME
);
3687 static JSParseNode
*
3688 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
3694 ts
= TS(tc
->compiler
);
3695 ts
->flags
|= TSF_DESTRUCTURING
;
3696 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
3697 ts
->flags
&= ~TSF_DESTRUCTURING
;
3700 if (!CheckDestructuring(cx
, data
, pn
, NULL
, tc
))
3706 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
3707 * This function assumes the cloned tree is for use in the same statement and
3708 * binding context as the original tree.
3710 static JSParseNode
*
3711 CloneParseTree(JSParseNode
*opn
, JSTreeContext
*tc
)
3713 JSParseNode
*pn
, *pn2
, *opn2
;
3715 pn
= NewOrRecycledNode(tc
);
3718 pn
->pn_type
= opn
->pn_type
;
3719 pn
->pn_pos
= opn
->pn_pos
;
3720 pn
->pn_op
= opn
->pn_op
;
3721 pn
->pn_used
= opn
->pn_used
;
3722 pn
->pn_defn
= opn
->pn_defn
;
3723 pn
->pn_arity
= opn
->pn_arity
;
3725 switch (pn
->pn_arity
) {
3726 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
3729 NULLCHECK(pn
->pn_funbox
=
3730 tc
->compiler
->newFunctionBox(opn
->pn_funbox
->object
, pn
, tc
));
3731 NULLCHECK(pn
->pn_body
= CloneParseTree(opn
->pn_body
, tc
));
3732 pn
->pn_cookie
= opn
->pn_cookie
;
3733 pn
->pn_dflags
= opn
->pn_dflags
;
3734 pn
->pn_blockid
= opn
->pn_blockid
;
3739 for (opn2
= opn
->pn_head
; opn2
; opn2
= opn2
->pn_next
) {
3740 NULLCHECK(pn2
= CloneParseTree(opn2
, tc
));
3743 pn
->pn_xflags
= opn
->pn_xflags
;
3747 NULLCHECK(pn
->pn_kid1
= CloneParseTree(opn
->pn_kid1
, tc
));
3748 NULLCHECK(pn
->pn_kid2
= CloneParseTree(opn
->pn_kid2
, tc
));
3749 NULLCHECK(pn
->pn_kid3
= CloneParseTree(opn
->pn_kid3
, tc
));
3753 NULLCHECK(pn
->pn_left
= CloneParseTree(opn
->pn_left
, tc
));
3754 if (opn
->pn_right
!= opn
->pn_left
)
3755 NULLCHECK(pn
->pn_right
= CloneParseTree(opn
->pn_right
, tc
));
3757 pn
->pn_right
= pn
->pn_left
;
3758 pn
->pn_val
= opn
->pn_val
;
3759 pn
->pn_iflags
= opn
->pn_iflags
;
3763 NULLCHECK(pn
->pn_kid
= CloneParseTree(opn
->pn_kid
, tc
));
3764 pn
->pn_num
= opn
->pn_num
;
3765 pn
->pn_hidden
= opn
->pn_hidden
;
3769 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
3770 pn
->pn_u
= opn
->pn_u
;
3773 * The old name is a use of its pn_lexdef. Make the clone also be a
3774 * use of that definition.
3776 JSDefinition
*dn
= pn
->pn_lexdef
;
3778 pn
->pn_link
= dn
->dn_uses
;
3780 } else if (opn
->pn_expr
) {
3781 NULLCHECK(pn
->pn_expr
= CloneParseTree(opn
->pn_expr
, tc
));
3784 * If the old name is a definition, the new one has pn_defn set.
3785 * Make the old name a use of the new node.
3788 opn
->pn_defn
= false;
3789 LinkUseToDef(opn
, (JSDefinition
*) pn
, tc
);
3795 pn
->pn_names
= opn
->pn_names
;
3796 NULLCHECK(pn
->pn_tree
= CloneParseTree(opn
->pn_tree
, tc
));
3800 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
3801 pn
->pn_u
= opn
->pn_u
;
3809 #endif /* JS_HAS_DESTRUCTURING */
3811 extern const char js_with_statement_str
[];
3813 static JSParseNode
*
3814 ContainsStmt(JSParseNode
*pn
, JSTokenType tt
)
3816 JSParseNode
*pn2
, *pnt
;
3820 if (PN_TYPE(pn
) == tt
)
3822 switch (pn
->pn_arity
) {
3824 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
3825 pnt
= ContainsStmt(pn2
, tt
);
3831 pnt
= ContainsStmt(pn
->pn_kid1
, tt
);
3834 pnt
= ContainsStmt(pn
->pn_kid2
, tt
);
3837 return ContainsStmt(pn
->pn_kid3
, tt
);
3840 * Limit recursion if pn is a binary expression, which can't contain a
3843 if (pn
->pn_op
!= JSOP_NOP
)
3845 pnt
= ContainsStmt(pn
->pn_left
, tt
);
3848 return ContainsStmt(pn
->pn_right
, tt
);
3850 if (pn
->pn_op
!= JSOP_NOP
)
3852 return ContainsStmt(pn
->pn_kid
, tt
);
3854 return ContainsStmt(pn
->maybeExpr(), tt
);
3856 return ContainsStmt(pn
->pn_tree
, tt
);
3862 static JSParseNode
*
3863 ReturnOrYield(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
3864 JSParser operandParser
)
3866 JSTokenType tt
, tt2
;
3867 JSParseNode
*pn
, *pn2
;
3869 tt
= CURRENT_TOKEN(ts
).type
;
3870 if (tt
== TOK_RETURN
&& !(tc
->flags
& TCF_IN_FUNCTION
)) {
3871 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3872 JSMSG_BAD_RETURN_OR_YIELD
, js_return_str
);
3876 pn
= NewParseNode(PN_UNARY
, tc
);
3880 #if JS_HAS_GENERATORS
3881 if (tt
== TOK_YIELD
)
3882 tc
->flags
|= TCF_FUN_IS_GENERATOR
;
3885 /* This is ugly, but we don't want to require a semicolon. */
3886 ts
->flags
|= TSF_OPERAND
;
3887 tt2
= js_PeekTokenSameLine(cx
, ts
);
3888 ts
->flags
&= ~TSF_OPERAND
;
3889 if (tt2
== TOK_ERROR
)
3892 if (tt2
!= TOK_EOF
&& tt2
!= TOK_EOL
&& tt2
!= TOK_SEMI
&& tt2
!= TOK_RC
3893 #if JS_HAS_GENERATORS
3894 && (tt
!= TOK_YIELD
||
3895 (tt2
!= tt
&& tt2
!= TOK_RB
&& tt2
!= TOK_RP
&&
3896 tt2
!= TOK_COLON
&& tt2
!= TOK_COMMA
))
3899 pn2
= operandParser(cx
, ts
, tc
);
3902 #if JS_HAS_GENERATORS
3903 if (tt
== TOK_RETURN
)
3905 tc
->flags
|= TCF_RETURN_EXPR
;
3906 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
3909 #if JS_HAS_GENERATORS
3910 if (tt
== TOK_RETURN
)
3912 tc
->flags
|= TCF_RETURN_VOID
;
3915 if ((~tc
->flags
& (TCF_RETURN_EXPR
| TCF_FUN_IS_GENERATOR
)) == 0) {
3916 /* As in Python (see PEP-255), disallow return v; in generators. */
3917 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
3918 JSMSG_BAD_GENERATOR_RETURN
,
3919 JSMSG_BAD_ANON_GENERATOR_RETURN
);
3923 if (JS_HAS_STRICT_OPTION(cx
) &&
3924 (~tc
->flags
& (TCF_RETURN_EXPR
| TCF_RETURN_VOID
)) == 0 &&
3925 !ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
3926 JSMSG_NO_RETURN_VALUE
,
3927 JSMSG_ANON_NO_RETURN_VALUE
)) {
3934 static JSParseNode
*
3935 PushLexicalScope(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
3940 JSObjectBox
*blockbox
;
3942 pn
= NewParseNode(PN_NAME
, tc
);
3946 obj
= js_NewBlockObject(cx
);
3950 blockbox
= tc
->compiler
->newObjectBox(obj
);
3954 js_PushBlockScope(tc
, stmt
, obj
, -1);
3955 pn
->pn_type
= TOK_LEXICALSCOPE
;
3956 pn
->pn_op
= JSOP_LEAVEBLOCK
;
3957 pn
->pn_objbox
= blockbox
;
3958 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
3960 if (!GenerateBlockId(tc
, stmt
->blockid
))
3962 pn
->pn_blockid
= stmt
->blockid
;
3966 #if JS_HAS_BLOCK_SCOPE
3968 static JSParseNode
*
3969 LetBlock(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, JSBool statement
)
3971 JSParseNode
*pn
, *pnblock
, *pnlet
;
3972 JSStmtInfo stmtInfo
;
3974 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LET
);
3976 /* Create the let binary node. */
3977 pnlet
= NewParseNode(PN_BINARY
, tc
);
3981 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_LET
);
3983 /* This is a let block or expression of the form: let (a, b, c) .... */
3984 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
3988 pn
->pn_expr
= pnlet
;
3990 pnlet
->pn_left
= Variables(cx
, ts
, tc
, true);
3991 if (!pnlet
->pn_left
)
3993 pnlet
->pn_left
->pn_xflags
= PNX_POPVAR
;
3995 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_LET
);
3997 ts
->flags
|= TSF_OPERAND
;
3998 if (statement
&& !js_MatchToken(cx
, ts
, TOK_LC
)) {
4000 * If this is really an expression in let statement guise, then we
4001 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4002 * the return value of the expression.
4004 pn
= NewParseNode(PN_UNARY
, tc
);
4007 pn
->pn_type
= TOK_SEMI
;
4009 pn
->pn_kid
= pnblock
;
4011 statement
= JS_FALSE
;
4013 ts
->flags
&= ~TSF_OPERAND
;
4016 pnlet
->pn_right
= Statements(cx
, ts
, tc
);
4017 if (!pnlet
->pn_right
)
4019 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LET
);
4022 * Change pnblock's opcode to the variant that propagates the last
4023 * result down after popping the block, and clear statement.
4025 pnblock
->pn_op
= JSOP_LEAVEBLOCKEXPR
;
4026 pnlet
->pn_right
= AssignExpr(cx
, ts
, tc
);
4027 if (!pnlet
->pn_right
)
4035 #endif /* JS_HAS_BLOCK_SCOPE */
4038 PushBlocklikeStatement(JSStmtInfo
*stmt
, JSStmtType type
, JSTreeContext
*tc
)
4040 js_PushStatement(tc
, stmt
, type
, -1);
4041 return GenerateBlockId(tc
, stmt
->blockid
);
4044 static JSParseNode
*
4045 NewBindingNode(JSTokenStream
*ts
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
4047 JSParseNode
*pn
= NULL
;
4049 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
4052 JS_ASSERT(!pn
->isPlaceholder());
4054 ale
= tc
->lexdeps
.lookup(atom
);
4057 JS_ASSERT(pn
->isPlaceholder());
4062 JS_ASSERT(pn
->pn_defn
);
4065 * A let binding at top level becomes a var before we get here, so if
4066 * pn and tc have the same blockid then that id must not be the bodyid.
4067 * If pn is a forward placeholder definition from the same or a higher
4068 * block then we claim it.
4070 JS_ASSERT_IF(let
&& pn
->pn_blockid
== tc
->blockid(),
4071 pn
->pn_blockid
!= tc
->bodyid
);
4073 if (pn
->isPlaceholder() && pn
->pn_blockid
>= (let
? tc
->blockid() : tc
->bodyid
)) {
4074 JS_ASSERT(pn
->isForward());
4076 pn
->pn_blockid
= tc
->blockid();
4078 tc
->lexdeps
.remove(tc
->compiler
, atom
);
4083 /* Make a new node for this declarator name (or destructuring pattern). */
4084 pn
= NewNameNode(tc
->compiler
->context
, ts
, atom
, tc
);
4090 #if JS_HAS_BLOCK_SCOPE
4092 RebindLets(JSParseNode
*pn
, JSTreeContext
*tc
)
4097 switch (pn
->pn_arity
) {
4099 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
4100 RebindLets(pn2
, tc
);
4104 RebindLets(pn
->pn_kid1
, tc
);
4105 RebindLets(pn
->pn_kid2
, tc
);
4106 RebindLets(pn
->pn_kid3
, tc
);
4110 RebindLets(pn
->pn_left
, tc
);
4111 RebindLets(pn
->pn_right
, tc
);
4115 RebindLets(pn
->pn_kid
, tc
);
4119 RebindLets(pn
->pn_body
, tc
);
4123 RebindLets(pn
->maybeExpr(), tc
);
4126 JS_ASSERT(pn
->pn_blockid
> tc
->topStmt
->blockid
);
4127 } else if (pn
->pn_used
) {
4128 if (pn
->pn_lexdef
->pn_blockid
== tc
->topStmt
->blockid
) {
4131 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
4133 while ((ale
= ALE_NEXT(ale
)) != NULL
) {
4134 if (ALE_ATOM(ale
) == pn
->pn_atom
) {
4135 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4141 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
4143 ale
= MakePlaceholder(pn
, tc
);
4147 JSDefinition
*dn
= ALE_DEFN(ale
);
4148 dn
->pn_type
= TOK_NAME
;
4149 dn
->pn_op
= JSOP_NOP
;
4150 dn
->pn_dflags
|= pn
->pn_dflags
& PND_FUNARG
;
4152 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4158 RebindLets(pn
->pn_tree
, tc
);
4164 #endif /* JS_HAS_BLOCK_SCOPE */
4166 static JSParseNode
*
4167 Statement(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4170 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
, *pn4
;
4171 JSStmtInfo stmtInfo
, *stmt
, *stmt2
;
4174 JS_CHECK_RECURSION(cx
, return NULL
);
4176 ts
->flags
|= TSF_OPERAND
;
4177 tt
= js_GetToken(cx
, ts
);
4178 ts
->flags
&= ~TSF_OPERAND
;
4180 #if JS_HAS_GETTER_SETTER
4181 if (tt
== TOK_NAME
) {
4182 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
4183 if (tt
== TOK_ERROR
)
4190 #if JS_HAS_XML_SUPPORT
4191 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
4192 tt
= js_PeekToken(cx
, ts
);
4193 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
4194 if (tt
== TOK_DBLCOLON
)
4197 return FunctionStmt(cx
, ts
, tc
);
4200 /* An IF node has three kids: condition, then, and optional else. */
4201 pn
= NewParseNode(PN_TERNARY
, tc
);
4204 pn1
= Condition(cx
, ts
, tc
);
4207 js_PushStatement(tc
, &stmtInfo
, STMT_IF
, -1);
4208 pn2
= Statement(cx
, ts
, tc
);
4211 ts
->flags
|= TSF_OPERAND
;
4212 if (js_MatchToken(cx
, ts
, TOK_ELSE
)) {
4213 ts
->flags
&= ~TSF_OPERAND
;
4214 stmtInfo
.type
= STMT_ELSE
;
4215 pn3
= Statement(cx
, ts
, tc
);
4218 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
4220 ts
->flags
&= ~TSF_OPERAND
;
4222 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4232 JSParseNode
*pn5
, *saveBlock
;
4233 JSBool seenDefault
= JS_FALSE
;
4235 pn
= NewParseNode(PN_BINARY
, tc
);
4238 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
4240 /* pn1 points to the switch's discriminant. */
4241 pn1
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
4245 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
4246 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
4249 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4250 * because that function states tc->topStmt->blockid.
4252 js_PushStatement(tc
, &stmtInfo
, STMT_SWITCH
, -1);
4254 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4255 pn2
= NewParseNode(PN_LIST
, tc
);
4259 if (!GenerateBlockIdForStmtNode(pn2
, tc
))
4261 saveBlock
= tc
->blockNode
;
4262 tc
->blockNode
= pn2
;
4264 while ((tt
= js_GetToken(cx
, ts
)) != TOK_RC
) {
4268 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4269 JSMSG_TOO_MANY_DEFAULTS
);
4272 seenDefault
= JS_TRUE
;
4276 pn3
= NewParseNode(PN_BINARY
, tc
);
4279 if (tt
== TOK_CASE
) {
4280 pn3
->pn_left
= Expr(cx
, ts
, tc
);
4285 if (pn2
->pn_count
== JS_BIT(16)) {
4286 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4287 JSMSG_TOO_MANY_CASES
);
4296 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4300 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
4302 pn4
= NewParseNode(PN_LIST
, tc
);
4305 pn4
->pn_type
= TOK_LC
;
4307 ts
->flags
|= TSF_OPERAND
;
4308 while ((tt
= js_PeekToken(cx
, ts
)) != TOK_RC
&&
4309 tt
!= TOK_CASE
&& tt
!= TOK_DEFAULT
) {
4310 ts
->flags
&= ~TSF_OPERAND
;
4311 if (tt
== TOK_ERROR
)
4313 pn5
= Statement(cx
, ts
, tc
);
4316 pn4
->pn_pos
.end
= pn5
->pn_pos
.end
;
4318 ts
->flags
|= TSF_OPERAND
;
4320 ts
->flags
&= ~TSF_OPERAND
;
4322 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4324 pn4
->pn_pos
.begin
= pn4
->pn_head
->pn_pos
.begin
;
4325 pn3
->pn_pos
.end
= pn4
->pn_pos
.end
;
4326 pn3
->pn_right
= pn4
;
4330 * Handle the case where there was a let declaration in any case in
4331 * the switch body, but not within an inner block. If it replaced
4332 * tc->blockNode with a new block node then we must refresh pn2 and
4333 * then restore tc->blockNode.
4335 if (tc
->blockNode
!= pn2
)
4336 pn2
= tc
->blockNode
;
4337 tc
->blockNode
= saveBlock
;
4340 pn
->pn_pos
.end
= pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4347 pn
= NewParseNode(PN_BINARY
, tc
);
4350 js_PushStatement(tc
, &stmtInfo
, STMT_WHILE_LOOP
, -1);
4351 pn2
= Condition(cx
, ts
, tc
);
4355 pn2
= Statement(cx
, ts
, tc
);
4359 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4364 pn
= NewParseNode(PN_BINARY
, tc
);
4367 js_PushStatement(tc
, &stmtInfo
, STMT_DO_LOOP
, -1);
4368 pn2
= Statement(cx
, ts
, tc
);
4372 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
4373 pn2
= Condition(cx
, ts
, tc
);
4377 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4379 if (JSVERSION_NUMBER(cx
) != JSVERSION_ECMA_3
) {
4381 * All legacy and extended versions must do automatic semicolon
4382 * insertion after do-while. See the testcase and discussion in
4383 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4385 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
4392 JSParseNode
*pnseq
= NULL
;
4393 #if JS_HAS_BLOCK_SCOPE
4394 JSParseNode
*pnlet
= NULL
;
4395 JSStmtInfo blockInfo
;
4398 /* A FOR node is binary, left is loop control and right is the body. */
4399 pn
= NewParseNode(PN_BINARY
, tc
);
4402 js_PushStatement(tc
, &stmtInfo
, STMT_FOR_LOOP
, -1);
4404 pn
->pn_op
= JSOP_ITER
;
4406 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
4407 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
4408 pn
->pn_iflags
= JSITER_FOREACH
;
4413 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4414 ts
->flags
|= TSF_OPERAND
;
4415 tt
= js_PeekToken(cx
, ts
);
4416 ts
->flags
&= ~TSF_OPERAND
;
4418 #if JS_HAS_BLOCK_SCOPE
4422 if (tt
== TOK_SEMI
) {
4423 if (pn
->pn_iflags
& JSITER_FOREACH
)
4426 /* No initializer -- set first kid of left sub-node to null. */
4430 * Set pn1 to a var list or an initializing expression.
4432 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4433 * of the for statement. This flag will be used by the RelExpr
4434 * production; if it is set, then the 'in' keyword will not be
4435 * recognized as an operator, leaving it available to be parsed as
4436 * part of a for/in loop.
4438 * A side effect of this restriction is that (unparenthesized)
4439 * expressions involving an 'in' operator are illegal in the init
4440 * clause of an ordinary for loop.
4442 tc
->flags
|= TCF_IN_FOR_INIT
;
4443 if (tt
== TOK_VAR
) {
4444 (void) js_GetToken(cx
, ts
);
4445 pn1
= Variables(cx
, ts
, tc
, false);
4446 #if JS_HAS_BLOCK_SCOPE
4447 } else if (tt
== TOK_LET
) {
4449 (void) js_GetToken(cx
, ts
);
4450 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
4451 pn1
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
4452 tt
= TOK_LEXICALSCOPE
;
4454 pnlet
= PushLexicalScope(cx
, ts
, tc
, &blockInfo
);
4457 blockInfo
.flags
|= SIF_FOR_BLOCK
;
4458 pn1
= Variables(cx
, ts
, tc
, false);
4462 pn1
= Expr(cx
, ts
, tc
);
4464 while (pn1
->pn_type
== TOK_RP
)
4468 tc
->flags
&= ~TCF_IN_FOR_INIT
;
4474 * We can be sure that it's a for/in loop if there's still an 'in'
4475 * keyword here, even if JavaScript recognizes 'in' as an operator,
4476 * as we've excluded 'in' from being parsed in RelExpr by setting
4477 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4479 if (pn1
&& js_MatchToken(cx
, ts
, TOK_IN
)) {
4480 pn
->pn_iflags
|= JSITER_ENUMERATE
;
4481 stmtInfo
.type
= STMT_FOR_IN_LOOP
;
4483 /* Check that the left side of the 'in' is valid. */
4484 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt
) || PN_TYPE(pn1
) == tt
);
4485 if (TOKEN_TYPE_IS_DECL(tt
)
4486 ? (pn1
->pn_count
> 1 || pn1
->pn_op
== JSOP_DEFCONST
4487 #if JS_HAS_DESTRUCTURING
4488 || (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4489 pn
->pn_op
== JSOP_ITER
&&
4490 !(pn
->pn_iflags
& JSITER_FOREACH
) &&
4491 (pn1
->pn_head
->pn_type
== TOK_RC
||
4492 (pn1
->pn_head
->pn_type
== TOK_RB
&&
4493 pn1
->pn_head
->pn_count
!= 2) ||
4494 (pn1
->pn_head
->pn_type
== TOK_ASSIGN
&&
4495 (pn1
->pn_head
->pn_left
->pn_type
!= TOK_RB
||
4496 pn1
->pn_head
->pn_left
->pn_count
!= 2))))
4499 : (pn1
->pn_type
!= TOK_NAME
&&
4500 pn1
->pn_type
!= TOK_DOT
&&
4501 #if JS_HAS_DESTRUCTURING
4502 ((JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4503 pn
->pn_op
== JSOP_ITER
&&
4504 !(pn
->pn_iflags
& JSITER_FOREACH
))
4505 ? (pn1
->pn_type
!= TOK_RB
|| pn1
->pn_count
!= 2)
4506 : (pn1
->pn_type
!= TOK_RB
&& pn1
->pn_type
!= TOK_RC
)) &&
4508 #if JS_HAS_LVALUE_RETURN
4509 pn1
->pn_type
!= TOK_LP
&&
4511 #if JS_HAS_XML_SUPPORT
4512 (pn1
->pn_type
!= TOK_UNARYOP
||
4513 pn1
->pn_op
!= JSOP_XMLNAME
) &&
4515 pn1
->pn_type
!= TOK_LB
)) {
4516 js_ReportCompileErrorNumber(cx
, ts
, pn1
, JSREPORT_ERROR
,
4517 JSMSG_BAD_FOR_LEFTSIDE
);
4521 /* pn2 points to the name or destructuring pattern on in's left. */
4523 uintN dflag
= PND_ASSIGNED
;
4525 if (TOKEN_TYPE_IS_DECL(tt
)) {
4526 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4527 pn1
->pn_xflags
|= PNX_FORINVAR
;
4530 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4531 * 'var', or 'const' to hoist the initializer or the entire
4532 * decl out of the loop head. TOK_VAR is the type for both
4533 * 'var' and 'const'.
4536 if ((pn2
->pn_type
== TOK_NAME
&& pn2
->maybeExpr())
4537 #if JS_HAS_DESTRUCTURING
4538 || pn2
->pn_type
== TOK_ASSIGN
4541 pnseq
= NewParseNode(PN_LIST
, tc
);
4544 pnseq
->pn_type
= TOK_SEQ
;
4545 pnseq
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4547 #if JS_HAS_BLOCK_SCOPE
4548 if (tt
== TOK_LET
) {
4550 * Hoist just the 'i' from 'for (let x = i in o)' to
4551 * before the loop, glued together via pnseq.
4553 pn3
= NewParseNode(PN_UNARY
, tc
);
4556 pn3
->pn_type
= TOK_SEMI
;
4557 pn3
->pn_op
= JSOP_NOP
;
4558 #if JS_HAS_DESTRUCTURING
4559 if (pn2
->pn_type
== TOK_ASSIGN
) {
4560 pn4
= pn2
->pn_right
;
4561 pn2
= pn1
->pn_head
= pn2
->pn_left
;
4566 pn2
->pn_expr
= NULL
;
4568 if (!RebindLets(pn4
, tc
))
4570 pn3
->pn_pos
= pn4
->pn_pos
;
4572 pnseq
->initList(pn3
);
4574 #endif /* JS_HAS_BLOCK_SCOPE */
4576 dflag
= PND_INITIALIZED
;
4579 * All of 'var x = i' is hoisted above 'for (x in o)',
4580 * so clear PNX_FORINVAR.
4582 * Request JSOP_POP here since the var is for a simple
4583 * name (it is not a destructuring binding's left-hand
4584 * side) and it has an initializer.
4586 pn1
->pn_xflags
&= ~PNX_FORINVAR
;
4587 pn1
->pn_xflags
|= PNX_POPVAR
;
4588 pnseq
->initList(pn1
);
4590 #if JS_HAS_DESTRUCTURING
4591 if (pn2
->pn_type
== TOK_ASSIGN
) {
4592 pn1
= CloneParseTree(pn2
->pn_left
, tc
);
4598 JS_ASSERT(pn2
->pn_type
== TOK_NAME
);
4599 pn1
= NewNameNode(cx
, ts
, pn2
->pn_atom
, tc
);
4602 pn1
->pn_type
= TOK_NAME
;
4603 pn1
->pn_op
= JSOP_NAME
;
4604 pn1
->pn_pos
= pn2
->pn_pos
;
4606 LinkUseToDef(pn1
, (JSDefinition
*) pn2
, tc
);
4615 #if JS_HAS_LVALUE_RETURN
4616 if (pn2
->pn_type
== TOK_LP
&&
4617 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
)) {
4621 #if JS_HAS_XML_SUPPORT
4622 if (pn2
->pn_type
== TOK_UNARYOP
)
4623 pn2
->pn_op
= JSOP_BINDXMLNAME
;
4627 switch (pn2
->pn_type
) {
4629 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4630 NoteLValue(cx
, pn2
, tc
, dflag
);
4633 #if JS_HAS_DESTRUCTURING
4636 JS_ASSERT(pn2
->pn_type
== TOK_RB
|| pn2
->pn_type
== TOK_RC
);
4640 /* Check for valid lvalues in var-less destructuring for-in. */
4641 if (pn1
== pn2
&& !CheckDestructuring(cx
, NULL
, pn2
, NULL
, tc
))
4644 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
4646 * Destructuring for-in requires [key, value] enumeration
4649 JS_ASSERT(pn
->pn_op
== JSOP_ITER
);
4650 if (!(pn
->pn_iflags
& JSITER_FOREACH
))
4651 pn
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
4660 * Parse the object expression as the right operand of 'in', first
4661 * removing the top statement from the statement-stack if this is a
4662 * 'for (let x in y)' loop.
4664 #if JS_HAS_BLOCK_SCOPE
4665 JSStmtInfo
*save
= tc
->topStmt
;
4667 tc
->topStmt
= save
->down
;
4669 pn2
= Expr(cx
, ts
, tc
);
4670 #if JS_HAS_BLOCK_SCOPE
4675 pn2
= NewBinary(TOK_IN
, JSOP_NOP
, pn1
, pn2
, tc
);
4680 if (pn
->pn_iflags
& JSITER_FOREACH
)
4682 pn
->pn_op
= JSOP_NOP
;
4684 /* Parse the loop condition or null into pn2. */
4685 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
4686 ts
->flags
|= TSF_OPERAND
;
4687 tt
= js_PeekToken(cx
, ts
);
4688 ts
->flags
&= ~TSF_OPERAND
;
4689 if (tt
== TOK_SEMI
) {
4692 pn2
= Expr(cx
, ts
, tc
);
4697 /* Parse the update expression or null into pn3. */
4698 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
4699 ts
->flags
|= TSF_OPERAND
;
4700 tt
= js_PeekToken(cx
, ts
);
4701 ts
->flags
&= ~TSF_OPERAND
;
4705 pn3
= Expr(cx
, ts
, tc
);
4710 /* Build the FORHEAD node to use as the left kid of pn. */
4711 pn4
= NewParseNode(PN_TERNARY
, tc
);
4714 pn4
->pn_type
= TOK_FORHEAD
;
4715 pn4
->pn_op
= JSOP_NOP
;
4722 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
4724 /* Parse the loop body into pn->pn_right. */
4725 pn2
= Statement(cx
, ts
, tc
);
4730 /* Record the absolute line number for source note emission. */
4731 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4733 #if JS_HAS_BLOCK_SCOPE
4736 pnlet
->pn_expr
= pn
;
4741 pnseq
->pn_pos
.end
= pn
->pn_pos
.end
;
4749 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
4750 JSMSG_BAD_FOR_EACH_LOOP
);
4755 JSParseNode
*catchList
, *lastCatch
;
4758 * try nodes are ternary.
4759 * kid1 is the try Statement
4760 * kid2 is the catch node list or null
4761 * kid3 is the finally Statement
4763 * catch nodes are ternary.
4764 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
4765 * kid2 is the catch guard or null if no guard
4766 * kid3 is the catch block
4768 * catch lvalue nodes are either:
4769 * TOK_NAME for a single identifier
4770 * TOK_RB or TOK_RC for a destructuring left-hand side
4772 * finally nodes are TOK_LC Statement lists.
4774 pn
= NewParseNode(PN_TERNARY
, tc
);
4777 pn
->pn_op
= JSOP_NOP
;
4779 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
4780 if (!PushBlocklikeStatement(&stmtInfo
, STMT_TRY
, tc
))
4782 pn
->pn_kid1
= Statements(cx
, ts
, tc
);
4785 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
4789 tt
= js_GetToken(cx
, ts
);
4790 if (tt
== TOK_CATCH
) {
4791 catchList
= NewParseNode(PN_LIST
, tc
);
4794 catchList
->pn_type
= TOK_RESERVED
;
4795 catchList
->makeEmpty();
4799 JSParseNode
*pnblock
;
4802 /* Check for another catch after unconditional catch. */
4803 if (lastCatch
&& !lastCatch
->pn_kid2
) {
4804 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4805 JSMSG_CATCH_AFTER_GENERAL
);
4810 * Create a lexical scope node around the whole catch clause,
4811 * including the head.
4813 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
4816 stmtInfo
.type
= STMT_CATCH
;
4819 * Legal catch forms are:
4821 * catch (lhs if <boolean_expression>)
4822 * where lhs is a name or a destructuring left-hand side.
4823 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
4825 pn2
= NewParseNode(PN_TERNARY
, tc
);
4828 pnblock
->pn_expr
= pn2
;
4829 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
4832 * Contrary to ECMA Ed. 3, the catch variable is lexically
4833 * scoped, not a property of a new Object instance. This is
4834 * an intentional change that anticipates ECMA Ed. 4.
4838 data
.binder
= BindLet
;
4839 data
.let
.overflow
= JSMSG_TOO_MANY_CATCH_VARS
;
4841 tt
= js_GetToken(cx
, ts
);
4843 #if JS_HAS_DESTRUCTURING
4846 pn3
= DestructuringExpr(cx
, &data
, tc
, tt
);
4853 label
= CURRENT_TOKEN(ts
).t_atom
;
4854 pn3
= NewBindingNode(ts
, label
, tc
, true);
4858 if (!data
.binder(cx
, &data
, label
, tc
))
4863 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4864 JSMSG_CATCH_IDENTIFIER
);
4869 #if JS_HAS_CATCH_GUARD
4871 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
4872 * to avoid conflicting with the JS2/ECMAv4 type annotation
4873 * catchguard syntax.
4875 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
4876 pn2
->pn_kid2
= Expr(cx
, ts
, tc
);
4881 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
4883 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
4884 pn2
->pn_kid3
= Statements(cx
, ts
, tc
);
4887 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
4890 catchList
->append(pnblock
);
4892 ts
->flags
|= TSF_OPERAND
;
4893 tt
= js_GetToken(cx
, ts
);
4894 ts
->flags
&= ~TSF_OPERAND
;
4895 } while (tt
== TOK_CATCH
);
4897 pn
->pn_kid2
= catchList
;
4899 if (tt
== TOK_FINALLY
) {
4900 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
4901 if (!PushBlocklikeStatement(&stmtInfo
, STMT_FINALLY
, tc
))
4903 pn
->pn_kid3
= Statements(cx
, ts
, tc
);
4906 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
4911 if (!catchList
&& !pn
->pn_kid3
) {
4912 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4913 JSMSG_CATCH_OR_FINALLY
);
4920 pn
= NewParseNode(PN_UNARY
, tc
);
4924 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
4925 ts
->flags
|= TSF_OPERAND
;
4926 tt
= js_PeekTokenSameLine(cx
, ts
);
4927 ts
->flags
&= ~TSF_OPERAND
;
4928 if (tt
== TOK_ERROR
)
4930 if (tt
== TOK_EOF
|| tt
== TOK_EOL
|| tt
== TOK_SEMI
|| tt
== TOK_RC
) {
4931 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4932 JSMSG_SYNTAX_ERROR
);
4936 pn2
= Expr(cx
, ts
, tc
);
4939 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4940 pn
->pn_op
= JSOP_THROW
;
4944 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
4946 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4947 JSMSG_CATCH_WITHOUT_TRY
);
4951 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4952 JSMSG_FINALLY_WITHOUT_TRY
);
4956 pn
= NewParseNode(PN_NULLARY
, tc
);
4959 if (!MatchLabel(cx
, ts
, pn
))
4962 label
= pn
->pn_atom
;
4964 for (; ; stmt
= stmt
->down
) {
4966 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4967 JSMSG_LABEL_NOT_FOUND
);
4970 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
)
4974 for (; ; stmt
= stmt
->down
) {
4976 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4980 if (STMT_IS_LOOP(stmt
) || stmt
->type
== STMT_SWITCH
)
4985 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4989 pn
= NewParseNode(PN_NULLARY
, tc
);
4992 if (!MatchLabel(cx
, ts
, pn
))
4995 label
= pn
->pn_atom
;
4997 for (stmt2
= NULL
; ; stmt
= stmt
->down
) {
4999 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5000 JSMSG_LABEL_NOT_FOUND
);
5003 if (stmt
->type
== STMT_LABEL
) {
5004 if (stmt
->label
== label
) {
5005 if (!stmt2
|| !STMT_IS_LOOP(stmt2
)) {
5006 js_ReportCompileErrorNumber(cx
, ts
, NULL
,
5008 JSMSG_BAD_CONTINUE
);
5018 for (; ; stmt
= stmt
->down
) {
5020 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5021 JSMSG_BAD_CONTINUE
);
5024 if (STMT_IS_LOOP(stmt
))
5029 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5033 pn
= NewParseNode(PN_BINARY
, tc
);
5036 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
5037 pn2
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
5040 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
5043 js_PushStatement(tc
, &stmtInfo
, STMT_WITH
, -1);
5044 pn2
= Statement(cx
, ts
, tc
);
5049 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5051 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5055 pn
= Variables(cx
, ts
, tc
, false);
5059 /* Tell js_EmitTree to generate a final POP. */
5060 pn
->pn_xflags
|= PNX_POPVAR
;
5063 #if JS_HAS_BLOCK_SCOPE
5067 JSObjectBox
*blockbox
;
5069 /* Check for a let statement or let expression. */
5070 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
5071 pn
= LetBlock(cx
, ts
, tc
, JS_TRUE
);
5072 if (!pn
|| pn
->pn_op
== JSOP_LEAVEBLOCK
)
5075 /* Let expressions require automatic semicolon insertion. */
5076 JS_ASSERT(pn
->pn_type
== TOK_SEMI
||
5077 pn
->pn_op
== JSOP_LEAVEBLOCKEXPR
);
5082 * This is a let declaration. We must be directly under a block per
5083 * the proposed ES4 specs, but not an implicit block created due to
5084 * 'for (let ...)'. If we pass this error test, make the enclosing
5085 * JSStmtInfo be our scope. Further let declarations in this block
5086 * will find this scope statement and use the same block object.
5088 * If we are the first let declaration in this block (i.e., when the
5089 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5090 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5094 (!STMT_MAYBE_SCOPE(stmt
) || (stmt
->flags
& SIF_FOR_BLOCK
))) {
5095 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5096 JSMSG_LET_DECL_NOT_IN_BLOCK
);
5100 if (stmt
&& (stmt
->flags
& SIF_SCOPE
)) {
5101 JS_ASSERT(tc
->blockChain
== stmt
->blockObj
);
5102 obj
= tc
->blockChain
;
5104 if (!stmt
|| (stmt
->flags
& SIF_BODY_BLOCK
)) {
5106 * ES4 specifies that let at top level and at body-block scope
5107 * does not shadow var, so convert back to var.
5109 CURRENT_TOKEN(ts
).type
= TOK_VAR
;
5110 CURRENT_TOKEN(ts
).t_op
= JSOP_DEFVAR
;
5112 pn
= Variables(cx
, ts
, tc
, false);
5115 pn
->pn_xflags
|= PNX_POPVAR
;
5120 * Some obvious assertions here, but they may help clarify the
5121 * situation. This stmt is not yet a scope, so it must not be a
5122 * catch block (catch is a lexical scope by definition).
5124 JS_ASSERT(!(stmt
->flags
& SIF_SCOPE
));
5125 JS_ASSERT(stmt
!= tc
->topScopeStmt
);
5126 JS_ASSERT(stmt
->type
== STMT_BLOCK
||
5127 stmt
->type
== STMT_SWITCH
||
5128 stmt
->type
== STMT_TRY
||
5129 stmt
->type
== STMT_FINALLY
);
5130 JS_ASSERT(!stmt
->downScope
);
5132 /* Convert the block statement into a scope statement. */
5133 JSObject
*obj
= js_NewBlockObject(tc
->compiler
->context
);
5137 blockbox
= tc
->compiler
->newObjectBox(obj
);
5142 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5143 * list stack, if it isn't already there. If it is there, but it
5144 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5147 stmt
->flags
|= SIF_SCOPE
;
5148 stmt
->downScope
= tc
->topScopeStmt
;
5149 tc
->topScopeStmt
= stmt
;
5150 JS_SCOPE_DEPTH_METERING(++tc
->scopeDepth
> tc
->maxScopeDepth
&&
5151 (tc
->maxScopeDepth
= tc
->scopeDepth
));
5153 STOBJ_SET_PARENT(obj
, tc
->blockChain
);
5154 tc
->blockChain
= obj
;
5155 stmt
->blockObj
= obj
;
5158 pn1
= tc
->blockNode
;
5159 JS_ASSERT(!pn1
|| pn1
->pn_type
!= TOK_LEXICALSCOPE
);
5162 /* Create a new lexical scope node for these statements. */
5163 pn1
= NewParseNode(PN_NAME
, tc
);
5167 pn1
->pn_type
= TOK_LEXICALSCOPE
;
5168 pn1
->pn_op
= JSOP_LEAVEBLOCK
;
5169 pn1
->pn_pos
= tc
->blockNode
->pn_pos
;
5170 pn1
->pn_objbox
= blockbox
;
5171 pn1
->pn_expr
= tc
->blockNode
;
5172 pn1
->pn_blockid
= tc
->blockNode
->pn_blockid
;
5173 tc
->blockNode
= pn1
;
5176 pn
= Variables(cx
, ts
, tc
, false);
5179 pn
->pn_xflags
= PNX_POPVAR
;
5182 #endif /* JS_HAS_BLOCK_SCOPE */
5185 pn
= ReturnOrYield(cx
, ts
, tc
, Expr
);
5194 oldflags
= tc
->flags
;
5195 tc
->flags
= oldflags
& ~TCF_HAS_FUNCTION_STMT
;
5196 if (!PushBlocklikeStatement(&stmtInfo
, STMT_BLOCK
, tc
))
5198 pn
= Statements(cx
, ts
, tc
);
5202 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
5206 * If we contain a function statement and our container is top-level
5207 * or another block, flag pn to preserve braces when decompiling.
5209 if ((tc
->flags
& TCF_HAS_FUNCTION_STMT
) &&
5210 (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)) {
5211 pn
->pn_xflags
|= PNX_NEEDBRACES
;
5213 tc
->flags
= oldflags
| (tc
->flags
& (TCF_FUN_FLAGS
| TCF_RETURN_FLAGS
));
5219 pn
= NewParseNode(PN_UNARY
, tc
);
5222 pn
->pn_type
= TOK_SEMI
;
5225 #if JS_HAS_DEBUGGER_KEYWORD
5227 pn
= NewParseNode(PN_NULLARY
, tc
);
5230 pn
->pn_type
= TOK_DEBUGGER
;
5231 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5233 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5235 #if JS_HAS_XML_SUPPORT
5237 pn
= NewParseNode(PN_UNARY
, tc
);
5240 if (!js_MatchToken(cx
, ts
, TOK_NAME
) ||
5241 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.xmlAtom
||
5242 !js_MatchToken(cx
, ts
, TOK_NAME
) ||
5243 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.namespaceAtom
||
5244 !js_MatchToken(cx
, ts
, TOK_ASSIGN
) ||
5245 CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
5246 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5247 JSMSG_BAD_DEFAULT_XML_NAMESPACE
);
5250 pn2
= Expr(cx
, ts
, tc
);
5253 pn
->pn_op
= JSOP_DEFXMLNS
;
5254 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5263 #if JS_HAS_XML_SUPPORT
5267 pn2
= Expr(cx
, ts
, tc
);
5271 if (js_PeekToken(cx
, ts
) == TOK_COLON
) {
5272 if (pn2
->pn_type
!= TOK_NAME
) {
5273 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5277 label
= pn2
->pn_atom
;
5278 for (stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
5279 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
) {
5280 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5281 JSMSG_DUPLICATE_LABEL
);
5287 (void) js_GetToken(cx
, ts
);
5289 /* Push a label struct and parse the statement. */
5290 js_PushStatement(tc
, &stmtInfo
, STMT_LABEL
, -1);
5291 stmtInfo
.label
= label
;
5292 pn
= Statement(cx
, ts
, tc
);
5296 /* Normalize empty statement to empty block for the decompiler. */
5297 if (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
) {
5298 pn
->pn_type
= TOK_LC
;
5299 pn
->pn_arity
= PN_LIST
;
5303 /* Pop the label, set pn_expr, and return early. */
5305 pn2
->pn_type
= TOK_COLON
;
5306 pn2
->pn_pos
.end
= pn
->pn_pos
.end
;
5311 pn
= NewParseNode(PN_UNARY
, tc
);
5314 pn
->pn_type
= TOK_SEMI
;
5315 pn
->pn_pos
= pn2
->pn_pos
;
5320 /* Check termination of this primitive statement. */
5321 return MatchOrInsertSemicolon(cx
, ts
) ? pn
: NULL
;
5325 NoteArgumentsUse(JSTreeContext
*tc
)
5327 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
5328 tc
->flags
|= TCF_FUN_USES_ARGUMENTS
;
5330 tc
->funbox
->node
->pn_dflags
|= PND_FUNARG
;
5333 static JSParseNode
*
5334 Variables(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, bool inLetHead
)
5338 JSStmtInfo
*scopeStmt
;
5340 JSParseNode
*pn
, *pn2
;
5344 * The three options here are:
5345 * - TOK_LET: We are parsing a let declaration.
5346 * - TOK_LP: We are parsing the head of a let block.
5347 * - Otherwise, we're parsing var declarations.
5349 tt
= CURRENT_TOKEN(ts
).type
;
5350 let
= (tt
== TOK_LET
|| tt
== TOK_LP
);
5351 JS_ASSERT(let
|| tt
== TOK_VAR
);
5353 #if JS_HAS_BLOCK_SCOPE
5354 bool popScope
= (inLetHead
|| (let
&& (tc
->flags
& TCF_IN_FOR_INIT
)));
5355 JSStmtInfo
*save
= tc
->topStmt
, *saveScope
= tc
->topScopeStmt
;
5358 /* Make sure that Statement set up the tree context correctly. */
5359 scopeStmt
= tc
->topScopeStmt
;
5361 while (scopeStmt
&& !(scopeStmt
->flags
& SIF_SCOPE
)) {
5362 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt
));
5363 scopeStmt
= scopeStmt
->downScope
;
5365 JS_ASSERT(scopeStmt
);
5368 data
.op
= let
? JSOP_NOP
: CURRENT_TOKEN(ts
).t_op
;
5369 pn
= NewParseNode(PN_LIST
, tc
);
5372 pn
->pn_op
= data
.op
;
5376 * SpiderMonkey const is really "write once per initialization evaluation"
5377 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5378 * this code will change soon.
5381 JS_ASSERT(tc
->blockChain
== scopeStmt
->blockObj
);
5382 data
.binder
= BindLet
;
5383 data
.let
.overflow
= JSMSG_TOO_MANY_LOCALS
;
5385 data
.binder
= BindVarOrConst
;
5389 tt
= js_GetToken(cx
, ts
);
5390 #if JS_HAS_DESTRUCTURING
5391 if (tt
== TOK_LB
|| tt
== TOK_LC
) {
5392 ts
->flags
|= TSF_DESTRUCTURING
;
5393 pn2
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
5394 ts
->flags
&= ~TSF_DESTRUCTURING
;
5398 if ((tc
->flags
& TCF_IN_FOR_INIT
) &&
5399 js_PeekToken(cx
, ts
) == TOK_IN
) {
5400 if (!CheckDestructuring(cx
, &data
, pn2
, NULL
, tc
))
5406 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_BAD_DESTRUCT_DECL
);
5407 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5410 #if JS_HAS_BLOCK_SCOPE
5412 tc
->topStmt
= save
->down
;
5413 tc
->topScopeStmt
= saveScope
->downScope
;
5416 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5417 #if JS_HAS_BLOCK_SCOPE
5420 tc
->topScopeStmt
= saveScope
;
5424 pn2
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, pn2
, init
, tc
);
5426 !CheckDestructuring(cx
, &data
,
5427 pn2
->pn_left
, pn2
->pn_right
,
5434 #endif /* JS_HAS_DESTRUCTURING */
5436 if (tt
!= TOK_NAME
) {
5437 if (tt
!= TOK_ERROR
) {
5438 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5439 JSMSG_NO_VARIABLE_NAME
);
5444 atom
= CURRENT_TOKEN(ts
).t_atom
;
5445 pn2
= NewBindingNode(ts
, atom
, tc
, let
);
5448 if (data
.op
== JSOP_DEFCONST
)
5449 pn2
->pn_dflags
|= PND_CONST
;
5451 if (!data
.binder(cx
, &data
, atom
, tc
))
5455 if (js_MatchToken(cx
, ts
, TOK_ASSIGN
)) {
5456 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5459 #if JS_HAS_BLOCK_SCOPE
5461 tc
->topStmt
= save
->down
;
5462 tc
->topScopeStmt
= saveScope
->downScope
;
5465 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5466 #if JS_HAS_BLOCK_SCOPE
5469 tc
->topScopeStmt
= saveScope
;
5476 pn2
= MakeAssignment(pn2
, init
, tc
);
5480 pn2
->pn_expr
= init
;
5483 pn2
->pn_op
= (PN_OP(pn2
) == JSOP_ARGUMENTS
)
5485 : (pn2
->pn_dflags
& PND_GVAR
)
5487 : (pn2
->pn_dflags
& PND_BOUND
)
5489 : (data
.op
== JSOP_DEFCONST
)
5493 NoteLValue(cx
, pn2
, tc
, PND_INITIALIZED
);
5495 /* The declarator's position must include the initializer. */
5496 pn2
->pn_pos
.end
= init
->pn_pos
.end
;
5498 if ((tc
->flags
& TCF_IN_FUNCTION
) &&
5499 atom
== cx
->runtime
->atomState
.argumentsAtom
) {
5500 NoteArgumentsUse(tc
);
5502 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5505 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5507 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5511 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5512 JSMSG_BAD_VAR_INIT
);
5516 static JSParseNode
*
5517 Expr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5519 JSParseNode
*pn
, *pn2
;
5521 pn
= AssignExpr(cx
, ts
, tc
);
5522 if (pn
&& js_MatchToken(cx
, ts
, TOK_COMMA
)) {
5523 pn2
= NewParseNode(PN_LIST
, tc
);
5526 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
5530 #if JS_HAS_GENERATORS
5532 if (pn2
->pn_type
== TOK_YIELD
) {
5533 js_ReportCompileErrorNumber(cx
, ts
, pn2
, JSREPORT_ERROR
,
5534 JSMSG_BAD_GENERATOR_SYNTAX
,
5539 pn2
= AssignExpr(cx
, ts
, tc
);
5543 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5544 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5549 static JSParseNode
*
5550 AssignExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5552 JSParseNode
*pn
, *pn2
;
5556 JS_CHECK_RECURSION(cx
, return NULL
);
5558 #if JS_HAS_GENERATORS
5559 ts
->flags
|= TSF_OPERAND
;
5560 if (js_MatchToken(cx
, ts
, TOK_YIELD
)) {
5561 ts
->flags
&= ~TSF_OPERAND
;
5562 return ReturnOrYield(cx
, ts
, tc
, AssignExpr
);
5564 ts
->flags
&= ~TSF_OPERAND
;
5567 pn
= CondExpr(cx
, ts
, tc
);
5571 tt
= js_GetToken(cx
, ts
);
5572 #if JS_HAS_GETTER_SETTER
5573 if (tt
== TOK_NAME
) {
5574 tt
= CheckGetterOrSetter(cx
, ts
, TOK_ASSIGN
);
5575 if (tt
== TOK_ERROR
)
5579 if (tt
!= TOK_ASSIGN
) {
5584 op
= CURRENT_TOKEN(ts
).t_op
;
5585 for (pn2
= pn
; pn2
->pn_type
== TOK_RP
; pn2
= pn2
->pn_kid
)
5587 switch (pn2
->pn_type
) {
5589 pn2
->pn_op
= JSOP_SETNAME
;
5590 NoteLValue(cx
, pn2
, tc
);
5593 pn2
->pn_op
= JSOP_SETPROP
;
5596 pn2
->pn_op
= JSOP_SETELEM
;
5598 #if JS_HAS_DESTRUCTURING
5601 if (op
!= JSOP_NOP
) {
5602 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5603 JSMSG_BAD_DESTRUCT_ASS
);
5606 pn
= AssignExpr(cx
, ts
, tc
);
5607 if (!pn
|| !CheckDestructuring(cx
, NULL
, pn2
, pn
, tc
))
5609 return NewBinary(TOK_ASSIGN
, op
, pn2
, pn
, tc
);
5611 #if JS_HAS_LVALUE_RETURN
5613 if (!MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
5617 #if JS_HAS_XML_SUPPORT
5619 if (pn2
->pn_op
== JSOP_XMLNAME
) {
5620 pn2
->pn_op
= JSOP_SETXMLNAME
;
5626 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5627 JSMSG_BAD_LEFTSIDE_OF_ASS
);
5631 return NewBinary(TOK_ASSIGN
, op
, pn2
, AssignExpr(cx
, ts
, tc
), tc
);
5634 static JSParseNode
*
5635 CondExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5637 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
;
5640 pn
= OrExpr(cx
, ts
, tc
);
5641 if (pn
&& js_MatchToken(cx
, ts
, TOK_HOOK
)) {
5643 pn
= NewParseNode(PN_TERNARY
, tc
);
5647 * Always accept the 'in' operator in the middle clause of a ternary,
5648 * where it's unambiguous, even if we might be parsing the init of a
5651 oldflags
= tc
->flags
;
5652 tc
->flags
&= ~TCF_IN_FOR_INIT
;
5653 pn2
= AssignExpr(cx
, ts
, tc
);
5654 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
5658 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
5659 pn3
= AssignExpr(cx
, ts
, tc
);
5662 pn
->pn_pos
.begin
= pn1
->pn_pos
.begin
;
5663 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
5671 static JSParseNode
*
5672 OrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5676 pn
= AndExpr(cx
, ts
, tc
);
5677 while (pn
&& js_MatchToken(cx
, ts
, TOK_OR
))
5678 pn
= NewBinary(TOK_OR
, JSOP_OR
, pn
, AndExpr(cx
, ts
, tc
), tc
);
5682 static JSParseNode
*
5683 AndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5687 pn
= BitOrExpr(cx
, ts
, tc
);
5688 while (pn
&& js_MatchToken(cx
, ts
, TOK_AND
))
5689 pn
= NewBinary(TOK_AND
, JSOP_AND
, pn
, BitOrExpr(cx
, ts
, tc
), tc
);
5693 static JSParseNode
*
5694 BitOrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5698 pn
= BitXorExpr(cx
, ts
, tc
);
5699 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITOR
)) {
5700 pn
= NewBinary(TOK_BITOR
, JSOP_BITOR
, pn
, BitXorExpr(cx
, ts
, tc
),
5706 static JSParseNode
*
5707 BitXorExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5711 pn
= BitAndExpr(cx
, ts
, tc
);
5712 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITXOR
)) {
5713 pn
= NewBinary(TOK_BITXOR
, JSOP_BITXOR
, pn
, BitAndExpr(cx
, ts
, tc
),
5719 static JSParseNode
*
5720 BitAndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5724 pn
= EqExpr(cx
, ts
, tc
);
5725 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITAND
))
5726 pn
= NewBinary(TOK_BITAND
, JSOP_BITAND
, pn
, EqExpr(cx
, ts
, tc
), tc
);
5730 static JSParseNode
*
5731 EqExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5736 pn
= RelExpr(cx
, ts
, tc
);
5737 while (pn
&& js_MatchToken(cx
, ts
, TOK_EQOP
)) {
5738 op
= CURRENT_TOKEN(ts
).t_op
;
5739 pn
= NewBinary(TOK_EQOP
, op
, pn
, RelExpr(cx
, ts
, tc
), tc
);
5744 static JSParseNode
*
5745 RelExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5750 uintN inForInitFlag
= tc
->flags
& TCF_IN_FOR_INIT
;
5753 * Uses of the in operator in ShiftExprs are always unambiguous,
5754 * so unset the flag that prohibits recognizing it.
5756 tc
->flags
&= ~TCF_IN_FOR_INIT
;
5758 pn
= ShiftExpr(cx
, ts
, tc
);
5760 (js_MatchToken(cx
, ts
, TOK_RELOP
) ||
5762 * Recognize the 'in' token as an operator only if we're not
5763 * currently in the init expr of a for loop.
5765 (inForInitFlag
== 0 && js_MatchToken(cx
, ts
, TOK_IN
)) ||
5766 js_MatchToken(cx
, ts
, TOK_INSTANCEOF
))) {
5767 tt
= CURRENT_TOKEN(ts
).type
;
5768 op
= CURRENT_TOKEN(ts
).t_op
;
5769 pn
= NewBinary(tt
, op
, pn
, ShiftExpr(cx
, ts
, tc
), tc
);
5771 /* Restore previous state of inForInit flag. */
5772 tc
->flags
|= inForInitFlag
;
5777 static JSParseNode
*
5778 ShiftExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5783 pn
= AddExpr(cx
, ts
, tc
);
5784 while (pn
&& js_MatchToken(cx
, ts
, TOK_SHOP
)) {
5785 op
= CURRENT_TOKEN(ts
).t_op
;
5786 pn
= NewBinary(TOK_SHOP
, op
, pn
, AddExpr(cx
, ts
, tc
), tc
);
5791 static JSParseNode
*
5792 AddExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5798 pn
= MulExpr(cx
, ts
, tc
);
5800 (js_MatchToken(cx
, ts
, TOK_PLUS
) ||
5801 js_MatchToken(cx
, ts
, TOK_MINUS
))) {
5802 tt
= CURRENT_TOKEN(ts
).type
;
5803 op
= (tt
== TOK_PLUS
) ? JSOP_ADD
: JSOP_SUB
;
5804 pn
= NewBinary(tt
, op
, pn
, MulExpr(cx
, ts
, tc
), tc
);
5809 static JSParseNode
*
5810 MulExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5816 pn
= UnaryExpr(cx
, ts
, tc
);
5818 (js_MatchToken(cx
, ts
, TOK_STAR
) ||
5819 js_MatchToken(cx
, ts
, TOK_DIVOP
))) {
5820 tt
= CURRENT_TOKEN(ts
).type
;
5821 op
= CURRENT_TOKEN(ts
).t_op
;
5822 pn
= NewBinary(tt
, op
, pn
, UnaryExpr(cx
, ts
, tc
), tc
);
5827 static JSParseNode
*
5828 SetLvalKid(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
, JSParseNode
*kid
,
5831 while (kid
->pn_type
== TOK_RP
)
5833 if (kid
->pn_type
!= TOK_NAME
&&
5834 kid
->pn_type
!= TOK_DOT
&&
5835 #if JS_HAS_LVALUE_RETURN
5836 (kid
->pn_type
!= TOK_LP
||
5837 (kid
->pn_op
!= JSOP_CALL
&& kid
->pn_op
!= JSOP_EVAL
&& kid
->pn_op
!= JSOP_APPLY
)) &&
5839 #if JS_HAS_XML_SUPPORT
5840 (kid
->pn_type
!= TOK_UNARYOP
|| kid
->pn_op
!= JSOP_XMLNAME
) &&
5842 kid
->pn_type
!= TOK_LB
) {
5843 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5844 JSMSG_BAD_OPERAND
, name
);
5851 static const char incop_name_str
[][10] = {"increment", "decrement"};
5854 SetIncOpKid(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
5855 JSParseNode
*pn
, JSParseNode
*kid
,
5856 JSTokenType tt
, JSBool preorder
)
5860 kid
= SetLvalKid(cx
, ts
, pn
, kid
, incop_name_str
[tt
== TOK_DEC
]);
5863 switch (kid
->pn_type
) {
5865 op
= (tt
== TOK_INC
)
5866 ? (preorder
? JSOP_INCNAME
: JSOP_NAMEINC
)
5867 : (preorder
? JSOP_DECNAME
: JSOP_NAMEDEC
);
5868 NoteLValue(cx
, kid
, tc
);
5872 op
= (tt
== TOK_INC
)
5873 ? (preorder
? JSOP_INCPROP
: JSOP_PROPINC
)
5874 : (preorder
? JSOP_DECPROP
: JSOP_PROPDEC
);
5877 #if JS_HAS_LVALUE_RETURN
5879 if (!MakeSetCall(cx
, kid
, tc
, JSMSG_BAD_INCOP_OPERAND
))
5883 #if JS_HAS_XML_SUPPORT
5885 if (kid
->pn_op
== JSOP_XMLNAME
)
5886 kid
->pn_op
= JSOP_SETXMLNAME
;
5890 op
= (tt
== TOK_INC
)
5891 ? (preorder
? JSOP_INCELEM
: JSOP_ELEMINC
)
5892 : (preorder
? JSOP_DECELEM
: JSOP_ELEMDEC
);
5903 static JSParseNode
*
5904 UnaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5907 JSParseNode
*pn
, *pn2
;
5909 JS_CHECK_RECURSION(cx
, return NULL
);
5911 ts
->flags
|= TSF_OPERAND
;
5912 tt
= js_GetToken(cx
, ts
);
5913 ts
->flags
&= ~TSF_OPERAND
;
5919 pn
= NewParseNode(PN_UNARY
, tc
);
5922 pn
->pn_type
= TOK_UNARYOP
; /* PLUS and MINUS are binary */
5923 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
5924 pn2
= UnaryExpr(cx
, ts
, tc
);
5927 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5933 pn
= NewParseNode(PN_UNARY
, tc
);
5936 pn2
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
5939 if (!SetIncOpKid(cx
, ts
, tc
, pn
, pn2
, tt
, JS_TRUE
))
5941 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5945 pn
= NewParseNode(PN_UNARY
, tc
);
5948 pn2
= UnaryExpr(cx
, ts
, tc
);
5951 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5954 * Under ECMA3, deleting any unary expression is valid -- it simply
5955 * returns true. Here we strip off any parentheses and fold constants
5956 * before checking for a call expression, in order to rule out delete
5957 * of a generator expression.
5959 while (pn2
->pn_type
== TOK_RP
)
5961 if (!js_FoldConstants(cx
, pn2
, tc
))
5963 switch (pn2
->pn_type
) {
5965 if (pn2
->pn_op
!= JSOP_SETCALL
&&
5966 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_DELETE_OPERAND
)) {
5971 pn2
->pn_op
= JSOP_DELNAME
;
5983 pn
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
5987 /* Don't look across a newline boundary for a postfix incop. */
5988 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
5989 ts
->flags
|= TSF_OPERAND
;
5990 tt
= js_PeekTokenSameLine(cx
, ts
);
5991 ts
->flags
&= ~TSF_OPERAND
;
5992 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
5993 (void) js_GetToken(cx
, ts
);
5994 pn2
= NewParseNode(PN_UNARY
, tc
);
5997 if (!SetIncOpKid(cx
, ts
, tc
, pn2
, pn
, tt
, JS_FALSE
))
5999 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6008 #if JS_HAS_GENERATORS
6011 * A dedicated helper for transplanting the comprehension expression E in
6013 * [E for (V in I)] // array comprehension
6014 * (E for (V in I)) // generator expression
6016 * from its initial location in the AST, on the left of the 'for', to its final
6017 * position on the right. To avoid a separate pass we do this by adjusting the
6018 * blockids and name binding links that were established when E was parsed.
6020 * A generator expression desugars like so:
6022 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6024 * so the transplanter must adjust static level as well as blockid. E's source
6025 * coordinates in root->pn_pos are critical to deciding which binding links to
6026 * preserve and which to cut.
6028 * NB: This is not a general tree transplanter -- it knows in particular that
6029 * the one or more bindings induced by V have not yet been created.
6031 class CompExprTransplanter
{
6039 CompExprTransplanter(JSParseNode
*pn
, JSTreeContext
*tc
, bool ge
, uintN adj
)
6040 : root(pn
), tc(tc
), genexp(ge
), adjust(adj
), funcLevel(0)
6044 bool transplant(JSParseNode
*pn
);
6048 * Any definitions nested within the comprehension expression of a generator
6049 * expression must move "down" one static level, which of course increases the
6050 * upvar-frame-skip count.
6053 BumpStaticLevel(JSParseNode
*pn
, JSTreeContext
*tc
)
6055 if (pn
->pn_cookie
!= FREE_UPVAR_COOKIE
) {
6056 uintN level
= UPVAR_FRAME_SKIP(pn
->pn_cookie
) + 1;
6058 JS_ASSERT(level
>= tc
->staticLevel
);
6059 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(level
, UPVAR_FRAME_SLOT(pn
->pn_cookie
));
6064 AdjustBlockId(JSParseNode
*pn
, uintN adjust
, JSTreeContext
*tc
)
6066 JS_ASSERT(pn
->pn_arity
== PN_LIST
|| pn
->pn_arity
== PN_FUNC
|| pn
->pn_arity
== PN_NAME
);
6067 pn
->pn_blockid
+= adjust
;
6068 if (pn
->pn_blockid
>= tc
->blockidGen
)
6069 tc
->blockidGen
= pn
->pn_blockid
+ 1;
6073 CompExprTransplanter::transplant(JSParseNode
*pn
)
6078 switch (pn
->pn_arity
) {
6080 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
6082 if (pn
->pn_pos
>= root
->pn_pos
)
6083 AdjustBlockId(pn
, adjust
, tc
);
6087 transplant(pn
->pn_kid1
);
6088 transplant(pn
->pn_kid2
);
6089 transplant(pn
->pn_kid3
);
6093 transplant(pn
->pn_left
);
6094 transplant(pn
->pn_right
);
6098 transplant(pn
->pn_kid
);
6104 * Only the first level of transplant recursion through functions needs
6105 * to reparent the funbox, since all descendant functions are correctly
6106 * linked under the top-most funbox. But every visit to this case needs
6107 * to update funbox->level.
6109 * Recall that funbox->level is the static level of the code containing
6110 * the definition or expression of the function and not the static level
6111 * of the function's body.
6113 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6115 funbox
->level
= tc
->staticLevel
+ funcLevel
;
6116 if (++funcLevel
== 1 && genexp
) {
6117 JSFunctionBox
*parent
= tc
->funbox
;
6119 JSFunctionBox
**funboxp
= &tc
->parent
->functionList
;
6120 while (*funboxp
!= funbox
)
6121 funboxp
= &(*funboxp
)->siblings
;
6122 *funboxp
= funbox
->siblings
;
6124 funbox
->parent
= parent
;
6125 funbox
->siblings
= parent
->kids
;
6126 parent
->kids
= funbox
;
6127 funbox
->level
= tc
->staticLevel
;
6133 transplant(pn
->maybeExpr());
6134 if (pn
->pn_arity
== PN_FUNC
)
6139 BumpStaticLevel(pn
, tc
);
6140 } else if (pn
->pn_used
) {
6141 JS_ASSERT(pn
->pn_op
!= JSOP_NOP
);
6142 JS_ASSERT(pn
->pn_cookie
== FREE_UPVAR_COOKIE
);
6144 JSDefinition
*dn
= pn
->pn_lexdef
;
6145 JS_ASSERT(dn
->pn_defn
);
6148 * Adjust the definition's block id only if it is a placeholder not
6149 * to the left of the root node, and if pn is the last use visited
6150 * in the comprehension expression (to avoid adjusting the blockid
6153 * Non-placeholder definitions within the comprehension expression
6154 * will be visited further below.
6156 if (dn
->isPlaceholder() && dn
->pn_pos
>= root
->pn_pos
&& dn
->dn_uses
== pn
) {
6158 BumpStaticLevel(dn
, tc
);
6159 AdjustBlockId(dn
, adjust
, tc
);
6162 JSAtom
*atom
= pn
->pn_atom
;
6164 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
6165 JS_ASSERT(!stmt
|| stmt
!= tc
->topStmt
);
6167 if (genexp
&& PN_OP(dn
) != JSOP_CALLEE
) {
6168 JS_ASSERT(!tc
->decls
.lookup(atom
));
6170 if (dn
->pn_pos
< root
->pn_pos
|| dn
->isPlaceholder()) {
6171 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, dn
->pn_atom
);
6175 if (dn
->pn_pos
>= root
->pn_pos
) {
6176 tc
->parent
->lexdeps
.remove(tc
->compiler
, atom
);
6178 JSDefinition
*dn2
= (JSDefinition
*)
6179 NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), dn
->pn_atom
, tc
);
6183 dn2
->pn_type
= dn
->pn_type
;
6184 dn2
->pn_pos
= root
->pn_pos
;
6185 dn2
->pn_defn
= true;
6186 dn2
->pn_dflags
|= PND_FORWARD
| PND_PLACEHOLDER
;
6188 JSParseNode
**pnup
= &dn
->dn_uses
;
6190 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_pos
>= root
->pn_pos
) {
6191 pnu
->pn_lexdef
= dn2
;
6192 dn2
->pn_dflags
|= pnu
->pn_dflags
& (PND_ASSIGNED
| PND_FUNARG
);
6193 pnup
= &pnu
->pn_link
;
6195 dn2
->dn_uses
= dn
->dn_uses
;
6196 dn
->dn_uses
= *pnup
;
6202 ALE_SET_DEFN(ale
, dn
);
6207 if (pn
->pn_pos
>= root
->pn_pos
)
6208 AdjustBlockId(pn
, adjust
, tc
);
6212 transplant(pn
->pn_tree
);
6219 * Starting from a |for| keyword after the first array initialiser element or
6220 * an expression in an open parenthesis, parse the tail of the comprehension
6221 * or generator expression signified by this |for| keyword in context.
6223 * Return null on failure, else return the top-most parse node for the array
6224 * comprehension or generator expression, with a unary node as the body of the
6225 * (possibly nested) for-loop, initialized by |type, op, kid|.
6227 static JSParseNode
*
6228 ComprehensionTail(JSParseNode
*kid
, uintN blockid
, JSTreeContext
*tc
,
6229 JSTokenType type
= TOK_SEMI
, JSOp op
= JSOP_NOP
)
6231 JSContext
*cx
= tc
->compiler
->context
;
6232 JSTokenStream
*ts
= TS(tc
->compiler
);
6235 JSParseNode
*pn
, *pn2
, *pn3
, **pnp
;
6236 JSStmtInfo stmtInfo
;
6241 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_FOR
);
6243 if (type
== TOK_SEMI
) {
6245 * Generator expression desugars to an immediately applied lambda that
6246 * yields the next value from a for-in loop (possibly nested, and with
6247 * optional if guard). Make pn be the TOK_LC body node.
6249 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6252 adjust
= pn
->pn_blockid
- blockid
;
6254 JS_ASSERT(type
== TOK_ARRAYPUSH
);
6257 * Make a parse-node and literal object representing the block scope of
6258 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6259 * aka the array initialiser case, has passed the blockid to claim for
6260 * the comprehension's block scope. We allocate that id or one above it
6261 * here, by calling js_PushLexicalScope.
6263 * In the case of a comprehension expression that has nested blocks
6264 * (e.g., let expressions), we will allocate a higher blockid but then
6265 * slide all blocks "to the right" to make room for the comprehension's
6268 adjust
= tc
->blockid();
6269 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6273 JS_ASSERT(blockid
<= pn
->pn_blockid
);
6274 JS_ASSERT(blockid
< tc
->blockidGen
);
6275 JS_ASSERT(tc
->bodyid
< blockid
);
6276 pn
->pn_blockid
= stmtInfo
.blockid
= blockid
;
6277 JS_ASSERT(adjust
< blockid
);
6278 adjust
= blockid
- adjust
;
6283 CompExprTransplanter
transplanter(kid
, tc
, type
== TOK_SEMI
, adjust
);
6284 transplanter
.transplant(kid
);
6288 data
.binder
= BindLet
;
6289 data
.let
.overflow
= JSMSG_ARRAY_INIT_TOO_BIG
;
6293 * FOR node is binary, left is loop control and right is body. Use
6294 * index to count each block-local let-variable on the left-hand side
6297 pn2
= NewParseNode(PN_BINARY
, tc
);
6301 pn2
->pn_op
= JSOP_ITER
;
6302 pn2
->pn_iflags
= JSITER_ENUMERATE
;
6303 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
6304 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
6305 pn2
->pn_iflags
|= JSITER_FOREACH
;
6309 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
6312 tt
= js_GetToken(cx
, ts
);
6314 #if JS_HAS_DESTRUCTURING
6317 ts
->flags
|= TSF_DESTRUCTURING
;
6318 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
6319 ts
->flags
&= ~TSF_DESTRUCTURING
;
6326 atom
= CURRENT_TOKEN(ts
).t_atom
;
6329 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6330 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6331 * in the operand stack frame. The code generator computes that,
6332 * and it tries to bind all names to slots, so we must let it do
6335 pn3
= NewBindingNode(ts
, atom
, tc
, true);
6341 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6342 JSMSG_NO_VARIABLE_NAME
);
6348 MUST_MATCH_TOKEN(TOK_IN
, JSMSG_IN_AFTER_FOR_NAME
);
6349 JSParseNode
*pn4
= Expr(cx
, ts
, tc
);
6352 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
6355 #if JS_HAS_DESTRUCTURING
6358 if (!CheckDestructuring(cx
, &data
, pn3
, NULL
, tc
))
6361 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
6362 /* Destructuring requires [key, value] enumeration in JS1.7. */
6363 if (pn3
->pn_type
!= TOK_RB
|| pn3
->pn_count
!= 2) {
6364 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6365 JSMSG_BAD_FOR_LEFTSIDE
);
6369 JS_ASSERT(pn2
->pn_op
== JSOP_ITER
);
6370 JS_ASSERT(pn2
->pn_iflags
& JSITER_ENUMERATE
);
6371 if (!(pn2
->pn_iflags
& JSITER_FOREACH
))
6372 pn2
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
6379 if (!data
.binder(cx
, &data
, atom
, tc
))
6386 pn2
->pn_left
= NewBinary(TOK_IN
, JSOP_NOP
, pn3
, pn4
, tc
);
6390 pnp
= &pn2
->pn_right
;
6391 } while (js_MatchToken(cx
, ts
, TOK_FOR
));
6393 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
6394 pn2
= NewParseNode(PN_TERNARY
, tc
);
6397 pn2
->pn_kid1
= Condition(cx
, ts
, tc
);
6401 pnp
= &pn2
->pn_kid2
;
6404 pn2
= NewParseNode(PN_UNARY
, tc
);
6407 pn2
->pn_type
= type
;
6412 if (type
== TOK_ARRAYPUSH
)
6417 #if JS_HAS_GENERATOR_EXPRS
6420 * Starting from a |for| keyword after an expression, parse the comprehension
6421 * tail completing this generator expression. Wrap the expression at kid in a
6422 * generator function that is immediately called to evaluate to the generator
6423 * iterator that is the value of this generator expression.
6425 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6426 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6427 * expression-statement node that constitutes the body of the |for| loop(s) in
6428 * the generator function.
6430 * Note how unlike Python, we do not evaluate the expression to the right of
6431 * the first |in| in the chain of |for| heads. Instead, a generator expression
6432 * is merely sugar for a generator function expression and its application.
6434 static JSParseNode
*
6435 GeneratorExpr(JSParseNode
*pn
, JSParseNode
*kid
, JSTreeContext
*tc
)
6437 /* Initialize pn, connecting it to kid. */
6438 JS_ASSERT(pn
->pn_arity
== PN_UNARY
);
6439 pn
->pn_type
= TOK_YIELD
;
6440 pn
->pn_op
= JSOP_YIELD
;
6441 pn
->pn_pos
= kid
->pn_pos
;
6443 pn
->pn_hidden
= JS_TRUE
;
6445 /* Make a new node for the desugared generator function. */
6446 JSParseNode
*genfn
= NewParseNode(PN_FUNC
, tc
);
6449 genfn
->pn_type
= TOK_FUNCTION
;
6450 genfn
->pn_op
= JSOP_LAMBDA
;
6451 JS_ASSERT(!genfn
->pn_body
);
6452 genfn
->pn_dflags
= PND_FUNARG
;
6455 JSTreeContext
gentc(tc
->compiler
);
6457 JSFunctionBox
*funbox
= EnterFunction(genfn
, tc
, &gentc
);
6462 * We assume conservatively that any deoptimization flag in tc->flags
6463 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6464 * propagate these flags into genfn. For code simplicity we also do
6465 * not detect if the flags were only set in the kid and could be
6466 * removed from tc->flags.
6468 gentc
.flags
|= TCF_FUN_IS_GENERATOR
| TCF_GENEXP_LAMBDA
|
6469 (tc
->flags
& (TCF_FUN_FLAGS
& ~TCF_FUN_PARAM_ARGUMENTS
));
6470 funbox
->tcflags
|= gentc
.flags
;
6471 genfn
->pn_funbox
= funbox
;
6472 genfn
->pn_blockid
= gentc
.bodyid
;
6474 JSParseNode
*body
= ComprehensionTail(pn
, tc
->blockid(), &gentc
);
6477 JS_ASSERT(!genfn
->pn_body
);
6478 genfn
->pn_body
= body
;
6479 genfn
->pn_pos
.begin
= body
->pn_pos
.begin
= kid
->pn_pos
.begin
;
6480 genfn
->pn_pos
.end
= body
->pn_pos
.end
= CURRENT_TOKEN(TS(tc
->compiler
)).pos
.end
;
6482 if (!LeaveFunction(genfn
, &gentc
, tc
))
6487 * Our result is a call expression that invokes the anonymous generator
6490 JSParseNode
*result
= NewParseNode(PN_LIST
, tc
);
6493 result
->pn_type
= TOK_LP
;
6494 result
->pn_op
= JSOP_CALL
;
6495 result
->pn_pos
.begin
= genfn
->pn_pos
.begin
;
6496 result
->initList(genfn
);
6500 static const char js_generator_str
[] = "generator";
6502 #endif /* JS_HAS_GENERATOR_EXPRS */
6503 #endif /* JS_HAS_GENERATORS */
6506 ArgumentList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6507 JSParseNode
*listNode
)
6511 ts
->flags
|= TSF_OPERAND
;
6512 matched
= js_MatchToken(cx
, ts
, TOK_RP
);
6513 ts
->flags
&= ~TSF_OPERAND
;
6516 JSParseNode
*argNode
= AssignExpr(cx
, ts
, tc
);
6519 #if JS_HAS_GENERATORS
6520 if (argNode
->pn_type
== TOK_YIELD
&&
6521 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6522 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6523 JSMSG_BAD_GENERATOR_SYNTAX
,
6528 #if JS_HAS_GENERATOR_EXPRS
6529 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
6530 JSParseNode
*pn
= NewParseNode(PN_UNARY
, tc
);
6533 argNode
= GeneratorExpr(pn
, argNode
, tc
);
6536 if (listNode
->pn_count
> 1 ||
6537 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6538 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6539 JSMSG_BAD_GENERATOR_SYNTAX
,
6545 listNode
->append(argNode
);
6546 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
6548 if (js_GetToken(cx
, ts
) != TOK_RP
) {
6549 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6550 JSMSG_PAREN_AFTER_ARGS
);
6557 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6558 static JSParseNode
*
6559 CheckForImmediatelyAppliedLambda(JSParseNode
*pn
)
6561 while (pn
->pn_type
== TOK_RP
)
6563 if (pn
->pn_type
== TOK_FUNCTION
) {
6564 JS_ASSERT(pn
->pn_arity
== PN_FUNC
);
6566 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6567 JS_ASSERT(((JSFunction
*) funbox
->object
)->flags
& JSFUN_LAMBDA
);
6568 if (!(funbox
->tcflags
& TCF_FUN_USES_ARGUMENTS
))
6569 pn
->pn_dflags
&= ~PND_FUNARG
;
6574 static JSParseNode
*
6575 MemberExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6576 JSBool allowCallSyntax
)
6578 JSParseNode
*pn
, *pn2
, *pn3
;
6581 JS_CHECK_RECURSION(cx
, return NULL
);
6583 /* Check for new expression first. */
6584 ts
->flags
|= TSF_OPERAND
;
6585 tt
= js_GetToken(cx
, ts
);
6586 ts
->flags
&= ~TSF_OPERAND
;
6587 if (tt
== TOK_NEW
) {
6588 pn
= NewParseNode(PN_LIST
, tc
);
6591 pn2
= MemberExpr(cx
, ts
, tc
, JS_FALSE
);
6594 pn2
= CheckForImmediatelyAppliedLambda(pn2
);
6595 pn
->pn_op
= JSOP_NEW
;
6597 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
6599 if (js_MatchToken(cx
, ts
, TOK_LP
) && !ArgumentList(cx
, ts
, tc
, pn
))
6601 if (pn
->pn_count
> ARGC_LIMIT
) {
6602 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6603 JSMSG_TOO_MANY_CON_ARGS
);
6606 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
6608 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
6612 if (pn
->pn_type
== TOK_ANYNAME
||
6613 pn
->pn_type
== TOK_AT
||
6614 pn
->pn_type
== TOK_DBLCOLON
) {
6615 pn2
= NewOrRecycledNode(tc
);
6618 pn2
->pn_type
= TOK_UNARYOP
;
6619 pn2
->pn_pos
= pn
->pn_pos
;
6620 pn2
->pn_op
= JSOP_XMLNAME
;
6621 pn2
->pn_arity
= PN_UNARY
;
6627 while ((tt
= js_GetToken(cx
, ts
)) > TOK_EOF
) {
6628 if (tt
== TOK_DOT
) {
6629 pn2
= NewNameNode(cx
, ts
, NULL
, tc
);
6632 #if JS_HAS_XML_SUPPORT
6633 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
6634 tt
= js_GetToken(cx
, ts
);
6635 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
6636 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
6640 if (tt
== TOK_NAME
) {
6641 pn2
->pn_op
= JSOP_GETPROP
;
6643 pn2
->pn_atom
= pn3
->pn_atom
;
6644 RecycleTree(pn3
, tc
);
6646 if (TOKEN_TYPE_IS_XML(tt
)) {
6647 pn2
->pn_type
= TOK_LB
;
6648 pn2
->pn_op
= JSOP_GETELEM
;
6649 } else if (tt
== TOK_RP
) {
6650 JSParseNode
*group
= pn3
;
6652 /* Recycle the useless TOK_RP node. */
6653 pn3
= group
->pn_kid
;
6654 group
->pn_kid
= NULL
;
6655 RecycleTree(group
, tc
);
6656 pn2
->pn_type
= TOK_FILTER
;
6657 pn2
->pn_op
= JSOP_FILTER
;
6659 /* A filtering predicate is like a with statement. */
6660 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6662 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6663 JSMSG_NAME_AFTER_DOT
);
6666 pn2
->pn_arity
= PN_BINARY
;
6668 pn2
->pn_right
= pn3
;
6671 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
6672 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NAME_AFTER_DOT
);
6673 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
6674 pn2
->pn_op
= JSOP_GETPROP
;
6676 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
6678 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6679 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6680 #if JS_HAS_XML_SUPPORT
6681 } else if (tt
== TOK_DBLDOT
) {
6682 pn2
= NewParseNode(PN_BINARY
, tc
);
6685 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
6686 tt
= js_GetToken(cx
, ts
);
6687 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
6688 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
6692 if (tt
== TOK_NAME
) {
6693 pn3
->pn_type
= TOK_STRING
;
6694 pn3
->pn_arity
= PN_NULLARY
;
6695 pn3
->pn_op
= JSOP_QNAMEPART
;
6696 } else if (!TOKEN_TYPE_IS_XML(tt
)) {
6697 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6698 JSMSG_NAME_AFTER_DOT
);
6701 pn2
->pn_op
= JSOP_DESCENDANTS
;
6703 pn2
->pn_right
= pn3
;
6704 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6705 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6707 } else if (tt
== TOK_LB
) {
6708 pn2
= NewParseNode(PN_BINARY
, tc
);
6711 pn3
= Expr(cx
, ts
, tc
);
6715 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
6716 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6717 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6720 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6721 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6722 * the interpreter from fast property access. However, if the
6723 * bracketed string is a uint32, we rewrite pn3 to be a number
6724 * instead of a string.
6727 if (pn3
->pn_type
== TOK_STRING
) {
6730 if (!js_IdIsIndex(ATOM_TO_JSID(pn3
->pn_atom
), &index
)) {
6731 pn2
->pn_type
= TOK_DOT
;
6732 pn2
->pn_op
= JSOP_GETPROP
;
6733 pn2
->pn_arity
= PN_NAME
;
6735 pn2
->pn_atom
= pn3
->pn_atom
;
6738 pn3
->pn_type
= TOK_NUMBER
;
6739 pn3
->pn_op
= JSOP_DOUBLE
;
6740 pn3
->pn_dval
= index
;
6742 pn2
->pn_op
= JSOP_GETELEM
;
6744 pn2
->pn_right
= pn3
;
6746 } else if (allowCallSyntax
&& tt
== TOK_LP
) {
6747 pn2
= NewParseNode(PN_LIST
, tc
);
6750 pn2
->pn_op
= JSOP_CALL
;
6752 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
6753 pn
= CheckForImmediatelyAppliedLambda(pn
);
6754 if (pn
->pn_op
== JSOP_NAME
) {
6755 if (pn
->pn_atom
== cx
->runtime
->atomState
.evalAtom
) {
6756 /* Select JSOP_EVAL and flag tc as heavyweight. */
6757 pn2
->pn_op
= JSOP_EVAL
;
6758 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6760 } else if (pn
->pn_op
== JSOP_GETPROP
) {
6761 if (pn
->pn_atom
== cx
->runtime
->atomState
.applyAtom
||
6762 pn
->pn_atom
== cx
->runtime
->atomState
.callAtom
) {
6763 /* Select JSOP_APPLY given foo.apply(...). */
6764 pn2
->pn_op
= JSOP_APPLY
;
6769 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6771 if (!ArgumentList(cx
, ts
, tc
, pn2
))
6773 if (pn2
->pn_count
> ARGC_LIMIT
) {
6774 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6775 JSMSG_TOO_MANY_FUN_ARGS
);
6778 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6786 if (tt
== TOK_ERROR
)
6791 static JSParseNode
*
6792 BracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6798 * Always accept the 'in' operator in a parenthesized expression,
6799 * where it's unambiguous, even if we might be parsing the init of a
6802 oldflags
= tc
->flags
;
6803 tc
->flags
&= ~TCF_IN_FOR_INIT
;
6804 pn
= Expr(cx
, ts
, tc
);
6805 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
6809 #if JS_HAS_XML_SUPPORT
6811 static JSParseNode
*
6812 EndBracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6816 pn
= BracketedExpr(cx
, ts
, tc
);
6820 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ATTR_EXPR
);
6825 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
6827 * AttributeIdentifier:
6828 * @ PropertySelector
6829 * @ QualifiedIdentifier
6836 * QualifiedIdentifier:
6837 * PropertySelector :: PropertySelector
6838 * PropertySelector :: [ Expression ]
6840 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
6842 * AttributeIdentifier:
6843 * @ QualifiedIdentifier
6850 * QualifiedIdentifier:
6851 * PropertySelector :: PropertySelector
6852 * PropertySelector :: [ Expression ]
6855 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
6856 * for that rule to result in a name node, but ECMA-357 extends the grammar
6857 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
6859 * QualifiedIdentifier:
6860 * PropertySelector QualifiedSuffix
6863 * :: PropertySelector
6867 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
6869 * PrimaryExpression:
6870 * Identifier QualifiedSuffix
6872 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
6873 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
6875 static JSParseNode
*
6876 PropertySelector(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6880 pn
= NewParseNode(PN_NULLARY
, tc
);
6883 if (pn
->pn_type
== TOK_STAR
) {
6884 pn
->pn_type
= TOK_ANYNAME
;
6885 pn
->pn_op
= JSOP_ANYNAME
;
6886 pn
->pn_atom
= cx
->runtime
->atomState
.starAtom
;
6888 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
6889 pn
->pn_op
= JSOP_QNAMEPART
;
6890 pn
->pn_arity
= PN_NAME
;
6891 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
6892 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
6897 static JSParseNode
*
6898 QualifiedSuffix(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
6901 JSParseNode
*pn2
, *pn3
;
6904 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_DBLCOLON
);
6905 pn2
= NewNameNode(cx
, ts
, NULL
, tc
);
6909 /* Left operand of :: must be evaluated if it is an identifier. */
6910 if (pn
->pn_op
== JSOP_QNAMEPART
)
6911 pn
->pn_op
= JSOP_NAME
;
6913 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
6914 tt
= js_GetToken(cx
, ts
);
6915 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
6916 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
6917 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
6918 pn2
->pn_op
= JSOP_QNAMECONST
;
6919 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6920 pn2
->pn_atom
= (tt
== TOK_STAR
)
6921 ? cx
->runtime
->atomState
.starAtom
6922 : CURRENT_TOKEN(ts
).t_atom
;
6924 pn2
->pn_cookie
= FREE_UPVAR_COOKIE
;
6929 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6930 JSMSG_SYNTAX_ERROR
);
6933 pn3
= EndBracketedExpr(cx
, ts
, tc
);
6937 pn2
->pn_op
= JSOP_QNAME
;
6938 pn2
->pn_arity
= PN_BINARY
;
6939 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6940 pn2
->pn_pos
.end
= pn3
->pn_pos
.end
;
6942 pn2
->pn_right
= pn3
;
6946 static JSParseNode
*
6947 QualifiedIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6951 pn
= PropertySelector(cx
, ts
, tc
);
6954 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
))
6955 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
6959 static JSParseNode
*
6960 AttributeIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6962 JSParseNode
*pn
, *pn2
;
6965 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_AT
);
6966 pn
= NewParseNode(PN_UNARY
, tc
);
6969 pn
->pn_op
= JSOP_TOATTRNAME
;
6970 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
6971 tt
= js_GetToken(cx
, ts
);
6972 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
6973 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
6974 pn2
= QualifiedIdentifier(cx
, ts
, tc
);
6975 } else if (tt
== TOK_LB
) {
6976 pn2
= EndBracketedExpr(cx
, ts
, tc
);
6978 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6979 JSMSG_SYNTAX_ERROR
);
6989 * Make a TOK_LC unary node whose pn_kid is an expression.
6991 static JSParseNode
*
6992 XMLExpr(JSContext
*cx
, JSTokenStream
*ts
, JSBool inTag
, JSTreeContext
*tc
)
6994 JSParseNode
*pn
, *pn2
;
6997 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LC
);
6998 pn
= NewParseNode(PN_UNARY
, tc
);
7003 * Turn off XML tag mode, but don't restore it after parsing this braced
7004 * expression. Instead, simply restore ts's old flags. This is required
7005 * because XMLExpr is called both from within a tag, and from within text
7006 * contained in an element, but outside of any start, end, or point tag.
7008 oldflags
= ts
->flags
;
7009 ts
->flags
= oldflags
& ~TSF_XMLTAGMODE
;
7010 pn2
= Expr(cx
, ts
, tc
);
7014 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_XML_EXPR
);
7015 ts
->flags
= oldflags
;
7017 pn
->pn_op
= inTag
? JSOP_XMLTAGEXPR
: JSOP_XMLELTEXPR
;
7022 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7023 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7024 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7025 * child of a container tag.
7027 static JSParseNode
*
7028 XMLAtomNode(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7033 pn
= NewParseNode(PN_NULLARY
, tc
);
7036 tp
= &CURRENT_TOKEN(ts
);
7037 pn
->pn_op
= tp
->t_op
;
7038 pn
->pn_atom
= tp
->t_atom
;
7039 if (tp
->type
== TOK_XMLPI
)
7040 pn
->pn_atom2
= tp
->t_atom2
;
7045 * Parse the productions:
7048 * XMLName XMLNameExpr?
7049 * { Expr } XMLNameExpr?
7051 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7052 * a list of names and/or expressions, a single expression, or a single name.
7053 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7056 static JSParseNode
*
7057 XMLNameExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7059 JSParseNode
*pn
, *pn2
, *list
;
7064 tt
= CURRENT_TOKEN(ts
).type
;
7066 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7070 JS_ASSERT(tt
== TOK_XMLNAME
);
7071 pn2
= XMLAtomNode(cx
, ts
, tc
);
7080 list
= NewParseNode(PN_LIST
, tc
);
7083 list
->pn_type
= TOK_XMLNAME
;
7084 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7086 list
->pn_xflags
= PNX_CANTFOLD
;
7089 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7092 } while ((tt
= js_GetToken(cx
, ts
)) == TOK_XMLNAME
|| tt
== TOK_LC
);
7099 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7100 * at compile time into a JSXML tree.
7102 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7103 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7104 : (pn)->pn_type != TOK_LC)
7107 * Parse the productions:
7111 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7112 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7114 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7115 * produces a list of name and attribute values and/or braced expressions, a
7116 * single expression, or a single name.
7118 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7119 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7120 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7121 * we parsed exactly one expression.
7123 static JSParseNode
*
7124 XMLTagContent(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7125 JSTokenType tagtype
, JSAtom
**namep
)
7127 JSParseNode
*pn
, *pn2
, *list
;
7130 pn
= XMLNameExpr(cx
, ts
, tc
);
7133 *namep
= (pn
->pn_arity
== PN_NULLARY
) ? pn
->pn_atom
: NULL
;
7136 while (js_MatchToken(cx
, ts
, TOK_XMLSPACE
)) {
7137 tt
= js_GetToken(cx
, ts
);
7138 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7143 pn2
= XMLNameExpr(cx
, ts
, tc
);
7147 list
= NewParseNode(PN_LIST
, tc
);
7150 list
->pn_type
= tagtype
;
7151 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7156 if (!XML_FOLDABLE(pn2
))
7157 pn
->pn_xflags
|= PNX_CANTFOLD
;
7159 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7160 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_NO_ASSIGN_IN_XML_ATTR
);
7161 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7163 tt
= js_GetToken(cx
, ts
);
7164 if (tt
== TOK_XMLATTR
) {
7165 pn2
= XMLAtomNode(cx
, ts
, tc
);
7166 } else if (tt
== TOK_LC
) {
7167 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7168 pn
->pn_xflags
|= PNX_CANTFOLD
;
7170 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7171 JSMSG_BAD_XML_ATTR_VALUE
);
7176 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7183 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7185 if ((tt) <= TOK_EOF) { \
7186 if ((tt) == TOK_EOF) { \
7187 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7188 JSMSG_END_OF_XML_SOURCE); \
7194 static JSParseNode
*
7195 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7199 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7200 * that opens the end tag for the container.
7203 XMLElementContent(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
7210 ts
->flags
&= ~TSF_XMLTAGMODE
;
7212 ts
->flags
|= TSF_XMLTEXTMODE
;
7213 tt
= js_GetToken(cx
, ts
);
7214 ts
->flags
&= ~TSF_XMLTEXTMODE
;
7215 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7217 JS_ASSERT(tt
== TOK_XMLSPACE
|| tt
== TOK_XMLTEXT
);
7218 textAtom
= CURRENT_TOKEN(ts
).t_atom
;
7220 /* Non-zero-length XML text scanned. */
7221 pn2
= XMLAtomNode(cx
, ts
, tc
);
7224 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7228 ts
->flags
|= TSF_OPERAND
;
7229 tt
= js_GetToken(cx
, ts
);
7230 ts
->flags
&= ~TSF_OPERAND
;
7231 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7232 if (tt
== TOK_XMLETAGO
)
7236 pn2
= XMLExpr(cx
, ts
, JS_FALSE
, tc
);
7237 pn
->pn_xflags
|= PNX_CANTFOLD
;
7238 } else if (tt
== TOK_XMLSTAGO
) {
7239 pn2
= XMLElementOrList(cx
, ts
, tc
, JS_FALSE
);
7241 pn2
->pn_xflags
&= ~PNX_XMLROOT
;
7242 pn
->pn_xflags
|= pn2
->pn_xflags
;
7245 JS_ASSERT(tt
== TOK_XMLCDATA
|| tt
== TOK_XMLCOMMENT
||
7247 pn2
= XMLAtomNode(cx
, ts
, tc
);
7251 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7255 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLETAGO
);
7256 ts
->flags
|= TSF_XMLTAGMODE
;
7261 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7263 static JSParseNode
*
7264 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7267 JSParseNode
*pn
, *pn2
, *list
;
7269 JSAtom
*startAtom
, *endAtom
;
7271 JS_CHECK_RECURSION(cx
, return NULL
);
7273 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLSTAGO
);
7274 pn
= NewParseNode(PN_LIST
, tc
);
7278 ts
->flags
|= TSF_XMLTAGMODE
;
7279 tt
= js_GetToken(cx
, ts
);
7280 if (tt
== TOK_ERROR
)
7283 if (tt
== TOK_XMLNAME
|| tt
== TOK_LC
) {
7285 * XMLElement. Append the tag and its contents, if any, to pn.
7287 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLSTAGO
, &startAtom
);
7290 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7292 tt
= js_GetToken(cx
, ts
);
7293 if (tt
== TOK_XMLPTAGC
) {
7294 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7295 if (pn2
->pn_type
== TOK_XMLSTAGO
) {
7297 RecycleTree(pn
, tc
);
7300 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
||
7301 pn2
->pn_type
== TOK_LC
);
7303 if (!XML_FOLDABLE(pn2
))
7304 pn
->pn_xflags
|= PNX_CANTFOLD
;
7306 pn
->pn_type
= TOK_XMLPTAGC
;
7307 pn
->pn_xflags
|= PNX_XMLROOT
;
7309 /* We had better have a tag-close (>) at this point. */
7310 if (tt
!= TOK_XMLTAGC
) {
7311 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7312 JSMSG_BAD_XML_TAG_SYNTAX
);
7315 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7317 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7318 if (pn2
->pn_type
!= TOK_XMLSTAGO
) {
7320 if (!XML_FOLDABLE(pn2
))
7321 pn
->pn_xflags
|= PNX_CANTFOLD
;
7323 pn
= NewParseNode(PN_LIST
, tc
);
7328 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7329 pn
->pn_type
= TOK_XMLELEM
;
7330 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
7332 if (!XML_FOLDABLE(pn2
))
7333 pn
->pn_xflags
|= PNX_CANTFOLD
;
7334 pn
->pn_xflags
|= PNX_XMLROOT
;
7336 /* Get element contents and delimiting end-tag-open sequence. */
7337 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7340 tt
= js_GetToken(cx
, ts
);
7341 XML_CHECK_FOR_ERROR_AND_EOF(tt
, NULL
);
7342 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7343 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7344 JSMSG_BAD_XML_TAG_SYNTAX
);
7348 /* Parse end tag; check mismatch at compile-time if we can. */
7349 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLETAGO
, &endAtom
);
7352 if (pn2
->pn_type
== TOK_XMLETAGO
) {
7353 /* Oops, end tag has attributes! */
7354 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7355 JSMSG_BAD_XML_TAG_SYNTAX
);
7358 if (endAtom
&& startAtom
&& endAtom
!= startAtom
) {
7359 JSString
*str
= ATOM_TO_STRING(startAtom
);
7361 /* End vs. start tag name mismatch: point to the tag name. */
7362 js_ReportCompileErrorNumber(cx
, ts
, pn2
,
7363 JSREPORT_UC
| JSREPORT_ERROR
,
7364 JSMSG_XML_TAG_NAME_MISMATCH
,
7365 JSSTRING_CHARS(str
));
7369 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7370 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
|| pn2
->pn_type
== TOK_LC
);
7371 list
= NewParseNode(PN_LIST
, tc
);
7374 list
->pn_type
= TOK_XMLETAGO
;
7375 list
->initList(pn2
);
7377 if (!XML_FOLDABLE(pn2
)) {
7378 list
->pn_xflags
|= PNX_CANTFOLD
;
7379 pn
->pn_xflags
|= PNX_CANTFOLD
;
7382 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7383 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_TAG_SYNTAX
);
7386 /* Set pn_op now that pn has been updated to its final value. */
7387 pn
->pn_op
= JSOP_TOXML
;
7388 } else if (allowList
&& tt
== TOK_XMLTAGC
) {
7389 /* XMLList Initialiser. */
7390 pn
->pn_type
= TOK_XMLLIST
;
7391 pn
->pn_op
= JSOP_TOXMLLIST
;
7393 pn
->pn_xflags
|= PNX_XMLROOT
;
7394 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7397 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_LIST_SYNTAX
);
7399 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7400 JSMSG_BAD_XML_NAME_SYNTAX
);
7404 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7405 ts
->flags
&= ~TSF_XMLTAGMODE
;
7409 static JSParseNode
*
7410 XMLElementOrListRoot(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7417 * Force XML support to be enabled so that comments and CDATA literals
7418 * are recognized, instead of <! followed by -- starting an HTML comment
7419 * to end of line (used in script tags to hide content from old browsers
7420 * that don't recognize <script>).
7422 oldopts
= JS_SetOptions(cx
, cx
->options
| JSOPTION_XML
);
7423 pn
= XMLElementOrList(cx
, ts
, tc
, allowList
);
7424 JS_SetOptions(cx
, oldopts
);
7429 JSCompiler::parseXMLText(JSObject
*chain
, bool allowList
)
7432 * Push a compiler frame if we have no frames, or if the top frame is a
7433 * lightweight function activation, or if its scope chain doesn't match
7434 * the one passed to us.
7436 JSTreeContext
tc(this);
7437 tc
.scopeChain
= chain
;
7439 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7440 TS(this)->flags
|= TSF_OPERAND
| TSF_XMLONLYMODE
;
7441 JSTokenType tt
= js_GetToken(context
, TS(this));
7442 TS(this)->flags
&= ~TSF_OPERAND
;
7445 if (tt
!= TOK_XMLSTAGO
) {
7446 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
7447 JSMSG_BAD_XML_MARKUP
);
7450 pn
= XMLElementOrListRoot(context
, TS(this), &tc
, allowList
);
7453 TS(this)->flags
&= ~TSF_XMLONLYMODE
;
7457 #endif /* JS_HAS_XMLSUPPORT */
7459 #if JS_HAS_BLOCK_SCOPE
7461 * Check whether blockid is an active scoping statement in tc. This code is
7462 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7463 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7464 * and let blocks and expressions (not let declarations).
7466 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7467 * due to hoisting, let in a for loop head, let block, or let expression acts
7468 * like Scheme's let: initializers are evaluated without the new let bindings
7471 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7472 * bindings push on the front of the tc->decls JSAtomList (either the singular
7473 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7474 * scope bindings of the same name.
7476 * This simplifies binding lookup code at the price of a linear search here,
7477 * but only if code uses let (var predominates), and even then this function's
7478 * loop iterates more than once only in crazy cases.
7481 BlockIdInScope(uintN blockid
, JSTreeContext
*tc
)
7483 if (blockid
> tc
->blockid())
7485 for (JSStmtInfo
*stmt
= tc
->topScopeStmt
; stmt
; stmt
= stmt
->downScope
) {
7486 if (stmt
->blockid
== blockid
)
7493 static JSParseNode
*
7494 PrimaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7495 JSTokenType tt
, JSBool afterDot
)
7497 JSParseNode
*pn
, *pn2
, *pn3
;
7500 JS_CHECK_RECURSION(cx
, return NULL
);
7502 #if JS_HAS_GETTER_SETTER
7503 if (tt
== TOK_NAME
) {
7504 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
7505 if (tt
== TOK_ERROR
)
7512 #if JS_HAS_XML_SUPPORT
7513 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7514 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
7515 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7516 pn2
= NewParseNode(PN_NULLARY
, tc
);
7519 pn2
->pn_type
= TOK_FUNCTION
;
7520 pn
= QualifiedSuffix(cx
, ts
, pn2
, tc
);
7525 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7527 pn
= FunctionExpr(cx
, ts
, tc
);
7537 pn
= NewParseNode(PN_LIST
, tc
);
7540 pn
->pn_type
= TOK_RB
;
7541 pn
->pn_op
= JSOP_NEWINIT
;
7544 #if JS_HAS_GENERATORS
7545 pn
->pn_blockid
= tc
->blockidGen
;
7548 ts
->flags
|= TSF_OPERAND
;
7549 matched
= js_MatchToken(cx
, ts
, TOK_RB
);
7550 ts
->flags
&= ~TSF_OPERAND
;
7552 for (index
= 0; ; index
++) {
7553 if (index
== ARRAY_INIT_LIMIT
) {
7554 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7555 JSMSG_ARRAY_INIT_TOO_BIG
);
7559 ts
->flags
|= TSF_OPERAND
;
7560 tt
= js_PeekToken(cx
, ts
);
7561 ts
->flags
&= ~TSF_OPERAND
;
7563 pn
->pn_xflags
|= PNX_ENDCOMMA
;
7567 if (tt
== TOK_COMMA
) {
7568 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7569 js_MatchToken(cx
, ts
, TOK_COMMA
);
7570 pn2
= NewParseNode(PN_NULLARY
, tc
);
7571 pn
->pn_xflags
|= PNX_HOLEY
;
7573 pn2
= AssignExpr(cx
, ts
, tc
);
7579 if (tt
!= TOK_COMMA
) {
7580 /* If we didn't already match TOK_COMMA in above case. */
7581 if (!js_MatchToken(cx
, ts
, TOK_COMMA
))
7586 #if JS_HAS_GENERATORS
7588 * At this point, (index == 0 && pn->pn_count != 0) implies one
7589 * element initialiser was parsed.
7591 * An array comprehension of the form:
7593 * [i * j for (i in o) for (j in p) if (i != j)]
7595 * translates to roughly the following let expression:
7597 * let (array = new Array, i, j) {
7598 * for (i in o) let {
7606 * where array is a nameless block-local variable. The "roughly"
7607 * means that an implementation may optimize away the array.push.
7608 * An array comprehension opens exactly one block scope, no matter
7609 * how many for heads it contains.
7611 * Each let () {...} or for (let ...) ... compiles to:
7613 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7615 * where <o> is a literal object representing the block scope,
7616 * with <n> properties, naming each var declared in the block.
7618 * Each var declaration in a let-block binds a name in <o> at
7619 * compile time, and allocates a slot on the operand stack at
7620 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7621 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7622 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7623 * local slot's stack index from fp->spbase.
7625 * The array comprehension iteration step, array.push(i * j) in
7626 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7627 * where <array> is the index of array's stack slot.
7630 pn
->pn_count
!= 0 &&
7631 js_MatchToken(cx
, ts
, TOK_FOR
)) {
7632 JSParseNode
*pnexp
, *pntop
;
7634 /* Relabel pn as an array comprehension node. */
7635 pn
->pn_type
= TOK_ARRAYCOMP
;
7638 * Remove the comprehension expression from pn's linked list
7639 * and save it via pnexp. We'll re-install it underneath the
7640 * ARRAYPUSH node after we parse the rest of the comprehension.
7643 JS_ASSERT(pn
->pn_count
== 1 || pn
->pn_count
== 2);
7644 pn
->pn_tail
= (--pn
->pn_count
== 1)
7645 ? &pn
->pn_head
->pn_next
7647 *pn
->pn_tail
= NULL
;
7649 pntop
= ComprehensionTail(pnexp
, pn
->pn_blockid
, tc
,
7650 TOK_ARRAYPUSH
, JSOP_ARRAYPUSH
);
7655 #endif /* JS_HAS_GENERATORS */
7657 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
7659 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7668 pn
= NewParseNode(PN_LIST
, tc
);
7671 pn
->pn_type
= TOK_RC
;
7672 pn
->pn_op
= JSOP_NEWINIT
;
7675 afterComma
= JS_FALSE
;
7677 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7678 tt
= js_GetToken(cx
, ts
);
7679 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7682 pn3
= NewParseNode(PN_NULLARY
, tc
);
7684 pn3
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
7687 #if JS_HAS_GETTER_SETTER
7691 atom
= CURRENT_TOKEN(ts
).t_atom
;
7692 if (atom
== cx
->runtime
->atomState
.getAtom
)
7694 else if (atom
== cx
->runtime
->atomState
.setAtom
)
7699 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7700 tt
= js_GetToken(cx
, ts
);
7701 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7702 if (tt
!= TOK_NAME
) {
7706 pn3
= NewNameNode(cx
, ts
, CURRENT_TOKEN(ts
).t_atom
, tc
);
7710 /* We have to fake a 'function' token here. */
7711 CURRENT_TOKEN(ts
).t_op
= JSOP_NOP
;
7712 CURRENT_TOKEN(ts
).type
= TOK_FUNCTION
;
7713 pn2
= FunctionExpr(cx
, ts
, tc
);
7714 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pn2
, tc
);
7720 pn3
= NewParseNode(PN_NULLARY
, tc
);
7722 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7726 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
7729 JSMSG_TRAILING_COMMA
)) {
7734 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7739 tt
= js_GetToken(cx
, ts
);
7740 #if JS_HAS_GETTER_SETTER
7741 if (tt
== TOK_NAME
) {
7742 tt
= CheckGetterOrSetter(cx
, ts
, TOK_COLON
);
7743 if (tt
== TOK_ERROR
)
7748 if (tt
!= TOK_COLON
) {
7749 #if JS_HAS_DESTRUCTURING_SHORTHAND
7750 if (tt
!= TOK_COMMA
&& tt
!= TOK_RC
) {
7752 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7753 JSMSG_COLON_AFTER_ID
);
7755 #if JS_HAS_DESTRUCTURING_SHORTHAND
7759 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7760 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7763 pn
->pn_xflags
|= PNX_DESTRUCT
;
7765 if (pnval
->pn_type
== TOK_NAME
) {
7766 pnval
->pn_arity
= PN_NAME
;
7767 InitNameNodeCommon(pnval
, tc
);
7772 op
= CURRENT_TOKEN(ts
).t_op
;
7773 pnval
= AssignExpr(cx
, ts
, tc
);
7776 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pnval
, tc
);
7777 #if JS_HAS_GETTER_SETTER
7784 tt
= js_GetToken(cx
, ts
);
7787 if (tt
!= TOK_COMMA
) {
7788 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7789 JSMSG_CURLY_AFTER_LIST
);
7792 afterComma
= JS_TRUE
;
7796 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7800 #if JS_HAS_BLOCK_SCOPE
7802 pn
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
7808 #if JS_HAS_SHARP_VARS
7810 pn
= NewParseNode(PN_UNARY
, tc
);
7813 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
7814 ts
->flags
|= TSF_OPERAND
;
7815 tt
= js_GetToken(cx
, ts
);
7816 ts
->flags
&= ~TSF_OPERAND
;
7817 if (tt
== TOK_USESHARP
|| tt
== TOK_DEFSHARP
||
7818 #if JS_HAS_XML_SUPPORT
7819 tt
== TOK_STAR
|| tt
== TOK_AT
||
7820 tt
== TOK_XMLSTAGO
/* XXXbe could be sharp? */ ||
7822 tt
== TOK_STRING
|| tt
== TOK_NUMBER
|| tt
== TOK_PRIMARY
) {
7823 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7824 JSMSG_BAD_SHARP_VAR_DEF
);
7827 pn
->pn_kid
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
7830 tc
->flags
|= TCF_HAS_SHARPS
;
7834 /* Check for forward/dangling references at runtime, to allow eval. */
7835 pn
= NewParseNode(PN_NULLARY
, tc
);
7838 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
7839 tc
->flags
|= TCF_HAS_SHARPS
;
7841 #endif /* JS_HAS_SHARP_VARS */
7847 pn
= NewParseNode(PN_UNARY
, tc
);
7850 pn2
= ParenExpr(cx
, ts
, tc
, pn
, &genexp
);
7855 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
7857 /* Check if parentheses were unnecessary. */
7858 if (pn2
->pn_type
== TOK_RP
||
7859 (js_CodeSpec
[pn2
->pn_op
].prec
>= js_CodeSpec
[JSOP_GETPROP
].prec
&&
7861 RecycleTree(pn
, tc
);
7864 pn
->pn_type
= TOK_RP
;
7867 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7871 #if JS_HAS_XML_SUPPORT
7873 pn
= QualifiedIdentifier(cx
, ts
, tc
);
7879 pn
= AttributeIdentifier(cx
, ts
, tc
);
7885 pn
= XMLElementOrListRoot(cx
, ts
, tc
, JS_TRUE
);
7889 #endif /* JS_HAS_XML_SUPPORT */
7892 #if JS_HAS_SHARP_VARS
7896 #if JS_HAS_XML_SUPPORT
7898 case TOK_XMLCOMMENT
:
7901 pn
= NewParseNode(PN_NULLARY
, tc
);
7904 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7905 #if JS_HAS_XML_SUPPORT
7906 if (tt
== TOK_XMLPI
)
7907 pn
->pn_atom2
= CURRENT_TOKEN(ts
).t_atom2
;
7910 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
7914 pn
= NewNameNode(cx
, ts
, CURRENT_TOKEN(ts
).t_atom
, tc
);
7917 JS_ASSERT(CURRENT_TOKEN(ts
).t_op
== JSOP_NAME
);
7918 pn
->pn_op
= JSOP_NAME
;
7920 if ((tc
->flags
& (TCF_IN_FUNCTION
| TCF_FUN_PARAM_ARGUMENTS
)) == TCF_IN_FUNCTION
&&
7921 pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
) {
7923 * Flag arguments usage so we can avoid unsafe optimizations such
7924 * as formal parameter assignment analysis (because of the hated
7925 * feature whereby arguments alias formals). We do this even for
7926 * a reference of the form foo.arguments, which ancient code may
7927 * still use instead of arguments (more hate).
7929 NoteArgumentsUse(tc
);
7932 * Bind early to JSOP_ARGUMENTS to relieve later code from having
7933 * to do this work (new rule for the emitter to count on).
7935 if (!afterDot
&& !(ts
->flags
& TSF_DESTRUCTURING
) && !tc
->inStatement(STMT_WITH
)) {
7936 pn
->pn_op
= JSOP_ARGUMENTS
;
7937 pn
->pn_dflags
|= PND_BOUND
;
7939 } else if ((!afterDot
7940 #if JS_HAS_XML_SUPPORT
7941 || js_PeekToken(cx
, ts
) == TOK_DBLCOLON
7943 ) && !(ts
->flags
& TSF_DESTRUCTURING
)) {
7944 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, pn
->pn_atom
, NULL
);
7945 if (!stmt
|| stmt
->type
!= STMT_WITH
) {
7948 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
7951 #if JS_HAS_BLOCK_SCOPE
7952 if (dn
->isLet() && !BlockIdInScope(dn
->pn_blockid
, tc
))
7960 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
7965 * No definition before this use in any lexical scope.
7966 * Add a mapping in tc->lexdeps from pn->pn_atom to a
7967 * new node for the forward-referenced definition. This
7968 * placeholder definition node will be adopted when we
7969 * parse the real defining declaration form, or left as
7970 * a free variable definition if we never see the real
7973 ale
= MakePlaceholder(pn
, tc
);
7979 * In case this is a forward reference to a function,
7980 * we pessimistically set PND_FUNARG if the next token
7981 * is not a left parenthesis.
7983 * If the definition eventually parsed into dn is not a
7984 * function, this flag won't hurt, and if we do parse a
7985 * function with pn's name, then the PND_FUNARG flag is
7986 * necessary for safe cx->display-based optimization of
7987 * the closure's static link.
7989 JS_ASSERT(PN_TYPE(dn
) == TOK_NAME
);
7990 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
7991 if (js_PeekToken(cx
, ts
) != TOK_LP
)
7992 dn
->pn_dflags
|= PND_FUNARG
;
7996 JS_ASSERT(dn
->pn_defn
);
7997 LinkUseToDef(pn
, dn
, tc
);
7999 /* Here we handle the backward function reference case. */
8000 if (js_PeekToken(cx
, ts
) != TOK_LP
)
8001 dn
->pn_dflags
|= PND_FUNARG
;
8003 pn
->pn_dflags
|= (dn
->pn_dflags
& PND_FUNARG
);
8007 #if JS_HAS_XML_SUPPORT
8008 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
8013 * Here PrimaryExpr is called after . or .. followed by a name
8014 * followed by ::. This is the only case where a keyword after
8015 * . or .. is not treated as a property name.
8017 str
= ATOM_TO_STRING(pn
->pn_atom
);
8018 tt
= js_CheckKeyword(JSSTRING_CHARS(str
), JSSTRING_LENGTH(str
));
8019 if (tt
== TOK_FUNCTION
) {
8020 pn
->pn_arity
= PN_NULLARY
;
8021 pn
->pn_type
= TOK_FUNCTION
;
8022 } else if (tt
!= TOK_EOF
) {
8023 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8024 JSMSG_KEYWORD_NOT_NS
);
8028 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
8039 pn
= NewParseNode(PN_NULLARY
, tc
);
8043 /* Token stream ensures that tokenbuf is NUL-terminated. */
8044 JS_ASSERT(*ts
->tokenbuf
.ptr
== (jschar
) 0);
8045 obj
= js_NewRegExpObject(cx
, ts
,
8047 ts
->tokenbuf
.ptr
- ts
->tokenbuf
.base
,
8048 CURRENT_TOKEN(ts
).t_reflags
);
8051 if (!(tc
->flags
& TCF_COMPILE_N_GO
)) {
8052 STOBJ_CLEAR_PARENT(obj
);
8053 STOBJ_CLEAR_PROTO(obj
);
8056 pn
->pn_objbox
= tc
->compiler
->newObjectBox(obj
);
8060 pn
->pn_op
= JSOP_REGEXP
;
8065 pn
= NewParseNode(PN_NULLARY
, tc
);
8068 pn
->pn_op
= JSOP_DOUBLE
;
8069 pn
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
8073 pn
= NewParseNode(PN_NULLARY
, tc
);
8076 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
8080 /* The scanner or one of its subroutines reported the error. */
8084 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8085 JSMSG_SYNTAX_ERROR
);
8091 static JSParseNode
*
8092 ParenExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
8093 JSParseNode
*pn1
, JSBool
*genexp
)
8098 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LP
);
8099 begin
= CURRENT_TOKEN(ts
).pos
.begin
;
8103 pn
= BracketedExpr(cx
, ts
, tc
);
8107 #if JS_HAS_GENERATOR_EXPRS
8108 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
8109 if (pn
->pn_type
== TOK_YIELD
) {
8110 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
8111 JSMSG_BAD_GENERATOR_SYNTAX
,
8115 if (pn
->pn_type
== TOK_COMMA
) {
8116 js_ReportCompileErrorNumber(cx
, ts
, pn
->last(), JSREPORT_ERROR
,
8117 JSMSG_BAD_GENERATOR_SYNTAX
,
8122 pn1
= NewParseNode(PN_UNARY
, tc
);
8126 pn
= GeneratorExpr(pn1
, pn
, tc
);
8129 pn
->pn_pos
.begin
= begin
;
8131 if (js_GetToken(cx
, ts
) != TOK_RP
) {
8132 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8133 JSMSG_BAD_GENERATOR_SYNTAX
,
8137 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8141 #endif /* JS_HAS_GENERATOR_EXPRS */
8147 * Fold from one constant type to another.
8148 * XXX handles only strings and numbers for now
8151 FoldType(JSContext
*cx
, JSParseNode
*pn
, JSTokenType type
)
8153 if (PN_TYPE(pn
) != type
) {
8156 if (pn
->pn_type
== TOK_STRING
) {
8158 if (!JS_ValueToNumber(cx
, ATOM_KEY(pn
->pn_atom
), &d
))
8161 pn
->pn_type
= TOK_NUMBER
;
8162 pn
->pn_op
= JSOP_DOUBLE
;
8167 if (pn
->pn_type
== TOK_NUMBER
) {
8168 JSString
*str
= js_NumberToString(cx
, pn
->pn_dval
);
8171 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8174 pn
->pn_type
= TOK_STRING
;
8175 pn
->pn_op
= JSOP_STRING
;
8186 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8187 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8188 * a successful call to this function.
8191 FoldBinaryNumeric(JSContext
*cx
, JSOp op
, JSParseNode
*pn1
, JSParseNode
*pn2
,
8192 JSParseNode
*pn
, JSTreeContext
*tc
)
8197 JS_ASSERT(pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
);
8203 i
= js_DoubleToECMAInt32(d
);
8204 j
= js_DoubleToECMAInt32(d2
);
8206 d
= (op
== JSOP_LSH
) ? i
<< j
: i
>> j
;
8210 j
= js_DoubleToECMAInt32(d2
);
8212 d
= js_DoubleToECMAUint32(d
) >> j
;
8230 /* XXX MSVC miscompiles such that (NaN == 0) */
8231 if (JSDOUBLE_IS_NaN(d2
))
8232 d
= *cx
->runtime
->jsNaN
;
8235 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
8236 d
= *cx
->runtime
->jsNaN
;
8237 else if ((JSDOUBLE_HI32(d
) ^ JSDOUBLE_HI32(d2
)) >> 31)
8238 d
= *cx
->runtime
->jsNegativeInfinity
;
8240 d
= *cx
->runtime
->jsPositiveInfinity
;
8248 d
= *cx
->runtime
->jsNaN
;
8251 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
8252 if (!(JSDOUBLE_IS_FINITE(d
) && JSDOUBLE_IS_INFINITE(d2
)))
8261 /* Take care to allow pn1 or pn2 to alias pn. */
8263 RecycleTree(pn1
, tc
);
8265 RecycleTree(pn2
, tc
);
8266 pn
->pn_type
= TOK_NUMBER
;
8267 pn
->pn_op
= JSOP_DOUBLE
;
8268 pn
->pn_arity
= PN_NULLARY
;
8273 #if JS_HAS_XML_SUPPORT
8276 FoldXMLConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
8279 JSParseNode
**pnp
, *pn1
, *pn2
;
8280 JSString
*accum
, *str
;
8282 JSTempValueRooter tvr
;
8284 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
8289 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8290 if (tt
== TOK_XMLETAGO
)
8291 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.etagoAtom
);
8292 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
)
8293 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.stagoAtom
);
8297 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8298 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8299 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8300 * Therefore, we have to add additonal protection from GC nesting under
8303 for (pn2
= pn1
, i
= j
= 0; pn2
; pn2
= pn2
->pn_next
, i
++) {
8304 /* The parser already rejected end-tags with attributes. */
8305 JS_ASSERT(tt
!= TOK_XMLETAGO
|| i
== 0);
8306 switch (pn2
->pn_type
) {
8315 if (pn2
->pn_arity
== PN_LIST
)
8317 str
= ATOM_TO_STRING(pn2
->pn_atom
);
8321 str
= js_MakeXMLCDATAString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8326 case TOK_XMLCOMMENT
:
8327 str
= js_MakeXMLCommentString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8333 str
= js_MakeXMLPIString(cx
, ATOM_TO_STRING(pn2
->pn_atom
),
8334 ATOM_TO_STRING(pn2
->pn_atom2
));
8341 JS_ASSERT(*pnp
== pn1
);
8342 if ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) &&
8343 (i
& 1) ^ (j
& 1)) {
8344 #ifdef DEBUG_brendanXXX
8345 printf("1: %d, %d => ", i
, j
);
8347 js_FileEscapedString(stdout
, accum
, 0);
8349 fputs("NULL", stdout
);
8350 fputc('\n', stdout
);
8352 } else if (accum
&& pn1
!= pn2
) {
8353 while (pn1
->pn_next
!= pn2
) {
8354 pn1
= RecycleTree(pn1
, tc
);
8357 pn1
->pn_type
= TOK_XMLTEXT
;
8358 pn1
->pn_op
= JSOP_STRING
;
8359 pn1
->pn_arity
= PN_NULLARY
;
8360 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8363 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8366 pnp
= &pn2
->pn_next
;
8373 JS_PUSH_TEMP_ROOT_STRING(cx
, accum
, &tvr
);
8374 str
= ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) && i
!= 0)
8375 ? js_AddAttributePart(cx
, i
& 1, accum
, str
)
8376 : js_ConcatStrings(cx
, accum
, str
);
8377 JS_POP_TEMP_ROOT(cx
, &tvr
);
8380 #ifdef DEBUG_brendanXXX
8381 printf("2: %d, %d => ", i
, j
);
8382 js_FileEscapedString(stdout
, str
, 0);
8383 printf(" (%u)\n", JSSTRING_LENGTH(str
));
8392 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8393 if (tt
== TOK_XMLPTAGC
)
8394 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.ptagcAtom
);
8395 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLETAGO
)
8396 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.tagcAtom
);
8399 accum
= js_ConcatStrings(cx
, accum
, str
);
8404 JS_ASSERT(*pnp
== pn1
);
8405 while (pn1
->pn_next
) {
8406 pn1
= RecycleTree(pn1
, tc
);
8409 pn1
->pn_type
= TOK_XMLTEXT
;
8410 pn1
->pn_op
= JSOP_STRING
;
8411 pn1
->pn_arity
= PN_NULLARY
;
8412 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8415 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8419 if (pn1
&& pn
->pn_count
== 1) {
8421 * Only one node under pn, and it has been folded: move pn1 onto pn
8422 * unless pn is an XML root (in which case we need it to tell the code
8423 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8424 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8425 * extra "<" and "/>" bracketing at runtime.
8427 if (!(pn
->pn_xflags
& PNX_XMLROOT
)) {
8429 } else if (tt
== TOK_XMLPTAGC
) {
8430 pn
->pn_type
= TOK_XMLELEM
;
8431 pn
->pn_op
= JSOP_TOXML
;
8437 #endif /* JS_HAS_XML_SUPPORT */
8440 StartsWith(JSParseNode
*pn
, JSTokenType tt
)
8442 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
8445 if (PN_TYPE(pn
) == tt
)
8447 switch (pn
->pn_arity
) {
8449 return tt
== TOK_FUNCTION
;
8452 TAIL_RECURSE(pn
->pn_head
);
8456 TAIL_RECURSE(pn
->pn_kid1
);
8460 TAIL_RECURSE(pn
->pn_left
);
8463 /* A parenthesized expression starts with a left parenthesis. */
8464 if (pn
->pn_type
== TOK_RP
)
8465 return tt
== TOK_LP
;
8467 TAIL_RECURSE(pn
->pn_kid
);
8470 if (pn
->pn_type
== TOK_DOT
|| pn
->pn_type
== TOK_DBLDOT
)
8471 TAIL_RECURSE(pn
->expr());
8474 TAIL_RECURSE(pn
->pn_tree
);
8482 Boolish(JSParseNode
*pn
)
8484 switch (pn
->pn_op
) {
8486 return pn
->pn_dval
!= 0 && !JSDOUBLE_IS_NaN(pn
->pn_dval
);
8489 return JSSTRING_LENGTH(ATOM_TO_STRING(pn
->pn_atom
)) != 0;
8491 #if JS_HAS_GENERATOR_EXPRS
8495 * A generator expression as an if or loop condition has no effects, it
8496 * simply results in a truthy object reference. This condition folding
8497 * is needed for the decompiler. See bug 442342 and bug 443074.
8499 if (pn
->pn_count
!= 1)
8501 JSParseNode
*pn2
= pn
->pn_head
;
8502 if (pn2
->pn_type
!= TOK_FUNCTION
)
8504 if (!(pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
))
8526 js_FoldConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, bool inCond
)
8528 JSParseNode
*pn1
= NULL
, *pn2
= NULL
, *pn3
= NULL
;
8530 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
8532 switch (pn
->pn_arity
) {
8535 uint16 oldflags
= tc
->flags
;
8536 JSFunctionBox
*oldlist
= tc
->functionList
;
8538 tc
->flags
= (uint16
) pn
->pn_funbox
->tcflags
;
8539 tc
->functionList
= pn
->pn_funbox
->kids
;
8540 if (!js_FoldConstants(cx
, pn
->pn_body
, tc
))
8542 pn
->pn_funbox
->kids
= tc
->functionList
;
8543 tc
->flags
= oldflags
;
8544 tc
->functionList
= oldlist
;
8550 /* Propagate inCond through logical connectives. */
8551 bool cond
= inCond
&& (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
);
8553 /* Save the list head in pn1 for later use. */
8554 for (pn1
= pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
8555 if (!js_FoldConstants(cx
, pn2
, tc
, cond
))
8562 /* Any kid may be null (e.g. for (;;)). */
8566 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_IF
))
8569 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_FORHEAD
))
8571 if (pn
->pn_type
== TOK_FORHEAD
&& pn2
->pn_op
== JSOP_TRUE
) {
8572 RecycleTree(pn2
, tc
);
8576 if (pn3
&& !js_FoldConstants(cx
, pn3
, tc
))
8584 /* Propagate inCond through logical connectives. */
8585 if (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
) {
8586 if (!js_FoldConstants(cx
, pn1
, tc
, inCond
))
8588 if (!js_FoldConstants(cx
, pn2
, tc
, inCond
))
8593 /* First kid may be null (for default case in switch). */
8594 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_WHILE
))
8596 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_DO
))
8601 /* Our kid may be null (e.g. return; vs. return e;). */
8604 !js_FoldConstants(cx
, pn1
, tc
,
8605 (inCond
&& pn
->pn_type
== TOK_RP
) ||
8606 pn
->pn_op
== JSOP_NOT
)) {
8613 * Skip pn1 down along a chain of dotted member expressions to avoid
8614 * excessive recursion. Our only goal here is to fold constants (if
8615 * any) in the primary expression operand to the left of the first
8620 while (pn1
&& pn1
->pn_arity
== PN_NAME
&& !pn1
->pn_used
)
8622 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
8629 if (!js_FoldConstants(cx
, pn1
, tc
))
8637 switch (pn
->pn_type
) {
8639 if (ContainsStmt(pn2
, TOK_VAR
) || ContainsStmt(pn3
, TOK_VAR
))
8644 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8645 while (pn1
->pn_type
== TOK_RP
)
8647 switch (pn1
->pn_type
) {
8649 if (pn1
->pn_dval
== 0 || JSDOUBLE_IS_NaN(pn1
->pn_dval
))
8653 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1
->pn_atom
)) == 0)
8657 if (pn1
->pn_op
== JSOP_TRUE
)
8659 if (pn1
->pn_op
== JSOP_FALSE
|| pn1
->pn_op
== JSOP_NULL
) {
8665 /* Early return to dodge common code that copies pn2 to pn. */
8669 #if JS_HAS_GENERATOR_EXPRS
8670 /* Don't fold a trailing |if (0)| in a generator expression. */
8671 if (!pn2
&& (tc
->flags
& TCF_GENEXP_LAMBDA
))
8677 * pn2 is the then- or else-statement subtree to compile. Take
8678 * care not to expose an object initialiser, which would be parsed
8679 * as a block, to the Statement parser via eval(uneval(e)) where e
8680 * is '1 ? {p:2, q:3}[i] : r;' or the like.
8682 if (pn
->pn_type
== TOK_HOOK
&& StartsWith(pn2
, TOK_RC
)) {
8683 pn
->pn_type
= TOK_RP
;
8684 pn
->pn_arity
= PN_UNARY
;
8686 if (pn3
&& pn3
!= pn2
)
8687 RecycleTree(pn3
, tc
);
8693 if (!pn2
|| (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
)) {
8695 * False condition and no else, or an empty then-statement was
8696 * moved up over pn. Either way, make pn an empty block (not an
8697 * empty statement, which does not decompile, even when labeled).
8698 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8699 * or an empty statement for a child.
8701 pn
->pn_type
= TOK_LC
;
8702 pn
->pn_arity
= PN_LIST
;
8705 RecycleTree(pn2
, tc
);
8706 if (pn3
&& pn3
!= pn2
)
8707 RecycleTree(pn3
, tc
);
8713 if (pn
->pn_arity
== PN_LIST
) {
8714 JSParseNode
**pnp
= &pn
->pn_head
;
8715 JS_ASSERT(*pnp
== pn1
);
8717 int cond
= Boolish(pn1
);
8718 if (cond
== (pn
->pn_type
== TOK_OR
)) {
8719 for (pn2
= pn1
->pn_next
; pn2
; pn2
= pn3
) {
8721 RecycleTree(pn2
, tc
);
8724 pn1
->pn_next
= NULL
;
8728 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
8729 if (pn
->pn_count
== 1)
8731 *pnp
= pn1
->pn_next
;
8732 RecycleTree(pn1
, tc
);
8735 pnp
= &pn1
->pn_next
;
8737 } while ((pn1
= *pnp
) != NULL
);
8739 // We may have to change arity from LIST to BINARY.
8741 if (pn
->pn_count
== 2) {
8743 pn1
->pn_next
= NULL
;
8744 JS_ASSERT(!pn2
->pn_next
);
8745 pn
->pn_arity
= PN_BINARY
;
8748 } else if (pn
->pn_count
== 1) {
8750 RecycleTree(pn1
, tc
);
8753 int cond
= Boolish(pn1
);
8754 if (cond
== (pn
->pn_type
== TOK_OR
)) {
8755 RecycleTree(pn2
, tc
);
8757 } else if (cond
!= -1) {
8758 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
8759 RecycleTree(pn1
, tc
);
8768 * Compound operators such as *= should be subject to folding, in case
8769 * the left-hand side is constant, and so that the decompiler produces
8770 * the same string that you get from decompiling a script or function
8771 * compiled from that same string. As with +, += is special.
8773 if (pn
->pn_op
== JSOP_NOP
)
8775 if (pn
->pn_op
!= JSOP_ADD
)
8780 if (pn
->pn_arity
== PN_LIST
) {
8781 size_t length
, length2
;
8783 JSString
*str
, *str2
;
8786 * Any string literal term with all others number or string means
8787 * this is a concatenation. If any term is not a string or number
8788 * literal, we can't fold.
8790 JS_ASSERT(pn
->pn_count
> 2);
8791 if (pn
->pn_xflags
& PNX_CANTFOLD
)
8793 if (pn
->pn_xflags
!= PNX_STRCAT
)
8796 /* Ok, we're concatenating: convert non-string constant operands. */
8798 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
8799 if (!FoldType(cx
, pn2
, TOK_STRING
))
8801 /* XXX fold only if all operands convert to string */
8802 if (pn2
->pn_type
!= TOK_STRING
)
8804 length
+= JSFLATSTR_LENGTH(ATOM_TO_STRING(pn2
->pn_atom
));
8807 /* Allocate a new buffer and string descriptor for the result. */
8808 chars
= (jschar
*) JS_malloc(cx
, (length
+ 1) * sizeof(jschar
));
8811 str
= js_NewString(cx
, chars
, length
);
8817 /* Fill the buffer, advancing chars and recycling kids as we go. */
8818 for (pn2
= pn1
; pn2
; pn2
= RecycleTree(pn2
, tc
)) {
8819 str2
= ATOM_TO_STRING(pn2
->pn_atom
);
8820 length2
= JSFLATSTR_LENGTH(str2
);
8821 js_strncpy(chars
, JSFLATSTR_CHARS(str2
), length2
);
8826 /* Atomize the result string and mutate pn to refer to it. */
8827 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8830 pn
->pn_type
= TOK_STRING
;
8831 pn
->pn_op
= JSOP_STRING
;
8832 pn
->pn_arity
= PN_NULLARY
;
8836 /* Handle a binary string concatenation. */
8837 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
8838 if (pn1
->pn_type
== TOK_STRING
|| pn2
->pn_type
== TOK_STRING
) {
8839 JSString
*left
, *right
, *str
;
8841 if (!FoldType(cx
, (pn1
->pn_type
!= TOK_STRING
) ? pn1
: pn2
,
8845 if (pn1
->pn_type
!= TOK_STRING
|| pn2
->pn_type
!= TOK_STRING
)
8847 left
= ATOM_TO_STRING(pn1
->pn_atom
);
8848 right
= ATOM_TO_STRING(pn2
->pn_atom
);
8849 str
= js_ConcatStrings(cx
, left
, right
);
8852 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8855 pn
->pn_type
= TOK_STRING
;
8856 pn
->pn_op
= JSOP_STRING
;
8857 pn
->pn_arity
= PN_NULLARY
;
8858 RecycleTree(pn1
, tc
);
8859 RecycleTree(pn2
, tc
);
8863 /* Can't concatenate string literals, let's try numbers. */
8871 if (pn
->pn_arity
== PN_LIST
) {
8872 JS_ASSERT(pn
->pn_count
> 2);
8873 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
8874 if (!FoldType(cx
, pn2
, TOK_NUMBER
))
8877 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
8878 /* XXX fold only if all operands convert to number */
8879 if (pn2
->pn_type
!= TOK_NUMBER
)
8883 JSOp op
= PN_OP(pn
);
8887 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
, tc
))
8889 while ((pn2
= pn3
) != NULL
) {
8891 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
, tc
))
8896 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
8897 if (!FoldType(cx
, pn1
, TOK_NUMBER
) ||
8898 !FoldType(cx
, pn2
, TOK_NUMBER
)) {
8901 if (pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
) {
8902 if (!FoldBinaryNumeric(cx
, PN_OP(pn
), pn1
, pn2
, pn
, tc
))
8909 while (pn1
->pn_type
== TOK_RP
)
8911 if (pn1
->pn_type
== TOK_NUMBER
) {
8914 /* Operate on one numeric constant. */
8916 switch (pn
->pn_op
) {
8918 d
= ~js_DoubleToECMAInt32(d
);
8924 * Negation of a zero doesn't produce a negative
8925 * zero on HPUX. Perform the operation by bit
8928 JSDOUBLE_HI32(d
) ^= JSDOUBLE_HI32_SIGNBIT
;
8938 pn
->pn_type
= TOK_PRIMARY
;
8939 pn
->pn_op
= (d
== 0 || JSDOUBLE_IS_NaN(d
)) ? JSOP_TRUE
: JSOP_FALSE
;
8940 pn
->pn_arity
= PN_NULLARY
;
8944 /* Return early to dodge the common TOK_NUMBER code. */
8947 pn
->pn_type
= TOK_NUMBER
;
8948 pn
->pn_op
= JSOP_DOUBLE
;
8949 pn
->pn_arity
= PN_NULLARY
;
8951 RecycleTree(pn1
, tc
);
8952 } else if (pn1
->pn_type
== TOK_PRIMARY
) {
8953 if (pn
->pn_op
== JSOP_NOT
&&
8954 (pn1
->pn_op
== JSOP_TRUE
||
8955 pn1
->pn_op
== JSOP_FALSE
)) {
8957 pn
->pn_op
= (pn
->pn_op
== JSOP_TRUE
) ? JSOP_FALSE
: JSOP_TRUE
;
8958 RecycleTree(pn1
, tc
);
8963 #if JS_HAS_XML_SUPPORT
8970 if (pn
->pn_arity
== PN_LIST
) {
8971 JS_ASSERT(pn
->pn_type
== TOK_XMLLIST
|| pn
->pn_count
!= 0);
8972 if (!FoldXMLConstants(cx
, pn
, tc
))
8978 if (pn1
->pn_type
== TOK_XMLNAME
) {
8980 JSObjectBox
*xmlbox
;
8982 v
= ATOM_KEY(pn1
->pn_atom
);
8983 if (!js_ToAttributeName(cx
, &v
))
8985 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
8987 xmlbox
= tc
->compiler
->newObjectBox(JSVAL_TO_OBJECT(v
));
8991 pn
->pn_type
= TOK_XMLNAME
;
8992 pn
->pn_op
= JSOP_OBJECT
;
8993 pn
->pn_arity
= PN_NULLARY
;
8994 pn
->pn_objbox
= xmlbox
;
8995 RecycleTree(pn1
, tc
);
8998 #endif /* JS_HAS_XML_SUPPORT */
9004 int cond
= Boolish(pn
);
9006 switch (pn
->pn_arity
) {
9011 RecycleTree(pn2
, tc
);
9012 } while ((pn2
= pn3
) != NULL
);
9015 RecycleFuncNameKids(pn
, tc
);
9020 JS_NOT_REACHED("unhandled arity");
9022 pn
->pn_type
= TOK_PRIMARY
;
9023 pn
->pn_op
= cond
? JSOP_TRUE
: JSOP_FALSE
;
9024 pn
->pn_arity
= PN_NULLARY
;