1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 * This is a recursive-descent parser for the JavaScript language specified by
45 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
46 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
47 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
48 * After tree construction, it rewrites trees to fold constants and evaluate
49 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
52 * This parser attempts no error recovery.
59 #include "jsarena.h" /* Added by JSIFY */
60 #include "jsutil.h" /* Added by JSIFY */
65 #include "jsversion.h"
79 #include "jsstaticcheck.h"
80 #include "jslibmath.h"
82 #if JS_HAS_XML_SUPPORT
86 #if JS_HAS_DESTRUCTURING
91 * Asserts to verify assumptions behind pn_ macros.
93 #define pn_offsetof(m) offsetof(JSParseNode, m)
95 JS_STATIC_ASSERT(pn_offsetof(pn_link
) == pn_offsetof(dn_uses
));
96 JS_STATIC_ASSERT(pn_offsetof(pn_u
.name
.atom
) == pn_offsetof(pn_u
.apair
.atom
));
101 * JS parsers, from lowest to highest precedence.
103 * Each parser takes a context, a token stream, and a tree context struct.
104 * Each returns a parse node tree or null on error.
107 typedef JSParseNode
*
108 JSParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
);
110 typedef JSParseNode
*
111 JSVariablesParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
114 typedef JSParseNode
*
115 JSMemberParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
116 JSBool allowCallSyntax
);
118 typedef JSParseNode
*
119 JSPrimaryParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
120 JSTokenType tt
, JSBool afterDot
);
122 typedef JSParseNode
*
123 JSParenParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
124 JSParseNode
*pn1
, JSBool
*genexp
);
126 static JSParser FunctionStmt
;
127 static JSParser FunctionExpr
;
128 static JSParser Statements
;
129 static JSParser Statement
;
130 static JSVariablesParser Variables
;
131 static JSParser Expr
;
132 static JSParser AssignExpr
;
133 static JSParser CondExpr
;
134 static JSParser OrExpr
;
135 static JSParser AndExpr
;
136 static JSParser BitOrExpr
;
137 static JSParser BitXorExpr
;
138 static JSParser BitAndExpr
;
139 static JSParser EqExpr
;
140 static JSParser RelExpr
;
141 static JSParser ShiftExpr
;
142 static JSParser AddExpr
;
143 static JSParser MulExpr
;
144 static JSParser UnaryExpr
;
145 static JSMemberParser MemberExpr
;
146 static JSPrimaryParser PrimaryExpr
;
147 static JSParenParser ParenExpr
;
150 * Insist that the next token be of type tt, or report errno and return null.
151 * NB: this macro uses cx and ts from its lexical environment.
153 #define MUST_MATCH_TOKEN(tt, errno) \
155 if (js_GetToken(cx, ts) != tt) { \
156 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
161 #ifdef METER_PARSENODES
162 static uint32 parsenodes
= 0;
163 static uint32 maxparsenodes
= 0;
164 static uint32 recyclednodes
= 0;
168 JSParseNode::become(JSParseNode
*pn2
)
171 JS_ASSERT(!pn2
->pn_defn
);
175 JSParseNode
**pnup
= &pn2
->pn_lexdef
->dn_uses
;
177 pnup
= &(*pnup
)->pn_link
;
179 pn_link
= pn2
->pn_link
;
182 pn2
->pn_used
= false;
185 /* If this is a function node fix up the pn_funbox->node back-pointer. */
186 if (PN_TYPE(pn2
) == TOK_FUNCTION
&& pn2
->pn_arity
== PN_FUNC
)
187 pn2
->pn_funbox
->node
= this;
189 pn_type
= pn2
->pn_type
;
191 pn_arity
= pn2
->pn_arity
;
192 pn_parens
= pn2
->pn_parens
;
202 pn_used
= pn_defn
= false;
203 pn_arity
= PN_NULLARY
;
208 JSCompiler::init(const jschar
*base
, size_t length
,
209 FILE *fp
, const char *filename
, uintN lineno
)
211 JSContext
*cx
= context
;
213 tempPoolMark
= JS_ARENA_MARK(&cx
->tempPool
);
214 if (!js_InitTokenStream(cx
, TS(this), base
, length
, fp
, filename
, lineno
)) {
215 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
219 /* Root atoms and objects allocated for the parsed tree. */
220 JS_KEEP_ATOMS(cx
->runtime
);
221 JS_PUSH_TEMP_ROOT_COMPILER(cx
, this, &tempRoot
);
225 JSCompiler::~JSCompiler()
227 JSContext
*cx
= context
;
230 JSPRINCIPALS_DROP(cx
, principals
);
231 JS_ASSERT(tempRoot
.u
.compiler
== this);
232 JS_POP_TEMP_ROOT(cx
, &tempRoot
);
233 JS_UNKEEP_ATOMS(cx
->runtime
);
234 js_CloseTokenStream(cx
, TS(this));
235 JS_ARENA_RELEASE(&cx
->tempPool
, tempPoolMark
);
239 JSCompiler::setPrincipals(JSPrincipals
*prin
)
241 JS_ASSERT(!principals
);
243 JSPRINCIPALS_HOLD(context
, prin
);
248 JSCompiler::newObjectBox(JSObject
*obj
)
253 * We use JSContext.tempPool to allocate parsed objects and place them on
254 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
255 * containing the entries must be alive until we are done with scanning,
256 * parsing and code generation for the whole script or top-level function.
259 JS_ARENA_ALLOCATE_TYPE(objbox
, JSObjectBox
, &context
->tempPool
);
261 js_ReportOutOfScriptQuota(context
);
264 objbox
->traceLink
= traceListHead
;
265 traceListHead
= objbox
;
266 objbox
->emitLink
= NULL
;
267 objbox
->object
= obj
;
272 JSCompiler::newFunctionBox(JSObject
*obj
, JSParseNode
*fn
, JSTreeContext
*tc
)
275 JS_ASSERT(HAS_FUNCTION_CLASS(obj
));
278 * We use JSContext.tempPool to allocate parsed objects and place them on
279 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
280 * containing the entries must be alive until we are done with scanning,
281 * parsing and code generation for the whole script or top-level function.
283 JSFunctionBox
*funbox
;
284 JS_ARENA_ALLOCATE_TYPE(funbox
, JSFunctionBox
, &context
->tempPool
);
286 js_ReportOutOfScriptQuota(context
);
289 funbox
->traceLink
= traceListHead
;
290 traceListHead
= funbox
;
291 funbox
->emitLink
= NULL
;
292 funbox
->object
= obj
;
294 funbox
->siblings
= tc
->functionList
;
295 tc
->functionList
= funbox
;
296 ++tc
->compiler
->functionCount
;
298 funbox
->parent
= tc
->funbox
;
299 funbox
->queued
= false;
300 funbox
->inLoop
= false;
301 for (JSStmtInfo
*stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
302 if (STMT_IS_LOOP(stmt
)) {
303 funbox
->inLoop
= true;
307 funbox
->level
= tc
->staticLevel
;
308 funbox
->tcflags
= TCF_IN_FUNCTION
| (tc
->flags
& TCF_COMPILE_N_GO
);
313 JSCompiler::trace(JSTracer
*trc
)
317 JS_ASSERT(tempRoot
.u
.compiler
== this);
318 objbox
= traceListHead
;
320 JS_CALL_OBJECT_TRACER(trc
, objbox
->object
, "parser.object");
321 objbox
= objbox
->traceLink
;
326 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
);
329 UnlinkFunctionBox(JSParseNode
*pn
, JSTreeContext
*tc
)
331 JSFunctionBox
*funbox
= pn
->pn_funbox
;
333 JS_ASSERT(funbox
->node
== pn
);
336 JSFunctionBox
**funboxp
= &tc
->functionList
;
338 if (*funboxp
== funbox
) {
339 *funboxp
= funbox
->siblings
;
342 funboxp
= &(*funboxp
)->siblings
;
345 uint16 oldflags
= tc
->flags
;
346 JSFunctionBox
*oldlist
= tc
->functionList
;
348 tc
->flags
= (uint16
) funbox
->tcflags
;
349 tc
->functionList
= funbox
->kids
;
350 UnlinkFunctionBoxes(pn
->pn_body
, tc
);
351 funbox
->kids
= tc
->functionList
;
352 tc
->flags
= oldflags
;
353 tc
->functionList
= oldlist
;
355 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
356 pn
->pn_funbox
= NULL
;
361 UnlinkFunctionBoxes(JSParseNode
*pn
, JSTreeContext
*tc
)
364 switch (pn
->pn_arity
) {
368 UnlinkFunctionBoxes(pn
->pn_kid
, tc
);
371 UnlinkFunctionBoxes(pn
->pn_left
, tc
);
372 UnlinkFunctionBoxes(pn
->pn_right
, tc
);
375 UnlinkFunctionBoxes(pn
->pn_kid1
, tc
);
376 UnlinkFunctionBoxes(pn
->pn_kid2
, tc
);
377 UnlinkFunctionBoxes(pn
->pn_kid3
, tc
);
380 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
381 UnlinkFunctionBoxes(pn2
, tc
);
384 UnlinkFunctionBox(pn
, tc
);
387 UnlinkFunctionBoxes(pn
->maybeExpr(), tc
);
390 UnlinkFunctionBoxes(pn
->pn_tree
, tc
);
396 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
);
399 RecycleTree(JSParseNode
*pn
, JSTreeContext
*tc
)
401 JSParseNode
*next
, **head
;
406 /* Catch back-to-back dup recycles. */
407 JS_ASSERT(pn
!= tc
->compiler
->nodeList
);
409 if (pn
->pn_used
|| pn
->pn_defn
) {
411 * JSAtomLists own definition nodes along with their used-node chains.
412 * Defer recycling such nodes until we unwind to top level to avoid
413 * linkage overhead or (alternatively) unlinking runtime complexity.
414 * Yes, this means dead code can contribute to static analysis results!
416 * Do recycle kids here, since they are no longer needed.
419 RecycleFuncNameKids(pn
, tc
);
421 UnlinkFunctionBoxes(pn
, tc
);
422 head
= &tc
->compiler
->nodeList
;
425 #ifdef METER_PARSENODES
433 RecycleFuncNameKids(JSParseNode
*pn
, JSTreeContext
*tc
)
435 switch (pn
->pn_arity
) {
437 UnlinkFunctionBox(pn
, tc
);
442 * Only a definition node might have a non-null strong pn_expr link
443 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
444 * Every node with the pn_used flag set has a non-null pn_lexdef
445 * weak reference to its definition node.
447 if (!pn
->pn_used
&& pn
->pn_expr
) {
448 RecycleTree(pn
->pn_expr
, tc
);
454 JS_ASSERT(PN_TYPE(pn
) == TOK_FUNCTION
);
459 NewOrRecycledNode(JSTreeContext
*tc
)
461 JSParseNode
*pn
, *pn2
;
463 pn
= tc
->compiler
->nodeList
;
465 JSContext
*cx
= tc
->compiler
->context
;
467 JS_ARENA_ALLOCATE_TYPE(pn
, JSParseNode
, &cx
->tempPool
);
469 js_ReportOutOfScriptQuota(cx
);
471 tc
->compiler
->nodeList
= pn
->pn_next
;
473 /* Recycle immediate descendents only, to save work and working set. */
474 switch (pn
->pn_arity
) {
476 RecycleTree(pn
->pn_body
, tc
);
481 while (pn2
&& !pn2
->pn_used
&& !pn2
->pn_defn
)
486 pn2
= RecycleTree(pn2
, tc
);
489 *pn
->pn_tail
= tc
->compiler
->nodeList
;
490 tc
->compiler
->nodeList
= pn
->pn_head
;
491 #ifdef METER_PARSENODES
492 recyclednodes
+= pn
->pn_count
;
499 RecycleTree(pn
->pn_kid1
, tc
);
500 RecycleTree(pn
->pn_kid2
, tc
);
501 RecycleTree(pn
->pn_kid3
, tc
);
504 if (pn
->pn_left
!= pn
->pn_right
)
505 RecycleTree(pn
->pn_left
, tc
);
506 RecycleTree(pn
->pn_right
, tc
);
509 RecycleTree(pn
->pn_kid
, tc
);
513 RecycleTree(pn
->pn_expr
, tc
);
520 #ifdef METER_PARSENODES
522 if (parsenodes
- recyclednodes
> maxparsenodes
)
523 maxparsenodes
= parsenodes
- recyclednodes
;
525 pn
->pn_used
= pn
->pn_defn
= false;
526 memset(&pn
->pn_u
, 0, sizeof pn
->pn_u
);
533 InitParseNode(JSParseNode
*pn
, JSTokenType type
, JSOp op
, JSParseNodeArity arity
)
537 pn
->pn_arity
= arity
;
538 pn
->pn_parens
= false;
539 JS_ASSERT(!pn
->pn_used
);
540 JS_ASSERT(!pn
->pn_defn
);
541 pn
->pn_next
= pn
->pn_link
= NULL
;
545 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
549 NewParseNode(JSParseNodeArity arity
, JSTreeContext
*tc
)
554 pn
= NewOrRecycledNode(tc
);
557 tp
= &CURRENT_TOKEN(&tc
->compiler
->tokenStream
);
558 InitParseNode(pn
, tp
->type
, JSOP_NOP
, arity
);
559 pn
->pn_pos
= tp
->pos
;
564 InitNameNodeCommon(JSParseNode
*pn
, JSTreeContext
*tc
)
567 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
568 pn
->pn_dflags
= tc
->atTopLevel() ? PND_TOPLEVEL
: 0;
569 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
570 pn
->pn_dflags
|= PND_BLOCKCHILD
;
571 pn
->pn_blockid
= tc
->blockid();
575 NewNameNode(JSContext
*cx
, JSTokenStream
*ts
, JSAtom
*atom
, JSTreeContext
*tc
)
579 pn
= NewParseNode(PN_NAME
, tc
);
582 InitNameNodeCommon(pn
, tc
);
588 NewBinary(JSTokenType tt
, JSOp op
, JSParseNode
*left
, JSParseNode
*right
,
591 JSParseNode
*pn
, *pn1
, *pn2
;
597 * Flatten a left-associative (left-heavy) tree of a given operator into
598 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
600 if (PN_TYPE(left
) == tt
&&
602 (js_CodeSpec
[op
].format
& JOF_LEFTASSOC
)) {
603 if (left
->pn_arity
!= PN_LIST
) {
604 pn1
= left
->pn_left
, pn2
= left
->pn_right
;
605 left
->pn_arity
= PN_LIST
;
606 left
->pn_parens
= false;
609 if (tt
== TOK_PLUS
) {
610 if (pn1
->pn_type
== TOK_STRING
)
611 left
->pn_xflags
|= PNX_STRCAT
;
612 else if (pn1
->pn_type
!= TOK_NUMBER
)
613 left
->pn_xflags
|= PNX_CANTFOLD
;
614 if (pn2
->pn_type
== TOK_STRING
)
615 left
->pn_xflags
|= PNX_STRCAT
;
616 else if (pn2
->pn_type
!= TOK_NUMBER
)
617 left
->pn_xflags
|= PNX_CANTFOLD
;
621 left
->pn_pos
.end
= right
->pn_pos
.end
;
622 if (tt
== TOK_PLUS
) {
623 if (right
->pn_type
== TOK_STRING
)
624 left
->pn_xflags
|= PNX_STRCAT
;
625 else if (right
->pn_type
!= TOK_NUMBER
)
626 left
->pn_xflags
|= PNX_CANTFOLD
;
632 * Fold constant addition immediately, to conserve node space and, what's
633 * more, so js_FoldConstants never sees mixed addition and concatenation
634 * operations with more than one leading non-string operand in a PN_LIST
635 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
636 * to "3pt", not "12pt").
638 if (tt
== TOK_PLUS
&&
639 left
->pn_type
== TOK_NUMBER
&&
640 right
->pn_type
== TOK_NUMBER
) {
641 left
->pn_dval
+= right
->pn_dval
;
642 left
->pn_pos
.end
= right
->pn_pos
.end
;
643 RecycleTree(right
, tc
);
647 pn
= NewOrRecycledNode(tc
);
650 InitParseNode(pn
, tt
, op
, PN_BINARY
);
651 pn
->pn_pos
.begin
= left
->pn_pos
.begin
;
652 pn
->pn_pos
.end
= right
->pn_pos
.end
;
654 pn
->pn_right
= right
;
658 #if JS_HAS_GETTER_SETTER
660 CheckGetterOrSetter(JSContext
*cx
, JSTokenStream
*ts
, JSTokenType tt
)
667 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_NAME
);
668 atom
= CURRENT_TOKEN(ts
).t_atom
;
670 if (atom
== rt
->atomState
.getterAtom
)
672 else if (atom
== rt
->atomState
.setterAtom
)
676 if (js_PeekTokenSameLine(cx
, ts
) != tt
)
678 (void) js_GetToken(cx
, ts
);
679 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
680 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
681 JSMSG_BAD_GETTER_OR_SETTER
,
687 CURRENT_TOKEN(ts
).t_op
= op
;
688 if (JS_HAS_STRICT_OPTION(cx
)) {
689 name
= js_AtomToPrintableString(cx
, atom
);
691 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
692 JSREPORT_WARNING
| JSREPORT_STRICT
,
693 JSMSG_DEPRECATED_USAGE
,
703 GenerateBlockId(JSTreeContext
*tc
, uint32
& blockid
)
705 if (tc
->blockidGen
== JS_BIT(20)) {
706 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
707 JSMSG_NEED_DIET
, "program");
710 blockid
= tc
->blockidGen
++;
715 GenerateBlockIdForStmtNode(JSParseNode
*pn
, JSTreeContext
*tc
)
717 JS_ASSERT(tc
->topStmt
);
718 JS_ASSERT(STMT_MAYBE_SCOPE(tc
->topStmt
));
719 JS_ASSERT(pn
->pn_type
== TOK_LC
|| pn
->pn_type
== TOK_LEXICALSCOPE
);
720 if (!GenerateBlockId(tc
, tc
->topStmt
->blockid
))
722 pn
->pn_blockid
= tc
->topStmt
->blockid
;
727 * Parse a top-level JS script.
730 JSCompiler::parse(JSObject
*chain
)
733 * Protect atoms from being collected by a GC activation, which might
734 * - nest on this thread due to out of memory (the so-called "last ditch"
735 * GC attempted within js_NewGCThing), or
736 * - run for any reason on another thread if this thread is suspended on
737 * an object lock before it finishes generating bytecode into a script
738 * protected from the GC by a root or a stack frame reference.
740 JSTreeContext
tc(this);
741 tc
.scopeChain
= chain
;
742 if (!GenerateBlockId(&tc
, tc
.bodyid
))
745 JSParseNode
*pn
= Statements(context
, TS(this), &tc
);
747 if (!js_MatchToken(context
, TS(this), TOK_EOF
)) {
748 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
752 if (!js_FoldConstants(context
, pn
, &tc
))
759 JS_STATIC_ASSERT(FREE_STATIC_LEVEL
== JS_BITMASK(JSFB_LEVEL_BITS
));
762 SetStaticLevel(JSTreeContext
*tc
, uintN staticLevel
)
765 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
766 * (0xffffffff) and other cookies with that level.
768 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
769 * practically speaking it leaves more than enough room for upvars. In fact
770 * we might want to split cookie fields giving fewer bits for skip and more
771 * for slot, but only based on evidence.
773 if (staticLevel
>= FREE_STATIC_LEVEL
) {
774 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
775 JSMSG_TOO_DEEP
, js_function_str
);
778 tc
->staticLevel
= staticLevel
;
783 * Compile a top-level script.
786 JSCompiler::compileScript(JSContext
*cx
, JSObject
*scopeChain
, JSStackFrame
*callerFrame
,
787 JSPrincipals
*principals
, uint32 tcflags
,
788 const jschar
*chars
, size_t length
,
789 FILE *file
, const char *filename
, uintN lineno
,
790 JSString
*source
/* = NULL */)
792 JSCompiler
jsc(cx
, principals
, callerFrame
);
793 JSArenaPool codePool
, notePool
;
796 uint32 scriptGlobals
;
798 #ifdef METER_PARSENODES
799 void *sbrk(ptrdiff_t), *before
= sbrk(0);
802 JS_ASSERT(!(tcflags
& ~(TCF_COMPILE_N_GO
| TCF_NO_SCRIPT_RVAL
|
803 TCF_STATIC_LEVEL_MASK
)));
806 * The scripted callerFrame can only be given for compile-and-go scripts
807 * and non-zero static level requires callerFrame.
809 JS_ASSERT_IF(callerFrame
, tcflags
& TCF_COMPILE_N_GO
);
810 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags
) != 0, callerFrame
);
812 if (!jsc
.init(chars
, length
, file
, filename
, lineno
))
815 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
816 &cx
->scriptStackQuota
);
817 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
818 &cx
->scriptStackQuota
);
820 JSCodeGenerator
cg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
822 MUST_FLOW_THROUGH("out");
824 /* Null script early in case of error, to reduce our code footprint. */
827 cg
.flags
|= uint16(tcflags
);
828 cg
.scopeChain
= scopeChain
;
829 if (!SetStaticLevel(&cg
, TCF_GET_STATIC_LEVEL(tcflags
)))
833 * If funbox is non-null after we create the new script, callerFrame->fun
834 * was saved in the 0th object table entry.
839 if (tcflags
& TCF_COMPILE_N_GO
) {
842 * Save eval program source in script->atomMap.vector[0] for the
843 * eval cache (see obj_eval in jsobj.cpp).
845 JSAtom
*atom
= js_AtomizeString(cx
, source
, 0);
846 if (!atom
|| !cg
.atomList
.add(&jsc
, atom
))
850 if (callerFrame
&& callerFrame
->fun
) {
852 * An eval script in a caller frame needs to have its enclosing
853 * function captured in case it refers to an upvar, and someone
854 * wishes to decompile it while it's running.
856 funbox
= jsc
.newObjectBox(FUN_OBJECT(callerFrame
->fun
));
859 funbox
->emitLink
= cg
.objectList
.lastbox
;
860 cg
.objectList
.lastbox
= funbox
;
861 cg
.objectList
.length
++;
866 * Inline Statements to emit as we go to save AST space. We must generate
867 * our script-body blockid since we aren't calling Statements.
870 if (!GenerateBlockId(&cg
, bodyid
))
874 #if JS_HAS_XML_SUPPORT
881 jsc
.tokenStream
.flags
|= TSF_OPERAND
;
882 tt
= js_PeekToken(cx
, &jsc
.tokenStream
);
883 jsc
.tokenStream
.flags
&= ~TSF_OPERAND
;
887 JS_ASSERT(tt
== TOK_ERROR
);
891 pn
= Statement(cx
, &jsc
.tokenStream
, &cg
);
894 JS_ASSERT(!cg
.blockNode
);
896 if (!js_FoldConstants(cx
, pn
, &cg
))
899 if (cg
.functionList
) {
900 if (!jsc
.analyzeFunctions(cg
.functionList
, cg
.flags
))
902 cg
.functionList
= NULL
;
905 if (!js_EmitTree(cx
, &cg
, pn
))
907 #if JS_HAS_XML_SUPPORT
908 if (PN_TYPE(pn
) != TOK_SEMI
||
910 !TREE_TYPE_IS_XML(PN_TYPE(pn
->pn_kid
))) {
914 RecycleTree(pn
, &cg
);
917 #if JS_HAS_XML_SUPPORT
919 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
920 * For background, see:
922 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
924 if (pn
&& onlyXML
&& (tcflags
& TCF_NO_SCRIPT_RVAL
)) {
925 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
, JSREPORT_ERROR
,
926 JSMSG_XML_WHOLE_PROGRAM
);
932 * Global variables and regexps share the index space with locals. Due to
933 * incremental code generation we need to patch the bytecode to adjust the
934 * local references to skip the globals.
936 scriptGlobals
= cg
.ngvars
+ cg
.regexpList
.length
;
937 if (scriptGlobals
!= 0) {
938 jsbytecode
*code
, *end
;
940 const JSCodeSpec
*cs
;
943 if (scriptGlobals
>= SLOTNO_LIMIT
)
946 for (end
= code
+ CG_OFFSET(&cg
); code
!= end
; code
+= len
) {
947 JS_ASSERT(code
< end
);
949 cs
= &js_CodeSpec
[op
];
950 len
= (cs
->length
> 0)
952 : js_GetVariableBytecodeLength(code
);
953 if (JOF_TYPE(cs
->format
) == JOF_LOCAL
||
954 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
)) {
956 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
957 * emitted only for a function.
959 JS_ASSERT((JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ==
960 (op
== JSOP_GETLOCALPROP
));
961 slot
= GET_SLOTNO(code
);
962 slot
+= scriptGlobals
;
963 if (slot
>= SLOTNO_LIMIT
)
965 SET_SLOTNO(code
, slot
);
970 #ifdef METER_PARSENODES
971 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
972 (char *)sbrk(0) - (char *)before
,
975 parsenodes
- recyclednodes
);
980 * Nowadays the threaded interpreter needs a stop instruction, so we
981 * do have to emit that here.
983 if (js_Emit1(cx
, &cg
, JSOP_STOP
) < 0)
985 #ifdef METER_PARSENODES
986 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
987 (char *)sbrk(0) - (char *)before
, CG_OFFSET(&cg
), cg
.noteCount
);
990 JS_DumpArenaStats(stdout
);
992 script
= js_NewScriptFromCG(cx
, &cg
);
993 if (script
&& funbox
)
994 script
->flags
|= JSSF_SAVED_CALLER_FUN
;
996 #ifdef JS_SCOPE_DEPTH_METER
998 JSObject
*obj
= scopeChain
;
1000 while ((obj
= OBJ_GET_PARENT(cx
, obj
)) != NULL
)
1002 JS_BASIC_STATS_ACCUM(&cx
->runtime
->hostenvScopeDepthStats
, depth
);
1007 JS_FinishArenaPool(&codePool
);
1008 JS_FinishArenaPool(¬ePool
);
1012 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
1013 JSREPORT_ERROR
, JSMSG_TOO_MANY_LOCALS
);
1019 * Insist on a final return before control flows out of pn. Try to be a bit
1020 * smart about loops: do {...; return e2;} while(0) at the end of a function
1021 * that contains an early return e1 will get a strict warning. Similarly for
1022 * iloops: while (true){...} is treated as though ... returns.
1024 #define ENDS_IN_OTHER 0
1025 #define ENDS_IN_RETURN 1
1026 #define ENDS_IN_BREAK 2
1029 HasFinalReturn(JSParseNode
*pn
)
1031 JSParseNode
*pn2
, *pn3
;
1032 uintN rv
, rv2
, hasDefault
;
1034 switch (pn
->pn_type
) {
1037 return ENDS_IN_OTHER
;
1038 return HasFinalReturn(pn
->last());
1042 return ENDS_IN_OTHER
;
1043 return HasFinalReturn(pn
->pn_kid2
) & HasFinalReturn(pn
->pn_kid3
);
1047 if (pn2
->pn_type
== TOK_PRIMARY
&& pn2
->pn_op
== JSOP_TRUE
)
1048 return ENDS_IN_RETURN
;
1049 if (pn2
->pn_type
== TOK_NUMBER
&& pn2
->pn_dval
)
1050 return ENDS_IN_RETURN
;
1051 return ENDS_IN_OTHER
;
1055 if (pn2
->pn_type
== TOK_PRIMARY
) {
1056 if (pn2
->pn_op
== JSOP_FALSE
)
1057 return HasFinalReturn(pn
->pn_left
);
1058 if (pn2
->pn_op
== JSOP_TRUE
)
1059 return ENDS_IN_RETURN
;
1061 if (pn2
->pn_type
== TOK_NUMBER
) {
1062 if (pn2
->pn_dval
== 0)
1063 return HasFinalReturn(pn
->pn_left
);
1064 return ENDS_IN_RETURN
;
1066 return ENDS_IN_OTHER
;
1070 if (pn2
->pn_arity
== PN_TERNARY
&& !pn2
->pn_kid2
)
1071 return ENDS_IN_RETURN
;
1072 return ENDS_IN_OTHER
;
1075 rv
= ENDS_IN_RETURN
;
1076 hasDefault
= ENDS_IN_OTHER
;
1078 if (pn2
->pn_type
== TOK_LEXICALSCOPE
)
1080 for (pn2
= pn2
->pn_head
; rv
&& pn2
; pn2
= pn2
->pn_next
) {
1081 if (pn2
->pn_type
== TOK_DEFAULT
)
1082 hasDefault
= ENDS_IN_RETURN
;
1083 pn3
= pn2
->pn_right
;
1084 JS_ASSERT(pn3
->pn_type
== TOK_LC
);
1086 rv2
= HasFinalReturn(pn3
->last());
1087 if (rv2
== ENDS_IN_OTHER
&& pn2
->pn_next
)
1088 /* Falling through to next case or default. */;
1093 /* If a final switch has no default case, we judge it harshly. */
1098 return ENDS_IN_BREAK
;
1101 return HasFinalReturn(pn
->pn_right
);
1104 return ENDS_IN_RETURN
;
1107 case TOK_LEXICALSCOPE
:
1108 return HasFinalReturn(pn
->expr());
1111 return ENDS_IN_RETURN
;
1114 /* If we have a finally block that returns, we are done. */
1116 rv
= HasFinalReturn(pn
->pn_kid3
);
1117 if (rv
== ENDS_IN_RETURN
)
1121 /* Else check the try block and any and all catch statements. */
1122 rv
= HasFinalReturn(pn
->pn_kid1
);
1124 JS_ASSERT(pn
->pn_kid2
->pn_arity
== PN_LIST
);
1125 for (pn2
= pn
->pn_kid2
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
1126 rv
&= HasFinalReturn(pn2
);
1131 /* Check this catch block's body. */
1132 return HasFinalReturn(pn
->pn_kid3
);
1135 /* Non-binary let statements are let declarations. */
1136 if (pn
->pn_arity
!= PN_BINARY
)
1137 return ENDS_IN_OTHER
;
1138 return HasFinalReturn(pn
->pn_right
);
1141 return ENDS_IN_OTHER
;
1146 ReportBadReturn(JSContext
*cx
, JSTreeContext
*tc
, uintN flags
, uintN errnum
,
1151 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1152 if (tc
->fun
->atom
) {
1153 name
= js_AtomToPrintableString(cx
, tc
->fun
->atom
);
1155 errnum
= anonerrnum
;
1158 return js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
, flags
,
1163 CheckFinalReturn(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*pn
)
1165 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1166 return HasFinalReturn(pn
) == ENDS_IN_RETURN
||
1167 ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
1168 JSMSG_NO_RETURN_VALUE
, JSMSG_ANON_NO_RETURN_VALUE
);
1171 static JSParseNode
*
1172 FunctionBody(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1174 JSStmtInfo stmtInfo
;
1175 uintN oldflags
, firstLine
;
1178 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1179 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
1180 stmtInfo
.flags
= SIF_BODY_BLOCK
;
1182 oldflags
= tc
->flags
;
1183 tc
->flags
&= ~(TCF_RETURN_EXPR
| TCF_RETURN_VOID
);
1186 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1187 * later, because we may have not peeked in ts yet, so Statements won't
1188 * acquire a valid pn->pn_pos.begin from the current token.
1190 firstLine
= ts
->lineno
;
1191 #if JS_HAS_EXPR_CLOSURES
1192 if (CURRENT_TOKEN(ts
).type
== TOK_LC
) {
1193 pn
= Statements(cx
, ts
, tc
);
1195 pn
= NewParseNode(PN_UNARY
, tc
);
1197 pn
->pn_kid
= AssignExpr(cx
, ts
, tc
);
1201 if (tc
->flags
& TCF_FUN_IS_GENERATOR
) {
1202 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
1203 JSMSG_BAD_GENERATOR_RETURN
,
1204 JSMSG_BAD_ANON_GENERATOR_RETURN
);
1207 pn
->pn_type
= TOK_RETURN
;
1208 pn
->pn_op
= JSOP_RETURN
;
1209 pn
->pn_pos
.end
= pn
->pn_kid
->pn_pos
.end
;
1215 pn
= Statements(cx
, ts
, tc
);
1219 JS_ASSERT(!(tc
->topStmt
->flags
& SIF_SCOPE
));
1220 js_PopStatement(tc
);
1221 pn
->pn_pos
.begin
.lineno
= firstLine
;
1223 /* Check for falling off the end of a function that returns a value. */
1224 if (JS_HAS_STRICT_OPTION(cx
) && (tc
->flags
& TCF_RETURN_EXPR
) &&
1225 !CheckFinalReturn(cx
, tc
, pn
)) {
1230 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
1234 static JSAtomListElement
*
1235 MakePlaceholder(JSParseNode
*pn
, JSTreeContext
*tc
)
1237 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, pn
->pn_atom
);
1241 JSDefinition
*dn
= (JSDefinition
*)
1242 NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), pn
->pn_atom
, tc
);
1246 ALE_SET_DEFN(ale
, dn
);
1248 dn
->pn_dflags
|= PND_PLACEHOLDER
;
1253 Define(JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
1255 JS_ASSERT(!pn
->pn_used
);
1256 JS_ASSERT_IF(pn
->pn_defn
, pn
->isPlaceholder());
1259 JSAtomListElement
*ale
= NULL
;
1260 JSAtomList
*list
= NULL
;
1263 ale
= (list
= &tc
->decls
)->rawLookup(atom
, hep
);
1265 ale
= (list
= &tc
->lexdeps
)->rawLookup(atom
, hep
);
1268 JSDefinition
*dn
= ALE_DEFN(ale
);
1270 JSParseNode
**pnup
= &dn
->dn_uses
;
1272 uintN start
= let
? pn
->pn_blockid
: tc
->bodyid
;
1274 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_blockid
>= start
) {
1275 JS_ASSERT(pnu
->pn_used
);
1276 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1277 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1278 pnup
= &pnu
->pn_link
;
1281 if (pnu
!= dn
->dn_uses
) {
1282 *pnup
= pn
->dn_uses
;
1283 pn
->dn_uses
= dn
->dn_uses
;
1286 if ((!pnu
|| pnu
->pn_blockid
< tc
->bodyid
) && list
!= &tc
->decls
)
1287 list
->rawRemove(tc
->compiler
, ale
, hep
);
1292 ale
= tc
->decls
.add(tc
->compiler
, atom
, let
? JSAtomList::SHADOW
: JSAtomList::UNIQUE
);
1295 ALE_SET_DEFN(ale
, pn
);
1297 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
1302 LinkUseToDef(JSParseNode
*pn
, JSDefinition
*dn
, JSTreeContext
*tc
)
1304 JS_ASSERT(!pn
->pn_used
);
1305 JS_ASSERT(!pn
->pn_defn
);
1306 JS_ASSERT(pn
!= dn
->dn_uses
);
1307 pn
->pn_link
= dn
->dn_uses
;
1314 ForgetUse(JSParseNode
*pn
)
1317 JS_ASSERT(!pn
->pn_defn
);
1321 JSParseNode
**pnup
= &pn
->lexdef()->dn_uses
;
1323 while ((pnu
= *pnup
) != pn
)
1324 pnup
= &pnu
->pn_link
;
1325 *pnup
= pn
->pn_link
;
1326 pn
->pn_used
= false;
1329 static JSParseNode
*
1330 MakeAssignment(JSParseNode
*pn
, JSParseNode
*rhs
, JSTreeContext
*tc
)
1332 JSParseNode
*lhs
= NewOrRecycledNode(tc
);
1338 JSDefinition
*dn
= pn
->pn_lexdef
;
1339 JSParseNode
**pnup
= &dn
->dn_uses
;
1342 pnup
= &(*pnup
)->pn_link
;
1344 lhs
->pn_link
= pn
->pn_link
;
1348 pn
->pn_type
= TOK_ASSIGN
;
1349 pn
->pn_op
= JSOP_NOP
;
1350 pn
->pn_arity
= PN_BINARY
;
1351 pn
->pn_parens
= false;
1352 pn
->pn_used
= pn
->pn_defn
= false;
1358 static JSParseNode
*
1359 MakeDefIntoUse(JSDefinition
*dn
, JSParseNode
*pn
, JSAtom
*atom
, JSTreeContext
*tc
)
1362 * If dn is var, const, or let, and it has an initializer, then we must
1363 * rewrite it to be an assignment node, whose freshly allocated left-hand
1364 * side becomes a use of pn.
1366 if (dn
->isBindingForm()) {
1367 JSParseNode
*rhs
= dn
->expr();
1369 JSParseNode
*lhs
= MakeAssignment(dn
, rhs
, tc
);
1372 //pn->dn_uses = lhs;
1373 dn
= (JSDefinition
*) lhs
;
1376 dn
->pn_op
= (js_CodeSpec
[dn
->pn_op
].format
& JOF_SET
) ? JSOP_SETNAME
: JSOP_NAME
;
1377 } else if (dn
->kind() == JSDefinition::FUNCTION
) {
1378 JS_ASSERT(dn
->isTopLevel());
1379 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
1380 dn
->pn_type
= TOK_NAME
;
1381 dn
->pn_arity
= PN_NAME
;
1385 /* Now make dn no longer a definition, rather a use of pn. */
1386 JS_ASSERT(dn
->pn_type
== TOK_NAME
);
1387 JS_ASSERT(dn
->pn_arity
== PN_NAME
);
1388 JS_ASSERT(dn
->pn_atom
== atom
);
1390 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
1391 JS_ASSERT(pnu
->pn_used
);
1392 JS_ASSERT(!pnu
->pn_defn
);
1393 pnu
->pn_lexdef
= (JSDefinition
*) pn
;
1394 pn
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
1396 pn
->pn_dflags
|= dn
->pn_dflags
& PND_USE2DEF_FLAGS
;
1399 dn
->pn_defn
= false;
1401 dn
->pn_lexdef
= (JSDefinition
*) pn
;
1402 dn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1403 dn
->pn_dflags
&= ~PND_BOUND
;
1408 DefineArg(JSParseNode
*pn
, JSAtom
*atom
, uintN i
, JSTreeContext
*tc
)
1410 JSParseNode
*argpn
, *argsbody
;
1412 /* Flag tc so we don't have to lookup arguments on every use. */
1413 if (atom
== tc
->compiler
->context
->runtime
->atomState
.argumentsAtom
)
1414 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1417 * Make an argument definition node, distinguished by being in tc->decls
1418 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1419 * list node returned via pn->pn_body.
1421 argpn
= NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), atom
, tc
);
1424 JS_ASSERT(PN_TYPE(argpn
) == TOK_NAME
&& PN_OP(argpn
) == JSOP_NOP
);
1426 /* Arguments are initialized by definition. */
1427 argpn
->pn_dflags
|= PND_INITIALIZED
;
1428 if (!Define(argpn
, atom
, tc
))
1431 argsbody
= pn
->pn_body
;
1433 argsbody
= NewParseNode(PN_LIST
, tc
);
1436 argsbody
->pn_type
= TOK_ARGSBODY
;
1437 argsbody
->pn_op
= JSOP_NOP
;
1438 argsbody
->makeEmpty();
1439 pn
->pn_body
= argsbody
;
1441 argsbody
->append(argpn
);
1443 argpn
->pn_op
= JSOP_GETARG
;
1444 argpn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, i
);
1445 argpn
->pn_dflags
|= PND_BOUND
;
1450 * Compile a JS function body, which might appear as the value of an event
1451 * handler attribute in an HTML <INPUT> tag.
1454 JSCompiler::compileFunctionBody(JSContext
*cx
, JSFunction
*fun
, JSPrincipals
*principals
,
1455 const jschar
*chars
, size_t length
,
1456 const char *filename
, uintN lineno
)
1458 JSCompiler
jsc(cx
, principals
);
1460 if (!jsc
.init(chars
, length
, NULL
, filename
, lineno
))
1463 /* No early return from after here until the js_FinishArenaPool calls. */
1464 JSArenaPool codePool
, notePool
;
1465 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
1466 &cx
->scriptStackQuota
);
1467 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
1468 &cx
->scriptStackQuota
);
1470 JSCodeGenerator
funcg(&jsc
, &codePool
, ¬ePool
, jsc
.tokenStream
.lineno
);
1471 funcg
.flags
|= TCF_IN_FUNCTION
;
1473 if (!GenerateBlockId(&funcg
, funcg
.bodyid
))
1476 /* FIXME: make Function format the source for a function definition. */
1477 jsc
.tokenStream
.tokens
[0].type
= TOK_NAME
;
1478 JSParseNode
*fn
= NewParseNode(PN_FUNC
, &funcg
);
1481 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
1483 uintN nargs
= fun
->nargs
;
1485 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
1489 for (uintN i
= 0; i
< nargs
; i
++) {
1490 JSAtom
*name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1491 if (!DefineArg(fn
, name
, i
, &funcg
)) {
1501 * Farble the body so that it looks like a block statement to js_EmitTree,
1502 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1503 * done parsing, we must fold constants, analyze any nested functions, and
1504 * generate code for this function, including a stop opcode at the end.
1506 CURRENT_TOKEN(&jsc
.tokenStream
).type
= TOK_LC
;
1507 JSParseNode
*pn
= fn
? FunctionBody(cx
, &jsc
.tokenStream
, &funcg
) : NULL
;
1509 if (!js_MatchToken(cx
, &jsc
.tokenStream
, TOK_EOF
)) {
1510 js_ReportCompileErrorNumber(cx
, &jsc
.tokenStream
, NULL
,
1511 JSREPORT_ERROR
, JSMSG_SYNTAX_ERROR
);
1513 } else if (!js_FoldConstants(cx
, pn
, &funcg
)) {
1514 /* js_FoldConstants reported the error already. */
1516 } else if (funcg
.functionList
&&
1517 !jsc
.analyzeFunctions(funcg
.functionList
, funcg
.flags
)) {
1521 JS_ASSERT(PN_TYPE(fn
->pn_body
) == TOK_ARGSBODY
);
1522 fn
->pn_body
->append(pn
);
1523 fn
->pn_body
->pn_pos
= pn
->pn_pos
;
1527 if (!js_EmitFunctionScript(cx
, &funcg
, pn
))
1532 /* Restore saved state and release code generation arenas. */
1533 JS_FinishArenaPool(&codePool
);
1534 JS_FinishArenaPool(¬ePool
);
1539 * Parameter block types for the several Binder functions. We use a common
1540 * helper function signature in order to share code among destructuring and
1541 * simple variable declaration parsers. In the destructuring case, the binder
1542 * function is called indirectly from the variable declaration parser by way
1543 * of CheckDestructuring and its friends.
1545 typedef struct BindData BindData
;
1548 (*Binder
)(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
);
1551 BindData() : fresh(true) {}
1553 JSParseNode
*pn
; /* name node for definition processing and
1554 error source coordinates */
1555 JSOp op
; /* prolog bytecode or nop */
1556 Binder binder
; /* binder, discriminates u */
1566 BindLocalVariable(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
,
1567 JSLocalKind localKind
)
1569 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1572 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1573 * Instead 'var arguments' always restates the predefined property of the
1574 * activation objects whose name is 'arguments'. Assignment to such a
1575 * variable must be handled specially.
1577 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
1580 return js_AddLocal(cx
, fun
, atom
, localKind
);
1583 #if JS_HAS_DESTRUCTURING
1585 * Forward declaration to maintain top-down presentation.
1587 static JSParseNode
*
1588 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
1592 BindDestructuringArg(JSContext
*cx
, BindData
*data
, JSAtom
*atom
,
1595 JSAtomListElement
*ale
;
1598 /* Flag tc so we don't have to lookup arguments on every use. */
1599 if (atom
== tc
->compiler
->context
->runtime
->atomState
.argumentsAtom
)
1600 tc
->flags
|= TCF_FUN_PARAM_ARGUMENTS
;
1602 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1603 ale
= tc
->decls
.lookup(atom
);
1605 if (!ale
&& !Define(pn
, atom
, tc
))
1608 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
1609 if (localKind
!= JSLOCAL_NONE
) {
1610 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
,
1611 JSREPORT_ERROR
, JSMSG_DESTRUCT_DUP_ARG
);
1615 uintN index
= tc
->fun
->u
.i
.nvars
;
1616 if (!BindLocalVariable(cx
, tc
->fun
, atom
, JSLOCAL_VAR
))
1618 pn
->pn_op
= JSOP_SETLOCAL
;
1619 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
1620 pn
->pn_dflags
|= PND_BOUND
;
1623 #endif /* JS_HAS_DESTRUCTURING */
1626 JSCompiler::newFunction(JSTreeContext
*tc
, JSAtom
*atom
, uintN lambda
)
1631 JS_ASSERT((lambda
& ~JSFUN_LAMBDA
) == 0);
1634 * Find the global compilation context in order to pre-set the newborn
1635 * function's parent slot to tc->scopeChain. If the global context is a
1636 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1637 * clear parent and proto.
1641 parent
= (tc
->flags
& TCF_IN_FUNCTION
) ? NULL
: tc
->scopeChain
;
1643 fun
= js_NewFunction(context
, NULL
, NULL
, 0, JSFUN_INTERPRETED
| lambda
,
1646 if (fun
&& !(tc
->flags
& TCF_COMPILE_N_GO
)) {
1647 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun
));
1648 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun
));
1654 MatchOrInsertSemicolon(JSContext
*cx
, JSTokenStream
*ts
)
1658 ts
->flags
|= TSF_OPERAND
;
1659 tt
= js_PeekTokenSameLine(cx
, ts
);
1660 ts
->flags
&= ~TSF_OPERAND
;
1661 if (tt
== TOK_ERROR
)
1663 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
1664 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1665 JSMSG_SEMI_BEFORE_STMNT
);
1668 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
1673 JSCompiler::analyzeFunctions(JSFunctionBox
*funbox
, uint16
& tcflags
)
1675 if (!markFunArgs(funbox
, tcflags
))
1677 setFunctionKinds(funbox
, tcflags
);
1682 * Mark as funargs any functions that reach up to one or more upvars across an
1683 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1685 * function f(o, p) {
1686 * o.m = function o_m(a) {
1687 * function g() { return p; }
1688 * function h() { return a; }
1693 * but without this extra marking phase, function g will not be marked as a
1694 * funarg since it is called from within its parent scope. But g reaches up to
1695 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1696 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1697 * nor uses an upvar "above" o_m's level.
1699 * If function g itself contained lambdas that contained non-lambdas that reach
1700 * up above its level, then those non-lambdas would have to be marked too. This
1701 * process is potentially exponential in the number of functions, but generally
1702 * not so complex. But it can't be done during a single recursive traversal of
1703 * the funbox tree, so we must use a work queue.
1705 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1706 * between the static level of the bodies of funbox and its peers (which must
1707 * be funbox->level + 1), and the static level of the nearest upvar among all
1708 * the upvars contained by funbox and its peers. If there are no upvars, return
1709 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1712 FindFunArgs(JSFunctionBox
*funbox
, int level
, JSFunctionBoxQueue
*queue
)
1714 uintN allskipmin
= FREE_STATIC_LEVEL
;
1717 JSParseNode
*fn
= funbox
->node
;
1718 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
1719 int fnlevel
= level
;
1722 * An eval can leak funbox, functions along its ancestor line, and its
1723 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1724 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1725 * already been marked as funargs by this point. Therefore we have to
1726 * flag only funbox->node and funbox->kids' nodes here.
1728 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1730 for (JSFunctionBox
*kid
= funbox
->kids
; kid
; kid
= kid
->siblings
)
1731 kid
->node
->setFunArg();
1735 * Compute in skipmin the least distance from fun's static level up to
1736 * an upvar, whether used directly by fun, or indirectly by a function
1739 uintN skipmin
= FREE_STATIC_LEVEL
;
1740 JSParseNode
*pn
= fn
->pn_body
;
1742 if (pn
->pn_type
== TOK_UPVARS
) {
1743 JSAtomList
upvars(pn
->pn_names
);
1744 JS_ASSERT(upvars
.count
!= 0);
1746 JSAtomListIterator
iter(&upvars
);
1747 JSAtomListElement
*ale
;
1749 while ((ale
= iter()) != NULL
) {
1750 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1752 if (!lexdep
->isFreeVar()) {
1753 uintN upvarLevel
= lexdep
->frameLevel();
1755 if (int(upvarLevel
) <= fnlevel
)
1758 uintN skip
= (funbox
->level
+ 1) - upvarLevel
;
1766 * If this function escapes, whether directly (the parser detects such
1767 * escapes) or indirectly (because this non-escaping function uses an
1768 * upvar that reaches across an outer function boundary where the outer
1769 * function escapes), enqueue it for further analysis, and bump fnlevel
1770 * to trap any non-escaping children.
1772 if (fn
->isFunArg()) {
1773 queue
->push(funbox
);
1774 fnlevel
= int(funbox
->level
);
1778 * Now process the current function's children, and recalibrate their
1779 * cumulative skipmin to be relative to the current static level.
1782 uintN kidskipmin
= FindFunArgs(funbox
->kids
, fnlevel
, queue
);
1784 JS_ASSERT(kidskipmin
!= 0);
1785 if (kidskipmin
!= FREE_STATIC_LEVEL
) {
1787 if (kidskipmin
!= 0 && kidskipmin
< skipmin
)
1788 skipmin
= kidskipmin
;
1793 * Finally, after we've traversed all of the current function's kids,
1794 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1795 * with allskipmin, but minimize across funbox and all of its siblings,
1796 * to compute our return value.
1798 if (skipmin
!= FREE_STATIC_LEVEL
) {
1799 fun
->u
.i
.skipmin
= skipmin
;
1800 if (skipmin
< allskipmin
)
1801 allskipmin
= skipmin
;
1803 } while ((funbox
= funbox
->siblings
) != NULL
);
1809 JSCompiler::markFunArgs(JSFunctionBox
*funbox
, uintN tcflags
)
1811 JSFunctionBoxQueue queue
;
1812 if (!queue
.init(functionCount
))
1815 FindFunArgs(funbox
, -1, &queue
);
1816 while ((funbox
= queue
.pull()) != NULL
) {
1817 JSParseNode
*fn
= funbox
->node
;
1818 JS_ASSERT(fn
->isFunArg());
1820 JSParseNode
*pn
= fn
->pn_body
;
1821 if (pn
->pn_type
== TOK_UPVARS
) {
1822 JSAtomList
upvars(pn
->pn_names
);
1823 JS_ASSERT(upvars
.count
!= 0);
1825 JSAtomListIterator
iter(&upvars
);
1826 JSAtomListElement
*ale
;
1828 while ((ale
= iter()) != NULL
) {
1829 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1831 if (!lexdep
->isFreeVar() &&
1832 !lexdep
->isFunArg() &&
1833 lexdep
->kind() == JSDefinition::FUNCTION
) {
1835 * Mark this formerly-Algol-like function as an escaping
1836 * function (i.e., as a funarg), because it is used from a
1837 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1840 * Progress is guaranteed because we set the funarg flag
1841 * here, which suppresses revisiting this function (thanks
1842 * to the !lexdep->isFunArg() test just above).
1844 lexdep
->setFunArg();
1846 JSFunctionBox
*afunbox
= lexdep
->pn_funbox
;
1847 queue
.push(afunbox
);
1850 * Walk over nested functions again, now that we have
1851 * changed the level across which it is unsafe to access
1852 * upvars using the runtime dynamic link (frame chain).
1855 FindFunArgs(afunbox
->kids
, afunbox
->level
, &queue
);
1864 MinBlockId(JSParseNode
*fn
, uint32 id
)
1866 if (fn
->pn_blockid
< id
)
1869 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
1870 if (pn
->pn_blockid
< id
)
1878 OneBlockId(JSParseNode
*fn
, uint32 id
)
1880 if (fn
->pn_blockid
!= id
)
1883 for (JSParseNode
*pn
= fn
->dn_uses
; pn
; pn
= pn
->pn_link
) {
1884 if (pn
->pn_blockid
!= id
)
1892 JSCompiler::setFunctionKinds(JSFunctionBox
*funbox
, uint16
& tcflags
)
1894 #ifdef JS_FUNCTION_METERING
1895 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
1897 # define FUN_METER(x) ((void)0)
1899 JSFunctionBox
*parent
= funbox
->parent
;
1902 JSParseNode
*fn
= funbox
->node
;
1905 setFunctionKinds(funbox
->kids
, tcflags
);
1907 JSParseNode
*pn
= fn
->pn_body
;
1908 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
1911 if (funbox
->tcflags
& TCF_FUN_HEAVYWEIGHT
) {
1913 JS_ASSERT(FUN_KIND(fun
) == JSFUN_INTERPRETED
);
1914 } else if (pn
->pn_type
!= TOK_UPVARS
) {
1916 * No lexical dependencies => null closure, for best performance.
1917 * A null closure needs no scope chain, but alas we've coupled
1918 * principals-finding to scope (for good fundamental reasons, but
1919 * the implementation overloads the parent slot and we should fix
1920 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1922 * In more detail: the ES3 spec allows the implementation to create
1923 * "joined function objects", or not, at its discretion. But real-
1924 * world implementations always create unique function objects for
1925 * closures, and this can be detected via mutation. Open question:
1926 * do popular implementations create unique function objects for
1929 * FIXME: bug 476950.
1931 FUN_METER(nofreeupvar
);
1932 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1934 JSAtomList
upvars(pn
->pn_names
);
1935 JS_ASSERT(upvars
.count
!= 0);
1937 JSAtomListIterator
iter(&upvars
);
1938 JSAtomListElement
*ale
;
1940 if (!fn
->isFunArg()) {
1942 * This function is Algol-like, it never escapes. So long as it
1943 * does not assign to outer variables, it needs only an upvars
1944 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1945 * bytecode to reach up the frame stack at runtime based on
1946 * those upvars' cookies.
1948 * Any assignments to upvars from functions called by this one
1949 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1950 * which load from stack homes when interpreting or from native
1951 * stack slots when executing a trace.
1953 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1954 * nested function to assign to an outer lexical variable, so
1955 * we defer adding yet more code footprint in the absence of
1956 * evidence motivating these opcodes.
1958 bool mutation
= !!(funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
);
1962 * Check that at least one outer lexical binding was assigned
1963 * to (global variables don't count). This is conservative: we
1964 * could limit assignments to those in the current function,
1965 * but that's too much work. As with flat closures (handled
1966 * below), we optimize for the case where outer bindings are
1967 * not reassigned anywhere.
1969 while ((ale
= iter()) != NULL
) {
1970 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
1972 if (!lexdep
->isFreeVar()) {
1973 JS_ASSERT(lexdep
->frameLevel() <= funbox
->level
);
1975 if (lexdep
->isAssigned())
1983 FUN_METER(onlyfreevar
);
1984 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1985 } else if (!mutation
&& !(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
)) {
1987 * Algol-like functions can read upvars using the dynamic
1988 * link (cx->fp/fp->down). They do not need to entrain and
1989 * search their environment.
1992 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
1994 if (!(funbox
->tcflags
& TCF_FUN_IS_GENERATOR
))
1995 FUN_METER(setupvar
);
2001 * For each lexical dependency from this closure to an outer
2002 * binding, analyze whether it is safe to copy the binding's
2003 * value into a flat closure slot when the closure is formed.
2005 while ((ale
= iter()) != NULL
) {
2006 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2008 if (!lexdep
->isFreeVar()) {
2012 * Consider the current function (the lambda, innermost
2013 * below) using a var x defined two static levels up:
2019 * return function () { return x; };
2024 * So long as (1) the initialization in 'var x = 42'
2025 * dominates all uses of g and (2) x is not reassigned,
2026 * it is safe to optimize the lambda to a flat closure.
2027 * Uncommenting the early call to g makes it unsafe to
2028 * so optimize (z could name a global setter that calls
2031 JSFunctionBox
*afunbox
= funbox
;
2032 uintN lexdepLevel
= lexdep
->frameLevel();
2034 JS_ASSERT(lexdepLevel
<= funbox
->level
);
2035 while (afunbox
->level
!= lexdepLevel
) {
2036 afunbox
= afunbox
->parent
;
2039 * afunbox can't be null because we are sure
2040 * to find a function box whose level == lexdepLevel
2041 * before walking off the top of the funbox tree.
2042 * See bug 493260 comments 16-18.
2044 * Assert but check anyway, to check future changes
2045 * that bind eval upvars in the parser.
2050 * If this function is reaching up across an
2051 * enclosing funarg, we cannot make a flat
2052 * closure. The display stops working once the
2055 if (!afunbox
|| afunbox
->node
->isFunArg())
2060 * If afunbox's function (which is at the same level as
2061 * lexdep) is in a loop, pessimistically assume the
2062 * variable initializer may be in the same loop. A flat
2063 * closure would then be unsafe, as the captured
2064 * variable could be assigned after the closure is
2065 * created. See bug 493232.
2067 if (afunbox
->inLoop
)
2071 * with and eval defeat lexical scoping; eval anywhere
2072 * in a variable's scope can assign to it. Both defeat
2073 * the flat closure optimization. The parser detects
2074 * these cases and flags the function heavyweight.
2076 if ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
)
2077 & TCF_FUN_HEAVYWEIGHT
) {
2082 * If afunbox's function is not a lambda, it will be
2083 * hoisted, so it could capture the undefined value
2084 * that by default initializes var/let/const
2085 * bindings. And if lexdep is a function that comes at
2086 * (meaning a function refers to its own name) or
2087 * strictly after afunbox, we also break to defeat the
2088 * flat closure optimization.
2090 JSFunction
*afun
= (JSFunction
*) afunbox
->object
;
2091 if (!(afun
->flags
& JSFUN_LAMBDA
)) {
2092 if (lexdep
->isBindingForm())
2094 if (lexdep
->pn_pos
>= afunbox
->node
->pn_pos
)
2098 if (!lexdep
->isInitialized())
2101 JSDefinition::Kind lexdepKind
= lexdep
->kind();
2102 if (lexdepKind
!= JSDefinition::CONST
) {
2103 if (lexdep
->isAssigned())
2107 * Any formal could be mutated behind our back via
2108 * the arguments object, so deoptimize if the outer
2109 * function uses arguments.
2111 * In a Function constructor call where the final
2112 * argument -- the body source for the function to
2113 * create -- contains a nested function definition
2114 * or expression, afunbox->parent will be null. The
2115 * body source might use |arguments| outside of any
2116 * nested functions it may contain, so we have to
2117 * check the tcflags parameter that was passed in
2118 * from JSCompiler::compileFunctionBody.
2120 if (lexdepKind
== JSDefinition::ARG
&&
2121 ((afunbox
->parent
? afunbox
->parent
->tcflags
: tcflags
) &
2122 TCF_FUN_USES_ARGUMENTS
)) {
2128 * Check quick-and-dirty dominance relation. Function
2129 * definitions dominate their uses thanks to hoisting.
2130 * Other binding forms hoist as undefined, of course,
2131 * so check forward-reference and blockid relations.
2133 if (lexdepKind
!= JSDefinition::FUNCTION
) {
2135 * Watch out for code such as
2139 * var jQuery = ... = function (...) {
2140 * return new jQuery.foo.bar(baz);
2145 * where the jQuery var is not reassigned, but of
2146 * course is not initialized at the time that the
2147 * would-be-flat closure containing the jQuery
2150 if (lexdep
->pn_pos
.end
>= afunbox
->node
->pn_pos
.end
)
2153 if (lexdep
->isTopLevel()
2154 ? !MinBlockId(afunbox
->node
, lexdep
->pn_blockid
)
2155 : !lexdep
->isBlockChild() ||
2156 !afunbox
->node
->isBlockChild() ||
2157 !OneBlockId(afunbox
->node
, lexdep
->pn_blockid
)) {
2166 FUN_METER(onlyfreevar
);
2167 FUN_SET_KIND(fun
, JSFUN_NULL_CLOSURE
);
2170 * We made it all the way through the upvar loop, so it's
2171 * safe to optimize to a flat closure.
2174 FUN_SET_KIND(fun
, JSFUN_FLAT_CLOSURE
);
2175 switch (PN_OP(fn
)) {
2177 fn
->pn_op
= JSOP_DEFFUN_FC
;
2179 case JSOP_DEFLOCALFUN
:
2180 fn
->pn_op
= JSOP_DEFLOCALFUN_FC
;
2183 fn
->pn_op
= JSOP_LAMBDA_FC
;
2186 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2187 JS_ASSERT(PN_OP(fn
) == JSOP_NOP
);
2190 FUN_METER(badfunarg
);
2195 if (FUN_KIND(fun
) == JSFUN_INTERPRETED
) {
2196 if (pn
->pn_type
!= TOK_UPVARS
) {
2198 parent
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2200 JSAtomList
upvars(pn
->pn_names
);
2201 JS_ASSERT(upvars
.count
!= 0);
2203 JSAtomListIterator
iter(&upvars
);
2204 JSAtomListElement
*ale
;
2207 * One or more upvars cannot be safely snapshot into a flat
2208 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2209 * all upvars, and for each non-free upvar, ensure that its
2210 * containing function has been flagged as heavyweight.
2212 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2213 * generating any code for a tree of nested functions.
2215 while ((ale
= iter()) != NULL
) {
2216 JSDefinition
*lexdep
= ALE_DEFN(ale
)->resolve();
2218 if (!lexdep
->isFreeVar()) {
2219 JSFunctionBox
*afunbox
= funbox
->parent
;
2220 uintN lexdepLevel
= lexdep
->frameLevel();
2224 * NB: afunbox->level is the static level of
2225 * the definition or expression of the function
2226 * parsed into afunbox, not the static level of
2227 * its body. Therefore we must add 1 to match
2228 * lexdep's level to find the afunbox whose
2229 * body contains the lexdep definition.
2231 if (afunbox
->level
+ 1U == lexdepLevel
||
2232 (lexdepLevel
== 0 && lexdep
->isLet())) {
2233 afunbox
->tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2236 afunbox
= afunbox
->parent
;
2238 if (!afunbox
&& (tcflags
& TCF_IN_FUNCTION
))
2239 tcflags
|= TCF_FUN_HEAVYWEIGHT
;
2245 funbox
= funbox
->siblings
;
2248 JS_ASSERT(funbox
->parent
== parent
);
2253 const char js_argument_str
[] = "argument";
2254 const char js_variable_str
[] = "variable";
2255 const char js_unknown_str
[] = "unknown";
2258 JSDefinition::kindString(Kind kind
)
2260 static const char *table
[] = {
2261 js_var_str
, js_const_str
, js_let_str
,
2262 js_function_str
, js_argument_str
, js_unknown_str
2265 JS_ASSERT(unsigned(kind
) <= unsigned(ARG
));
2269 static JSFunctionBox
*
2270 EnterFunction(JSParseNode
*fn
, JSTreeContext
*tc
, JSTreeContext
*funtc
,
2271 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2273 JSFunction
*fun
= tc
->compiler
->newFunction(tc
, funAtom
, lambda
);
2277 /* Create box for fun->object early to protect against last-ditch GC. */
2278 JSFunctionBox
*funbox
= tc
->compiler
->newFunctionBox(FUN_OBJECT(fun
), fn
, tc
);
2282 /* Initialize non-default members of funtc. */
2283 funtc
->flags
|= funbox
->tcflags
;
2284 funtc
->blockidGen
= tc
->blockidGen
;
2285 if (!GenerateBlockId(funtc
, funtc
->bodyid
))
2288 funtc
->funbox
= funbox
;
2290 if (!SetStaticLevel(funtc
, tc
->staticLevel
+ 1))
2297 LeaveFunction(JSParseNode
*fn
, JSTreeContext
*funtc
, JSTreeContext
*tc
,
2298 JSAtom
*funAtom
= NULL
, uintN lambda
= JSFUN_LAMBDA
)
2300 tc
->blockidGen
= funtc
->blockidGen
;
2302 fn
->pn_funbox
->tcflags
|= funtc
->flags
& (TCF_FUN_FLAGS
| TCF_COMPILE_N_GO
);
2304 fn
->pn_dflags
|= PND_INITIALIZED
;
2305 JS_ASSERT_IF(tc
->atTopLevel() && lambda
== 0 && funAtom
,
2306 fn
->pn_dflags
& PND_TOPLEVEL
);
2307 if (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)
2308 fn
->pn_dflags
|= PND_BLOCKCHILD
;
2311 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2312 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2313 * params and body. We do this only if there are lexical dependencies not
2314 * satisfied by the function's declarations, to avoid penalizing functions
2315 * that use only their arguments and other local bindings.
2317 if (funtc
->lexdeps
.count
!= 0) {
2318 JSAtomListIterator
iter(&funtc
->lexdeps
);
2319 JSAtomListElement
*ale
;
2320 int foundCallee
= 0;
2322 while ((ale
= iter()) != NULL
) {
2323 JSAtom
*atom
= ALE_ATOM(ale
);
2324 JSDefinition
*dn
= ALE_DEFN(ale
);
2325 JS_ASSERT(dn
->isPlaceholder());
2327 if (atom
== funAtom
&& lambda
!= 0) {
2328 dn
->pn_op
= JSOP_CALLEE
;
2329 dn
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
->staticLevel
, CALLEE_UPVAR_SLOT
);
2330 dn
->pn_dflags
|= PND_BOUND
;
2333 * If this named function expression uses its own name other
2334 * than to call itself, flag this function as using arguments,
2335 * as if it had used arguments.callee instead of its own name.
2337 * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2338 * we are out of tcflags bits at the moment. If it deoptimizes
2339 * code unfairly (see JSCompiler::setFunctionKinds, where this
2340 * flag is interpreted in its broader sense, not only to mean
2341 * "this function might leak arguments.callee"), we can perhaps
2342 * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2343 * use that more precisely, both here and for unnamed function
2347 fn
->pn_funbox
->tcflags
|= TCF_FUN_USES_ARGUMENTS
;
2352 if (!(fn
->pn_funbox
->tcflags
& TCF_FUN_SETS_OUTER_NAME
) &&
2355 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2356 * any use of dn in funtc assigns. See NoteLValue for the easy
2357 * backward-reference case; this is the hard forward-reference
2358 * case where we pay a higher price.
2360 for (JSParseNode
*pnu
= dn
->dn_uses
; pnu
; pnu
= pnu
->pn_link
) {
2361 if (pnu
->isAssigned() && pnu
->pn_blockid
>= funtc
->bodyid
) {
2362 fn
->pn_funbox
->tcflags
|= TCF_FUN_SETS_OUTER_NAME
;
2368 JSAtomListElement
*outer_ale
= tc
->decls
.lookup(atom
);
2370 outer_ale
= tc
->lexdeps
.lookup(atom
);
2373 * Insert dn's uses list at the front of outer_dn's list.
2375 * Without loss of generality or correctness, we allow a dn to
2376 * be in inner and outer lexdeps, since the purpose of lexdeps
2377 * is one-pass coordination of name use and definition across
2378 * functions, and if different dn's are used we'll merge lists
2379 * when leaving the inner function.
2381 * The dn == outer_dn case arises with generator expressions
2382 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2383 * case), and nowhere else, currently.
2385 JSDefinition
*outer_dn
= ALE_DEFN(outer_ale
);
2387 if (dn
!= outer_dn
) {
2388 JSParseNode
**pnup
= &dn
->dn_uses
;
2391 while ((pnu
= *pnup
) != NULL
) {
2392 pnu
->pn_lexdef
= outer_dn
;
2393 pnup
= &pnu
->pn_link
;
2397 * Make dn be a use that redirects to outer_dn, because we
2398 * can't replace dn with outer_dn in all the pn_namesets in
2399 * the AST where it may be. Instead we make it forward to
2400 * outer_dn. See JSDefinition::resolve.
2402 *pnup
= outer_dn
->dn_uses
;
2403 outer_dn
->dn_uses
= dn
;
2404 outer_dn
->pn_dflags
|= dn
->pn_dflags
& ~PND_PLACEHOLDER
;
2405 dn
->pn_defn
= false;
2407 dn
->pn_lexdef
= outer_dn
;
2410 /* Add an outer lexical dependency for ale's definition. */
2411 outer_ale
= tc
->lexdeps
.add(tc
->compiler
, atom
);
2414 ALE_SET_DEFN(outer_ale
, ALE_DEFN(ale
));
2418 if (funtc
->lexdeps
.count
- foundCallee
!= 0) {
2419 JSParseNode
*body
= fn
->pn_body
;
2421 fn
->pn_body
= NewParseNode(PN_NAMESET
, tc
);
2425 fn
->pn_body
->pn_type
= TOK_UPVARS
;
2426 fn
->pn_body
->pn_pos
= body
->pn_pos
;
2428 funtc
->lexdeps
.remove(tc
->compiler
, funAtom
);
2429 fn
->pn_body
->pn_names
= funtc
->lexdeps
;
2430 fn
->pn_body
->pn_tree
= body
;
2433 funtc
->lexdeps
.clear();
2439 static JSParseNode
*
2440 FunctionDef(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2444 JSParseNode
*pn
, *body
, *result
;
2447 JSAtomListElement
*ale
;
2448 #if JS_HAS_DESTRUCTURING
2449 JSParseNode
*item
, *list
= NULL
;
2450 bool destructuringArg
= false, duplicatedArg
= false;
2453 /* Make a TOK_FUNCTION node. */
2454 #if JS_HAS_GETTER_SETTER
2455 op
= CURRENT_TOKEN(ts
).t_op
;
2457 pn
= NewParseNode(PN_FUNC
, tc
);
2461 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2464 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2465 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2467 * Also treat function sub-statements (non-lambda, non-top-level functions)
2468 * as escaping funargs, since we can't statically analyze their definitions
2471 bool topLevel
= tc
->atTopLevel();
2472 pn
->pn_dflags
= (lambda
|| !topLevel
) ? PND_FUNARG
: 0;
2474 /* Scan the optional function name into funAtom. */
2475 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
2476 tt
= js_GetToken(cx
, ts
);
2477 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
2478 if (tt
== TOK_NAME
) {
2479 funAtom
= CURRENT_TOKEN(ts
).t_atom
;
2481 if (lambda
== 0 && (cx
->options
& JSOPTION_ANONFUNFIX
)) {
2482 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2483 JSMSG_SYNTAX_ERROR
);
2491 * Record names for function statements in tc->decls so we know when to
2492 * avoid optimizing variable references that might name a function.
2494 if (lambda
== 0 && funAtom
) {
2495 ale
= tc
->decls
.lookup(funAtom
);
2497 JSDefinition
*dn
= ALE_DEFN(ale
);
2498 JSDefinition::Kind dn_kind
= dn
->kind();
2500 JS_ASSERT(!dn
->pn_used
);
2501 JS_ASSERT(dn
->pn_defn
);
2503 if (JS_HAS_STRICT_OPTION(cx
) || dn_kind
== JSDefinition::CONST
) {
2504 const char *name
= js_AtomToPrintableString(cx
, funAtom
);
2506 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2507 (dn_kind
!= JSDefinition::CONST
)
2508 ? JSREPORT_WARNING
| JSREPORT_STRICT
2510 JSMSG_REDECLARED_VAR
,
2511 JSDefinition::kindString(dn_kind
),
2518 ALE_SET_DEFN(ale
, pn
);
2520 pn
->dn_uses
= dn
; /* dn->dn_uses is now pn_link */
2522 if (!MakeDefIntoUse(dn
, pn
, funAtom
, tc
))
2525 } else if (topLevel
) {
2527 * If this function was used before it was defined, claim the
2528 * pre-created definition node for this function that PrimaryExpr
2529 * put in tc->lexdeps on first forward reference, and recycle pn.
2533 ale
= tc
->lexdeps
.rawLookup(funAtom
, hep
);
2535 JSDefinition
*fn
= ALE_DEFN(ale
);
2537 JS_ASSERT(fn
->pn_defn
);
2538 fn
->pn_type
= TOK_FUNCTION
;
2539 fn
->pn_arity
= PN_FUNC
;
2540 fn
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2542 fn
->pn_cookie
= FREE_UPVAR_COOKIE
;
2544 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
2545 RecycleTree(pn
, tc
);
2549 if (!Define(pn
, funAtom
, tc
))
2554 * A function nested at top level inside another's body needs only a
2555 * local variable to bind its name to its value, and not an activation
2556 * object property (it might also need the activation property, if the
2557 * outer function contains with statements, e.g., but the stack slot
2558 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2559 * JSOP_GETLOCAL bytecode).
2562 pn
->pn_dflags
|= PND_TOPLEVEL
;
2564 if (tc
->flags
& TCF_IN_FUNCTION
) {
2565 JSLocalKind localKind
;
2569 * Define a local in the outer function so that BindNameToSlot
2570 * can properly optimize accesses. Note that we need a local
2571 * variable, not an argument, for the function statement. Thus
2572 * we add a variable even if a parameter with the given name
2575 localKind
= js_LookupLocal(cx
, tc
->fun
, funAtom
, &index
);
2576 switch (localKind
) {
2579 index
= tc
->fun
->u
.i
.nvars
;
2580 if (!js_AddLocal(cx
, tc
->fun
, funAtom
, JSLOCAL_VAR
))
2585 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
2586 pn
->pn_dflags
|= PND_BOUND
;
2595 /* Initialize early for possible flags mutation via DestructuringExpr. */
2596 JSTreeContext
funtc(tc
->compiler
);
2598 JSFunctionBox
*funbox
= EnterFunction(pn
, tc
, &funtc
, funAtom
, lambda
);
2602 JSFunction
*fun
= (JSFunction
*) funbox
->object
;
2604 #if JS_HAS_GETTER_SETTER
2606 fun
->flags
|= (op
== JSOP_GETTER
) ? JSPROP_GETTER
: JSPROP_SETTER
;
2609 /* Now parse formal argument list and compute fun->nargs. */
2610 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_FORMAL
);
2611 if (!js_MatchToken(cx
, ts
, TOK_RP
)) {
2613 tt
= js_GetToken(cx
, ts
);
2615 #if JS_HAS_DESTRUCTURING
2620 JSParseNode
*lhs
, *rhs
;
2623 /* See comment below in the TOK_NAME case. */
2625 goto report_dup_and_destructuring
;
2626 destructuringArg
= true;
2629 * A destructuring formal parameter turns into one or more
2630 * local variables initialized from properties of a single
2631 * anonymous positional parameter, so here we must tweak our
2632 * binder and its data.
2635 data
.op
= JSOP_DEFVAR
;
2636 data
.binder
= BindDestructuringArg
;
2637 lhs
= DestructuringExpr(cx
, &data
, &funtc
, tt
);
2642 * Adjust fun->nargs to count the single anonymous positional
2643 * parameter that is to be destructured.
2646 if (!js_AddLocal(cx
, fun
, NULL
, JSLOCAL_ARG
))
2650 * Synthesize a destructuring assignment from the single
2651 * anonymous positional parameter into the destructuring
2652 * left-hand-side expression and accumulate it in list.
2654 rhs
= NewNameNode(cx
, ts
, cx
->runtime
->atomState
.emptyAtom
, &funtc
);
2657 rhs
->pn_type
= TOK_NAME
;
2658 rhs
->pn_op
= JSOP_GETARG
;
2659 rhs
->pn_cookie
= MAKE_UPVAR_COOKIE(funtc
.staticLevel
, slot
);
2660 rhs
->pn_dflags
|= PND_BOUND
;
2662 item
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, lhs
, rhs
, &funtc
);
2666 list
= NewParseNode(PN_LIST
, &funtc
);
2669 list
->pn_type
= TOK_COMMA
;
2675 #endif /* JS_HAS_DESTRUCTURING */
2680 * Check for a duplicate parameter name, a "feature" that
2681 * ECMA-262 requires. This is a SpiderMonkey strict warning,
2682 * soon to be an ES3.1 strict error.
2684 * Further, if any argument is a destructuring pattern, forbid
2685 * duplicates. We will report the error either now if we have
2686 * seen a destructuring pattern already, or later when we find
2687 * the first pattern.
2689 JSAtom
*atom
= CURRENT_TOKEN(ts
).t_atom
;
2690 if (JS_HAS_STRICT_OPTION(cx
) &&
2691 js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2692 #if JS_HAS_DESTRUCTURING
2693 if (destructuringArg
)
2694 goto report_dup_and_destructuring
;
2695 duplicatedArg
= true;
2697 const char *name
= js_AtomToPrintableString(cx
, atom
);
2699 !js_ReportCompileErrorNumber(cx
, TS(funtc
.compiler
),
2703 JSMSG_DUPLICATE_FORMAL
,
2708 if (!DefineArg(pn
, atom
, fun
->nargs
, &funtc
))
2710 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2716 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2717 JSMSG_MISSING_FORMAL
);
2722 #if JS_HAS_DESTRUCTURING
2723 report_dup_and_destructuring
:
2724 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), NULL
,
2726 JSMSG_DESTRUCT_DUP_ARG
);
2730 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
2732 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FORMAL
);
2735 #if JS_HAS_EXPR_CLOSURES
2736 ts
->flags
|= TSF_OPERAND
;
2737 tt
= js_GetToken(cx
, ts
);
2738 ts
->flags
&= ~TSF_OPERAND
;
2741 fun
->flags
|= JSFUN_EXPR_CLOSURE
;
2744 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_BODY
);
2747 body
= FunctionBody(cx
, ts
, &funtc
);
2751 #if JS_HAS_EXPR_CLOSURES
2753 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2754 else if (lambda
== 0 && !MatchOrInsertSemicolon(cx
, ts
))
2757 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
2759 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2761 #if JS_HAS_DESTRUCTURING
2763 * If there were destructuring formal parameters, prepend the initializing
2764 * comma expression that we synthesized to body. If the body is a lexical
2765 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2766 * parameter destructuring code without bracing the decompilation of the
2767 * function body's lexical scope.
2770 if (body
->pn_arity
!= PN_LIST
) {
2773 block
= NewParseNode(PN_LIST
, tc
);
2776 block
->pn_type
= TOK_SEQ
;
2777 block
->pn_pos
= body
->pn_pos
;
2778 block
->initList(body
);
2783 item
= NewParseNode(PN_UNARY
, tc
);
2787 item
->pn_type
= TOK_SEMI
;
2788 item
->pn_pos
.begin
= item
->pn_pos
.end
= body
->pn_pos
.begin
;
2789 item
->pn_kid
= list
;
2790 item
->pn_next
= body
->pn_head
;
2791 body
->pn_head
= item
;
2792 if (body
->pn_tail
== &body
->pn_head
)
2793 body
->pn_tail
= &item
->pn_next
;
2795 body
->pn_xflags
|= PNX_DESTRUCT
;
2800 * If we collected flags that indicate nested heavyweight functions, or
2801 * this function contains heavyweight-making statements (with statement,
2802 * visible eval call, or assignment to 'arguments'), flag the function as
2803 * heavyweight (requiring a call object per invocation).
2805 if (funtc
.flags
& TCF_FUN_HEAVYWEIGHT
) {
2806 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
2807 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2810 * If this function is a named statement function not at top-level
2811 * (i.e. not a top-level function definiton or expression), then our
2812 * enclosing function, if any, must be heavyweight.
2814 if (!topLevel
&& lambda
== 0 && funAtom
)
2815 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2821 * ECMA ed. 3 standard: function expression, possibly anonymous.
2824 } else if (!funAtom
) {
2826 * If this anonymous function definition is *not* embedded within a
2827 * larger expression, we treat it as an expression statement, not as
2828 * a function declaration -- and not as a syntax error (as ECMA-262
2829 * Edition 3 would have it). Backward compatibility must trump all,
2830 * unless JSOPTION_ANONFUNFIX is set.
2832 result
= NewParseNode(PN_UNARY
, tc
);
2835 result
->pn_type
= TOK_SEMI
;
2836 result
->pn_pos
= pn
->pn_pos
;
2837 result
->pn_kid
= pn
;
2839 } else if (!topLevel
) {
2841 * ECMA ed. 3 extension: a function expression statement not at the
2842 * top level, e.g., in a compound statement such as the "then" part
2843 * of an "if" statement, binds a closure only if control reaches that
2851 funbox
->kids
= funtc
.functionList
;
2853 pn
->pn_funbox
= funbox
;
2856 pn
->pn_body
->append(body
);
2857 pn
->pn_body
->pn_pos
= body
->pn_pos
;
2862 pn
->pn_blockid
= tc
->blockid();
2864 if (!LeaveFunction(pn
, &funtc
, tc
, funAtom
, lambda
))
2870 static JSParseNode
*
2871 FunctionStmt(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2873 return FunctionDef(cx
, ts
, tc
, 0);
2876 static JSParseNode
*
2877 FunctionExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2879 return FunctionDef(cx
, ts
, tc
, JSFUN_LAMBDA
);
2883 * Parse the statements in a block, creating a TOK_LC node that lists the
2884 * statements' trees. If called from block-parsing code, the caller must
2885 * match { before and } after.
2887 static JSParseNode
*
2888 Statements(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2890 JSParseNode
*pn
, *pn2
, *saveBlock
;
2893 JS_CHECK_RECURSION(cx
, return NULL
);
2895 pn
= NewParseNode(PN_LIST
, tc
);
2898 pn
->pn_type
= TOK_LC
;
2900 pn
->pn_blockid
= tc
->blockid();
2901 saveBlock
= tc
->blockNode
;
2905 ts
->flags
|= TSF_OPERAND
;
2906 tt
= js_PeekToken(cx
, ts
);
2907 ts
->flags
&= ~TSF_OPERAND
;
2908 if (tt
<= TOK_EOF
|| tt
== TOK_RC
) {
2909 if (tt
== TOK_ERROR
) {
2910 if (ts
->flags
& TSF_EOF
)
2911 ts
->flags
|= TSF_UNEXPECTED_EOF
;
2916 pn2
= Statement(cx
, ts
, tc
);
2918 if (ts
->flags
& TSF_EOF
)
2919 ts
->flags
|= TSF_UNEXPECTED_EOF
;
2923 if (pn2
->pn_type
== TOK_FUNCTION
) {
2925 * PNX_FUNCDEFS notifies the emitter that the block contains top-
2926 * level function definitions that should be processed before the
2929 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
2930 * is relevant only for function definitions not at top-level,
2931 * which we call function statements.
2933 if (tc
->atTopLevel())
2934 pn
->pn_xflags
|= PNX_FUNCDEFS
;
2936 tc
->flags
|= TCF_HAS_FUNCTION_STMT
;
2942 * Handle the case where there was a let declaration under this block. If
2943 * it replaced tc->blockNode with a new block node then we must refresh pn
2944 * and then restore tc->blockNode.
2946 if (tc
->blockNode
!= pn
)
2948 tc
->blockNode
= saveBlock
;
2950 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2954 static JSParseNode
*
2955 Condition(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2959 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
2960 pn
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
2963 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
2965 /* Check for (a = b) and warn about possible (a == b) mistype. */
2966 if (pn
->pn_type
== TOK_ASSIGN
&&
2967 pn
->pn_op
== JSOP_NOP
&&
2969 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
2970 JSREPORT_WARNING
| JSREPORT_STRICT
,
2971 JSMSG_EQUAL_AS_ASSIGN
,
2979 MatchLabel(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
)
2984 tt
= js_PeekTokenSameLine(cx
, ts
);
2985 if (tt
== TOK_ERROR
)
2987 if (tt
== TOK_NAME
) {
2988 (void) js_GetToken(cx
, ts
);
2989 label
= CURRENT_TOKEN(ts
).t_atom
;
2993 pn
->pn_atom
= label
;
2998 BindLet(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3002 JSAtomListElement
*ale
;
3006 * Top-level 'let' is the same as 'var' currently -- this may change in a
3007 * successor standard to ES3.1 that specifies 'let'.
3009 JS_ASSERT(!tc
->atTopLevel());
3012 blockObj
= tc
->blockChain
;
3013 ale
= tc
->decls
.lookup(atom
);
3014 if (ale
&& ALE_DEFN(ale
)->pn_blockid
== tc
->blockid()) {
3015 const char *name
= js_AtomToPrintableString(cx
, atom
);
3017 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3018 JSREPORT_ERROR
, JSMSG_REDECLARED_VAR
,
3019 (ale
&& ALE_DEFN(ale
)->isConst())
3027 n
= OBJ_BLOCK_COUNT(cx
, blockObj
);
3028 if (n
== JS_BIT(16)) {
3029 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3030 JSREPORT_ERROR
, data
->let
.overflow
);
3035 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3036 * This is balanced by PopStatement, defined immediately below.
3038 if (!Define(pn
, atom
, tc
, true))
3042 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3043 * upvar cookie whose skip tells the current static level. The emitter will
3044 * adjust the node's slot based on its stack depth model -- and, for global
3045 * and eval code, JSCompiler::compileScript will adjust the slot again to
3046 * include script->nfixed.
3048 pn
->pn_op
= JSOP_GETLOCAL
;
3049 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, n
);
3050 pn
->pn_dflags
|= PND_LET
| PND_BOUND
;
3053 * Use JSPROP_ENUMERATE to aid the disassembler. Define the let binding's
3054 * property before storing pn in a reserved slot, since block_reserveSlots
3055 * depends on OBJ_SCOPE(blockObj)->entryCount.
3057 if (!js_DefineNativeProperty(cx
, blockObj
, ATOM_TO_JSID(atom
), JSVAL_VOID
,
3062 SPROP_HAS_SHORTID
, (int16
) n
, NULL
)) {
3067 * Store pn temporarily in what would be reserved slots in a cloned block
3068 * object (once the prototype's final population is known, after all 'let'
3069 * bindings for this block have been parsed). We will free these reserved
3070 * slots in jsemit.cpp:EmitEnterBlock.
3072 uintN slot
= JSSLOT_FREE(&js_BlockClass
) + n
;
3073 if (slot
>= STOBJ_NSLOTS(blockObj
) &&
3074 !js_GrowSlots(cx
, blockObj
, slot
+ 1)) {
3077 OBJ_SCOPE(blockObj
)->freeslot
= slot
+ 1;
3078 STOBJ_SET_SLOT(blockObj
, slot
, PRIVATE_TO_JSVAL(pn
));
3083 PopStatement(JSTreeContext
*tc
)
3085 JSStmtInfo
*stmt
= tc
->topStmt
;
3087 if (stmt
->flags
& SIF_SCOPE
) {
3088 JSObject
*obj
= stmt
->blockObj
;
3089 JSScope
*scope
= OBJ_SCOPE(obj
);
3090 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj
));
3092 for (JSScopeProperty
*sprop
= scope
->lastProp
; sprop
; sprop
= sprop
->parent
) {
3093 JSAtom
*atom
= JSID_TO_ATOM(sprop
->id
);
3095 /* Beware the empty destructuring dummy. */
3096 if (atom
== tc
->compiler
->context
->runtime
->atomState
.emptyAtom
)
3098 tc
->decls
.remove(tc
->compiler
, atom
);
3102 * The block scope will not be modified again. It may be shared. Clear
3103 * scope->object to make scope->owned() false.
3105 scope
->object
= NULL
;
3107 js_PopStatement(tc
);
3111 OuterLet(JSTreeContext
*tc
, JSStmtInfo
*stmt
, JSAtom
*atom
)
3113 while (stmt
->downScope
) {
3114 stmt
= js_LexicalLookup(tc
, atom
, NULL
, stmt
->downScope
);
3117 if (stmt
->type
== STMT_BLOCK
)
3124 BindVarOrConst(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
3126 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
3127 JSParseNode
*pn
= data
->pn
;
3129 if (stmt
&& stmt
->type
== STMT_WITH
) {
3130 pn
->pn_op
= JSOP_NAME
;
3131 data
->fresh
= false;
3135 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
3139 JSDefinition
*dn
= ale
? ALE_DEFN(ale
) : NULL
;
3140 JSDefinition::Kind dn_kind
= dn
? dn
->kind() : JSDefinition::VAR
;
3143 if (dn_kind
== JSDefinition::ARG
) {
3144 name
= js_AtomToPrintableString(cx
, atom
);
3148 if (op
== JSOP_DEFCONST
) {
3149 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3150 JSREPORT_ERROR
, JSMSG_REDECLARED_PARAM
,
3154 if (!js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3155 JSREPORT_WARNING
| JSREPORT_STRICT
,
3156 JSMSG_VAR_HIDES_ARG
, name
)) {
3160 bool error
= (op
== JSOP_DEFCONST
||
3161 dn_kind
== JSDefinition::CONST
||
3162 (dn_kind
== JSDefinition::LET
&&
3163 (stmt
->type
!= STMT_CATCH
|| OuterLet(tc
, stmt
, atom
))));
3165 if (JS_HAS_STRICT_OPTION(cx
)
3166 ? op
!= JSOP_DEFVAR
|| dn_kind
!= JSDefinition::VAR
3168 name
= js_AtomToPrintableString(cx
, atom
);
3170 !js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3172 ? JSREPORT_WARNING
| JSREPORT_STRICT
3174 JSMSG_REDECLARED_VAR
,
3175 JSDefinition::kindString(dn_kind
),
3184 if (!Define(pn
, atom
, tc
))
3188 * A var declaration never recreates an existing binding, it restates
3189 * it and possibly reinitializes its value. Beware that if pn becomes a
3190 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3191 * const (typically a const would ;-), then pn must be rewritten into a
3192 * TOK_ASSIGN node. See Variables, further below.
3194 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3195 * There the x definition is hoisted but the x = 2 assignment mutates
3196 * the block-local binding of x.
3198 JSDefinition
*dn
= ALE_DEFN(ale
);
3200 data
->fresh
= false;
3203 /* Make pnu be a fresh name node that uses dn. */
3204 JSParseNode
*pnu
= pn
;
3207 pnu
= NewNameNode(cx
, TS(tc
->compiler
), atom
, tc
);
3212 LinkUseToDef(pnu
, dn
, tc
);
3213 pnu
->pn_op
= JSOP_NAME
;
3216 while (dn
->kind() == JSDefinition::LET
) {
3218 ale
= ALE_NEXT(ale
);
3219 } while (ale
&& ALE_ATOM(ale
) != atom
);
3226 JS_ASSERT_IF(data
->op
== JSOP_DEFCONST
,
3227 dn
->kind() == JSDefinition::CONST
);
3232 * A var or const that is shadowed by one or more let bindings of the
3233 * same name, but that has not been declared until this point, must be
3234 * hoisted above the let bindings.
3239 ale
= tc
->lexdeps
.rawLookup(atom
, hep
);
3242 tc
->lexdeps
.rawRemove(tc
->compiler
, ale
, hep
);
3244 JSParseNode
*pn2
= NewNameNode(cx
, TS(tc
->compiler
), atom
, tc
);
3248 /* The token stream may be past the location for pn. */
3249 pn2
->pn_type
= TOK_NAME
;
3250 pn2
->pn_pos
= pn
->pn_pos
;
3253 pn
->pn_op
= JSOP_NAME
;
3256 ale
= tc
->decls
.add(tc
->compiler
, atom
, JSAtomList::HOIST
);
3259 ALE_SET_DEFN(ale
, pn
);
3261 pn
->pn_dflags
&= ~PND_PLACEHOLDER
;
3264 if (data
->op
== JSOP_DEFCONST
)
3265 pn
->pn_dflags
|= PND_CONST
;
3267 if (!(tc
->flags
& TCF_IN_FUNCTION
)) {
3269 * If we are generating global or eval-called-from-global code, bind a
3270 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3271 * up global variable access by memoizing name-to-slot mappings in the
3272 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3273 * can't be done due to a pre-existing property of the same name as the
3274 * var or const but incompatible attributes/getter/setter/etc, these
3275 * ops devolve to JSOP_NAME, etc.
3277 * For now, don't try to lookup eval frame variables at compile time.
3278 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3279 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3281 pn
->pn_op
= JSOP_NAME
;
3282 if ((tc
->flags
& TCF_COMPILING
) && !tc
->compiler
->callerFrame
) {
3283 JSCodeGenerator
*cg
= (JSCodeGenerator
*) tc
;
3285 /* Index atom so we can map fast global number to name. */
3286 ale
= cg
->atomList
.add(tc
->compiler
, atom
);
3290 /* Defend against cg->ngvars 16-bit overflow. */
3291 uintN slot
= ALE_INDEX(ale
);
3292 if ((slot
+ 1) >> 16)
3295 if ((uint16
)(slot
+ 1) > cg
->ngvars
)
3296 cg
->ngvars
= (uint16
)(slot
+ 1);
3298 pn
->pn_op
= JSOP_GETGVAR
;
3299 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, slot
);
3300 pn
->pn_dflags
|= PND_BOUND
| PND_GVAR
;
3305 if (atom
== cx
->runtime
->atomState
.argumentsAtom
) {
3306 pn
->pn_op
= JSOP_ARGUMENTS
;
3307 pn
->pn_dflags
|= PND_BOUND
;
3311 JSLocalKind localKind
= js_LookupLocal(cx
, tc
->fun
, atom
, NULL
);
3312 if (localKind
== JSLOCAL_NONE
) {
3314 * Property not found in current variable scope: we have not seen this
3315 * variable before. Define a new local variable by adding a property to
3316 * the function's scope and allocating one slot in the function's vars
3317 * frame. Any locals declared in a with statement body are handled at
3318 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3319 * and heavyweight-function-local vars.
3321 localKind
= (data
->op
== JSOP_DEFCONST
) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
3323 uintN index
= tc
->fun
->u
.i
.nvars
;
3324 if (!BindLocalVariable(cx
, tc
->fun
, atom
, localKind
))
3326 pn
->pn_op
= JSOP_GETLOCAL
;
3327 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(tc
->staticLevel
, index
);
3328 pn
->pn_dflags
|= PND_BOUND
;
3332 if (localKind
== JSLOCAL_ARG
) {
3333 /* We checked errors and strict warnings earlier -- see above. */
3334 JS_ASSERT(ale
&& ALE_DEFN(ale
)->kind() == JSDefinition::ARG
);
3336 /* Not an argument, must be a redeclared local var. */
3337 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
3339 pn
->pn_op
= JSOP_NAME
;
3344 MakeSetCall(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN msg
)
3348 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
3349 JS_ASSERT(pn
->pn_op
== JSOP_CALL
|| pn
->pn_op
== JSOP_EVAL
|| pn
->pn_op
== JSOP_APPLY
);
3351 if (pn2
->pn_type
== TOK_FUNCTION
&& (pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
)) {
3352 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
, msg
);
3355 pn
->pn_op
= JSOP_SETCALL
;
3360 NoteLValue(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN dflag
= PND_ASSIGNED
)
3363 JSDefinition
*dn
= pn
->pn_lexdef
;
3366 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3367 * occur as direct kids of the same block with no forward refs to x.
3369 if (!(dn
->pn_dflags
& (PND_INITIALIZED
| PND_CONST
| PND_PLACEHOLDER
)) &&
3370 dn
->isBlockChild() &&
3371 pn
->isBlockChild() &&
3372 dn
->pn_blockid
== pn
->pn_blockid
&&
3373 dn
->pn_pos
.end
<= pn
->pn_pos
.begin
&&
3374 dn
->dn_uses
== pn
) {
3375 dflag
= PND_INITIALIZED
;
3378 dn
->pn_dflags
|= dflag
;
3380 if (dn
->frameLevel() != tc
->staticLevel
) {
3382 * The above condition takes advantage of the all-ones nature of
3383 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3384 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3386 JS_ASSERT_IF(dn
->pn_cookie
!= FREE_UPVAR_COOKIE
,
3387 dn
->frameLevel() < tc
->staticLevel
);
3388 tc
->flags
|= TCF_FUN_SETS_OUTER_NAME
;
3392 pn
->pn_dflags
|= dflag
;
3394 if (pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
3395 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3398 #if JS_HAS_DESTRUCTURING
3401 BindDestructuringVar(JSContext
*cx
, BindData
*data
, JSParseNode
*pn
,
3407 * Destructuring is a form of assignment, so just as for an initialized
3408 * simple variable, we must check for assignment to 'arguments' and flag
3409 * the enclosing function (if any) as heavyweight.
3411 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
3413 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
3414 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3417 if (!data
->binder(cx
, data
, atom
, tc
))
3421 * Select the appropriate name-setting opcode, respecting eager selection
3422 * done by the data->binder function.
3424 if (pn
->pn_dflags
& PND_BOUND
) {
3425 pn
->pn_op
= (pn
->pn_op
== JSOP_ARGUMENTS
)
3427 : (pn
->pn_dflags
& PND_GVAR
)
3431 pn
->pn_op
= (data
->op
== JSOP_DEFCONST
)
3436 if (data
->op
== JSOP_DEFCONST
)
3437 pn
->pn_dflags
|= PND_CONST
;
3439 NoteLValue(cx
, pn
, tc
, PND_INITIALIZED
);
3444 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3445 * LHS expression except a destructuring initialiser, and R is on the stack.
3446 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3447 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3448 * then push its property name QN. At this point the stack looks like
3450 * [... R, R[P], QB, QN]
3452 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3453 * its operands with left-hand side above right-hand side:
3455 * [rval, lval, xval]
3457 * and pops all three values, setting lval[xval] = rval. But we cannot select
3458 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3459 * which can be optimized further. So we select JSOP_SETNAME.
3462 BindDestructuringLHS(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
3464 switch (pn
->pn_type
) {
3466 NoteLValue(cx
, pn
, tc
);
3471 pn
->pn_op
= JSOP_SETNAME
;
3474 #if JS_HAS_LVALUE_RETURN
3476 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
3481 #if JS_HAS_XML_SUPPORT
3483 if (pn
->pn_op
== JSOP_XMLNAME
) {
3484 pn
->pn_op
= JSOP_BINDXMLNAME
;
3491 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
,
3492 JSREPORT_ERROR
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
3499 typedef struct FindPropValData
{
3500 uint32 numvars
; /* # of destructuring vars in left side */
3501 uint32 maxstep
; /* max # of steps searching right side */
3502 JSDHashTable table
; /* hash table for O(1) right side search */
3505 typedef struct FindPropValEntry
{
3506 JSDHashEntryHdr hdr
;
3511 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3512 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3513 ((pnkey)->pn_type == TOK_NUMBER || \
3514 (pnkey)->pn_type == TOK_STRING || \
3515 (pnkey)->pn_type == TOK_NAME)) || \
3516 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3518 static JSDHashNumber
3519 HashFindPropValKey(JSDHashTable
*table
, const void *key
)
3521 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3523 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3524 return (pnkey
->pn_type
== TOK_NUMBER
)
3525 ? (JSDHashNumber
) (JSDOUBLE_HI32(pnkey
->pn_dval
) ^
3526 JSDOUBLE_LO32(pnkey
->pn_dval
))
3527 : ATOM_HASH(pnkey
->pn_atom
);
3531 MatchFindPropValEntry(JSDHashTable
*table
,
3532 const JSDHashEntryHdr
*entry
,
3535 const FindPropValEntry
*fpve
= (const FindPropValEntry
*)entry
;
3536 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
3538 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3539 return pnkey
->pn_type
== fpve
->pnkey
->pn_type
&&
3540 ((pnkey
->pn_type
== TOK_NUMBER
)
3541 ? pnkey
->pn_dval
== fpve
->pnkey
->pn_dval
3542 : pnkey
->pn_atom
== fpve
->pnkey
->pn_atom
);
3545 static const JSDHashTableOps FindPropValOps
= {
3549 MatchFindPropValEntry
,
3550 JS_DHashMoveEntryStub
,
3551 JS_DHashClearEntryStub
,
3552 JS_DHashFinalizeStub
,
3556 #define STEP_HASH_THRESHOLD 10
3557 #define BIG_DESTRUCTURING 5
3558 #define BIG_OBJECT_INIT 20
3560 static JSParseNode
*
3561 FindPropertyValue(JSParseNode
*pn
, JSParseNode
*pnid
, FindPropValData
*data
)
3563 FindPropValEntry
*entry
;
3564 JSParseNode
*pnhit
, *pnhead
, *pnprop
, *pnkey
;
3567 /* If we have a hash table, use it as the sole source of truth. */
3568 if (data
->table
.ops
) {
3569 entry
= (FindPropValEntry
*)
3570 JS_DHashTableOperate(&data
->table
, pnid
, JS_DHASH_LOOKUP
);
3571 return JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
) ? entry
->pnval
: NULL
;
3574 /* If pn is not an object initialiser node, we can't do anything here. */
3575 if (pn
->pn_type
!= TOK_RC
)
3579 * We must search all the way through pn's list, to handle the case of an
3580 * id duplicated for two or more property initialisers.
3584 ASSERT_VALID_PROPERTY_KEY(pnid
);
3585 pnhead
= pn
->pn_head
;
3586 if (pnid
->pn_type
== TOK_NUMBER
) {
3587 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3588 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3589 if (pnprop
->pn_op
== JSOP_NOP
) {
3590 pnkey
= pnprop
->pn_left
;
3591 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3592 if (pnkey
->pn_type
== TOK_NUMBER
&&
3593 pnkey
->pn_dval
== pnid
->pn_dval
) {
3600 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
3601 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3602 if (pnprop
->pn_op
== JSOP_NOP
) {
3603 pnkey
= pnprop
->pn_left
;
3604 ASSERT_VALID_PROPERTY_KEY(pnkey
);
3605 if (pnkey
->pn_type
== pnid
->pn_type
&&
3606 pnkey
->pn_atom
== pnid
->pn_atom
) {
3616 /* Hit via full search -- see whether it's time to create the hash table. */
3617 JS_ASSERT(!data
->table
.ops
);
3618 if (step
> data
->maxstep
) {
3619 data
->maxstep
= step
;
3620 if (step
>= STEP_HASH_THRESHOLD
&&
3621 data
->numvars
>= BIG_DESTRUCTURING
&&
3622 pn
->pn_count
>= BIG_OBJECT_INIT
&&
3623 JS_DHashTableInit(&data
->table
, &FindPropValOps
, pn
,
3624 sizeof(FindPropValEntry
),
3625 JS_DHASH_DEFAULT_CAPACITY(pn
->pn_count
)))
3627 for (pn
= pnhead
; pn
; pn
= pn
->pn_next
) {
3628 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
3629 ASSERT_VALID_PROPERTY_KEY(pn
->pn_left
);
3630 entry
= (FindPropValEntry
*)
3631 JS_DHashTableOperate(&data
->table
, pn
->pn_left
,
3633 entry
->pnval
= pn
->pn_right
;
3637 return pnhit
->pn_right
;
3641 * If data is null, the caller is AssignExpr and instead of binding variables,
3642 * we specialize lvalues in the propery value positions of the left-hand side.
3643 * If right is null, just check for well-formed lvalues.
3645 * See also UndominateInitializers, immediately below. If you change either of
3646 * these functions, you might have to change the other to match.
3649 CheckDestructuring(JSContext
*cx
, BindData
*data
,
3650 JSParseNode
*left
, JSParseNode
*right
,
3654 FindPropValData fpvd
;
3655 JSParseNode
*lhs
, *rhs
, *pn
, *pn2
;
3657 if (left
->pn_type
== TOK_ARRAYCOMP
) {
3658 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), left
,
3659 JSREPORT_ERROR
, JSMSG_ARRAY_COMP_LEFTSIDE
);
3663 #if JS_HAS_DESTRUCTURING_SHORTHAND
3664 if (right
&& right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3665 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), right
,
3666 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
3671 fpvd
.table
.ops
= NULL
;
3672 lhs
= left
->pn_head
;
3673 if (left
->pn_type
== TOK_RB
) {
3674 rhs
= (right
&& right
->pn_type
== left
->pn_type
)
3679 pn
= lhs
, pn2
= rhs
;
3681 /* Nullary comma is an elision; binary comma is an expression.*/
3682 if (pn
->pn_type
!= TOK_COMMA
|| pn
->pn_arity
!= PN_NULLARY
) {
3683 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3684 ok
= CheckDestructuring(cx
, data
, pn
, pn2
, tc
);
3687 if (pn
->pn_type
!= TOK_NAME
)
3690 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3692 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3704 JS_ASSERT(left
->pn_type
== TOK_RC
);
3705 fpvd
.numvars
= left
->pn_count
;
3710 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
3713 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3715 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
3716 ok
= CheckDestructuring(cx
, data
, pn
, rhs
, tc
);
3718 if (pn
->pn_type
!= TOK_NAME
)
3721 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
3723 ok
= BindDestructuringLHS(cx
, pn
, tc
);
3733 * The catch/finally handler implementation in the interpreter assumes
3734 * that any operation that introduces a new scope (like a "let" or "with"
3735 * block) increases the stack depth. This way, it is possible to restore
3736 * the scope chain based on stack depth of the handler alone. "let" with
3737 * an empty destructuring pattern like in
3741 * would violate this assumption as the there would be no let locals to
3742 * store on the stack. To satisfy it we add an empty property to such
3743 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3744 * slots, would be always positive.
3746 * Note that we add such a property even if the block has locals due to
3747 * later let declarations in it. We optimize for code simplicity here,
3748 * not the fastest runtime performance with empty [] or {}.
3751 data
->binder
== BindLet
&&
3752 OBJ_BLOCK_COUNT(cx
, tc
->blockChain
) == 0) {
3753 ok
= !!js_DefineNativeProperty(cx
, tc
->blockChain
,
3754 ATOM_TO_JSID(cx
->runtime
->
3755 atomState
.emptyAtom
),
3756 JSVAL_VOID
, NULL
, NULL
,
3760 SPROP_HAS_SHORTID
, 0, NULL
);
3769 JS_DHashTableFinish(&fpvd
.table
);
3773 js_ReportCompileErrorNumber(cx
, TS(tc
->compiler
), pn
, JSREPORT_ERROR
,
3774 JSMSG_NO_VARIABLE_NAME
);
3780 * This is a greatly pared down version of CheckDestructuring that extends the
3781 * pn_pos.end source coordinate of each name in a destructuring binding such as
3783 * var [x, y] = [function () y, 42];
3785 * to cover its corresponding initializer, so that the initialized binding does
3786 * not appear to dominate any closures in its initializer. See bug 496134.
3788 * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
3789 * not very precise. With one-pass SSA construction from structured source code
3790 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3791 * Languages", Brandis and Mössenböck), we could do much better.
3793 * See CheckDestructuring, immediately above. If you change either of these
3794 * functions, you might have to change the other to match.
3797 UndominateInitializers(JSParseNode
*left
, JSParseNode
*right
, JSTreeContext
*tc
)
3799 FindPropValData fpvd
;
3800 JSParseNode
*lhs
, *rhs
;
3802 JS_ASSERT(left
->pn_type
!= TOK_ARRAYCOMP
);
3805 #if JS_HAS_DESTRUCTURING_SHORTHAND
3806 if (right
->pn_arity
== PN_LIST
&& (right
->pn_xflags
& PNX_DESTRUCT
)) {
3807 js_ReportCompileErrorNumber(tc
->compiler
->context
, TS(tc
->compiler
), right
,
3808 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
3813 if (right
->pn_type
!= left
->pn_type
)
3816 fpvd
.table
.ops
= NULL
;
3817 lhs
= left
->pn_head
;
3818 if (left
->pn_type
== TOK_RB
) {
3819 rhs
= right
->pn_head
;
3821 while (lhs
&& rhs
) {
3822 /* Nullary comma is an elision; binary comma is an expression.*/
3823 if (lhs
->pn_type
!= TOK_COMMA
|| lhs
->pn_arity
!= PN_NULLARY
) {
3824 if (lhs
->pn_type
== TOK_RB
|| lhs
->pn_type
== TOK_RC
) {
3825 if (!UndominateInitializers(lhs
, rhs
, tc
))
3828 lhs
->pn_pos
.end
= rhs
->pn_pos
.end
;
3836 JS_ASSERT(left
->pn_type
== TOK_RC
);
3837 fpvd
.numvars
= left
->pn_count
;
3841 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
3842 JSParseNode
*pn
= lhs
->pn_right
;
3844 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
3845 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
3846 if (rhs
&& !UndominateInitializers(pn
, rhs
, tc
))
3850 pn
->pn_pos
.end
= rhs
->pn_pos
.end
;
3859 static JSParseNode
*
3860 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
3866 ts
= TS(tc
->compiler
);
3867 ts
->flags
|= TSF_DESTRUCTURING
;
3868 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
3869 ts
->flags
&= ~TSF_DESTRUCTURING
;
3872 if (!CheckDestructuring(cx
, data
, pn
, NULL
, tc
))
3878 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
3879 * This function assumes the cloned tree is for use in the same statement and
3880 * binding context as the original tree.
3882 static JSParseNode
*
3883 CloneParseTree(JSParseNode
*opn
, JSTreeContext
*tc
)
3885 JSParseNode
*pn
, *pn2
, *opn2
;
3887 pn
= NewOrRecycledNode(tc
);
3890 pn
->pn_type
= opn
->pn_type
;
3891 pn
->pn_pos
= opn
->pn_pos
;
3892 pn
->pn_op
= opn
->pn_op
;
3893 pn
->pn_used
= opn
->pn_used
;
3894 pn
->pn_defn
= opn
->pn_defn
;
3895 pn
->pn_arity
= opn
->pn_arity
;
3896 pn
->pn_parens
= opn
->pn_parens
;
3898 switch (pn
->pn_arity
) {
3899 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
3902 NULLCHECK(pn
->pn_funbox
=
3903 tc
->compiler
->newFunctionBox(opn
->pn_funbox
->object
, pn
, tc
));
3904 NULLCHECK(pn
->pn_body
= CloneParseTree(opn
->pn_body
, tc
));
3905 pn
->pn_cookie
= opn
->pn_cookie
;
3906 pn
->pn_dflags
= opn
->pn_dflags
;
3907 pn
->pn_blockid
= opn
->pn_blockid
;
3912 for (opn2
= opn
->pn_head
; opn2
; opn2
= opn2
->pn_next
) {
3913 NULLCHECK(pn2
= CloneParseTree(opn2
, tc
));
3916 pn
->pn_xflags
= opn
->pn_xflags
;
3920 NULLCHECK(pn
->pn_kid1
= CloneParseTree(opn
->pn_kid1
, tc
));
3921 NULLCHECK(pn
->pn_kid2
= CloneParseTree(opn
->pn_kid2
, tc
));
3922 NULLCHECK(pn
->pn_kid3
= CloneParseTree(opn
->pn_kid3
, tc
));
3926 NULLCHECK(pn
->pn_left
= CloneParseTree(opn
->pn_left
, tc
));
3927 if (opn
->pn_right
!= opn
->pn_left
)
3928 NULLCHECK(pn
->pn_right
= CloneParseTree(opn
->pn_right
, tc
));
3930 pn
->pn_right
= pn
->pn_left
;
3931 pn
->pn_val
= opn
->pn_val
;
3932 pn
->pn_iflags
= opn
->pn_iflags
;
3936 NULLCHECK(pn
->pn_kid
= CloneParseTree(opn
->pn_kid
, tc
));
3937 pn
->pn_num
= opn
->pn_num
;
3938 pn
->pn_hidden
= opn
->pn_hidden
;
3942 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
3943 pn
->pn_u
= opn
->pn_u
;
3946 * The old name is a use of its pn_lexdef. Make the clone also be a
3947 * use of that definition.
3949 JSDefinition
*dn
= pn
->pn_lexdef
;
3951 pn
->pn_link
= dn
->dn_uses
;
3953 } else if (opn
->pn_expr
) {
3954 NULLCHECK(pn
->pn_expr
= CloneParseTree(opn
->pn_expr
, tc
));
3957 * If the old name is a definition, the new one has pn_defn set.
3958 * Make the old name a use of the new node.
3961 opn
->pn_defn
= false;
3962 LinkUseToDef(opn
, (JSDefinition
*) pn
, tc
);
3968 pn
->pn_names
= opn
->pn_names
;
3969 NULLCHECK(pn
->pn_tree
= CloneParseTree(opn
->pn_tree
, tc
));
3973 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
3974 pn
->pn_u
= opn
->pn_u
;
3982 #endif /* JS_HAS_DESTRUCTURING */
3984 extern const char js_with_statement_str
[];
3986 static JSParseNode
*
3987 ContainsStmt(JSParseNode
*pn
, JSTokenType tt
)
3989 JSParseNode
*pn2
, *pnt
;
3993 if (PN_TYPE(pn
) == tt
)
3995 switch (pn
->pn_arity
) {
3997 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
3998 pnt
= ContainsStmt(pn2
, tt
);
4004 pnt
= ContainsStmt(pn
->pn_kid1
, tt
);
4007 pnt
= ContainsStmt(pn
->pn_kid2
, tt
);
4010 return ContainsStmt(pn
->pn_kid3
, tt
);
4013 * Limit recursion if pn is a binary expression, which can't contain a
4016 if (pn
->pn_op
!= JSOP_NOP
)
4018 pnt
= ContainsStmt(pn
->pn_left
, tt
);
4021 return ContainsStmt(pn
->pn_right
, tt
);
4023 if (pn
->pn_op
!= JSOP_NOP
)
4025 return ContainsStmt(pn
->pn_kid
, tt
);
4027 return ContainsStmt(pn
->maybeExpr(), tt
);
4029 return ContainsStmt(pn
->pn_tree
, tt
);
4035 static JSParseNode
*
4036 ReturnOrYield(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4037 JSParser operandParser
)
4039 JSTokenType tt
, tt2
;
4040 JSParseNode
*pn
, *pn2
;
4042 tt
= CURRENT_TOKEN(ts
).type
;
4043 if (tt
== TOK_RETURN
&& !(tc
->flags
& TCF_IN_FUNCTION
)) {
4044 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4045 JSMSG_BAD_RETURN_OR_YIELD
, js_return_str
);
4049 pn
= NewParseNode(PN_UNARY
, tc
);
4053 #if JS_HAS_GENERATORS
4054 if (tt
== TOK_YIELD
)
4055 tc
->flags
|= TCF_FUN_IS_GENERATOR
;
4058 /* This is ugly, but we don't want to require a semicolon. */
4059 ts
->flags
|= TSF_OPERAND
;
4060 tt2
= js_PeekTokenSameLine(cx
, ts
);
4061 ts
->flags
&= ~TSF_OPERAND
;
4062 if (tt2
== TOK_ERROR
)
4065 if (tt2
!= TOK_EOF
&& tt2
!= TOK_EOL
&& tt2
!= TOK_SEMI
&& tt2
!= TOK_RC
4066 #if JS_HAS_GENERATORS
4067 && (tt
!= TOK_YIELD
||
4068 (tt2
!= tt
&& tt2
!= TOK_RB
&& tt2
!= TOK_RP
&&
4069 tt2
!= TOK_COLON
&& tt2
!= TOK_COMMA
))
4072 pn2
= operandParser(cx
, ts
, tc
);
4075 #if JS_HAS_GENERATORS
4076 if (tt
== TOK_RETURN
)
4078 tc
->flags
|= TCF_RETURN_EXPR
;
4079 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4082 #if JS_HAS_GENERATORS
4083 if (tt
== TOK_RETURN
)
4085 tc
->flags
|= TCF_RETURN_VOID
;
4088 if ((~tc
->flags
& (TCF_RETURN_EXPR
| TCF_FUN_IS_GENERATOR
)) == 0) {
4089 /* As in Python (see PEP-255), disallow return v; in generators. */
4090 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
4091 JSMSG_BAD_GENERATOR_RETURN
,
4092 JSMSG_BAD_ANON_GENERATOR_RETURN
);
4096 if (JS_HAS_STRICT_OPTION(cx
) &&
4097 (~tc
->flags
& (TCF_RETURN_EXPR
| TCF_RETURN_VOID
)) == 0 &&
4098 !ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
4099 JSMSG_NO_RETURN_VALUE
,
4100 JSMSG_ANON_NO_RETURN_VALUE
)) {
4107 static JSParseNode
*
4108 PushLexicalScope(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4113 JSObjectBox
*blockbox
;
4115 pn
= NewParseNode(PN_NAME
, tc
);
4119 obj
= js_NewBlockObject(cx
);
4123 blockbox
= tc
->compiler
->newObjectBox(obj
);
4127 js_PushBlockScope(tc
, stmt
, obj
, -1);
4128 pn
->pn_type
= TOK_LEXICALSCOPE
;
4129 pn
->pn_op
= JSOP_LEAVEBLOCK
;
4130 pn
->pn_objbox
= blockbox
;
4131 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
4133 if (!GenerateBlockId(tc
, stmt
->blockid
))
4135 pn
->pn_blockid
= stmt
->blockid
;
4139 #if JS_HAS_BLOCK_SCOPE
4141 static JSParseNode
*
4142 LetBlock(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, JSBool statement
)
4144 JSParseNode
*pn
, *pnblock
, *pnlet
;
4145 JSStmtInfo stmtInfo
;
4147 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LET
);
4149 /* Create the let binary node. */
4150 pnlet
= NewParseNode(PN_BINARY
, tc
);
4154 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_LET
);
4156 /* This is a let block or expression of the form: let (a, b, c) .... */
4157 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
4161 pn
->pn_expr
= pnlet
;
4163 pnlet
->pn_left
= Variables(cx
, ts
, tc
, true);
4164 if (!pnlet
->pn_left
)
4166 pnlet
->pn_left
->pn_xflags
= PNX_POPVAR
;
4168 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_LET
);
4170 ts
->flags
|= TSF_OPERAND
;
4171 if (statement
&& !js_MatchToken(cx
, ts
, TOK_LC
)) {
4173 * If this is really an expression in let statement guise, then we
4174 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4175 * the return value of the expression.
4177 pn
= NewParseNode(PN_UNARY
, tc
);
4180 pn
->pn_type
= TOK_SEMI
;
4182 pn
->pn_kid
= pnblock
;
4184 statement
= JS_FALSE
;
4186 ts
->flags
&= ~TSF_OPERAND
;
4189 pnlet
->pn_right
= Statements(cx
, ts
, tc
);
4190 if (!pnlet
->pn_right
)
4192 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LET
);
4195 * Change pnblock's opcode to the variant that propagates the last
4196 * result down after popping the block, and clear statement.
4198 pnblock
->pn_op
= JSOP_LEAVEBLOCKEXPR
;
4199 pnlet
->pn_right
= AssignExpr(cx
, ts
, tc
);
4200 if (!pnlet
->pn_right
)
4208 #endif /* JS_HAS_BLOCK_SCOPE */
4211 PushBlocklikeStatement(JSStmtInfo
*stmt
, JSStmtType type
, JSTreeContext
*tc
)
4213 js_PushStatement(tc
, stmt
, type
, -1);
4214 return GenerateBlockId(tc
, stmt
->blockid
);
4217 static JSParseNode
*
4218 NewBindingNode(JSTokenStream
*ts
, JSAtom
*atom
, JSTreeContext
*tc
, bool let
= false)
4220 JSParseNode
*pn
= NULL
;
4222 JSAtomListElement
*ale
= tc
->decls
.lookup(atom
);
4225 JS_ASSERT(!pn
->isPlaceholder());
4227 ale
= tc
->lexdeps
.lookup(atom
);
4230 JS_ASSERT(pn
->isPlaceholder());
4235 JS_ASSERT(pn
->pn_defn
);
4238 * A let binding at top level becomes a var before we get here, so if
4239 * pn and tc have the same blockid then that id must not be the bodyid.
4240 * If pn is a forward placeholder definition from the same or a higher
4241 * block then we claim it.
4243 JS_ASSERT_IF(let
&& pn
->pn_blockid
== tc
->blockid(),
4244 pn
->pn_blockid
!= tc
->bodyid
);
4246 if (pn
->isPlaceholder() && pn
->pn_blockid
>= (let
? tc
->blockid() : tc
->bodyid
)) {
4248 pn
->pn_blockid
= tc
->blockid();
4250 tc
->lexdeps
.remove(tc
->compiler
, atom
);
4255 /* Make a new node for this declarator name (or destructuring pattern). */
4256 pn
= NewNameNode(tc
->compiler
->context
, ts
, atom
, tc
);
4262 #if JS_HAS_BLOCK_SCOPE
4264 RebindLets(JSParseNode
*pn
, JSTreeContext
*tc
)
4269 switch (pn
->pn_arity
) {
4271 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
4272 RebindLets(pn2
, tc
);
4276 RebindLets(pn
->pn_kid1
, tc
);
4277 RebindLets(pn
->pn_kid2
, tc
);
4278 RebindLets(pn
->pn_kid3
, tc
);
4282 RebindLets(pn
->pn_left
, tc
);
4283 RebindLets(pn
->pn_right
, tc
);
4287 RebindLets(pn
->pn_kid
, tc
);
4291 RebindLets(pn
->pn_body
, tc
);
4295 RebindLets(pn
->maybeExpr(), tc
);
4298 JS_ASSERT(pn
->pn_blockid
> tc
->topStmt
->blockid
);
4299 } else if (pn
->pn_used
) {
4300 if (pn
->pn_lexdef
->pn_blockid
== tc
->topStmt
->blockid
) {
4303 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
4305 while ((ale
= ALE_NEXT(ale
)) != NULL
) {
4306 if (ALE_ATOM(ale
) == pn
->pn_atom
) {
4307 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4313 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
4315 ale
= MakePlaceholder(pn
, tc
);
4319 JSDefinition
*dn
= ALE_DEFN(ale
);
4320 dn
->pn_type
= TOK_NAME
;
4321 dn
->pn_op
= JSOP_NOP
;
4322 dn
->pn_dflags
|= pn
->pn_dflags
& PND_USE2DEF_FLAGS
;
4324 LinkUseToDef(pn
, ALE_DEFN(ale
), tc
);
4330 RebindLets(pn
->pn_tree
, tc
);
4336 #endif /* JS_HAS_BLOCK_SCOPE */
4338 static JSParseNode
*
4339 Statement(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4342 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
, *pn4
;
4343 JSStmtInfo stmtInfo
, *stmt
, *stmt2
;
4346 JS_CHECK_RECURSION(cx
, return NULL
);
4348 ts
->flags
|= TSF_OPERAND
;
4349 tt
= js_GetToken(cx
, ts
);
4350 ts
->flags
&= ~TSF_OPERAND
;
4352 #if JS_HAS_GETTER_SETTER
4353 if (tt
== TOK_NAME
) {
4354 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
4355 if (tt
== TOK_ERROR
)
4362 #if JS_HAS_XML_SUPPORT
4363 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
4364 tt
= js_PeekToken(cx
, ts
);
4365 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
4366 if (tt
== TOK_DBLCOLON
)
4369 return FunctionStmt(cx
, ts
, tc
);
4372 /* An IF node has three kids: condition, then, and optional else. */
4373 pn
= NewParseNode(PN_TERNARY
, tc
);
4376 pn1
= Condition(cx
, ts
, tc
);
4379 js_PushStatement(tc
, &stmtInfo
, STMT_IF
, -1);
4380 pn2
= Statement(cx
, ts
, tc
);
4383 ts
->flags
|= TSF_OPERAND
;
4384 if (js_MatchToken(cx
, ts
, TOK_ELSE
)) {
4385 ts
->flags
&= ~TSF_OPERAND
;
4386 stmtInfo
.type
= STMT_ELSE
;
4387 pn3
= Statement(cx
, ts
, tc
);
4390 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
4392 ts
->flags
&= ~TSF_OPERAND
;
4394 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4404 JSParseNode
*pn5
, *saveBlock
;
4405 JSBool seenDefault
= JS_FALSE
;
4407 pn
= NewParseNode(PN_BINARY
, tc
);
4410 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
4412 /* pn1 points to the switch's discriminant. */
4413 pn1
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
4417 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
4418 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
4421 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4422 * because that function states tc->topStmt->blockid.
4424 js_PushStatement(tc
, &stmtInfo
, STMT_SWITCH
, -1);
4426 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4427 pn2
= NewParseNode(PN_LIST
, tc
);
4431 if (!GenerateBlockIdForStmtNode(pn2
, tc
))
4433 saveBlock
= tc
->blockNode
;
4434 tc
->blockNode
= pn2
;
4436 while ((tt
= js_GetToken(cx
, ts
)) != TOK_RC
) {
4440 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4441 JSMSG_TOO_MANY_DEFAULTS
);
4444 seenDefault
= JS_TRUE
;
4448 pn3
= NewParseNode(PN_BINARY
, tc
);
4451 if (tt
== TOK_CASE
) {
4452 pn3
->pn_left
= Expr(cx
, ts
, tc
);
4457 if (pn2
->pn_count
== JS_BIT(16)) {
4458 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4459 JSMSG_TOO_MANY_CASES
);
4468 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4472 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
4474 pn4
= NewParseNode(PN_LIST
, tc
);
4477 pn4
->pn_type
= TOK_LC
;
4479 ts
->flags
|= TSF_OPERAND
;
4480 while ((tt
= js_PeekToken(cx
, ts
)) != TOK_RC
&&
4481 tt
!= TOK_CASE
&& tt
!= TOK_DEFAULT
) {
4482 ts
->flags
&= ~TSF_OPERAND
;
4483 if (tt
== TOK_ERROR
)
4485 pn5
= Statement(cx
, ts
, tc
);
4488 pn4
->pn_pos
.end
= pn5
->pn_pos
.end
;
4490 ts
->flags
|= TSF_OPERAND
;
4492 ts
->flags
&= ~TSF_OPERAND
;
4494 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4496 pn4
->pn_pos
.begin
= pn4
->pn_head
->pn_pos
.begin
;
4497 pn3
->pn_pos
.end
= pn4
->pn_pos
.end
;
4498 pn3
->pn_right
= pn4
;
4502 * Handle the case where there was a let declaration in any case in
4503 * the switch body, but not within an inner block. If it replaced
4504 * tc->blockNode with a new block node then we must refresh pn2 and
4505 * then restore tc->blockNode.
4507 if (tc
->blockNode
!= pn2
)
4508 pn2
= tc
->blockNode
;
4509 tc
->blockNode
= saveBlock
;
4512 pn
->pn_pos
.end
= pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4519 pn
= NewParseNode(PN_BINARY
, tc
);
4522 js_PushStatement(tc
, &stmtInfo
, STMT_WHILE_LOOP
, -1);
4523 pn2
= Condition(cx
, ts
, tc
);
4527 pn2
= Statement(cx
, ts
, tc
);
4531 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4536 pn
= NewParseNode(PN_BINARY
, tc
);
4539 js_PushStatement(tc
, &stmtInfo
, STMT_DO_LOOP
, -1);
4540 pn2
= Statement(cx
, ts
, tc
);
4544 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
4545 pn2
= Condition(cx
, ts
, tc
);
4549 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4551 if (JSVERSION_NUMBER(cx
) != JSVERSION_ECMA_3
) {
4553 * All legacy and extended versions must do automatic semicolon
4554 * insertion after do-while. See the testcase and discussion in
4555 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4557 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
4564 JSParseNode
*pnseq
= NULL
;
4565 #if JS_HAS_BLOCK_SCOPE
4566 JSParseNode
*pnlet
= NULL
;
4567 JSStmtInfo blockInfo
;
4570 /* A FOR node is binary, left is loop control and right is the body. */
4571 pn
= NewParseNode(PN_BINARY
, tc
);
4574 js_PushStatement(tc
, &stmtInfo
, STMT_FOR_LOOP
, -1);
4576 pn
->pn_op
= JSOP_ITER
;
4578 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
4579 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
4580 pn
->pn_iflags
= JSITER_FOREACH
;
4585 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4586 ts
->flags
|= TSF_OPERAND
;
4587 tt
= js_PeekToken(cx
, ts
);
4588 ts
->flags
&= ~TSF_OPERAND
;
4590 #if JS_HAS_BLOCK_SCOPE
4594 if (tt
== TOK_SEMI
) {
4595 if (pn
->pn_iflags
& JSITER_FOREACH
)
4598 /* No initializer -- set first kid of left sub-node to null. */
4602 * Set pn1 to a var list or an initializing expression.
4604 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4605 * of the for statement. This flag will be used by the RelExpr
4606 * production; if it is set, then the 'in' keyword will not be
4607 * recognized as an operator, leaving it available to be parsed as
4608 * part of a for/in loop.
4610 * A side effect of this restriction is that (unparenthesized)
4611 * expressions involving an 'in' operator are illegal in the init
4612 * clause of an ordinary for loop.
4614 tc
->flags
|= TCF_IN_FOR_INIT
;
4615 if (tt
== TOK_VAR
) {
4616 (void) js_GetToken(cx
, ts
);
4617 pn1
= Variables(cx
, ts
, tc
, false);
4618 #if JS_HAS_BLOCK_SCOPE
4619 } else if (tt
== TOK_LET
) {
4621 (void) js_GetToken(cx
, ts
);
4622 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
4623 pn1
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
4624 tt
= TOK_LEXICALSCOPE
;
4626 pnlet
= PushLexicalScope(cx
, ts
, tc
, &blockInfo
);
4629 blockInfo
.flags
|= SIF_FOR_BLOCK
;
4630 pn1
= Variables(cx
, ts
, tc
, false);
4634 pn1
= Expr(cx
, ts
, tc
);
4636 tc
->flags
&= ~TCF_IN_FOR_INIT
;
4642 * We can be sure that it's a for/in loop if there's still an 'in'
4643 * keyword here, even if JavaScript recognizes 'in' as an operator,
4644 * as we've excluded 'in' from being parsed in RelExpr by setting
4645 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4647 if (pn1
&& js_MatchToken(cx
, ts
, TOK_IN
)) {
4648 pn
->pn_iflags
|= JSITER_ENUMERATE
;
4649 stmtInfo
.type
= STMT_FOR_IN_LOOP
;
4651 /* Check that the left side of the 'in' is valid. */
4652 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt
) || PN_TYPE(pn1
) == tt
);
4653 if (TOKEN_TYPE_IS_DECL(tt
)
4654 ? (pn1
->pn_count
> 1 || pn1
->pn_op
== JSOP_DEFCONST
4655 #if JS_HAS_DESTRUCTURING
4656 || (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4657 pn
->pn_op
== JSOP_ITER
&&
4658 !(pn
->pn_iflags
& JSITER_FOREACH
) &&
4659 (pn1
->pn_head
->pn_type
== TOK_RC
||
4660 (pn1
->pn_head
->pn_type
== TOK_RB
&&
4661 pn1
->pn_head
->pn_count
!= 2) ||
4662 (pn1
->pn_head
->pn_type
== TOK_ASSIGN
&&
4663 (pn1
->pn_head
->pn_left
->pn_type
!= TOK_RB
||
4664 pn1
->pn_head
->pn_left
->pn_count
!= 2))))
4667 : (pn1
->pn_type
!= TOK_NAME
&&
4668 pn1
->pn_type
!= TOK_DOT
&&
4669 #if JS_HAS_DESTRUCTURING
4670 ((JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
4671 pn
->pn_op
== JSOP_ITER
&&
4672 !(pn
->pn_iflags
& JSITER_FOREACH
))
4673 ? (pn1
->pn_type
!= TOK_RB
|| pn1
->pn_count
!= 2)
4674 : (pn1
->pn_type
!= TOK_RB
&& pn1
->pn_type
!= TOK_RC
)) &&
4676 #if JS_HAS_LVALUE_RETURN
4677 pn1
->pn_type
!= TOK_LP
&&
4679 #if JS_HAS_XML_SUPPORT
4680 (pn1
->pn_type
!= TOK_UNARYOP
||
4681 pn1
->pn_op
!= JSOP_XMLNAME
) &&
4683 pn1
->pn_type
!= TOK_LB
)) {
4684 js_ReportCompileErrorNumber(cx
, ts
, pn1
, JSREPORT_ERROR
,
4685 JSMSG_BAD_FOR_LEFTSIDE
);
4689 /* pn2 points to the name or destructuring pattern on in's left. */
4691 uintN dflag
= PND_ASSIGNED
;
4693 if (TOKEN_TYPE_IS_DECL(tt
)) {
4694 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4695 pn1
->pn_xflags
|= PNX_FORINVAR
;
4698 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4699 * 'var', or 'const' to hoist the initializer or the entire
4700 * decl out of the loop head. TOK_VAR is the type for both
4701 * 'var' and 'const'.
4704 if ((pn2
->pn_type
== TOK_NAME
&& pn2
->maybeExpr())
4705 #if JS_HAS_DESTRUCTURING
4706 || pn2
->pn_type
== TOK_ASSIGN
4709 pnseq
= NewParseNode(PN_LIST
, tc
);
4712 pnseq
->pn_type
= TOK_SEQ
;
4713 pnseq
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4715 #if JS_HAS_BLOCK_SCOPE
4716 if (tt
== TOK_LET
) {
4718 * Hoist just the 'i' from 'for (let x = i in o)' to
4719 * before the loop, glued together via pnseq.
4721 pn3
= NewParseNode(PN_UNARY
, tc
);
4724 pn3
->pn_type
= TOK_SEMI
;
4725 pn3
->pn_op
= JSOP_NOP
;
4726 #if JS_HAS_DESTRUCTURING
4727 if (pn2
->pn_type
== TOK_ASSIGN
) {
4728 pn4
= pn2
->pn_right
;
4729 pn2
= pn1
->pn_head
= pn2
->pn_left
;
4734 pn2
->pn_expr
= NULL
;
4736 if (!RebindLets(pn4
, tc
))
4738 pn3
->pn_pos
= pn4
->pn_pos
;
4740 pnseq
->initList(pn3
);
4742 #endif /* JS_HAS_BLOCK_SCOPE */
4744 dflag
= PND_INITIALIZED
;
4747 * All of 'var x = i' is hoisted above 'for (x in o)',
4748 * so clear PNX_FORINVAR.
4750 * Request JSOP_POP here since the var is for a simple
4751 * name (it is not a destructuring binding's left-hand
4752 * side) and it has an initializer.
4754 pn1
->pn_xflags
&= ~PNX_FORINVAR
;
4755 pn1
->pn_xflags
|= PNX_POPVAR
;
4756 pnseq
->initList(pn1
);
4758 #if JS_HAS_DESTRUCTURING
4759 if (pn2
->pn_type
== TOK_ASSIGN
) {
4760 pn1
= CloneParseTree(pn2
->pn_left
, tc
);
4766 JS_ASSERT(pn2
->pn_type
== TOK_NAME
);
4767 pn1
= NewNameNode(cx
, ts
, pn2
->pn_atom
, tc
);
4770 pn1
->pn_type
= TOK_NAME
;
4771 pn1
->pn_op
= JSOP_NAME
;
4772 pn1
->pn_pos
= pn2
->pn_pos
;
4774 LinkUseToDef(pn1
, (JSDefinition
*) pn2
, tc
);
4783 #if JS_HAS_LVALUE_RETURN
4784 if (pn2
->pn_type
== TOK_LP
&&
4785 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
)) {
4789 #if JS_HAS_XML_SUPPORT
4790 if (pn2
->pn_type
== TOK_UNARYOP
)
4791 pn2
->pn_op
= JSOP_BINDXMLNAME
;
4795 switch (pn2
->pn_type
) {
4797 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4798 NoteLValue(cx
, pn2
, tc
, dflag
);
4801 #if JS_HAS_DESTRUCTURING
4804 JS_ASSERT(pn2
->pn_type
== TOK_RB
|| pn2
->pn_type
== TOK_RC
);
4808 /* Check for valid lvalues in var-less destructuring for-in. */
4809 if (pn1
== pn2
&& !CheckDestructuring(cx
, NULL
, pn2
, NULL
, tc
))
4812 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
4814 * Destructuring for-in requires [key, value] enumeration
4817 JS_ASSERT(pn
->pn_op
== JSOP_ITER
);
4818 if (!(pn
->pn_iflags
& JSITER_FOREACH
))
4819 pn
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
4828 * Parse the object expression as the right operand of 'in', first
4829 * removing the top statement from the statement-stack if this is a
4830 * 'for (let x in y)' loop.
4832 #if JS_HAS_BLOCK_SCOPE
4833 JSStmtInfo
*save
= tc
->topStmt
;
4835 tc
->topStmt
= save
->down
;
4837 pn2
= Expr(cx
, ts
, tc
);
4838 #if JS_HAS_BLOCK_SCOPE
4843 pn2
= NewBinary(TOK_IN
, JSOP_NOP
, pn1
, pn2
, tc
);
4848 if (pn
->pn_iflags
& JSITER_FOREACH
)
4850 pn
->pn_op
= JSOP_NOP
;
4852 /* Parse the loop condition or null into pn2. */
4853 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
4854 ts
->flags
|= TSF_OPERAND
;
4855 tt
= js_PeekToken(cx
, ts
);
4856 ts
->flags
&= ~TSF_OPERAND
;
4857 if (tt
== TOK_SEMI
) {
4860 pn2
= Expr(cx
, ts
, tc
);
4865 /* Parse the update expression or null into pn3. */
4866 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
4867 ts
->flags
|= TSF_OPERAND
;
4868 tt
= js_PeekToken(cx
, ts
);
4869 ts
->flags
&= ~TSF_OPERAND
;
4873 pn3
= Expr(cx
, ts
, tc
);
4878 /* Build the FORHEAD node to use as the left kid of pn. */
4879 pn4
= NewParseNode(PN_TERNARY
, tc
);
4882 pn4
->pn_type
= TOK_FORHEAD
;
4883 pn4
->pn_op
= JSOP_NOP
;
4890 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
4892 /* Parse the loop body into pn->pn_right. */
4893 pn2
= Statement(cx
, ts
, tc
);
4898 /* Record the absolute line number for source note emission. */
4899 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4901 #if JS_HAS_BLOCK_SCOPE
4904 pnlet
->pn_expr
= pn
;
4909 pnseq
->pn_pos
.end
= pn
->pn_pos
.end
;
4917 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
4918 JSMSG_BAD_FOR_EACH_LOOP
);
4923 JSParseNode
*catchList
, *lastCatch
;
4926 * try nodes are ternary.
4927 * kid1 is the try Statement
4928 * kid2 is the catch node list or null
4929 * kid3 is the finally Statement
4931 * catch nodes are ternary.
4932 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
4933 * kid2 is the catch guard or null if no guard
4934 * kid3 is the catch block
4936 * catch lvalue nodes are either:
4937 * TOK_NAME for a single identifier
4938 * TOK_RB or TOK_RC for a destructuring left-hand side
4940 * finally nodes are TOK_LC Statement lists.
4942 pn
= NewParseNode(PN_TERNARY
, tc
);
4945 pn
->pn_op
= JSOP_NOP
;
4947 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
4948 if (!PushBlocklikeStatement(&stmtInfo
, STMT_TRY
, tc
))
4950 pn
->pn_kid1
= Statements(cx
, ts
, tc
);
4953 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
4957 tt
= js_GetToken(cx
, ts
);
4958 if (tt
== TOK_CATCH
) {
4959 catchList
= NewParseNode(PN_LIST
, tc
);
4962 catchList
->pn_type
= TOK_RESERVED
;
4963 catchList
->makeEmpty();
4967 JSParseNode
*pnblock
;
4970 /* Check for another catch after unconditional catch. */
4971 if (lastCatch
&& !lastCatch
->pn_kid2
) {
4972 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4973 JSMSG_CATCH_AFTER_GENERAL
);
4978 * Create a lexical scope node around the whole catch clause,
4979 * including the head.
4981 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
4984 stmtInfo
.type
= STMT_CATCH
;
4987 * Legal catch forms are:
4989 * catch (lhs if <boolean_expression>)
4990 * where lhs is a name or a destructuring left-hand side.
4991 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
4993 pn2
= NewParseNode(PN_TERNARY
, tc
);
4996 pnblock
->pn_expr
= pn2
;
4997 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
5000 * Contrary to ECMA Ed. 3, the catch variable is lexically
5001 * scoped, not a property of a new Object instance. This is
5002 * an intentional change that anticipates ECMA Ed. 4.
5006 data
.binder
= BindLet
;
5007 data
.let
.overflow
= JSMSG_TOO_MANY_CATCH_VARS
;
5009 tt
= js_GetToken(cx
, ts
);
5011 #if JS_HAS_DESTRUCTURING
5014 pn3
= DestructuringExpr(cx
, &data
, tc
, tt
);
5021 label
= CURRENT_TOKEN(ts
).t_atom
;
5022 pn3
= NewBindingNode(ts
, label
, tc
, true);
5026 if (!data
.binder(cx
, &data
, label
, tc
))
5031 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5032 JSMSG_CATCH_IDENTIFIER
);
5037 #if JS_HAS_CATCH_GUARD
5039 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5040 * to avoid conflicting with the JS2/ECMAv4 type annotation
5041 * catchguard syntax.
5043 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
5044 pn2
->pn_kid2
= Expr(cx
, ts
, tc
);
5049 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
5051 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
5052 pn2
->pn_kid3
= Statements(cx
, ts
, tc
);
5055 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
5058 catchList
->append(pnblock
);
5060 ts
->flags
|= TSF_OPERAND
;
5061 tt
= js_GetToken(cx
, ts
);
5062 ts
->flags
&= ~TSF_OPERAND
;
5063 } while (tt
== TOK_CATCH
);
5065 pn
->pn_kid2
= catchList
;
5067 if (tt
== TOK_FINALLY
) {
5068 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
5069 if (!PushBlocklikeStatement(&stmtInfo
, STMT_FINALLY
, tc
))
5071 pn
->pn_kid3
= Statements(cx
, ts
, tc
);
5074 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
5079 if (!catchList
&& !pn
->pn_kid3
) {
5080 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5081 JSMSG_CATCH_OR_FINALLY
);
5088 pn
= NewParseNode(PN_UNARY
, tc
);
5092 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5093 ts
->flags
|= TSF_OPERAND
;
5094 tt
= js_PeekTokenSameLine(cx
, ts
);
5095 ts
->flags
&= ~TSF_OPERAND
;
5096 if (tt
== TOK_ERROR
)
5098 if (tt
== TOK_EOF
|| tt
== TOK_EOL
|| tt
== TOK_SEMI
|| tt
== TOK_RC
) {
5099 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5100 JSMSG_SYNTAX_ERROR
);
5104 pn2
= Expr(cx
, ts
, tc
);
5107 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5108 pn
->pn_op
= JSOP_THROW
;
5112 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5114 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5115 JSMSG_CATCH_WITHOUT_TRY
);
5119 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5120 JSMSG_FINALLY_WITHOUT_TRY
);
5124 pn
= NewParseNode(PN_NULLARY
, tc
);
5127 if (!MatchLabel(cx
, ts
, pn
))
5130 label
= pn
->pn_atom
;
5132 for (; ; stmt
= stmt
->down
) {
5134 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5135 JSMSG_LABEL_NOT_FOUND
);
5138 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
)
5142 for (; ; stmt
= stmt
->down
) {
5144 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5148 if (STMT_IS_LOOP(stmt
) || stmt
->type
== STMT_SWITCH
)
5153 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5157 pn
= NewParseNode(PN_NULLARY
, tc
);
5160 if (!MatchLabel(cx
, ts
, pn
))
5163 label
= pn
->pn_atom
;
5165 for (stmt2
= NULL
; ; stmt
= stmt
->down
) {
5167 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5168 JSMSG_LABEL_NOT_FOUND
);
5171 if (stmt
->type
== STMT_LABEL
) {
5172 if (stmt
->label
== label
) {
5173 if (!stmt2
|| !STMT_IS_LOOP(stmt2
)) {
5174 js_ReportCompileErrorNumber(cx
, ts
, NULL
,
5176 JSMSG_BAD_CONTINUE
);
5186 for (; ; stmt
= stmt
->down
) {
5188 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5189 JSMSG_BAD_CONTINUE
);
5192 if (STMT_IS_LOOP(stmt
))
5197 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5201 pn
= NewParseNode(PN_BINARY
, tc
);
5204 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
5205 pn2
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
5208 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
5211 js_PushStatement(tc
, &stmtInfo
, STMT_WITH
, -1);
5212 pn2
= Statement(cx
, ts
, tc
);
5217 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5219 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5223 pn
= Variables(cx
, ts
, tc
, false);
5227 /* Tell js_EmitTree to generate a final POP. */
5228 pn
->pn_xflags
|= PNX_POPVAR
;
5231 #if JS_HAS_BLOCK_SCOPE
5235 JSObjectBox
*blockbox
;
5237 /* Check for a let statement or let expression. */
5238 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
5239 pn
= LetBlock(cx
, ts
, tc
, JS_TRUE
);
5240 if (!pn
|| pn
->pn_op
== JSOP_LEAVEBLOCK
)
5243 /* Let expressions require automatic semicolon insertion. */
5244 JS_ASSERT(pn
->pn_type
== TOK_SEMI
||
5245 pn
->pn_op
== JSOP_LEAVEBLOCKEXPR
);
5250 * This is a let declaration. We must be directly under a block per
5251 * the proposed ES4 specs, but not an implicit block created due to
5252 * 'for (let ...)'. If we pass this error test, make the enclosing
5253 * JSStmtInfo be our scope. Further let declarations in this block
5254 * will find this scope statement and use the same block object.
5256 * If we are the first let declaration in this block (i.e., when the
5257 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5258 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5262 (!STMT_MAYBE_SCOPE(stmt
) || (stmt
->flags
& SIF_FOR_BLOCK
))) {
5263 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5264 JSMSG_LET_DECL_NOT_IN_BLOCK
);
5268 if (stmt
&& (stmt
->flags
& SIF_SCOPE
)) {
5269 JS_ASSERT(tc
->blockChain
== stmt
->blockObj
);
5270 obj
= tc
->blockChain
;
5272 if (!stmt
|| (stmt
->flags
& SIF_BODY_BLOCK
)) {
5274 * ES4 specifies that let at top level and at body-block scope
5275 * does not shadow var, so convert back to var.
5277 CURRENT_TOKEN(ts
).type
= TOK_VAR
;
5278 CURRENT_TOKEN(ts
).t_op
= JSOP_DEFVAR
;
5280 pn
= Variables(cx
, ts
, tc
, false);
5283 pn
->pn_xflags
|= PNX_POPVAR
;
5288 * Some obvious assertions here, but they may help clarify the
5289 * situation. This stmt is not yet a scope, so it must not be a
5290 * catch block (catch is a lexical scope by definition).
5292 JS_ASSERT(!(stmt
->flags
& SIF_SCOPE
));
5293 JS_ASSERT(stmt
!= tc
->topScopeStmt
);
5294 JS_ASSERT(stmt
->type
== STMT_BLOCK
||
5295 stmt
->type
== STMT_SWITCH
||
5296 stmt
->type
== STMT_TRY
||
5297 stmt
->type
== STMT_FINALLY
);
5298 JS_ASSERT(!stmt
->downScope
);
5300 /* Convert the block statement into a scope statement. */
5301 JSObject
*obj
= js_NewBlockObject(tc
->compiler
->context
);
5305 blockbox
= tc
->compiler
->newObjectBox(obj
);
5310 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5311 * list stack, if it isn't already there. If it is there, but it
5312 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5315 stmt
->flags
|= SIF_SCOPE
;
5316 stmt
->downScope
= tc
->topScopeStmt
;
5317 tc
->topScopeStmt
= stmt
;
5318 JS_SCOPE_DEPTH_METERING(++tc
->scopeDepth
> tc
->maxScopeDepth
&&
5319 (tc
->maxScopeDepth
= tc
->scopeDepth
));
5321 STOBJ_SET_PARENT(obj
, tc
->blockChain
);
5322 tc
->blockChain
= obj
;
5323 stmt
->blockObj
= obj
;
5326 pn1
= tc
->blockNode
;
5327 JS_ASSERT(!pn1
|| pn1
->pn_type
!= TOK_LEXICALSCOPE
);
5330 /* Create a new lexical scope node for these statements. */
5331 pn1
= NewParseNode(PN_NAME
, tc
);
5335 pn1
->pn_type
= TOK_LEXICALSCOPE
;
5336 pn1
->pn_op
= JSOP_LEAVEBLOCK
;
5337 pn1
->pn_pos
= tc
->blockNode
->pn_pos
;
5338 pn1
->pn_objbox
= blockbox
;
5339 pn1
->pn_expr
= tc
->blockNode
;
5340 pn1
->pn_blockid
= tc
->blockNode
->pn_blockid
;
5341 tc
->blockNode
= pn1
;
5344 pn
= Variables(cx
, ts
, tc
, false);
5347 pn
->pn_xflags
= PNX_POPVAR
;
5350 #endif /* JS_HAS_BLOCK_SCOPE */
5353 pn
= ReturnOrYield(cx
, ts
, tc
, Expr
);
5362 oldflags
= tc
->flags
;
5363 tc
->flags
= oldflags
& ~TCF_HAS_FUNCTION_STMT
;
5364 if (!PushBlocklikeStatement(&stmtInfo
, STMT_BLOCK
, tc
))
5366 pn
= Statements(cx
, ts
, tc
);
5370 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
5374 * If we contain a function statement and our container is top-level
5375 * or another block, flag pn to preserve braces when decompiling.
5377 if ((tc
->flags
& TCF_HAS_FUNCTION_STMT
) &&
5378 (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)) {
5379 pn
->pn_xflags
|= PNX_NEEDBRACES
;
5381 tc
->flags
= oldflags
| (tc
->flags
& (TCF_FUN_FLAGS
| TCF_RETURN_FLAGS
));
5387 pn
= NewParseNode(PN_UNARY
, tc
);
5390 pn
->pn_type
= TOK_SEMI
;
5393 #if JS_HAS_DEBUGGER_KEYWORD
5395 pn
= NewParseNode(PN_NULLARY
, tc
);
5398 pn
->pn_type
= TOK_DEBUGGER
;
5399 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5401 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5403 #if JS_HAS_XML_SUPPORT
5405 pn
= NewParseNode(PN_UNARY
, tc
);
5408 if (!js_MatchToken(cx
, ts
, TOK_NAME
) ||
5409 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.xmlAtom
||
5410 !js_MatchToken(cx
, ts
, TOK_NAME
) ||
5411 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.namespaceAtom
||
5412 !js_MatchToken(cx
, ts
, TOK_ASSIGN
) ||
5413 CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
5414 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5415 JSMSG_BAD_DEFAULT_XML_NAMESPACE
);
5418 pn2
= Expr(cx
, ts
, tc
);
5421 pn
->pn_op
= JSOP_DEFXMLNS
;
5422 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5431 #if JS_HAS_XML_SUPPORT
5435 pn2
= Expr(cx
, ts
, tc
);
5439 if (js_PeekToken(cx
, ts
) == TOK_COLON
) {
5440 if (pn2
->pn_type
!= TOK_NAME
) {
5441 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5445 label
= pn2
->pn_atom
;
5446 for (stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
5447 if (stmt
->type
== STMT_LABEL
&& stmt
->label
== label
) {
5448 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5449 JSMSG_DUPLICATE_LABEL
);
5455 (void) js_GetToken(cx
, ts
);
5457 /* Push a label struct and parse the statement. */
5458 js_PushStatement(tc
, &stmtInfo
, STMT_LABEL
, -1);
5459 stmtInfo
.label
= label
;
5460 pn
= Statement(cx
, ts
, tc
);
5464 /* Normalize empty statement to empty block for the decompiler. */
5465 if (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
) {
5466 pn
->pn_type
= TOK_LC
;
5467 pn
->pn_arity
= PN_LIST
;
5471 /* Pop the label, set pn_expr, and return early. */
5473 pn2
->pn_type
= TOK_COLON
;
5474 pn2
->pn_pos
.end
= pn
->pn_pos
.end
;
5479 pn
= NewParseNode(PN_UNARY
, tc
);
5482 pn
->pn_type
= TOK_SEMI
;
5483 pn
->pn_pos
= pn2
->pn_pos
;
5488 /* Check termination of this primitive statement. */
5489 return MatchOrInsertSemicolon(cx
, ts
) ? pn
: NULL
;
5493 NoteArgumentsUse(JSTreeContext
*tc
)
5495 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
5496 tc
->flags
|= TCF_FUN_USES_ARGUMENTS
;
5498 tc
->funbox
->node
->pn_dflags
|= PND_FUNARG
;
5501 static JSParseNode
*
5502 Variables(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, bool inLetHead
)
5506 JSStmtInfo
*scopeStmt
;
5508 JSParseNode
*pn
, *pn2
;
5512 * The three options here are:
5513 * - TOK_LET: We are parsing a let declaration.
5514 * - TOK_LP: We are parsing the head of a let block.
5515 * - Otherwise, we're parsing var declarations.
5517 tt
= CURRENT_TOKEN(ts
).type
;
5518 let
= (tt
== TOK_LET
|| tt
== TOK_LP
);
5519 JS_ASSERT(let
|| tt
== TOK_VAR
);
5521 #if JS_HAS_BLOCK_SCOPE
5522 bool popScope
= (inLetHead
|| (let
&& (tc
->flags
& TCF_IN_FOR_INIT
)));
5523 JSStmtInfo
*save
= tc
->topStmt
, *saveScope
= tc
->topScopeStmt
;
5526 /* Make sure that Statement set up the tree context correctly. */
5527 scopeStmt
= tc
->topScopeStmt
;
5529 while (scopeStmt
&& !(scopeStmt
->flags
& SIF_SCOPE
)) {
5530 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt
));
5531 scopeStmt
= scopeStmt
->downScope
;
5533 JS_ASSERT(scopeStmt
);
5536 data
.op
= let
? JSOP_NOP
: CURRENT_TOKEN(ts
).t_op
;
5537 pn
= NewParseNode(PN_LIST
, tc
);
5540 pn
->pn_op
= data
.op
;
5544 * SpiderMonkey const is really "write once per initialization evaluation"
5545 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5546 * this code will change soon.
5549 JS_ASSERT(tc
->blockChain
== scopeStmt
->blockObj
);
5550 data
.binder
= BindLet
;
5551 data
.let
.overflow
= JSMSG_TOO_MANY_LOCALS
;
5553 data
.binder
= BindVarOrConst
;
5557 tt
= js_GetToken(cx
, ts
);
5558 #if JS_HAS_DESTRUCTURING
5559 if (tt
== TOK_LB
|| tt
== TOK_LC
) {
5560 ts
->flags
|= TSF_DESTRUCTURING
;
5561 pn2
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
5562 ts
->flags
&= ~TSF_DESTRUCTURING
;
5566 if (!CheckDestructuring(cx
, &data
, pn2
, NULL
, tc
))
5568 if ((tc
->flags
& TCF_IN_FOR_INIT
) &&
5569 js_PeekToken(cx
, ts
) == TOK_IN
) {
5574 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_BAD_DESTRUCT_DECL
);
5575 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5578 #if JS_HAS_BLOCK_SCOPE
5580 tc
->topStmt
= save
->down
;
5581 tc
->topScopeStmt
= saveScope
->downScope
;
5584 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5585 #if JS_HAS_BLOCK_SCOPE
5588 tc
->topScopeStmt
= saveScope
;
5592 if (!init
|| !UndominateInitializers(pn2
, init
, tc
))
5595 pn2
= NewBinary(TOK_ASSIGN
, JSOP_NOP
, pn2
, init
, tc
);
5601 #endif /* JS_HAS_DESTRUCTURING */
5603 if (tt
!= TOK_NAME
) {
5604 if (tt
!= TOK_ERROR
) {
5605 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5606 JSMSG_NO_VARIABLE_NAME
);
5611 atom
= CURRENT_TOKEN(ts
).t_atom
;
5612 pn2
= NewBindingNode(ts
, atom
, tc
, let
);
5615 if (data
.op
== JSOP_DEFCONST
)
5616 pn2
->pn_dflags
|= PND_CONST
;
5618 if (!data
.binder(cx
, &data
, atom
, tc
))
5622 if (js_MatchToken(cx
, ts
, TOK_ASSIGN
)) {
5623 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
5626 #if JS_HAS_BLOCK_SCOPE
5628 tc
->topStmt
= save
->down
;
5629 tc
->topScopeStmt
= saveScope
->downScope
;
5632 JSParseNode
*init
= AssignExpr(cx
, ts
, tc
);
5633 #if JS_HAS_BLOCK_SCOPE
5636 tc
->topScopeStmt
= saveScope
;
5643 pn2
= MakeAssignment(pn2
, init
, tc
);
5647 pn2
->pn_expr
= init
;
5650 pn2
->pn_op
= (PN_OP(pn2
) == JSOP_ARGUMENTS
)
5652 : (pn2
->pn_dflags
& PND_GVAR
)
5654 : (pn2
->pn_dflags
& PND_BOUND
)
5656 : (data
.op
== JSOP_DEFCONST
)
5660 NoteLValue(cx
, pn2
, tc
, data
.fresh
? PND_INITIALIZED
: PND_ASSIGNED
);
5662 /* The declarator's position must include the initializer. */
5663 pn2
->pn_pos
.end
= init
->pn_pos
.end
;
5665 if ((tc
->flags
& TCF_IN_FUNCTION
) &&
5666 atom
== cx
->runtime
->atomState
.argumentsAtom
) {
5667 NoteArgumentsUse(tc
);
5669 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5672 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5674 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5678 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5679 JSMSG_BAD_VAR_INIT
);
5683 static JSParseNode
*
5684 Expr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5686 JSParseNode
*pn
, *pn2
;
5688 pn
= AssignExpr(cx
, ts
, tc
);
5689 if (pn
&& js_MatchToken(cx
, ts
, TOK_COMMA
)) {
5690 pn2
= NewParseNode(PN_LIST
, tc
);
5693 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
5697 #if JS_HAS_GENERATORS
5699 if (pn2
->pn_type
== TOK_YIELD
&& !pn2
->pn_parens
) {
5700 js_ReportCompileErrorNumber(cx
, ts
, pn2
, JSREPORT_ERROR
,
5701 JSMSG_BAD_GENERATOR_SYNTAX
,
5706 pn2
= AssignExpr(cx
, ts
, tc
);
5710 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
5711 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
5716 static JSParseNode
*
5717 AssignExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5719 JSParseNode
*pn
, *rhs
;
5723 JS_CHECK_RECURSION(cx
, return NULL
);
5725 #if JS_HAS_GENERATORS
5726 ts
->flags
|= TSF_OPERAND
;
5727 if (js_MatchToken(cx
, ts
, TOK_YIELD
)) {
5728 ts
->flags
&= ~TSF_OPERAND
;
5729 return ReturnOrYield(cx
, ts
, tc
, AssignExpr
);
5731 ts
->flags
&= ~TSF_OPERAND
;
5734 pn
= CondExpr(cx
, ts
, tc
);
5738 tt
= js_GetToken(cx
, ts
);
5739 #if JS_HAS_GETTER_SETTER
5740 if (tt
== TOK_NAME
) {
5741 tt
= CheckGetterOrSetter(cx
, ts
, TOK_ASSIGN
);
5742 if (tt
== TOK_ERROR
)
5746 if (tt
!= TOK_ASSIGN
) {
5751 op
= CURRENT_TOKEN(ts
).t_op
;
5752 switch (pn
->pn_type
) {
5754 pn
->pn_op
= JSOP_SETNAME
;
5755 NoteLValue(cx
, pn
, tc
);
5758 pn
->pn_op
= JSOP_SETPROP
;
5761 pn
->pn_op
= JSOP_SETELEM
;
5763 #if JS_HAS_DESTRUCTURING
5766 if (op
!= JSOP_NOP
) {
5767 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5768 JSMSG_BAD_DESTRUCT_ASS
);
5771 rhs
= AssignExpr(cx
, ts
, tc
);
5772 if (!rhs
|| !CheckDestructuring(cx
, NULL
, pn
, rhs
, tc
))
5774 return NewBinary(TOK_ASSIGN
, op
, pn
, rhs
, tc
);
5776 #if JS_HAS_LVALUE_RETURN
5778 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
5782 #if JS_HAS_XML_SUPPORT
5784 if (pn
->pn_op
== JSOP_XMLNAME
) {
5785 pn
->pn_op
= JSOP_SETXMLNAME
;
5791 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5792 JSMSG_BAD_LEFTSIDE_OF_ASS
);
5796 rhs
= AssignExpr(cx
, ts
, tc
);
5797 if (rhs
&& PN_TYPE(pn
) == TOK_NAME
&& pn
->pn_used
) {
5798 JSDefinition
*dn
= pn
->pn_lexdef
;
5801 * If the definition is not flagged as assigned, we must have imputed
5802 * the initialized flag to it, to optimize for flat closures. But that
5803 * optimization uses source coordinates to check dominance relations,
5804 * so we must extend the end of the definition to cover the right-hand
5805 * side of this assignment, i.e., the initializer.
5807 if (!dn
->isAssigned()) {
5808 JS_ASSERT(dn
->isInitialized());
5809 dn
->pn_pos
.end
= rhs
->pn_pos
.end
;
5813 return NewBinary(TOK_ASSIGN
, op
, pn
, rhs
, tc
);
5816 static JSParseNode
*
5817 CondExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5819 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
;
5822 pn
= OrExpr(cx
, ts
, tc
);
5823 if (pn
&& js_MatchToken(cx
, ts
, TOK_HOOK
)) {
5825 pn
= NewParseNode(PN_TERNARY
, tc
);
5829 * Always accept the 'in' operator in the middle clause of a ternary,
5830 * where it's unambiguous, even if we might be parsing the init of a
5833 oldflags
= tc
->flags
;
5834 tc
->flags
&= ~TCF_IN_FOR_INIT
;
5835 pn2
= AssignExpr(cx
, ts
, tc
);
5836 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
5840 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
5841 pn3
= AssignExpr(cx
, ts
, tc
);
5844 pn
->pn_pos
.begin
= pn1
->pn_pos
.begin
;
5845 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
5853 static JSParseNode
*
5854 OrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5858 pn
= AndExpr(cx
, ts
, tc
);
5859 while (pn
&& js_MatchToken(cx
, ts
, TOK_OR
))
5860 pn
= NewBinary(TOK_OR
, JSOP_OR
, pn
, AndExpr(cx
, ts
, tc
), tc
);
5864 static JSParseNode
*
5865 AndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5869 pn
= BitOrExpr(cx
, ts
, tc
);
5870 while (pn
&& js_MatchToken(cx
, ts
, TOK_AND
))
5871 pn
= NewBinary(TOK_AND
, JSOP_AND
, pn
, BitOrExpr(cx
, ts
, tc
), tc
);
5875 static JSParseNode
*
5876 BitOrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5880 pn
= BitXorExpr(cx
, ts
, tc
);
5881 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITOR
)) {
5882 pn
= NewBinary(TOK_BITOR
, JSOP_BITOR
, pn
, BitXorExpr(cx
, ts
, tc
),
5888 static JSParseNode
*
5889 BitXorExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5893 pn
= BitAndExpr(cx
, ts
, tc
);
5894 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITXOR
)) {
5895 pn
= NewBinary(TOK_BITXOR
, JSOP_BITXOR
, pn
, BitAndExpr(cx
, ts
, tc
),
5901 static JSParseNode
*
5902 BitAndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5906 pn
= EqExpr(cx
, ts
, tc
);
5907 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITAND
))
5908 pn
= NewBinary(TOK_BITAND
, JSOP_BITAND
, pn
, EqExpr(cx
, ts
, tc
), tc
);
5912 static JSParseNode
*
5913 EqExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5918 pn
= RelExpr(cx
, ts
, tc
);
5919 while (pn
&& js_MatchToken(cx
, ts
, TOK_EQOP
)) {
5920 op
= CURRENT_TOKEN(ts
).t_op
;
5921 pn
= NewBinary(TOK_EQOP
, op
, pn
, RelExpr(cx
, ts
, tc
), tc
);
5926 static JSParseNode
*
5927 RelExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5932 uintN inForInitFlag
= tc
->flags
& TCF_IN_FOR_INIT
;
5935 * Uses of the in operator in ShiftExprs are always unambiguous,
5936 * so unset the flag that prohibits recognizing it.
5938 tc
->flags
&= ~TCF_IN_FOR_INIT
;
5940 pn
= ShiftExpr(cx
, ts
, tc
);
5942 (js_MatchToken(cx
, ts
, TOK_RELOP
) ||
5944 * Recognize the 'in' token as an operator only if we're not
5945 * currently in the init expr of a for loop.
5947 (inForInitFlag
== 0 && js_MatchToken(cx
, ts
, TOK_IN
)) ||
5948 js_MatchToken(cx
, ts
, TOK_INSTANCEOF
))) {
5949 tt
= CURRENT_TOKEN(ts
).type
;
5950 op
= CURRENT_TOKEN(ts
).t_op
;
5951 pn
= NewBinary(tt
, op
, pn
, ShiftExpr(cx
, ts
, tc
), tc
);
5953 /* Restore previous state of inForInit flag. */
5954 tc
->flags
|= inForInitFlag
;
5959 static JSParseNode
*
5960 ShiftExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5965 pn
= AddExpr(cx
, ts
, tc
);
5966 while (pn
&& js_MatchToken(cx
, ts
, TOK_SHOP
)) {
5967 op
= CURRENT_TOKEN(ts
).t_op
;
5968 pn
= NewBinary(TOK_SHOP
, op
, pn
, AddExpr(cx
, ts
, tc
), tc
);
5973 static JSParseNode
*
5974 AddExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5980 pn
= MulExpr(cx
, ts
, tc
);
5982 (js_MatchToken(cx
, ts
, TOK_PLUS
) ||
5983 js_MatchToken(cx
, ts
, TOK_MINUS
))) {
5984 tt
= CURRENT_TOKEN(ts
).type
;
5985 op
= (tt
== TOK_PLUS
) ? JSOP_ADD
: JSOP_SUB
;
5986 pn
= NewBinary(tt
, op
, pn
, MulExpr(cx
, ts
, tc
), tc
);
5991 static JSParseNode
*
5992 MulExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
5998 pn
= UnaryExpr(cx
, ts
, tc
);
6000 (js_MatchToken(cx
, ts
, TOK_STAR
) ||
6001 js_MatchToken(cx
, ts
, TOK_DIVOP
))) {
6002 tt
= CURRENT_TOKEN(ts
).type
;
6003 op
= CURRENT_TOKEN(ts
).t_op
;
6004 pn
= NewBinary(tt
, op
, pn
, UnaryExpr(cx
, ts
, tc
), tc
);
6009 static JSParseNode
*
6010 SetLvalKid(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
, JSParseNode
*kid
,
6013 if (kid
->pn_type
!= TOK_NAME
&&
6014 kid
->pn_type
!= TOK_DOT
&&
6015 #if JS_HAS_LVALUE_RETURN
6016 (kid
->pn_type
!= TOK_LP
||
6017 (kid
->pn_op
!= JSOP_CALL
&& kid
->pn_op
!= JSOP_EVAL
&& kid
->pn_op
!= JSOP_APPLY
)) &&
6019 #if JS_HAS_XML_SUPPORT
6020 (kid
->pn_type
!= TOK_UNARYOP
|| kid
->pn_op
!= JSOP_XMLNAME
) &&
6022 kid
->pn_type
!= TOK_LB
) {
6023 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6024 JSMSG_BAD_OPERAND
, name
);
6031 static const char incop_name_str
[][10] = {"increment", "decrement"};
6034 SetIncOpKid(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6035 JSParseNode
*pn
, JSParseNode
*kid
,
6036 JSTokenType tt
, JSBool preorder
)
6040 kid
= SetLvalKid(cx
, ts
, pn
, kid
, incop_name_str
[tt
== TOK_DEC
]);
6043 switch (kid
->pn_type
) {
6045 op
= (tt
== TOK_INC
)
6046 ? (preorder
? JSOP_INCNAME
: JSOP_NAMEINC
)
6047 : (preorder
? JSOP_DECNAME
: JSOP_NAMEDEC
);
6048 NoteLValue(cx
, kid
, tc
);
6052 op
= (tt
== TOK_INC
)
6053 ? (preorder
? JSOP_INCPROP
: JSOP_PROPINC
)
6054 : (preorder
? JSOP_DECPROP
: JSOP_PROPDEC
);
6057 #if JS_HAS_LVALUE_RETURN
6059 if (!MakeSetCall(cx
, kid
, tc
, JSMSG_BAD_INCOP_OPERAND
))
6063 #if JS_HAS_XML_SUPPORT
6065 if (kid
->pn_op
== JSOP_XMLNAME
)
6066 kid
->pn_op
= JSOP_SETXMLNAME
;
6070 op
= (tt
== TOK_INC
)
6071 ? (preorder
? JSOP_INCELEM
: JSOP_ELEMINC
)
6072 : (preorder
? JSOP_DECELEM
: JSOP_ELEMDEC
);
6083 static JSParseNode
*
6084 UnaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6087 JSParseNode
*pn
, *pn2
;
6089 JS_CHECK_RECURSION(cx
, return NULL
);
6091 ts
->flags
|= TSF_OPERAND
;
6092 tt
= js_GetToken(cx
, ts
);
6093 ts
->flags
&= ~TSF_OPERAND
;
6099 pn
= NewParseNode(PN_UNARY
, tc
);
6102 pn
->pn_type
= TOK_UNARYOP
; /* PLUS and MINUS are binary */
6103 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
6104 pn2
= UnaryExpr(cx
, ts
, tc
);
6107 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6113 pn
= NewParseNode(PN_UNARY
, tc
);
6116 pn2
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
6119 if (!SetIncOpKid(cx
, ts
, tc
, pn
, pn2
, tt
, JS_TRUE
))
6121 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6125 pn
= NewParseNode(PN_UNARY
, tc
);
6128 pn2
= UnaryExpr(cx
, ts
, tc
);
6131 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
6134 * Under ECMA3, deleting any unary expression is valid -- it simply
6135 * returns true. Here we fold constants before checking for a call
6136 * expression, in order to rule out delete of a generator expression.
6138 if (!js_FoldConstants(cx
, pn2
, tc
))
6140 switch (pn2
->pn_type
) {
6142 if (pn2
->pn_op
!= JSOP_SETCALL
&&
6143 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_DELETE_OPERAND
)) {
6148 pn2
->pn_op
= JSOP_DELNAME
;
6160 pn
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
6164 /* Don't look across a newline boundary for a postfix incop. */
6165 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
6166 ts
->flags
|= TSF_OPERAND
;
6167 tt
= js_PeekTokenSameLine(cx
, ts
);
6168 ts
->flags
&= ~TSF_OPERAND
;
6169 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
6170 (void) js_GetToken(cx
, ts
);
6171 pn2
= NewParseNode(PN_UNARY
, tc
);
6174 if (!SetIncOpKid(cx
, ts
, tc
, pn2
, pn
, tt
, JS_FALSE
))
6176 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6185 #if JS_HAS_GENERATORS
6188 * A dedicated helper for transplanting the comprehension expression E in
6190 * [E for (V in I)] // array comprehension
6191 * (E for (V in I)) // generator expression
6193 * from its initial location in the AST, on the left of the 'for', to its final
6194 * position on the right. To avoid a separate pass we do this by adjusting the
6195 * blockids and name binding links that were established when E was parsed.
6197 * A generator expression desugars like so:
6199 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6201 * so the transplanter must adjust static level as well as blockid. E's source
6202 * coordinates in root->pn_pos are critical to deciding which binding links to
6203 * preserve and which to cut.
6205 * NB: This is not a general tree transplanter -- it knows in particular that
6206 * the one or more bindings induced by V have not yet been created.
6208 class CompExprTransplanter
{
6216 CompExprTransplanter(JSParseNode
*pn
, JSTreeContext
*tc
, bool ge
, uintN adj
)
6217 : root(pn
), tc(tc
), genexp(ge
), adjust(adj
), funcLevel(0)
6221 bool transplant(JSParseNode
*pn
);
6225 * Any definitions nested within the comprehension expression of a generator
6226 * expression must move "down" one static level, which of course increases the
6227 * upvar-frame-skip count.
6230 BumpStaticLevel(JSParseNode
*pn
, JSTreeContext
*tc
)
6232 if (pn
->pn_cookie
!= FREE_UPVAR_COOKIE
) {
6233 uintN level
= UPVAR_FRAME_SKIP(pn
->pn_cookie
) + 1;
6235 JS_ASSERT(level
>= tc
->staticLevel
);
6236 if (level
>= FREE_STATIC_LEVEL
) {
6237 JS_ReportErrorNumber(tc
->compiler
->context
, js_GetErrorMessage
, NULL
,
6238 JSMSG_TOO_DEEP
, js_function_str
);
6242 pn
->pn_cookie
= MAKE_UPVAR_COOKIE(level
, UPVAR_FRAME_SLOT(pn
->pn_cookie
));
6248 AdjustBlockId(JSParseNode
*pn
, uintN adjust
, JSTreeContext
*tc
)
6250 JS_ASSERT(pn
->pn_arity
== PN_LIST
|| pn
->pn_arity
== PN_FUNC
|| pn
->pn_arity
== PN_NAME
);
6251 pn
->pn_blockid
+= adjust
;
6252 if (pn
->pn_blockid
>= tc
->blockidGen
)
6253 tc
->blockidGen
= pn
->pn_blockid
+ 1;
6257 CompExprTransplanter::transplant(JSParseNode
*pn
)
6262 switch (pn
->pn_arity
) {
6264 for (JSParseNode
*pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
6266 if (pn
->pn_pos
>= root
->pn_pos
)
6267 AdjustBlockId(pn
, adjust
, tc
);
6271 transplant(pn
->pn_kid1
);
6272 transplant(pn
->pn_kid2
);
6273 transplant(pn
->pn_kid3
);
6277 transplant(pn
->pn_left
);
6279 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6280 if (pn
->pn_right
!= pn
->pn_left
)
6281 transplant(pn
->pn_right
);
6285 transplant(pn
->pn_kid
);
6291 * Only the first level of transplant recursion through functions needs
6292 * to reparent the funbox, since all descendant functions are correctly
6293 * linked under the top-most funbox. But every visit to this case needs
6294 * to update funbox->level.
6296 * Recall that funbox->level is the static level of the code containing
6297 * the definition or expression of the function and not the static level
6298 * of the function's body.
6300 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6302 funbox
->level
= tc
->staticLevel
+ funcLevel
;
6303 if (++funcLevel
== 1 && genexp
) {
6304 JSFunctionBox
*parent
= tc
->funbox
;
6306 JSFunctionBox
**funboxp
= &tc
->parent
->functionList
;
6307 while (*funboxp
!= funbox
)
6308 funboxp
= &(*funboxp
)->siblings
;
6309 *funboxp
= funbox
->siblings
;
6311 funbox
->parent
= parent
;
6312 funbox
->siblings
= parent
->kids
;
6313 parent
->kids
= funbox
;
6314 funbox
->level
= tc
->staticLevel
;
6320 transplant(pn
->maybeExpr());
6321 if (pn
->pn_arity
== PN_FUNC
)
6325 if (genexp
&& !BumpStaticLevel(pn
, tc
))
6327 } else if (pn
->pn_used
) {
6328 JS_ASSERT(pn
->pn_op
!= JSOP_NOP
);
6329 JS_ASSERT(pn
->pn_cookie
== FREE_UPVAR_COOKIE
);
6331 JSDefinition
*dn
= pn
->pn_lexdef
;
6332 JS_ASSERT(dn
->pn_defn
);
6335 * Adjust the definition's block id only if it is a placeholder not
6336 * to the left of the root node, and if pn is the last use visited
6337 * in the comprehension expression (to avoid adjusting the blockid
6340 * Non-placeholder definitions within the comprehension expression
6341 * will be visited further below.
6343 if (dn
->isPlaceholder() && dn
->pn_pos
>= root
->pn_pos
&& dn
->dn_uses
== pn
) {
6344 if (genexp
&& !BumpStaticLevel(dn
, tc
))
6346 AdjustBlockId(dn
, adjust
, tc
);
6349 JSAtom
*atom
= pn
->pn_atom
;
6351 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, atom
, NULL
);
6352 JS_ASSERT(!stmt
|| stmt
!= tc
->topStmt
);
6354 if (genexp
&& PN_OP(dn
) != JSOP_CALLEE
) {
6355 JS_ASSERT(!tc
->decls
.lookup(atom
));
6357 if (dn
->pn_pos
< root
->pn_pos
|| dn
->isPlaceholder()) {
6358 JSAtomListElement
*ale
= tc
->lexdeps
.add(tc
->compiler
, dn
->pn_atom
);
6362 if (dn
->pn_pos
>= root
->pn_pos
) {
6363 tc
->parent
->lexdeps
.remove(tc
->compiler
, atom
);
6365 JSDefinition
*dn2
= (JSDefinition
*)
6366 NewNameNode(tc
->compiler
->context
, TS(tc
->compiler
), dn
->pn_atom
, tc
);
6370 dn2
->pn_type
= dn
->pn_type
;
6371 dn2
->pn_pos
= root
->pn_pos
;
6372 dn2
->pn_defn
= true;
6373 dn2
->pn_dflags
|= PND_PLACEHOLDER
;
6375 JSParseNode
**pnup
= &dn
->dn_uses
;
6377 while ((pnu
= *pnup
) != NULL
&& pnu
->pn_pos
>= root
->pn_pos
) {
6378 pnu
->pn_lexdef
= dn2
;
6379 dn2
->pn_dflags
|= pnu
->pn_dflags
& PND_USE2DEF_FLAGS
;
6380 pnup
= &pnu
->pn_link
;
6382 dn2
->dn_uses
= dn
->dn_uses
;
6383 dn
->dn_uses
= *pnup
;
6389 ALE_SET_DEFN(ale
, dn
);
6394 if (pn
->pn_pos
>= root
->pn_pos
)
6395 AdjustBlockId(pn
, adjust
, tc
);
6399 transplant(pn
->pn_tree
);
6406 * Starting from a |for| keyword after the first array initialiser element or
6407 * an expression in an open parenthesis, parse the tail of the comprehension
6408 * or generator expression signified by this |for| keyword in context.
6410 * Return null on failure, else return the top-most parse node for the array
6411 * comprehension or generator expression, with a unary node as the body of the
6412 * (possibly nested) for-loop, initialized by |type, op, kid|.
6414 static JSParseNode
*
6415 ComprehensionTail(JSParseNode
*kid
, uintN blockid
, JSTreeContext
*tc
,
6416 JSTokenType type
= TOK_SEMI
, JSOp op
= JSOP_NOP
)
6418 JSContext
*cx
= tc
->compiler
->context
;
6419 JSTokenStream
*ts
= TS(tc
->compiler
);
6422 JSParseNode
*pn
, *pn2
, *pn3
, **pnp
;
6423 JSStmtInfo stmtInfo
;
6428 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_FOR
);
6430 if (type
== TOK_SEMI
) {
6432 * Generator expression desugars to an immediately applied lambda that
6433 * yields the next value from a for-in loop (possibly nested, and with
6434 * optional if guard). Make pn be the TOK_LC body node.
6436 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6439 adjust
= pn
->pn_blockid
- blockid
;
6441 JS_ASSERT(type
== TOK_ARRAYPUSH
);
6444 * Make a parse-node and literal object representing the block scope of
6445 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6446 * aka the array initialiser case, has passed the blockid to claim for
6447 * the comprehension's block scope. We allocate that id or one above it
6448 * here, by calling js_PushLexicalScope.
6450 * In the case of a comprehension expression that has nested blocks
6451 * (e.g., let expressions), we will allocate a higher blockid but then
6452 * slide all blocks "to the right" to make room for the comprehension's
6455 adjust
= tc
->blockid();
6456 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
6460 JS_ASSERT(blockid
<= pn
->pn_blockid
);
6461 JS_ASSERT(blockid
< tc
->blockidGen
);
6462 JS_ASSERT(tc
->bodyid
< blockid
);
6463 pn
->pn_blockid
= stmtInfo
.blockid
= blockid
;
6464 JS_ASSERT(adjust
< blockid
);
6465 adjust
= blockid
- adjust
;
6470 CompExprTransplanter
transplanter(kid
, tc
, type
== TOK_SEMI
, adjust
);
6471 transplanter
.transplant(kid
);
6475 data
.binder
= BindLet
;
6476 data
.let
.overflow
= JSMSG_ARRAY_INIT_TOO_BIG
;
6480 * FOR node is binary, left is loop control and right is body. Use
6481 * index to count each block-local let-variable on the left-hand side
6484 pn2
= NewParseNode(PN_BINARY
, tc
);
6488 pn2
->pn_op
= JSOP_ITER
;
6489 pn2
->pn_iflags
= JSITER_ENUMERATE
;
6490 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
6491 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
6492 pn2
->pn_iflags
|= JSITER_FOREACH
;
6496 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
6499 tt
= js_GetToken(cx
, ts
);
6501 #if JS_HAS_DESTRUCTURING
6504 ts
->flags
|= TSF_DESTRUCTURING
;
6505 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
6506 ts
->flags
&= ~TSF_DESTRUCTURING
;
6513 atom
= CURRENT_TOKEN(ts
).t_atom
;
6516 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6517 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6518 * in the operand stack frame. The code generator computes that,
6519 * and it tries to bind all names to slots, so we must let it do
6522 pn3
= NewBindingNode(ts
, atom
, tc
, true);
6528 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6529 JSMSG_NO_VARIABLE_NAME
);
6535 MUST_MATCH_TOKEN(TOK_IN
, JSMSG_IN_AFTER_FOR_NAME
);
6536 JSParseNode
*pn4
= Expr(cx
, ts
, tc
);
6539 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
6542 #if JS_HAS_DESTRUCTURING
6545 if (!CheckDestructuring(cx
, &data
, pn3
, NULL
, tc
))
6548 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
6549 /* Destructuring requires [key, value] enumeration in JS1.7. */
6550 if (pn3
->pn_type
!= TOK_RB
|| pn3
->pn_count
!= 2) {
6551 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6552 JSMSG_BAD_FOR_LEFTSIDE
);
6556 JS_ASSERT(pn2
->pn_op
== JSOP_ITER
);
6557 JS_ASSERT(pn2
->pn_iflags
& JSITER_ENUMERATE
);
6558 if (!(pn2
->pn_iflags
& JSITER_FOREACH
))
6559 pn2
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
6566 if (!data
.binder(cx
, &data
, atom
, tc
))
6573 pn2
->pn_left
= NewBinary(TOK_IN
, JSOP_NOP
, pn3
, pn4
, tc
);
6577 pnp
= &pn2
->pn_right
;
6578 } while (js_MatchToken(cx
, ts
, TOK_FOR
));
6580 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
6581 pn2
= NewParseNode(PN_TERNARY
, tc
);
6584 pn2
->pn_kid1
= Condition(cx
, ts
, tc
);
6588 pnp
= &pn2
->pn_kid2
;
6591 pn2
= NewParseNode(PN_UNARY
, tc
);
6594 pn2
->pn_type
= type
;
6599 if (type
== TOK_ARRAYPUSH
)
6604 #if JS_HAS_GENERATOR_EXPRS
6607 * Starting from a |for| keyword after an expression, parse the comprehension
6608 * tail completing this generator expression. Wrap the expression at kid in a
6609 * generator function that is immediately called to evaluate to the generator
6610 * iterator that is the value of this generator expression.
6612 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6613 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6614 * expression-statement node that constitutes the body of the |for| loop(s) in
6615 * the generator function.
6617 * Note how unlike Python, we do not evaluate the expression to the right of
6618 * the first |in| in the chain of |for| heads. Instead, a generator expression
6619 * is merely sugar for a generator function expression and its application.
6621 static JSParseNode
*
6622 GeneratorExpr(JSParseNode
*pn
, JSParseNode
*kid
, JSTreeContext
*tc
)
6624 /* Initialize pn, connecting it to kid. */
6625 JS_ASSERT(pn
->pn_arity
== PN_UNARY
);
6626 pn
->pn_type
= TOK_YIELD
;
6627 pn
->pn_op
= JSOP_YIELD
;
6628 pn
->pn_parens
= true;
6629 pn
->pn_pos
= kid
->pn_pos
;
6631 pn
->pn_hidden
= true;
6633 /* Make a new node for the desugared generator function. */
6634 JSParseNode
*genfn
= NewParseNode(PN_FUNC
, tc
);
6637 genfn
->pn_type
= TOK_FUNCTION
;
6638 genfn
->pn_op
= JSOP_LAMBDA
;
6639 JS_ASSERT(!genfn
->pn_body
);
6640 genfn
->pn_dflags
= PND_FUNARG
;
6643 JSTreeContext
gentc(tc
->compiler
);
6645 JSFunctionBox
*funbox
= EnterFunction(genfn
, tc
, &gentc
);
6650 * We assume conservatively that any deoptimization flag in tc->flags
6651 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6652 * propagate these flags into genfn. For code simplicity we also do
6653 * not detect if the flags were only set in the kid and could be
6654 * removed from tc->flags.
6656 gentc
.flags
|= TCF_FUN_IS_GENERATOR
| TCF_GENEXP_LAMBDA
|
6657 (tc
->flags
& (TCF_FUN_FLAGS
& ~TCF_FUN_PARAM_ARGUMENTS
));
6658 funbox
->tcflags
|= gentc
.flags
;
6659 genfn
->pn_funbox
= funbox
;
6660 genfn
->pn_blockid
= gentc
.bodyid
;
6662 JSParseNode
*body
= ComprehensionTail(pn
, tc
->blockid(), &gentc
);
6665 JS_ASSERT(!genfn
->pn_body
);
6666 genfn
->pn_body
= body
;
6667 genfn
->pn_pos
.begin
= body
->pn_pos
.begin
= kid
->pn_pos
.begin
;
6668 genfn
->pn_pos
.end
= body
->pn_pos
.end
= CURRENT_TOKEN(TS(tc
->compiler
)).pos
.end
;
6670 if (!LeaveFunction(genfn
, &gentc
, tc
))
6675 * Our result is a call expression that invokes the anonymous generator
6678 JSParseNode
*result
= NewParseNode(PN_LIST
, tc
);
6681 result
->pn_type
= TOK_LP
;
6682 result
->pn_op
= JSOP_CALL
;
6683 result
->pn_pos
.begin
= genfn
->pn_pos
.begin
;
6684 result
->initList(genfn
);
6688 static const char js_generator_str
[] = "generator";
6690 #endif /* JS_HAS_GENERATOR_EXPRS */
6691 #endif /* JS_HAS_GENERATORS */
6694 ArgumentList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6695 JSParseNode
*listNode
)
6699 ts
->flags
|= TSF_OPERAND
;
6700 matched
= js_MatchToken(cx
, ts
, TOK_RP
);
6701 ts
->flags
&= ~TSF_OPERAND
;
6704 JSParseNode
*argNode
= AssignExpr(cx
, ts
, tc
);
6707 #if JS_HAS_GENERATORS
6708 if (argNode
->pn_type
== TOK_YIELD
&&
6709 !argNode
->pn_parens
&&
6710 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6711 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6712 JSMSG_BAD_GENERATOR_SYNTAX
,
6717 #if JS_HAS_GENERATOR_EXPRS
6718 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
6719 JSParseNode
*pn
= NewParseNode(PN_UNARY
, tc
);
6722 argNode
= GeneratorExpr(pn
, argNode
, tc
);
6725 if (listNode
->pn_count
> 1 ||
6726 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
6727 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
6728 JSMSG_BAD_GENERATOR_SYNTAX
,
6734 listNode
->append(argNode
);
6735 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
6737 if (js_GetToken(cx
, ts
) != TOK_RP
) {
6738 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6739 JSMSG_PAREN_AFTER_ARGS
);
6746 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6747 static JSParseNode
*
6748 CheckForImmediatelyAppliedLambda(JSParseNode
*pn
)
6750 while (pn
->pn_type
== TOK_RP
)
6752 if (pn
->pn_type
== TOK_FUNCTION
) {
6753 JS_ASSERT(pn
->pn_arity
== PN_FUNC
);
6755 JSFunctionBox
*funbox
= pn
->pn_funbox
;
6756 JS_ASSERT(((JSFunction
*) funbox
->object
)->flags
& JSFUN_LAMBDA
);
6757 if (!(funbox
->tcflags
& TCF_FUN_USES_ARGUMENTS
))
6758 pn
->pn_dflags
&= ~PND_FUNARG
;
6763 static JSParseNode
*
6764 MemberExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
6765 JSBool allowCallSyntax
)
6767 JSParseNode
*pn
, *pn2
, *pn3
;
6770 JS_CHECK_RECURSION(cx
, return NULL
);
6772 /* Check for new expression first. */
6773 ts
->flags
|= TSF_OPERAND
;
6774 tt
= js_GetToken(cx
, ts
);
6775 ts
->flags
&= ~TSF_OPERAND
;
6776 if (tt
== TOK_NEW
) {
6777 pn
= NewParseNode(PN_LIST
, tc
);
6780 pn2
= MemberExpr(cx
, ts
, tc
, JS_FALSE
);
6783 pn2
= CheckForImmediatelyAppliedLambda(pn2
);
6784 pn
->pn_op
= JSOP_NEW
;
6786 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
6788 if (js_MatchToken(cx
, ts
, TOK_LP
) && !ArgumentList(cx
, ts
, tc
, pn
))
6790 if (pn
->pn_count
> ARGC_LIMIT
) {
6791 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6792 JSMSG_TOO_MANY_CON_ARGS
);
6795 pn
->pn_pos
.end
= pn
->last()->pn_pos
.end
;
6797 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
6801 if (pn
->pn_type
== TOK_ANYNAME
||
6802 pn
->pn_type
== TOK_AT
||
6803 pn
->pn_type
== TOK_DBLCOLON
) {
6804 pn2
= NewOrRecycledNode(tc
);
6807 pn2
->pn_type
= TOK_UNARYOP
;
6808 pn2
->pn_pos
= pn
->pn_pos
;
6809 pn2
->pn_op
= JSOP_XMLNAME
;
6810 pn2
->pn_arity
= PN_UNARY
;
6811 pn2
->pn_parens
= false;
6817 while ((tt
= js_GetToken(cx
, ts
)) > TOK_EOF
) {
6818 if (tt
== TOK_DOT
) {
6819 pn2
= NewNameNode(cx
, ts
, NULL
, tc
);
6822 #if JS_HAS_XML_SUPPORT
6823 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
6824 tt
= js_GetToken(cx
, ts
);
6825 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
6826 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
6830 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
6831 if (tt
== TOK_NAME
&& pn3
->pn_type
== TOK_NAME
) {
6832 pn2
->pn_op
= JSOP_GETPROP
;
6834 pn2
->pn_atom
= pn3
->pn_atom
;
6835 RecycleTree(pn3
, tc
);
6838 pn2
->pn_type
= TOK_FILTER
;
6839 pn2
->pn_op
= JSOP_FILTER
;
6841 /* A filtering predicate is like a with statement. */
6842 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6843 } else if (TOKEN_TYPE_IS_XML(PN_TYPE(pn3
))) {
6844 pn2
->pn_type
= TOK_LB
;
6845 pn2
->pn_op
= JSOP_GETELEM
;
6847 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6848 JSMSG_NAME_AFTER_DOT
);
6851 pn2
->pn_arity
= PN_BINARY
;
6853 pn2
->pn_right
= pn3
;
6856 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
6857 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NAME_AFTER_DOT
);
6858 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
6859 pn2
->pn_op
= JSOP_GETPROP
;
6861 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
6863 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6864 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6865 #if JS_HAS_XML_SUPPORT
6866 } else if (tt
== TOK_DBLDOT
) {
6867 pn2
= NewParseNode(PN_BINARY
, tc
);
6870 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
6871 tt
= js_GetToken(cx
, ts
);
6872 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
6873 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
6877 if (tt
== TOK_NAME
) {
6878 pn3
->pn_type
= TOK_STRING
;
6879 pn3
->pn_arity
= PN_NULLARY
;
6880 pn3
->pn_op
= JSOP_QNAMEPART
;
6881 } else if (!TOKEN_TYPE_IS_XML(tt
)) {
6882 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
6883 JSMSG_NAME_AFTER_DOT
);
6886 pn2
->pn_op
= JSOP_DESCENDANTS
;
6888 pn2
->pn_right
= pn3
;
6889 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6890 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6892 } else if (tt
== TOK_LB
) {
6893 pn2
= NewParseNode(PN_BINARY
, tc
);
6896 pn3
= Expr(cx
, ts
, tc
);
6900 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
6901 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6902 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6905 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6906 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6907 * the interpreter from fast property access. However, if the
6908 * bracketed string is a uint32, we rewrite pn3 to be a number
6909 * instead of a string.
6912 if (pn3
->pn_type
== TOK_STRING
) {
6915 if (!js_IdIsIndex(ATOM_TO_JSID(pn3
->pn_atom
), &index
)) {
6916 pn2
->pn_type
= TOK_DOT
;
6917 pn2
->pn_op
= JSOP_GETPROP
;
6918 pn2
->pn_arity
= PN_NAME
;
6920 pn2
->pn_atom
= pn3
->pn_atom
;
6923 pn3
->pn_type
= TOK_NUMBER
;
6924 pn3
->pn_op
= JSOP_DOUBLE
;
6925 pn3
->pn_dval
= index
;
6927 pn2
->pn_op
= JSOP_GETELEM
;
6929 pn2
->pn_right
= pn3
;
6931 } else if (allowCallSyntax
&& tt
== TOK_LP
) {
6932 pn2
= NewParseNode(PN_LIST
, tc
);
6935 pn2
->pn_op
= JSOP_CALL
;
6937 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
6938 pn
= CheckForImmediatelyAppliedLambda(pn
);
6939 if (pn
->pn_op
== JSOP_NAME
) {
6940 if (pn
->pn_atom
== cx
->runtime
->atomState
.evalAtom
) {
6941 /* Select JSOP_EVAL and flag tc as heavyweight. */
6942 pn2
->pn_op
= JSOP_EVAL
;
6943 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
6945 } else if (pn
->pn_op
== JSOP_GETPROP
) {
6946 if (pn
->pn_atom
== cx
->runtime
->atomState
.applyAtom
||
6947 pn
->pn_atom
== cx
->runtime
->atomState
.callAtom
) {
6948 /* Select JSOP_APPLY given foo.apply(...). */
6949 pn2
->pn_op
= JSOP_APPLY
;
6954 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
6956 if (!ArgumentList(cx
, ts
, tc
, pn2
))
6958 if (pn2
->pn_count
> ARGC_LIMIT
) {
6959 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6960 JSMSG_TOO_MANY_FUN_ARGS
);
6963 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
6971 if (tt
== TOK_ERROR
)
6976 static JSParseNode
*
6977 BracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
6983 * Always accept the 'in' operator in a parenthesized expression,
6984 * where it's unambiguous, even if we might be parsing the init of a
6987 oldflags
= tc
->flags
;
6988 tc
->flags
&= ~TCF_IN_FOR_INIT
;
6989 pn
= Expr(cx
, ts
, tc
);
6990 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
6994 #if JS_HAS_XML_SUPPORT
6996 static JSParseNode
*
6997 EndBracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7001 pn
= BracketedExpr(cx
, ts
, tc
);
7005 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ATTR_EXPR
);
7010 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7012 * AttributeIdentifier:
7013 * @ PropertySelector
7014 * @ QualifiedIdentifier
7021 * QualifiedIdentifier:
7022 * PropertySelector :: PropertySelector
7023 * PropertySelector :: [ Expression ]
7025 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7027 * AttributeIdentifier:
7028 * @ QualifiedIdentifier
7035 * QualifiedIdentifier:
7036 * PropertySelector :: PropertySelector
7037 * PropertySelector :: [ Expression ]
7040 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7041 * for that rule to result in a name node, but ECMA-357 extends the grammar
7042 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7044 * QualifiedIdentifier:
7045 * PropertySelector QualifiedSuffix
7048 * :: PropertySelector
7052 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7054 * PrimaryExpression:
7055 * Identifier QualifiedSuffix
7057 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7058 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7060 static JSParseNode
*
7061 PropertySelector(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7065 pn
= NewParseNode(PN_NULLARY
, tc
);
7068 if (pn
->pn_type
== TOK_STAR
) {
7069 pn
->pn_type
= TOK_ANYNAME
;
7070 pn
->pn_op
= JSOP_ANYNAME
;
7071 pn
->pn_atom
= cx
->runtime
->atomState
.starAtom
;
7073 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
7074 pn
->pn_op
= JSOP_QNAMEPART
;
7075 pn
->pn_arity
= PN_NAME
;
7076 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7077 pn
->pn_cookie
= FREE_UPVAR_COOKIE
;
7082 static JSParseNode
*
7083 QualifiedSuffix(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
7086 JSParseNode
*pn2
, *pn3
;
7089 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_DBLCOLON
);
7090 pn2
= NewNameNode(cx
, ts
, NULL
, tc
);
7094 /* Left operand of :: must be evaluated if it is an identifier. */
7095 if (pn
->pn_op
== JSOP_QNAMEPART
)
7096 pn
->pn_op
= JSOP_NAME
;
7098 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7099 tt
= js_GetToken(cx
, ts
);
7100 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7101 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7102 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
7103 pn2
->pn_op
= JSOP_QNAMECONST
;
7104 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7105 pn2
->pn_atom
= (tt
== TOK_STAR
)
7106 ? cx
->runtime
->atomState
.starAtom
7107 : CURRENT_TOKEN(ts
).t_atom
;
7109 pn2
->pn_cookie
= FREE_UPVAR_COOKIE
;
7114 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7115 JSMSG_SYNTAX_ERROR
);
7118 pn3
= EndBracketedExpr(cx
, ts
, tc
);
7122 pn2
->pn_op
= JSOP_QNAME
;
7123 pn2
->pn_arity
= PN_BINARY
;
7124 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7125 pn2
->pn_pos
.end
= pn3
->pn_pos
.end
;
7127 pn2
->pn_right
= pn3
;
7131 static JSParseNode
*
7132 QualifiedIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7136 pn
= PropertySelector(cx
, ts
, tc
);
7139 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
7140 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7141 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
7142 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
7147 static JSParseNode
*
7148 AttributeIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7150 JSParseNode
*pn
, *pn2
;
7153 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_AT
);
7154 pn
= NewParseNode(PN_UNARY
, tc
);
7157 pn
->pn_op
= JSOP_TOATTRNAME
;
7158 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7159 tt
= js_GetToken(cx
, ts
);
7160 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7161 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
7162 pn2
= QualifiedIdentifier(cx
, ts
, tc
);
7163 } else if (tt
== TOK_LB
) {
7164 pn2
= EndBracketedExpr(cx
, ts
, tc
);
7166 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7167 JSMSG_SYNTAX_ERROR
);
7177 * Make a TOK_LC unary node whose pn_kid is an expression.
7179 static JSParseNode
*
7180 XMLExpr(JSContext
*cx
, JSTokenStream
*ts
, JSBool inTag
, JSTreeContext
*tc
)
7182 JSParseNode
*pn
, *pn2
;
7185 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LC
);
7186 pn
= NewParseNode(PN_UNARY
, tc
);
7191 * Turn off XML tag mode, but don't restore it after parsing this braced
7192 * expression. Instead, simply restore ts's old flags. This is required
7193 * because XMLExpr is called both from within a tag, and from within text
7194 * contained in an element, but outside of any start, end, or point tag.
7196 oldflags
= ts
->flags
;
7197 ts
->flags
= oldflags
& ~TSF_XMLTAGMODE
;
7198 pn2
= Expr(cx
, ts
, tc
);
7202 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_XML_EXPR
);
7203 ts
->flags
= oldflags
;
7205 pn
->pn_op
= inTag
? JSOP_XMLTAGEXPR
: JSOP_XMLELTEXPR
;
7210 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7211 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7212 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7213 * child of a container tag.
7215 static JSParseNode
*
7216 XMLAtomNode(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7221 pn
= NewParseNode(PN_NULLARY
, tc
);
7224 tp
= &CURRENT_TOKEN(ts
);
7225 pn
->pn_op
= tp
->t_op
;
7226 pn
->pn_atom
= tp
->t_atom
;
7227 if (tp
->type
== TOK_XMLPI
)
7228 pn
->pn_atom2
= tp
->t_atom2
;
7233 * Parse the productions:
7236 * XMLName XMLNameExpr?
7237 * { Expr } XMLNameExpr?
7239 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7240 * a list of names and/or expressions, a single expression, or a single name.
7241 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7244 static JSParseNode
*
7245 XMLNameExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
7247 JSParseNode
*pn
, *pn2
, *list
;
7252 tt
= CURRENT_TOKEN(ts
).type
;
7254 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7258 JS_ASSERT(tt
== TOK_XMLNAME
);
7259 pn2
= XMLAtomNode(cx
, ts
, tc
);
7268 list
= NewParseNode(PN_LIST
, tc
);
7271 list
->pn_type
= TOK_XMLNAME
;
7272 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7274 list
->pn_xflags
= PNX_CANTFOLD
;
7277 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7280 } while ((tt
= js_GetToken(cx
, ts
)) == TOK_XMLNAME
|| tt
== TOK_LC
);
7287 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7288 * at compile time into a JSXML tree.
7290 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7291 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7292 : (pn)->pn_type != TOK_LC)
7295 * Parse the productions:
7299 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7300 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7302 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7303 * produces a list of name and attribute values and/or braced expressions, a
7304 * single expression, or a single name.
7306 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7307 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7308 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7309 * we parsed exactly one expression.
7311 static JSParseNode
*
7312 XMLTagContent(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7313 JSTokenType tagtype
, JSAtom
**namep
)
7315 JSParseNode
*pn
, *pn2
, *list
;
7318 pn
= XMLNameExpr(cx
, ts
, tc
);
7321 *namep
= (pn
->pn_arity
== PN_NULLARY
) ? pn
->pn_atom
: NULL
;
7324 while (js_MatchToken(cx
, ts
, TOK_XMLSPACE
)) {
7325 tt
= js_GetToken(cx
, ts
);
7326 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7331 pn2
= XMLNameExpr(cx
, ts
, tc
);
7335 list
= NewParseNode(PN_LIST
, tc
);
7338 list
->pn_type
= tagtype
;
7339 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
7344 if (!XML_FOLDABLE(pn2
))
7345 pn
->pn_xflags
|= PNX_CANTFOLD
;
7347 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7348 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_NO_ASSIGN_IN_XML_ATTR
);
7349 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7351 tt
= js_GetToken(cx
, ts
);
7352 if (tt
== TOK_XMLATTR
) {
7353 pn2
= XMLAtomNode(cx
, ts
, tc
);
7354 } else if (tt
== TOK_LC
) {
7355 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
7356 pn
->pn_xflags
|= PNX_CANTFOLD
;
7358 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7359 JSMSG_BAD_XML_ATTR_VALUE
);
7364 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7371 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7373 if ((tt) <= TOK_EOF) { \
7374 if ((tt) == TOK_EOF) { \
7375 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7376 JSMSG_END_OF_XML_SOURCE); \
7382 static JSParseNode
*
7383 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7387 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7388 * that opens the end tag for the container.
7391 XMLElementContent(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
7398 ts
->flags
&= ~TSF_XMLTAGMODE
;
7400 ts
->flags
|= TSF_XMLTEXTMODE
;
7401 tt
= js_GetToken(cx
, ts
);
7402 ts
->flags
&= ~TSF_XMLTEXTMODE
;
7403 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7405 JS_ASSERT(tt
== TOK_XMLSPACE
|| tt
== TOK_XMLTEXT
);
7406 textAtom
= CURRENT_TOKEN(ts
).t_atom
;
7408 /* Non-zero-length XML text scanned. */
7409 pn2
= XMLAtomNode(cx
, ts
, tc
);
7412 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7416 ts
->flags
|= TSF_OPERAND
;
7417 tt
= js_GetToken(cx
, ts
);
7418 ts
->flags
&= ~TSF_OPERAND
;
7419 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
7420 if (tt
== TOK_XMLETAGO
)
7424 pn2
= XMLExpr(cx
, ts
, JS_FALSE
, tc
);
7425 pn
->pn_xflags
|= PNX_CANTFOLD
;
7426 } else if (tt
== TOK_XMLSTAGO
) {
7427 pn2
= XMLElementOrList(cx
, ts
, tc
, JS_FALSE
);
7429 pn2
->pn_xflags
&= ~PNX_XMLROOT
;
7430 pn
->pn_xflags
|= pn2
->pn_xflags
;
7433 JS_ASSERT(tt
== TOK_XMLCDATA
|| tt
== TOK_XMLCOMMENT
||
7435 pn2
= XMLAtomNode(cx
, ts
, tc
);
7439 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
7443 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLETAGO
);
7444 ts
->flags
|= TSF_XMLTAGMODE
;
7449 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7451 static JSParseNode
*
7452 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7455 JSParseNode
*pn
, *pn2
, *list
;
7457 JSAtom
*startAtom
, *endAtom
;
7459 JS_CHECK_RECURSION(cx
, return NULL
);
7461 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLSTAGO
);
7462 pn
= NewParseNode(PN_LIST
, tc
);
7466 ts
->flags
|= TSF_XMLTAGMODE
;
7467 tt
= js_GetToken(cx
, ts
);
7468 if (tt
== TOK_ERROR
)
7471 if (tt
== TOK_XMLNAME
|| tt
== TOK_LC
) {
7473 * XMLElement. Append the tag and its contents, if any, to pn.
7475 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLSTAGO
, &startAtom
);
7478 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7480 tt
= js_GetToken(cx
, ts
);
7481 if (tt
== TOK_XMLPTAGC
) {
7482 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7483 if (pn2
->pn_type
== TOK_XMLSTAGO
) {
7485 RecycleTree(pn
, tc
);
7488 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
||
7489 pn2
->pn_type
== TOK_LC
);
7491 if (!XML_FOLDABLE(pn2
))
7492 pn
->pn_xflags
|= PNX_CANTFOLD
;
7494 pn
->pn_type
= TOK_XMLPTAGC
;
7495 pn
->pn_xflags
|= PNX_XMLROOT
;
7497 /* We had better have a tag-close (>) at this point. */
7498 if (tt
!= TOK_XMLTAGC
) {
7499 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7500 JSMSG_BAD_XML_TAG_SYNTAX
);
7503 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7505 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7506 if (pn2
->pn_type
!= TOK_XMLSTAGO
) {
7508 if (!XML_FOLDABLE(pn2
))
7509 pn
->pn_xflags
|= PNX_CANTFOLD
;
7511 pn
= NewParseNode(PN_LIST
, tc
);
7516 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7517 pn
->pn_type
= TOK_XMLELEM
;
7518 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
7520 if (!XML_FOLDABLE(pn2
))
7521 pn
->pn_xflags
|= PNX_CANTFOLD
;
7522 pn
->pn_xflags
|= PNX_XMLROOT
;
7524 /* Get element contents and delimiting end-tag-open sequence. */
7525 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7528 tt
= js_GetToken(cx
, ts
);
7529 XML_CHECK_FOR_ERROR_AND_EOF(tt
, NULL
);
7530 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
7531 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7532 JSMSG_BAD_XML_TAG_SYNTAX
);
7536 /* Parse end tag; check mismatch at compile-time if we can. */
7537 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLETAGO
, &endAtom
);
7540 if (pn2
->pn_type
== TOK_XMLETAGO
) {
7541 /* Oops, end tag has attributes! */
7542 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7543 JSMSG_BAD_XML_TAG_SYNTAX
);
7546 if (endAtom
&& startAtom
&& endAtom
!= startAtom
) {
7547 JSString
*str
= ATOM_TO_STRING(startAtom
);
7549 /* End vs. start tag name mismatch: point to the tag name. */
7550 js_ReportCompileErrorNumber(cx
, ts
, pn2
,
7551 JSREPORT_UC
| JSREPORT_ERROR
,
7552 JSMSG_XML_TAG_NAME_MISMATCH
,
7557 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7558 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
|| pn2
->pn_type
== TOK_LC
);
7559 list
= NewParseNode(PN_LIST
, tc
);
7562 list
->pn_type
= TOK_XMLETAGO
;
7563 list
->initList(pn2
);
7565 if (!XML_FOLDABLE(pn2
)) {
7566 list
->pn_xflags
|= PNX_CANTFOLD
;
7567 pn
->pn_xflags
|= PNX_CANTFOLD
;
7570 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
7571 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_TAG_SYNTAX
);
7574 /* Set pn_op now that pn has been updated to its final value. */
7575 pn
->pn_op
= JSOP_TOXML
;
7576 } else if (allowList
&& tt
== TOK_XMLTAGC
) {
7577 /* XMLList Initialiser. */
7578 pn
->pn_type
= TOK_XMLLIST
;
7579 pn
->pn_op
= JSOP_TOXMLLIST
;
7581 pn
->pn_xflags
|= PNX_XMLROOT
;
7582 if (!XMLElementContent(cx
, ts
, pn
, tc
))
7585 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_LIST_SYNTAX
);
7587 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7588 JSMSG_BAD_XML_NAME_SYNTAX
);
7592 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7593 ts
->flags
&= ~TSF_XMLTAGMODE
;
7597 static JSParseNode
*
7598 XMLElementOrListRoot(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7605 * Force XML support to be enabled so that comments and CDATA literals
7606 * are recognized, instead of <! followed by -- starting an HTML comment
7607 * to end of line (used in script tags to hide content from old browsers
7608 * that don't recognize <script>).
7610 oldopts
= JS_SetOptions(cx
, cx
->options
| JSOPTION_XML
);
7611 pn
= XMLElementOrList(cx
, ts
, tc
, allowList
);
7612 JS_SetOptions(cx
, oldopts
);
7617 JSCompiler::parseXMLText(JSObject
*chain
, bool allowList
)
7620 * Push a compiler frame if we have no frames, or if the top frame is a
7621 * lightweight function activation, or if its scope chain doesn't match
7622 * the one passed to us.
7624 JSTreeContext
tc(this);
7625 tc
.scopeChain
= chain
;
7627 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7628 TS(this)->flags
|= TSF_OPERAND
| TSF_XMLONLYMODE
;
7629 JSTokenType tt
= js_GetToken(context
, TS(this));
7630 TS(this)->flags
&= ~TSF_OPERAND
;
7633 if (tt
!= TOK_XMLSTAGO
) {
7634 js_ReportCompileErrorNumber(context
, TS(this), NULL
, JSREPORT_ERROR
,
7635 JSMSG_BAD_XML_MARKUP
);
7638 pn
= XMLElementOrListRoot(context
, TS(this), &tc
, allowList
);
7641 TS(this)->flags
&= ~TSF_XMLONLYMODE
;
7645 #endif /* JS_HAS_XMLSUPPORT */
7647 #if JS_HAS_BLOCK_SCOPE
7649 * Check whether blockid is an active scoping statement in tc. This code is
7650 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7651 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7652 * and let blocks and expressions (not let declarations).
7654 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7655 * due to hoisting, let in a for loop head, let block, or let expression acts
7656 * like Scheme's let: initializers are evaluated without the new let bindings
7659 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7660 * bindings push on the front of the tc->decls JSAtomList (either the singular
7661 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7662 * scope bindings of the same name.
7664 * This simplifies binding lookup code at the price of a linear search here,
7665 * but only if code uses let (var predominates), and even then this function's
7666 * loop iterates more than once only in crazy cases.
7669 BlockIdInScope(uintN blockid
, JSTreeContext
*tc
)
7671 if (blockid
> tc
->blockid())
7673 for (JSStmtInfo
*stmt
= tc
->topScopeStmt
; stmt
; stmt
= stmt
->downScope
) {
7674 if (stmt
->blockid
== blockid
)
7681 static JSParseNode
*
7682 PrimaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
7683 JSTokenType tt
, JSBool afterDot
)
7685 JSParseNode
*pn
, *pn2
, *pn3
;
7688 JS_CHECK_RECURSION(cx
, return NULL
);
7690 #if JS_HAS_GETTER_SETTER
7691 if (tt
== TOK_NAME
) {
7692 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
7693 if (tt
== TOK_ERROR
)
7700 #if JS_HAS_XML_SUPPORT
7701 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7702 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
7703 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7704 pn2
= NewParseNode(PN_NULLARY
, tc
);
7707 pn2
->pn_type
= TOK_FUNCTION
;
7708 pn
= QualifiedSuffix(cx
, ts
, pn2
, tc
);
7713 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7715 pn
= FunctionExpr(cx
, ts
, tc
);
7725 pn
= NewParseNode(PN_LIST
, tc
);
7728 pn
->pn_type
= TOK_RB
;
7729 pn
->pn_op
= JSOP_NEWINIT
;
7732 #if JS_HAS_GENERATORS
7733 pn
->pn_blockid
= tc
->blockidGen
;
7736 ts
->flags
|= TSF_OPERAND
;
7737 matched
= js_MatchToken(cx
, ts
, TOK_RB
);
7738 ts
->flags
&= ~TSF_OPERAND
;
7740 for (index
= 0; ; index
++) {
7741 if (index
== ARRAY_INIT_LIMIT
) {
7742 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7743 JSMSG_ARRAY_INIT_TOO_BIG
);
7747 ts
->flags
|= TSF_OPERAND
;
7748 tt
= js_PeekToken(cx
, ts
);
7749 ts
->flags
&= ~TSF_OPERAND
;
7751 pn
->pn_xflags
|= PNX_ENDCOMMA
;
7755 if (tt
== TOK_COMMA
) {
7756 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7757 js_MatchToken(cx
, ts
, TOK_COMMA
);
7758 pn2
= NewParseNode(PN_NULLARY
, tc
);
7759 pn
->pn_xflags
|= PNX_HOLEY
;
7761 pn2
= AssignExpr(cx
, ts
, tc
);
7767 if (tt
!= TOK_COMMA
) {
7768 /* If we didn't already match TOK_COMMA in above case. */
7769 if (!js_MatchToken(cx
, ts
, TOK_COMMA
))
7774 #if JS_HAS_GENERATORS
7776 * At this point, (index == 0 && pn->pn_count != 0) implies one
7777 * element initialiser was parsed.
7779 * An array comprehension of the form:
7781 * [i * j for (i in o) for (j in p) if (i != j)]
7783 * translates to roughly the following let expression:
7785 * let (array = new Array, i, j) {
7786 * for (i in o) let {
7794 * where array is a nameless block-local variable. The "roughly"
7795 * means that an implementation may optimize away the array.push.
7796 * An array comprehension opens exactly one block scope, no matter
7797 * how many for heads it contains.
7799 * Each let () {...} or for (let ...) ... compiles to:
7801 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7803 * where <o> is a literal object representing the block scope,
7804 * with <n> properties, naming each var declared in the block.
7806 * Each var declaration in a let-block binds a name in <o> at
7807 * compile time, and allocates a slot on the operand stack at
7808 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7809 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7810 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7811 * local slot's stack index from fp->spbase.
7813 * The array comprehension iteration step, array.push(i * j) in
7814 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7815 * where <array> is the index of array's stack slot.
7818 pn
->pn_count
!= 0 &&
7819 js_MatchToken(cx
, ts
, TOK_FOR
)) {
7820 JSParseNode
*pnexp
, *pntop
;
7822 /* Relabel pn as an array comprehension node. */
7823 pn
->pn_type
= TOK_ARRAYCOMP
;
7826 * Remove the comprehension expression from pn's linked list
7827 * and save it via pnexp. We'll re-install it underneath the
7828 * ARRAYPUSH node after we parse the rest of the comprehension.
7831 JS_ASSERT(pn
->pn_count
== 1 || pn
->pn_count
== 2);
7832 pn
->pn_tail
= (--pn
->pn_count
== 1)
7833 ? &pn
->pn_head
->pn_next
7835 *pn
->pn_tail
= NULL
;
7837 pntop
= ComprehensionTail(pnexp
, pn
->pn_blockid
, tc
,
7838 TOK_ARRAYPUSH
, JSOP_ARRAYPUSH
);
7843 #endif /* JS_HAS_GENERATORS */
7845 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
7847 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7856 pn
= NewParseNode(PN_LIST
, tc
);
7859 pn
->pn_type
= TOK_RC
;
7860 pn
->pn_op
= JSOP_NEWINIT
;
7863 afterComma
= JS_FALSE
;
7865 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7866 tt
= js_GetToken(cx
, ts
);
7867 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7870 pn3
= NewParseNode(PN_NULLARY
, tc
);
7872 pn3
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
7875 #if JS_HAS_GETTER_SETTER
7879 atom
= CURRENT_TOKEN(ts
).t_atom
;
7880 if (atom
== cx
->runtime
->atomState
.getAtom
)
7882 else if (atom
== cx
->runtime
->atomState
.setAtom
)
7887 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
7888 tt
= js_GetToken(cx
, ts
);
7889 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
7890 if (tt
!= TOK_NAME
) {
7894 pn3
= NewNameNode(cx
, ts
, CURRENT_TOKEN(ts
).t_atom
, tc
);
7898 /* We have to fake a 'function' token here. */
7899 CURRENT_TOKEN(ts
).t_op
= JSOP_NOP
;
7900 CURRENT_TOKEN(ts
).type
= TOK_FUNCTION
;
7901 pn2
= FunctionExpr(cx
, ts
, tc
);
7902 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pn2
, tc
);
7908 pn3
= NewParseNode(PN_NULLARY
, tc
);
7910 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
7914 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
7917 JSMSG_TRAILING_COMMA
)) {
7922 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7927 tt
= js_GetToken(cx
, ts
);
7928 #if JS_HAS_GETTER_SETTER
7929 if (tt
== TOK_NAME
) {
7930 tt
= CheckGetterOrSetter(cx
, ts
, TOK_COLON
);
7931 if (tt
== TOK_ERROR
)
7936 if (tt
!= TOK_COLON
) {
7937 #if JS_HAS_DESTRUCTURING_SHORTHAND
7938 if (tt
!= TOK_COMMA
&& tt
!= TOK_RC
) {
7940 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7941 JSMSG_COLON_AFTER_ID
);
7943 #if JS_HAS_DESTRUCTURING_SHORTHAND
7947 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7948 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7951 pn
->pn_xflags
|= PNX_DESTRUCT
;
7953 if (pnval
->pn_type
== TOK_NAME
) {
7954 pnval
->pn_arity
= PN_NAME
;
7955 InitNameNodeCommon(pnval
, tc
);
7960 op
= CURRENT_TOKEN(ts
).t_op
;
7961 pnval
= AssignExpr(cx
, ts
, tc
);
7964 pn2
= NewBinary(TOK_COLON
, op
, pn3
, pnval
, tc
);
7965 #if JS_HAS_GETTER_SETTER
7972 tt
= js_GetToken(cx
, ts
);
7975 if (tt
!= TOK_COMMA
) {
7976 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
7977 JSMSG_CURLY_AFTER_LIST
);
7980 afterComma
= JS_TRUE
;
7984 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
7988 #if JS_HAS_BLOCK_SCOPE
7990 pn
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
7996 #if JS_HAS_SHARP_VARS
7998 pn
= NewParseNode(PN_UNARY
, tc
);
8001 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
8002 ts
->flags
|= TSF_OPERAND
;
8003 tt
= js_GetToken(cx
, ts
);
8004 ts
->flags
&= ~TSF_OPERAND
;
8005 if (tt
== TOK_USESHARP
|| tt
== TOK_DEFSHARP
||
8006 #if JS_HAS_XML_SUPPORT
8007 tt
== TOK_STAR
|| tt
== TOK_AT
||
8008 tt
== TOK_XMLSTAGO
/* XXXbe could be sharp? */ ||
8010 tt
== TOK_STRING
|| tt
== TOK_NUMBER
|| tt
== TOK_PRIMARY
) {
8011 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8012 JSMSG_BAD_SHARP_VAR_DEF
);
8015 pn
->pn_kid
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
8018 tc
->flags
|= TCF_HAS_SHARPS
;
8022 /* Check for forward/dangling references at runtime, to allow eval. */
8023 pn
= NewParseNode(PN_NULLARY
, tc
);
8026 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
8027 tc
->flags
|= TCF_HAS_SHARPS
;
8029 #endif /* JS_HAS_SHARP_VARS */
8035 pn
= ParenExpr(cx
, ts
, tc
, NULL
, &genexp
);
8038 pn
->pn_parens
= true;
8040 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
8044 #if JS_HAS_XML_SUPPORT
8046 pn
= QualifiedIdentifier(cx
, ts
, tc
);
8052 pn
= AttributeIdentifier(cx
, ts
, tc
);
8058 pn
= XMLElementOrListRoot(cx
, ts
, tc
, JS_TRUE
);
8062 #endif /* JS_HAS_XML_SUPPORT */
8065 #if JS_HAS_SHARP_VARS
8069 #if JS_HAS_XML_SUPPORT
8071 case TOK_XMLCOMMENT
:
8074 pn
= NewParseNode(PN_NULLARY
, tc
);
8077 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
8078 #if JS_HAS_XML_SUPPORT
8079 if (tt
== TOK_XMLPI
)
8080 pn
->pn_atom2
= CURRENT_TOKEN(ts
).t_atom2
;
8083 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
8087 pn
= NewNameNode(cx
, ts
, CURRENT_TOKEN(ts
).t_atom
, tc
);
8090 JS_ASSERT(CURRENT_TOKEN(ts
).t_op
== JSOP_NAME
);
8091 pn
->pn_op
= JSOP_NAME
;
8093 if ((tc
->flags
& (TCF_IN_FUNCTION
| TCF_FUN_PARAM_ARGUMENTS
)) == TCF_IN_FUNCTION
&&
8094 pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
) {
8096 * Flag arguments usage so we can avoid unsafe optimizations such
8097 * as formal parameter assignment analysis (because of the hated
8098 * feature whereby arguments alias formals). We do this even for
8099 * a reference of the form foo.arguments, which ancient code may
8100 * still use instead of arguments (more hate).
8102 NoteArgumentsUse(tc
);
8105 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8106 * to do this work (new rule for the emitter to count on).
8108 if (!afterDot
&& !(ts
->flags
& TSF_DESTRUCTURING
) && !tc
->inStatement(STMT_WITH
)) {
8109 pn
->pn_op
= JSOP_ARGUMENTS
;
8110 pn
->pn_dflags
|= PND_BOUND
;
8112 } else if ((!afterDot
8113 #if JS_HAS_XML_SUPPORT
8114 || js_PeekToken(cx
, ts
) == TOK_DBLCOLON
8116 ) && !(ts
->flags
& TSF_DESTRUCTURING
)) {
8117 JSStmtInfo
*stmt
= js_LexicalLookup(tc
, pn
->pn_atom
, NULL
);
8118 if (!stmt
|| stmt
->type
!= STMT_WITH
) {
8121 JSAtomListElement
*ale
= tc
->decls
.lookup(pn
->pn_atom
);
8124 #if JS_HAS_BLOCK_SCOPE
8126 * Skip out-of-scope let bindings along an ALE list or hash
8127 * chain. These can happen due to |let (x = x) x| block and
8128 * expression bindings, where the x on the right of = comes
8129 * from an outer scope. See bug 496532.
8131 while (dn
->isLet() && !BlockIdInScope(dn
->pn_blockid
, tc
)) {
8133 ale
= ALE_NEXT(ale
);
8134 } while (ale
&& ALE_ATOM(ale
) != pn
->pn_atom
);
8145 ale
= tc
->lexdeps
.lookup(pn
->pn_atom
);
8150 * No definition before this use in any lexical scope.
8151 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8152 * new node for the forward-referenced definition. This
8153 * placeholder definition node will be adopted when we
8154 * parse the real defining declaration form, or left as
8155 * a free variable definition if we never see the real
8158 ale
= MakePlaceholder(pn
, tc
);
8164 * In case this is a forward reference to a function,
8165 * we pessimistically set PND_FUNARG if the next token
8166 * is not a left parenthesis.
8168 * If the definition eventually parsed into dn is not a
8169 * function, this flag won't hurt, and if we do parse a
8170 * function with pn's name, then the PND_FUNARG flag is
8171 * necessary for safe cx->display-based optimization of
8172 * the closure's static link.
8174 JS_ASSERT(PN_TYPE(dn
) == TOK_NAME
);
8175 JS_ASSERT(dn
->pn_op
== JSOP_NOP
);
8176 if (js_PeekToken(cx
, ts
) != TOK_LP
)
8177 dn
->pn_dflags
|= PND_FUNARG
;
8181 JS_ASSERT(dn
->pn_defn
);
8182 LinkUseToDef(pn
, dn
, tc
);
8184 /* Here we handle the backward function reference case. */
8185 if (js_PeekToken(cx
, ts
) != TOK_LP
)
8186 dn
->pn_dflags
|= PND_FUNARG
;
8188 pn
->pn_dflags
|= (dn
->pn_dflags
& PND_FUNARG
);
8192 #if JS_HAS_XML_SUPPORT
8193 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
8198 * Here PrimaryExpr is called after . or .. followed by a name
8199 * followed by ::. This is the only case where a keyword after
8200 * . or .. is not treated as a property name.
8202 str
= ATOM_TO_STRING(pn
->pn_atom
);
8203 tt
= js_CheckKeyword(str
->chars(), str
->length());
8204 if (tt
== TOK_FUNCTION
) {
8205 pn
->pn_arity
= PN_NULLARY
;
8206 pn
->pn_type
= TOK_FUNCTION
;
8207 } else if (tt
!= TOK_EOF
) {
8208 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8209 JSMSG_KEYWORD_NOT_NS
);
8213 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
8224 pn
= NewParseNode(PN_NULLARY
, tc
);
8228 /* Token stream ensures that tokenbuf is NUL-terminated. */
8229 JS_ASSERT(*ts
->tokenbuf
.ptr
== (jschar
) 0);
8230 obj
= js_NewRegExpObject(cx
, ts
,
8232 ts
->tokenbuf
.ptr
- ts
->tokenbuf
.base
,
8233 CURRENT_TOKEN(ts
).t_reflags
);
8236 if (!(tc
->flags
& TCF_COMPILE_N_GO
)) {
8237 STOBJ_CLEAR_PARENT(obj
);
8238 STOBJ_CLEAR_PROTO(obj
);
8241 pn
->pn_objbox
= tc
->compiler
->newObjectBox(obj
);
8245 pn
->pn_op
= JSOP_REGEXP
;
8250 pn
= NewParseNode(PN_NULLARY
, tc
);
8253 pn
->pn_op
= JSOP_DOUBLE
;
8254 pn
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
8258 pn
= NewParseNode(PN_NULLARY
, tc
);
8261 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
8265 /* The scanner or one of its subroutines reported the error. */
8269 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8270 JSMSG_SYNTAX_ERROR
);
8276 static JSParseNode
*
8277 ParenExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
8278 JSParseNode
*pn1
, JSBool
*genexp
)
8283 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LP
);
8284 begin
= CURRENT_TOKEN(ts
).pos
.begin
;
8288 pn
= BracketedExpr(cx
, ts
, tc
);
8292 #if JS_HAS_GENERATOR_EXPRS
8293 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
8294 if (pn
->pn_type
== TOK_YIELD
&& !pn
->pn_parens
) {
8295 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
8296 JSMSG_BAD_GENERATOR_SYNTAX
,
8300 if (pn
->pn_type
== TOK_COMMA
&& !pn
->pn_parens
) {
8301 js_ReportCompileErrorNumber(cx
, ts
, pn
->last(), JSREPORT_ERROR
,
8302 JSMSG_BAD_GENERATOR_SYNTAX
,
8307 pn1
= NewParseNode(PN_UNARY
, tc
);
8311 pn
= GeneratorExpr(pn1
, pn
, tc
);
8314 pn
->pn_pos
.begin
= begin
;
8316 if (js_GetToken(cx
, ts
) != TOK_RP
) {
8317 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
8318 JSMSG_BAD_GENERATOR_SYNTAX
,
8322 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
8326 #endif /* JS_HAS_GENERATOR_EXPRS */
8332 * Fold from one constant type to another.
8333 * XXX handles only strings and numbers for now
8336 FoldType(JSContext
*cx
, JSParseNode
*pn
, JSTokenType type
)
8338 if (PN_TYPE(pn
) != type
) {
8341 if (pn
->pn_type
== TOK_STRING
) {
8343 if (!JS_ValueToNumber(cx
, ATOM_KEY(pn
->pn_atom
), &d
))
8346 pn
->pn_type
= TOK_NUMBER
;
8347 pn
->pn_op
= JSOP_DOUBLE
;
8352 if (pn
->pn_type
== TOK_NUMBER
) {
8353 JSString
*str
= js_NumberToString(cx
, pn
->pn_dval
);
8356 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8359 pn
->pn_type
= TOK_STRING
;
8360 pn
->pn_op
= JSOP_STRING
;
8371 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8372 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8373 * a successful call to this function.
8376 FoldBinaryNumeric(JSContext
*cx
, JSOp op
, JSParseNode
*pn1
, JSParseNode
*pn2
,
8377 JSParseNode
*pn
, JSTreeContext
*tc
)
8382 JS_ASSERT(pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
);
8388 i
= js_DoubleToECMAInt32(d
);
8389 j
= js_DoubleToECMAInt32(d2
);
8391 d
= (op
== JSOP_LSH
) ? i
<< j
: i
>> j
;
8395 j
= js_DoubleToECMAInt32(d2
);
8397 d
= js_DoubleToECMAUint32(d
) >> j
;
8415 /* XXX MSVC miscompiles such that (NaN == 0) */
8416 if (JSDOUBLE_IS_NaN(d2
))
8417 d
= *cx
->runtime
->jsNaN
;
8420 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
8421 d
= *cx
->runtime
->jsNaN
;
8422 else if ((JSDOUBLE_HI32(d
) ^ JSDOUBLE_HI32(d2
)) >> 31)
8423 d
= *cx
->runtime
->jsNegativeInfinity
;
8425 d
= *cx
->runtime
->jsPositiveInfinity
;
8433 d
= *cx
->runtime
->jsNaN
;
8442 /* Take care to allow pn1 or pn2 to alias pn. */
8444 RecycleTree(pn1
, tc
);
8446 RecycleTree(pn2
, tc
);
8447 pn
->pn_type
= TOK_NUMBER
;
8448 pn
->pn_op
= JSOP_DOUBLE
;
8449 pn
->pn_arity
= PN_NULLARY
;
8454 #if JS_HAS_XML_SUPPORT
8457 FoldXMLConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
8460 JSParseNode
**pnp
, *pn1
, *pn2
;
8461 JSString
*accum
, *str
;
8463 JSTempValueRooter tvr
;
8465 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
8470 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8471 if (tt
== TOK_XMLETAGO
)
8472 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.etagoAtom
);
8473 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
)
8474 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.stagoAtom
);
8478 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8479 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8480 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8481 * Therefore, we have to add additonal protection from GC nesting under
8484 for (pn2
= pn1
, i
= j
= 0; pn2
; pn2
= pn2
->pn_next
, i
++) {
8485 /* The parser already rejected end-tags with attributes. */
8486 JS_ASSERT(tt
!= TOK_XMLETAGO
|| i
== 0);
8487 switch (pn2
->pn_type
) {
8496 if (pn2
->pn_arity
== PN_LIST
)
8498 str
= ATOM_TO_STRING(pn2
->pn_atom
);
8502 str
= js_MakeXMLCDATAString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8507 case TOK_XMLCOMMENT
:
8508 str
= js_MakeXMLCommentString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
8514 str
= js_MakeXMLPIString(cx
, ATOM_TO_STRING(pn2
->pn_atom
),
8515 ATOM_TO_STRING(pn2
->pn_atom2
));
8522 JS_ASSERT(*pnp
== pn1
);
8523 if ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) &&
8524 (i
& 1) ^ (j
& 1)) {
8525 #ifdef DEBUG_brendanXXX
8526 printf("1: %d, %d => ", i
, j
);
8528 js_FileEscapedString(stdout
, accum
, 0);
8530 fputs("NULL", stdout
);
8531 fputc('\n', stdout
);
8533 } else if (accum
&& pn1
!= pn2
) {
8534 while (pn1
->pn_next
!= pn2
) {
8535 pn1
= RecycleTree(pn1
, tc
);
8538 pn1
->pn_type
= TOK_XMLTEXT
;
8539 pn1
->pn_op
= JSOP_STRING
;
8540 pn1
->pn_arity
= PN_NULLARY
;
8541 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8544 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8547 pnp
= &pn2
->pn_next
;
8554 JS_PUSH_TEMP_ROOT_STRING(cx
, accum
, &tvr
);
8555 str
= ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) && i
!= 0)
8556 ? js_AddAttributePart(cx
, i
& 1, accum
, str
)
8557 : js_ConcatStrings(cx
, accum
, str
);
8558 JS_POP_TEMP_ROOT(cx
, &tvr
);
8561 #ifdef DEBUG_brendanXXX
8562 printf("2: %d, %d => ", i
, j
);
8563 js_FileEscapedString(stdout
, str
, 0);
8564 printf(" (%u)\n", str
->length());
8573 if ((pn
->pn_xflags
& PNX_CANTFOLD
) == 0) {
8574 if (tt
== TOK_XMLPTAGC
)
8575 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.ptagcAtom
);
8576 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLETAGO
)
8577 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.tagcAtom
);
8580 accum
= js_ConcatStrings(cx
, accum
, str
);
8585 JS_ASSERT(*pnp
== pn1
);
8586 while (pn1
->pn_next
) {
8587 pn1
= RecycleTree(pn1
, tc
);
8590 pn1
->pn_type
= TOK_XMLTEXT
;
8591 pn1
->pn_op
= JSOP_STRING
;
8592 pn1
->pn_arity
= PN_NULLARY
;
8593 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
8596 JS_ASSERT(pnp
!= &pn1
->pn_next
);
8600 if (pn1
&& pn
->pn_count
== 1) {
8602 * Only one node under pn, and it has been folded: move pn1 onto pn
8603 * unless pn is an XML root (in which case we need it to tell the code
8604 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8605 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8606 * extra "<" and "/>" bracketing at runtime.
8608 if (!(pn
->pn_xflags
& PNX_XMLROOT
)) {
8610 } else if (tt
== TOK_XMLPTAGC
) {
8611 pn
->pn_type
= TOK_XMLELEM
;
8612 pn
->pn_op
= JSOP_TOXML
;
8618 #endif /* JS_HAS_XML_SUPPORT */
8621 Boolish(JSParseNode
*pn
)
8623 switch (pn
->pn_op
) {
8625 return pn
->pn_dval
!= 0 && !JSDOUBLE_IS_NaN(pn
->pn_dval
);
8628 return ATOM_TO_STRING(pn
->pn_atom
)->length() != 0;
8630 #if JS_HAS_GENERATOR_EXPRS
8634 * A generator expression as an if or loop condition has no effects, it
8635 * simply results in a truthy object reference. This condition folding
8636 * is needed for the decompiler. See bug 442342 and bug 443074.
8638 if (pn
->pn_count
!= 1)
8640 JSParseNode
*pn2
= pn
->pn_head
;
8641 if (pn2
->pn_type
!= TOK_FUNCTION
)
8643 if (!(pn2
->pn_funbox
->tcflags
& TCF_GENEXP_LAMBDA
))
8665 js_FoldConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, bool inCond
)
8667 JSParseNode
*pn1
= NULL
, *pn2
= NULL
, *pn3
= NULL
;
8669 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
8671 switch (pn
->pn_arity
) {
8674 uint16 oldflags
= tc
->flags
;
8675 JSFunctionBox
*oldlist
= tc
->functionList
;
8677 tc
->flags
= (uint16
) pn
->pn_funbox
->tcflags
;
8678 tc
->functionList
= pn
->pn_funbox
->kids
;
8679 if (!js_FoldConstants(cx
, pn
->pn_body
, tc
))
8681 pn
->pn_funbox
->kids
= tc
->functionList
;
8682 tc
->flags
= oldflags
;
8683 tc
->functionList
= oldlist
;
8689 /* Propagate inCond through logical connectives. */
8690 bool cond
= inCond
&& (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
);
8692 /* Save the list head in pn1 for later use. */
8693 for (pn1
= pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
8694 if (!js_FoldConstants(cx
, pn2
, tc
, cond
))
8701 /* Any kid may be null (e.g. for (;;)). */
8705 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_IF
))
8708 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_FORHEAD
))
8710 if (pn
->pn_type
== TOK_FORHEAD
&& pn2
->pn_op
== JSOP_TRUE
) {
8711 RecycleTree(pn2
, tc
);
8715 if (pn3
&& !js_FoldConstants(cx
, pn3
, tc
))
8723 /* Propagate inCond through logical connectives. */
8724 if (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
) {
8725 if (!js_FoldConstants(cx
, pn1
, tc
, inCond
))
8727 if (!js_FoldConstants(cx
, pn2
, tc
, inCond
))
8732 /* First kid may be null (for default case in switch). */
8733 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_WHILE
))
8735 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_DO
))
8740 /* Our kid may be null (e.g. return; vs. return e;). */
8742 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_op
== JSOP_NOT
))
8748 * Skip pn1 down along a chain of dotted member expressions to avoid
8749 * excessive recursion. Our only goal here is to fold constants (if
8750 * any) in the primary expression operand to the left of the first
8755 while (pn1
&& pn1
->pn_arity
== PN_NAME
&& !pn1
->pn_used
)
8757 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
8764 if (!js_FoldConstants(cx
, pn1
, tc
))
8772 switch (pn
->pn_type
) {
8774 if (ContainsStmt(pn2
, TOK_VAR
) || ContainsStmt(pn3
, TOK_VAR
))
8779 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8780 switch (pn1
->pn_type
) {
8782 if (pn1
->pn_dval
== 0 || JSDOUBLE_IS_NaN(pn1
->pn_dval
))
8786 if (ATOM_TO_STRING(pn1
->pn_atom
)->length() == 0)
8790 if (pn1
->pn_op
== JSOP_TRUE
)
8792 if (pn1
->pn_op
== JSOP_FALSE
|| pn1
->pn_op
== JSOP_NULL
) {
8798 /* Early return to dodge common code that copies pn2 to pn. */
8802 #if JS_HAS_GENERATOR_EXPRS
8803 /* Don't fold a trailing |if (0)| in a generator expression. */
8804 if (!pn2
&& (tc
->flags
& TCF_GENEXP_LAMBDA
))
8808 if (pn2
&& !pn2
->pn_defn
)
8810 if (!pn2
|| (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
)) {
8812 * False condition and no else, or an empty then-statement was
8813 * moved up over pn. Either way, make pn an empty block (not an
8814 * empty statement, which does not decompile, even when labeled).
8815 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8816 * or an empty statement for a child.
8818 pn
->pn_type
= TOK_LC
;
8819 pn
->pn_arity
= PN_LIST
;
8822 RecycleTree(pn2
, tc
);
8823 if (pn3
&& pn3
!= pn2
)
8824 RecycleTree(pn3
, tc
);
8830 if (pn
->pn_arity
== PN_LIST
) {
8831 JSParseNode
**pnp
= &pn
->pn_head
;
8832 JS_ASSERT(*pnp
== pn1
);
8834 int cond
= Boolish(pn1
);
8835 if (cond
== (pn
->pn_type
== TOK_OR
)) {
8836 for (pn2
= pn1
->pn_next
; pn2
; pn2
= pn3
) {
8838 RecycleTree(pn2
, tc
);
8841 pn1
->pn_next
= NULL
;
8845 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
8846 if (pn
->pn_count
== 1)
8848 *pnp
= pn1
->pn_next
;
8849 RecycleTree(pn1
, tc
);
8852 pnp
= &pn1
->pn_next
;
8854 } while ((pn1
= *pnp
) != NULL
);
8856 // We may have to change arity from LIST to BINARY.
8858 if (pn
->pn_count
== 2) {
8860 pn1
->pn_next
= NULL
;
8861 JS_ASSERT(!pn2
->pn_next
);
8862 pn
->pn_arity
= PN_BINARY
;
8865 } else if (pn
->pn_count
== 1) {
8867 RecycleTree(pn1
, tc
);
8870 int cond
= Boolish(pn1
);
8871 if (cond
== (pn
->pn_type
== TOK_OR
)) {
8872 RecycleTree(pn2
, tc
);
8874 } else if (cond
!= -1) {
8875 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
8876 RecycleTree(pn1
, tc
);
8885 * Compound operators such as *= should be subject to folding, in case
8886 * the left-hand side is constant, and so that the decompiler produces
8887 * the same string that you get from decompiling a script or function
8888 * compiled from that same string. As with +, += is special.
8890 if (pn
->pn_op
== JSOP_NOP
)
8892 if (pn
->pn_op
!= JSOP_ADD
)
8897 if (pn
->pn_arity
== PN_LIST
) {
8898 size_t length
, length2
;
8900 JSString
*str
, *str2
;
8903 * Any string literal term with all others number or string means
8904 * this is a concatenation. If any term is not a string or number
8905 * literal, we can't fold.
8907 JS_ASSERT(pn
->pn_count
> 2);
8908 if (pn
->pn_xflags
& PNX_CANTFOLD
)
8910 if (pn
->pn_xflags
!= PNX_STRCAT
)
8913 /* Ok, we're concatenating: convert non-string constant operands. */
8915 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
8916 if (!FoldType(cx
, pn2
, TOK_STRING
))
8918 /* XXX fold only if all operands convert to string */
8919 if (pn2
->pn_type
!= TOK_STRING
)
8921 length
+= ATOM_TO_STRING(pn2
->pn_atom
)->flatLength();
8924 /* Allocate a new buffer and string descriptor for the result. */
8925 chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
8928 str
= js_NewString(cx
, chars
, length
);
8934 /* Fill the buffer, advancing chars and recycling kids as we go. */
8935 for (pn2
= pn1
; pn2
; pn2
= RecycleTree(pn2
, tc
)) {
8936 str2
= ATOM_TO_STRING(pn2
->pn_atom
);
8937 length2
= str2
->flatLength();
8938 js_strncpy(chars
, str2
->flatChars(), length2
);
8943 /* Atomize the result string and mutate pn to refer to it. */
8944 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8947 pn
->pn_type
= TOK_STRING
;
8948 pn
->pn_op
= JSOP_STRING
;
8949 pn
->pn_arity
= PN_NULLARY
;
8953 /* Handle a binary string concatenation. */
8954 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
8955 if (pn1
->pn_type
== TOK_STRING
|| pn2
->pn_type
== TOK_STRING
) {
8956 JSString
*left
, *right
, *str
;
8958 if (!FoldType(cx
, (pn1
->pn_type
!= TOK_STRING
) ? pn1
: pn2
,
8962 if (pn1
->pn_type
!= TOK_STRING
|| pn2
->pn_type
!= TOK_STRING
)
8964 left
= ATOM_TO_STRING(pn1
->pn_atom
);
8965 right
= ATOM_TO_STRING(pn2
->pn_atom
);
8966 str
= js_ConcatStrings(cx
, left
, right
);
8969 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
8972 pn
->pn_type
= TOK_STRING
;
8973 pn
->pn_op
= JSOP_STRING
;
8974 pn
->pn_arity
= PN_NULLARY
;
8975 RecycleTree(pn1
, tc
);
8976 RecycleTree(pn2
, tc
);
8980 /* Can't concatenate string literals, let's try numbers. */
8988 if (pn
->pn_arity
== PN_LIST
) {
8989 JS_ASSERT(pn
->pn_count
> 2);
8990 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
8991 if (!FoldType(cx
, pn2
, TOK_NUMBER
))
8994 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
8995 /* XXX fold only if all operands convert to number */
8996 if (pn2
->pn_type
!= TOK_NUMBER
)
9000 JSOp op
= PN_OP(pn
);
9004 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
, tc
))
9006 while ((pn2
= pn3
) != NULL
) {
9008 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
, tc
))
9013 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
9014 if (!FoldType(cx
, pn1
, TOK_NUMBER
) ||
9015 !FoldType(cx
, pn2
, TOK_NUMBER
)) {
9018 if (pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
) {
9019 if (!FoldBinaryNumeric(cx
, PN_OP(pn
), pn1
, pn2
, pn
, tc
))
9026 if (pn1
->pn_type
== TOK_NUMBER
) {
9029 /* Operate on one numeric constant. */
9031 switch (pn
->pn_op
) {
9033 d
= ~js_DoubleToECMAInt32(d
);
9039 * Negation of a zero doesn't produce a negative
9040 * zero on HPUX. Perform the operation by bit
9043 JSDOUBLE_HI32(d
) ^= JSDOUBLE_HI32_SIGNBIT
;
9053 pn
->pn_type
= TOK_PRIMARY
;
9054 pn
->pn_op
= (d
== 0 || JSDOUBLE_IS_NaN(d
)) ? JSOP_TRUE
: JSOP_FALSE
;
9055 pn
->pn_arity
= PN_NULLARY
;
9059 /* Return early to dodge the common TOK_NUMBER code. */
9062 pn
->pn_type
= TOK_NUMBER
;
9063 pn
->pn_op
= JSOP_DOUBLE
;
9064 pn
->pn_arity
= PN_NULLARY
;
9066 RecycleTree(pn1
, tc
);
9067 } else if (pn1
->pn_type
== TOK_PRIMARY
) {
9068 if (pn
->pn_op
== JSOP_NOT
&&
9069 (pn1
->pn_op
== JSOP_TRUE
||
9070 pn1
->pn_op
== JSOP_FALSE
)) {
9072 pn
->pn_op
= (pn
->pn_op
== JSOP_TRUE
) ? JSOP_FALSE
: JSOP_TRUE
;
9073 RecycleTree(pn1
, tc
);
9078 #if JS_HAS_XML_SUPPORT
9085 if (pn
->pn_arity
== PN_LIST
) {
9086 JS_ASSERT(pn
->pn_type
== TOK_XMLLIST
|| pn
->pn_count
!= 0);
9087 if (!FoldXMLConstants(cx
, pn
, tc
))
9093 if (pn1
->pn_type
== TOK_XMLNAME
) {
9095 JSObjectBox
*xmlbox
;
9097 v
= ATOM_KEY(pn1
->pn_atom
);
9098 if (!js_ToAttributeName(cx
, &v
))
9100 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
9102 xmlbox
= tc
->compiler
->newObjectBox(JSVAL_TO_OBJECT(v
));
9106 pn
->pn_type
= TOK_XMLNAME
;
9107 pn
->pn_op
= JSOP_OBJECT
;
9108 pn
->pn_arity
= PN_NULLARY
;
9109 pn
->pn_objbox
= xmlbox
;
9110 RecycleTree(pn1
, tc
);
9113 #endif /* JS_HAS_XML_SUPPORT */
9119 int cond
= Boolish(pn
);
9121 switch (pn
->pn_arity
) {
9126 RecycleTree(pn2
, tc
);
9127 } while ((pn2
= pn3
) != NULL
);
9130 RecycleFuncNameKids(pn
, tc
);
9135 JS_NOT_REACHED("unhandled arity");
9137 pn
->pn_type
= TOK_PRIMARY
;
9138 pn
->pn_op
= cond
? JSOP_TRUE
: JSOP_FALSE
;
9139 pn
->pn_arity
= PN_NULLARY
;