Bug 470165 - Cleanup the GTK nsFilePicker code; r+sr=roc
[wine-gecko.git] / js / src / jsparse.cpp
blob74f3638a0a12b2998e3f5f9874c4645bb4be0995
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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 ***** */
42 * JS parser.
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
50 * generate bytecode.
52 * This parser attempts no error recovery.
54 #include "jsstddef.h"
55 #include <stdlib.h>
56 #include <string.h>
57 #include <math.h>
58 #include "jstypes.h"
59 #include "jsarena.h" /* Added by JSIFY */
60 #include "jsutil.h" /* Added by JSIFY */
61 #include "jsapi.h"
62 #include "jsarray.h"
63 #include "jsatom.h"
64 #include "jscntxt.h"
65 #include "jsversion.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsinterp.h"
69 #include "jsiter.h"
70 #include "jslock.h"
71 #include "jsnum.h"
72 #include "jsobj.h"
73 #include "jsopcode.h"
74 #include "jsparse.h"
75 #include "jsscan.h"
76 #include "jsscope.h"
77 #include "jsscript.h"
78 #include "jsstr.h"
79 #include "jsstaticcheck.h"
81 #if JS_HAS_XML_SUPPORT
82 #include "jsxml.h"
83 #endif
85 #if JS_HAS_DESTRUCTURING
86 #include "jsdhash.h"
87 #endif
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) \
147 JS_BEGIN_MACRO \
148 if (js_GetToken(cx, ts) != tt) { \
149 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
150 return NULL; \
152 JS_END_MACRO
154 #ifdef METER_PARSENODES
155 static uint32 parsenodes = 0;
156 static uint32 maxparsenodes = 0;
157 static uint32 recyclednodes = 0;
158 #endif
160 JSBool
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);
171 return JS_FALSE;
173 if (principals)
174 JSPRINCIPALS_HOLD(cx, principals);
175 pc->principals = principals;
176 pc->callerFrame = callerFrame;
177 pc->nodeList = NULL;
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);
183 return JS_TRUE;
186 void
187 js_FinishParseContext(JSContext *cx, JSParseContext *pc)
189 if (pc->principals)
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);
198 void
199 js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,
200 JSPrincipals *principals)
202 JS_ASSERT(!pc->principals);
203 if (principals)
204 JSPRINCIPALS_HOLD(cx, principals);
205 pc->principals = principals;
208 JSParsedObjectBox *
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.
219 JS_ASSERT(obj);
220 JS_ARENA_ALLOCATE_TYPE(pob, JSParsedObjectBox, &cx->tempPool);
221 if (!pob) {
222 js_ReportOutOfScriptQuota(cx);
223 return NULL;
225 pob->traceLink = pc->traceListHead;
226 pob->emitLink = NULL;
227 pob->object = obj;
228 pc->traceListHead = pob;
229 return pob;
233 void
234 js_TraceParseContext(JSTracer *trc, JSParseContext *pc)
236 JSParsedObjectBox *pob;
238 JS_ASSERT(pc->tempRoot.u.parseContext == pc);
239 pob = pc->traceListHead;
240 while (pob) {
241 JS_CALL_OBJECT_TRACER(trc, pob->object, "parser.object");
242 pob = pob->traceLink;
246 static JSParseNode *
247 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
249 JSParseNode *next;
251 if (!pn)
252 return NULL;
254 /* Catch back-to-back dup recycles. */
255 JS_ASSERT(pn != tc->parseContext->nodeList);
256 next = pn->pn_next;
257 pn->pn_next = tc->parseContext->nodeList;
258 tc->parseContext->nodeList = pn;
259 #ifdef METER_PARSENODES
260 recyclednodes++;
261 #endif
262 return next;
265 static JSParseNode *
266 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
268 JSParseNode *pn;
270 pn = tc->parseContext->nodeList;
271 if (!pn) {
272 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
273 if (!pn)
274 js_ReportOutOfScriptQuota(cx);
275 } else {
276 tc->parseContext->nodeList = pn->pn_next;
278 /* Recycle immediate descendents only, to save work and working set. */
279 switch (pn->pn_arity) {
280 case PN_FUNC:
281 RecycleTree(pn->pn_body, tc);
282 break;
283 case PN_LIST:
284 if (pn->pn_head) {
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;
290 #endif
292 break;
293 case PN_TERNARY:
294 RecycleTree(pn->pn_kid1, tc);
295 RecycleTree(pn->pn_kid2, tc);
296 RecycleTree(pn->pn_kid3, tc);
297 break;
298 case PN_BINARY:
299 if (pn->pn_left != pn->pn_right)
300 RecycleTree(pn->pn_left, tc);
301 RecycleTree(pn->pn_right, tc);
302 break;
303 case PN_UNARY:
304 RecycleTree(pn->pn_kid, tc);
305 break;
306 case PN_NAME:
307 RecycleTree(pn->pn_expr, tc);
308 break;
309 case PN_NULLARY:
310 break;
313 if (pn) {
314 #ifdef METER_PARSENODES
315 parsenodes++;
316 if (parsenodes - recyclednodes > maxparsenodes)
317 maxparsenodes = parsenodes - recyclednodes;
318 #endif
319 memset(&pn->pn_u, 0, sizeof pn->pn_u);
320 pn->pn_next = NULL;
322 return pn;
326 * Allocate a JSParseNode from cx's temporary arena.
328 static JSParseNode *
329 NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
330 JSTreeContext *tc)
332 JSParseNode *pn;
333 JSToken *tp;
335 pn = NewOrRecycledNode(cx, tc);
336 if (!pn)
337 return NULL;
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;
343 return pn;
346 static JSParseNode *
347 NewBinary(JSContext *cx, JSTokenType tt,
348 JSOp op, JSParseNode *left, JSParseNode *right,
349 JSTreeContext *tc)
351 JSParseNode *pn, *pn1, *pn2;
353 if (!left || !right)
354 return NULL;
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 &&
361 left->pn_op == op &&
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;
387 return left;
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);
403 return left;
406 pn = NewOrRecycledNode(cx, tc);
407 if (!pn)
408 return NULL;
409 pn->pn_type = tt;
410 pn->pn_pos.begin = left->pn_pos.begin;
411 pn->pn_pos.end = right->pn_pos.end;
412 pn->pn_op = op;
413 pn->pn_arity = PN_BINARY;
414 pn->pn_left = left;
415 pn->pn_right = right;
416 return pn;
419 #if JS_HAS_GETTER_SETTER
420 static JSTokenType
421 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
423 JSAtom *atom;
424 JSRuntime *rt;
425 JSOp op;
426 const char *name;
428 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
429 atom = CURRENT_TOKEN(ts).t_atom;
430 rt = cx->runtime;
431 if (atom == rt->atomState.getterAtom)
432 op = JSOP_GETTER;
433 else if (atom == rt->atomState.setterAtom)
434 op = JSOP_SETTER;
435 else
436 return TOK_NAME;
437 if (js_PeekTokenSameLine(cx, ts) != tt)
438 return TOK_NAME;
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,
443 (op == JSOP_GETTER)
444 ? js_getter_str
445 : js_setter_str);
446 return TOK_ERROR;
448 CURRENT_TOKEN(ts).t_op = op;
449 if (JS_HAS_STRICT_OPTION(cx)) {
450 name = js_AtomToPrintableString(cx, atom);
451 if (!name ||
452 !js_ReportCompileErrorNumber(cx, ts, NULL,
453 JSREPORT_WARNING | JSREPORT_STRICT,
454 JSMSG_DEPRECATED_USAGE,
455 name)) {
456 return TOK_ERROR;
459 return tt;
461 #endif
464 * Parse a top-level JS script.
466 JSParseNode *
467 js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc)
469 JSTreeContext tc;
470 JSParseNode *pn;
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);
483 if (pn) {
484 if (!js_MatchToken(cx, TS(pc), TOK_EOF)) {
485 js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,
486 JSMSG_SYNTAX_ERROR);
487 pn = NULL;
488 } else {
489 pn->pn_type = TOK_LC;
490 if (!js_FoldConstants(cx, pn, &tc))
491 pn = NULL;
495 TREE_CONTEXT_FINISH(cx, &tc);
496 return pn;
500 * Compile a top-level script.
502 extern JSScript *
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)
508 JSParseContext pc;
509 JSArenaPool codePool, notePool;
510 JSCodeGenerator cg;
511 JSTokenType tt;
512 JSParseNode *pn;
513 uint32 scriptGlobals;
514 JSScript *script;
515 #ifdef METER_PARSENODES
516 void *sbrk(ptrdiff_t), *before = sbrk(0);
517 #endif
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)) {
531 return NULL;
534 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
535 &cx->scriptStackQuota);
536 JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
537 &cx->scriptStackQuota);
538 js_InitCodeGenerator(cx, &cg, &pc, &codePool, &notePool,
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. */
559 for (;;) {
560 pc.tokenStream.flags |= TSF_OPERAND;
561 tt = js_PeekToken(cx, &pc.tokenStream);
562 pc.tokenStream.flags &= ~TSF_OPERAND;
563 if (tt <= TOK_EOF) {
564 if (tt == TOK_EOF)
565 break;
566 JS_ASSERT(tt == TOK_ERROR);
567 script = NULL;
568 goto out;
571 pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
572 if (!pn) {
573 script = NULL;
574 goto out;
576 JS_ASSERT(!cg.treeContext.blockNode);
578 if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
579 !js_EmitTree(cx, &cg, pn)) {
580 script = NULL;
581 goto out;
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;
594 JSOp op;
595 const JSCodeSpec *cs;
596 uintN len, slot;
598 if (scriptGlobals >= SLOTNO_LIMIT)
599 goto too_many_slots;
600 code = CG_BASE(&cg);
601 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
602 JS_ASSERT(code < end);
603 op = (JSOp) *code;
604 cs = &js_CodeSpec[op];
605 len = (cs->length > 0)
606 ? (uintN) cs->length
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)
619 goto too_many_slots;
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,
628 parsenodes,
629 maxparsenodes,
630 parsenodes - recyclednodes);
631 before = sbrk(0);
632 #endif
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) {
639 script = NULL;
640 goto out;
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);
645 #endif
646 #ifdef JS_ARENAMETER
647 JS_DumpArenaStats(stdout);
648 #endif
649 script = js_NewScriptFromCG(cx, &cg);
651 #ifdef JS_SCOPE_DEPTH_METER
652 if (script) {
653 JSObject *obj = scopeChain;
654 uintN depth = 1;
655 while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
656 ++depth;
657 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
659 #endif
661 out:
662 js_FinishCodeGenerator(cx, &cg);
663 JS_FinishArenaPool(&codePool);
664 JS_FinishArenaPool(&notePool);
665 js_FinishParseContext(cx, &pc);
666 return script;
668 too_many_slots:
669 js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
670 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
671 script = NULL;
672 goto out;
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
685 static int
686 HasFinalReturn(JSParseNode *pn)
688 JSParseNode *pn2, *pn3;
689 uintN rv, rv2, hasDefault;
691 switch (pn->pn_type) {
692 case TOK_LC:
693 if (!pn->pn_head)
694 return ENDS_IN_OTHER;
695 return HasFinalReturn(PN_LAST(pn));
697 case TOK_IF:
698 if (!pn->pn_kid3)
699 return ENDS_IN_OTHER;
700 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
702 case TOK_WHILE:
703 pn2 = pn->pn_left;
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;
710 case TOK_DO:
711 pn2 = pn->pn_right;
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;
725 case TOK_FOR:
726 pn2 = pn->pn_left;
727 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
728 return ENDS_IN_RETURN;
729 return ENDS_IN_OTHER;
731 case TOK_SWITCH:
732 rv = ENDS_IN_RETURN;
733 hasDefault = ENDS_IN_OTHER;
734 pn2 = pn->pn_right;
735 if (pn2->pn_type == TOK_LEXICALSCOPE)
736 pn2 = pn2->pn_expr;
737 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
738 if (pn2->pn_type == TOK_DEFAULT)
739 hasDefault = ENDS_IN_RETURN;
740 pn3 = pn2->pn_right;
741 JS_ASSERT(pn3->pn_type == TOK_LC);
742 if (pn3->pn_head) {
743 rv2 = HasFinalReturn(PN_LAST(pn3));
744 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
745 /* Falling through to next case or default. */;
746 else
747 rv &= rv2;
750 /* If a final switch has no default case, we judge it harshly. */
751 rv &= hasDefault;
752 return rv;
754 case TOK_BREAK:
755 return ENDS_IN_BREAK;
757 case TOK_WITH:
758 return HasFinalReturn(pn->pn_right);
760 case TOK_RETURN:
761 return ENDS_IN_RETURN;
763 case TOK_COLON:
764 case TOK_LEXICALSCOPE:
765 return HasFinalReturn(pn->pn_expr);
767 case TOK_THROW:
768 return ENDS_IN_RETURN;
770 case TOK_TRY:
771 /* If we have a finally block that returns, we are done. */
772 if (pn->pn_kid3) {
773 rv = HasFinalReturn(pn->pn_kid3);
774 if (rv == ENDS_IN_RETURN)
775 return rv;
778 /* Else check the try block and any and all catch statements. */
779 rv = HasFinalReturn(pn->pn_kid1);
780 if (pn->pn_kid2) {
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);
785 return rv;
787 case TOK_CATCH:
788 /* Check this catch block's body. */
789 return HasFinalReturn(pn->pn_kid3);
791 case TOK_LET:
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);
797 default:
798 return ENDS_IN_OTHER;
802 static JSBool
803 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
804 uintN anonerrnum)
806 const char *name;
808 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
809 if (tc->u.fun->atom) {
810 name = js_AtomToPrintableString(cx, tc->u.fun->atom);
811 } else {
812 errnum = anonerrnum;
813 name = NULL;
815 return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,
816 errnum, name);
819 static JSBool
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);
828 static JSParseNode *
829 FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
831 JSStmtInfo stmtInfo;
832 uintN oldflags, firstLine;
833 JSParseNode *pn;
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);
851 } else {
852 pn = NewParseNode(cx, ts, PN_UNARY, tc);
853 if (pn) {
854 pn->pn_kid = AssignExpr(cx, ts, tc);
855 if (!pn->pn_kid) {
856 pn = NULL;
857 } else {
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);
862 pn = NULL;
863 } else {
864 pn->pn_type = TOK_RETURN;
865 pn->pn_op = JSOP_RETURN;
866 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
871 #else
872 pn = Statements(cx, ts, tc);
873 #endif
875 if (pn) {
876 js_PopStatement(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)) {
882 pn = NULL;
886 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
887 return pn;
891 * Compile a JS function body, which might appear as the value of an event
892 * handler attribute in an HTML <INPUT> tag.
894 JSBool
895 js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
896 const jschar *chars, size_t length,
897 const char *filename, uintN lineno)
899 JSParseContext pc;
900 JSArenaPool codePool, notePool;
901 JSCodeGenerator funcg;
902 JSParseNode *pn;
904 if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,
905 filename, lineno)) {
906 return JS_FALSE;
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(&notePool, "note", 1024, sizeof(jssrcnote),
913 &cx->scriptStackQuota);
914 js_InitCodeGenerator(cx, &funcg, &pc, &codePool, &notePool,
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
925 * JSParseNode tree.
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);
932 if (pn) {
933 if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {
934 js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
935 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
936 pn = NULL;
937 } else {
938 if (!js_FoldConstants(cx, pn, &funcg.treeContext) ||
939 !js_EmitFunctionScript(cx, &funcg, pn)) {
940 pn = NULL;
945 /* Restore saved state and release code generation arenas. */
946 js_FinishCodeGenerator(cx, &funcg);
947 JS_FinishArenaPool(&codePool);
948 JS_FinishArenaPool(&notePool);
949 js_FinishParseContext(cx, &pc);
950 return pn != NULL;
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;
962 typedef JSBool
963 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
965 struct BindData {
966 JSParseNode *pn; /* error source coordinate */
967 JSOp op; /* prolog bytecode or nop */
968 Binder binder; /* binder, discriminates u */
969 union {
970 struct {
971 uintN overflow;
972 } let;
973 } u;
976 static JSBool
977 BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
979 const char *name;
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);
987 if (!name ||
988 !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,
989 JSREPORT_WARNING | JSREPORT_STRICT,
990 JSMSG_DUPLICATE_FORMAL,
991 name)) {
992 return JS_FALSE;
996 return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);
999 static JSBool
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)
1012 return JS_TRUE;
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,
1023 JSTokenType tt);
1025 static JSBool
1026 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1027 JSTreeContext *tc)
1029 JSAtomListElement *ale;
1030 const char *name;
1032 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1033 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1034 if (!ale) {
1035 ale = js_IndexAtom(cx, atom, &tc->decls);
1036 if (!ale)
1037 return JS_FALSE;
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);
1043 if (!name ||
1044 !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1045 JSREPORT_WARNING | JSREPORT_STRICT,
1046 JSMSG_DUPLICATE_FORMAL,
1047 name)) {
1048 return JS_FALSE;
1050 } else {
1051 if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))
1052 return JS_FALSE;
1054 return JS_TRUE;
1056 #endif /* JS_HAS_DESTRUCTURING */
1058 static JSFunction *
1059 NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
1060 uintN lambda)
1062 JSObject *parent;
1063 JSFunction *fun;
1065 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1066 parent = (tc->flags & TCF_IN_FUNCTION)
1067 ? FUN_OBJECT(tc->u.fun)
1068 : tc->u.scopeChain;
1069 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1070 parent, atom);
1071 if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1072 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1073 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1075 return fun;
1078 static JSParseNode *
1079 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
1080 uintN lambda)
1082 JSOp op, prevop;
1083 JSParseNode *pn, *body, *result;
1084 JSTokenType tt;
1085 JSAtom *funAtom;
1086 JSParsedObjectBox *funpob;
1087 JSAtomListElement *ale;
1088 JSFunction *fun;
1089 JSTreeContext funtc;
1090 #if JS_HAS_DESTRUCTURING
1091 JSParseNode *item, *list = NULL;
1092 #endif
1094 /* Make a TOK_FUNCTION node. */
1095 #if JS_HAS_GETTER_SETTER
1096 op = CURRENT_TOKEN(ts).t_op;
1097 #endif
1098 pn = NewParseNode(cx, ts, PN_FUNC, tc);
1099 if (!pn)
1100 return NULL;
1101 #ifdef DEBUG
1102 pn->pn_index = (uint32) -1;
1103 #endif
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;
1111 } else {
1112 if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
1113 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1114 JSMSG_SYNTAX_ERROR);
1115 return NULL;
1117 funAtom = NULL;
1118 js_UngetToken(ts);
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);
1127 if (ale) {
1128 prevop = ALE_JSOP(ale);
1129 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
1130 const char *name = js_AtomToPrintableString(cx, funAtom);
1131 if (!name ||
1132 !js_ReportCompileErrorNumber(cx, ts, NULL,
1133 (prevop != JSOP_DEFCONST)
1134 ? JSREPORT_WARNING |
1135 JSREPORT_STRICT
1136 : JSREPORT_ERROR,
1137 JSMSG_REDECLARED_VAR,
1138 (prevop == JSOP_DEFFUN)
1139 ? js_function_str
1140 : (prevop == JSOP_DEFCONST)
1141 ? js_const_str
1142 : js_var_str,
1143 name)) {
1144 return NULL;
1147 if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
1148 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1149 } else {
1150 ale = js_IndexAtom(cx, funAtom, &tc->decls);
1151 if (!ale)
1152 return NULL;
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
1172 * exists.
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))
1177 return NULL;
1182 fun = NewCompilerFunction(cx, tc, funAtom, lambda);
1183 if (!fun)
1184 return NULL;
1186 #if JS_HAS_GETTER_SETTER
1187 if (op != JSOP_NOP)
1188 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
1189 #endif
1192 * Create wrapping box for fun->object early to protect against a
1193 * last-ditch GC.
1195 funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));
1196 if (!funpob)
1197 return NULL;
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);
1202 funtc.u.fun = fun;
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)) {
1207 do {
1208 tt = js_GetToken(cx, ts);
1209 switch (tt) {
1210 #if JS_HAS_DESTRUCTURING
1211 case TOK_LB:
1212 case TOK_LC:
1214 BindData data;
1215 JSParseNode *lhs, *rhs;
1216 jsint slot;
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.
1224 data.pn = NULL;
1225 data.op = JSOP_DEFVAR;
1226 data.binder = BindDestructuringArg;
1227 lhs = DestructuringExpr(cx, &data, &funtc, tt);
1228 if (!lhs)
1229 return NULL;
1232 * Adjust fun->nargs to count the single anonymous positional
1233 * parameter that is to be destructured.
1235 slot = fun->nargs;
1236 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
1237 return NULL;
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);
1245 if (!rhs)
1246 return NULL;
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);
1253 if (!item)
1254 return NULL;
1255 if (!list) {
1256 list = NewParseNode(cx, ts, PN_LIST, tc);
1257 if (!list)
1258 return NULL;
1259 list->pn_type = TOK_COMMA;
1260 PN_INIT_LIST(list);
1262 PN_APPEND(list, item);
1263 break;
1265 #endif /* JS_HAS_DESTRUCTURING */
1267 case TOK_NAME:
1268 if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc))
1269 return NULL;
1270 break;
1272 default:
1273 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1274 JSMSG_MISSING_FORMAL);
1275 return NULL;
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;
1286 if (tt != TOK_LC) {
1287 js_UngetToken(ts);
1288 fun->flags |= JSFUN_EXPR_CLOSURE;
1290 #else
1291 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1292 #endif
1293 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
1295 body = FunctionBody(cx, ts, &funtc);
1296 if (!body)
1297 return NULL;
1299 #if JS_HAS_EXPR_CLOSURES
1300 if (tt == TOK_LC)
1301 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1302 else if (lambda == 0)
1303 js_MatchToken(cx, ts, TOK_SEMI);
1304 #else
1305 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1306 #endif
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.
1317 if (list) {
1318 if (body->pn_arity != PN_LIST) {
1319 JSParseNode *block;
1321 block = NewParseNode(cx, ts, PN_LIST, tc);
1322 if (!block)
1323 return NULL;
1324 block->pn_type = TOK_SEQ;
1325 block->pn_pos = body->pn_pos;
1326 PN_INIT_LIST_1(block, body);
1328 body = block;
1331 item = NewParseNode(cx, ts, PN_UNARY, tc);
1332 if (!item)
1333 return NULL;
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;
1342 ++body->pn_count;
1344 #endif
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
1351 * per invocation).
1353 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1354 fun->flags |= JSFUN_HEAVYWEIGHT;
1355 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1356 } else {
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;
1372 result = pn;
1373 if (lambda != 0) {
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);
1387 if (!result)
1388 return NULL;
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
1398 * sub-statement.
1400 op = JSOP_DEFFUN;
1401 } else {
1402 op = JSOP_NOP;
1405 pn->pn_funpob = funpob;
1406 pn->pn_op = op;
1407 pn->pn_body = body;
1408 pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO);
1409 TREE_CONTEXT_FINISH(cx, &funtc);
1410 return result;
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;
1434 JSTokenType tt;
1436 JS_CHECK_RECURSION(cx, return NULL);
1438 pn = NewParseNode(cx, ts, PN_LIST, tc);
1439 if (!pn)
1440 return NULL;
1441 saveBlock = tc->blockNode;
1442 tc->blockNode = pn;
1443 PN_INIT_LIST(pn);
1445 for (;;) {
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)
1451 return NULL;
1452 break;
1454 pn2 = Statement(cx, ts, tc);
1455 if (!pn2) {
1456 if (ts->flags & TSF_EOF)
1457 ts->flags |= TSF_UNEXPECTED_EOF;
1458 return NULL;
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
1465 * rest of nodes.
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;
1473 else
1474 tc->flags |= TCF_HAS_FUNCTION_STMT;
1476 PN_APPEND(pn, pn2);
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)
1485 pn = tc->blockNode;
1486 tc->blockNode = saveBlock;
1488 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1489 return pn;
1492 static JSParseNode *
1493 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1495 JSParseNode *pn;
1497 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1498 pn = ParenExpr(cx, ts, tc, NULL, NULL);
1499 if (!pn)
1500 return 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,
1514 "")) {
1515 return NULL;
1518 return pn;
1521 static JSBool
1522 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1524 JSAtom *label;
1525 JSTokenType tt;
1527 tt = js_PeekTokenSameLine(cx, ts);
1528 if (tt == TOK_ERROR)
1529 return JS_FALSE;
1530 if (tt == TOK_NAME) {
1531 (void) js_GetToken(cx, ts);
1532 label = CURRENT_TOKEN(ts).t_atom;
1533 } else {
1534 label = NULL;
1536 pn->pn_atom = label;
1537 return JS_TRUE;
1540 static JSBool
1541 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1543 JSObject *blockObj;
1544 JSScopeProperty *sprop;
1545 JSAtomListElement *ale;
1546 uintN n;
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)) {
1552 const char *name;
1554 if (sprop) {
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);
1560 if (name) {
1561 js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1562 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
1563 (ale && ALE_JSOP(ale) == JSOP_DEFCONST)
1564 ? js_const_str
1565 : "variable",
1566 name);
1568 return JS_FALSE;
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);
1575 return JS_FALSE;
1578 /* Use JSPROP_ENUMERATE to aid the disassembler. */
1579 return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),
1580 JSVAL_VOID, NULL, NULL,
1581 JSPROP_ENUMERATE |
1582 JSPROP_PERMANENT |
1583 JSPROP_SHARED,
1584 SPROP_HAS_SHORTID, (int16) n, NULL);
1587 static JSBool
1588 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1590 JSStmtInfo *stmt;
1591 JSAtomListElement *ale;
1592 JSOp op, prevop;
1593 const char *name;
1594 JSLocalKind localKind;
1596 stmt = js_LexicalLookup(tc, atom, NULL);
1597 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1598 op = data->op;
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);
1605 if (!name ||
1606 !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1607 (op != JSOP_DEFCONST &&
1608 prevop != JSOP_DEFCONST)
1609 ? JSREPORT_WARNING |
1610 JSREPORT_STRICT
1611 : JSREPORT_ERROR,
1612 JSMSG_REDECLARED_VAR,
1613 (prevop == JSOP_DEFFUN)
1614 ? js_function_str
1615 : (prevop == JSOP_DEFCONST)
1616 ? js_const_str
1617 : js_var_str,
1618 name)) {
1619 return JS_FALSE;
1622 if (op == JSOP_DEFVAR && prevop == JSOP_DEFFUN)
1623 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1625 if (!ale) {
1626 ale = js_IndexAtom(cx, atom, &tc->decls);
1627 if (!ale)
1628 return JS_FALSE;
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
1635 * compile time.
1637 return JS_TRUE;
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
1648 * slot-less vars.
1650 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
1651 if (!js_InWithStatement(tc) &&
1652 !BindLocalVariable(cx, tc->u.fun, atom, localKind)) {
1653 return JS_FALSE;
1655 } else if (localKind == JSLOCAL_ARG) {
1656 name = js_AtomToPrintableString(cx, atom);
1657 if (!name)
1658 return JS_FALSE;
1660 if (op == JSOP_DEFCONST) {
1661 js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1662 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
1663 name);
1664 return JS_FALSE;
1666 if (!js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1667 JSREPORT_WARNING | JSREPORT_STRICT,
1668 JSMSG_VAR_HIDES_ARG, name)) {
1669 return JS_FALSE;
1671 } else {
1672 /* Not an argument, must be a redeclared local var. */
1673 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1675 return JS_TRUE;
1678 static JSBool
1679 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
1681 JSParseNode *pn2;
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);
1685 pn2 = pn->pn_head;
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);
1689 return JS_FALSE;
1691 pn->pn_op = JSOP_SETCALL;
1692 return JS_TRUE;
1695 #if JS_HAS_DESTRUCTURING
1697 static JSBool
1698 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
1699 JSTreeContext *tc)
1701 JSAtom *atom;
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);
1709 atom = pn->pn_atom;
1710 if (atom == cx->runtime->atomState.argumentsAtom)
1711 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1713 data->pn = pn;
1714 if (!data->binder(cx, data, atom, tc))
1715 return JS_FALSE;
1716 data->pn = NULL;
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;
1727 } else {
1728 pn->pn_op = JSOP_SETNAME;
1729 pn->pn_const = JS_FALSE;
1731 return JS_TRUE;
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.
1752 static JSBool
1753 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
1755 while (pn->pn_type == TOK_RP)
1756 pn = pn->pn_kid;
1758 switch (pn->pn_type) {
1759 case TOK_NAME:
1760 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
1761 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1762 /* FALL THROUGH */
1763 case TOK_DOT:
1764 case TOK_LB:
1765 pn->pn_op = JSOP_SETNAME;
1766 break;
1768 #if JS_HAS_LVALUE_RETURN
1769 case TOK_LP:
1770 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
1771 return JS_FALSE;
1772 break;
1773 #endif
1775 #if JS_HAS_XML_SUPPORT
1776 case TOK_UNARYOP:
1777 if (pn->pn_op == JSOP_XMLNAME) {
1778 pn->pn_op = JSOP_BINDXMLNAME;
1779 break;
1781 /* FALL THROUGH */
1782 #endif
1784 default:
1785 js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,
1786 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
1787 return JS_FALSE;
1790 return JS_TRUE;
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 */
1797 } FindPropValData;
1799 typedef struct FindPropValEntry {
1800 JSDHashEntryHdr hdr;
1801 JSParseNode *pnkey;
1802 JSParseNode *pnval;
1803 } FindPropValEntry;
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);
1823 static JSBool
1824 MatchFindPropValEntry(JSDHashTable *table,
1825 const JSDHashEntryHdr *entry,
1826 const void *key)
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 = {
1839 JS_DHashAllocTable,
1840 JS_DHashFreeTable,
1841 HashFindPropValKey,
1842 MatchFindPropValEntry,
1843 JS_DHashMoveEntryStub,
1844 JS_DHashClearEntryStub,
1845 JS_DHashFinalizeStub,
1846 NULL
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;
1858 uint32 step;
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)
1869 return NULL;
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.
1875 pnhit = NULL;
1876 step = 0;
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) {
1889 pnhit = pnprop;
1891 ++step;
1894 } else {
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) {
1902 pnhit = pnprop;
1904 ++step;
1908 if (!pnhit)
1909 return NULL;
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,
1927 JS_DHASH_ADD);
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.
1940 static JSBool
1941 CheckDestructuring(JSContext *cx, BindData *data,
1942 JSParseNode *left, JSParseNode *right,
1943 JSTreeContext *tc)
1945 JSBool ok;
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);
1952 return JS_FALSE;
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);
1959 return JS_FALSE;
1961 #endif
1963 fpvd.table.ops = NULL;
1964 lhs = left->pn_head;
1965 if (lhs && lhs->pn_type == TOK_DEFSHARP) {
1966 pn = lhs;
1967 goto no_var_name;
1970 if (left->pn_type == TOK_RB) {
1971 rhs = (right && right->pn_type == left->pn_type)
1972 ? right->pn_head
1973 : NULL;
1975 while (lhs) {
1976 pn = lhs, pn2 = rhs;
1977 if (!data) {
1978 /* Skip parenthesization if not in a variable declaration. */
1979 while (pn->pn_type == TOK_RP)
1980 pn = pn->pn_kid;
1981 if (pn2) {
1982 while (pn2->pn_type == TOK_RP)
1983 pn2 = pn2->pn_kid;
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);
1991 } else {
1992 if (data) {
1993 if (pn->pn_type != TOK_NAME)
1994 goto no_var_name;
1996 ok = BindDestructuringVar(cx, data, pn, tc);
1997 } else {
1998 ok = BindDestructuringLHS(cx, pn, tc);
2001 if (!ok)
2002 goto out;
2005 lhs = lhs->pn_next;
2006 if (rhs)
2007 rhs = rhs->pn_next;
2009 } else {
2010 JS_ASSERT(left->pn_type == TOK_RC);
2011 fpvd.numvars = left->pn_count;
2012 fpvd.maxstep = 0;
2013 rhs = NULL;
2015 while (lhs) {
2016 JS_ASSERT(lhs->pn_type == TOK_COLON);
2017 pn = lhs->pn_right;
2018 if (!data) {
2019 /* Skip parenthesization if not in a variable declaration. */
2020 while (pn->pn_type == TOK_RP)
2021 pn = pn->pn_kid;
2024 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
2025 if (right) {
2026 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
2027 if (rhs && !data) {
2028 while (rhs->pn_type == TOK_RP)
2029 rhs = rhs->pn_kid;
2033 ok = CheckDestructuring(cx, data, pn, rhs, tc);
2034 } else if (data) {
2035 if (pn->pn_type != TOK_NAME)
2036 goto no_var_name;
2038 ok = BindDestructuringVar(cx, data, pn, tc);
2039 } else {
2040 ok = BindDestructuringLHS(cx, pn, tc);
2042 if (!ok)
2043 goto out;
2045 lhs = lhs->pn_next;
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
2056 * let [] = 1;
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 {}.
2067 if (data &&
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,
2074 JSPROP_ENUMERATE |
2075 JSPROP_PERMANENT |
2076 JSPROP_SHARED,
2077 SPROP_HAS_SHORTID, 0, NULL);
2078 if (!ok)
2079 goto out;
2082 ok = JS_TRUE;
2084 out:
2085 if (fpvd.table.ops)
2086 JS_DHashTableFinish(&fpvd.table);
2087 return ok;
2089 no_var_name:
2090 js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, JSREPORT_ERROR,
2091 JSMSG_NO_VARIABLE_NAME);
2092 ok = JS_FALSE;
2093 goto out;
2096 static JSParseNode *
2097 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
2098 JSTokenType tt)
2100 JSParseNode *pn;
2102 pn = PrimaryExpr(cx, TS(tc->parseContext), tc, tt, JS_FALSE);
2103 if (!pn)
2104 return NULL;
2105 if (!CheckDestructuring(cx, data, pn, NULL, tc))
2106 return NULL;
2107 return pn;
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);
2117 if (!pn)
2118 return NULL;
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
2127 case PN_FUNC:
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;
2133 break;
2135 case PN_LIST:
2136 PN_INIT_LIST(pn);
2137 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
2138 NULLCHECK(pn2 = CloneParseTree(cx, opn2, tc));
2139 PN_APPEND(pn, pn2);
2141 pn->pn_extra = opn->pn_extra;
2142 break;
2144 case PN_TERNARY:
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));
2148 break;
2150 case PN_BINARY:
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));
2154 else
2155 pn->pn_right = pn->pn_left;
2156 pn->pn_val = opn->pn_val;
2157 pn->pn_iflags = opn->pn_iflags;
2158 break;
2160 case PN_UNARY:
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;
2164 break;
2166 case PN_NAME:
2167 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
2168 pn->pn_u = opn->pn_u;
2169 if (opn->pn_expr)
2170 NULLCHECK(pn->pn_expr = CloneParseTree(cx, opn->pn_expr, tc));
2171 break;
2173 case PN_NULLARY:
2174 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
2175 pn->pn_u = opn->pn_u;
2176 break;
2178 #undef NULLCHECK
2180 return pn;
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;
2192 if (!pn)
2193 return NULL;
2194 if (pn->pn_type == tt)
2195 return pn;
2196 switch (pn->pn_arity) {
2197 case PN_LIST:
2198 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2199 pnt = ContainsStmt(pn2, tt);
2200 if (pnt)
2201 return pnt;
2203 break;
2204 case PN_TERNARY:
2205 pnt = ContainsStmt(pn->pn_kid1, tt);
2206 if (pnt)
2207 return pnt;
2208 pnt = ContainsStmt(pn->pn_kid2, tt);
2209 if (pnt)
2210 return pnt;
2211 return ContainsStmt(pn->pn_kid3, tt);
2212 case PN_BINARY:
2214 * Limit recursion if pn is a binary expression, which can't contain a
2215 * var statement.
2217 if (pn->pn_op != JSOP_NOP)
2218 return NULL;
2219 pnt = ContainsStmt(pn->pn_left, tt);
2220 if (pnt)
2221 return pnt;
2222 return ContainsStmt(pn->pn_right, tt);
2223 case PN_UNARY:
2224 if (pn->pn_op != JSOP_NOP)
2225 return NULL;
2226 return ContainsStmt(pn->pn_kid, tt);
2227 case PN_NAME:
2228 return ContainsStmt(pn->pn_expr, tt);
2229 default:;
2231 return NULL;
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);
2245 return NULL;
2248 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2249 if (!pn)
2250 return NULL;
2252 #if JS_HAS_GENERATORS
2253 if (tt == TOK_YIELD)
2254 tc->flags |= TCF_FUN_IS_GENERATOR;
2255 #endif
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)
2262 return NULL;
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))
2269 #endif
2271 pn2 = operandParser(cx, ts, tc);
2272 if (!pn2)
2273 return NULL;
2274 #if JS_HAS_GENERATORS
2275 if (tt == TOK_RETURN)
2276 #endif
2277 tc->flags |= TCF_RETURN_EXPR;
2278 pn->pn_pos.end = pn2->pn_pos.end;
2279 pn->pn_kid = pn2;
2280 } else {
2281 #if JS_HAS_GENERATORS
2282 if (tt == TOK_RETURN)
2283 #endif
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);
2292 return NULL;
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)) {
2300 return NULL;
2303 return pn;
2306 static JSParseNode *
2307 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2308 JSStmtInfo *stmtInfo)
2310 JSParseNode *pn;
2311 JSObject *obj;
2312 JSParsedObjectBox *blockpob;
2314 pn = NewParseNode(cx, ts, PN_NAME, tc);
2315 if (!pn)
2316 return NULL;
2318 obj = js_NewBlockObject(cx);
2319 if (!obj)
2320 return NULL;
2322 blockpob = js_NewParsedObjectBox(cx, tc->parseContext, obj);
2323 if (!blockpob)
2324 return NULL;
2326 js_PushBlockScope(tc, stmtInfo, obj, -1);
2327 pn->pn_type = TOK_LEXICALSCOPE;
2328 pn->pn_op = JSOP_LEAVEBLOCK;
2329 pn->pn_pob = blockpob;
2330 pn->pn_slot = -1;
2331 return pn;
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);
2346 if (!pnlet)
2347 return NULL;
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);
2353 if (!pnblock)
2354 return NULL;
2355 pn = pnblock;
2356 pn->pn_expr = pnlet;
2358 pnlet->pn_left = Variables(cx, ts, tc);
2359 if (!pnlet->pn_left)
2360 return NULL;
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);
2373 if (!pn)
2374 return NULL;
2375 pn->pn_type = TOK_SEMI;
2376 pn->pn_num = -1;
2377 pn->pn_kid = pnblock;
2379 statement = JS_FALSE;
2381 ts->flags &= ~TSF_OPERAND;
2383 if (statement) {
2384 pnlet->pn_right = Statements(cx, ts, tc);
2385 if (!pnlet->pn_right)
2386 return NULL;
2387 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
2388 } else {
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)
2396 return NULL;
2399 js_PopStatement(tc);
2400 return pn;
2403 #endif /* JS_HAS_BLOCK_SCOPE */
2405 static JSParseNode *
2406 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2408 JSTokenType tt;
2409 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
2410 JSStmtInfo stmtInfo, *stmt, *stmt2;
2411 JSAtom *label;
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)
2423 return NULL;
2425 #endif
2427 switch (tt) {
2428 case TOK_FUNCTION:
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)
2434 goto expression;
2435 #endif
2436 return FunctionStmt(cx, ts, tc);
2438 case TOK_IF:
2439 /* An IF node has three kids: condition, then, and optional else. */
2440 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2441 if (!pn)
2442 return NULL;
2443 pn1 = Condition(cx, ts, tc);
2444 if (!pn1)
2445 return NULL;
2446 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
2447 pn2 = Statement(cx, ts, tc);
2448 if (!pn2)
2449 return NULL;
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);
2455 if (!pn3)
2456 return NULL;
2457 pn->pn_pos.end = pn3->pn_pos.end;
2458 } else {
2459 ts->flags &= ~TSF_OPERAND;
2460 pn3 = NULL;
2461 pn->pn_pos.end = pn2->pn_pos.end;
2463 js_PopStatement(tc);
2464 pn->pn_kid1 = pn1;
2465 pn->pn_kid2 = pn2;
2466 pn->pn_kid3 = pn3;
2467 return pn;
2469 case TOK_SWITCH:
2471 JSParseNode *pn5, *saveBlock;
2472 JSBool seenDefault = JS_FALSE;
2474 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2475 if (!pn)
2476 return NULL;
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);
2481 if (!pn1)
2482 return 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);
2489 if (!pn2)
2490 return NULL;
2491 saveBlock = tc->blockNode;
2492 tc->blockNode = pn2;
2493 PN_INIT_LIST(pn2);
2495 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
2497 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
2498 switch (tt) {
2499 case TOK_DEFAULT:
2500 if (seenDefault) {
2501 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2502 JSMSG_TOO_MANY_DEFAULTS);
2503 return NULL;
2505 seenDefault = JS_TRUE;
2506 /* FALL THROUGH */
2508 case TOK_CASE:
2509 pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
2510 if (!pn3)
2511 return NULL;
2512 if (tt == TOK_CASE) {
2513 pn3->pn_left = Expr(cx, ts, tc);
2514 if (!pn3->pn_left)
2515 return NULL;
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);
2521 return NULL;
2523 break;
2525 case TOK_ERROR:
2526 return NULL;
2528 default:
2529 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2530 JSMSG_BAD_SWITCH);
2531 return NULL;
2533 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
2535 pn4 = NewParseNode(cx, ts, PN_LIST, tc);
2536 if (!pn4)
2537 return NULL;
2538 pn4->pn_type = TOK_LC;
2539 PN_INIT_LIST(pn4);
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)
2545 return NULL;
2546 pn5 = Statement(cx, ts, tc);
2547 if (!pn5)
2548 return NULL;
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. */
2556 if (pn4->pn_head)
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;
2574 pn->pn_left = pn1;
2575 pn->pn_right = pn2;
2576 return pn;
2579 case TOK_WHILE:
2580 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2581 if (!pn)
2582 return NULL;
2583 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
2584 pn2 = Condition(cx, ts, tc);
2585 if (!pn2)
2586 return NULL;
2587 pn->pn_left = pn2;
2588 pn2 = Statement(cx, ts, tc);
2589 if (!pn2)
2590 return NULL;
2591 js_PopStatement(tc);
2592 pn->pn_pos.end = pn2->pn_pos.end;
2593 pn->pn_right = pn2;
2594 return pn;
2596 case TOK_DO:
2597 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2598 if (!pn)
2599 return NULL;
2600 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
2601 pn2 = Statement(cx, ts, tc);
2602 if (!pn2)
2603 return NULL;
2604 pn->pn_left = pn2;
2605 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
2606 pn2 = Condition(cx, ts, tc);
2607 if (!pn2)
2608 return NULL;
2609 js_PopStatement(tc);
2610 pn->pn_pos.end = pn2->pn_pos.end;
2611 pn->pn_right = pn2;
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);
2619 return pn;
2621 break;
2623 case TOK_FOR:
2625 JSParseNode *pnseq = NULL;
2626 #if JS_HAS_BLOCK_SCOPE
2627 JSParseNode *pnlet = NULL;
2628 JSStmtInfo blockInfo;
2629 #endif
2631 /* A FOR node is binary, left is loop control and right is the body. */
2632 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2633 if (!pn)
2634 return NULL;
2635 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
2637 pn->pn_op = JSOP_ITER;
2638 pn->pn_iflags = 0;
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;
2642 else
2643 js_UngetToken(ts);
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)
2652 goto bad_for_each;
2654 /* No initializer -- set first kid of left sub-node to null. */
2655 pn1 = NULL;
2656 } else {
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;
2680 } else {
2681 pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
2682 if (!pnlet)
2683 return NULL;
2684 blockInfo.flags |= SIF_FOR_BLOCK;
2685 pn1 = Variables(cx, ts, tc);
2687 #endif
2688 } else {
2689 pn1 = Expr(cx, ts, tc);
2690 if (pn1) {
2691 while (pn1->pn_type == TOK_RP)
2692 pn1 = pn1->pn_kid;
2695 tc->flags &= ~TCF_IN_FOR_INIT;
2696 if (!pn1)
2697 return NULL;
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))))
2724 #endif
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)) &&
2734 #endif
2735 #if JS_HAS_LVALUE_RETURN
2736 pn1->pn_type != TOK_LP &&
2737 #endif
2738 #if JS_HAS_XML_SUPPORT
2739 (pn1->pn_type != TOK_UNARYOP ||
2740 pn1->pn_op != JSOP_XMLNAME) &&
2741 #endif
2742 pn1->pn_type != TOK_LB)) {
2743 js_ReportCompileErrorNumber(cx, ts, pn1, JSREPORT_ERROR,
2744 JSMSG_BAD_FOR_LEFTSIDE);
2745 return NULL;
2748 /* pn2 points to the name or destructuring pattern on in's left. */
2749 pn2 = NULL;
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'.
2761 pn2 = pn1->pn_head;
2762 if (pn2->pn_type == TOK_NAME && pn2->pn_expr
2763 #if JS_HAS_DESTRUCTURING
2764 || pn2->pn_type == TOK_ASSIGN
2765 #endif
2767 pnseq = NewParseNode(cx, ts, PN_LIST, tc);
2768 if (!pnseq)
2769 return NULL;
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);
2778 if (!pn3)
2779 return NULL;
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;
2786 } else
2787 #endif
2789 pn4 = pn2->pn_expr;
2790 pn2->pn_expr = NULL;
2792 pn3->pn_pos = pn4->pn_pos;
2793 pn3->pn_kid = pn4;
2794 PN_INIT_LIST_1(pnseq, pn3);
2795 } else {
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);
2811 if (!pn1)
2812 return NULL;
2813 } else
2814 #endif
2816 pn1 = NewParseNode(cx, ts, PN_NAME, tc);
2817 if (!pn1)
2818 return NULL;
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;
2824 pn1->pn_slot = -1;
2825 pn1->pn_const = pn2->pn_const;
2827 pn2 = pn1;
2832 if (!pn2) {
2833 pn2 = pn1;
2834 #if JS_HAS_LVALUE_RETURN
2835 if (pn2->pn_type == TOK_LP &&
2836 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
2837 return NULL;
2839 #endif
2840 #if JS_HAS_XML_SUPPORT
2841 if (pn2->pn_type == TOK_UNARYOP)
2842 pn2->pn_op = JSOP_BINDXMLNAME;
2843 #endif
2846 switch (pn2->pn_type) {
2847 case TOK_NAME:
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;
2851 break;
2853 #if JS_HAS_DESTRUCTURING
2854 case TOK_ASSIGN:
2855 pn2 = pn2->pn_left;
2856 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
2857 /* FALL THROUGH */
2858 case TOK_RB:
2859 case TOK_RC:
2860 /* Check for valid lvalues in var-less destructuring for-in. */
2861 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
2862 return NULL;
2864 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
2866 * Destructuring for-in requires [key, value] enumeration
2867 * in JS1.7.
2869 JS_ASSERT(pn->pn_op == JSOP_ITER);
2870 if (!(pn->pn_iflags & JSITER_FOREACH))
2871 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
2873 break;
2874 #endif
2876 default:;
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);
2881 if (!pn2)
2882 return NULL;
2883 pn->pn_left = pn2;
2884 } else {
2885 if (pn->pn_iflags & JSITER_FOREACH)
2886 goto bad_for_each;
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) {
2895 pn2 = NULL;
2896 } else {
2897 pn2 = Expr(cx, ts, tc);
2898 if (!pn2)
2899 return NULL;
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;
2907 if (tt == TOK_RP) {
2908 pn3 = NULL;
2909 } else {
2910 pn3 = Expr(cx, ts, tc);
2911 if (!pn3)
2912 return NULL;
2915 /* Build the FORHEAD node to use as the left kid of pn. */
2916 pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
2917 if (!pn4)
2918 return NULL;
2919 pn4->pn_type = TOK_FORHEAD;
2920 pn4->pn_op = JSOP_NOP;
2921 pn4->pn_kid1 = pn1;
2922 pn4->pn_kid2 = pn2;
2923 pn4->pn_kid3 = pn3;
2924 pn->pn_left = pn4;
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);
2931 if (!pn2)
2932 return NULL;
2933 pn->pn_right = pn2;
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
2939 if (pnlet) {
2940 js_PopStatement(tc);
2941 pnlet->pn_expr = pn;
2942 pn = pnlet;
2944 #endif
2945 if (pnseq) {
2946 pnseq->pn_pos.end = pn->pn_pos.end;
2947 PN_APPEND(pnseq, pn);
2948 pn = pnseq;
2950 js_PopStatement(tc);
2951 return pn;
2953 bad_for_each:
2954 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
2955 JSMSG_BAD_FOR_EACH_LOOP);
2956 return NULL;
2959 case TOK_TRY: {
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);
2980 if (!pn)
2981 return NULL;
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);
2987 if (!pn->pn_kid1)
2988 return NULL;
2989 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
2990 js_PopStatement(tc);
2992 catchList = NULL;
2993 tt = js_GetToken(cx, ts);
2994 if (tt == TOK_CATCH) {
2995 catchList = NewParseNode(cx, ts, PN_LIST, tc);
2996 if (!catchList)
2997 return NULL;
2998 catchList->pn_type = TOK_RESERVED;
2999 PN_INIT_LIST(catchList);
3000 lastCatch = NULL;
3002 do {
3003 JSParseNode *pnblock;
3004 BindData data;
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);
3010 return NULL;
3014 * Create a lexical scope node around the whole catch clause,
3015 * including the head.
3017 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
3018 if (!pnblock)
3019 return NULL;
3020 stmtInfo.type = STMT_CATCH;
3023 * Legal catch forms are:
3024 * catch (lhs)
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);
3030 if (!pn2)
3031 return NULL;
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.
3040 data.pn = NULL;
3041 data.op = JSOP_NOP;
3042 data.binder = BindLet;
3043 data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
3045 tt = js_GetToken(cx, ts);
3046 switch (tt) {
3047 #if JS_HAS_DESTRUCTURING
3048 case TOK_LB:
3049 case TOK_LC:
3050 pn3 = DestructuringExpr(cx, &data, tc, tt);
3051 if (!pn3)
3052 return NULL;
3053 break;
3054 #endif
3056 case TOK_NAME:
3057 label = CURRENT_TOKEN(ts).t_atom;
3058 if (!data.binder(cx, &data, label, tc))
3059 return NULL;
3061 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
3062 if (!pn3)
3063 return NULL;
3064 pn3->pn_atom = label;
3065 pn3->pn_slot = -1;
3066 break;
3068 default:
3069 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3070 JSMSG_CATCH_IDENTIFIER);
3071 return NULL;
3074 pn2->pn_kid1 = pn3;
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);
3083 if (!pn2->pn_kid2)
3084 return NULL;
3086 #endif
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);
3091 if (!pn2->pn_kid3)
3092 return NULL;
3093 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
3094 js_PopStatement(tc);
3096 PN_APPEND(catchList, pnblock);
3097 lastCatch = pn2;
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);
3109 if (!pn->pn_kid3)
3110 return NULL;
3111 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
3112 js_PopStatement(tc);
3113 } else {
3114 js_UngetToken(ts);
3116 if (!catchList && !pn->pn_kid3) {
3117 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3118 JSMSG_CATCH_OR_FINALLY);
3119 return NULL;
3121 return pn;
3124 case TOK_THROW:
3125 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3126 if (!pn)
3127 return NULL;
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)
3134 return NULL;
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);
3138 return NULL;
3141 pn2 = Expr(cx, ts, tc);
3142 if (!pn2)
3143 return NULL;
3144 pn->pn_pos.end = pn2->pn_pos.end;
3145 pn->pn_op = JSOP_THROW;
3146 pn->pn_kid = pn2;
3147 break;
3149 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
3150 case TOK_CATCH:
3151 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3152 JSMSG_CATCH_WITHOUT_TRY);
3153 return NULL;
3155 case TOK_FINALLY:
3156 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3157 JSMSG_FINALLY_WITHOUT_TRY);
3158 return NULL;
3160 case TOK_BREAK:
3161 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3162 if (!pn)
3163 return NULL;
3164 if (!MatchLabel(cx, ts, pn))
3165 return NULL;
3166 stmt = tc->topStmt;
3167 label = pn->pn_atom;
3168 if (label) {
3169 for (; ; stmt = stmt->down) {
3170 if (!stmt) {
3171 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3172 JSMSG_LABEL_NOT_FOUND);
3173 return NULL;
3175 if (stmt->type == STMT_LABEL && stmt->u.label == label)
3176 break;
3178 } else {
3179 for (; ; stmt = stmt->down) {
3180 if (!stmt) {
3181 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3182 JSMSG_TOUGH_BREAK);
3183 return NULL;
3185 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
3186 break;
3189 if (label)
3190 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3191 break;
3193 case TOK_CONTINUE:
3194 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3195 if (!pn)
3196 return NULL;
3197 if (!MatchLabel(cx, ts, pn))
3198 return NULL;
3199 stmt = tc->topStmt;
3200 label = pn->pn_atom;
3201 if (label) {
3202 for (stmt2 = NULL; ; stmt = stmt->down) {
3203 if (!stmt) {
3204 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3205 JSMSG_LABEL_NOT_FOUND);
3206 return NULL;
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,
3212 JSREPORT_ERROR,
3213 JSMSG_BAD_CONTINUE);
3214 return NULL;
3216 break;
3218 } else {
3219 stmt2 = stmt;
3222 } else {
3223 for (; ; stmt = stmt->down) {
3224 if (!stmt) {
3225 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3226 JSMSG_BAD_CONTINUE);
3227 return NULL;
3229 if (STMT_IS_LOOP(stmt))
3230 break;
3233 if (label)
3234 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3235 break;
3237 case TOK_WITH:
3238 pn = NewParseNode(cx, ts, PN_BINARY, tc);
3239 if (!pn)
3240 return NULL;
3241 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
3242 pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
3243 if (!pn2)
3244 return NULL;
3245 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
3246 pn->pn_left = pn2;
3248 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
3249 pn2 = Statement(cx, ts, tc);
3250 if (!pn2)
3251 return NULL;
3252 js_PopStatement(tc);
3254 pn->pn_pos.end = pn2->pn_pos.end;
3255 pn->pn_right = pn2;
3256 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3257 return pn;
3259 case TOK_VAR:
3260 pn = Variables(cx, ts, tc);
3261 if (!pn)
3262 return NULL;
3264 /* Tell js_EmitTree to generate a final POP. */
3265 pn->pn_extra |= PNX_POPVAR;
3266 break;
3268 #if JS_HAS_BLOCK_SCOPE
3269 case TOK_LET:
3271 JSObject *obj;
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)
3278 return pn;
3280 /* Let expressions require automatic semicolon insertion. */
3281 JS_ASSERT(pn->pn_type == TOK_SEMI ||
3282 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
3283 break;
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.
3297 stmt = tc->topStmt;
3298 if (stmt &&
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);
3302 return NULL;
3305 if (stmt && (stmt->flags & SIF_SCOPE)) {
3306 JS_ASSERT(tc->blockChain == stmt->u.blockObj);
3307 obj = tc->blockChain;
3308 } else {
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);
3318 if (!pn)
3319 return NULL;
3320 pn->pn_extra |= PNX_POPVAR;
3321 break;
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);
3339 if (!obj)
3340 return NULL;
3341 blockpob = js_NewParsedObjectBox(cx, tc->parseContext, obj);
3342 if (!blockpob)
3343 return NULL;
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
3349 * block.
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;
3361 #ifdef DEBUG
3362 pn1 = tc->blockNode;
3363 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
3364 #endif
3366 /* Create a new lexical scope node for these statements. */
3367 pn1 = NewParseNode(cx, ts, PN_NAME, tc);
3368 if (!pn1)
3369 return NULL;
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;
3376 pn1->pn_slot = -1;
3377 tc->blockNode = pn1;
3380 pn = Variables(cx, ts, tc);
3381 if (!pn)
3382 return NULL;
3383 pn->pn_extra = PNX_POPVAR;
3384 break;
3386 #endif /* JS_HAS_BLOCK_SCOPE */
3388 case TOK_RETURN:
3389 pn = ReturnOrYield(cx, ts, tc, Expr);
3390 if (!pn)
3391 return NULL;
3392 break;
3394 case TOK_LC:
3396 uintN oldflags;
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);
3402 if (!pn)
3403 return NULL;
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));
3417 return pn;
3420 case TOK_EOL:
3421 case TOK_SEMI:
3422 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3423 if (!pn)
3424 return NULL;
3425 pn->pn_type = TOK_SEMI;
3426 return pn;
3428 #if JS_HAS_DEBUGGER_KEYWORD
3429 case TOK_DEBUGGER:
3430 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3431 if (!pn)
3432 return NULL;
3433 pn->pn_type = TOK_DEBUGGER;
3434 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3435 break;
3436 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3438 #if JS_HAS_XML_SUPPORT
3439 case TOK_DEFAULT:
3440 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3441 if (!pn)
3442 return NULL;
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);
3451 return NULL;
3453 pn2 = Expr(cx, ts, tc);
3454 if (!pn2)
3455 return NULL;
3456 pn->pn_op = JSOP_DEFXMLNS;
3457 pn->pn_pos.end = pn2->pn_pos.end;
3458 pn->pn_kid = pn2;
3459 tc->flags |= TCF_HAS_DEFXMLNS;
3460 break;
3461 #endif
3463 case TOK_ERROR:
3464 return NULL;
3466 default:
3467 #if JS_HAS_XML_SUPPORT
3468 expression:
3469 #endif
3470 js_UngetToken(ts);
3471 pn2 = Expr(cx, ts, tc);
3472 if (!pn2)
3473 return NULL;
3475 if (js_PeekToken(cx, ts) == TOK_COLON) {
3476 if (pn2->pn_type != TOK_NAME) {
3477 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3478 JSMSG_BAD_LABEL);
3479 return NULL;
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);
3486 return NULL;
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);
3495 if (!pn)
3496 return NULL;
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;
3502 PN_INIT_LIST(pn);
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;
3509 pn2->pn_expr = pn;
3510 return pn2;
3513 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3514 if (!pn)
3515 return NULL;
3516 pn->pn_type = TOK_SEMI;
3517 pn->pn_pos = pn2->pn_pos;
3518 pn->pn_kid = pn2;
3519 break;
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)
3528 return NULL;
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);
3532 return NULL;
3536 (void) js_MatchToken(cx, ts, TOK_SEMI);
3537 return pn;
3540 static JSParseNode *
3541 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3543 JSTokenType tt;
3544 JSBool let;
3545 JSStmtInfo *scopeStmt;
3546 BindData data;
3547 JSParseNode *pn, *pn2;
3548 JSAtom *atom;
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;
3562 if (let) {
3563 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
3564 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
3565 scopeStmt = scopeStmt->downScope;
3567 JS_ASSERT(scopeStmt);
3570 data.pn = NULL;
3571 data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
3572 pn = NewParseNode(cx, ts, PN_LIST, tc);
3573 if (!pn)
3574 return NULL;
3575 pn->pn_op = data.op;
3576 PN_INIT_LIST(pn);
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.
3587 if (let) {
3588 JS_ASSERT(tc->blockChain == scopeStmt->u.blockObj);
3589 data.binder = BindLet;
3590 data.u.let.overflow = JSMSG_TOO_MANY_LOCALS;
3591 } else {
3592 data.binder = BindVarOrConst;
3595 do {
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);
3600 if (!pn2)
3601 return NULL;
3603 if ((tc->flags & TCF_IN_FOR_INIT) &&
3604 js_PeekToken(cx, ts) == TOK_IN) {
3605 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
3606 return NULL;
3607 PN_APPEND(pn, pn2);
3608 continue;
3611 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
3612 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
3613 goto bad_var_init;
3615 pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP,
3616 pn2, AssignExpr(cx, ts, tc),
3617 tc);
3618 if (!pn2 ||
3619 !CheckDestructuring(cx, &data,
3620 pn2->pn_left, pn2->pn_right,
3621 tc)) {
3622 return NULL;
3624 PN_APPEND(pn, pn2);
3625 continue;
3627 #endif
3629 if (tt != TOK_NAME) {
3630 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3631 JSMSG_NO_VARIABLE_NAME);
3632 return NULL;
3634 atom = CURRENT_TOKEN(ts).t_atom;
3635 if (!data.binder(cx, &data, atom, tc))
3636 return NULL;
3638 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
3639 if (!pn2)
3640 return NULL;
3641 pn2->pn_op = JSOP_NAME;
3642 pn2->pn_atom = atom;
3643 pn2->pn_slot = -1;
3644 if (!let)
3645 pn2->pn_const = (data.op == JSOP_DEFCONST);
3646 PN_APPEND(pn, pn2);
3648 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
3649 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
3650 goto bad_var_init;
3652 pn2->pn_expr = AssignExpr(cx, ts, tc);
3653 if (!pn2->pn_expr)
3654 return NULL;
3655 pn2->pn_op = (!let && data.op == JSOP_DEFCONST)
3656 ? JSOP_SETCONST
3657 : JSOP_SETNAME;
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;
3665 return pn;
3667 bad_var_init:
3668 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3669 JSMSG_BAD_VAR_INIT);
3670 return NULL;
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);
3681 if (!pn2)
3682 return NULL;
3683 pn2->pn_pos.begin = pn->pn_pos.begin;
3684 PN_INIT_LIST_1(pn2, pn);
3685 pn = pn2;
3686 do {
3687 #if JS_HAS_GENERATORS
3688 pn2 = PN_LAST(pn);
3689 if (pn2->pn_type == TOK_YIELD) {
3690 js_ReportCompileErrorNumber(cx, ts, pn2, JSREPORT_ERROR,
3691 JSMSG_BAD_GENERATOR_SYNTAX,
3692 js_yield_str);
3693 return NULL;
3695 #endif
3696 pn2 = AssignExpr(cx, ts, tc);
3697 if (!pn2)
3698 return NULL;
3699 PN_APPEND(pn, pn2);
3700 } while (js_MatchToken(cx, ts, TOK_COMMA));
3701 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
3703 return pn;
3706 static JSParseNode *
3707 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3709 JSParseNode *pn, *pn2;
3710 JSTokenType tt;
3711 JSOp op;
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;
3722 #endif
3724 pn = CondExpr(cx, ts, tc);
3725 if (!pn)
3726 return NULL;
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)
3733 return NULL;
3735 #endif
3736 if (tt != TOK_ASSIGN) {
3737 js_UngetToken(ts);
3738 return pn;
3741 op = CURRENT_TOKEN(ts).t_op;
3742 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
3743 continue;
3744 switch (pn2->pn_type) {
3745 case TOK_NAME:
3746 pn2->pn_op = JSOP_SETNAME;
3747 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
3748 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3749 break;
3750 case TOK_DOT:
3751 pn2->pn_op = JSOP_SETPROP;
3752 break;
3753 case TOK_LB:
3754 pn2->pn_op = JSOP_SETELEM;
3755 break;
3756 #if JS_HAS_DESTRUCTURING
3757 case TOK_RB:
3758 case TOK_RC:
3759 if (op != JSOP_NOP) {
3760 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3761 JSMSG_BAD_DESTRUCT_ASS);
3762 return NULL;
3764 pn = AssignExpr(cx, ts, tc);
3765 if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc))
3766 return NULL;
3767 return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc);
3768 #endif
3769 #if JS_HAS_LVALUE_RETURN
3770 case TOK_LP:
3771 if (!MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3772 return NULL;
3773 break;
3774 #endif
3775 #if JS_HAS_XML_SUPPORT
3776 case TOK_UNARYOP:
3777 if (pn2->pn_op == JSOP_XMLNAME) {
3778 pn2->pn_op = JSOP_SETXMLNAME;
3779 break;
3781 /* FALL THROUGH */
3782 #endif
3783 default:
3784 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3785 JSMSG_BAD_LEFTSIDE_OF_ASS);
3786 return NULL;
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;
3796 uintN oldflags;
3798 pn = OrExpr(cx, ts, tc);
3799 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
3800 pn1 = pn;
3801 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
3802 if (!pn)
3803 return NULL;
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
3807 * for statement.
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);
3814 if (!pn2)
3815 return NULL;
3816 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
3817 pn3 = AssignExpr(cx, ts, tc);
3818 if (!pn3)
3819 return NULL;
3820 pn->pn_pos.begin = pn1->pn_pos.begin;
3821 pn->pn_pos.end = pn3->pn_pos.end;
3822 pn->pn_kid1 = pn1;
3823 pn->pn_kid2 = pn2;
3824 pn->pn_kid3 = pn3;
3826 return pn;
3829 static JSParseNode *
3830 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3832 JSParseNode *pn;
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);
3837 return pn;
3840 static JSParseNode *
3841 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3843 JSParseNode *pn;
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);
3848 return pn;
3851 static JSParseNode *
3852 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3854 JSParseNode *pn;
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),
3859 tc);
3861 return pn;
3864 static JSParseNode *
3865 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3867 JSParseNode *pn;
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),
3872 tc);
3874 return pn;
3877 static JSParseNode *
3878 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3880 JSParseNode *pn;
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);
3885 return pn;
3888 static JSParseNode *
3889 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3891 JSParseNode *pn;
3892 JSOp op;
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);
3899 return pn;
3902 static JSParseNode *
3903 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3905 JSParseNode *pn;
3906 JSTokenType tt;
3907 JSOp op;
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);
3917 while (pn &&
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;
3932 return pn;
3935 static JSParseNode *
3936 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3938 JSParseNode *pn;
3939 JSOp op;
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);
3946 return pn;
3949 static JSParseNode *
3950 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3952 JSParseNode *pn;
3953 JSTokenType tt;
3954 JSOp op;
3956 pn = MulExpr(cx, ts, tc);
3957 while (pn &&
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);
3964 return pn;
3967 static JSParseNode *
3968 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3970 JSParseNode *pn;
3971 JSTokenType tt;
3972 JSOp op;
3974 pn = UnaryExpr(cx, ts, tc);
3975 while (pn &&
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);
3982 return pn;
3985 static JSParseNode *
3986 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
3987 const char *name)
3989 while (kid->pn_type == TOK_RP)
3990 kid = kid->pn_kid;
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)) &&
3996 #endif
3997 #if JS_HAS_XML_SUPPORT
3998 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
3999 #endif
4000 kid->pn_type != TOK_LB) {
4001 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4002 JSMSG_BAD_OPERAND, name);
4003 return NULL;
4005 pn->pn_kid = kid;
4006 return kid;
4009 static const char incop_name_str[][10] = {"increment", "decrement"};
4011 static JSBool
4012 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4013 JSParseNode *pn, JSParseNode *kid,
4014 JSTokenType tt, JSBool preorder)
4016 JSOp op;
4018 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
4019 if (!kid)
4020 return JS_FALSE;
4021 switch (kid->pn_type) {
4022 case TOK_NAME:
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;
4028 break;
4030 case TOK_DOT:
4031 op = (tt == TOK_INC)
4032 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
4033 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
4034 break;
4036 #if JS_HAS_LVALUE_RETURN
4037 case TOK_LP:
4038 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
4039 return JS_FALSE;
4040 /* FALL THROUGH */
4041 #endif
4042 #if JS_HAS_XML_SUPPORT
4043 case TOK_UNARYOP:
4044 if (kid->pn_op == JSOP_XMLNAME)
4045 kid->pn_op = JSOP_SETXMLNAME;
4046 /* FALL THROUGH */
4047 #endif
4048 case TOK_LB:
4049 op = (tt == TOK_INC)
4050 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
4051 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
4052 break;
4054 default:
4055 JS_ASSERT(0);
4056 op = JSOP_NOP;
4058 pn->pn_op = op;
4059 return JS_TRUE;
4062 static JSParseNode *
4063 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4065 JSTokenType tt;
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;
4074 switch (tt) {
4075 case TOK_UNARYOP:
4076 case TOK_PLUS:
4077 case TOK_MINUS:
4078 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4079 if (!pn)
4080 return NULL;
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);
4084 if (!pn2)
4085 return NULL;
4086 pn->pn_pos.end = pn2->pn_pos.end;
4087 pn->pn_kid = pn2;
4088 break;
4090 case TOK_INC:
4091 case TOK_DEC:
4092 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4093 if (!pn)
4094 return NULL;
4095 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
4096 if (!pn2)
4097 return NULL;
4098 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
4099 return NULL;
4100 pn->pn_pos.end = pn2->pn_pos.end;
4101 break;
4103 case TOK_DELETE:
4104 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4105 if (!pn)
4106 return NULL;
4107 pn2 = UnaryExpr(cx, ts, tc);
4108 if (!pn2)
4109 return NULL;
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)
4119 pn2 = pn2->pn_kid;
4120 if (!js_FoldConstants(cx, pn2, tc))
4121 return NULL;
4122 if (pn2->pn_type == TOK_LP &&
4123 pn2->pn_op != JSOP_SETCALL &&
4124 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
4125 return NULL;
4127 pn->pn_kid = pn2;
4128 break;
4130 case TOK_ERROR:
4131 return NULL;
4133 default:
4134 js_UngetToken(ts);
4135 pn = MemberExpr(cx, ts, tc, JS_TRUE);
4136 if (!pn)
4137 return NULL;
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);
4147 if (!pn2)
4148 return NULL;
4149 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
4150 return NULL;
4151 pn2->pn_pos.begin = pn->pn_pos.begin;
4152 pn = pn2;
4155 break;
4157 return pn;
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;
4177 BindData data;
4178 JSRuntime *rt;
4179 JSTokenType tt;
4180 JSAtom *atom;
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);
4190 if (!pn)
4191 return NULL;
4192 pnp = &pn->pn_expr;
4194 data.pn = NULL;
4195 data.op = JSOP_NOP;
4196 data.binder = BindLet;
4197 data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
4199 rt = cx->runtime;
4200 do {
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
4204 * of the IN.
4206 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
4207 if (!pn2)
4208 return NULL;
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;
4215 else
4216 js_UngetToken(ts);
4218 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4220 tt = js_GetToken(cx, ts);
4221 switch (tt) {
4222 #if JS_HAS_DESTRUCTURING
4223 case TOK_LB:
4224 case TOK_LC:
4225 pn3 = DestructuringExpr(cx, &data, tc, tt);
4226 if (!pn3)
4227 return NULL;
4229 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
4230 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4231 JSMSG_BAD_FOR_LEFTSIDE);
4232 return NULL;
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;
4242 break;
4243 #endif
4245 case TOK_NAME:
4246 atom = CURRENT_TOKEN(ts).t_atom;
4247 if (!data.binder(cx, &data, atom, tc))
4248 return NULL;
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
4255 * the deed.
4257 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
4258 if (!pn3)
4259 return NULL;
4260 pn3->pn_op = JSOP_NAME;
4261 pn3->pn_atom = atom;
4262 pn3->pn_slot = -1;
4263 break;
4265 default:
4266 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4267 JSMSG_NO_VARIABLE_NAME);
4269 case TOK_ERROR:
4270 return NULL;
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);
4276 if (!pn3)
4277 return NULL;
4279 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
4280 pn2->pn_left = pn3;
4281 *pnp = pn2;
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);
4287 if (!pn2)
4288 return NULL;
4289 pn2->pn_kid1 = Condition(cx, ts, tc);
4290 if (!pn2->pn_kid1)
4291 return NULL;
4292 *pnp = pn2;
4293 pnp = &pn2->pn_kid2;
4296 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
4297 if (!pn2)
4298 return NULL;
4299 pn2->pn_type = type;
4300 pn2->pn_op = op;
4301 pn2->pn_kid = kid;
4302 *pnp = pn2;
4304 js_PopStatement(tc);
4305 return pn;
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;
4330 JSFunction *fun;
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;
4337 pn->pn_kid = kid;
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);
4345 if (!body)
4346 return NULL;
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);
4354 if (!fun)
4355 return NULL;
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);
4364 if (!lambda)
4365 return NULL;
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,
4370 FUN_OBJECT(fun));
4371 if (!lambda->pn_funpob)
4372 return NULL;
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);
4382 if (!pn)
4383 return NULL;
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;
4391 return pn;
4394 static const char js_generator_str[] = "generator";
4396 #endif /* JS_HAS_GENERATOR_EXPRS */
4397 #endif /* JS_HAS_GENERATORS */
4399 static JSBool
4400 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4401 JSParseNode *listNode)
4403 JSBool matched;
4405 ts->flags |= TSF_OPERAND;
4406 matched = js_MatchToken(cx, ts, TOK_RP);
4407 ts->flags &= ~TSF_OPERAND;
4408 if (!matched) {
4409 do {
4410 #if JS_HAS_GENERATOR_EXPRS
4411 uintN oldflags = tc->flags;
4412 #endif
4413 JSParseNode *argNode = AssignExpr(cx, ts, tc);
4414 if (!argNode)
4415 return JS_FALSE;
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,
4421 js_yield_str);
4422 return JS_FALSE;
4424 #endif
4425 #if JS_HAS_GENERATOR_EXPRS
4426 if (js_MatchToken(cx, ts, TOK_FOR)) {
4427 JSParseNode *pn = NewParseNode(cx, ts, PN_UNARY, tc);
4428 if (!pn)
4429 return JS_FALSE;
4430 argNode = GeneratorExpr(cx, ts, tc, oldflags, pn, argNode);
4431 if (!argNode)
4432 return JS_FALSE;
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,
4437 js_generator_str);
4438 return JS_FALSE;
4441 #endif
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);
4448 return JS_FALSE;
4451 return JS_TRUE;
4454 static JSParseNode *
4455 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4456 JSBool allowCallSyntax)
4458 JSParseNode *pn, *pn2, *pn3;
4459 JSTokenType tt;
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);
4469 if (!pn)
4470 return NULL;
4471 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
4472 if (!pn2)
4473 return NULL;
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))
4479 return NULL;
4480 if (pn->pn_count > ARGC_LIMIT) {
4481 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4482 JSMSG_TOO_MANY_CON_ARGS);
4483 return NULL;
4485 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
4486 } else {
4487 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
4488 if (!pn)
4489 return NULL;
4491 if (pn->pn_type == TOK_ANYNAME ||
4492 pn->pn_type == TOK_AT ||
4493 pn->pn_type == TOK_DBLCOLON) {
4494 pn2 = NewOrRecycledNode(cx, tc);
4495 if (!pn2)
4496 return NULL;
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;
4501 pn2->pn_kid = pn;
4502 pn = pn2;
4506 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
4507 if (tt == TOK_DOT) {
4508 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
4509 if (!pn2)
4510 return NULL;
4511 pn2->pn_slot = -1;
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);
4517 if (!pn3)
4518 return NULL;
4519 tt = PN_TYPE(pn3);
4520 if (tt == TOK_NAME) {
4521 pn2->pn_op = JSOP_GETPROP;
4522 pn2->pn_expr = pn;
4523 pn2->pn_atom = pn3->pn_atom;
4524 RecycleTree(pn3, tc);
4525 } else {
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;
4541 } else {
4542 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4543 JSMSG_NAME_AFTER_DOT);
4544 return NULL;
4546 pn2->pn_arity = PN_BINARY;
4547 pn2->pn_left = pn;
4548 pn2->pn_right = pn3;
4550 #else
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;
4555 pn2->pn_expr = pn;
4556 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
4557 #endif
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);
4563 if (!pn2)
4564 return NULL;
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);
4569 if (!pn3)
4570 return NULL;
4571 tt = PN_TYPE(pn3);
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);
4579 return NULL;
4581 pn2->pn_op = JSOP_DESCENDANTS;
4582 pn2->pn_left = pn;
4583 pn2->pn_right = pn3;
4584 pn2->pn_pos.begin = pn->pn_pos.begin;
4585 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4586 #endif
4587 } else if (tt == TOK_LB) {
4588 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
4589 if (!pn2)
4590 return NULL;
4591 pn3 = Expr(cx, ts, tc);
4592 if (!pn3)
4593 return NULL;
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.
4606 do {
4607 if (pn3->pn_type == TOK_STRING) {
4608 jsuint index;
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;
4614 pn2->pn_expr = pn;
4615 pn2->pn_atom = pn3->pn_atom;
4616 break;
4618 pn3->pn_type = TOK_NUMBER;
4619 pn3->pn_op = JSOP_DOUBLE;
4620 pn3->pn_dval = index;
4622 pn2->pn_op = JSOP_GETELEM;
4623 pn2->pn_left = pn;
4624 pn2->pn_right = pn3;
4625 } while (0);
4626 } else if (allowCallSyntax && tt == TOK_LP) {
4627 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
4628 if (!pn2)
4629 return NULL;
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))
4648 return NULL;
4649 if (pn2->pn_count > ARGC_LIMIT) {
4650 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4651 JSMSG_TOO_MANY_FUN_ARGS);
4652 return NULL;
4654 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4655 } else {
4656 js_UngetToken(ts);
4657 return pn;
4660 pn = pn2;
4662 if (tt == TOK_ERROR)
4663 return NULL;
4664 return pn;
4667 static JSParseNode *
4668 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4670 uintN oldflags;
4671 JSParseNode *pn;
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
4676 * for statement.
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);
4682 return pn;
4685 #if JS_HAS_XML_SUPPORT
4687 static JSParseNode *
4688 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4690 JSParseNode *pn;
4692 pn = BracketedExpr(cx, ts, tc);
4693 if (!pn)
4694 return NULL;
4696 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
4697 return pn;
4701 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
4703 * AttributeIdentifier:
4704 * @ PropertySelector
4705 * @ QualifiedIdentifier
4706 * @ [ Expression ]
4708 * PropertySelector:
4709 * Identifier
4712 * QualifiedIdentifier:
4713 * PropertySelector :: PropertySelector
4714 * PropertySelector :: [ Expression ]
4716 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
4718 * AttributeIdentifier:
4719 * @ QualifiedIdentifier
4720 * @ [ Expression ]
4722 * PropertySelector:
4723 * Identifier
4726 * QualifiedIdentifier:
4727 * PropertySelector :: PropertySelector
4728 * PropertySelector :: [ Expression ]
4729 * PropertySelector
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
4738 * QualifiedSuffix:
4739 * :: PropertySelector
4740 * :: [ Expression ]
4741 * /nothing/
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)
4754 JSParseNode *pn;
4756 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4757 if (!pn)
4758 return NULL;
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;
4763 } else {
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;
4768 pn->pn_slot = -1;
4770 return pn;
4773 static JSParseNode *
4774 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
4775 JSTreeContext *tc)
4777 JSParseNode *pn2, *pn3;
4778 JSTokenType tt;
4780 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
4781 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
4782 if (!pn2)
4783 return NULL;
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;
4798 pn2->pn_expr = pn;
4799 pn2->pn_slot = -1;
4800 return pn2;
4803 if (tt != TOK_LB) {
4804 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4805 JSMSG_SYNTAX_ERROR);
4806 return NULL;
4808 pn3 = EndBracketedExpr(cx, ts, tc);
4809 if (!pn3)
4810 return NULL;
4812 pn2->pn_op = JSOP_QNAME;
4813 pn2->pn_arity = PN_BINARY;
4814 pn2->pn_left = pn;
4815 pn2->pn_right = pn3;
4816 return pn2;
4819 static JSParseNode *
4820 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4822 JSParseNode *pn;
4824 pn = PropertySelector(cx, ts, tc);
4825 if (!pn)
4826 return NULL;
4827 if (js_MatchToken(cx, ts, TOK_DBLCOLON))
4828 pn = QualifiedSuffix(cx, ts, pn, tc);
4829 return pn;
4832 static JSParseNode *
4833 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4835 JSParseNode *pn, *pn2;
4836 JSTokenType tt;
4838 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
4839 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4840 if (!pn)
4841 return NULL;
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);
4850 } else {
4851 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4852 JSMSG_SYNTAX_ERROR);
4853 return NULL;
4855 if (!pn2)
4856 return NULL;
4857 pn->pn_kid = pn2;
4858 return pn;
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;
4868 uintN oldflags;
4870 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
4871 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4872 if (!pn)
4873 return NULL;
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);
4884 if (!pn2)
4885 return NULL;
4887 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
4888 ts->flags = oldflags;
4889 pn->pn_kid = pn2;
4890 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
4891 return pn;
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)
4903 JSParseNode *pn;
4904 JSToken *tp;
4906 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4907 if (!pn)
4908 return NULL;
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;
4914 return pn;
4918 * Parse the productions:
4920 * XMLNameExpr:
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
4927 * will be TOK_LC.
4929 static JSParseNode *
4930 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4932 JSParseNode *pn, *pn2, *list;
4933 JSTokenType tt;
4935 pn = list = NULL;
4936 do {
4937 tt = CURRENT_TOKEN(ts).type;
4938 if (tt == TOK_LC) {
4939 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
4940 if (!pn2)
4941 return NULL;
4942 } else {
4943 JS_ASSERT(tt == TOK_XMLNAME);
4944 pn2 = XMLAtomNode(cx, ts, tc);
4945 if (!pn2)
4946 return NULL;
4949 if (!pn) {
4950 pn = pn2;
4951 } else {
4952 if (!list) {
4953 list = NewParseNode(cx, ts, PN_LIST, tc);
4954 if (!list)
4955 return NULL;
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;
4960 pn = list;
4962 pn->pn_pos.end = pn2->pn_pos.end;
4963 PN_APPEND(pn, pn2);
4965 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
4967 js_UngetToken(ts);
4968 return pn;
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:
4982 * XMLTagContent:
4983 * XMLNameExpr
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;
5001 JSTokenType tt;
5003 pn = XMLNameExpr(cx, ts, tc);
5004 if (!pn)
5005 return NULL;
5006 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
5007 list = NULL;
5009 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
5010 tt = js_GetToken(cx, ts);
5011 if (tt != TOK_XMLNAME && tt != TOK_LC) {
5012 js_UngetToken(ts);
5013 break;
5016 pn2 = XMLNameExpr(cx, ts, tc);
5017 if (!pn2)
5018 return NULL;
5019 if (!list) {
5020 list = NewParseNode(cx, ts, PN_LIST, tc);
5021 if (!list)
5022 return NULL;
5023 list->pn_type = tagtype;
5024 list->pn_pos.begin = pn->pn_pos.begin;
5025 PN_INIT_LIST_1(list, pn);
5026 pn = list;
5028 PN_APPEND(pn, pn2);
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;
5042 } else {
5043 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5044 JSMSG_BAD_XML_ATTR_VALUE);
5045 return NULL;
5047 if (!pn2)
5048 return NULL;
5049 pn->pn_pos.end = pn2->pn_pos.end;
5050 PN_APPEND(pn, pn2);
5053 return pn;
5056 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
5057 JS_BEGIN_MACRO \
5058 if ((tt) <= TOK_EOF) { \
5059 if ((tt) == TOK_EOF) { \
5060 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
5061 JSMSG_END_OF_XML_SOURCE); \
5063 return result; \
5065 JS_END_MACRO
5067 static JSParseNode *
5068 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5069 JSBool allowList);
5072 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
5073 * that opens the end tag for the container.
5075 static JSBool
5076 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
5077 JSTreeContext *tc)
5079 JSTokenType tt;
5080 JSParseNode *pn2;
5081 JSAtom *textAtom;
5083 ts->flags &= ~TSF_XMLTAGMODE;
5084 for (;;) {
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;
5092 if (textAtom) {
5093 /* Non-zero-length XML text scanned. */
5094 pn2 = XMLAtomNode(cx, ts, tc);
5095 if (!pn2)
5096 return JS_FALSE;
5097 pn->pn_pos.end = pn2->pn_pos.end;
5098 PN_APPEND(pn, pn2);
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)
5106 break;
5108 if (tt == TOK_LC) {
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);
5113 if (pn2) {
5114 pn2->pn_extra &= ~PNX_XMLROOT;
5115 pn->pn_extra |= pn2->pn_extra;
5117 } else {
5118 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
5119 tt == TOK_XMLPI);
5120 pn2 = XMLAtomNode(cx, ts, tc);
5122 if (!pn2)
5123 return JS_FALSE;
5124 pn->pn_pos.end = pn2->pn_pos.end;
5125 PN_APPEND(pn, pn2);
5128 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
5129 ts->flags |= TSF_XMLTAGMODE;
5130 return JS_TRUE;
5134 * Return a PN_LIST node containing an XML or XMLList Initialiser.
5136 static JSParseNode *
5137 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5138 JSBool allowList)
5140 JSParseNode *pn, *pn2, *list;
5141 JSTokenType tt;
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);
5148 if (!pn)
5149 return NULL;
5151 ts->flags |= TSF_XMLTAGMODE;
5152 tt = js_GetToken(cx, ts);
5153 if (tt == TOK_ERROR)
5154 return NULL;
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);
5161 if (!pn2)
5162 return NULL;
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) {
5169 PN_INIT_LIST(pn);
5170 RecycleTree(pn, tc);
5171 pn = pn2;
5172 } else {
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;
5181 } else {
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);
5186 return NULL;
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;
5195 pn2 = pn;
5196 pn = NewParseNode(cx, ts, PN_LIST, tc);
5197 if (!pn)
5198 return NULL;
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))
5210 return NULL;
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);
5217 return NULL;
5220 /* Parse end tag; check mismatch at compile-time if we can. */
5221 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
5222 if (!pn2)
5223 return NULL;
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);
5228 return NULL;
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));
5238 return NULL;
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);
5244 if (!list)
5245 return NULL;
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;
5264 PN_INIT_LIST(pn);
5265 pn->pn_extra |= PNX_XMLROOT;
5266 if (!XMLElementContent(cx, ts, pn, tc))
5267 return NULL;
5269 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
5270 } else {
5271 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5272 JSMSG_BAD_XML_NAME_SYNTAX);
5273 return NULL;
5276 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5277 ts->flags &= ~TSF_XMLTAGMODE;
5278 return pn;
5281 static JSParseNode *
5282 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5283 JSBool allowList)
5285 uint32 oldopts;
5286 JSParseNode *pn;
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);
5297 return pn;
5300 JS_FRIEND_API(JSParseNode *)
5301 js_ParseXMLText(JSContext *cx, JSObject *chain, JSParseContext *pc,
5302 JSBool allowList)
5304 JSParseNode *pn;
5305 JSTreeContext tc;
5306 JSTokenType tt;
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);
5324 pn = NULL;
5325 } else {
5326 pn = XMLElementOrListRoot(cx, TS(pc), &tc, allowList);
5329 TS(pc)->flags &= ~TSF_XMLONLYMODE;
5330 TREE_CONTEXT_FINISH(cx, &tc);
5331 return pn;
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;
5341 JSOp op;
5342 #if JS_HAS_SHARP_VARS
5343 JSParseNode *defsharp;
5344 JSBool notsharp;
5345 #endif
5347 JS_CHECK_RECURSION(cx, return NULL);
5349 #if JS_HAS_SHARP_VARS
5350 defsharp = NULL;
5351 notsharp = JS_FALSE;
5352 again:
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.
5358 #endif
5360 #if JS_HAS_GETTER_SETTER
5361 if (tt == TOK_NAME) {
5362 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
5363 if (tt == TOK_ERROR)
5364 return NULL;
5366 #endif
5368 switch (tt) {
5369 case TOK_FUNCTION:
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);
5375 if (!pn2)
5376 return NULL;
5377 pn2->pn_type = TOK_FUNCTION;
5378 pn = QualifiedSuffix(cx, ts, pn2, tc);
5379 if (!pn)
5380 return NULL;
5381 break;
5383 ts->flags &= ~TSF_KEYWORD_IS_NAME;
5384 #endif
5385 pn = FunctionExpr(cx, ts, tc);
5386 if (!pn)
5387 return NULL;
5388 break;
5390 case TOK_LB:
5392 JSBool matched;
5393 jsuint index;
5395 pn = NewParseNode(cx, ts, PN_LIST, tc);
5396 if (!pn)
5397 return NULL;
5398 pn->pn_type = TOK_RB;
5399 pn->pn_op = JSOP_NEWINIT;
5401 #if JS_HAS_SHARP_VARS
5402 if (defsharp) {
5403 PN_INIT_LIST_1(pn, defsharp);
5404 defsharp = NULL;
5405 } else
5406 #endif
5407 PN_INIT_LIST(pn);
5409 ts->flags |= TSF_OPERAND;
5410 matched = js_MatchToken(cx, ts, TOK_RB);
5411 ts->flags &= ~TSF_OPERAND;
5412 if (!matched) {
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);
5417 return NULL;
5420 ts->flags |= TSF_OPERAND;
5421 tt = js_PeekToken(cx, ts);
5422 ts->flags &= ~TSF_OPERAND;
5423 if (tt == TOK_RB) {
5424 pn->pn_extra |= PNX_ENDCOMMA;
5425 break;
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);
5432 } else {
5433 pn2 = AssignExpr(cx, ts, tc);
5435 if (!pn2)
5436 return NULL;
5437 PN_APPEND(pn, pn2);
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))
5442 break;
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 {
5460 * for (j in p)
5461 * if (i != j)
5462 * array.push(i * j)
5464 * array
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.
5490 if (index == 0 &&
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
5507 : &pn->pn_head;
5508 *pn->pn_tail = NULL;
5510 pntop = ComprehensionTail(cx, ts, tc, TOK_ARRAYPUSH,
5511 JSOP_ARRAYPUSH, pnexp);
5512 if (!pntop)
5513 return NULL;
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;
5521 return pn;
5524 case TOK_LC:
5526 JSBool afterComma;
5527 JSParseNode *pnval;
5529 pn = NewParseNode(cx, ts, PN_LIST, tc);
5530 if (!pn)
5531 return NULL;
5532 pn->pn_type = TOK_RC;
5533 pn->pn_op = JSOP_NEWINIT;
5535 #if JS_HAS_SHARP_VARS
5536 if (defsharp) {
5537 PN_INIT_LIST_1(pn, defsharp);
5538 defsharp = NULL;
5539 } else
5540 #endif
5541 PN_INIT_LIST(pn);
5543 afterComma = JS_FALSE;
5544 for (;;) {
5545 ts->flags |= TSF_KEYWORD_IS_NAME;
5546 tt = js_GetToken(cx, ts);
5547 ts->flags &= ~TSF_KEYWORD_IS_NAME;
5548 switch (tt) {
5549 case TOK_NUMBER:
5550 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
5551 if (pn3)
5552 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
5553 break;
5554 case TOK_NAME:
5555 #if JS_HAS_GETTER_SETTER
5557 JSAtom *atom;
5559 atom = CURRENT_TOKEN(ts).t_atom;
5560 if (atom == cx->runtime->atomState.getAtom)
5561 op = JSOP_GETTER;
5562 else if (atom == cx->runtime->atomState.setAtom)
5563 op = JSOP_SETTER;
5564 else
5565 goto property_name;
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) {
5571 js_UngetToken(ts);
5572 goto property_name;
5574 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
5575 if (!pn3)
5576 return NULL;
5577 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
5578 pn3->pn_slot = -1;
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);
5585 goto skip;
5587 property_name:
5588 #endif
5589 case TOK_STRING:
5590 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
5591 if (pn3)
5592 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
5593 break;
5594 case TOK_RC:
5595 if (afterComma &&
5596 !js_ReportCompileErrorNumber(cx, ts, NULL,
5597 JSREPORT_WARNING |
5598 JSREPORT_STRICT,
5599 JSMSG_TRAILING_COMMA)) {
5600 return NULL;
5602 goto end_obj_init;
5603 default:
5604 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5605 JSMSG_BAD_PROP_ID);
5606 return NULL;
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)
5614 return NULL;
5616 #endif
5618 if (tt != TOK_COLON) {
5619 #if JS_HAS_DESTRUCTURING_SHORTHAND
5620 if (tt != TOK_COMMA && tt != TOK_RC) {
5621 #endif
5622 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5623 JSMSG_COLON_AFTER_ID);
5624 return NULL;
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.
5632 js_UngetToken(ts);
5633 pn->pn_extra |= PNX_SHORTHAND;
5634 pnval = pn3;
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;
5641 op = JSOP_NOP;
5642 #endif
5643 } else {
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
5650 skip:
5651 #endif
5652 if (!pn2)
5653 return NULL;
5654 PN_APPEND(pn, pn2);
5656 tt = js_GetToken(cx, ts);
5657 if (tt == TOK_RC)
5658 goto end_obj_init;
5659 if (tt != TOK_COMMA) {
5660 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5661 JSMSG_CURLY_AFTER_LIST);
5662 return NULL;
5664 afterComma = JS_TRUE;
5667 end_obj_init:
5668 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5669 return pn;
5672 #if JS_HAS_BLOCK_SCOPE
5673 case TOK_LET:
5674 pn = LetBlock(cx, ts, tc, JS_FALSE);
5675 if (!pn)
5676 return NULL;
5677 break;
5678 #endif
5680 #if JS_HAS_SHARP_VARS
5681 case TOK_DEFSHARP:
5682 if (defsharp)
5683 goto badsharp;
5684 defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
5685 if (!defsharp)
5686 return NULL;
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;
5691 goto again;
5693 case TOK_USESHARP:
5694 /* Check for forward/dangling references at runtime, to allow eval. */
5695 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5696 if (!pn)
5697 return NULL;
5698 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
5699 notsharp = JS_TRUE;
5700 break;
5701 #endif /* JS_HAS_SHARP_VARS */
5703 case TOK_LP:
5705 JSBool genexp;
5707 pn = NewParseNode(cx, ts, PN_UNARY, tc);
5708 if (!pn)
5709 return NULL;
5710 pn2 = ParenExpr(cx, ts, tc, pn, &genexp);
5711 if (!pn2)
5712 return NULL;
5713 if (genexp)
5714 return pn2;
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 &&
5719 !afterDot)) {
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);
5733 pn = pn2;
5734 } else {
5735 pn->pn_type = TOK_RP;
5736 pn->pn_kid = pn2;
5738 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5739 break;
5742 #if JS_HAS_XML_SUPPORT
5743 case TOK_STAR:
5744 pn = QualifiedIdentifier(cx, ts, tc);
5745 if (!pn)
5746 return NULL;
5747 notsharp = JS_TRUE;
5748 break;
5750 case TOK_AT:
5751 pn = AttributeIdentifier(cx, ts, tc);
5752 if (!pn)
5753 return NULL;
5754 notsharp = JS_TRUE;
5755 break;
5757 case TOK_XMLSTAGO:
5758 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
5759 if (!pn)
5760 return NULL;
5761 notsharp = JS_TRUE; /* XXXbe could be sharp? */
5762 break;
5763 #endif /* JS_HAS_XML_SUPPORT */
5765 case TOK_STRING:
5766 #if JS_HAS_SHARP_VARS
5767 notsharp = JS_TRUE;
5768 /* FALL THROUGH */
5769 #endif
5771 #if JS_HAS_XML_SUPPORT
5772 case TOK_XMLCDATA:
5773 case TOK_XMLCOMMENT:
5774 case TOK_XMLPI:
5775 #endif
5776 case TOK_NAME:
5777 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5778 if (!pn)
5779 return NULL;
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;
5784 else
5785 #endif
5786 pn->pn_op = CURRENT_TOKEN(ts).t_op;
5787 if (tt == TOK_NAME) {
5788 pn->pn_arity = PN_NAME;
5789 pn->pn_slot = -1;
5791 #if JS_HAS_XML_SUPPORT
5792 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
5793 if (afterDot) {
5794 JSString *str;
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);
5812 return NULL;
5815 pn = QualifiedSuffix(cx, ts, pn, tc);
5816 if (!pn)
5817 return NULL;
5818 break;
5820 #endif
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;
5828 break;
5830 case TOK_REGEXP:
5832 JSObject *obj;
5834 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5835 if (!pn)
5836 return NULL;
5838 /* Token stream ensures that tokenbuf is NUL-terminated. */
5839 JS_ASSERT(*ts->tokenbuf.ptr == (jschar) 0);
5840 obj = js_NewRegExpObject(cx, ts,
5841 ts->tokenbuf.base,
5842 ts->tokenbuf.ptr - ts->tokenbuf.base,
5843 CURRENT_TOKEN(ts).t_reflags);
5844 if (!obj)
5845 return NULL;
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);
5852 if (!pn->pn_pob)
5853 return NULL;
5855 pn->pn_op = JSOP_REGEXP;
5856 break;
5859 case TOK_NUMBER:
5860 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5861 if (!pn)
5862 return NULL;
5863 pn->pn_op = JSOP_DOUBLE;
5864 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
5865 #if JS_HAS_SHARP_VARS
5866 notsharp = JS_TRUE;
5867 #endif
5868 break;
5870 case TOK_PRIMARY:
5871 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5872 if (!pn)
5873 return NULL;
5874 pn->pn_op = CURRENT_TOKEN(ts).t_op;
5875 #if JS_HAS_SHARP_VARS
5876 notsharp = JS_TRUE;
5877 #endif
5878 break;
5880 case TOK_ERROR:
5881 /* The scanner or one of its subroutines reported the error. */
5882 return NULL;
5884 default:
5885 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5886 JSMSG_SYNTAX_ERROR);
5887 return NULL;
5890 #if JS_HAS_SHARP_VARS
5891 if (defsharp) {
5892 if (notsharp) {
5893 badsharp:
5894 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5895 JSMSG_BAD_SHARP_VAR_DEF);
5896 return NULL;
5898 defsharp->pn_kid = pn;
5899 return defsharp;
5901 #endif
5902 return pn;
5905 static JSParseNode *
5906 ParenExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5907 JSParseNode *pn1, JSBool *genexp)
5909 JSTokenPtr begin;
5910 JSParseNode *pn;
5911 #if JS_HAS_GENERATOR_EXPRS
5912 uintN oldflags = tc->flags;
5913 #endif
5915 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LP);
5916 begin = CURRENT_TOKEN(ts).pos.begin;
5918 if (genexp)
5919 *genexp = JS_FALSE;
5920 pn = BracketedExpr(cx, ts, tc);
5921 if (!pn)
5922 return NULL;
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,
5929 js_yield_str);
5930 return NULL;
5932 if (pn->pn_type == TOK_COMMA) {
5933 js_ReportCompileErrorNumber(cx, ts, PN_LAST(pn), JSREPORT_ERROR,
5934 JSMSG_BAD_GENERATOR_SYNTAX,
5935 js_generator_str);
5936 return NULL;
5938 if (!pn1) {
5939 pn1 = NewParseNode(cx, ts, PN_UNARY, tc);
5940 if (!pn1)
5941 return NULL;
5943 pn->pn_pos.begin = begin;
5944 pn = GeneratorExpr(cx, ts, tc, oldflags, pn1, pn);
5945 if (!pn)
5946 return NULL;
5947 if (genexp) {
5948 if (js_GetToken(cx, ts) != TOK_RP) {
5949 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5950 JSMSG_BAD_GENERATOR_SYNTAX,
5951 js_generator_str);
5952 return NULL;
5954 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5955 *genexp = JS_TRUE;
5958 #endif /* JS_HAS_GENERATOR_EXPRS */
5960 return pn;
5964 * Fold from one constant type to another.
5965 * XXX handles only strings and numbers for now
5967 static JSBool
5968 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
5970 if (pn->pn_type != type) {
5971 switch (type) {
5972 case TOK_NUMBER:
5973 if (pn->pn_type == TOK_STRING) {
5974 jsdouble d;
5975 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
5976 return JS_FALSE;
5977 pn->pn_dval = d;
5978 pn->pn_type = TOK_NUMBER;
5979 pn->pn_op = JSOP_DOUBLE;
5981 break;
5983 case TOK_STRING:
5984 if (pn->pn_type == TOK_NUMBER) {
5985 JSString *str = js_NumberToString(cx, pn->pn_dval);
5986 if (!str)
5987 return JS_FALSE;
5988 pn->pn_atom = js_AtomizeString(cx, str, 0);
5989 if (!pn->pn_atom)
5990 return JS_FALSE;
5991 pn->pn_type = TOK_STRING;
5992 pn->pn_op = JSOP_STRING;
5994 break;
5996 default:;
5999 return JS_TRUE;
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.
6007 static JSBool
6008 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
6009 JSParseNode *pn, JSTreeContext *tc)
6011 jsdouble d, d2;
6012 int32 i, j;
6014 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
6015 d = pn1->pn_dval;
6016 d2 = pn2->pn_dval;
6017 switch (op) {
6018 case JSOP_LSH:
6019 case JSOP_RSH:
6020 i = js_DoubleToECMAInt32(d);
6021 j = js_DoubleToECMAInt32(d2);
6022 j &= 31;
6023 d = (op == JSOP_LSH) ? i << j : i >> j;
6024 break;
6026 case JSOP_URSH:
6027 j = js_DoubleToECMAInt32(d2);
6028 j &= 31;
6029 d = js_DoubleToECMAUint32(d) >> j;
6030 break;
6032 case JSOP_ADD:
6033 d += d2;
6034 break;
6036 case JSOP_SUB:
6037 d -= d2;
6038 break;
6040 case JSOP_MUL:
6041 d *= d2;
6042 break;
6044 case JSOP_DIV:
6045 if (d2 == 0) {
6046 #if defined(XP_WIN)
6047 /* XXX MSVC miscompiles such that (NaN == 0) */
6048 if (JSDOUBLE_IS_NaN(d2))
6049 d = *cx->runtime->jsNaN;
6050 else
6051 #endif
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;
6056 else
6057 d = *cx->runtime->jsPositiveInfinity;
6058 } else {
6059 d /= d2;
6061 break;
6063 case JSOP_MOD:
6064 if (d2 == 0) {
6065 d = *cx->runtime->jsNaN;
6066 } else {
6067 #if defined(XP_WIN)
6068 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
6069 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
6070 #endif
6071 d = fmod(d, d2);
6073 break;
6075 default:;
6078 /* Take care to allow pn1 or pn2 to alias pn. */
6079 if (pn1 != pn)
6080 RecycleTree(pn1, tc);
6081 if (pn2 != pn)
6082 RecycleTree(pn2, tc);
6083 pn->pn_type = TOK_NUMBER;
6084 pn->pn_op = JSOP_DOUBLE;
6085 pn->pn_arity = PN_NULLARY;
6086 pn->pn_dval = d;
6087 return JS_TRUE;
6090 #if JS_HAS_XML_SUPPORT
6092 static JSBool
6093 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
6095 JSTokenType tt;
6096 JSParseNode **pnp, *pn1, *pn2;
6097 JSString *accum, *str;
6098 uint32 i, j;
6099 JSTempValueRooter tvr;
6101 JS_ASSERT(pn->pn_arity == PN_LIST);
6102 tt = PN_TYPE(pn);
6103 pnp = &pn->pn_head;
6104 pn1 = *pnp;
6105 accum = NULL;
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
6118 * js_ConcatStrings.
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) {
6124 case TOK_XMLATTR:
6125 if (!accum)
6126 goto cantfold;
6127 /* FALL THROUGH */
6128 case TOK_XMLNAME:
6129 case TOK_XMLSPACE:
6130 case TOK_XMLTEXT:
6131 case TOK_STRING:
6132 if (pn2->pn_arity == PN_LIST)
6133 goto cantfold;
6134 str = ATOM_TO_STRING(pn2->pn_atom);
6135 break;
6137 case TOK_XMLCDATA:
6138 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
6139 if (!str)
6140 return JS_FALSE;
6141 break;
6143 case TOK_XMLCOMMENT:
6144 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
6145 if (!str)
6146 return JS_FALSE;
6147 break;
6149 case TOK_XMLPI:
6150 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
6151 ATOM_TO_STRING(pn2->pn_atom2));
6152 if (!str)
6153 return JS_FALSE;
6154 break;
6156 cantfold:
6157 default:
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);
6163 if (accum)
6164 js_FileEscapedString(stdout, accum, 0);
6165 else
6166 fputs("NULL", stdout);
6167 fputc('\n', stdout);
6168 #endif
6169 } else if (accum && pn1 != pn2) {
6170 while (pn1->pn_next != pn2) {
6171 pn1 = RecycleTree(pn1, tc);
6172 --pn->pn_count;
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);
6178 if (!pn1->pn_atom)
6179 return JS_FALSE;
6180 JS_ASSERT(pnp != &pn1->pn_next);
6181 *pnp = pn1;
6183 pnp = &pn2->pn_next;
6184 pn1 = *pnp;
6185 accum = NULL;
6186 continue;
6189 if (accum) {
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);
6195 if (!str)
6196 return JS_FALSE;
6197 #ifdef DEBUG_brendanXXX
6198 printf("2: %d, %d => ", i, j);
6199 js_FileEscapedString(stdout, str, 0);
6200 printf(" (%u)\n", JSSTRING_LENGTH(str));
6201 #endif
6202 ++j;
6204 accum = str;
6207 if (accum) {
6208 str = NULL;
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);
6215 if (str) {
6216 accum = js_ConcatStrings(cx, accum, str);
6217 if (!accum)
6218 return JS_FALSE;
6221 JS_ASSERT(*pnp == pn1);
6222 while (pn1->pn_next) {
6223 pn1 = RecycleTree(pn1, tc);
6224 --pn->pn_count;
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);
6230 if (!pn1->pn_atom)
6231 return JS_FALSE;
6232 JS_ASSERT(pnp != &pn1->pn_next);
6233 *pnp = pn1;
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;
6251 return JS_TRUE;
6254 #endif /* JS_HAS_XML_SUPPORT */
6256 static JSBool
6257 StartsWith(JSParseNode *pn, JSTokenType tt)
6259 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
6261 recur:
6262 if (pn->pn_type == tt)
6263 return JS_TRUE;
6264 switch (pn->pn_arity) {
6265 case PN_FUNC:
6266 return tt == TOK_FUNCTION;
6267 case PN_LIST:
6268 if (pn->pn_head)
6269 TAIL_RECURSE(pn->pn_head);
6270 break;
6271 case PN_TERNARY:
6272 if (pn->pn_kid1)
6273 TAIL_RECURSE(pn->pn_kid1);
6274 break;
6275 case PN_BINARY:
6276 if (pn->pn_left)
6277 TAIL_RECURSE(pn->pn_left);
6278 break;
6279 case PN_UNARY:
6280 /* A parenthesized expression starts with a left parenthesis. */
6281 if (pn->pn_type == TOK_RP)
6282 return tt == TOK_LP;
6283 if (pn->pn_kid)
6284 TAIL_RECURSE(pn->pn_kid);
6285 break;
6286 case PN_NAME:
6287 if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT)
6288 TAIL_RECURSE(pn->pn_expr);
6289 /* FALL THROUGH */
6291 return JS_FALSE;
6292 #undef TAIL_RECURSE
6295 static int
6296 Boolish(JSParseNode *pn)
6298 switch (pn->pn_op) {
6299 case JSOP_DOUBLE:
6300 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
6302 case JSOP_STRING:
6303 return JSSTRING_LENGTH(ATOM_TO_STRING(pn->pn_atom)) != 0;
6305 #if JS_HAS_GENERATOR_EXPRS
6306 case JSOP_CALL:
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)
6314 break;
6315 JSParseNode *pn2 = pn->pn_head;
6316 if (pn2->pn_type != TOK_FUNCTION)
6317 break;
6318 if (!(pn2->pn_flags & TCF_GENEXP_LAMBDA))
6319 break;
6320 /* FALL THROUGH */
6322 #endif
6324 case JSOP_DEFFUN:
6325 case JSOP_NAMEDFUNOBJ:
6326 case JSOP_ANONFUNOBJ:
6327 case JSOP_THIS:
6328 case JSOP_TRUE:
6329 return 1;
6331 case JSOP_NULL:
6332 case JSOP_FALSE:
6333 return 0;
6335 default:;
6337 return -1;
6340 JSBool
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) {
6348 case PN_FUNC:
6350 uint16 oldflags = tc->flags;
6352 tc->flags = (uint16) pn->pn_flags;
6353 if (!js_FoldConstants(cx, pn->pn_body, tc))
6354 return JS_FALSE;
6355 tc->flags = oldflags;
6356 break;
6359 case PN_LIST:
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))
6367 return JS_FALSE;
6369 break;
6372 case PN_TERNARY:
6373 /* Any kid may be null (e.g. for (;;)). */
6374 pn1 = pn->pn_kid1;
6375 pn2 = pn->pn_kid2;
6376 pn3 = pn->pn_kid3;
6377 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
6378 return JS_FALSE;
6379 if (pn2) {
6380 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
6381 return JS_FALSE;
6382 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
6383 RecycleTree(pn2, tc);
6384 pn->pn_kid2 = NULL;
6387 if (pn3 && !js_FoldConstants(cx, pn3, tc))
6388 return JS_FALSE;
6389 break;
6391 case PN_BINARY:
6392 pn1 = pn->pn_left;
6393 pn2 = pn->pn_right;
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))
6398 return JS_FALSE;
6399 if (!js_FoldConstants(cx, pn2, tc, inCond))
6400 return JS_FALSE;
6401 break;
6404 /* First kid may be null (for default case in switch). */
6405 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
6406 return JS_FALSE;
6407 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
6408 return JS_FALSE;
6409 break;
6411 case PN_UNARY:
6412 /* Our kid may be null (e.g. return; vs. return e;). */
6413 pn1 = pn->pn_kid;
6414 if (pn1 &&
6415 !js_FoldConstants(cx, pn1, tc,
6416 (inCond && pn->pn_type == TOK_RP) ||
6417 pn->pn_op == JSOP_NOT)) {
6418 return JS_FALSE;
6420 break;
6422 case PN_NAME:
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
6427 * dot in the chain.
6429 pn1 = pn->pn_expr;
6430 while (pn1 && pn1->pn_arity == PN_NAME)
6431 pn1 = pn1->pn_expr;
6432 if (pn1 && !js_FoldConstants(cx, pn1, tc))
6433 return JS_FALSE;
6434 break;
6436 case PN_NULLARY:
6437 break;
6440 switch (pn->pn_type) {
6441 case TOK_IF:
6442 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
6443 break;
6444 /* FALL THROUGH */
6446 case TOK_HOOK:
6447 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
6448 while (pn1->pn_type == TOK_RP)
6449 pn1 = pn1->pn_kid;
6450 switch (pn1->pn_type) {
6451 case TOK_NUMBER:
6452 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
6453 pn2 = pn3;
6454 break;
6455 case TOK_STRING:
6456 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
6457 pn2 = pn3;
6458 break;
6459 case TOK_PRIMARY:
6460 if (pn1->pn_op == JSOP_TRUE)
6461 break;
6462 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
6463 pn2 = pn3;
6464 break;
6466 /* FALL THROUGH */
6467 default:
6468 /* Early return to dodge common code that copies pn2 to pn. */
6469 return JS_TRUE;
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))
6475 break;
6476 #endif
6478 if (pn2) {
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;
6488 pn->pn_kid = pn2;
6489 if (pn3 && pn3 != pn2)
6490 RecycleTree(pn3, tc);
6491 break;
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;
6505 PN_INIT_LIST(pn);
6507 RecycleTree(pn2, tc);
6508 if (pn3 && pn3 != pn2)
6509 RecycleTree(pn3, tc);
6510 break;
6512 case TOK_OR:
6513 case TOK_AND:
6514 if (inCond) {
6515 if (pn->pn_arity == PN_LIST) {
6516 JSParseNode **pnp = &pn->pn_head;
6517 JS_ASSERT(*pnp == pn1);
6518 do {
6519 int cond = Boolish(pn1);
6520 if (cond == (pn->pn_type == TOK_OR)) {
6521 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
6522 pn3 = pn2->pn_next;
6523 RecycleTree(pn2, tc);
6524 --pn->pn_count;
6526 pn1->pn_next = NULL;
6527 break;
6529 if (cond != -1) {
6530 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
6531 if (pn->pn_count == 1)
6532 break;
6533 *pnp = pn1->pn_next;
6534 RecycleTree(pn1, tc);
6535 --pn->pn_count;
6536 } else {
6537 pnp = &pn1->pn_next;
6539 } while ((pn1 = *pnp) != NULL);
6541 // We may have to change arity from LIST to BINARY.
6542 pn1 = pn->pn_head;
6543 if (pn->pn_count == 2) {
6544 pn2 = pn1->pn_next;
6545 pn1->pn_next = NULL;
6546 JS_ASSERT(!pn2->pn_next);
6547 pn->pn_arity = PN_BINARY;
6548 pn->pn_left = pn1;
6549 pn->pn_right = pn2;
6550 } else if (pn->pn_count == 1) {
6551 PN_MOVE_NODE(pn, pn1);
6552 RecycleTree(pn1, tc);
6554 } else {
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);
6566 break;
6568 case TOK_ASSIGN:
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)
6576 break;
6577 if (pn->pn_op != JSOP_ADD)
6578 goto do_binary_op;
6579 /* FALL THROUGH */
6581 case TOK_PLUS:
6582 if (pn->pn_arity == PN_LIST) {
6583 size_t length, length2;
6584 jschar *chars;
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)
6594 return JS_TRUE;
6595 if (pn->pn_extra != PNX_STRCAT)
6596 goto do_binary_op;
6598 /* Ok, we're concatenating: convert non-string constant operands. */
6599 length = 0;
6600 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
6601 if (!FoldType(cx, pn2, TOK_STRING))
6602 return JS_FALSE;
6603 /* XXX fold only if all operands convert to string */
6604 if (pn2->pn_type != TOK_STRING)
6605 return JS_TRUE;
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));
6611 if (!chars)
6612 return JS_FALSE;
6613 str = js_NewString(cx, chars, length);
6614 if (!str) {
6615 JS_free(cx, chars);
6616 return JS_FALSE;
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);
6624 chars += length2;
6626 *chars = 0;
6628 /* Atomize the result string and mutate pn to refer to it. */
6629 pn->pn_atom = js_AtomizeString(cx, str, 0);
6630 if (!pn->pn_atom)
6631 return JS_FALSE;
6632 pn->pn_type = TOK_STRING;
6633 pn->pn_op = JSOP_STRING;
6634 pn->pn_arity = PN_NULLARY;
6635 break;
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,
6644 TOK_STRING)) {
6645 return JS_FALSE;
6647 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
6648 return JS_TRUE;
6649 left = ATOM_TO_STRING(pn1->pn_atom);
6650 right = ATOM_TO_STRING(pn2->pn_atom);
6651 str = js_ConcatStrings(cx, left, right);
6652 if (!str)
6653 return JS_FALSE;
6654 pn->pn_atom = js_AtomizeString(cx, str, 0);
6655 if (!pn->pn_atom)
6656 return JS_FALSE;
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);
6662 break;
6665 /* Can't concatenate string literals, let's try numbers. */
6666 goto do_binary_op;
6668 case TOK_STAR:
6669 case TOK_SHOP:
6670 case TOK_MINUS:
6671 case TOK_DIVOP:
6672 do_binary_op:
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))
6677 return JS_FALSE;
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)
6682 break;
6684 if (!pn2) {
6685 JSOp op = PN_OP(pn);
6687 pn2 = pn1->pn_next;
6688 pn3 = pn2->pn_next;
6689 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
6690 return JS_FALSE;
6691 while ((pn2 = pn3) != NULL) {
6692 pn3 = pn2->pn_next;
6693 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
6694 return JS_FALSE;
6697 } else {
6698 JS_ASSERT(pn->pn_arity == PN_BINARY);
6699 if (!FoldType(cx, pn1, TOK_NUMBER) ||
6700 !FoldType(cx, pn2, TOK_NUMBER)) {
6701 return JS_FALSE;
6703 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
6704 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
6705 return JS_FALSE;
6708 break;
6710 case TOK_UNARYOP:
6711 while (pn1->pn_type == TOK_RP)
6712 pn1 = pn1->pn_kid;
6713 if (pn1->pn_type == TOK_NUMBER) {
6714 jsdouble d;
6716 /* Operate on one numeric constant. */
6717 d = pn1->pn_dval;
6718 switch (pn->pn_op) {
6719 case JSOP_BITNOT:
6720 d = ~js_DoubleToECMAInt32(d);
6721 break;
6723 case JSOP_NEG:
6724 #ifdef HPUX
6726 * Negation of a zero doesn't produce a negative
6727 * zero on HPUX. Perform the operation by bit
6728 * twiddling.
6730 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
6731 #else
6732 d = -d;
6733 #endif
6734 break;
6736 case JSOP_POS:
6737 break;
6739 case JSOP_NOT:
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;
6743 /* FALL THROUGH */
6745 default:
6746 /* Return early to dodge the common TOK_NUMBER code. */
6747 return JS_TRUE;
6749 pn->pn_type = TOK_NUMBER;
6750 pn->pn_op = JSOP_DOUBLE;
6751 pn->pn_arity = PN_NULLARY;
6752 pn->pn_dval = d;
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);
6763 break;
6765 #if JS_HAS_XML_SUPPORT
6766 case TOK_XMLELEM:
6767 case TOK_XMLLIST:
6768 case TOK_XMLPTAGC:
6769 case TOK_XMLSTAGO:
6770 case TOK_XMLETAGO:
6771 case TOK_XMLNAME:
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))
6775 return JS_FALSE;
6777 break;
6779 case TOK_AT:
6780 if (pn1->pn_type == TOK_XMLNAME) {
6781 jsval v;
6782 JSParsedObjectBox *xmlpob;
6784 v = ATOM_KEY(pn1->pn_atom);
6785 if (!js_ToAttributeName(cx, &v))
6786 return JS_FALSE;
6787 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
6789 xmlpob = js_NewParsedObjectBox(cx, tc->parseContext,
6790 JSVAL_TO_OBJECT(v));
6791 if (!xmlpob)
6792 return JS_FALSE;
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);
6800 break;
6801 #endif /* JS_HAS_XML_SUPPORT */
6803 default:;
6806 if (inCond) {
6807 int cond = Boolish(pn);
6808 if (cond >= 0) {
6809 if (pn->pn_arity == PN_LIST) {
6810 pn2 = pn->pn_head;
6811 do {
6812 pn3 = pn2->pn_next;
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;
6822 return JS_TRUE;