1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 * This is a recursive-descent parser for the JavaScript language specified by
45 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
46 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
47 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
48 * After tree construction, it rewrites trees to fold constants and evaluate
49 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
52 * This parser attempts no error recovery.
59 #include "jsarena.h" /* Added by JSIFY */
60 #include "jsutil.h" /* Added by JSIFY */
65 #include "jsversion.h"
79 #include "jsstaticcheck.h"
81 #if JS_HAS_XML_SUPPORT
85 #if JS_HAS_DESTRUCTURING
90 * Asserts to verify assumptions behind pn_ macros.
92 JS_STATIC_ASSERT(offsetof(JSParseNode
, pn_u
.name
.atom
) ==
93 offsetof(JSParseNode
, pn_u
.apair
.atom
));
94 JS_STATIC_ASSERT(offsetof(JSParseNode
, pn_u
.name
.slot
) ==
95 offsetof(JSParseNode
, pn_u
.lexical
.slot
));
98 * JS parsers, from lowest to highest precedence.
100 * Each parser takes a context, a token stream, and a tree context struct.
101 * Each returns a parse node tree or null on error.
104 typedef JSParseNode
*
105 JSParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
);
107 typedef JSParseNode
*
108 JSMemberParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
109 JSBool allowCallSyntax
);
111 typedef JSParseNode
*
112 JSPrimaryParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
113 JSTokenType tt
, JSBool afterDot
);
115 typedef JSParseNode
*
116 JSParenParser(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
117 JSParseNode
*pn1
, JSBool
*genexp
);
119 static JSParser FunctionStmt
;
120 static JSParser FunctionExpr
;
121 static JSParser Statements
;
122 static JSParser Statement
;
123 static JSParser Variables
;
124 static JSParser Expr
;
125 static JSParser AssignExpr
;
126 static JSParser CondExpr
;
127 static JSParser OrExpr
;
128 static JSParser AndExpr
;
129 static JSParser BitOrExpr
;
130 static JSParser BitXorExpr
;
131 static JSParser BitAndExpr
;
132 static JSParser EqExpr
;
133 static JSParser RelExpr
;
134 static JSParser ShiftExpr
;
135 static JSParser AddExpr
;
136 static JSParser MulExpr
;
137 static JSParser UnaryExpr
;
138 static JSMemberParser MemberExpr
;
139 static JSPrimaryParser PrimaryExpr
;
140 static JSParenParser ParenExpr
;
143 * Insist that the next token be of type tt, or report errno and return null.
144 * NB: this macro uses cx and ts from its lexical environment.
146 #define MUST_MATCH_TOKEN(tt, errno) \
148 if (js_GetToken(cx, ts) != tt) { \
149 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
154 #ifdef METER_PARSENODES
155 static uint32 parsenodes
= 0;
156 static uint32 maxparsenodes
= 0;
157 static uint32 recyclednodes
= 0;
161 js_InitParseContext(JSContext
*cx
, JSParseContext
*pc
, JSPrincipals
*principals
,
162 JSStackFrame
*callerFrame
,
163 const jschar
*base
, size_t length
,
164 FILE *fp
, const char *filename
, uintN lineno
)
166 JS_ASSERT_IF(callerFrame
, callerFrame
->script
);
168 pc
->tempPoolMark
= JS_ARENA_MARK(&cx
->tempPool
);
169 if (!js_InitTokenStream(cx
, TS(pc
), base
, length
, fp
, filename
, lineno
)) {
170 JS_ARENA_RELEASE(&cx
->tempPool
, pc
->tempPoolMark
);
174 JSPRINCIPALS_HOLD(cx
, principals
);
175 pc
->principals
= principals
;
176 pc
->callerFrame
= callerFrame
;
178 pc
->traceListHead
= NULL
;
180 /* Root atoms and objects allocated for the parsed tree. */
181 JS_KEEP_ATOMS(cx
->runtime
);
182 JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx
, pc
, &pc
->tempRoot
);
187 js_FinishParseContext(JSContext
*cx
, JSParseContext
*pc
)
190 JSPRINCIPALS_DROP(cx
, pc
->principals
);
191 JS_ASSERT(pc
->tempRoot
.u
.parseContext
== pc
);
192 JS_POP_TEMP_ROOT(cx
, &pc
->tempRoot
);
193 JS_UNKEEP_ATOMS(cx
->runtime
);
194 js_CloseTokenStream(cx
, TS(pc
));
195 JS_ARENA_RELEASE(&cx
->tempPool
, pc
->tempPoolMark
);
199 js_InitCompilePrincipals(JSContext
*cx
, JSParseContext
*pc
,
200 JSPrincipals
*principals
)
202 JS_ASSERT(!pc
->principals
);
204 JSPRINCIPALS_HOLD(cx
, principals
);
205 pc
->principals
= principals
;
209 js_NewParsedObjectBox(JSContext
*cx
, JSParseContext
*pc
, JSObject
*obj
)
211 JSParsedObjectBox
*pob
;
214 * We use JSContext.tempPool to allocate parsed objects and place them on
215 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
216 * containing the entries must be alive until we are done with scanning,
217 * parsing and code generation for the whole script or top-level function.
220 JS_ARENA_ALLOCATE_TYPE(pob
, JSParsedObjectBox
, &cx
->tempPool
);
222 js_ReportOutOfScriptQuota(cx
);
225 pob
->traceLink
= pc
->traceListHead
;
226 pob
->emitLink
= NULL
;
228 pc
->traceListHead
= pob
;
234 js_TraceParseContext(JSTracer
*trc
, JSParseContext
*pc
)
236 JSParsedObjectBox
*pob
;
238 JS_ASSERT(pc
->tempRoot
.u
.parseContext
== pc
);
239 pob
= pc
->traceListHead
;
241 JS_CALL_OBJECT_TRACER(trc
, pob
->object
, "parser.object");
242 pob
= pob
->traceLink
;
247 RecycleTree(JSParseNode
*pn
, JSTreeContext
*tc
)
254 /* Catch back-to-back dup recycles. */
255 JS_ASSERT(pn
!= tc
->parseContext
->nodeList
);
257 pn
->pn_next
= tc
->parseContext
->nodeList
;
258 tc
->parseContext
->nodeList
= pn
;
259 #ifdef METER_PARSENODES
266 NewOrRecycledNode(JSContext
*cx
, JSTreeContext
*tc
)
270 pn
= tc
->parseContext
->nodeList
;
272 JS_ARENA_ALLOCATE_TYPE(pn
, JSParseNode
, &cx
->tempPool
);
274 js_ReportOutOfScriptQuota(cx
);
276 tc
->parseContext
->nodeList
= pn
->pn_next
;
278 /* Recycle immediate descendents only, to save work and working set. */
279 switch (pn
->pn_arity
) {
281 RecycleTree(pn
->pn_body
, tc
);
285 /* XXX check for dup recycles in the list */
286 *pn
->pn_tail
= tc
->parseContext
->nodeList
;
287 tc
->parseContext
->nodeList
= pn
->pn_head
;
288 #ifdef METER_PARSENODES
289 recyclednodes
+= pn
->pn_count
;
294 RecycleTree(pn
->pn_kid1
, tc
);
295 RecycleTree(pn
->pn_kid2
, tc
);
296 RecycleTree(pn
->pn_kid3
, tc
);
299 if (pn
->pn_left
!= pn
->pn_right
)
300 RecycleTree(pn
->pn_left
, tc
);
301 RecycleTree(pn
->pn_right
, tc
);
304 RecycleTree(pn
->pn_kid
, tc
);
307 RecycleTree(pn
->pn_expr
, tc
);
314 #ifdef METER_PARSENODES
316 if (parsenodes
- recyclednodes
> maxparsenodes
)
317 maxparsenodes
= parsenodes
- recyclednodes
;
319 memset(&pn
->pn_u
, 0, sizeof pn
->pn_u
);
326 * Allocate a JSParseNode from cx's temporary arena.
329 NewParseNode(JSContext
*cx
, JSTokenStream
*ts
, JSParseNodeArity arity
,
335 pn
= NewOrRecycledNode(cx
, tc
);
338 tp
= &CURRENT_TOKEN(ts
);
339 pn
->pn_type
= tp
->type
;
340 pn
->pn_pos
= tp
->pos
;
341 pn
->pn_op
= JSOP_NOP
;
342 pn
->pn_arity
= arity
;
347 NewBinary(JSContext
*cx
, JSTokenType tt
,
348 JSOp op
, JSParseNode
*left
, JSParseNode
*right
,
351 JSParseNode
*pn
, *pn1
, *pn2
;
357 * Flatten a left-associative (left-heavy) tree of a given operator into
358 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
360 if (left
->pn_type
== tt
&&
362 (js_CodeSpec
[op
].format
& JOF_LEFTASSOC
)) {
363 if (left
->pn_arity
!= PN_LIST
) {
364 pn1
= left
->pn_left
, pn2
= left
->pn_right
;
365 left
->pn_arity
= PN_LIST
;
366 PN_INIT_LIST_1(left
, pn1
);
367 PN_APPEND(left
, pn2
);
368 if (tt
== TOK_PLUS
) {
369 if (pn1
->pn_type
== TOK_STRING
)
370 left
->pn_extra
|= PNX_STRCAT
;
371 else if (pn1
->pn_type
!= TOK_NUMBER
)
372 left
->pn_extra
|= PNX_CANTFOLD
;
373 if (pn2
->pn_type
== TOK_STRING
)
374 left
->pn_extra
|= PNX_STRCAT
;
375 else if (pn2
->pn_type
!= TOK_NUMBER
)
376 left
->pn_extra
|= PNX_CANTFOLD
;
379 PN_APPEND(left
, right
);
380 left
->pn_pos
.end
= right
->pn_pos
.end
;
381 if (tt
== TOK_PLUS
) {
382 if (right
->pn_type
== TOK_STRING
)
383 left
->pn_extra
|= PNX_STRCAT
;
384 else if (right
->pn_type
!= TOK_NUMBER
)
385 left
->pn_extra
|= PNX_CANTFOLD
;
391 * Fold constant addition immediately, to conserve node space and, what's
392 * more, so js_FoldConstants never sees mixed addition and concatenation
393 * operations with more than one leading non-string operand in a PN_LIST
394 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
395 * to "3pt", not "12pt").
397 if (tt
== TOK_PLUS
&&
398 left
->pn_type
== TOK_NUMBER
&&
399 right
->pn_type
== TOK_NUMBER
) {
400 left
->pn_dval
+= right
->pn_dval
;
401 left
->pn_pos
.end
= right
->pn_pos
.end
;
402 RecycleTree(right
, tc
);
406 pn
= NewOrRecycledNode(cx
, tc
);
410 pn
->pn_pos
.begin
= left
->pn_pos
.begin
;
411 pn
->pn_pos
.end
= right
->pn_pos
.end
;
413 pn
->pn_arity
= PN_BINARY
;
415 pn
->pn_right
= right
;
419 #if JS_HAS_GETTER_SETTER
421 CheckGetterOrSetter(JSContext
*cx
, JSTokenStream
*ts
, JSTokenType tt
)
428 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_NAME
);
429 atom
= CURRENT_TOKEN(ts
).t_atom
;
431 if (atom
== rt
->atomState
.getterAtom
)
433 else if (atom
== rt
->atomState
.setterAtom
)
437 if (js_PeekTokenSameLine(cx
, ts
) != tt
)
439 (void) js_GetToken(cx
, ts
);
440 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
441 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
442 JSMSG_BAD_GETTER_OR_SETTER
,
448 CURRENT_TOKEN(ts
).t_op
= op
;
449 if (JS_HAS_STRICT_OPTION(cx
)) {
450 name
= js_AtomToPrintableString(cx
, atom
);
452 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
453 JSREPORT_WARNING
| JSREPORT_STRICT
,
454 JSMSG_DEPRECATED_USAGE
,
464 * Parse a top-level JS script.
467 js_ParseScript(JSContext
*cx
, JSObject
*chain
, JSParseContext
*pc
)
473 * Protect atoms from being collected by a GC activation, which might
474 * - nest on this thread due to out of memory (the so-called "last ditch"
475 * GC attempted within js_NewGCThing), or
476 * - run for any reason on another thread if this thread is suspended on
477 * an object lock before it finishes generating bytecode into a script
478 * protected from the GC by a root or a stack frame reference.
480 TREE_CONTEXT_INIT(&tc
, pc
);
481 tc
.u
.scopeChain
= chain
;
482 pn
= Statements(cx
, TS(pc
), &tc
);
484 if (!js_MatchToken(cx
, TS(pc
), TOK_EOF
)) {
485 js_ReportCompileErrorNumber(cx
, TS(pc
), NULL
, JSREPORT_ERROR
,
489 pn
->pn_type
= TOK_LC
;
490 if (!js_FoldConstants(cx
, pn
, &tc
))
495 TREE_CONTEXT_FINISH(cx
, &tc
);
500 * Compile a top-level script.
503 js_CompileScript(JSContext
*cx
, JSObject
*scopeChain
, JSStackFrame
*callerFrame
,
504 JSPrincipals
*principals
, uint32 tcflags
,
505 const jschar
*chars
, size_t length
,
506 FILE *file
, const char *filename
, uintN lineno
)
509 JSArenaPool codePool
, notePool
;
513 uint32 scriptGlobals
;
515 #ifdef METER_PARSENODES
516 void *sbrk(ptrdiff_t), *before
= sbrk(0);
519 JS_ASSERT(!(tcflags
& ~(TCF_COMPILE_N_GO
| TCF_NO_SCRIPT_RVAL
|
520 TCF_STATIC_DEPTH_MASK
)));
523 * The scripted callerFrame can only be given for compile-and-go scripts
524 * and non-zero static depth requires callerFrame.
526 JS_ASSERT_IF(callerFrame
, tcflags
& TCF_COMPILE_N_GO
);
527 JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags
) != 0, callerFrame
);
529 if (!js_InitParseContext(cx
, &pc
, principals
, callerFrame
, chars
, length
,
530 file
, filename
, lineno
)) {
534 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
535 &cx
->scriptStackQuota
);
536 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
537 &cx
->scriptStackQuota
);
538 js_InitCodeGenerator(cx
, &cg
, &pc
, &codePool
, ¬ePool
,
539 pc
.tokenStream
.lineno
);
541 MUST_FLOW_THROUGH("out");
542 cg
.treeContext
.flags
|= (uint16
) tcflags
;
543 cg
.treeContext
.u
.scopeChain
= scopeChain
;
544 cg
.staticDepth
= TCF_GET_STATIC_DEPTH(tcflags
);
546 if ((tcflags
& TCF_COMPILE_N_GO
) && callerFrame
&& callerFrame
->fun
) {
548 * An eval script in a caller frame needs to have its enclosing function
549 * captured in case it uses an upvar reference, and someone wishes to
550 * decompile it while running.
552 JSParsedObjectBox
*pob
= js_NewParsedObjectBox(cx
, &pc
, callerFrame
->callee
);
553 pob
->emitLink
= cg
.objectList
.lastPob
;
554 cg
.objectList
.lastPob
= pob
;
555 cg
.objectList
.length
++;
558 /* Inline Statements() to emit as we go to save space. */
560 pc
.tokenStream
.flags
|= TSF_OPERAND
;
561 tt
= js_PeekToken(cx
, &pc
.tokenStream
);
562 pc
.tokenStream
.flags
&= ~TSF_OPERAND
;
566 JS_ASSERT(tt
== TOK_ERROR
);
571 pn
= Statement(cx
, &pc
.tokenStream
, &cg
.treeContext
);
576 JS_ASSERT(!cg
.treeContext
.blockNode
);
578 if (!js_FoldConstants(cx
, pn
, &cg
.treeContext
) ||
579 !js_EmitTree(cx
, &cg
, pn
)) {
583 RecycleTree(pn
, &cg
.treeContext
);
587 * Global variables and regexps shares the index space with locals. Due to
588 * incremental code generation we need to patch the bytecode to adjust the
589 * local references to skip the globals.
591 scriptGlobals
= cg
.treeContext
.ngvars
+ cg
.regexpList
.length
;
592 if (scriptGlobals
!= 0) {
593 jsbytecode
*code
, *end
;
595 const JSCodeSpec
*cs
;
598 if (scriptGlobals
>= SLOTNO_LIMIT
)
601 for (end
= code
+ CG_OFFSET(&cg
); code
!= end
; code
+= len
) {
602 JS_ASSERT(code
< end
);
604 cs
= &js_CodeSpec
[op
];
605 len
= (cs
->length
> 0)
607 : js_GetVariableBytecodeLength(code
);
608 if (JOF_TYPE(cs
->format
) == JOF_LOCAL
||
609 (JOF_TYPE(cs
->format
) == JOF_SLOTATOM
)) {
611 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
612 * emitted only for a function.
614 JS_ASSERT((JOF_TYPE(cs
->format
) == JOF_SLOTATOM
) ==
615 (op
== JSOP_GETLOCALPROP
));
616 slot
= GET_SLOTNO(code
);
617 slot
+= scriptGlobals
;
618 if (slot
>= SLOTNO_LIMIT
)
620 SET_SLOTNO(code
, slot
);
625 #ifdef METER_PARSENODES
626 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
627 (char *)sbrk(0) - (char *)before
,
630 parsenodes
- recyclednodes
);
635 * Nowadays the threaded interpreter needs a stop instruction, so we
636 * do have to emit that here.
638 if (js_Emit1(cx
, &cg
, JSOP_STOP
) < 0) {
642 #ifdef METER_PARSENODES
643 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
644 (char *)sbrk(0) - (char *)before
, CG_OFFSET(cg
), cg
->noteCount
);
647 JS_DumpArenaStats(stdout
);
649 script
= js_NewScriptFromCG(cx
, &cg
);
651 #ifdef JS_SCOPE_DEPTH_METER
653 JSObject
*obj
= scopeChain
;
655 while ((obj
= OBJ_GET_PARENT(cx
, obj
)) != NULL
)
657 JS_BASIC_STATS_ACCUM(&cx
->runtime
->hostenvScopeDepthStats
, depth
);
662 js_FinishCodeGenerator(cx
, &cg
);
663 JS_FinishArenaPool(&codePool
);
664 JS_FinishArenaPool(¬ePool
);
665 js_FinishParseContext(cx
, &pc
);
669 js_ReportCompileErrorNumber(cx
, &pc
.tokenStream
, NULL
,
670 JSREPORT_ERROR
, JSMSG_TOO_MANY_LOCALS
);
676 * Insist on a final return before control flows out of pn. Try to be a bit
677 * smart about loops: do {...; return e2;} while(0) at the end of a function
678 * that contains an early return e1 will get a strict warning. Similarly for
679 * iloops: while (true){...} is treated as though ... returns.
681 #define ENDS_IN_OTHER 0
682 #define ENDS_IN_RETURN 1
683 #define ENDS_IN_BREAK 2
686 HasFinalReturn(JSParseNode
*pn
)
688 JSParseNode
*pn2
, *pn3
;
689 uintN rv
, rv2
, hasDefault
;
691 switch (pn
->pn_type
) {
694 return ENDS_IN_OTHER
;
695 return HasFinalReturn(PN_LAST(pn
));
699 return ENDS_IN_OTHER
;
700 return HasFinalReturn(pn
->pn_kid2
) & HasFinalReturn(pn
->pn_kid3
);
704 if (pn2
->pn_type
== TOK_PRIMARY
&& pn2
->pn_op
== JSOP_TRUE
)
705 return ENDS_IN_RETURN
;
706 if (pn2
->pn_type
== TOK_NUMBER
&& pn2
->pn_dval
)
707 return ENDS_IN_RETURN
;
708 return ENDS_IN_OTHER
;
712 if (pn2
->pn_type
== TOK_PRIMARY
) {
713 if (pn2
->pn_op
== JSOP_FALSE
)
714 return HasFinalReturn(pn
->pn_left
);
715 if (pn2
->pn_op
== JSOP_TRUE
)
716 return ENDS_IN_RETURN
;
718 if (pn2
->pn_type
== TOK_NUMBER
) {
719 if (pn2
->pn_dval
== 0)
720 return HasFinalReturn(pn
->pn_left
);
721 return ENDS_IN_RETURN
;
723 return ENDS_IN_OTHER
;
727 if (pn2
->pn_arity
== PN_TERNARY
&& !pn2
->pn_kid2
)
728 return ENDS_IN_RETURN
;
729 return ENDS_IN_OTHER
;
733 hasDefault
= ENDS_IN_OTHER
;
735 if (pn2
->pn_type
== TOK_LEXICALSCOPE
)
737 for (pn2
= pn2
->pn_head
; rv
&& pn2
; pn2
= pn2
->pn_next
) {
738 if (pn2
->pn_type
== TOK_DEFAULT
)
739 hasDefault
= ENDS_IN_RETURN
;
741 JS_ASSERT(pn3
->pn_type
== TOK_LC
);
743 rv2
= HasFinalReturn(PN_LAST(pn3
));
744 if (rv2
== ENDS_IN_OTHER
&& pn2
->pn_next
)
745 /* Falling through to next case or default. */;
750 /* If a final switch has no default case, we judge it harshly. */
755 return ENDS_IN_BREAK
;
758 return HasFinalReturn(pn
->pn_right
);
761 return ENDS_IN_RETURN
;
764 case TOK_LEXICALSCOPE
:
765 return HasFinalReturn(pn
->pn_expr
);
768 return ENDS_IN_RETURN
;
771 /* If we have a finally block that returns, we are done. */
773 rv
= HasFinalReturn(pn
->pn_kid3
);
774 if (rv
== ENDS_IN_RETURN
)
778 /* Else check the try block and any and all catch statements. */
779 rv
= HasFinalReturn(pn
->pn_kid1
);
781 JS_ASSERT(pn
->pn_kid2
->pn_arity
== PN_LIST
);
782 for (pn2
= pn
->pn_kid2
->pn_head
; pn2
; pn2
= pn2
->pn_next
)
783 rv
&= HasFinalReturn(pn2
);
788 /* Check this catch block's body. */
789 return HasFinalReturn(pn
->pn_kid3
);
792 /* Non-binary let statements are let declarations. */
793 if (pn
->pn_arity
!= PN_BINARY
)
794 return ENDS_IN_OTHER
;
795 return HasFinalReturn(pn
->pn_right
);
798 return ENDS_IN_OTHER
;
803 ReportBadReturn(JSContext
*cx
, JSTreeContext
*tc
, uintN flags
, uintN errnum
,
808 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
809 if (tc
->u
.fun
->atom
) {
810 name
= js_AtomToPrintableString(cx
, tc
->u
.fun
->atom
);
815 return js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), NULL
, flags
,
820 CheckFinalReturn(JSContext
*cx
, JSTreeContext
*tc
, JSParseNode
*pn
)
822 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
823 return HasFinalReturn(pn
) == ENDS_IN_RETURN
||
824 ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
825 JSMSG_NO_RETURN_VALUE
, JSMSG_ANON_NO_RETURN_VALUE
);
829 FunctionBody(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
832 uintN oldflags
, firstLine
;
835 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
836 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
837 stmtInfo
.flags
= SIF_BODY_BLOCK
;
839 oldflags
= tc
->flags
;
840 tc
->flags
&= ~(TCF_RETURN_EXPR
| TCF_RETURN_VOID
);
843 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
844 * later, because we may have not peeked in ts yet, so Statements won't
845 * acquire a valid pn->pn_pos.begin from the current token.
847 firstLine
= ts
->lineno
;
848 #if JS_HAS_EXPR_CLOSURES
849 if (CURRENT_TOKEN(ts
).type
== TOK_LC
) {
850 pn
= Statements(cx
, ts
, tc
);
852 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
854 pn
->pn_kid
= AssignExpr(cx
, ts
, tc
);
858 if (tc
->flags
& TCF_FUN_IS_GENERATOR
) {
859 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
860 JSMSG_BAD_GENERATOR_RETURN
,
861 JSMSG_BAD_ANON_GENERATOR_RETURN
);
864 pn
->pn_type
= TOK_RETURN
;
865 pn
->pn_op
= JSOP_RETURN
;
866 pn
->pn_pos
.end
= pn
->pn_kid
->pn_pos
.end
;
872 pn
= Statements(cx
, ts
, tc
);
877 pn
->pn_pos
.begin
.lineno
= firstLine
;
879 /* Check for falling off the end of a function that returns a value. */
880 if (JS_HAS_STRICT_OPTION(cx
) && (tc
->flags
& TCF_RETURN_EXPR
) &&
881 !CheckFinalReturn(cx
, tc
, pn
)) {
886 tc
->flags
= oldflags
| (tc
->flags
& (TCF_FUN_FLAGS
| TCF_HAS_DEFXMLNS
));
891 * Compile a JS function body, which might appear as the value of an event
892 * handler attribute in an HTML <INPUT> tag.
895 js_CompileFunctionBody(JSContext
*cx
, JSFunction
*fun
, JSPrincipals
*principals
,
896 const jschar
*chars
, size_t length
,
897 const char *filename
, uintN lineno
)
900 JSArenaPool codePool
, notePool
;
901 JSCodeGenerator funcg
;
904 if (!js_InitParseContext(cx
, &pc
, principals
, NULL
, chars
, length
, NULL
,
909 /* No early return from this point until js_FinishParseContext call. */
910 JS_INIT_ARENA_POOL(&codePool
, "code", 1024, sizeof(jsbytecode
),
911 &cx
->scriptStackQuota
);
912 JS_INIT_ARENA_POOL(¬ePool
, "note", 1024, sizeof(jssrcnote
),
913 &cx
->scriptStackQuota
);
914 js_InitCodeGenerator(cx
, &funcg
, &pc
, &codePool
, ¬ePool
,
915 pc
.tokenStream
.lineno
);
916 funcg
.treeContext
.flags
|= TCF_IN_FUNCTION
;
917 funcg
.treeContext
.u
.fun
= fun
;
920 * Farble the body so that it looks like a block statement to js_EmitTree,
921 * which is called beneath FunctionBody; see Statements, further below in
922 * this file. FunctionBody pushes a STMT_BLOCK record around its call to
923 * Statements, so Statements will not compile each statement as it loops
924 * to save JSParseNode space -- it will not compile at all, only build a
927 * Therefore we must fold constants, allocate try notes, and generate code
928 * for this function, including a stop opcode at the end.
930 CURRENT_TOKEN(&pc
.tokenStream
).type
= TOK_LC
;
931 pn
= FunctionBody(cx
, &pc
.tokenStream
, &funcg
.treeContext
);
933 if (!js_MatchToken(cx
, &pc
.tokenStream
, TOK_EOF
)) {
934 js_ReportCompileErrorNumber(cx
, &pc
.tokenStream
, NULL
,
935 JSREPORT_ERROR
, JSMSG_SYNTAX_ERROR
);
938 if (!js_FoldConstants(cx
, pn
, &funcg
.treeContext
) ||
939 !js_EmitFunctionScript(cx
, &funcg
, pn
)) {
945 /* Restore saved state and release code generation arenas. */
946 js_FinishCodeGenerator(cx
, &funcg
);
947 JS_FinishArenaPool(&codePool
);
948 JS_FinishArenaPool(¬ePool
);
949 js_FinishParseContext(cx
, &pc
);
954 * Parameter block types for the several Binder functions. We use a common
955 * helper function signature in order to share code among destructuring and
956 * simple variable declaration parsers. In the destructuring case, the binder
957 * function is called indirectly from the variable declaration parser by way
958 * of CheckDestructuring and its friends.
960 typedef struct BindData BindData
;
963 (*Binder
)(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
);
966 JSParseNode
*pn
; /* error source coordinate */
967 JSOp op
; /* prolog bytecode or nop */
968 Binder binder
; /* binder, discriminates u */
977 BindArg(JSContext
*cx
, JSAtom
*atom
, JSTreeContext
*tc
)
982 * Check for a duplicate parameter name, a "feature" required by ECMA-262.
984 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
985 if (js_LookupLocal(cx
, tc
->u
.fun
, atom
, NULL
) != JSLOCAL_NONE
) {
986 name
= js_AtomToPrintableString(cx
, atom
);
988 !js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), NULL
,
989 JSREPORT_WARNING
| JSREPORT_STRICT
,
990 JSMSG_DUPLICATE_FORMAL
,
996 return js_AddLocal(cx
, tc
->u
.fun
, atom
, JSLOCAL_ARG
);
1000 BindLocalVariable(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
,
1001 JSLocalKind localKind
)
1003 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1006 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1007 * Instead 'var arguments' always restates the predefined property of the
1008 * activation objects with unhidden name 'arguments'. Assignment to such
1009 * a variable must be handled specially.
1011 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
1014 return js_AddLocal(cx
, fun
, atom
, localKind
);
1017 #if JS_HAS_DESTRUCTURING
1019 * Forward declaration to maintain top-down presentation.
1021 static JSParseNode
*
1022 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
1026 BindDestructuringArg(JSContext
*cx
, BindData
*data
, JSAtom
*atom
,
1029 JSAtomListElement
*ale
;
1032 JS_ASSERT(tc
->flags
& TCF_IN_FUNCTION
);
1033 ATOM_LIST_SEARCH(ale
, &tc
->decls
, atom
);
1035 ale
= js_IndexAtom(cx
, atom
, &tc
->decls
);
1038 ALE_SET_JSOP(ale
, data
->op
);
1041 if (js_LookupLocal(cx
, tc
->u
.fun
, atom
, NULL
) != JSLOCAL_NONE
) {
1042 name
= js_AtomToPrintableString(cx
, atom
);
1044 !js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), data
->pn
,
1045 JSREPORT_WARNING
| JSREPORT_STRICT
,
1046 JSMSG_DUPLICATE_FORMAL
,
1051 if (!BindLocalVariable(cx
, tc
->u
.fun
, atom
, JSLOCAL_VAR
))
1056 #endif /* JS_HAS_DESTRUCTURING */
1059 NewCompilerFunction(JSContext
*cx
, JSTreeContext
*tc
, JSAtom
*atom
,
1065 JS_ASSERT((lambda
& ~JSFUN_LAMBDA
) == 0);
1066 parent
= (tc
->flags
& TCF_IN_FUNCTION
)
1067 ? FUN_OBJECT(tc
->u
.fun
)
1069 fun
= js_NewFunction(cx
, NULL
, NULL
, 0, JSFUN_INTERPRETED
| lambda
,
1071 if (fun
&& !(tc
->flags
& TCF_COMPILE_N_GO
)) {
1072 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun
));
1073 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun
));
1078 static JSParseNode
*
1079 FunctionDef(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
1083 JSParseNode
*pn
, *body
, *result
;
1086 JSParsedObjectBox
*funpob
;
1087 JSAtomListElement
*ale
;
1089 JSTreeContext funtc
;
1090 #if JS_HAS_DESTRUCTURING
1091 JSParseNode
*item
, *list
= NULL
;
1094 /* Make a TOK_FUNCTION node. */
1095 #if JS_HAS_GETTER_SETTER
1096 op
= CURRENT_TOKEN(ts
).t_op
;
1098 pn
= NewParseNode(cx
, ts
, PN_FUNC
, tc
);
1102 pn
->pn_index
= (uint32
) -1;
1105 /* Scan the optional function name into funAtom. */
1106 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
1107 tt
= js_GetToken(cx
, ts
);
1108 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
1109 if (tt
== TOK_NAME
) {
1110 funAtom
= CURRENT_TOKEN(ts
).t_atom
;
1112 if (lambda
== 0 && (cx
->options
& JSOPTION_ANONFUNFIX
)) {
1113 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1114 JSMSG_SYNTAX_ERROR
);
1122 * Record names for function statements in tc->decls so we know when to
1123 * avoid optimizing variable references that might name a function.
1125 if (lambda
== 0 && funAtom
) {
1126 ATOM_LIST_SEARCH(ale
, &tc
->decls
, funAtom
);
1128 prevop
= ALE_JSOP(ale
);
1129 if (JS_HAS_STRICT_OPTION(cx
) || prevop
== JSOP_DEFCONST
) {
1130 const char *name
= js_AtomToPrintableString(cx
, funAtom
);
1132 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
1133 (prevop
!= JSOP_DEFCONST
)
1134 ? JSREPORT_WARNING
|
1137 JSMSG_REDECLARED_VAR
,
1138 (prevop
== JSOP_DEFFUN
)
1140 : (prevop
== JSOP_DEFCONST
)
1147 if (!AT_TOP_LEVEL(tc
) && prevop
== JSOP_DEFVAR
)
1148 tc
->flags
|= TCF_FUN_CLOSURE_VS_VAR
;
1150 ale
= js_IndexAtom(cx
, funAtom
, &tc
->decls
);
1154 ALE_SET_JSOP(ale
, JSOP_DEFFUN
);
1157 * A function nested at top level inside another's body needs only a
1158 * local variable to bind its name to its value, and not an activation
1159 * object property (it might also need the activation property, if the
1160 * outer function contains with statements, e.g., but the stack slot
1161 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
1162 * JSOP_GETLOCAL bytecode).
1164 if (AT_TOP_LEVEL(tc
) && (tc
->flags
& TCF_IN_FUNCTION
)) {
1165 JSLocalKind localKind
;
1168 * Define a property on the outer function so that BindNameToSlot
1169 * can properly optimize accesses. Note that we need a variable,
1170 * not an argument, for the function statement. Thus we add a
1171 * variable even if the parameter with the given name already
1174 localKind
= js_LookupLocal(cx
, tc
->u
.fun
, funAtom
, NULL
);
1175 if (localKind
== JSLOCAL_NONE
|| localKind
== JSLOCAL_ARG
) {
1176 if (!js_AddLocal(cx
, tc
->u
.fun
, funAtom
, JSLOCAL_VAR
))
1182 fun
= NewCompilerFunction(cx
, tc
, funAtom
, lambda
);
1186 #if JS_HAS_GETTER_SETTER
1188 fun
->flags
|= (op
== JSOP_GETTER
) ? JSPROP_GETTER
: JSPROP_SETTER
;
1192 * Create wrapping box for fun->object early to protect against a
1195 funpob
= js_NewParsedObjectBox(cx
, tc
->parseContext
, FUN_OBJECT(fun
));
1199 /* Initialize early for possible flags mutation via DestructuringExpr. */
1200 TREE_CONTEXT_INIT(&funtc
, tc
->parseContext
);
1201 funtc
.flags
|= TCF_IN_FUNCTION
| (tc
->flags
& TCF_COMPILE_N_GO
);
1204 /* Now parse formal argument list and compute fun->nargs. */
1205 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_FORMAL
);
1206 if (!js_MatchToken(cx
, ts
, TOK_RP
)) {
1208 tt
= js_GetToken(cx
, ts
);
1210 #if JS_HAS_DESTRUCTURING
1215 JSParseNode
*lhs
, *rhs
;
1219 * A destructuring formal parameter turns into one or more
1220 * local variables initialized from properties of a single
1221 * anonymous positional parameter, so here we must tweak our
1222 * binder and its data.
1225 data
.op
= JSOP_DEFVAR
;
1226 data
.binder
= BindDestructuringArg
;
1227 lhs
= DestructuringExpr(cx
, &data
, &funtc
, tt
);
1232 * Adjust fun->nargs to count the single anonymous positional
1233 * parameter that is to be destructured.
1236 if (!js_AddLocal(cx
, fun
, NULL
, JSLOCAL_ARG
))
1240 * Synthesize a destructuring assignment from the single
1241 * anonymous positional parameter into the destructuring
1242 * left-hand-side expression and accumulate it in list.
1244 rhs
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
1247 rhs
->pn_type
= TOK_NAME
;
1248 rhs
->pn_op
= JSOP_GETARG
;
1249 rhs
->pn_atom
= cx
->runtime
->atomState
.emptyAtom
;
1250 rhs
->pn_slot
= slot
;
1252 item
= NewBinary(cx
, TOK_ASSIGN
, JSOP_NOP
, lhs
, rhs
, tc
);
1256 list
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
1259 list
->pn_type
= TOK_COMMA
;
1262 PN_APPEND(list
, item
);
1265 #endif /* JS_HAS_DESTRUCTURING */
1268 if (!BindArg(cx
, CURRENT_TOKEN(ts
).t_atom
, &funtc
))
1273 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
1274 JSMSG_MISSING_FORMAL
);
1277 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
1279 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FORMAL
);
1282 #if JS_HAS_EXPR_CLOSURES
1283 ts
->flags
|= TSF_OPERAND
;
1284 tt
= js_GetToken(cx
, ts
);
1285 ts
->flags
&= ~TSF_OPERAND
;
1288 fun
->flags
|= JSFUN_EXPR_CLOSURE
;
1291 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_BODY
);
1293 pn
->pn_pos
.begin
= CURRENT_TOKEN(ts
).pos
.begin
;
1295 body
= FunctionBody(cx
, ts
, &funtc
);
1299 #if JS_HAS_EXPR_CLOSURES
1301 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
1302 else if (lambda
== 0)
1303 js_MatchToken(cx
, ts
, TOK_SEMI
);
1305 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_BODY
);
1307 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1309 #if JS_HAS_DESTRUCTURING
1311 * If there were destructuring formal parameters, prepend the initializing
1312 * comma expression that we synthesized to body. If the body is a lexical
1313 * scope node, we must make a special TOK_SEQ node, to prepend the formal
1314 * parameter destructuring code without bracing the decompilation of the
1315 * function body's lexical scope.
1318 if (body
->pn_arity
!= PN_LIST
) {
1321 block
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
1324 block
->pn_type
= TOK_SEQ
;
1325 block
->pn_pos
= body
->pn_pos
;
1326 PN_INIT_LIST_1(block
, body
);
1331 item
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
1335 item
->pn_type
= TOK_SEMI
;
1336 item
->pn_pos
.begin
= item
->pn_pos
.end
= body
->pn_pos
.begin
;
1337 item
->pn_kid
= list
;
1338 item
->pn_next
= body
->pn_head
;
1339 body
->pn_head
= item
;
1340 if (body
->pn_tail
== &body
->pn_head
)
1341 body
->pn_tail
= &item
->pn_next
;
1347 * If we collected flags that indicate nested heavyweight functions, or
1348 * this function contains heavyweight-making statements (references to
1349 * __parent__ or __proto__; use of with, or eval; and assignment to
1350 * arguments), flag the function as heavyweight (requiring a call object
1353 if (funtc
.flags
& TCF_FUN_HEAVYWEIGHT
) {
1354 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
1355 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1358 * If this function is a named statement function not at top-level
1359 * (i.e. not a top-level function definiton or expression), then
1360 * our enclosing function, if any, must be heavyweight.
1362 * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
1363 * so it won't be set here. Assert that it's not. We have to check
1364 * it later, in js_EmitTree, after js_EmitFunctionScript has traversed
1365 * the function's body.
1367 JS_ASSERT(!(funtc
.flags
& TCF_FUN_USES_NONLOCALS
));
1368 if (lambda
== 0 && funAtom
&& !AT_TOP_LEVEL(tc
))
1369 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1375 * ECMA ed. 3 standard: function expression, possibly anonymous.
1377 op
= funAtom
? JSOP_NAMEDFUNOBJ
: JSOP_ANONFUNOBJ
;
1378 } else if (!funAtom
) {
1380 * If this anonymous function definition is *not* embedded within a
1381 * larger expression, we treat it as an expression statement, not as
1382 * a function declaration -- and not as a syntax error (as ECMA-262
1383 * Edition 3 would have it). Backward compatibility must trump all,
1384 * unless JSOPTION_ANONFUNFIX is set.
1386 result
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
1389 result
->pn_type
= TOK_SEMI
;
1390 result
->pn_pos
= pn
->pn_pos
;
1391 result
->pn_kid
= pn
;
1392 op
= JSOP_ANONFUNOBJ
;
1393 } else if (!AT_TOP_LEVEL(tc
)) {
1395 * ECMA ed. 3 extension: a function expression statement not at the
1396 * top level, e.g., in a compound statement such as the "then" part
1397 * of an "if" statement, binds a closure only if control reaches that
1405 pn
->pn_funpob
= funpob
;
1408 pn
->pn_flags
= funtc
.flags
& (TCF_FUN_FLAGS
| TCF_HAS_DEFXMLNS
| TCF_COMPILE_N_GO
);
1409 TREE_CONTEXT_FINISH(cx
, &funtc
);
1413 static JSParseNode
*
1414 FunctionStmt(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1416 return FunctionDef(cx
, ts
, tc
, 0);
1419 static JSParseNode
*
1420 FunctionExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1422 return FunctionDef(cx
, ts
, tc
, JSFUN_LAMBDA
);
1426 * Parse the statements in a block, creating a TOK_LC node that lists the
1427 * statements' trees. If called from block-parsing code, the caller must
1428 * match { before and } after.
1430 static JSParseNode
*
1431 Statements(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1433 JSParseNode
*pn
, *pn2
, *saveBlock
;
1436 JS_CHECK_RECURSION(cx
, return NULL
);
1438 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
1441 saveBlock
= tc
->blockNode
;
1446 ts
->flags
|= TSF_OPERAND
;
1447 tt
= js_PeekToken(cx
, ts
);
1448 ts
->flags
&= ~TSF_OPERAND
;
1449 if (tt
<= TOK_EOF
|| tt
== TOK_RC
) {
1450 if (tt
== TOK_ERROR
)
1454 pn2
= Statement(cx
, ts
, tc
);
1456 if (ts
->flags
& TSF_EOF
)
1457 ts
->flags
|= TSF_UNEXPECTED_EOF
;
1461 if (pn2
->pn_type
== TOK_FUNCTION
) {
1463 * PNX_FUNCDEFS notifies the emitter that the block contains top-
1464 * level function definitions that should be processed before the
1467 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
1468 * is relevant only for function definitions not at top-level,
1469 * which we call function statements.
1471 if (AT_TOP_LEVEL(tc
))
1472 pn
->pn_extra
|= PNX_FUNCDEFS
;
1474 tc
->flags
|= TCF_HAS_FUNCTION_STMT
;
1480 * Handle the case where there was a let declaration under this block. If
1481 * it replaced tc->blockNode with a new block node then we must refresh pn
1482 * and then restore tc->blockNode.
1484 if (tc
->blockNode
!= pn
)
1486 tc
->blockNode
= saveBlock
;
1488 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
1492 static JSParseNode
*
1493 Condition(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
1497 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_COND
);
1498 pn
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
1501 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_COND
);
1504 * Check for (a = b) and warn about possible (a == b) mistype iff b's
1505 * operator has greater precedence than ==.
1507 if (pn
->pn_type
== TOK_ASSIGN
&&
1508 pn
->pn_op
== JSOP_NOP
&&
1509 pn
->pn_right
->pn_type
> TOK_EQOP
)
1511 if (!js_ReportCompileErrorNumber(cx
, ts
, NULL
,
1512 JSREPORT_WARNING
| JSREPORT_STRICT
,
1513 JSMSG_EQUAL_AS_ASSIGN
,
1522 MatchLabel(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
)
1527 tt
= js_PeekTokenSameLine(cx
, ts
);
1528 if (tt
== TOK_ERROR
)
1530 if (tt
== TOK_NAME
) {
1531 (void) js_GetToken(cx
, ts
);
1532 label
= CURRENT_TOKEN(ts
).t_atom
;
1536 pn
->pn_atom
= label
;
1541 BindLet(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
1544 JSScopeProperty
*sprop
;
1545 JSAtomListElement
*ale
;
1548 blockObj
= tc
->blockChain
;
1549 sprop
= SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj
), ATOM_TO_JSID(atom
));
1550 ATOM_LIST_SEARCH(ale
, &tc
->decls
, atom
);
1551 if (sprop
|| (ale
&& ALE_JSOP(ale
) == JSOP_DEFCONST
)) {
1555 JS_ASSERT(sprop
->flags
& SPROP_HAS_SHORTID
);
1556 JS_ASSERT((uint16
)sprop
->shortid
< OBJ_BLOCK_COUNT(cx
, blockObj
));
1559 name
= js_AtomToPrintableString(cx
, atom
);
1561 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), data
->pn
,
1562 JSREPORT_ERROR
, JSMSG_REDECLARED_VAR
,
1563 (ale
&& ALE_JSOP(ale
) == JSOP_DEFCONST
)
1571 n
= OBJ_BLOCK_COUNT(cx
, blockObj
);
1572 if (n
== JS_BIT(16)) {
1573 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), data
->pn
,
1574 JSREPORT_ERROR
, data
->u
.let
.overflow
);
1578 /* Use JSPROP_ENUMERATE to aid the disassembler. */
1579 return js_DefineNativeProperty(cx
, blockObj
, ATOM_TO_JSID(atom
),
1580 JSVAL_VOID
, NULL
, NULL
,
1584 SPROP_HAS_SHORTID
, (int16
) n
, NULL
);
1588 BindVarOrConst(JSContext
*cx
, BindData
*data
, JSAtom
*atom
, JSTreeContext
*tc
)
1591 JSAtomListElement
*ale
;
1594 JSLocalKind localKind
;
1596 stmt
= js_LexicalLookup(tc
, atom
, NULL
);
1597 ATOM_LIST_SEARCH(ale
, &tc
->decls
, atom
);
1599 if ((stmt
&& stmt
->type
!= STMT_WITH
) || ale
) {
1600 prevop
= ale
? ALE_JSOP(ale
) : JSOP_DEFVAR
;
1601 if (JS_HAS_STRICT_OPTION(cx
)
1602 ? op
!= JSOP_DEFVAR
|| prevop
!= JSOP_DEFVAR
1603 : op
== JSOP_DEFCONST
|| prevop
== JSOP_DEFCONST
) {
1604 name
= js_AtomToPrintableString(cx
, atom
);
1606 !js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), data
->pn
,
1607 (op
!= JSOP_DEFCONST
&&
1608 prevop
!= JSOP_DEFCONST
)
1609 ? JSREPORT_WARNING
|
1612 JSMSG_REDECLARED_VAR
,
1613 (prevop
== JSOP_DEFFUN
)
1615 : (prevop
== JSOP_DEFCONST
)
1622 if (op
== JSOP_DEFVAR
&& prevop
== JSOP_DEFFUN
)
1623 tc
->flags
|= TCF_FUN_CLOSURE_VS_VAR
;
1626 ale
= js_IndexAtom(cx
, atom
, &tc
->decls
);
1630 ALE_SET_JSOP(ale
, op
);
1632 if (!(tc
->flags
& TCF_IN_FUNCTION
)) {
1634 * Don't lookup global variables or variables in an active frame at
1640 localKind
= js_LookupLocal(cx
, tc
->u
.fun
, atom
, NULL
);
1641 if (localKind
== JSLOCAL_NONE
) {
1643 * Property not found in current variable scope: we have not seen this
1644 * variable before. Define a new local variable by adding a property
1645 * to the function's scope, allocating one slot in the function's vars
1646 * frame. Any locals declared in with statement bodies are handled at
1647 * runtime, by script prolog JSOP_DEFVAR opcodes generated for
1650 localKind
= (data
->op
== JSOP_DEFCONST
) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
1651 if (!js_InWithStatement(tc
) &&
1652 !BindLocalVariable(cx
, tc
->u
.fun
, atom
, localKind
)) {
1655 } else if (localKind
== JSLOCAL_ARG
) {
1656 name
= js_AtomToPrintableString(cx
, atom
);
1660 if (op
== JSOP_DEFCONST
) {
1661 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), data
->pn
,
1662 JSREPORT_ERROR
, JSMSG_REDECLARED_PARAM
,
1666 if (!js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), data
->pn
,
1667 JSREPORT_WARNING
| JSREPORT_STRICT
,
1668 JSMSG_VAR_HIDES_ARG
, name
)) {
1672 /* Not an argument, must be a redeclared local var. */
1673 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1679 MakeSetCall(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, uintN msg
)
1683 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
1684 JS_ASSERT(pn
->pn_op
== JSOP_CALL
|| pn
->pn_op
== JSOP_EVAL
|| pn
->pn_op
== JSOP_APPLY
);
1686 if (pn2
->pn_type
== TOK_FUNCTION
&& (pn2
->pn_flags
& TCF_GENEXP_LAMBDA
)) {
1687 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), pn
,
1688 JSREPORT_ERROR
, msg
);
1691 pn
->pn_op
= JSOP_SETCALL
;
1695 #if JS_HAS_DESTRUCTURING
1698 BindDestructuringVar(JSContext
*cx
, BindData
*data
, JSParseNode
*pn
,
1704 * Destructuring is a form of assignment, so just as for an initialized
1705 * simple variable, we must check for assignment to 'arguments' and flag
1706 * the enclosing function (if any) as heavyweight.
1708 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
1710 if (atom
== cx
->runtime
->atomState
.argumentsAtom
)
1711 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1714 if (!data
->binder(cx
, data
, atom
, tc
))
1719 * Select the appropriate name-setting opcode, which may be specialized
1720 * further for local variable and argument slot optimizations. At this
1721 * point, we can't select the optimal final opcode, yet we must preserve
1722 * the CONST bit and convey "set", not "get".
1724 if (data
->op
== JSOP_DEFCONST
) {
1725 pn
->pn_op
= JSOP_SETCONST
;
1726 pn
->pn_const
= JS_TRUE
;
1728 pn
->pn_op
= JSOP_SETNAME
;
1729 pn
->pn_const
= JS_FALSE
;
1735 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
1736 * LHS expression except a destructuring initialiser, and R is on the stack.
1737 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
1738 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
1739 * then push its property name QN. At this point the stack looks like
1741 * [... R, R[P], QB, QN]
1743 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
1744 * its operands with left-hand side above right-hand side:
1746 * [rval, lval, xval]
1748 * and pops all three values, setting lval[xval] = rval. But we cannot select
1749 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
1750 * which can be optimized further. So we select JSOP_SETNAME.
1753 BindDestructuringLHS(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
1755 while (pn
->pn_type
== TOK_RP
)
1758 switch (pn
->pn_type
) {
1760 if (pn
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
1761 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
1765 pn
->pn_op
= JSOP_SETNAME
;
1768 #if JS_HAS_LVALUE_RETURN
1770 if (!MakeSetCall(cx
, pn
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
1775 #if JS_HAS_XML_SUPPORT
1777 if (pn
->pn_op
== JSOP_XMLNAME
) {
1778 pn
->pn_op
= JSOP_BINDXMLNAME
;
1785 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), pn
,
1786 JSREPORT_ERROR
, JSMSG_BAD_LEFTSIDE_OF_ASS
);
1793 typedef struct FindPropValData
{
1794 uint32 numvars
; /* # of destructuring vars in left side */
1795 uint32 maxstep
; /* max # of steps searching right side */
1796 JSDHashTable table
; /* hash table for O(1) right side search */
1799 typedef struct FindPropValEntry
{
1800 JSDHashEntryHdr hdr
;
1805 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
1806 JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
1807 ((pnkey)->pn_type == TOK_NUMBER || \
1808 (pnkey)->pn_type == TOK_STRING || \
1809 (pnkey)->pn_type == TOK_NAME))
1811 static JSDHashNumber
1812 HashFindPropValKey(JSDHashTable
*table
, const void *key
)
1814 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
1816 ASSERT_VALID_PROPERTY_KEY(pnkey
);
1817 return (pnkey
->pn_type
== TOK_NUMBER
)
1818 ? (JSDHashNumber
) (JSDOUBLE_HI32(pnkey
->pn_dval
) ^
1819 JSDOUBLE_LO32(pnkey
->pn_dval
))
1820 : ATOM_HASH(pnkey
->pn_atom
);
1824 MatchFindPropValEntry(JSDHashTable
*table
,
1825 const JSDHashEntryHdr
*entry
,
1828 const FindPropValEntry
*fpve
= (const FindPropValEntry
*)entry
;
1829 const JSParseNode
*pnkey
= (const JSParseNode
*)key
;
1831 ASSERT_VALID_PROPERTY_KEY(pnkey
);
1832 return pnkey
->pn_type
== fpve
->pnkey
->pn_type
&&
1833 ((pnkey
->pn_type
== TOK_NUMBER
)
1834 ? pnkey
->pn_dval
== fpve
->pnkey
->pn_dval
1835 : pnkey
->pn_atom
== fpve
->pnkey
->pn_atom
);
1838 static const JSDHashTableOps FindPropValOps
= {
1842 MatchFindPropValEntry
,
1843 JS_DHashMoveEntryStub
,
1844 JS_DHashClearEntryStub
,
1845 JS_DHashFinalizeStub
,
1849 #define STEP_HASH_THRESHOLD 10
1850 #define BIG_DESTRUCTURING 5
1851 #define BIG_OBJECT_INIT 20
1853 static JSParseNode
*
1854 FindPropertyValue(JSParseNode
*pn
, JSParseNode
*pnid
, FindPropValData
*data
)
1856 FindPropValEntry
*entry
;
1857 JSParseNode
*pnhit
, *pnhead
, *pnprop
, *pnkey
;
1860 /* If we have a hash table, use it as the sole source of truth. */
1861 if (data
->table
.ops
) {
1862 entry
= (FindPropValEntry
*)
1863 JS_DHashTableOperate(&data
->table
, pnid
, JS_DHASH_LOOKUP
);
1864 return JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
) ? entry
->pnval
: NULL
;
1867 /* If pn is not an object initialiser node, we can't do anything here. */
1868 if (pn
->pn_type
!= TOK_RC
)
1872 * We must search all the way through pn's list, to handle the case of an
1873 * id duplicated for two or more property initialisers.
1877 ASSERT_VALID_PROPERTY_KEY(pnid
);
1878 pnhead
= pn
->pn_head
;
1879 if (pnhead
&& pnhead
->pn_type
== TOK_DEFSHARP
)
1880 pnhead
= pnhead
->pn_next
;
1881 if (pnid
->pn_type
== TOK_NUMBER
) {
1882 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
1883 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
1884 if (pnprop
->pn_op
== JSOP_NOP
) {
1885 pnkey
= pnprop
->pn_left
;
1886 ASSERT_VALID_PROPERTY_KEY(pnkey
);
1887 if (pnkey
->pn_type
== TOK_NUMBER
&&
1888 pnkey
->pn_dval
== pnid
->pn_dval
) {
1895 for (pnprop
= pnhead
; pnprop
; pnprop
= pnprop
->pn_next
) {
1896 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
1897 if (pnprop
->pn_op
== JSOP_NOP
) {
1898 pnkey
= pnprop
->pn_left
;
1899 ASSERT_VALID_PROPERTY_KEY(pnkey
);
1900 if (pnkey
->pn_type
== pnid
->pn_type
&&
1901 pnkey
->pn_atom
== pnid
->pn_atom
) {
1911 /* Hit via full search -- see whether it's time to create the hash table. */
1912 JS_ASSERT(!data
->table
.ops
);
1913 if (step
> data
->maxstep
) {
1914 data
->maxstep
= step
;
1915 if (step
>= STEP_HASH_THRESHOLD
&&
1916 data
->numvars
>= BIG_DESTRUCTURING
&&
1917 pn
->pn_count
>= BIG_OBJECT_INIT
&&
1918 JS_DHashTableInit(&data
->table
, &FindPropValOps
, pn
,
1919 sizeof(FindPropValEntry
),
1920 JS_DHASH_DEFAULT_CAPACITY(pn
->pn_count
)))
1922 for (pn
= pnhead
; pn
; pn
= pn
->pn_next
) {
1923 JS_ASSERT(pnprop
->pn_type
== TOK_COLON
);
1924 ASSERT_VALID_PROPERTY_KEY(pn
->pn_left
);
1925 entry
= (FindPropValEntry
*)
1926 JS_DHashTableOperate(&data
->table
, pn
->pn_left
,
1928 entry
->pnval
= pn
->pn_right
;
1932 return pnhit
->pn_right
;
1936 * If data is null, the caller is AssignExpr and instead of binding variables,
1937 * we specialize lvalues in the propery value positions of the left-hand side.
1938 * If right is null, just check for well-formed lvalues.
1941 CheckDestructuring(JSContext
*cx
, BindData
*data
,
1942 JSParseNode
*left
, JSParseNode
*right
,
1946 FindPropValData fpvd
;
1947 JSParseNode
*lhs
, *rhs
, *pn
, *pn2
;
1949 if (left
->pn_type
== TOK_ARRAYCOMP
) {
1950 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), left
,
1951 JSREPORT_ERROR
, JSMSG_ARRAY_COMP_LEFTSIDE
);
1955 #if JS_HAS_DESTRUCTURING_SHORTHAND
1956 if (right
&& right
->pn_arity
== PN_LIST
&& (right
->pn_extra
& PNX_SHORTHAND
)) {
1957 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), right
,
1958 JSREPORT_ERROR
, JSMSG_BAD_OBJECT_INIT
);
1963 fpvd
.table
.ops
= NULL
;
1964 lhs
= left
->pn_head
;
1965 if (lhs
&& lhs
->pn_type
== TOK_DEFSHARP
) {
1970 if (left
->pn_type
== TOK_RB
) {
1971 rhs
= (right
&& right
->pn_type
== left
->pn_type
)
1976 pn
= lhs
, pn2
= rhs
;
1978 /* Skip parenthesization if not in a variable declaration. */
1979 while (pn
->pn_type
== TOK_RP
)
1982 while (pn2
->pn_type
== TOK_RP
)
1987 /* Nullary comma is an elision; binary comma is an expression.*/
1988 if (pn
->pn_type
!= TOK_COMMA
|| pn
->pn_arity
!= PN_NULLARY
) {
1989 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
1990 ok
= CheckDestructuring(cx
, data
, pn
, pn2
, tc
);
1993 if (pn
->pn_type
!= TOK_NAME
)
1996 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
1998 ok
= BindDestructuringLHS(cx
, pn
, tc
);
2010 JS_ASSERT(left
->pn_type
== TOK_RC
);
2011 fpvd
.numvars
= left
->pn_count
;
2016 JS_ASSERT(lhs
->pn_type
== TOK_COLON
);
2019 /* Skip parenthesization if not in a variable declaration. */
2020 while (pn
->pn_type
== TOK_RP
)
2024 if (pn
->pn_type
== TOK_RB
|| pn
->pn_type
== TOK_RC
) {
2026 rhs
= FindPropertyValue(right
, lhs
->pn_left
, &fpvd
);
2028 while (rhs
->pn_type
== TOK_RP
)
2033 ok
= CheckDestructuring(cx
, data
, pn
, rhs
, tc
);
2035 if (pn
->pn_type
!= TOK_NAME
)
2038 ok
= BindDestructuringVar(cx
, data
, pn
, tc
);
2040 ok
= BindDestructuringLHS(cx
, pn
, tc
);
2050 * The catch/finally handler implementation in the interpreter assumes
2051 * that any operation that introduces a new scope (like a "let" or "with"
2052 * block) increases the stack depth. This way, it is possible to restore
2053 * the scope chain based on stack depth of the handler alone. "let" with
2054 * an empty destructuring pattern like in
2058 * would violate this assumption as the there would be no let locals to
2059 * store on the stack. To satisfy it we add an empty property to such
2060 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
2061 * slots, would be always positive.
2063 * Note that we add such a property even if the block has locals due to
2064 * later let declarations in it. We optimize for code simplicity here,
2065 * not the fastest runtime performance with empty [] or {}.
2068 data
->binder
== BindLet
&&
2069 OBJ_BLOCK_COUNT(cx
, tc
->blockChain
) == 0) {
2070 ok
= js_DefineNativeProperty(cx
, tc
->blockChain
,
2071 ATOM_TO_JSID(cx
->runtime
->
2072 atomState
.emptyAtom
),
2073 JSVAL_VOID
, NULL
, NULL
,
2077 SPROP_HAS_SHORTID
, 0, NULL
);
2086 JS_DHashTableFinish(&fpvd
.table
);
2090 js_ReportCompileErrorNumber(cx
, TS(tc
->parseContext
), pn
, JSREPORT_ERROR
,
2091 JSMSG_NO_VARIABLE_NAME
);
2096 static JSParseNode
*
2097 DestructuringExpr(JSContext
*cx
, BindData
*data
, JSTreeContext
*tc
,
2102 pn
= PrimaryExpr(cx
, TS(tc
->parseContext
), tc
, tt
, JS_FALSE
);
2105 if (!CheckDestructuring(cx
, data
, pn
, NULL
, tc
))
2110 // Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
2111 static JSParseNode
*
2112 CloneParseTree(JSContext
*cx
, JSParseNode
*opn
, JSTreeContext
*tc
)
2114 JSParseNode
*pn
, *pn2
, *opn2
;
2116 pn
= NewOrRecycledNode(cx
, tc
);
2119 pn
->pn_type
= opn
->pn_type
;
2120 pn
->pn_pos
= opn
->pn_pos
;
2121 pn
->pn_op
= opn
->pn_op
;
2122 pn
->pn_arity
= opn
->pn_arity
;
2124 switch (pn
->pn_arity
) {
2125 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
2128 NULLCHECK(pn
->pn_funpob
=
2129 js_NewParsedObjectBox(cx
, tc
->parseContext
, opn
->pn_funpob
->object
));
2130 NULLCHECK(pn
->pn_body
= CloneParseTree(cx
, opn
->pn_body
, tc
));
2131 pn
->pn_flags
= opn
->pn_flags
;
2132 pn
->pn_index
= opn
->pn_index
;
2137 for (opn2
= opn
->pn_head
; opn2
; opn2
= opn2
->pn_next
) {
2138 NULLCHECK(pn2
= CloneParseTree(cx
, opn2
, tc
));
2141 pn
->pn_extra
= opn
->pn_extra
;
2145 NULLCHECK(pn
->pn_kid1
= CloneParseTree(cx
, opn
->pn_kid1
, tc
));
2146 NULLCHECK(pn
->pn_kid2
= CloneParseTree(cx
, opn
->pn_kid2
, tc
));
2147 NULLCHECK(pn
->pn_kid3
= CloneParseTree(cx
, opn
->pn_kid3
, tc
));
2151 NULLCHECK(pn
->pn_left
= CloneParseTree(cx
, opn
->pn_left
, tc
));
2152 if (opn
->pn_right
!= opn
->pn_left
)
2153 NULLCHECK(pn
->pn_right
= CloneParseTree(cx
, opn
->pn_right
, tc
));
2155 pn
->pn_right
= pn
->pn_left
;
2156 pn
->pn_val
= opn
->pn_val
;
2157 pn
->pn_iflags
= opn
->pn_iflags
;
2161 NULLCHECK(pn
->pn_kid
= CloneParseTree(cx
, opn
->pn_kid
, tc
));
2162 pn
->pn_num
= opn
->pn_num
;
2163 pn
->pn_hidden
= opn
->pn_hidden
;
2167 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
2168 pn
->pn_u
= opn
->pn_u
;
2170 NULLCHECK(pn
->pn_expr
= CloneParseTree(cx
, opn
->pn_expr
, tc
));
2174 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
2175 pn
->pn_u
= opn
->pn_u
;
2183 #endif /* JS_HAS_DESTRUCTURING */
2185 extern const char js_with_statement_str
[];
2187 static JSParseNode
*
2188 ContainsStmt(JSParseNode
*pn
, JSTokenType tt
)
2190 JSParseNode
*pn2
, *pnt
;
2194 if (pn
->pn_type
== tt
)
2196 switch (pn
->pn_arity
) {
2198 for (pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
2199 pnt
= ContainsStmt(pn2
, tt
);
2205 pnt
= ContainsStmt(pn
->pn_kid1
, tt
);
2208 pnt
= ContainsStmt(pn
->pn_kid2
, tt
);
2211 return ContainsStmt(pn
->pn_kid3
, tt
);
2214 * Limit recursion if pn is a binary expression, which can't contain a
2217 if (pn
->pn_op
!= JSOP_NOP
)
2219 pnt
= ContainsStmt(pn
->pn_left
, tt
);
2222 return ContainsStmt(pn
->pn_right
, tt
);
2224 if (pn
->pn_op
!= JSOP_NOP
)
2226 return ContainsStmt(pn
->pn_kid
, tt
);
2228 return ContainsStmt(pn
->pn_expr
, tt
);
2234 static JSParseNode
*
2235 ReturnOrYield(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2236 JSParser operandParser
)
2238 JSTokenType tt
, tt2
;
2239 JSParseNode
*pn
, *pn2
;
2241 tt
= CURRENT_TOKEN(ts
).type
;
2242 if (tt
== TOK_RETURN
&& !(tc
->flags
& TCF_IN_FUNCTION
)) {
2243 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2244 JSMSG_BAD_RETURN_OR_YIELD
, js_return_str
);
2248 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
2252 #if JS_HAS_GENERATORS
2253 if (tt
== TOK_YIELD
)
2254 tc
->flags
|= TCF_FUN_IS_GENERATOR
;
2257 /* This is ugly, but we don't want to require a semicolon. */
2258 ts
->flags
|= TSF_OPERAND
;
2259 tt2
= js_PeekTokenSameLine(cx
, ts
);
2260 ts
->flags
&= ~TSF_OPERAND
;
2261 if (tt2
== TOK_ERROR
)
2264 if (tt2
!= TOK_EOF
&& tt2
!= TOK_EOL
&& tt2
!= TOK_SEMI
&& tt2
!= TOK_RC
2265 #if JS_HAS_GENERATORS
2266 && (tt
!= TOK_YIELD
||
2267 (tt2
!= tt
&& tt2
!= TOK_RB
&& tt2
!= TOK_RP
&&
2268 tt2
!= TOK_COLON
&& tt2
!= TOK_COMMA
))
2271 pn2
= operandParser(cx
, ts
, tc
);
2274 #if JS_HAS_GENERATORS
2275 if (tt
== TOK_RETURN
)
2277 tc
->flags
|= TCF_RETURN_EXPR
;
2278 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2281 #if JS_HAS_GENERATORS
2282 if (tt
== TOK_RETURN
)
2284 tc
->flags
|= TCF_RETURN_VOID
;
2287 if ((~tc
->flags
& (TCF_RETURN_EXPR
| TCF_FUN_IS_GENERATOR
)) == 0) {
2288 /* As in Python (see PEP-255), disallow return v; in generators. */
2289 ReportBadReturn(cx
, tc
, JSREPORT_ERROR
,
2290 JSMSG_BAD_GENERATOR_RETURN
,
2291 JSMSG_BAD_ANON_GENERATOR_RETURN
);
2295 if (JS_HAS_STRICT_OPTION(cx
) &&
2296 (~tc
->flags
& (TCF_RETURN_EXPR
| TCF_RETURN_VOID
)) == 0 &&
2297 !ReportBadReturn(cx
, tc
, JSREPORT_WARNING
| JSREPORT_STRICT
,
2298 JSMSG_NO_RETURN_VALUE
,
2299 JSMSG_ANON_NO_RETURN_VALUE
)) {
2306 static JSParseNode
*
2307 PushLexicalScope(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
2308 JSStmtInfo
*stmtInfo
)
2312 JSParsedObjectBox
*blockpob
;
2314 pn
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
2318 obj
= js_NewBlockObject(cx
);
2322 blockpob
= js_NewParsedObjectBox(cx
, tc
->parseContext
, obj
);
2326 js_PushBlockScope(tc
, stmtInfo
, obj
, -1);
2327 pn
->pn_type
= TOK_LEXICALSCOPE
;
2328 pn
->pn_op
= JSOP_LEAVEBLOCK
;
2329 pn
->pn_pob
= blockpob
;
2334 #if JS_HAS_BLOCK_SCOPE
2336 static JSParseNode
*
2337 LetBlock(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
, JSBool statement
)
2339 JSParseNode
*pn
, *pnblock
, *pnlet
;
2340 JSStmtInfo stmtInfo
;
2342 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LET
);
2344 /* Create the let binary node. */
2345 pnlet
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
2349 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_LET
);
2351 /* This is a let block or expression of the form: let (a, b, c) .... */
2352 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
2356 pn
->pn_expr
= pnlet
;
2358 pnlet
->pn_left
= Variables(cx
, ts
, tc
);
2359 if (!pnlet
->pn_left
)
2361 pnlet
->pn_left
->pn_extra
= PNX_POPVAR
;
2363 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_LET
);
2365 ts
->flags
|= TSF_OPERAND
;
2366 if (statement
&& !js_MatchToken(cx
, ts
, TOK_LC
)) {
2368 * If this is really an expression in let statement guise, then we
2369 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
2370 * the return value of the expression.
2372 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
2375 pn
->pn_type
= TOK_SEMI
;
2377 pn
->pn_kid
= pnblock
;
2379 statement
= JS_FALSE
;
2381 ts
->flags
&= ~TSF_OPERAND
;
2384 pnlet
->pn_right
= Statements(cx
, ts
, tc
);
2385 if (!pnlet
->pn_right
)
2387 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_LET
);
2390 * Change pnblock's opcode to the variant that propagates the last
2391 * result down after popping the block, and clear statement.
2393 pnblock
->pn_op
= JSOP_LEAVEBLOCKEXPR
;
2394 pnlet
->pn_right
= AssignExpr(cx
, ts
, tc
);
2395 if (!pnlet
->pn_right
)
2399 js_PopStatement(tc
);
2403 #endif /* JS_HAS_BLOCK_SCOPE */
2405 static JSParseNode
*
2406 Statement(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
2409 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
, *pn4
;
2410 JSStmtInfo stmtInfo
, *stmt
, *stmt2
;
2413 JS_CHECK_RECURSION(cx
, return NULL
);
2415 ts
->flags
|= TSF_OPERAND
;
2416 tt
= js_GetToken(cx
, ts
);
2417 ts
->flags
&= ~TSF_OPERAND
;
2419 #if JS_HAS_GETTER_SETTER
2420 if (tt
== TOK_NAME
) {
2421 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
2422 if (tt
== TOK_ERROR
)
2429 #if JS_HAS_XML_SUPPORT
2430 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
2431 tt
= js_PeekToken(cx
, ts
);
2432 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
2433 if (tt
== TOK_DBLCOLON
)
2436 return FunctionStmt(cx
, ts
, tc
);
2439 /* An IF node has three kids: condition, then, and optional else. */
2440 pn
= NewParseNode(cx
, ts
, PN_TERNARY
, tc
);
2443 pn1
= Condition(cx
, ts
, tc
);
2446 js_PushStatement(tc
, &stmtInfo
, STMT_IF
, -1);
2447 pn2
= Statement(cx
, ts
, tc
);
2450 ts
->flags
|= TSF_OPERAND
;
2451 if (js_MatchToken(cx
, ts
, TOK_ELSE
)) {
2452 ts
->flags
&= ~TSF_OPERAND
;
2453 stmtInfo
.type
= STMT_ELSE
;
2454 pn3
= Statement(cx
, ts
, tc
);
2457 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
2459 ts
->flags
&= ~TSF_OPERAND
;
2461 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2463 js_PopStatement(tc
);
2471 JSParseNode
*pn5
, *saveBlock
;
2472 JSBool seenDefault
= JS_FALSE
;
2474 pn
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
2477 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_SWITCH
);
2479 /* pn1 points to the switch's discriminant. */
2480 pn1
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
2484 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_SWITCH
);
2485 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_SWITCH
);
2487 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
2488 pn2
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
2491 saveBlock
= tc
->blockNode
;
2492 tc
->blockNode
= pn2
;
2495 js_PushStatement(tc
, &stmtInfo
, STMT_SWITCH
, -1);
2497 while ((tt
= js_GetToken(cx
, ts
)) != TOK_RC
) {
2501 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2502 JSMSG_TOO_MANY_DEFAULTS
);
2505 seenDefault
= JS_TRUE
;
2509 pn3
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
2512 if (tt
== TOK_CASE
) {
2513 pn3
->pn_left
= Expr(cx
, ts
, tc
);
2517 PN_APPEND(pn2
, pn3
);
2518 if (pn2
->pn_count
== JS_BIT(16)) {
2519 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2520 JSMSG_TOO_MANY_CASES
);
2529 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
2533 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_AFTER_CASE
);
2535 pn4
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
2538 pn4
->pn_type
= TOK_LC
;
2540 ts
->flags
|= TSF_OPERAND
;
2541 while ((tt
= js_PeekToken(cx
, ts
)) != TOK_RC
&&
2542 tt
!= TOK_CASE
&& tt
!= TOK_DEFAULT
) {
2543 ts
->flags
&= ~TSF_OPERAND
;
2544 if (tt
== TOK_ERROR
)
2546 pn5
= Statement(cx
, ts
, tc
);
2549 pn4
->pn_pos
.end
= pn5
->pn_pos
.end
;
2550 PN_APPEND(pn4
, pn5
);
2551 ts
->flags
|= TSF_OPERAND
;
2553 ts
->flags
&= ~TSF_OPERAND
;
2555 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
2557 pn4
->pn_pos
.begin
= pn4
->pn_head
->pn_pos
.begin
;
2558 pn3
->pn_pos
.end
= pn4
->pn_pos
.end
;
2559 pn3
->pn_right
= pn4
;
2563 * Handle the case where there was a let declaration in any case in
2564 * the switch body, but not within an inner block. If it replaced
2565 * tc->blockNode with a new block node then we must refresh pn2 and
2566 * then restore tc->blockNode.
2568 if (tc
->blockNode
!= pn2
)
2569 pn2
= tc
->blockNode
;
2570 tc
->blockNode
= saveBlock
;
2571 js_PopStatement(tc
);
2573 pn
->pn_pos
.end
= pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
2580 pn
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
2583 js_PushStatement(tc
, &stmtInfo
, STMT_WHILE_LOOP
, -1);
2584 pn2
= Condition(cx
, ts
, tc
);
2588 pn2
= Statement(cx
, ts
, tc
);
2591 js_PopStatement(tc
);
2592 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2597 pn
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
2600 js_PushStatement(tc
, &stmtInfo
, STMT_DO_LOOP
, -1);
2601 pn2
= Statement(cx
, ts
, tc
);
2605 MUST_MATCH_TOKEN(TOK_WHILE
, JSMSG_WHILE_AFTER_DO
);
2606 pn2
= Condition(cx
, ts
, tc
);
2609 js_PopStatement(tc
);
2610 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2612 if (JSVERSION_NUMBER(cx
) != JSVERSION_ECMA_3
) {
2614 * All legacy and extended versions must do automatic semicolon
2615 * insertion after do-while. See the testcase and discussion in
2616 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
2618 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
2625 JSParseNode
*pnseq
= NULL
;
2626 #if JS_HAS_BLOCK_SCOPE
2627 JSParseNode
*pnlet
= NULL
;
2628 JSStmtInfo blockInfo
;
2631 /* A FOR node is binary, left is loop control and right is the body. */
2632 pn
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
2635 js_PushStatement(tc
, &stmtInfo
, STMT_FOR_LOOP
, -1);
2637 pn
->pn_op
= JSOP_ITER
;
2639 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
2640 if (CURRENT_TOKEN(ts
).t_atom
== cx
->runtime
->atomState
.eachAtom
)
2641 pn
->pn_iflags
= JSITER_FOREACH
;
2646 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
2647 ts
->flags
|= TSF_OPERAND
;
2648 tt
= js_PeekToken(cx
, ts
);
2649 ts
->flags
&= ~TSF_OPERAND
;
2650 if (tt
== TOK_SEMI
) {
2651 if (pn
->pn_iflags
& JSITER_FOREACH
)
2654 /* No initializer -- set first kid of left sub-node to null. */
2658 * Set pn1 to a var list or an initializing expression.
2660 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
2661 * of the for statement. This flag will be used by the RelExpr
2662 * production; if it is set, then the 'in' keyword will not be
2663 * recognized as an operator, leaving it available to be parsed as
2664 * part of a for/in loop.
2666 * A side effect of this restriction is that (unparenthesized)
2667 * expressions involving an 'in' operator are illegal in the init
2668 * clause of an ordinary for loop.
2670 tc
->flags
|= TCF_IN_FOR_INIT
;
2671 if (tt
== TOK_VAR
) {
2672 (void) js_GetToken(cx
, ts
);
2673 pn1
= Variables(cx
, ts
, tc
);
2674 #if JS_HAS_BLOCK_SCOPE
2675 } else if (tt
== TOK_LET
) {
2676 (void) js_GetToken(cx
, ts
);
2677 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
2678 pn1
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
2679 tt
= TOK_LEXICALSCOPE
;
2681 pnlet
= PushLexicalScope(cx
, ts
, tc
, &blockInfo
);
2684 blockInfo
.flags
|= SIF_FOR_BLOCK
;
2685 pn1
= Variables(cx
, ts
, tc
);
2689 pn1
= Expr(cx
, ts
, tc
);
2691 while (pn1
->pn_type
== TOK_RP
)
2695 tc
->flags
&= ~TCF_IN_FOR_INIT
;
2701 * We can be sure that it's a for/in loop if there's still an 'in'
2702 * keyword here, even if JavaScript recognizes 'in' as an operator,
2703 * as we've excluded 'in' from being parsed in RelExpr by setting
2704 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
2706 if (pn1
&& js_MatchToken(cx
, ts
, TOK_IN
)) {
2707 pn
->pn_iflags
|= JSITER_ENUMERATE
;
2708 stmtInfo
.type
= STMT_FOR_IN_LOOP
;
2710 /* Check that the left side of the 'in' is valid. */
2711 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt
) || pn1
->pn_type
== tt
);
2712 if (TOKEN_TYPE_IS_DECL(tt
)
2713 ? (pn1
->pn_count
> 1 || pn1
->pn_op
== JSOP_DEFCONST
2714 #if JS_HAS_DESTRUCTURING
2715 || (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
2716 pn
->pn_op
== JSOP_ITER
&&
2717 !(pn
->pn_iflags
& JSITER_FOREACH
) &&
2718 (pn1
->pn_head
->pn_type
== TOK_RC
||
2719 (pn1
->pn_head
->pn_type
== TOK_RB
&&
2720 pn1
->pn_head
->pn_count
!= 2) ||
2721 (pn1
->pn_head
->pn_type
== TOK_ASSIGN
&&
2722 (pn1
->pn_head
->pn_left
->pn_type
!= TOK_RB
||
2723 pn1
->pn_head
->pn_left
->pn_count
!= 2))))
2726 : (pn1
->pn_type
!= TOK_NAME
&&
2727 pn1
->pn_type
!= TOK_DOT
&&
2728 #if JS_HAS_DESTRUCTURING
2729 ((JSVERSION_NUMBER(cx
) == JSVERSION_1_7
&&
2730 pn
->pn_op
== JSOP_ITER
&&
2731 !(pn
->pn_iflags
& JSITER_FOREACH
))
2732 ? (pn1
->pn_type
!= TOK_RB
|| pn1
->pn_count
!= 2)
2733 : (pn1
->pn_type
!= TOK_RB
&& pn1
->pn_type
!= TOK_RC
)) &&
2735 #if JS_HAS_LVALUE_RETURN
2736 pn1
->pn_type
!= TOK_LP
&&
2738 #if JS_HAS_XML_SUPPORT
2739 (pn1
->pn_type
!= TOK_UNARYOP
||
2740 pn1
->pn_op
!= JSOP_XMLNAME
) &&
2742 pn1
->pn_type
!= TOK_LB
)) {
2743 js_ReportCompileErrorNumber(cx
, ts
, pn1
, JSREPORT_ERROR
,
2744 JSMSG_BAD_FOR_LEFTSIDE
);
2748 /* pn2 points to the name or destructuring pattern on in's left. */
2751 if (TOKEN_TYPE_IS_DECL(tt
)) {
2752 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
2753 pn1
->pn_extra
|= PNX_FORINVAR
;
2756 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
2757 * 'var', or 'const' to hoist the initializer or the entire
2758 * decl out of the loop head. TOK_VAR is the type for both
2759 * 'var' and 'const'.
2762 if (pn2
->pn_type
== TOK_NAME
&& pn2
->pn_expr
2763 #if JS_HAS_DESTRUCTURING
2764 || pn2
->pn_type
== TOK_ASSIGN
2767 pnseq
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
2770 pnseq
->pn_type
= TOK_SEQ
;
2771 pnseq
->pn_pos
.begin
= pn
->pn_pos
.begin
;
2772 if (tt
== TOK_LET
) {
2774 * Hoist just the 'i' from 'for (let x = i in o)' to
2775 * before the loop, glued together via pnseq.
2777 pn3
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
2780 pn3
->pn_type
= TOK_SEMI
;
2781 pn3
->pn_op
= JSOP_NOP
;
2782 #if JS_HAS_DESTRUCTURING
2783 if (pn2
->pn_type
== TOK_ASSIGN
) {
2784 pn4
= pn2
->pn_right
;
2785 pn2
= pn1
->pn_head
= pn2
->pn_left
;
2790 pn2
->pn_expr
= NULL
;
2792 pn3
->pn_pos
= pn4
->pn_pos
;
2794 PN_INIT_LIST_1(pnseq
, pn3
);
2797 * All of 'var x = i' is hoisted above 'for (x in o)',
2798 * so clear PNX_FORINVAR.
2800 * Request JSOP_POP here since the var is for a simple
2801 * name (it is not a destructuring binding's left-hand
2802 * side) and it has an initializer.
2804 pn1
->pn_extra
&= ~PNX_FORINVAR
;
2805 pn1
->pn_extra
|= PNX_POPVAR
;
2806 PN_INIT_LIST_1(pnseq
, pn1
);
2808 #if JS_HAS_DESTRUCTURING
2809 if (pn2
->pn_type
== TOK_ASSIGN
) {
2810 pn1
= CloneParseTree(cx
, pn2
->pn_left
, tc
);
2816 pn1
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
2819 pn1
->pn_type
= TOK_NAME
;
2820 pn1
->pn_op
= JSOP_NAME
;
2821 pn1
->pn_pos
= pn2
->pn_pos
;
2822 pn1
->pn_atom
= pn2
->pn_atom
;
2823 pn1
->pn_expr
= NULL
;
2825 pn1
->pn_const
= pn2
->pn_const
;
2834 #if JS_HAS_LVALUE_RETURN
2835 if (pn2
->pn_type
== TOK_LP
&&
2836 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
)) {
2840 #if JS_HAS_XML_SUPPORT
2841 if (pn2
->pn_type
== TOK_UNARYOP
)
2842 pn2
->pn_op
= JSOP_BINDXMLNAME
;
2846 switch (pn2
->pn_type
) {
2848 /* Beware 'for (arguments in ...)' with or without a 'var'. */
2849 if (pn2
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
2850 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
2853 #if JS_HAS_DESTRUCTURING
2856 JS_ASSERT(pn2
->pn_type
== TOK_RB
|| pn2
->pn_type
== TOK_RC
);
2860 /* Check for valid lvalues in var-less destructuring for-in. */
2861 if (pn1
== pn2
&& !CheckDestructuring(cx
, NULL
, pn2
, NULL
, tc
))
2864 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
2866 * Destructuring for-in requires [key, value] enumeration
2869 JS_ASSERT(pn
->pn_op
== JSOP_ITER
);
2870 if (!(pn
->pn_iflags
& JSITER_FOREACH
))
2871 pn
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
2879 /* Parse the object expression as the right operand of 'in'. */
2880 pn2
= NewBinary(cx
, TOK_IN
, JSOP_NOP
, pn1
, Expr(cx
, ts
, tc
), tc
);
2885 if (pn
->pn_iflags
& JSITER_FOREACH
)
2887 pn
->pn_op
= JSOP_NOP
;
2889 /* Parse the loop condition or null into pn2. */
2890 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_INIT
);
2891 ts
->flags
|= TSF_OPERAND
;
2892 tt
= js_PeekToken(cx
, ts
);
2893 ts
->flags
&= ~TSF_OPERAND
;
2894 if (tt
== TOK_SEMI
) {
2897 pn2
= Expr(cx
, ts
, tc
);
2902 /* Parse the update expression or null into pn3. */
2903 MUST_MATCH_TOKEN(TOK_SEMI
, JSMSG_SEMI_AFTER_FOR_COND
);
2904 ts
->flags
|= TSF_OPERAND
;
2905 tt
= js_PeekToken(cx
, ts
);
2906 ts
->flags
&= ~TSF_OPERAND
;
2910 pn3
= Expr(cx
, ts
, tc
);
2915 /* Build the FORHEAD node to use as the left kid of pn. */
2916 pn4
= NewParseNode(cx
, ts
, PN_TERNARY
, tc
);
2919 pn4
->pn_type
= TOK_FORHEAD
;
2920 pn4
->pn_op
= JSOP_NOP
;
2927 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
2929 /* Parse the loop body into pn->pn_right. */
2930 pn2
= Statement(cx
, ts
, tc
);
2935 /* Record the absolute line number for source note emission. */
2936 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
2938 #if JS_HAS_BLOCK_SCOPE
2940 js_PopStatement(tc
);
2941 pnlet
->pn_expr
= pn
;
2946 pnseq
->pn_pos
.end
= pn
->pn_pos
.end
;
2947 PN_APPEND(pnseq
, pn
);
2950 js_PopStatement(tc
);
2954 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
2955 JSMSG_BAD_FOR_EACH_LOOP
);
2960 JSParseNode
*catchList
, *lastCatch
;
2963 * try nodes are ternary.
2964 * kid1 is the try Statement
2965 * kid2 is the catch node list or null
2966 * kid3 is the finally Statement
2968 * catch nodes are ternary.
2969 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
2970 * kid2 is the catch guard or null if no guard
2971 * kid3 is the catch block
2973 * catch lvalue nodes are either:
2974 * TOK_NAME for a single identifier
2975 * TOK_RB or TOK_RC for a destructuring left-hand side
2977 * finally nodes are TOK_LC Statement lists.
2979 pn
= NewParseNode(cx
, ts
, PN_TERNARY
, tc
);
2982 pn
->pn_op
= JSOP_NOP
;
2984 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_TRY
);
2985 js_PushStatement(tc
, &stmtInfo
, STMT_TRY
, -1);
2986 pn
->pn_kid1
= Statements(cx
, ts
, tc
);
2989 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_TRY
);
2990 js_PopStatement(tc
);
2993 tt
= js_GetToken(cx
, ts
);
2994 if (tt
== TOK_CATCH
) {
2995 catchList
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
2998 catchList
->pn_type
= TOK_RESERVED
;
2999 PN_INIT_LIST(catchList
);
3003 JSParseNode
*pnblock
;
3006 /* Check for another catch after unconditional catch. */
3007 if (lastCatch
&& !lastCatch
->pn_kid2
) {
3008 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3009 JSMSG_CATCH_AFTER_GENERAL
);
3014 * Create a lexical scope node around the whole catch clause,
3015 * including the head.
3017 pnblock
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
3020 stmtInfo
.type
= STMT_CATCH
;
3023 * Legal catch forms are:
3025 * catch (lhs if <boolean_expression>)
3026 * where lhs is a name or a destructuring left-hand side.
3027 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
3029 pn2
= NewParseNode(cx
, ts
, PN_TERNARY
, tc
);
3032 pnblock
->pn_expr
= pn2
;
3033 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_CATCH
);
3036 * Contrary to ECMA Ed. 3, the catch variable is lexically
3037 * scoped, not a property of a new Object instance. This is
3038 * an intentional change that anticipates ECMA Ed. 4.
3042 data
.binder
= BindLet
;
3043 data
.u
.let
.overflow
= JSMSG_TOO_MANY_CATCH_VARS
;
3045 tt
= js_GetToken(cx
, ts
);
3047 #if JS_HAS_DESTRUCTURING
3050 pn3
= DestructuringExpr(cx
, &data
, tc
, tt
);
3057 label
= CURRENT_TOKEN(ts
).t_atom
;
3058 if (!data
.binder(cx
, &data
, label
, tc
))
3061 pn3
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
3064 pn3
->pn_atom
= label
;
3069 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3070 JSMSG_CATCH_IDENTIFIER
);
3075 #if JS_HAS_CATCH_GUARD
3077 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
3078 * to avoid conflicting with the JS2/ECMAv4 type annotation
3079 * catchguard syntax.
3081 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
3082 pn2
->pn_kid2
= Expr(cx
, ts
, tc
);
3087 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_CATCH
);
3089 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_CATCH
);
3090 pn2
->pn_kid3
= Statements(cx
, ts
, tc
);
3093 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_CATCH
);
3094 js_PopStatement(tc
);
3096 PN_APPEND(catchList
, pnblock
);
3098 ts
->flags
|= TSF_OPERAND
;
3099 tt
= js_GetToken(cx
, ts
);
3100 ts
->flags
&= ~TSF_OPERAND
;
3101 } while (tt
== TOK_CATCH
);
3103 pn
->pn_kid2
= catchList
;
3105 if (tt
== TOK_FINALLY
) {
3106 MUST_MATCH_TOKEN(TOK_LC
, JSMSG_CURLY_BEFORE_FINALLY
);
3107 js_PushStatement(tc
, &stmtInfo
, STMT_FINALLY
, -1);
3108 pn
->pn_kid3
= Statements(cx
, ts
, tc
);
3111 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_AFTER_FINALLY
);
3112 js_PopStatement(tc
);
3116 if (!catchList
&& !pn
->pn_kid3
) {
3117 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3118 JSMSG_CATCH_OR_FINALLY
);
3125 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
3129 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
3130 ts
->flags
|= TSF_OPERAND
;
3131 tt
= js_PeekTokenSameLine(cx
, ts
);
3132 ts
->flags
&= ~TSF_OPERAND
;
3133 if (tt
== TOK_ERROR
)
3135 if (tt
== TOK_EOF
|| tt
== TOK_EOL
|| tt
== TOK_SEMI
|| tt
== TOK_RC
) {
3136 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3137 JSMSG_SYNTAX_ERROR
);
3141 pn2
= Expr(cx
, ts
, tc
);
3144 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
3145 pn
->pn_op
= JSOP_THROW
;
3149 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
3151 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3152 JSMSG_CATCH_WITHOUT_TRY
);
3156 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3157 JSMSG_FINALLY_WITHOUT_TRY
);
3161 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
3164 if (!MatchLabel(cx
, ts
, pn
))
3167 label
= pn
->pn_atom
;
3169 for (; ; stmt
= stmt
->down
) {
3171 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3172 JSMSG_LABEL_NOT_FOUND
);
3175 if (stmt
->type
== STMT_LABEL
&& stmt
->u
.label
== label
)
3179 for (; ; stmt
= stmt
->down
) {
3181 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3185 if (STMT_IS_LOOP(stmt
) || stmt
->type
== STMT_SWITCH
)
3190 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
3194 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
3197 if (!MatchLabel(cx
, ts
, pn
))
3200 label
= pn
->pn_atom
;
3202 for (stmt2
= NULL
; ; stmt
= stmt
->down
) {
3204 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3205 JSMSG_LABEL_NOT_FOUND
);
3208 if (stmt
->type
== STMT_LABEL
) {
3209 if (stmt
->u
.label
== label
) {
3210 if (!stmt2
|| !STMT_IS_LOOP(stmt2
)) {
3211 js_ReportCompileErrorNumber(cx
, ts
, NULL
,
3213 JSMSG_BAD_CONTINUE
);
3223 for (; ; stmt
= stmt
->down
) {
3225 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3226 JSMSG_BAD_CONTINUE
);
3229 if (STMT_IS_LOOP(stmt
))
3234 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
3238 pn
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
3241 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_BEFORE_WITH
);
3242 pn2
= ParenExpr(cx
, ts
, tc
, NULL
, NULL
);
3245 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_WITH
);
3248 js_PushStatement(tc
, &stmtInfo
, STMT_WITH
, -1);
3249 pn2
= Statement(cx
, ts
, tc
);
3252 js_PopStatement(tc
);
3254 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
3256 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3260 pn
= Variables(cx
, ts
, tc
);
3264 /* Tell js_EmitTree to generate a final POP. */
3265 pn
->pn_extra
|= PNX_POPVAR
;
3268 #if JS_HAS_BLOCK_SCOPE
3272 JSParsedObjectBox
*blockpob
;
3274 /* Check for a let statement or let expression. */
3275 if (js_PeekToken(cx
, ts
) == TOK_LP
) {
3276 pn
= LetBlock(cx
, ts
, tc
, JS_TRUE
);
3277 if (!pn
|| pn
->pn_op
== JSOP_LEAVEBLOCK
)
3280 /* Let expressions require automatic semicolon insertion. */
3281 JS_ASSERT(pn
->pn_type
== TOK_SEMI
||
3282 pn
->pn_op
== JSOP_LEAVEBLOCKEXPR
);
3287 * This is a let declaration. We must be directly under a block per
3288 * the proposed ES4 specs, but not an implicit block created due to
3289 * 'for (let ...)'. If we pass this error test, make the enclosing
3290 * JSStmtInfo be our scope. Further let declarations in this block
3291 * will find this scope statement and use the same block object.
3293 * If we are the first let declaration in this block (i.e., when the
3294 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
3295 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
3299 (!STMT_MAYBE_SCOPE(stmt
) || (stmt
->flags
& SIF_FOR_BLOCK
))) {
3300 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3301 JSMSG_LET_DECL_NOT_IN_BLOCK
);
3305 if (stmt
&& (stmt
->flags
& SIF_SCOPE
)) {
3306 JS_ASSERT(tc
->blockChain
== stmt
->u
.blockObj
);
3307 obj
= tc
->blockChain
;
3309 if (!stmt
|| (stmt
->flags
& SIF_BODY_BLOCK
)) {
3311 * ES4 specifies that let at top level and at body-block scope
3312 * does not shadow var, so convert back to var.
3314 CURRENT_TOKEN(ts
).type
= TOK_VAR
;
3315 CURRENT_TOKEN(ts
).t_op
= JSOP_DEFVAR
;
3317 pn
= Variables(cx
, ts
, tc
);
3320 pn
->pn_extra
|= PNX_POPVAR
;
3325 * Some obvious assertions here, but they may help clarify the
3326 * situation. This stmt is not yet a scope, so it must not be a
3327 * catch block (which is a lexical scope by definition).
3329 JS_ASSERT(!(stmt
->flags
& SIF_SCOPE
));
3330 JS_ASSERT(stmt
!= tc
->topScopeStmt
);
3331 JS_ASSERT(stmt
->type
== STMT_BLOCK
||
3332 stmt
->type
== STMT_SWITCH
||
3333 stmt
->type
== STMT_TRY
||
3334 stmt
->type
== STMT_FINALLY
);
3335 JS_ASSERT(!stmt
->downScope
);
3337 /* Convert the block statement into a scope statement. */
3338 obj
= js_NewBlockObject(cx
);
3341 blockpob
= js_NewParsedObjectBox(cx
, tc
->parseContext
, obj
);
3346 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
3347 * list stack, if it isn't already there. If it is there, but it
3348 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
3351 stmt
->flags
|= SIF_SCOPE
;
3352 stmt
->downScope
= tc
->topScopeStmt
;
3353 tc
->topScopeStmt
= stmt
;
3354 JS_SCOPE_DEPTH_METERING(++tc
->scopeDepth
> tc
->maxScopeDepth
&&
3355 (tc
->maxScopeDepth
= tc
->scopeDepth
));
3357 STOBJ_SET_PARENT(obj
, tc
->blockChain
);
3358 tc
->blockChain
= obj
;
3359 stmt
->u
.blockObj
= obj
;
3362 pn1
= tc
->blockNode
;
3363 JS_ASSERT(!pn1
|| pn1
->pn_type
!= TOK_LEXICALSCOPE
);
3366 /* Create a new lexical scope node for these statements. */
3367 pn1
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
3371 pn1
->pn_type
= TOK_LEXICALSCOPE
;
3372 pn1
->pn_op
= JSOP_LEAVEBLOCK
;
3373 pn1
->pn_pos
= tc
->blockNode
->pn_pos
;
3374 pn1
->pn_pob
= blockpob
;
3375 pn1
->pn_expr
= tc
->blockNode
;
3377 tc
->blockNode
= pn1
;
3380 pn
= Variables(cx
, ts
, tc
);
3383 pn
->pn_extra
= PNX_POPVAR
;
3386 #endif /* JS_HAS_BLOCK_SCOPE */
3389 pn
= ReturnOrYield(cx
, ts
, tc
, Expr
);
3398 oldflags
= tc
->flags
;
3399 tc
->flags
= oldflags
& ~TCF_HAS_FUNCTION_STMT
;
3400 js_PushStatement(tc
, &stmtInfo
, STMT_BLOCK
, -1);
3401 pn
= Statements(cx
, ts
, tc
);
3405 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_COMPOUND
);
3406 js_PopStatement(tc
);
3409 * If we contain a function statement and our container is top-level
3410 * or another block, flag pn to preserve braces when decompiling.
3412 if ((tc
->flags
& TCF_HAS_FUNCTION_STMT
) &&
3413 (!tc
->topStmt
|| tc
->topStmt
->type
== STMT_BLOCK
)) {
3414 pn
->pn_extra
|= PNX_NEEDBRACES
;
3416 tc
->flags
= oldflags
| (tc
->flags
& (TCF_FUN_FLAGS
| TCF_RETURN_FLAGS
));
3422 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
3425 pn
->pn_type
= TOK_SEMI
;
3428 #if JS_HAS_DEBUGGER_KEYWORD
3430 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
3433 pn
->pn_type
= TOK_DEBUGGER
;
3434 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3436 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3438 #if JS_HAS_XML_SUPPORT
3440 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
3443 if (!js_MatchToken(cx
, ts
, TOK_NAME
) ||
3444 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.xmlAtom
||
3445 !js_MatchToken(cx
, ts
, TOK_NAME
) ||
3446 CURRENT_TOKEN(ts
).t_atom
!= cx
->runtime
->atomState
.namespaceAtom
||
3447 !js_MatchToken(cx
, ts
, TOK_ASSIGN
) ||
3448 CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
) {
3449 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3450 JSMSG_BAD_DEFAULT_XML_NAMESPACE
);
3453 pn2
= Expr(cx
, ts
, tc
);
3456 pn
->pn_op
= JSOP_DEFXMLNS
;
3457 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
3459 tc
->flags
|= TCF_HAS_DEFXMLNS
;
3467 #if JS_HAS_XML_SUPPORT
3471 pn2
= Expr(cx
, ts
, tc
);
3475 if (js_PeekToken(cx
, ts
) == TOK_COLON
) {
3476 if (pn2
->pn_type
!= TOK_NAME
) {
3477 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3481 label
= pn2
->pn_atom
;
3482 for (stmt
= tc
->topStmt
; stmt
; stmt
= stmt
->down
) {
3483 if (stmt
->type
== STMT_LABEL
&& stmt
->u
.label
== label
) {
3484 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3485 JSMSG_DUPLICATE_LABEL
);
3489 (void) js_GetToken(cx
, ts
);
3491 /* Push a label struct and parse the statement. */
3492 js_PushStatement(tc
, &stmtInfo
, STMT_LABEL
, -1);
3493 stmtInfo
.u
.label
= label
;
3494 pn
= Statement(cx
, ts
, tc
);
3498 /* Normalize empty statement to empty block for the decompiler. */
3499 if (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
) {
3500 pn
->pn_type
= TOK_LC
;
3501 pn
->pn_arity
= PN_LIST
;
3505 /* Pop the label, set pn_expr, and return early. */
3506 js_PopStatement(tc
);
3507 pn2
->pn_type
= TOK_COLON
;
3508 pn2
->pn_pos
.end
= pn
->pn_pos
.end
;
3513 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
3516 pn
->pn_type
= TOK_SEMI
;
3517 pn
->pn_pos
= pn2
->pn_pos
;
3522 /* Check termination of this primitive statement. */
3523 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
3524 ts
->flags
|= TSF_OPERAND
;
3525 tt
= js_PeekTokenSameLine(cx
, ts
);
3526 ts
->flags
&= ~TSF_OPERAND
;
3527 if (tt
== TOK_ERROR
)
3529 if (tt
!= TOK_EOF
&& tt
!= TOK_EOL
&& tt
!= TOK_SEMI
&& tt
!= TOK_RC
) {
3530 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3531 JSMSG_SEMI_BEFORE_STMNT
);
3536 (void) js_MatchToken(cx
, ts
, TOK_SEMI
);
3540 static JSParseNode
*
3541 Variables(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3545 JSStmtInfo
*scopeStmt
;
3547 JSParseNode
*pn
, *pn2
;
3551 * The three options here are:
3552 * - TOK_LET: We are parsing a let declaration.
3553 * - TOK_LP: We are parsing the head of a let block.
3554 * - Otherwise, we're parsing var declarations.
3556 tt
= CURRENT_TOKEN(ts
).type
;
3557 let
= (tt
== TOK_LET
|| tt
== TOK_LP
);
3558 JS_ASSERT(let
|| tt
== TOK_VAR
);
3560 /* Make sure that Statement set the tree context up correctly. */
3561 scopeStmt
= tc
->topScopeStmt
;
3563 while (scopeStmt
&& !(scopeStmt
->flags
& SIF_SCOPE
)) {
3564 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt
));
3565 scopeStmt
= scopeStmt
->downScope
;
3567 JS_ASSERT(scopeStmt
);
3571 data
.op
= let
? JSOP_NOP
: CURRENT_TOKEN(ts
).t_op
;
3572 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
3575 pn
->pn_op
= data
.op
;
3579 * The tricky part of this code is to create special parsenode opcodes for
3580 * getting and setting variables (which will be stored as special slots in
3581 * the frame). The most complicated case is an eval() inside a function.
3582 * If the evaluated string references variables in the enclosing function,
3583 * then we need to generate the special variable opcodes. We determine
3584 * this by looking up the variable's id in the current variable object.
3585 * Fortunately, we can avoid doing this for let declared variables.
3588 JS_ASSERT(tc
->blockChain
== scopeStmt
->u
.blockObj
);
3589 data
.binder
= BindLet
;
3590 data
.u
.let
.overflow
= JSMSG_TOO_MANY_LOCALS
;
3592 data
.binder
= BindVarOrConst
;
3596 tt
= js_GetToken(cx
, ts
);
3597 #if JS_HAS_DESTRUCTURING
3598 if (tt
== TOK_LB
|| tt
== TOK_LC
) {
3599 pn2
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
3603 if ((tc
->flags
& TCF_IN_FOR_INIT
) &&
3604 js_PeekToken(cx
, ts
) == TOK_IN
) {
3605 if (!CheckDestructuring(cx
, &data
, pn2
, NULL
, tc
))
3611 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_BAD_DESTRUCT_DECL
);
3612 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
3615 pn2
= NewBinary(cx
, TOK_ASSIGN
, JSOP_NOP
,
3616 pn2
, AssignExpr(cx
, ts
, tc
),
3619 !CheckDestructuring(cx
, &data
,
3620 pn2
->pn_left
, pn2
->pn_right
,
3629 if (tt
!= TOK_NAME
) {
3630 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3631 JSMSG_NO_VARIABLE_NAME
);
3634 atom
= CURRENT_TOKEN(ts
).t_atom
;
3635 if (!data
.binder(cx
, &data
, atom
, tc
))
3638 pn2
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
3641 pn2
->pn_op
= JSOP_NAME
;
3642 pn2
->pn_atom
= atom
;
3645 pn2
->pn_const
= (data
.op
== JSOP_DEFCONST
);
3648 if (js_MatchToken(cx
, ts
, TOK_ASSIGN
)) {
3649 if (CURRENT_TOKEN(ts
).t_op
!= JSOP_NOP
)
3652 pn2
->pn_expr
= AssignExpr(cx
, ts
, tc
);
3655 pn2
->pn_op
= (!let
&& data
.op
== JSOP_DEFCONST
)
3658 pn2
->pn_pos
.end
= pn2
->pn_expr
->pn_pos
.end
;
3659 if (!let
&& atom
== cx
->runtime
->atomState
.argumentsAtom
)
3660 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3662 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
3664 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
3668 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3669 JSMSG_BAD_VAR_INIT
);
3673 static JSParseNode
*
3674 Expr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3676 JSParseNode
*pn
, *pn2
;
3678 pn
= AssignExpr(cx
, ts
, tc
);
3679 if (pn
&& js_MatchToken(cx
, ts
, TOK_COMMA
)) {
3680 pn2
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
3683 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
3684 PN_INIT_LIST_1(pn2
, pn
);
3687 #if JS_HAS_GENERATORS
3689 if (pn2
->pn_type
== TOK_YIELD
) {
3690 js_ReportCompileErrorNumber(cx
, ts
, pn2
, JSREPORT_ERROR
,
3691 JSMSG_BAD_GENERATOR_SYNTAX
,
3696 pn2
= AssignExpr(cx
, ts
, tc
);
3700 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
3701 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
3706 static JSParseNode
*
3707 AssignExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3709 JSParseNode
*pn
, *pn2
;
3713 JS_CHECK_RECURSION(cx
, return NULL
);
3715 #if JS_HAS_GENERATORS
3716 ts
->flags
|= TSF_OPERAND
;
3717 if (js_MatchToken(cx
, ts
, TOK_YIELD
)) {
3718 ts
->flags
&= ~TSF_OPERAND
;
3719 return ReturnOrYield(cx
, ts
, tc
, AssignExpr
);
3721 ts
->flags
&= ~TSF_OPERAND
;
3724 pn
= CondExpr(cx
, ts
, tc
);
3728 tt
= js_GetToken(cx
, ts
);
3729 #if JS_HAS_GETTER_SETTER
3730 if (tt
== TOK_NAME
) {
3731 tt
= CheckGetterOrSetter(cx
, ts
, TOK_ASSIGN
);
3732 if (tt
== TOK_ERROR
)
3736 if (tt
!= TOK_ASSIGN
) {
3741 op
= CURRENT_TOKEN(ts
).t_op
;
3742 for (pn2
= pn
; pn2
->pn_type
== TOK_RP
; pn2
= pn2
->pn_kid
)
3744 switch (pn2
->pn_type
) {
3746 pn2
->pn_op
= JSOP_SETNAME
;
3747 if (pn2
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
3748 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
3751 pn2
->pn_op
= JSOP_SETPROP
;
3754 pn2
->pn_op
= JSOP_SETELEM
;
3756 #if JS_HAS_DESTRUCTURING
3759 if (op
!= JSOP_NOP
) {
3760 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3761 JSMSG_BAD_DESTRUCT_ASS
);
3764 pn
= AssignExpr(cx
, ts
, tc
);
3765 if (!pn
|| !CheckDestructuring(cx
, NULL
, pn2
, pn
, tc
))
3767 return NewBinary(cx
, TOK_ASSIGN
, op
, pn2
, pn
, tc
);
3769 #if JS_HAS_LVALUE_RETURN
3771 if (!MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_LEFTSIDE_OF_ASS
))
3775 #if JS_HAS_XML_SUPPORT
3777 if (pn2
->pn_op
== JSOP_XMLNAME
) {
3778 pn2
->pn_op
= JSOP_SETXMLNAME
;
3784 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
3785 JSMSG_BAD_LEFTSIDE_OF_ASS
);
3789 return NewBinary(cx
, TOK_ASSIGN
, op
, pn2
, AssignExpr(cx
, ts
, tc
), tc
);
3792 static JSParseNode
*
3793 CondExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3795 JSParseNode
*pn
, *pn1
, *pn2
, *pn3
;
3798 pn
= OrExpr(cx
, ts
, tc
);
3799 if (pn
&& js_MatchToken(cx
, ts
, TOK_HOOK
)) {
3801 pn
= NewParseNode(cx
, ts
, PN_TERNARY
, tc
);
3805 * Always accept the 'in' operator in the middle clause of a ternary,
3806 * where it's unambiguous, even if we might be parsing the init of a
3809 oldflags
= tc
->flags
;
3810 tc
->flags
&= ~TCF_IN_FOR_INIT
;
3811 pn2
= AssignExpr(cx
, ts
, tc
);
3812 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
3816 MUST_MATCH_TOKEN(TOK_COLON
, JSMSG_COLON_IN_COND
);
3817 pn3
= AssignExpr(cx
, ts
, tc
);
3820 pn
->pn_pos
.begin
= pn1
->pn_pos
.begin
;
3821 pn
->pn_pos
.end
= pn3
->pn_pos
.end
;
3829 static JSParseNode
*
3830 OrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3834 pn
= AndExpr(cx
, ts
, tc
);
3835 while (pn
&& js_MatchToken(cx
, ts
, TOK_OR
))
3836 pn
= NewBinary(cx
, TOK_OR
, JSOP_OR
, pn
, AndExpr(cx
, ts
, tc
), tc
);
3840 static JSParseNode
*
3841 AndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3845 pn
= BitOrExpr(cx
, ts
, tc
);
3846 while (pn
&& js_MatchToken(cx
, ts
, TOK_AND
))
3847 pn
= NewBinary(cx
, TOK_AND
, JSOP_AND
, pn
, BitOrExpr(cx
, ts
, tc
), tc
);
3851 static JSParseNode
*
3852 BitOrExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3856 pn
= BitXorExpr(cx
, ts
, tc
);
3857 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITOR
)) {
3858 pn
= NewBinary(cx
, TOK_BITOR
, JSOP_BITOR
, pn
, BitXorExpr(cx
, ts
, tc
),
3864 static JSParseNode
*
3865 BitXorExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3869 pn
= BitAndExpr(cx
, ts
, tc
);
3870 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITXOR
)) {
3871 pn
= NewBinary(cx
, TOK_BITXOR
, JSOP_BITXOR
, pn
, BitAndExpr(cx
, ts
, tc
),
3877 static JSParseNode
*
3878 BitAndExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3882 pn
= EqExpr(cx
, ts
, tc
);
3883 while (pn
&& js_MatchToken(cx
, ts
, TOK_BITAND
))
3884 pn
= NewBinary(cx
, TOK_BITAND
, JSOP_BITAND
, pn
, EqExpr(cx
, ts
, tc
), tc
);
3888 static JSParseNode
*
3889 EqExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3894 pn
= RelExpr(cx
, ts
, tc
);
3895 while (pn
&& js_MatchToken(cx
, ts
, TOK_EQOP
)) {
3896 op
= CURRENT_TOKEN(ts
).t_op
;
3897 pn
= NewBinary(cx
, TOK_EQOP
, op
, pn
, RelExpr(cx
, ts
, tc
), tc
);
3902 static JSParseNode
*
3903 RelExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3908 uintN inForInitFlag
= tc
->flags
& TCF_IN_FOR_INIT
;
3911 * Uses of the in operator in ShiftExprs are always unambiguous,
3912 * so unset the flag that prohibits recognizing it.
3914 tc
->flags
&= ~TCF_IN_FOR_INIT
;
3916 pn
= ShiftExpr(cx
, ts
, tc
);
3918 (js_MatchToken(cx
, ts
, TOK_RELOP
) ||
3920 * Recognize the 'in' token as an operator only if we're not
3921 * currently in the init expr of a for loop.
3923 (inForInitFlag
== 0 && js_MatchToken(cx
, ts
, TOK_IN
)) ||
3924 js_MatchToken(cx
, ts
, TOK_INSTANCEOF
))) {
3925 tt
= CURRENT_TOKEN(ts
).type
;
3926 op
= CURRENT_TOKEN(ts
).t_op
;
3927 pn
= NewBinary(cx
, tt
, op
, pn
, ShiftExpr(cx
, ts
, tc
), tc
);
3929 /* Restore previous state of inForInit flag. */
3930 tc
->flags
|= inForInitFlag
;
3935 static JSParseNode
*
3936 ShiftExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3941 pn
= AddExpr(cx
, ts
, tc
);
3942 while (pn
&& js_MatchToken(cx
, ts
, TOK_SHOP
)) {
3943 op
= CURRENT_TOKEN(ts
).t_op
;
3944 pn
= NewBinary(cx
, TOK_SHOP
, op
, pn
, AddExpr(cx
, ts
, tc
), tc
);
3949 static JSParseNode
*
3950 AddExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3956 pn
= MulExpr(cx
, ts
, tc
);
3958 (js_MatchToken(cx
, ts
, TOK_PLUS
) ||
3959 js_MatchToken(cx
, ts
, TOK_MINUS
))) {
3960 tt
= CURRENT_TOKEN(ts
).type
;
3961 op
= (tt
== TOK_PLUS
) ? JSOP_ADD
: JSOP_SUB
;
3962 pn
= NewBinary(cx
, tt
, op
, pn
, MulExpr(cx
, ts
, tc
), tc
);
3967 static JSParseNode
*
3968 MulExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
3974 pn
= UnaryExpr(cx
, ts
, tc
);
3976 (js_MatchToken(cx
, ts
, TOK_STAR
) ||
3977 js_MatchToken(cx
, ts
, TOK_DIVOP
))) {
3978 tt
= CURRENT_TOKEN(ts
).type
;
3979 op
= CURRENT_TOKEN(ts
).t_op
;
3980 pn
= NewBinary(cx
, tt
, op
, pn
, UnaryExpr(cx
, ts
, tc
), tc
);
3985 static JSParseNode
*
3986 SetLvalKid(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
, JSParseNode
*kid
,
3989 while (kid
->pn_type
== TOK_RP
)
3991 if (kid
->pn_type
!= TOK_NAME
&&
3992 kid
->pn_type
!= TOK_DOT
&&
3993 #if JS_HAS_LVALUE_RETURN
3994 (kid
->pn_type
!= TOK_LP
||
3995 (kid
->pn_op
!= JSOP_CALL
&& kid
->pn_op
!= JSOP_EVAL
&& kid
->pn_op
!= JSOP_APPLY
)) &&
3997 #if JS_HAS_XML_SUPPORT
3998 (kid
->pn_type
!= TOK_UNARYOP
|| kid
->pn_op
!= JSOP_XMLNAME
) &&
4000 kid
->pn_type
!= TOK_LB
) {
4001 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4002 JSMSG_BAD_OPERAND
, name
);
4009 static const char incop_name_str
[][10] = {"increment", "decrement"};
4012 SetIncOpKid(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4013 JSParseNode
*pn
, JSParseNode
*kid
,
4014 JSTokenType tt
, JSBool preorder
)
4018 kid
= SetLvalKid(cx
, ts
, pn
, kid
, incop_name_str
[tt
== TOK_DEC
]);
4021 switch (kid
->pn_type
) {
4023 op
= (tt
== TOK_INC
)
4024 ? (preorder
? JSOP_INCNAME
: JSOP_NAMEINC
)
4025 : (preorder
? JSOP_DECNAME
: JSOP_NAMEDEC
);
4026 if (kid
->pn_atom
== cx
->runtime
->atomState
.argumentsAtom
)
4027 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
4031 op
= (tt
== TOK_INC
)
4032 ? (preorder
? JSOP_INCPROP
: JSOP_PROPINC
)
4033 : (preorder
? JSOP_DECPROP
: JSOP_PROPDEC
);
4036 #if JS_HAS_LVALUE_RETURN
4038 if (!MakeSetCall(cx
, kid
, tc
, JSMSG_BAD_INCOP_OPERAND
))
4042 #if JS_HAS_XML_SUPPORT
4044 if (kid
->pn_op
== JSOP_XMLNAME
)
4045 kid
->pn_op
= JSOP_SETXMLNAME
;
4049 op
= (tt
== TOK_INC
)
4050 ? (preorder
? JSOP_INCELEM
: JSOP_ELEMINC
)
4051 : (preorder
? JSOP_DECELEM
: JSOP_ELEMDEC
);
4062 static JSParseNode
*
4063 UnaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4066 JSParseNode
*pn
, *pn2
;
4068 JS_CHECK_RECURSION(cx
, return NULL
);
4070 ts
->flags
|= TSF_OPERAND
;
4071 tt
= js_GetToken(cx
, ts
);
4072 ts
->flags
&= ~TSF_OPERAND
;
4078 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4081 pn
->pn_type
= TOK_UNARYOP
; /* PLUS and MINUS are binary */
4082 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
4083 pn2
= UnaryExpr(cx
, ts
, tc
);
4086 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4092 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4095 pn2
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
4098 if (!SetIncOpKid(cx
, ts
, tc
, pn
, pn2
, tt
, JS_TRUE
))
4100 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4104 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4107 pn2
= UnaryExpr(cx
, ts
, tc
);
4110 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4113 * Under ECMA3, deleting any unary expression is valid -- it simply
4114 * returns true. Here we strip off any parentheses and fold constants
4115 * before checking for a call expression, in order to rule out delete
4116 * of a generator expression.
4118 while (pn2
->pn_type
== TOK_RP
)
4120 if (!js_FoldConstants(cx
, pn2
, tc
))
4122 if (pn2
->pn_type
== TOK_LP
&&
4123 pn2
->pn_op
!= JSOP_SETCALL
&&
4124 !MakeSetCall(cx
, pn2
, tc
, JSMSG_BAD_DELETE_OPERAND
)) {
4135 pn
= MemberExpr(cx
, ts
, tc
, JS_TRUE
);
4139 /* Don't look across a newline boundary for a postfix incop. */
4140 if (ON_CURRENT_LINE(ts
, pn
->pn_pos
)) {
4141 ts
->flags
|= TSF_OPERAND
;
4142 tt
= js_PeekTokenSameLine(cx
, ts
);
4143 ts
->flags
&= ~TSF_OPERAND
;
4144 if (tt
== TOK_INC
|| tt
== TOK_DEC
) {
4145 (void) js_GetToken(cx
, ts
);
4146 pn2
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4149 if (!SetIncOpKid(cx
, ts
, tc
, pn2
, pn
, tt
, JS_FALSE
))
4151 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4160 #if JS_HAS_GENERATORS
4163 * Starting from a |for| keyword after the first array initialiser element or
4164 * an expression in an open parenthesis, parse the tail of the comprehension
4165 * or generator expression signified by this |for| keyword in context.
4167 * Return null on failure, else return the top-most parse node for the array
4168 * comprehension or generator expression, with a unary node as the body of the
4169 * (possibly nested) for-loop, initialized by |type, op, kid|.
4171 static JSParseNode
*
4172 ComprehensionTail(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4173 JSTokenType type
, JSOp op
, JSParseNode
*kid
)
4175 JSParseNode
*pn
, *pn2
, *pn3
, **pnp
;
4176 JSStmtInfo stmtInfo
;
4182 JS_ASSERT(type
== TOK_SEMI
|| type
== TOK_ARRAYPUSH
);
4183 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_FOR
);
4186 * Make a parse-node and literal object representing the block scope of
4187 * this array comprehension or generator expression.
4189 pn
= PushLexicalScope(cx
, ts
, tc
, &stmtInfo
);
4196 data
.binder
= BindLet
;
4197 data
.u
.let
.overflow
= JSMSG_ARRAY_INIT_TOO_BIG
;
4202 * FOR node is binary, left is loop control and right is body. Use
4203 * index to count each block-local let-variable on the left-hand side
4206 pn2
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
4210 pn2
->pn_op
= JSOP_ITER
;
4211 pn2
->pn_iflags
= JSITER_ENUMERATE
;
4212 if (js_MatchToken(cx
, ts
, TOK_NAME
)) {
4213 if (CURRENT_TOKEN(ts
).t_atom
== rt
->atomState
.eachAtom
)
4214 pn2
->pn_iflags
|= JSITER_FOREACH
;
4218 MUST_MATCH_TOKEN(TOK_LP
, JSMSG_PAREN_AFTER_FOR
);
4220 tt
= js_GetToken(cx
, ts
);
4222 #if JS_HAS_DESTRUCTURING
4225 pn3
= DestructuringExpr(cx
, &data
, tc
, tt
);
4229 if (pn3
->pn_type
!= TOK_RB
|| pn3
->pn_count
!= 2) {
4230 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4231 JSMSG_BAD_FOR_LEFTSIDE
);
4235 if (JSVERSION_NUMBER(cx
) == JSVERSION_1_7
) {
4236 /* Destructuring requires [key, value] enumeration in JS1.7. */
4237 JS_ASSERT(pn2
->pn_op
== JSOP_ITER
);
4238 JS_ASSERT(pn2
->pn_iflags
& JSITER_ENUMERATE
);
4239 if (!(pn2
->pn_iflags
& JSITER_FOREACH
))
4240 pn2
->pn_iflags
|= JSITER_FOREACH
| JSITER_KEYVALUE
;
4246 atom
= CURRENT_TOKEN(ts
).t_atom
;
4247 if (!data
.binder(cx
, &data
, atom
, tc
))
4251 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
4252 * JSOP_GETLOCAL here, because we don't yet know the block's depth
4253 * in the operand stack frame. The code generator computes that,
4254 * and it tries to bind all names to slots, so we must let it do
4257 pn3
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
4260 pn3
->pn_op
= JSOP_NAME
;
4261 pn3
->pn_atom
= atom
;
4266 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4267 JSMSG_NO_VARIABLE_NAME
);
4273 MUST_MATCH_TOKEN(TOK_IN
, JSMSG_IN_AFTER_FOR_NAME
);
4274 pn3
= NewBinary(cx
, TOK_IN
, JSOP_NOP
, pn3
,
4275 Expr(cx
, ts
, tc
), tc
);
4279 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_AFTER_FOR_CTRL
);
4282 pnp
= &pn2
->pn_right
;
4283 } while (js_MatchToken(cx
, ts
, TOK_FOR
));
4285 if (js_MatchToken(cx
, ts
, TOK_IF
)) {
4286 pn2
= NewParseNode(cx
, ts
, PN_TERNARY
, tc
);
4289 pn2
->pn_kid1
= Condition(cx
, ts
, tc
);
4293 pnp
= &pn2
->pn_kid2
;
4296 pn2
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4299 pn2
->pn_type
= type
;
4304 js_PopStatement(tc
);
4308 #if JS_HAS_GENERATOR_EXPRS
4311 * Starting from a |for| keyword after an expression, parse the comprehension
4312 * tail completing this generator expression. Wrap the expression at kid in a
4313 * generator function that is immediately called to evaluate to the generator
4314 * iterator that is the value of this generator expression.
4316 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
4317 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
4318 * expression-statement node that constitutes the body of the |for| loop(s) in
4319 * the generator function.
4321 * Note how unlike Python, we do not evaluate the expression to the right of
4322 * the first |in| in the chain of |for| heads. Instead, a generator expression
4323 * is merely sugar for a generator function expression and its application.
4325 static JSParseNode
*
4326 GeneratorExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4327 uintN oldflags
, JSParseNode
*pn
, JSParseNode
*kid
)
4329 JSParseNode
*body
, *lambda
;
4332 /* Initialize pn, connecting it to kid. */
4333 JS_ASSERT(pn
->pn_arity
== PN_UNARY
);
4334 pn
->pn_type
= TOK_YIELD
;
4335 pn
->pn_op
= JSOP_YIELD
;
4336 pn
->pn_pos
= kid
->pn_pos
;
4338 pn
->pn_hidden
= JS_TRUE
;
4341 * Parse the comprehension tail at hand, making pn the kid of the loop
4342 * body's expression statement.
4344 body
= ComprehensionTail(cx
, ts
, tc
, TOK_SEMI
, JSOP_NOP
, pn
);
4347 body
->pn_pos
.begin
= kid
->pn_pos
.begin
;
4350 * Make the generator function and flag it as interpreted ASAP (see the
4351 * comment in FunctionBody).
4353 fun
= NewCompilerFunction(cx
, tc
, NULL
, JSFUN_LAMBDA
);
4358 * This generator function is referenced by an anonymous function object
4359 * node. Here is where we must take care to propagate certain tc->flags
4360 * that may have changed from oldflags to reflect crucial facts about the
4361 * expression on the left of |for| and in the comprehension tail after it.
4363 lambda
= NewParseNode(cx
, ts
, PN_FUNC
, tc
);
4366 lambda
->pn_type
= TOK_FUNCTION
;
4367 lambda
->pn_op
= JSOP_ANONFUNOBJ
;
4368 lambda
->pn_pos
.begin
= body
->pn_pos
.begin
;
4369 lambda
->pn_funpob
= js_NewParsedObjectBox(cx
, tc
->parseContext
,
4371 if (!lambda
->pn_funpob
)
4373 lambda
->pn_body
= body
;
4374 lambda
->pn_flags
= TCF_FUN_IS_GENERATOR
| TCF_GENEXP_LAMBDA
|
4375 ((oldflags
^ tc
->flags
) & TCF_FUN_FLAGS
);
4378 * Re-use pn to name the result node, a call expression invoking the
4379 * anonymous generator function object.
4381 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
4384 pn
->pn_type
= TOK_LP
;
4385 pn
->pn_op
= JSOP_CALL
;
4386 pn
->pn_pos
.begin
= lambda
->pn_pos
.begin
;
4387 PN_INIT_LIST_1(pn
, lambda
);
4389 body
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4390 tc
->flags
= oldflags
;
4394 static const char js_generator_str
[] = "generator";
4396 #endif /* JS_HAS_GENERATOR_EXPRS */
4397 #endif /* JS_HAS_GENERATORS */
4400 ArgumentList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4401 JSParseNode
*listNode
)
4405 ts
->flags
|= TSF_OPERAND
;
4406 matched
= js_MatchToken(cx
, ts
, TOK_RP
);
4407 ts
->flags
&= ~TSF_OPERAND
;
4410 #if JS_HAS_GENERATOR_EXPRS
4411 uintN oldflags
= tc
->flags
;
4413 JSParseNode
*argNode
= AssignExpr(cx
, ts
, tc
);
4416 #if JS_HAS_GENERATORS
4417 if (argNode
->pn_type
== TOK_YIELD
&&
4418 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
4419 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
4420 JSMSG_BAD_GENERATOR_SYNTAX
,
4425 #if JS_HAS_GENERATOR_EXPRS
4426 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
4427 JSParseNode
*pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4430 argNode
= GeneratorExpr(cx
, ts
, tc
, oldflags
, pn
, argNode
);
4433 if (listNode
->pn_count
> 1 ||
4434 js_PeekToken(cx
, ts
) == TOK_COMMA
) {
4435 js_ReportCompileErrorNumber(cx
, ts
, argNode
, JSREPORT_ERROR
,
4436 JSMSG_BAD_GENERATOR_SYNTAX
,
4442 PN_APPEND(listNode
, argNode
);
4443 } while (js_MatchToken(cx
, ts
, TOK_COMMA
));
4445 if (js_GetToken(cx
, ts
) != TOK_RP
) {
4446 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4447 JSMSG_PAREN_AFTER_ARGS
);
4454 static JSParseNode
*
4455 MemberExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4456 JSBool allowCallSyntax
)
4458 JSParseNode
*pn
, *pn2
, *pn3
;
4461 JS_CHECK_RECURSION(cx
, return NULL
);
4463 /* Check for new expression first. */
4464 ts
->flags
|= TSF_OPERAND
;
4465 tt
= js_GetToken(cx
, ts
);
4466 ts
->flags
&= ~TSF_OPERAND
;
4467 if (tt
== TOK_NEW
) {
4468 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
4471 pn2
= MemberExpr(cx
, ts
, tc
, JS_FALSE
);
4474 pn
->pn_op
= JSOP_NEW
;
4475 PN_INIT_LIST_1(pn
, pn2
);
4476 pn
->pn_pos
.begin
= pn2
->pn_pos
.begin
;
4478 if (js_MatchToken(cx
, ts
, TOK_LP
) && !ArgumentList(cx
, ts
, tc
, pn
))
4480 if (pn
->pn_count
> ARGC_LIMIT
) {
4481 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4482 JSMSG_TOO_MANY_CON_ARGS
);
4485 pn
->pn_pos
.end
= PN_LAST(pn
)->pn_pos
.end
;
4487 pn
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_FALSE
);
4491 if (pn
->pn_type
== TOK_ANYNAME
||
4492 pn
->pn_type
== TOK_AT
||
4493 pn
->pn_type
== TOK_DBLCOLON
) {
4494 pn2
= NewOrRecycledNode(cx
, tc
);
4497 pn2
->pn_type
= TOK_UNARYOP
;
4498 pn2
->pn_pos
= pn
->pn_pos
;
4499 pn2
->pn_op
= JSOP_XMLNAME
;
4500 pn2
->pn_arity
= PN_UNARY
;
4506 while ((tt
= js_GetToken(cx
, ts
)) > TOK_EOF
) {
4507 if (tt
== TOK_DOT
) {
4508 pn2
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
4512 #if JS_HAS_XML_SUPPORT
4513 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
4514 tt
= js_GetToken(cx
, ts
);
4515 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
4516 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
4520 if (tt
== TOK_NAME
) {
4521 pn2
->pn_op
= JSOP_GETPROP
;
4523 pn2
->pn_atom
= pn3
->pn_atom
;
4524 RecycleTree(pn3
, tc
);
4526 if (TOKEN_TYPE_IS_XML(tt
)) {
4527 pn2
->pn_type
= TOK_LB
;
4528 pn2
->pn_op
= JSOP_GETELEM
;
4529 } else if (tt
== TOK_RP
) {
4530 JSParseNode
*group
= pn3
;
4532 /* Recycle the useless TOK_RP node. */
4533 pn3
= group
->pn_kid
;
4534 group
->pn_kid
= NULL
;
4535 RecycleTree(group
, tc
);
4536 pn2
->pn_type
= TOK_FILTER
;
4537 pn2
->pn_op
= JSOP_FILTER
;
4539 /* A filtering predicate is like a with statement. */
4540 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
4542 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4543 JSMSG_NAME_AFTER_DOT
);
4546 pn2
->pn_arity
= PN_BINARY
;
4548 pn2
->pn_right
= pn3
;
4551 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
4552 MUST_MATCH_TOKEN(TOK_NAME
, JSMSG_NAME_AFTER_DOT
);
4553 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
4554 pn2
->pn_op
= JSOP_GETPROP
;
4556 pn2
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
4558 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4559 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4560 #if JS_HAS_XML_SUPPORT
4561 } else if (tt
== TOK_DBLDOT
) {
4562 pn2
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
4565 ts
->flags
|= TSF_OPERAND
| TSF_KEYWORD_IS_NAME
;
4566 tt
= js_GetToken(cx
, ts
);
4567 ts
->flags
&= ~(TSF_OPERAND
| TSF_KEYWORD_IS_NAME
);
4568 pn3
= PrimaryExpr(cx
, ts
, tc
, tt
, JS_TRUE
);
4572 if (tt
== TOK_NAME
) {
4573 pn3
->pn_type
= TOK_STRING
;
4574 pn3
->pn_arity
= PN_NULLARY
;
4575 pn3
->pn_op
= JSOP_QNAMEPART
;
4576 } else if (!TOKEN_TYPE_IS_XML(tt
)) {
4577 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4578 JSMSG_NAME_AFTER_DOT
);
4581 pn2
->pn_op
= JSOP_DESCENDANTS
;
4583 pn2
->pn_right
= pn3
;
4584 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4585 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4587 } else if (tt
== TOK_LB
) {
4588 pn2
= NewParseNode(cx
, ts
, PN_BINARY
, tc
);
4591 pn3
= Expr(cx
, ts
, tc
);
4595 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_IN_INDEX
);
4596 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4597 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4600 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
4601 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
4602 * the interpreter from fast property access. However, if the
4603 * bracketed string is a uint32, we rewrite pn3 to be a number
4604 * instead of a string.
4607 if (pn3
->pn_type
== TOK_STRING
) {
4610 if (!js_IdIsIndex(ATOM_TO_JSID(pn3
->pn_atom
), &index
)) {
4611 pn2
->pn_type
= TOK_DOT
;
4612 pn2
->pn_op
= JSOP_GETPROP
;
4613 pn2
->pn_arity
= PN_NAME
;
4615 pn2
->pn_atom
= pn3
->pn_atom
;
4618 pn3
->pn_type
= TOK_NUMBER
;
4619 pn3
->pn_op
= JSOP_DOUBLE
;
4620 pn3
->pn_dval
= index
;
4622 pn2
->pn_op
= JSOP_GETELEM
;
4624 pn2
->pn_right
= pn3
;
4626 } else if (allowCallSyntax
&& tt
== TOK_LP
) {
4627 pn2
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
4631 pn2
->pn_op
= JSOP_CALL
;
4632 if (pn
->pn_op
== JSOP_NAME
&&
4633 pn
->pn_atom
== cx
->runtime
->atomState
.evalAtom
) {
4634 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
4635 pn2
->pn_op
= JSOP_EVAL
;
4636 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
4637 } else if (pn
->pn_op
== JSOP_GETPROP
&&
4638 (pn
->pn_atom
== cx
->runtime
->atomState
.applyAtom
||
4639 pn
->pn_atom
== cx
->runtime
->atomState
.callAtom
)) {
4640 /* Pick JSOP_APPLY if apply(...). */
4641 pn2
->pn_op
= JSOP_APPLY
;
4644 PN_INIT_LIST_1(pn2
, pn
);
4645 pn2
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4647 if (!ArgumentList(cx
, ts
, tc
, pn2
))
4649 if (pn2
->pn_count
> ARGC_LIMIT
) {
4650 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4651 JSMSG_TOO_MANY_FUN_ARGS
);
4654 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
4662 if (tt
== TOK_ERROR
)
4667 static JSParseNode
*
4668 BracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4674 * Always accept the 'in' operator in a parenthesized expression,
4675 * where it's unambiguous, even if we might be parsing the init of a
4678 oldflags
= tc
->flags
;
4679 tc
->flags
&= ~TCF_IN_FOR_INIT
;
4680 pn
= Expr(cx
, ts
, tc
);
4681 tc
->flags
= oldflags
| (tc
->flags
& TCF_FUN_FLAGS
);
4685 #if JS_HAS_XML_SUPPORT
4687 static JSParseNode
*
4688 EndBracketedExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4692 pn
= BracketedExpr(cx
, ts
, tc
);
4696 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_ATTR_EXPR
);
4701 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
4703 * AttributeIdentifier:
4704 * @ PropertySelector
4705 * @ QualifiedIdentifier
4712 * QualifiedIdentifier:
4713 * PropertySelector :: PropertySelector
4714 * PropertySelector :: [ Expression ]
4716 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
4718 * AttributeIdentifier:
4719 * @ QualifiedIdentifier
4726 * QualifiedIdentifier:
4727 * PropertySelector :: PropertySelector
4728 * PropertySelector :: [ Expression ]
4731 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
4732 * for that rule to result in a name node, but ECMA-357 extends the grammar
4733 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
4735 * QualifiedIdentifier:
4736 * PropertySelector QualifiedSuffix
4739 * :: PropertySelector
4743 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
4745 * PrimaryExpression:
4746 * Identifier QualifiedSuffix
4748 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
4749 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
4751 static JSParseNode
*
4752 PropertySelector(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4756 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
4759 if (pn
->pn_type
== TOK_STAR
) {
4760 pn
->pn_type
= TOK_ANYNAME
;
4761 pn
->pn_op
= JSOP_ANYNAME
;
4762 pn
->pn_atom
= cx
->runtime
->atomState
.starAtom
;
4764 JS_ASSERT(pn
->pn_type
== TOK_NAME
);
4765 pn
->pn_op
= JSOP_QNAMEPART
;
4766 pn
->pn_arity
= PN_NAME
;
4767 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
4773 static JSParseNode
*
4774 QualifiedSuffix(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
4777 JSParseNode
*pn2
, *pn3
;
4780 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_DBLCOLON
);
4781 pn2
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
4785 /* Left operand of :: must be evaluated if it is an identifier. */
4786 if (pn
->pn_op
== JSOP_QNAMEPART
)
4787 pn
->pn_op
= JSOP_NAME
;
4789 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
4790 tt
= js_GetToken(cx
, ts
);
4791 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
4792 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
4793 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
4794 pn2
->pn_op
= JSOP_QNAMECONST
;
4795 pn2
->pn_atom
= (tt
== TOK_STAR
)
4796 ? cx
->runtime
->atomState
.starAtom
4797 : CURRENT_TOKEN(ts
).t_atom
;
4804 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4805 JSMSG_SYNTAX_ERROR
);
4808 pn3
= EndBracketedExpr(cx
, ts
, tc
);
4812 pn2
->pn_op
= JSOP_QNAME
;
4813 pn2
->pn_arity
= PN_BINARY
;
4815 pn2
->pn_right
= pn3
;
4819 static JSParseNode
*
4820 QualifiedIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4824 pn
= PropertySelector(cx
, ts
, tc
);
4827 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
))
4828 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
4832 static JSParseNode
*
4833 AttributeIdentifier(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4835 JSParseNode
*pn
, *pn2
;
4838 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_AT
);
4839 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4842 pn
->pn_op
= JSOP_TOATTRNAME
;
4843 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
4844 tt
= js_GetToken(cx
, ts
);
4845 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
4846 if (tt
== TOK_STAR
|| tt
== TOK_NAME
) {
4847 pn2
= QualifiedIdentifier(cx
, ts
, tc
);
4848 } else if (tt
== TOK_LB
) {
4849 pn2
= EndBracketedExpr(cx
, ts
, tc
);
4851 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
4852 JSMSG_SYNTAX_ERROR
);
4862 * Make a TOK_LC unary node whose pn_kid is an expression.
4864 static JSParseNode
*
4865 XMLExpr(JSContext
*cx
, JSTokenStream
*ts
, JSBool inTag
, JSTreeContext
*tc
)
4867 JSParseNode
*pn
, *pn2
;
4870 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LC
);
4871 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
4876 * Turn off XML tag mode, but don't restore it after parsing this braced
4877 * expression. Instead, simply restore ts's old flags. This is required
4878 * because XMLExpr is called both from within a tag, and from within text
4879 * contained in an element, but outside of any start, end, or point tag.
4881 oldflags
= ts
->flags
;
4882 ts
->flags
= oldflags
& ~TSF_XMLTAGMODE
;
4883 pn2
= Expr(cx
, ts
, tc
);
4887 MUST_MATCH_TOKEN(TOK_RC
, JSMSG_CURLY_IN_XML_EXPR
);
4888 ts
->flags
= oldflags
;
4890 pn
->pn_op
= inTag
? JSOP_XMLTAGEXPR
: JSOP_XMLELTEXPR
;
4895 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
4896 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
4897 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
4898 * child of a container tag.
4900 static JSParseNode
*
4901 XMLAtomNode(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4906 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
4909 tp
= &CURRENT_TOKEN(ts
);
4910 pn
->pn_op
= tp
->t_op
;
4911 pn
->pn_atom
= tp
->t_atom
;
4912 if (tp
->type
== TOK_XMLPI
)
4913 pn
->pn_atom2
= tp
->t_atom2
;
4918 * Parse the productions:
4921 * XMLName XMLNameExpr?
4922 * { Expr } XMLNameExpr?
4924 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
4925 * a list of names and/or expressions, a single expression, or a single name.
4926 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
4929 static JSParseNode
*
4930 XMLNameExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
)
4932 JSParseNode
*pn
, *pn2
, *list
;
4937 tt
= CURRENT_TOKEN(ts
).type
;
4939 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
4943 JS_ASSERT(tt
== TOK_XMLNAME
);
4944 pn2
= XMLAtomNode(cx
, ts
, tc
);
4953 list
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
4956 list
->pn_type
= TOK_XMLNAME
;
4957 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
4958 PN_INIT_LIST_1(list
, pn
);
4959 list
->pn_extra
= PNX_CANTFOLD
;
4962 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
4965 } while ((tt
= js_GetToken(cx
, ts
)) == TOK_XMLNAME
|| tt
== TOK_LC
);
4972 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
4973 * at compile time into a JSXML tree.
4975 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
4976 ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \
4977 : (pn)->pn_type != TOK_LC)
4980 * Parse the productions:
4984 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
4985 * XMLTagContent S XMLNameExpr S? = S? { Expr }
4987 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
4988 * produces a list of name and attribute values and/or braced expressions, a
4989 * single expression, or a single name.
4991 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
4992 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
4993 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
4994 * we parsed exactly one expression.
4996 static JSParseNode
*
4997 XMLTagContent(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
4998 JSTokenType tagtype
, JSAtom
**namep
)
5000 JSParseNode
*pn
, *pn2
, *list
;
5003 pn
= XMLNameExpr(cx
, ts
, tc
);
5006 *namep
= (pn
->pn_arity
== PN_NULLARY
) ? pn
->pn_atom
: NULL
;
5009 while (js_MatchToken(cx
, ts
, TOK_XMLSPACE
)) {
5010 tt
= js_GetToken(cx
, ts
);
5011 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
5016 pn2
= XMLNameExpr(cx
, ts
, tc
);
5020 list
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
5023 list
->pn_type
= tagtype
;
5024 list
->pn_pos
.begin
= pn
->pn_pos
.begin
;
5025 PN_INIT_LIST_1(list
, pn
);
5029 if (!XML_FOLDABLE(pn2
))
5030 pn
->pn_extra
|= PNX_CANTFOLD
;
5032 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
5033 MUST_MATCH_TOKEN(TOK_ASSIGN
, JSMSG_NO_ASSIGN_IN_XML_ATTR
);
5034 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
5036 tt
= js_GetToken(cx
, ts
);
5037 if (tt
== TOK_XMLATTR
) {
5038 pn2
= XMLAtomNode(cx
, ts
, tc
);
5039 } else if (tt
== TOK_LC
) {
5040 pn2
= XMLExpr(cx
, ts
, JS_TRUE
, tc
);
5041 pn
->pn_extra
|= PNX_CANTFOLD
;
5043 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5044 JSMSG_BAD_XML_ATTR_VALUE
);
5049 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5056 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
5058 if ((tt) <= TOK_EOF) { \
5059 if ((tt) == TOK_EOF) { \
5060 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
5061 JSMSG_END_OF_XML_SOURCE); \
5067 static JSParseNode
*
5068 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
5072 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
5073 * that opens the end tag for the container.
5076 XMLElementContent(JSContext
*cx
, JSTokenStream
*ts
, JSParseNode
*pn
,
5083 ts
->flags
&= ~TSF_XMLTAGMODE
;
5085 ts
->flags
|= TSF_XMLTEXTMODE
;
5086 tt
= js_GetToken(cx
, ts
);
5087 ts
->flags
&= ~TSF_XMLTEXTMODE
;
5088 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
5090 JS_ASSERT(tt
== TOK_XMLSPACE
|| tt
== TOK_XMLTEXT
);
5091 textAtom
= CURRENT_TOKEN(ts
).t_atom
;
5093 /* Non-zero-length XML text scanned. */
5094 pn2
= XMLAtomNode(cx
, ts
, tc
);
5097 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5101 ts
->flags
|= TSF_OPERAND
;
5102 tt
= js_GetToken(cx
, ts
);
5103 ts
->flags
&= ~TSF_OPERAND
;
5104 XML_CHECK_FOR_ERROR_AND_EOF(tt
, JS_FALSE
);
5105 if (tt
== TOK_XMLETAGO
)
5109 pn2
= XMLExpr(cx
, ts
, JS_FALSE
, tc
);
5110 pn
->pn_extra
|= PNX_CANTFOLD
;
5111 } else if (tt
== TOK_XMLSTAGO
) {
5112 pn2
= XMLElementOrList(cx
, ts
, tc
, JS_FALSE
);
5114 pn2
->pn_extra
&= ~PNX_XMLROOT
;
5115 pn
->pn_extra
|= pn2
->pn_extra
;
5118 JS_ASSERT(tt
== TOK_XMLCDATA
|| tt
== TOK_XMLCOMMENT
||
5120 pn2
= XMLAtomNode(cx
, ts
, tc
);
5124 pn
->pn_pos
.end
= pn2
->pn_pos
.end
;
5128 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLETAGO
);
5129 ts
->flags
|= TSF_XMLTAGMODE
;
5134 * Return a PN_LIST node containing an XML or XMLList Initialiser.
5136 static JSParseNode
*
5137 XMLElementOrList(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
5140 JSParseNode
*pn
, *pn2
, *list
;
5142 JSAtom
*startAtom
, *endAtom
;
5144 JS_CHECK_RECURSION(cx
, return NULL
);
5146 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_XMLSTAGO
);
5147 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
5151 ts
->flags
|= TSF_XMLTAGMODE
;
5152 tt
= js_GetToken(cx
, ts
);
5153 if (tt
== TOK_ERROR
)
5156 if (tt
== TOK_XMLNAME
|| tt
== TOK_LC
) {
5158 * XMLElement. Append the tag and its contents, if any, to pn.
5160 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLSTAGO
, &startAtom
);
5163 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
5165 tt
= js_GetToken(cx
, ts
);
5166 if (tt
== TOK_XMLPTAGC
) {
5167 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
5168 if (pn2
->pn_type
== TOK_XMLSTAGO
) {
5170 RecycleTree(pn
, tc
);
5173 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
||
5174 pn2
->pn_type
== TOK_LC
);
5175 PN_INIT_LIST_1(pn
, pn2
);
5176 if (!XML_FOLDABLE(pn2
))
5177 pn
->pn_extra
|= PNX_CANTFOLD
;
5179 pn
->pn_type
= TOK_XMLPTAGC
;
5180 pn
->pn_extra
|= PNX_XMLROOT
;
5182 /* We had better have a tag-close (>) at this point. */
5183 if (tt
!= TOK_XMLTAGC
) {
5184 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5185 JSMSG_BAD_XML_TAG_SYNTAX
);
5188 pn2
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5190 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
5191 if (pn2
->pn_type
!= TOK_XMLSTAGO
) {
5192 PN_INIT_LIST_1(pn
, pn2
);
5193 if (!XML_FOLDABLE(pn2
))
5194 pn
->pn_extra
|= PNX_CANTFOLD
;
5196 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
5201 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
5202 pn
->pn_type
= TOK_XMLELEM
;
5203 PN_INIT_LIST_1(pn
, pn2
);
5204 if (!XML_FOLDABLE(pn2
))
5205 pn
->pn_extra
|= PNX_CANTFOLD
;
5206 pn
->pn_extra
|= PNX_XMLROOT
;
5208 /* Get element contents and delimiting end-tag-open sequence. */
5209 if (!XMLElementContent(cx
, ts
, pn
, tc
))
5212 tt
= js_GetToken(cx
, ts
);
5213 XML_CHECK_FOR_ERROR_AND_EOF(tt
, NULL
);
5214 if (tt
!= TOK_XMLNAME
&& tt
!= TOK_LC
) {
5215 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5216 JSMSG_BAD_XML_TAG_SYNTAX
);
5220 /* Parse end tag; check mismatch at compile-time if we can. */
5221 pn2
= XMLTagContent(cx
, ts
, tc
, TOK_XMLETAGO
, &endAtom
);
5224 if (pn2
->pn_type
== TOK_XMLETAGO
) {
5225 /* Oops, end tag has attributes! */
5226 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5227 JSMSG_BAD_XML_TAG_SYNTAX
);
5230 if (endAtom
&& startAtom
&& endAtom
!= startAtom
) {
5231 JSString
*str
= ATOM_TO_STRING(startAtom
);
5233 /* End vs. start tag name mismatch: point to the tag name. */
5234 js_ReportCompileErrorNumber(cx
, ts
, pn2
,
5235 JSREPORT_UC
| JSREPORT_ERROR
,
5236 JSMSG_XML_TAG_NAME_MISMATCH
,
5237 JSSTRING_CHARS(str
));
5241 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
5242 JS_ASSERT(pn2
->pn_type
== TOK_XMLNAME
|| pn2
->pn_type
== TOK_LC
);
5243 list
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
5246 list
->pn_type
= TOK_XMLETAGO
;
5247 PN_INIT_LIST_1(list
, pn2
);
5248 PN_APPEND(pn
, list
);
5249 if (!XML_FOLDABLE(pn2
)) {
5250 list
->pn_extra
|= PNX_CANTFOLD
;
5251 pn
->pn_extra
|= PNX_CANTFOLD
;
5254 js_MatchToken(cx
, ts
, TOK_XMLSPACE
);
5255 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_TAG_SYNTAX
);
5258 /* Set pn_op now that pn has been updated to its final value. */
5259 pn
->pn_op
= JSOP_TOXML
;
5260 } else if (allowList
&& tt
== TOK_XMLTAGC
) {
5261 /* XMLList Initialiser. */
5262 pn
->pn_type
= TOK_XMLLIST
;
5263 pn
->pn_op
= JSOP_TOXMLLIST
;
5265 pn
->pn_extra
|= PNX_XMLROOT
;
5266 if (!XMLElementContent(cx
, ts
, pn
, tc
))
5269 MUST_MATCH_TOKEN(TOK_XMLTAGC
, JSMSG_BAD_XML_LIST_SYNTAX
);
5271 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5272 JSMSG_BAD_XML_NAME_SYNTAX
);
5276 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5277 ts
->flags
&= ~TSF_XMLTAGMODE
;
5281 static JSParseNode
*
5282 XMLElementOrListRoot(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
5289 * Force XML support to be enabled so that comments and CDATA literals
5290 * are recognized, instead of <! followed by -- starting an HTML comment
5291 * to end of line (used in script tags to hide content from old browsers
5292 * that don't recognize <script>).
5294 oldopts
= JS_SetOptions(cx
, cx
->options
| JSOPTION_XML
);
5295 pn
= XMLElementOrList(cx
, ts
, tc
, allowList
);
5296 JS_SetOptions(cx
, oldopts
);
5300 JS_FRIEND_API(JSParseNode
*)
5301 js_ParseXMLText(JSContext
*cx
, JSObject
*chain
, JSParseContext
*pc
,
5309 * Push a compiler frame if we have no frames, or if the top frame is a
5310 * lightweight function activation, or if its scope chain doesn't match
5311 * the one passed to us.
5313 TREE_CONTEXT_INIT(&tc
, pc
);
5314 tc
.u
.scopeChain
= chain
;
5316 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
5317 TS(pc
)->flags
|= TSF_OPERAND
| TSF_XMLONLYMODE
;
5318 tt
= js_GetToken(cx
, TS(pc
));
5319 TS(pc
)->flags
&= ~TSF_OPERAND
;
5321 if (tt
!= TOK_XMLSTAGO
) {
5322 js_ReportCompileErrorNumber(cx
, TS(pc
), NULL
, JSREPORT_ERROR
,
5323 JSMSG_BAD_XML_MARKUP
);
5326 pn
= XMLElementOrListRoot(cx
, TS(pc
), &tc
, allowList
);
5329 TS(pc
)->flags
&= ~TSF_XMLONLYMODE
;
5330 TREE_CONTEXT_FINISH(cx
, &tc
);
5334 #endif /* JS_HAS_XMLSUPPORT */
5336 static JSParseNode
*
5337 PrimaryExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
5338 JSTokenType tt
, JSBool afterDot
)
5340 JSParseNode
*pn
, *pn2
, *pn3
;
5342 #if JS_HAS_SHARP_VARS
5343 JSParseNode
*defsharp
;
5347 JS_CHECK_RECURSION(cx
, return NULL
);
5349 #if JS_HAS_SHARP_VARS
5351 notsharp
= JS_FALSE
;
5354 * Control flows here after #n= is scanned. If the following primary is
5355 * not valid after such a "sharp variable" definition, the tt switch case
5356 * should set notsharp.
5360 #if JS_HAS_GETTER_SETTER
5361 if (tt
== TOK_NAME
) {
5362 tt
= CheckGetterOrSetter(cx
, ts
, TOK_FUNCTION
);
5363 if (tt
== TOK_ERROR
)
5370 #if JS_HAS_XML_SUPPORT
5371 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
5372 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
5373 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
5374 pn2
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5377 pn2
->pn_type
= TOK_FUNCTION
;
5378 pn
= QualifiedSuffix(cx
, ts
, pn2
, tc
);
5383 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
5385 pn
= FunctionExpr(cx
, ts
, tc
);
5395 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
5398 pn
->pn_type
= TOK_RB
;
5399 pn
->pn_op
= JSOP_NEWINIT
;
5401 #if JS_HAS_SHARP_VARS
5403 PN_INIT_LIST_1(pn
, defsharp
);
5409 ts
->flags
|= TSF_OPERAND
;
5410 matched
= js_MatchToken(cx
, ts
, TOK_RB
);
5411 ts
->flags
&= ~TSF_OPERAND
;
5413 for (index
= 0; ; index
++) {
5414 if (index
== ARRAY_INIT_LIMIT
) {
5415 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5416 JSMSG_ARRAY_INIT_TOO_BIG
);
5420 ts
->flags
|= TSF_OPERAND
;
5421 tt
= js_PeekToken(cx
, ts
);
5422 ts
->flags
&= ~TSF_OPERAND
;
5424 pn
->pn_extra
|= PNX_ENDCOMMA
;
5428 if (tt
== TOK_COMMA
) {
5429 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
5430 js_MatchToken(cx
, ts
, TOK_COMMA
);
5431 pn2
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5433 pn2
= AssignExpr(cx
, ts
, tc
);
5439 if (tt
!= TOK_COMMA
) {
5440 /* If we didn't already match TOK_COMMA in above case. */
5441 if (!js_MatchToken(cx
, ts
, TOK_COMMA
))
5446 #if JS_HAS_GENERATORS
5448 * At this point, (index == 0 && pn->pn_count != 0) implies one
5449 * element initialiser was parsed (possibly with a defsharp before
5450 * the left bracket).
5452 * An array comprehension of the form:
5454 * [i * j for (i in o) for (j in p) if (i != j)]
5456 * translates to roughly the following let expression:
5458 * let (array = new Array, i, j) {
5459 * for (i in o) let {
5467 * where array is a nameless block-local variable. The "roughly"
5468 * means that an implementation may optimize away the array.push.
5469 * An array comprehension opens exactly one block scope, no matter
5470 * how many for heads it contains.
5472 * Each let () {...} or for (let ...) ... compiles to:
5474 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
5476 * where <o> is a literal object representing the block scope,
5477 * with <n> properties, naming each var declared in the block.
5479 * Each var declaration in a let-block binds a name in <o> at
5480 * compile time, and allocates a slot on the operand stack at
5481 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
5482 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
5483 * JSOP_FORLOCAL. These ops all have an immediate operand, the
5484 * local slot's stack index from fp->spbase.
5486 * The array comprehension iteration step, array.push(i * j) in
5487 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
5488 * where <array> is the index of array's stack slot.
5491 pn
->pn_count
!= 0 &&
5492 js_MatchToken(cx
, ts
, TOK_FOR
)) {
5493 JSParseNode
*pnexp
, *pntop
;
5495 /* Relabel pn as an array comprehension node. */
5496 pn
->pn_type
= TOK_ARRAYCOMP
;
5499 * Remove the comprehension expression from pn's linked list
5500 * and save it via pnexp. We'll re-install it underneath the
5501 * ARRAYPUSH node after we parse the rest of the comprehension.
5503 pnexp
= PN_LAST(pn
);
5504 JS_ASSERT(pn
->pn_count
== 1 || pn
->pn_count
== 2);
5505 pn
->pn_tail
= (--pn
->pn_count
== 1)
5506 ? &pn
->pn_head
->pn_next
5508 *pn
->pn_tail
= NULL
;
5510 pntop
= ComprehensionTail(cx
, ts
, tc
, TOK_ARRAYPUSH
,
5511 JSOP_ARRAYPUSH
, pnexp
);
5514 PN_APPEND(pn
, pntop
);
5516 #endif /* JS_HAS_GENERATORS */
5518 MUST_MATCH_TOKEN(TOK_RB
, JSMSG_BRACKET_AFTER_LIST
);
5520 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5529 pn
= NewParseNode(cx
, ts
, PN_LIST
, tc
);
5532 pn
->pn_type
= TOK_RC
;
5533 pn
->pn_op
= JSOP_NEWINIT
;
5535 #if JS_HAS_SHARP_VARS
5537 PN_INIT_LIST_1(pn
, defsharp
);
5543 afterComma
= JS_FALSE
;
5545 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
5546 tt
= js_GetToken(cx
, ts
);
5547 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
5550 pn3
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5552 pn3
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
5555 #if JS_HAS_GETTER_SETTER
5559 atom
= CURRENT_TOKEN(ts
).t_atom
;
5560 if (atom
== cx
->runtime
->atomState
.getAtom
)
5562 else if (atom
== cx
->runtime
->atomState
.setAtom
)
5567 ts
->flags
|= TSF_KEYWORD_IS_NAME
;
5568 tt
= js_GetToken(cx
, ts
);
5569 ts
->flags
&= ~TSF_KEYWORD_IS_NAME
;
5570 if (tt
!= TOK_NAME
) {
5574 pn3
= NewParseNode(cx
, ts
, PN_NAME
, tc
);
5577 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
5580 /* We have to fake a 'function' token here. */
5581 CURRENT_TOKEN(ts
).t_op
= JSOP_NOP
;
5582 CURRENT_TOKEN(ts
).type
= TOK_FUNCTION
;
5583 pn2
= FunctionExpr(cx
, ts
, tc
);
5584 pn2
= NewBinary(cx
, TOK_COLON
, op
, pn3
, pn2
, tc
);
5590 pn3
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5592 pn3
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
5596 !js_ReportCompileErrorNumber(cx
, ts
, NULL
,
5599 JSMSG_TRAILING_COMMA
)) {
5604 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5609 tt
= js_GetToken(cx
, ts
);
5610 #if JS_HAS_GETTER_SETTER
5611 if (tt
== TOK_NAME
) {
5612 tt
= CheckGetterOrSetter(cx
, ts
, TOK_COLON
);
5613 if (tt
== TOK_ERROR
)
5618 if (tt
!= TOK_COLON
) {
5619 #if JS_HAS_DESTRUCTURING_SHORTHAND
5620 if (tt
!= TOK_COMMA
&& tt
!= TOK_RC
) {
5622 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5623 JSMSG_COLON_AFTER_ID
);
5625 #if JS_HAS_DESTRUCTURING_SHORTHAND
5629 * Support, e.g., |var {x, y} = o| as destructuring shorthand
5630 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
5633 pn
->pn_extra
|= PNX_SHORTHAND
;
5635 if (pnval
->pn_type
== TOK_NAME
) {
5636 pnval
->pn_arity
= PN_NAME
;
5637 pnval
->pn_expr
= NULL
;
5638 pnval
->pn_slot
= -1;
5639 pnval
->pn_const
= JS_FALSE
;
5644 op
= CURRENT_TOKEN(ts
).t_op
;
5645 pnval
= AssignExpr(cx
, ts
, tc
);
5648 pn2
= NewBinary(cx
, TOK_COLON
, op
, pn3
, pnval
, tc
);
5649 #if JS_HAS_GETTER_SETTER
5656 tt
= js_GetToken(cx
, ts
);
5659 if (tt
!= TOK_COMMA
) {
5660 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5661 JSMSG_CURLY_AFTER_LIST
);
5664 afterComma
= JS_TRUE
;
5668 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5672 #if JS_HAS_BLOCK_SCOPE
5674 pn
= LetBlock(cx
, ts
, tc
, JS_FALSE
);
5680 #if JS_HAS_SHARP_VARS
5684 defsharp
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
5687 defsharp
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
5688 ts
->flags
|= TSF_OPERAND
;
5689 tt
= js_GetToken(cx
, ts
);
5690 ts
->flags
&= ~TSF_OPERAND
;
5694 /* Check for forward/dangling references at runtime, to allow eval. */
5695 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5698 pn
->pn_num
= (jsint
) CURRENT_TOKEN(ts
).t_dval
;
5701 #endif /* JS_HAS_SHARP_VARS */
5707 pn
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
5710 pn2
= ParenExpr(cx
, ts
, tc
, pn
, &genexp
);
5716 MUST_MATCH_TOKEN(TOK_RP
, JSMSG_PAREN_IN_PAREN
);
5717 if (pn2
->pn_type
== TOK_RP
||
5718 (js_CodeSpec
[pn2
->pn_op
].prec
>= js_CodeSpec
[JSOP_GETPROP
].prec
&&
5721 * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly
5722 * to help the decompiler look ahead from a JSOP_ENDINIT to see a
5723 * JSOP_GROUP followed by a POP or POPV. That sequence means the
5724 * parentheses are mandatory, to disambiguate object initialisers
5725 * as expression statements from block statements.
5727 * Also drop pn if pn2 is a member or a primary expression of any
5728 * kind. This is required to avoid generating a JSOP_GROUP that
5729 * will null the |obj| interpreter register, causing |this| in any
5730 * call of that member expression to bind to the global object.
5732 RecycleTree(pn
, tc
);
5735 pn
->pn_type
= TOK_RP
;
5738 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5742 #if JS_HAS_XML_SUPPORT
5744 pn
= QualifiedIdentifier(cx
, ts
, tc
);
5751 pn
= AttributeIdentifier(cx
, ts
, tc
);
5758 pn
= XMLElementOrListRoot(cx
, ts
, tc
, JS_TRUE
);
5761 notsharp
= JS_TRUE
; /* XXXbe could be sharp? */
5763 #endif /* JS_HAS_XML_SUPPORT */
5766 #if JS_HAS_SHARP_VARS
5771 #if JS_HAS_XML_SUPPORT
5773 case TOK_XMLCOMMENT
:
5777 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5780 pn
->pn_atom
= CURRENT_TOKEN(ts
).t_atom
;
5781 #if JS_HAS_XML_SUPPORT
5782 if (tt
== TOK_XMLPI
)
5783 pn
->pn_atom2
= CURRENT_TOKEN(ts
).t_atom2
;
5786 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
5787 if (tt
== TOK_NAME
) {
5788 pn
->pn_arity
= PN_NAME
;
5791 #if JS_HAS_XML_SUPPORT
5792 if (js_MatchToken(cx
, ts
, TOK_DBLCOLON
)) {
5797 * Here PrimaryExpr is called after '.' or '..' and we
5798 * just scanned .name:: or ..name:: . This is the only
5799 * case where a keyword after '.' or '..' is not
5800 * treated as a property name.
5802 str
= ATOM_TO_STRING(pn
->pn_atom
);
5803 tt
= js_CheckKeyword(JSSTRING_CHARS(str
),
5804 JSSTRING_LENGTH(str
));
5805 if (tt
== TOK_FUNCTION
) {
5806 pn
->pn_arity
= PN_NULLARY
;
5807 pn
->pn_type
= TOK_FUNCTION
;
5808 } else if (tt
!= TOK_EOF
) {
5809 js_ReportCompileErrorNumber(
5810 cx
, ts
, NULL
, JSREPORT_ERROR
,
5811 JSMSG_KEYWORD_NOT_NS
);
5815 pn
= QualifiedSuffix(cx
, ts
, pn
, tc
);
5822 /* Unqualified __parent__ and __proto__ uses require activations. */
5823 if (pn
->pn_atom
== cx
->runtime
->atomState
.parentAtom
||
5824 pn
->pn_atom
== cx
->runtime
->atomState
.protoAtom
) {
5825 tc
->flags
|= TCF_FUN_HEAVYWEIGHT
;
5834 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5838 /* Token stream ensures that tokenbuf is NUL-terminated. */
5839 JS_ASSERT(*ts
->tokenbuf
.ptr
== (jschar
) 0);
5840 obj
= js_NewRegExpObject(cx
, ts
,
5842 ts
->tokenbuf
.ptr
- ts
->tokenbuf
.base
,
5843 CURRENT_TOKEN(ts
).t_reflags
);
5846 if (!(tc
->flags
& TCF_COMPILE_N_GO
)) {
5847 STOBJ_CLEAR_PARENT(obj
);
5848 STOBJ_CLEAR_PROTO(obj
);
5851 pn
->pn_pob
= js_NewParsedObjectBox(cx
, tc
->parseContext
, obj
);
5855 pn
->pn_op
= JSOP_REGEXP
;
5860 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5863 pn
->pn_op
= JSOP_DOUBLE
;
5864 pn
->pn_dval
= CURRENT_TOKEN(ts
).t_dval
;
5865 #if JS_HAS_SHARP_VARS
5871 pn
= NewParseNode(cx
, ts
, PN_NULLARY
, tc
);
5874 pn
->pn_op
= CURRENT_TOKEN(ts
).t_op
;
5875 #if JS_HAS_SHARP_VARS
5881 /* The scanner or one of its subroutines reported the error. */
5885 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5886 JSMSG_SYNTAX_ERROR
);
5890 #if JS_HAS_SHARP_VARS
5894 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5895 JSMSG_BAD_SHARP_VAR_DEF
);
5898 defsharp
->pn_kid
= pn
;
5905 static JSParseNode
*
5906 ParenExpr(JSContext
*cx
, JSTokenStream
*ts
, JSTreeContext
*tc
,
5907 JSParseNode
*pn1
, JSBool
*genexp
)
5911 #if JS_HAS_GENERATOR_EXPRS
5912 uintN oldflags
= tc
->flags
;
5915 JS_ASSERT(CURRENT_TOKEN(ts
).type
== TOK_LP
);
5916 begin
= CURRENT_TOKEN(ts
).pos
.begin
;
5920 pn
= BracketedExpr(cx
, ts
, tc
);
5924 #if JS_HAS_GENERATOR_EXPRS
5925 if (js_MatchToken(cx
, ts
, TOK_FOR
)) {
5926 if (pn
->pn_type
== TOK_YIELD
) {
5927 js_ReportCompileErrorNumber(cx
, ts
, pn
, JSREPORT_ERROR
,
5928 JSMSG_BAD_GENERATOR_SYNTAX
,
5932 if (pn
->pn_type
== TOK_COMMA
) {
5933 js_ReportCompileErrorNumber(cx
, ts
, PN_LAST(pn
), JSREPORT_ERROR
,
5934 JSMSG_BAD_GENERATOR_SYNTAX
,
5939 pn1
= NewParseNode(cx
, ts
, PN_UNARY
, tc
);
5943 pn
->pn_pos
.begin
= begin
;
5944 pn
= GeneratorExpr(cx
, ts
, tc
, oldflags
, pn1
, pn
);
5948 if (js_GetToken(cx
, ts
) != TOK_RP
) {
5949 js_ReportCompileErrorNumber(cx
, ts
, NULL
, JSREPORT_ERROR
,
5950 JSMSG_BAD_GENERATOR_SYNTAX
,
5954 pn
->pn_pos
.end
= CURRENT_TOKEN(ts
).pos
.end
;
5958 #endif /* JS_HAS_GENERATOR_EXPRS */
5964 * Fold from one constant type to another.
5965 * XXX handles only strings and numbers for now
5968 FoldType(JSContext
*cx
, JSParseNode
*pn
, JSTokenType type
)
5970 if (pn
->pn_type
!= type
) {
5973 if (pn
->pn_type
== TOK_STRING
) {
5975 if (!JS_ValueToNumber(cx
, ATOM_KEY(pn
->pn_atom
), &d
))
5978 pn
->pn_type
= TOK_NUMBER
;
5979 pn
->pn_op
= JSOP_DOUBLE
;
5984 if (pn
->pn_type
== TOK_NUMBER
) {
5985 JSString
*str
= js_NumberToString(cx
, pn
->pn_dval
);
5988 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
5991 pn
->pn_type
= TOK_STRING
;
5992 pn
->pn_op
= JSOP_STRING
;
6003 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
6004 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
6005 * a successful call to this function.
6008 FoldBinaryNumeric(JSContext
*cx
, JSOp op
, JSParseNode
*pn1
, JSParseNode
*pn2
,
6009 JSParseNode
*pn
, JSTreeContext
*tc
)
6014 JS_ASSERT(pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
);
6020 i
= js_DoubleToECMAInt32(d
);
6021 j
= js_DoubleToECMAInt32(d2
);
6023 d
= (op
== JSOP_LSH
) ? i
<< j
: i
>> j
;
6027 j
= js_DoubleToECMAInt32(d2
);
6029 d
= js_DoubleToECMAUint32(d
) >> j
;
6047 /* XXX MSVC miscompiles such that (NaN == 0) */
6048 if (JSDOUBLE_IS_NaN(d2
))
6049 d
= *cx
->runtime
->jsNaN
;
6052 if (d
== 0 || JSDOUBLE_IS_NaN(d
))
6053 d
= *cx
->runtime
->jsNaN
;
6054 else if ((JSDOUBLE_HI32(d
) ^ JSDOUBLE_HI32(d2
)) >> 31)
6055 d
= *cx
->runtime
->jsNegativeInfinity
;
6057 d
= *cx
->runtime
->jsPositiveInfinity
;
6065 d
= *cx
->runtime
->jsNaN
;
6068 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
6069 if (!(JSDOUBLE_IS_FINITE(d
) && JSDOUBLE_IS_INFINITE(d2
)))
6078 /* Take care to allow pn1 or pn2 to alias pn. */
6080 RecycleTree(pn1
, tc
);
6082 RecycleTree(pn2
, tc
);
6083 pn
->pn_type
= TOK_NUMBER
;
6084 pn
->pn_op
= JSOP_DOUBLE
;
6085 pn
->pn_arity
= PN_NULLARY
;
6090 #if JS_HAS_XML_SUPPORT
6093 FoldXMLConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
)
6096 JSParseNode
**pnp
, *pn1
, *pn2
;
6097 JSString
*accum
, *str
;
6099 JSTempValueRooter tvr
;
6101 JS_ASSERT(pn
->pn_arity
== PN_LIST
);
6106 if ((pn
->pn_extra
& PNX_CANTFOLD
) == 0) {
6107 if (tt
== TOK_XMLETAGO
)
6108 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.etagoAtom
);
6109 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
)
6110 accum
= ATOM_TO_STRING(cx
->runtime
->atomState
.stagoAtom
);
6114 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
6115 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
6116 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
6117 * Therefore, we have to add additonal protection from GC nesting under
6120 for (pn2
= pn1
, i
= j
= 0; pn2
; pn2
= pn2
->pn_next
, i
++) {
6121 /* The parser already rejected end-tags with attributes. */
6122 JS_ASSERT(tt
!= TOK_XMLETAGO
|| i
== 0);
6123 switch (pn2
->pn_type
) {
6132 if (pn2
->pn_arity
== PN_LIST
)
6134 str
= ATOM_TO_STRING(pn2
->pn_atom
);
6138 str
= js_MakeXMLCDATAString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
6143 case TOK_XMLCOMMENT
:
6144 str
= js_MakeXMLCommentString(cx
, ATOM_TO_STRING(pn2
->pn_atom
));
6150 str
= js_MakeXMLPIString(cx
, ATOM_TO_STRING(pn2
->pn_atom
),
6151 ATOM_TO_STRING(pn2
->pn_atom2
));
6158 JS_ASSERT(*pnp
== pn1
);
6159 if ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) &&
6160 (i
& 1) ^ (j
& 1)) {
6161 #ifdef DEBUG_brendanXXX
6162 printf("1: %d, %d => ", i
, j
);
6164 js_FileEscapedString(stdout
, accum
, 0);
6166 fputs("NULL", stdout
);
6167 fputc('\n', stdout
);
6169 } else if (accum
&& pn1
!= pn2
) {
6170 while (pn1
->pn_next
!= pn2
) {
6171 pn1
= RecycleTree(pn1
, tc
);
6174 pn1
->pn_type
= TOK_XMLTEXT
;
6175 pn1
->pn_op
= JSOP_STRING
;
6176 pn1
->pn_arity
= PN_NULLARY
;
6177 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
6180 JS_ASSERT(pnp
!= &pn1
->pn_next
);
6183 pnp
= &pn2
->pn_next
;
6190 JS_PUSH_TEMP_ROOT_STRING(cx
, accum
, &tvr
);
6191 str
= ((tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLPTAGC
) && i
!= 0)
6192 ? js_AddAttributePart(cx
, i
& 1, accum
, str
)
6193 : js_ConcatStrings(cx
, accum
, str
);
6194 JS_POP_TEMP_ROOT(cx
, &tvr
);
6197 #ifdef DEBUG_brendanXXX
6198 printf("2: %d, %d => ", i
, j
);
6199 js_FileEscapedString(stdout
, str
, 0);
6200 printf(" (%u)\n", JSSTRING_LENGTH(str
));
6209 if ((pn
->pn_extra
& PNX_CANTFOLD
) == 0) {
6210 if (tt
== TOK_XMLPTAGC
)
6211 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.ptagcAtom
);
6212 else if (tt
== TOK_XMLSTAGO
|| tt
== TOK_XMLETAGO
)
6213 str
= ATOM_TO_STRING(cx
->runtime
->atomState
.tagcAtom
);
6216 accum
= js_ConcatStrings(cx
, accum
, str
);
6221 JS_ASSERT(*pnp
== pn1
);
6222 while (pn1
->pn_next
) {
6223 pn1
= RecycleTree(pn1
, tc
);
6226 pn1
->pn_type
= TOK_XMLTEXT
;
6227 pn1
->pn_op
= JSOP_STRING
;
6228 pn1
->pn_arity
= PN_NULLARY
;
6229 pn1
->pn_atom
= js_AtomizeString(cx
, accum
, 0);
6232 JS_ASSERT(pnp
!= &pn1
->pn_next
);
6236 if (pn1
&& pn
->pn_count
== 1) {
6238 * Only one node under pn, and it has been folded: move pn1 onto pn
6239 * unless pn is an XML root (in which case we need it to tell the code
6240 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
6241 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
6242 * extra "<" and "/>" bracketing at runtime.
6244 if (!(pn
->pn_extra
& PNX_XMLROOT
)) {
6245 PN_MOVE_NODE(pn
, pn1
);
6246 } else if (tt
== TOK_XMLPTAGC
) {
6247 pn
->pn_type
= TOK_XMLELEM
;
6248 pn
->pn_op
= JSOP_TOXML
;
6254 #endif /* JS_HAS_XML_SUPPORT */
6257 StartsWith(JSParseNode
*pn
, JSTokenType tt
)
6259 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
6262 if (pn
->pn_type
== tt
)
6264 switch (pn
->pn_arity
) {
6266 return tt
== TOK_FUNCTION
;
6269 TAIL_RECURSE(pn
->pn_head
);
6273 TAIL_RECURSE(pn
->pn_kid1
);
6277 TAIL_RECURSE(pn
->pn_left
);
6280 /* A parenthesized expression starts with a left parenthesis. */
6281 if (pn
->pn_type
== TOK_RP
)
6282 return tt
== TOK_LP
;
6284 TAIL_RECURSE(pn
->pn_kid
);
6287 if (pn
->pn_type
== TOK_DOT
|| pn
->pn_type
== TOK_DBLDOT
)
6288 TAIL_RECURSE(pn
->pn_expr
);
6296 Boolish(JSParseNode
*pn
)
6298 switch (pn
->pn_op
) {
6300 return pn
->pn_dval
!= 0 && !JSDOUBLE_IS_NaN(pn
->pn_dval
);
6303 return JSSTRING_LENGTH(ATOM_TO_STRING(pn
->pn_atom
)) != 0;
6305 #if JS_HAS_GENERATOR_EXPRS
6309 * A generator expression as an if or loop condition has no effects, it
6310 * simply results in a truthy object reference. This condition folding
6311 * is needed for the decompiler. See bug 442342 and bug 443074.
6313 if (pn
->pn_count
!= 1)
6315 JSParseNode
*pn2
= pn
->pn_head
;
6316 if (pn2
->pn_type
!= TOK_FUNCTION
)
6318 if (!(pn2
->pn_flags
& TCF_GENEXP_LAMBDA
))
6325 case JSOP_NAMEDFUNOBJ
:
6326 case JSOP_ANONFUNOBJ
:
6341 js_FoldConstants(JSContext
*cx
, JSParseNode
*pn
, JSTreeContext
*tc
, bool inCond
)
6343 JSParseNode
*pn1
= NULL
, *pn2
= NULL
, *pn3
= NULL
;
6345 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
6347 switch (pn
->pn_arity
) {
6350 uint16 oldflags
= tc
->flags
;
6352 tc
->flags
= (uint16
) pn
->pn_flags
;
6353 if (!js_FoldConstants(cx
, pn
->pn_body
, tc
))
6355 tc
->flags
= oldflags
;
6361 /* Propagate inCond through logical connectives. */
6362 bool cond
= inCond
&& (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
);
6364 /* Save the list head in pn1 for later use. */
6365 for (pn1
= pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
6366 if (!js_FoldConstants(cx
, pn2
, tc
, cond
))
6373 /* Any kid may be null (e.g. for (;;)). */
6377 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_IF
))
6380 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_FORHEAD
))
6382 if (pn
->pn_type
== TOK_FORHEAD
&& pn2
->pn_op
== JSOP_TRUE
) {
6383 RecycleTree(pn2
, tc
);
6387 if (pn3
&& !js_FoldConstants(cx
, pn3
, tc
))
6395 /* Propagate inCond through logical connectives. */
6396 if (pn
->pn_type
== TOK_OR
|| pn
->pn_type
== TOK_AND
) {
6397 if (!js_FoldConstants(cx
, pn1
, tc
, inCond
))
6399 if (!js_FoldConstants(cx
, pn2
, tc
, inCond
))
6404 /* First kid may be null (for default case in switch). */
6405 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
, pn
->pn_type
== TOK_WHILE
))
6407 if (!js_FoldConstants(cx
, pn2
, tc
, pn
->pn_type
== TOK_DO
))
6412 /* Our kid may be null (e.g. return; vs. return e;). */
6415 !js_FoldConstants(cx
, pn1
, tc
,
6416 (inCond
&& pn
->pn_type
== TOK_RP
) ||
6417 pn
->pn_op
== JSOP_NOT
)) {
6424 * Skip pn1 down along a chain of dotted member expressions to avoid
6425 * excessive recursion. Our only goal here is to fold constants (if
6426 * any) in the primary expression operand to the left of the first
6430 while (pn1
&& pn1
->pn_arity
== PN_NAME
)
6432 if (pn1
&& !js_FoldConstants(cx
, pn1
, tc
))
6440 switch (pn
->pn_type
) {
6442 if (ContainsStmt(pn2
, TOK_VAR
) || ContainsStmt(pn3
, TOK_VAR
))
6447 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
6448 while (pn1
->pn_type
== TOK_RP
)
6450 switch (pn1
->pn_type
) {
6452 if (pn1
->pn_dval
== 0 || JSDOUBLE_IS_NaN(pn1
->pn_dval
))
6456 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1
->pn_atom
)) == 0)
6460 if (pn1
->pn_op
== JSOP_TRUE
)
6462 if (pn1
->pn_op
== JSOP_FALSE
|| pn1
->pn_op
== JSOP_NULL
) {
6468 /* Early return to dodge common code that copies pn2 to pn. */
6472 #if JS_HAS_GENERATOR_EXPRS
6473 /* Don't fold a trailing |if (0)| in a generator expression. */
6474 if (!pn2
&& (tc
->flags
& TCF_GENEXP_LAMBDA
))
6480 * pn2 is the then- or else-statement subtree to compile. Take
6481 * care not to expose an object initialiser, which would be parsed
6482 * as a block, to the Statement parser via eval(uneval(e)) where e
6483 * is '1 ? {p:2, q:3}[i] : r;' or the like.
6485 if (pn
->pn_type
== TOK_HOOK
&& StartsWith(pn2
, TOK_RC
)) {
6486 pn
->pn_type
= TOK_RP
;
6487 pn
->pn_arity
= PN_UNARY
;
6489 if (pn3
&& pn3
!= pn2
)
6490 RecycleTree(pn3
, tc
);
6493 PN_MOVE_NODE(pn
, pn2
);
6495 if (!pn2
|| (pn
->pn_type
== TOK_SEMI
&& !pn
->pn_kid
)) {
6497 * False condition and no else, or an empty then-statement was
6498 * moved up over pn. Either way, make pn an empty block (not an
6499 * empty statement, which does not decompile, even when labeled).
6500 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
6501 * or an empty statement for a child.
6503 pn
->pn_type
= TOK_LC
;
6504 pn
->pn_arity
= PN_LIST
;
6507 RecycleTree(pn2
, tc
);
6508 if (pn3
&& pn3
!= pn2
)
6509 RecycleTree(pn3
, tc
);
6515 if (pn
->pn_arity
== PN_LIST
) {
6516 JSParseNode
**pnp
= &pn
->pn_head
;
6517 JS_ASSERT(*pnp
== pn1
);
6519 int cond
= Boolish(pn1
);
6520 if (cond
== (pn
->pn_type
== TOK_OR
)) {
6521 for (pn2
= pn1
->pn_next
; pn2
; pn2
= pn3
) {
6523 RecycleTree(pn2
, tc
);
6526 pn1
->pn_next
= NULL
;
6530 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
6531 if (pn
->pn_count
== 1)
6533 *pnp
= pn1
->pn_next
;
6534 RecycleTree(pn1
, tc
);
6537 pnp
= &pn1
->pn_next
;
6539 } while ((pn1
= *pnp
) != NULL
);
6541 // We may have to change arity from LIST to BINARY.
6543 if (pn
->pn_count
== 2) {
6545 pn1
->pn_next
= NULL
;
6546 JS_ASSERT(!pn2
->pn_next
);
6547 pn
->pn_arity
= PN_BINARY
;
6550 } else if (pn
->pn_count
== 1) {
6551 PN_MOVE_NODE(pn
, pn1
);
6552 RecycleTree(pn1
, tc
);
6555 int cond
= Boolish(pn1
);
6556 if (cond
== (pn
->pn_type
== TOK_OR
)) {
6557 RecycleTree(pn2
, tc
);
6558 PN_MOVE_NODE(pn
, pn1
);
6559 } else if (cond
!= -1) {
6560 JS_ASSERT(cond
== (pn
->pn_type
== TOK_AND
));
6561 RecycleTree(pn1
, tc
);
6562 PN_MOVE_NODE(pn
, pn2
);
6570 * Compound operators such as *= should be subject to folding, in case
6571 * the left-hand side is constant, and so that the decompiler produces
6572 * the same string that you get from decompiling a script or function
6573 * compiled from that same string. As with +, += is special.
6575 if (pn
->pn_op
== JSOP_NOP
)
6577 if (pn
->pn_op
!= JSOP_ADD
)
6582 if (pn
->pn_arity
== PN_LIST
) {
6583 size_t length
, length2
;
6585 JSString
*str
, *str2
;
6588 * Any string literal term with all others number or string means
6589 * this is a concatenation. If any term is not a string or number
6590 * literal, we can't fold.
6592 JS_ASSERT(pn
->pn_count
> 2);
6593 if (pn
->pn_extra
& PNX_CANTFOLD
)
6595 if (pn
->pn_extra
!= PNX_STRCAT
)
6598 /* Ok, we're concatenating: convert non-string constant operands. */
6600 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
6601 if (!FoldType(cx
, pn2
, TOK_STRING
))
6603 /* XXX fold only if all operands convert to string */
6604 if (pn2
->pn_type
!= TOK_STRING
)
6606 length
+= JSFLATSTR_LENGTH(ATOM_TO_STRING(pn2
->pn_atom
));
6609 /* Allocate a new buffer and string descriptor for the result. */
6610 chars
= (jschar
*) JS_malloc(cx
, (length
+ 1) * sizeof(jschar
));
6613 str
= js_NewString(cx
, chars
, length
);
6619 /* Fill the buffer, advancing chars and recycling kids as we go. */
6620 for (pn2
= pn1
; pn2
; pn2
= RecycleTree(pn2
, tc
)) {
6621 str2
= ATOM_TO_STRING(pn2
->pn_atom
);
6622 length2
= JSFLATSTR_LENGTH(str2
);
6623 js_strncpy(chars
, JSFLATSTR_CHARS(str2
), length2
);
6628 /* Atomize the result string and mutate pn to refer to it. */
6629 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
6632 pn
->pn_type
= TOK_STRING
;
6633 pn
->pn_op
= JSOP_STRING
;
6634 pn
->pn_arity
= PN_NULLARY
;
6638 /* Handle a binary string concatenation. */
6639 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
6640 if (pn1
->pn_type
== TOK_STRING
|| pn2
->pn_type
== TOK_STRING
) {
6641 JSString
*left
, *right
, *str
;
6643 if (!FoldType(cx
, (pn1
->pn_type
!= TOK_STRING
) ? pn1
: pn2
,
6647 if (pn1
->pn_type
!= TOK_STRING
|| pn2
->pn_type
!= TOK_STRING
)
6649 left
= ATOM_TO_STRING(pn1
->pn_atom
);
6650 right
= ATOM_TO_STRING(pn2
->pn_atom
);
6651 str
= js_ConcatStrings(cx
, left
, right
);
6654 pn
->pn_atom
= js_AtomizeString(cx
, str
, 0);
6657 pn
->pn_type
= TOK_STRING
;
6658 pn
->pn_op
= JSOP_STRING
;
6659 pn
->pn_arity
= PN_NULLARY
;
6660 RecycleTree(pn1
, tc
);
6661 RecycleTree(pn2
, tc
);
6665 /* Can't concatenate string literals, let's try numbers. */
6673 if (pn
->pn_arity
== PN_LIST
) {
6674 JS_ASSERT(pn
->pn_count
> 2);
6675 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
6676 if (!FoldType(cx
, pn2
, TOK_NUMBER
))
6679 for (pn2
= pn1
; pn2
; pn2
= pn2
->pn_next
) {
6680 /* XXX fold only if all operands convert to number */
6681 if (pn2
->pn_type
!= TOK_NUMBER
)
6685 JSOp op
= PN_OP(pn
);
6689 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
, tc
))
6691 while ((pn2
= pn3
) != NULL
) {
6693 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
, tc
))
6698 JS_ASSERT(pn
->pn_arity
== PN_BINARY
);
6699 if (!FoldType(cx
, pn1
, TOK_NUMBER
) ||
6700 !FoldType(cx
, pn2
, TOK_NUMBER
)) {
6703 if (pn1
->pn_type
== TOK_NUMBER
&& pn2
->pn_type
== TOK_NUMBER
) {
6704 if (!FoldBinaryNumeric(cx
, PN_OP(pn
), pn1
, pn2
, pn
, tc
))
6711 while (pn1
->pn_type
== TOK_RP
)
6713 if (pn1
->pn_type
== TOK_NUMBER
) {
6716 /* Operate on one numeric constant. */
6718 switch (pn
->pn_op
) {
6720 d
= ~js_DoubleToECMAInt32(d
);
6726 * Negation of a zero doesn't produce a negative
6727 * zero on HPUX. Perform the operation by bit
6730 JSDOUBLE_HI32(d
) ^= JSDOUBLE_HI32_SIGNBIT
;
6740 pn
->pn_type
= TOK_PRIMARY
;
6741 pn
->pn_op
= (d
== 0 || JSDOUBLE_IS_NaN(d
)) ? JSOP_TRUE
: JSOP_FALSE
;
6742 pn
->pn_arity
= PN_NULLARY
;
6746 /* Return early to dodge the common TOK_NUMBER code. */
6749 pn
->pn_type
= TOK_NUMBER
;
6750 pn
->pn_op
= JSOP_DOUBLE
;
6751 pn
->pn_arity
= PN_NULLARY
;
6753 RecycleTree(pn1
, tc
);
6754 } else if (pn1
->pn_type
== TOK_PRIMARY
) {
6755 if (pn
->pn_op
== JSOP_NOT
&&
6756 (pn1
->pn_op
== JSOP_TRUE
||
6757 pn1
->pn_op
== JSOP_FALSE
)) {
6758 PN_MOVE_NODE(pn
, pn1
);
6759 pn
->pn_op
= (pn
->pn_op
== JSOP_TRUE
) ? JSOP_FALSE
: JSOP_TRUE
;
6760 RecycleTree(pn1
, tc
);
6765 #if JS_HAS_XML_SUPPORT
6772 if (pn
->pn_arity
== PN_LIST
) {
6773 JS_ASSERT(pn
->pn_type
== TOK_XMLLIST
|| pn
->pn_count
!= 0);
6774 if (!FoldXMLConstants(cx
, pn
, tc
))
6780 if (pn1
->pn_type
== TOK_XMLNAME
) {
6782 JSParsedObjectBox
*xmlpob
;
6784 v
= ATOM_KEY(pn1
->pn_atom
);
6785 if (!js_ToAttributeName(cx
, &v
))
6787 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
6789 xmlpob
= js_NewParsedObjectBox(cx
, tc
->parseContext
,
6790 JSVAL_TO_OBJECT(v
));
6794 pn
->pn_type
= TOK_XMLNAME
;
6795 pn
->pn_op
= JSOP_OBJECT
;
6796 pn
->pn_arity
= PN_NULLARY
;
6797 pn
->pn_pob
= xmlpob
;
6798 RecycleTree(pn1
, tc
);
6801 #endif /* JS_HAS_XML_SUPPORT */
6807 int cond
= Boolish(pn
);
6809 if (pn
->pn_arity
== PN_LIST
) {
6813 RecycleTree(pn2
, tc
);
6814 } while ((pn2
= pn3
) != NULL
);
6816 pn
->pn_type
= TOK_PRIMARY
;
6817 pn
->pn_op
= cond
? JSOP_TRUE
: JSOP_FALSE
;
6818 pn
->pn_arity
= PN_NULLARY
;